class RallyAPI::RallyRestJson

Main Class to instantiate when using the tool

Constants

DEFAULT_WSAPI_VERSION

Attributes

custom_fields_for_type[R]
logger[RW]
low_debug[RW]
proxy_info[RW]
rally_alias_types[RW]
rally_connection[R]
rally_default_project[RW]
rally_default_workspace[RW]
rally_headers[RW]
rally_project_name[RW]
rally_rest_api_compat[RW]
rally_url[RW]
rally_workspace_name[RW]
wsapi_version[RW]

Public Class Methods

new(args) click to toggle source
# File lib/rally_api/rally_rest_json.rb, line 54
def initialize(args)
  pre_init()

  auth_info = {}

  auth_info[:base_url]  = @rally_url = args[:base_url] || "https://rally1.rallydev.com/slm"
  auth_info[:username]  = args[:username]
  auth_info[:password]  = args[:password]
  auth_info[:api_key]   = args[:api_key]
  @rally_workspace_name = args[:workspace]
  @rally_project_name   = args[:project]
  @wsapi_version        = args[:version]  || DEFAULT_WSAPI_VERSION
  @rally_headers        = args[:headers]  || CustomHttpHeader.new
  @proxy_info           = args[:proxy]
  @skip_sec_key         = args[:skip_sec_key]

  #flag to help RallyRestAPI users to use snake case field names eg Defect.fixed_in_build vs Defect["FixedInBuild"]
  @rally_rest_api_compat  = args[:rally_rest_api_compat] || false

  @low_debug = args[:debug]  || false
  @logger    = args[:logger] || nil    #assumes this is an instance of Logger

  @rally_connection = RallyJsonConnection.new(@rally_headers, @low_debug, @proxy_info)
  #@rally_connection.set_client_user(@rally_url, @rally_user, @rally_password)
  @rally_connection.set_auth(auth_info)
  @rally_connection.logger  = @logger unless @logger.nil?

  if @wsapi_version.to_s.include?("v2.") && !@skip_sec_key && auth_info[:api_key].nil?
    @rally_connection.setup_security_token(security_url)
  end

  if !@rally_workspace_name.nil?
    @rally_default_workspace = find_workspace(@rally_workspace_name)
    raise StandardError, "unable to find default workspace #{@rally_workspace_name}" if @rally_default_workspace.nil?
  end

  if !@rally_project_name.nil?
    @rally_default_project = find_project(@rally_default_workspace, @rally_project_name)
    raise StandardError, "unable to find default project #{@rally_project_name}" if @rally_default_project.nil?
  end

  self
end

Private Class Methods

camel_case_word(sym) click to toggle source
# File lib/rally_api/rally_rest_json.rb, line 536
def self.camel_case_word(sym)
  word = sym.to_s

  if word.start_with?("c_")
    custom_field_without_c = word.sub("c_", "")
    camelized = camelize(custom_field_without_c)
    camelized[0] = custom_field_without_c[0]
    "c_#{camelized}"
  else
    camelize(word)
  end
end
camelize(str) click to toggle source

implemented basic version here to not have to include ActiveSupport

# File lib/rally_api/rally_rest_json.rb, line 550
def self.camelize(str)
  str.split('_').map {|w| w.capitalize}.join
end
fix_case(values) click to toggle source
# File lib/rally_api/rally_rest_json.rb, line 529
def self.fix_case(values)
  values.inject({}) do |new_values, (key, value)|
    new_values[camel_case_word(key)] = value.is_a?(Hash) ? fix_case(value) : value
    new_values
  end
end

Public Instance Methods

adjust_find_threads(num_threads) click to toggle source
# File lib/rally_api/rally_rest_json.rb, line 292
def adjust_find_threads(num_threads)
  @rally_connection.set_find_threads(num_threads)
end
allowed_values(type, field, workspace = nil) click to toggle source

todo - check support for portfolio item fields

# File lib/rally_api/rally_rest_json.rb, line 357
def allowed_values(type, field, workspace = nil)
  rally_workspace_object(workspace)
  
  type_def = get_typedef_for(type, workspace)
  allowed_vals = {}
  type_def["Attributes"].each do |attr|
    next if attr["ElementName"] != field
    attr["AllowedValues"].each do |val_ref|
      val = val_ref["StringValue"]
      val = "Null" if val.nil? || val.empty?
      allowed_vals[val] = true
    end
  end
  allowed_vals
