Class: RestBuilder

Inherits:
Object
  • Object
show all
Defined in:
lib/rally_rest_api/rest_builder.rb

Overview

:nodoc:

Direct Known Subclasses

TimeoutCatchingRestBuilder

Constant Summary collapse

COLLECTION_TYPES =
[:tags, :dependents, :dependencies, :defects, :duplicates, :children, 
:predecessors, :test_cases, :artifacts, :changesets]

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(base_url, username, password, version = "current", http_headers = CustomHttpHeader.new) ⇒ RestBuilder

Returns a new instance of RestBuilder.



12
13
14
# File 'lib/rally_rest_api/rest_builder.rb', line 12

def initialize(base_url, username, password, version = "current", http_headers = CustomHttpHeader.new)
  @base_url, @username, @password, @version, @http_headers = base_url, username, password, version, http_headers
end

Instance Attribute Details

#base_urlObject (readonly)

Returns the value of attribute base_url.



9
10
11
# File 'lib/rally_rest_api/rest_builder.rb', line 9

def base_url
  @base_url
end

#http_headersObject (readonly)

Returns the value of attribute http_headers.



9
10
11
# File 'lib/rally_rest_api/rest_builder.rb', line 9

def http_headers
  @http_headers
end

#loggerObject

Returns the value of attribute logger.



10
11
12
# File 'lib/rally_rest_api/rest_builder.rb', line 10

def logger
  @logger
end

#passwordObject (readonly)

Returns the value of attribute password.



9
10
11
# File 'lib/rally_rest_api/rest_builder.rb', line 9

def password
  @password
end

#usernameObject (readonly)

Returns the value of attribute username.



9
10
11
# File 'lib/rally_rest_api/rest_builder.rb', line 9

def username
  @username
end

Instance Method Details

#builder_block(args) ⇒ Object

Because we are adapting to the xml builder as such:

We say to the RallyRestAPI:
  slm.create(:feature, :name => "feature name")

We tell the xml builder:
  b.feature {
     b.name("feature name")
  }

in the case where one element is a collection, RallyRest would look like

slm.create(:feature, :name => "name", :dependancies => [sr1, sr2])

This needs to be converted to

b.feature {
   b.name("name")
   b.Dependancies {
     b.SupplementalRequirement(:ref => "http://....")
     b.SupplementalRequirement(:ref => "http://....")
   }
}

in this case we need to create a block to handle the nested calls (b.Supp…)

There are also nested/complex values, for example slm.create(:defect, :web_link => => “123”, :description => “foo” )

b.defect {
  b.web_link {
    b.id "123"
    b.description "foo"
  }
}

We need to convert the args hash into a block that can be fed to the builder. This will send the keys of the map as “methods” (b.name) and the values of map as the arguments to the method (“feature name”).

Additionally we camel-case the elements (as JDOM can’t handle elements case free). #convert_arg_for_builder will convert the value portion of the hash to the appropiate string, or block for the xml builder



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/rally_rest_api/rest_builder.rb', line 159

def builder_block(args)
  sort_block = lambda { |a,b| a.to_s <=> b.to_s }  # Sort the keys, for testing
  lambda do |builder|
    args.sort(&sort_block).each do |(attr, value)|
	if COLLECTION_TYPES.include? attr
 # The call to builder with only a type and a block needs to be marked as such
 # note the '&'
 builder.__send__(camel_case_word(attr), &convert_arg_for_builder([value].flatten))
	elsif value.instance_of? Hash
 builder.__send__(camel_case_word(attr), &builder_block(value))
	else
 builder.__send__(camel_case_word(attr), convert_arg_for_builder(value))
	end
    end
  end
end

#camel_case_word(sym) ⇒ Object



114
115
116
# File 'lib/rally_rest_api/rest_builder.rb', line 114

def camel_case_word(sym)
  sym.to_s.split("_").map { |word| word.capitalize }.join
end

#check_for_errors(response) ⇒ Object



102
103
104
105
106
107
108
109
110
111
112
# File 'lib/rally_rest_api/rest_builder.rb', line 102

def check_for_errors(response)
  case response
  when Net::HTTPUnauthorized
    raise Rally::NotAuthenticatedError.new("Invalid Username or Password.")
  else
    s = response.body
    document = REXML::Document.new s
    node = REXML::XPath.first document, '//Errors'
    raise node.to_s if node && node.has_elements?
  end
end

#collection_type?(type) ⇒ Boolean

Returns:

  • (Boolean)


204
205
206
# File 'lib/rally_rest_api/rest_builder.rb', line 204

def collection_type?(type)
  COLLECTION_TYPES.include?(type)
end

#convert_arg_for_builder(value) ⇒ Object

Convert the values of the hash passed to the RestApi appropiatly RestObject –> a hash with the ref value nil –> “null” ref values Time –> iso8601 time strings Arrays of RestObjects –> block that has a nested xml value



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/rally_rest_api/rest_builder.rb', line 182

