class RallyAPI::RallyRestJson
Main Class to instantiate when using the tool
Constants
- DEFAULT_WSAPI_VERSION
Attributes
Public Class Methods
# 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
# 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
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
# 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
# File lib/rally_api/rally_rest_json.rb, line 292 def adjust_find_threads(num_threads) @rally_connection.set_find_threads(num_threads) end
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
# 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
# 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
# File lib/rally_api/rally_rest_json.rb, line 130 def debug_logging_off @low_debug = false @rally_connection.low_debug = false end
# File lib/rally_api/rally_rest_json.rb, line 125 def debug_logging_on @low_debug = true @rally_connection.low_debug = true end
# 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
-----
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
# 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
# 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
# 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
# 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
# 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
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
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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
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 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
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
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
# File lib/rally_api/rally_rest_json.rb, line 407 def make_create_url(type) "#{@rally_url}/webservice/#{@wsapi_version}/#{type}/create" end
# File lib/rally_api/rally_rest_json.rb, line 395 def make_get_url(type) "#{@rally_url}/webservice/#{@wsapi_version}/#{type}" end
# 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
# File lib/rally_api/rally_rest_json.rb, line 403 def make_read_url(type,oid) "#{@rally_url}/webservice/#{@wsapi_version}/#{type}/#{oid}" end
# 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
# 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
# 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
# File lib/rally_api/rally_rest_json.rb, line 399 def security_url "#{@rally_url}/webservice/#{@wsapi_version}/security/authorize" end
# 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
# File lib/rally_api/rally_rest_json.rb, line 425 def warnings(json_obj) @rally_connection.check_for_errors(json_obj) end