end
create(type, fields, params = {}) click to toggle source
# File lib/rally_api/rally_rest_json.rb, line 173
def create(type, fields, params = {})
  type = check_type(type)
  if (fields["Workspace"].nil? && fields["Project"].nil?)
    fields["Workspace"] = rally_workspace_object._ref unless rally_workspace_object.nil?
    fields["Project"] = @rally_default_project._ref unless @rally_default_project.nil?
  end

  ws_ref = fields["Workspace"]
  ws_ref = ws_ref["_ref"] unless ws_ref.class == String || ws_ref.nil?
  params[:workspace] = ws_ref

  fields = RallyAPI::RallyRestJson.fix_case(fields) if @rally_rest_api_compat
  postable_fields = check_fields(fields)
  postable_fields = adjust_for_custom(type, postable_fields) if @wsapi_version =~ /^v2/
  object2create = { type => postable_fields }
  args = { :method => :post, :payload => object2create }
  json_response = send_request(make_create_url(type), args, params)
  #todo - check for warnings
  RallyObject.new(self, json_response["CreateResult"]["Object"], warnings(json_response)).read()
end
custom_fields_for(type, workspace = rally_workspace_object) click to toggle source
# File lib/rally_api/rally_rest_json.rb, line 373
def custom_fields_for(type, workspace = rally_workspace_object)
  @custom_fields_for_type[workspace.ObjectID] = {} if @custom_fields_for_type[workspace.ObjectID].nil?
  if @custom_fields_for_type[workspace.ObjectID][type].nil?
    @custom_fields_for_type[workspace.ObjectID][type] = {}
    type_def = get_typedef_for(type, workspace)
    type_def["Attributes"].each do |attr|
      next if attr["Custom"].to_s == "false"
      elem_name = attr["ElementName"]
      elem_name = elem_name.slice(2..256) unless @wsapi_version.start_with?("1")
      @custom_fields_for_type[workspace.ObjectID][type][elem_name] = attr["ElementName"]
    end
  end
  return @custom_fields_for_type[workspace.ObjectID][type]
end
debug_logging_off() click to toggle source
# File lib/rally_api/rally_rest_json.rb, line 130
def debug_logging_off
  @low_debug = false
  @rally_connection.low_debug = false
end
debug_logging_on() click to toggle source
# File lib/rally_api/rally_rest_json.rb, line 125
def debug_logging_on
  @low_debug = true
  @rally_connection.low_debug = true
end
delete(ref_to_delete) click to toggle source
# File lib/rally_api/rally_rest_json.rb, line 204
def delete(ref_to_delete)
  args = { :method => :delete }
  #json_response = @rally_connection.delete_object(ref_to_delete, args)
  json_response = send_request(ref_to_delete, args)
  json_response["OperationResult"]
end
find(query_obj = RallyQuery.new) { |query_obj| ... } click to toggle source
-----

Querying Rally example test_query = RallyAPI::RallyQuery.new() test_query.type = :defect test_query.fetch = “Name” test_query.workspace = {“_ref” => “rally1.rallydev.com/slm/webservice/v2.0/workspace/12345” } optional test_query.project = {“_ref” => “rally1.rallydev.com/slm/webservice/v2.0/project/12345” } optional test_query.page_size = 200 optional - default is 200 test_query.limit = 1000 optional - default is 99999 test_query.project_scope_up = false test_query.project_scope_down = true test_query.order = “Name Asc” test_query.query_string = “(Severity = "High")”

results = @rally.find(test_query)

tip - set the fetch string of the query to the fields you need -
only resort to the read method if you want your code to be slow

results.each do |defect|

puts defect.Name   # or defect["Name"]
defect.read    #read the whole defect from Rally to get all fields (eg Severity)
puts defect.Severity

end query_obj is RallyQuery

# File lib/rally_api/rally_rest_json.rb, line 272
def find(query_obj = RallyQuery.new)
  yield query_obj if block_given?

  if query_obj.workspace.nil?
    query_obj.workspace = rally_workspace_object unless rally_workspace_object.nil?
  end

  errs = query_obj.validate()
  if errs.length > 0
    raise StandardError, "Errors making Rally Query: #{errs.to_s}"
  end

  query_url = make_query_url(@rally_url, @wsapi_version, check_type(query_obj.type.to_s))
  query_url += '.js' if @wsapi_version !~ /^v2/
  query_params = query_obj.make_query_params
  args =  {:method => :get}
  json_response = @rally_connection.get_all_json_results(query_url, args, query_params, query_obj.limit)
  RallyQueryResult.new(self, json_response, warnings(json_response))