def convert_arg_for_builder(value)
  case value
  when RestObject
    { :ref => value.ref }
  when NilClass
    { :ref => "null"}
  when Time
    value.iso8601
  when Array
    lambda do |builder|
value.each do |rest_object|
  builder.__send__(rest_object.type, convert_arg_for_builder(rest_object))
end
    end
  else
    value
  end
end

#create_builderObject



96
97
98
99
100
# File 'lib/rally_rest_api/rest_builder.rb', line 96

def create_builder
  b = Builder::XmlMarkup.new
  b.instruct!
  b
end

#create_rest(artifact_type, args, username, password) ⇒ Object

create_rest - convert slm builder style:

slm.create(:feature, :name => "feature name")

Into xml builder style:

b.feature {
   b.name("feature name")
}

then call create on the REST api



32
33
34
35
36
37
38
39
40
41
# File 'lib/rally_rest_api/rest_builder.rb', line 32

def create_rest(artifact_type, args, username, password)
  type = camel_case_word(artifact_type)
  debug "RestBuilder#create_rest artifact_type = #{type}"
  b = create_builder
  xml = b.__send__(type, &builder_block(args))

  result = post_xml("#{self.base_url}/webservice/#{@version}/#{type}/create", xml, username, password)
  doc = REXML::Document.new result
  doc.root.elements["Object"].to_s
end

#debug(message) ⇒ Object



208
209
210
# File 'lib/rally_rest_api/rest_builder.rb', line 208

def debug(message)
  @logger.debug message if @logger
end

#delete_rest(ref_url, username, password) ⇒ Object



66
67
68
69
70
71
# File 'lib/rally_rest_api/rest_builder.rb', line 66

def delete_rest(ref_url, username, password)
  debug  "RestBuilder#delete_rest ref_url = #{ref_url} username = #{ username } pass = #{ password }"
  url = URI.parse(ref_url)
  req = Net::HTTP::Delete.new(url.path)
  send_request(url, req, username, password)
end

#marshal_dumpObject



16
17
18
# File 'lib/rally_rest_api/rest_builder.rb', line 16

def marshal_dump
  [@username, @password, @base_url, @version]
end

#marshal_load(stuff) ⇒ Object



20
21
22
# File 'lib/rally_rest_api/rest_builder.rb', line 20

def marshal_load(stuff)
  @username, @password, @base_url, @version = *stuff
end

#post_xml(url, xml, username, password) ⇒ Object



73
74
75
76
77
78
79
# File 'lib/rally_rest_api/rest_builder.rb', line 73

def post_xml(url, xml, username, password)
  debug  "RestBuilder#post_xml xml = #{xml} as user #{ self.username } pass = #{ self.password }"
  url = URI.parse(url)
  req = Net::HTTP::Post.new(url.path)
  req.body = xml
  send_request(url, req, username, password)
end

#read_rest(ref_url, username, password) ⇒ Object



59
60
61
62
63
64
# File 'lib/rally_rest_api/rest_builder.rb', line 59

def read_rest(ref_url, username, password)
  debug  "RestBuilder#read_rest ref_url = #{ref_url} username = #{ username } pass = #{ password }"
  url = URI.parse(ref_url)
  req = Net::HTTP::Get.new(url.path + (url.query ? "?" + url.query : ""))
  send_request(url, req, username, password)
end

#send_request(url, req, username, password) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/rally_rest_api/rest_builder.rb', line 81

def send_request(url, req, username, password)
  @http_headers.add_headers(req)
  req.basic_auth username, password
  req.content_type = 'text/xml'
  proxy = ENV['http_proxy'] ? URI.parse(ENV['http_proxy']) : OpenStruct.new
  http = Net::HTTP.new(url.host, url.port, proxy.host, proxy.port, proxy.user, proxy.password)
  http.use_ssl = true if url.scheme == "https"
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE
  debug  "RestBuilder#send_request req = #{req.inspect} -- #{url}"
  response = http.start { |http| http.request(req) }
  debug  "RestBuilder#send_request result = #{response.body}"
  check_for_errors(response)
  response.body
end

#update_rest(artifact_type, url, args, username, password) ⇒ Object

update_rest - convert slm builder style:

feature.update(:name => "feature name")

Into xml builder style:

b.feature(:ref => "http://...") {
   b.name("feature name")
}

then call create on the REST api



51
52
53
54
55
56
57
# File 'lib/rally_rest_api/rest_builder.rb', line 51

def update_rest(artifact_type, url, args, username, password)
  debug  "RestBuilder#update_rest url = #{url}"
  b = create_builder
  # and pass that to the builder
  xml = b.__send__(artifact_type, :ref => url, &builder_block(args))
  post_xml(url, xml, username, password)
end