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.



64
65
66
67
# File 'lib/rally_rest_api/rest_builder.rb', line 64

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
  @cookie_jar = {}
end

Instance Attribute Details

#base_urlObject (readonly)

Returns the value of attribute base_url.



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

def base_url
  @base_url
end

#http_headersObject (readonly)

Returns the value of attribute http_headers.



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

def http_headers
  @http_headers
end

#loggerObject

Returns the value of attribute logger.



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

def logger
  @logger
end

#passwordObject (readonly)

Returns the value of attribute password.



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

def password
  @password
end

#use_cookiesObject

Returns the value of attribute use_cookies.



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

def use_cookies
  @use_cookies
end

#usernameObject (readonly)

Returns the value of attribute username.



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

def username
  @username
end

Instance Method Details

#add_cookies(req) ⇒ Object



134
135
136
137
# File 'lib/rally_rest_api/rest_builder.rb', line 134

def add_cookies(req)
  req.add_field('cookie', format_cookie(@cookie_jar))
  req
end

#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



255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
# File 'lib/rally_rest_api/rest_builder.rb', line 255

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



210
211
212
# File 'lib/rally_rest_api/rest_builder.rb', line 210

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

#check_for_errors(response) ⇒ Object



197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/rally_rest_api/rest_builder.rb', line 197

def check_for_errors(response)
  case response
  when Net::HTTPUnauthorized
    raise Rally::NotAuthenticatedError.new("Invalid Username or Password.")
  else
    #s = response.body
    s = response.plain_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)


300
301
302
# File 'lib/rally_rest_api/rest_builder.rb', line 300

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



278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
# File 'lib/rally_rest_api/rest_builder.rb', line 278

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



191
192
193
194
195
# File 'lib/rally_rest_api/rest_builder.rb', line 191

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



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

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



304
305
306
# File 'lib/rally_rest_api/rest_builder.rb', line 304

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

#delete_rest(ref_url, username, password) ⇒ Object



119
120
121
122
123
124
# File 'lib/rally_rest_api/rest_builder.rb', line 119

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


151
152
153
154
155
156
157
158
# File 'lib/rally_rest_api/rest_builder.rb', line 151

def format_cookie (cookie_jar)
  cookie_str = ""
  cookie_jar.each do |cookie_name, cookie_value|
    cookie_str += "; " unless cookie_str.empty?
    cookie_str += "#{cookie_name}=#{cookie_value}"
  end
  cookie_str
end

#marshal_dumpObject



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

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

#marshal_load(stuff) ⇒ Object



73
74
75
# File 'lib/rally_rest_api/rest_builder.rb', line 73

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

#parse_cookies(cookie_str) ⇒ Object



139
140
141
142
143
144
145
146
147
148
149
# File 'lib/rally_rest_api/rest_builder.rb', line 139

def parse_cookies (cookie_str)
  cookies = cookie_str.split(', ')
  cookies.collect! { |cookie| cookie.split(";")[0] }

  cookie_jar = {}
  cookies.each do |cookie|
    c = cookie.split("=")
    cookie_jar[c[0]] = c[1] || ""
  end
  cookie_jar
end

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



126
127
128
129
130
131
132
# File 'lib/rally_rest_api/rest_builder.rb', line 126

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



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

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



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/rally_rest_api/rest_builder.rb', line 165

def send_request(url, req, username, password)
  @http_headers.add_headers(req)
  req.basic_auth username, password
  req.content_type = 'text/xml'
  req.add_field('Accept-Encoding', 'gzip, deflate')
  add_cookies(req) if @use_cookies
  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
  #http.ssl_version = "SSLv3"
  http.read_timeout = 300
  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
  debug  "RestBuilder#send_request result = #{response.plain_body}"
  headerstr = ""
  response.each_header { |k,v| headerstr << "#{k}--#{v}|" }
  store_cookies(response) if @use_cookies
  debug  "RestBuilder#send_request result headers = #{headerstr} and length is #{response.plain_body.length}"
  check_for_errors(response)
  response.plain_body
end

#store_cookies(response) ⇒ Object



160
161
162
163
# File 'lib/rally_rest_api/rest_builder.rb', line 160

def store_cookies(response)
  @cookie_jar.merge!(parse_cookies(response.response['set-cookie']))
  response
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



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

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