end
find_project(workspace_object, project_name) click to toggle source
# File lib/rally_api/rally_rest_json.rb, line 149
def find_project(workspace_object, project_name)
  if workspace_object.nil?
    raise StandardError, "A workspace must be provided to find a project"
  end

  query = RallyQuery.new()
  query.type          = :project
  query.query_string  = "((Name = \"#{project_name}\") AND (State = \"Open\"))"
  query.limit         = 20
  query.fetch         = true
  query.workspace     = workspace_object

  results = find(query)
  return results.first if results.length > 0
  nil
end
find_workspace(workspace_name, matcher_ref = 'Name') click to toggle source
# File lib/rally_api/rally_rest_json.rb, line 135
def find_workspace(workspace_name, matcher_ref = 'Name')
  # use matcher_ref to search for 'Name' or '_ref'
  sub = self.user["Subscription"].read({:fetch => "Workspaces,Name,State"})
  workspace = nil
  sub.Workspaces.each do |ws|
    #ws.read
    if (ws[matcher_ref] == workspace_name) && (ws["State"] == "Open")
      workspace = ws
      break  #doing a break for performance some customers have 100+ workspaces - no need to do the others
    end
  end
  workspace
end
get_fields_for(type, workspace = rally_workspace_object) click to toggle source
# File lib/rally_api/rally_rest_json.rb, line 345
def get_fields_for(type, workspace = rally_workspace_object)
  type_def = get_typedef_for(type, workspace)
  return nil if type_def.nil?
  fields = {}
  type_def["Attributes"].each do |attr|
    field_name = attr["ElementName"]
    fields[field_name] = attr
  end
  fields
end
get_typedef_for(type, workspace = rally_workspace_object) click to toggle source
# File lib/rally_api/rally_rest_json.rb, line 333
def get_typedef_for(type, workspace = rally_workspace_object)
  type = type.to_s if type.class == Symbol
  type = check_type(type)
  type_def_query             = RallyQuery.new()
  type_def_query.type        = "typedefinition"
  type_def_query.workspace   = workspace
  type_def_query.fetch       = true
  type_def_query.query_string= "(TypePath = \"#{type}\")"
  type_def = find(type_def_query)
  type_def.first
end
rally_workspace_object(new_wksp = nil) click to toggle source
# File lib/rally_api/rally_rest_json.rb, line 111
def rally_workspace_object(new_wksp = nil)
  return @rally_default_workspace if new_wksp.nil?
  if new_wksp.instance_of?(String)
    if new_wksp.match(/rallydev.com/)
      @rally_default_workspace = find_workspace(new_wksp, '_ref')
    else
      @rally_default_workspace = find_workspace(new_wksp, 'Name')  
    end
  else
    @rally_default_workspace = new_wksp
  end
  return @rally_default_workspace
end
rank_above(ref_to_rank, relative_ref) click to toggle source

rankAbove=%2Fhierarchicalrequirement%2F4624552599 {“hierarchicalrequirement”:{“_ref”:“rally1.rallydev.com/slm/webservice/v2.0/hierarchicalrequirement/4616818613”}}

# File lib/rally_api/rally_rest_json.rb, line 298
def rank_above(ref_to_rank, relative_ref)
  ref = ref_to_rank
  params = {}
  params[:rankAbove] = short_ref(relative_ref)
  params[:fetch] = "true"
  json_update = { get_type_from_ref(ref_to_rank) => {"_ref" => ref_to_rank} }
  args = { :method => :put, :payload => json_update }
  #update = @rally_connection.put_object(ref, args, params, json_update)
  json_response = send_request(ref, args, params)
  RallyObject.new(self, json_response["OperationResult"]["Object"], warnings(json_response))
end
rank_below(ref_to_rank, relative_ref) click to toggle source

ref to object.js? rankBelow=%2Fhierarchicalrequirement%2F4624552599

# File lib/rally_api/rally_rest_json.rb, line 311
def rank_below(ref_to_rank, relative_ref)
  ref = ref_to_rank
  params = {}
  params[:rankBelow] = short_ref(relative_ref)
  params[:fetch] = "true"
  json_update = { get_type_from_ref(ref_to_rank) => {"_ref" => ref_to_rank} }
  args = { :method => :put, :payload => json_update }
  json_response = send_request(ref, args, params)
  RallyObject.new(self, json_response["OperationResult"]["Object"], warnings(json_response))
end
rank_to(ref_to_rank, location = "TOP") click to toggle source
# File lib/rally_api/rally_rest_json.rb, line 322
def rank_to(ref_to_rank, location = "TOP")
  ref = ref_to_rank
  params = {}
  params[:rankTo] = location
  params[:fetch]  = "true"
  json_update = { get_type_from_ref(ref_to_rank) => {"_ref" => ref_to_rank} }
  args = { :method => :put, :payload => json_update }
  json_response = send_request(ref, args, params)
  RallyObject.new(self, json_response["OperationResult"]["Object"], warnings(json_response))
