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
# 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
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

#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

#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



220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
# File 'lib/rally_rest_api/rest_builder.rb', line 220

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



175
176
177
# File 'lib/rally_rest_api/rest_builder.rb', line 175

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

#check_for_errors(response) ⇒ Object



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

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)


265
266
267
# File 'lib/rally_rest_api/rest_builder.rb', line 265

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



243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
# File 'lib/rally_rest_api/rest_builder.rb', line 243

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



156
157
158
159
160
# File 'lib/rally_rest_api/rest_builder.rb', line 156

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



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

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



269
270
271
# File 'lib/rally_rest_api/rest_builder.rb', line 269

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

#delete_rest(ref_url, username, password) ⇒ Object



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

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



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

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

#marshal_load(stuff) ⇒ Object



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

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

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



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

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



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

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



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/rally_rest_api/rest_builder.rb', line 133

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')
  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.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}|" }
  debug  "RestBuilder#send_request result headers = #{headerstr} and length is #{response.plain_body.length}"
  check_for_errors(response)
  response.plain_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



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

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