end
read(type, obj_id, params = {}) click to toggle source
# File lib/rally_api/rally_rest_json.rb, line 194
def read(type, obj_id, params = {})
  type = check_type(type)
  ref = check_id(type.to_s, obj_id)
  params[:workspace] = rally_workspace_object.ref if params[:workspace].nil?
  args = { :method => :get }
  json_response = send_request(ref, args, params)
  rally_type = json_response.keys[0]
  RallyObject.new(self, json_response[rally_type], warnings(json_response))
end
read_collection(collection_hash, params = {}) click to toggle source
# File lib/rally_api/rally_rest_json.rb, line 222
def read_collection(collection_hash, params = {})
  collection_count = collection_hash['Count']
  start = 1
  pagesize = params[:pagesize] || 200
  full_collection = []
  start.step(collection_count, pagesize).each do |page_start|
    page = reread(collection_hash, {:pagesize => 200, :start => page_start})
    full_collection.concat(page["Results"])
  end
  {"Results" => full_collection}
end
reread(json_object, params = {}) click to toggle source
# File lib/rally_api/rally_rest_json.rb, line 211
def reread(json_object, params = {})
  args = { :method => :get }
  if params[:workspace].nil? && (json_object["Workspace"] && json_object["Workspace"]["_ref"])
    params[:workspace] = json_object['Workspace']['_ref']
  end
  #json_response = @rally_connection.read_object(json_object["_ref"], args, params)
  json_response = send_request(json_object["_ref"], args, params)
  rally_type = json_response.keys[0]
  json_response[rally_type]
end
send_request(url = nil, args = nil, params = {}) click to toggle source
# File lib/rally_api/rally_rest_json.rb, line 98
def send_request(url = nil, args = nil, params = {})
  url += '.js' if @wsapi_version !~ /^v2/ && url !~ /\.js$/  # append ".js" if it is needed
  if params[:workspace].nil? && rally_workspace_object
    params[:workspace] = rally_workspace_object['_ref']
  end
  if params[:workspace]
    wksp_id = params[:workspace].match(/workspace\/(\d*)/)[1]
    url += "?workspace=workspace/#{wksp_id}"
  end
  @rally_connection.send_request(url, args, params)
end
update(type, obj_id, fields, params = {}) click to toggle source
# File lib/rally_api/rally_rest_json.rb, line 234
def update(type, obj_id, fields, params = {})
  type = check_type(type)
  ref = check_id(type.to_s, obj_id)
  fields = RallyAPI::RallyRestJson.fix_case(fields) if @rally_rest_api_compat
  update_fields = check_fields(fields)
  update_fields = adjust_for_custom(type.to_s, update_fields) if @wsapi_version =~ /^v2/
  json_update = {type.to_s => update_fields}
  args = {:method => :post, :payload => json_update}
  json_response = send_request(ref, args, params)
  #todo check for warnings on json_response["OperationResult"]
  RallyObject.new(self, reread({"_ref" => ref}), warnings(json_response))
end
Also aliased as: update_ref
update_ref(type, obj_id, fields, params = {})
Alias for: update
user(params = {}) click to toggle source
# File lib/rally_api/rally_rest_json.rb, line 166
def user(params = {})
  args = { :method => :get }
  json_response = send_request(make_get_url("user"), args, params)
  rally_type = json_response.keys[0]
  RallyObject.new(self, json_response[rally_type], warnings(json_response))
end

Private Instance Methods

adjust_for_custom(type, fields) click to toggle source
# File lib/rally_api/rally_rest_json.rb, line 510
def adjust_for_custom(type, fields)
  # only needed for when using WSAPI v2.x or later, custom fields must be prefixed with 'c_'
  begin
    type_custom_fields = custom_fields_for(type)
  rescue => ex
    return fields
  end
  transform = {}
  fields.each do |name, value|
    is_custom = type_custom_fields.select {|fn| fn.downcase == name.downcase}
    if is_custom.length > 0
      transform[is_custom.first.last] = value
    else
      transform[name] = value
    end
  end
  transform
end
check_fields(fields) click to toggle source
# File lib/rally_api/rally_rest_json.rb, line 481
def check_fields(fields)
  fields.each do |key, val|
    fields[key] = make_ref_field(val)

    if val.class == Time
      fields[key] = val.iso8601
    end
  end
  fields
end
check_id(type, idstring) click to toggle source

expecting idstring to have “FormattedID|DE45” or the objectID or a ref itself

# File lib/rally_api/rally_rest_json.rb, line 451
def check_id(type, idstring)
  if idstring.class == Fixnum
    return make_read_url(type, idstring)
  end

  if (idstring.class == String)
    return idstring if idstring.index("http") == 0
    return ref_by_formatted_id(type, idstring.split("|")[1]) if idstring.index("FormattedID") == 0
  end
  make_read_url(type, idstring)
end
check_type(type_name) click to toggle source

check for an alias of a type - eg userstory => hierarchicalrequirement you can add to @rally_alias_types as desired

# File lib/rally_api/rally_rest_json.rb, line 431
def check_type(type_name)
  alias_name = @rally_alias_types[type_name.downcase.to_s]
  return alias_name unless alias_name.nil?
  return type_name
end
get_type_from_ref(ref) click to toggle source

eg rally1.rallydev.com/slm/webservice/v2.0/defect/12345

# File lib/rally_api/rally_rest_json.rb, line 477
def get_type_from_ref(ref)
  ref.split("/")[-2]
end
has_ref?(json_object) click to toggle source

ref should be like rally1.rallydev.com/slm/webservice/v2.0/defect/12345

# File lib/rally_api/rally_rest_json.rb, line 438
def has_ref?(json_object)
  if json_object["_ref"].nil?
    return false
  end
  if @wsapi_version =~ /^v2/
    return true if json_object["_ref"] =~ /^https:\/\/\S*(\/slm\/webservice)\S*$/
  else
    return true if json_object["_ref"] =~ /^https:\/\/\S*(\/slm\/webservice)\S*.js$/
  end
  false
end
make_create_url(type) click to toggle source
# File lib/rally_api/rally_rest_json.rb, line 407
def make_create_url(type)
  "#{@rally_url}/webservice/#{@wsapi_version}/#{type}/create"
end
make_get_url(type) click to toggle source
# File lib/rally_api/rally_rest_json.rb, line 395
def make_get_url(type)
  "#{@rally_url}/webservice/#{@wsapi_version}/#{type}"
end
make_query_url(rally_url, wsapi_version, type) click to toggle source
# File lib/rally_api/rally_rest_json.rb, line 411
def make_query_url(rally_url, wsapi_version, type)
  "#{rally_url}/webservice/#{wsapi_version}/#{type}"
end
make_read_url(type,oid) click to toggle source
# File lib/rally_api/rally_rest_json.rb, line 403
def make_read_url(type,oid)
  "#{@rally_url}/webservice/#{@wsapi_version}/#{type}/#{oid}"
end
make_ref_field(val) click to toggle source
# File lib/rally_api/rally_rest_json.rb, line 492
def make_ref_field(val)
  return val.getref if val.kind_of? RallyObject
  return val if val.kind_of? Hash
  return val if val.is_a?(Hash)

  if val.kind_of? Enumerable
    return val.collect do |collection_element|
      if collection_element.kind_of? RallyObject
        {"_type" => collection_element.type, "_ref" => collection_element.getref}
      else
        collection_element
      end
    end
  end

  val
end
pre_init() click to toggle source
# File lib/rally_api/rally_rest_json.rb, line 390
def pre_init()
  @rally_alias_types      = { "story" => "HierarchicalRequirement", "userstory" => "HierarchicalRequirement" }
  @custom_fields_for_type = {}
end
ref_by_formatted_id(type, fid) click to toggle source
# File lib/rally_api/rally_rest_json.rb, line 463
def ref_by_formatted_id(type, fid)
  query = RallyQuery.new()
  query.type          = type.downcase
  query.query_string  = "(FormattedID = #{fid})"
  query.limit         = 20
  query.fetch         = "FormattedID"
  query.workspace     = rally_workspace_object

  results = find(query)
  return results.first["_ref"] if results.length > 0
  nil
end
security_url() click to toggle source
# File lib/rally_api/rally_rest_json.rb, line 399
def security_url
  "#{@rally_url}/webservice/#{@wsapi_version}/security/authorize"
end
short_ref(long_ref) click to toggle source
# File lib/rally_api/rally_rest_json.rb, line 415
def short_ref(long_ref)
  if @wsapi_version =~ /^v2/
    path_components = long_ref.split("/")
    path_components[-2..-1].join("/")
  else
    ref_pieces = long_ref.split("/")
    "/#{ref_pieces[-2]}/#{ref_pieces[-1].split(".js")[0]}"
  end
end
warnings(json_obj) click to toggle source
# File lib/rally_api/rally_rest_json.rb, line 425
def warnings(json_obj)
  @rally_connection.check_for_errors(json_obj)
end