mingle-macro-development-toolkit 1.3.2 → 1.3.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,27 +3,35 @@
3
3
  position: 3
4
4
  color: 'ff3300'
5
5
  id: 339
6
+ project_id: 78
6
7
  - name: Defect
7
8
  position: 5
8
9
  color: '000599'
9
10
  id: 340
11
+ project_id: 78
10
12
  - name: Task
11
13
  position: 4
12
14
  color: 'ffe066'
13
15
  id: 341
16
+ project_id: 78
14
17
  - name: Sprint
15
18
  position: 2
16
19
  color: 'ff0db6'
17
20
  id: 342
21
+ project_id: 78
18
22
  - name: Release
19
23
  position: 1
20
24
  color: '990024'
21
25
  id: 343
26
+ project_id: 78
22
27
  - name: Feature
23
28
  position: 6
24
29
  color: '296600'
25
30
  id: 344
31
+ project_id: 78
26
32
  - name: Epic Story
27
33
  position: 7
28
34
  color: '00d91d'
29
- id: 345
35
+ id: 345
36
+ project_id: 78
37
+
@@ -1,5 +1,7 @@
1
1
  ---
2
2
  - name: Current Release
3
3
  display_value: 'Release 3'
4
+ project_id: 78
4
5
  - name: Current Sprint
5
- display_value: 'Iteration 7'
6
+ display_value: 'Iteration 7'
7
+ project_id: 78
@@ -3,39 +3,49 @@
3
3
  name: Status
4
4
  description: ""
5
5
  type_description: Managed text list
6
+ project_id: 78
6
7
  - id: 1141
7
8
  name: Priority
8
9
  description: ""
9
10
  type_description: Managed text list
11
+ project_id: 78
10
12
  - id: 1142
11
13
  name: Development Started On
12
14
  description: ""
13
15
  type_description: Managed text list
16
+ project_id: 78
14
17
  - id: 1143
15
18
  description: ""
16
19
  name: Development Completed On
17
20
  type_description: Date
21
+ project_id: 78
18
22
  - id: 1144
19
23
  description: ""
20
24
  name: Added On
21
25
  type_description: Date
26
+ project_id: 78
22
27
  - id: 1145
23
28
  description: ""
24
29
  name: Estimate - Planning
25
- type_description: Formula
30
+ type_description: Formula
31
+ project_id: 78
26
32
  - id: 1146
27
33
  description: ""
28
34
  name: Risk
29
- type_description: Managed text list
35
+ type_description: Managed text list
36
+ project_id: 78
30
37
  - id: 1147
31
38
  description: ""
32
39
  name: Task Estimate
33
40
  type_description: Managed text list
41
+ project_id: 78
34
42
  - id: 1148
35
43
  description:
36
44
  name: Estimate - Task
37
45
  type_description: Managed text list
46
+ project_id: 78
38
47
  - id: 1149
39
48
  description: Individual assigned to a work item
40
49
  name: Owner
41
- type_description: User
50
+ type_description: User
51
+ project_id: 78
@@ -2,20 +2,32 @@
2
2
 
3
3
  require 'delegate'
4
4
  require 'test/unit'
5
+ require 'net/http'
5
6
  require 'macro_development_toolkit'
6
7
  require File.dirname(__FILE__) + '/rest_loader'
7
8
 
8
9
  class Test::Unit::TestCase
9
-
10
- def project(name)
11
- @project ||= RESTfulLoaders::ProjectLoader.new(name, nil, self).project
12
- end
13
-
10
+
11
+ def project(resource)
12
+ @projects ||= {}
13
+ @projects[resource] ||= load_project_resource(resource)
14
+ end
15
+
16
+ def projects(*resources)
17
+ resources.collect {|resource| project(resource)}
18
+ end
19
+
14
20
  def errors
15
21
  @errors ||= []
16
- end
22
+ end
17
23
 
18
24
  def alert(message)
19
25
  errors << message
20
- end
21
- end
26
+ end
27
+
28
+ private
29
+
30
+ def load_project_resource(resource)
31
+ RESTfulLoaders::ProjectLoader.new(resource, self).project
32
+ end
33
+ end
@@ -1,311 +1,233 @@
1
1
  #Copyright 2010 ThoughtWorks, Inc. All rights reserved.
2
2
 
3
3
  module RESTfulLoaders
4
+
4
5
  class RemoteError < StandardError
5
6
  def self.parse(response_body)
6
7
  Hash.from_xml(response_body)['errors'].delete("error")
7
- end
8
- end
9
-
10
- class Base
11
- def initialize(project_name)
12
- @xml_model = if (String === project_name)
13
- @project_name = project_name
14
- OpenStruct.new(Hash.from_xml(get(project_name)))
15
- else
16
- project_name
17
- end
18
- raise "No such project resource available! #{@project_name}" unless (@xml_model && @xml_model.to_s != '')
19
8
  end
9
+ end
20
10
 
21
- def get(url)
22
- url = URI.parse(url) unless url.respond_to?(:path)
11
+ module LoaderHelper
12
+
13
+ def extract(key, container)
14
+ container[key] ? container[key][key.singularize] : []
15
+ end
16
+
17
+ def get(resource)
18
+ url = URI.parse(resource)
23
19
  get_request = Net::HTTP::Get.new(url.request_uri)
24
20
  get_request.basic_auth(url.user, url.password)
25
21
  response = Net::HTTP.start(url.host, url.port) { |http| http.request(get_request) }
26
- if response.code.to_s != "200"
22
+ if response.code.to_s != "200"
27
23
  raise RemoteError, RemoteError.parse(response.body)
28
24
  end
29
- response.body
30
- end
31
-
32
- def load_all_card_types_from_xml
33
- extract_array_of 'card_types', :from => @xml_model.project
34
- end
35
-
36
- def load_all_property_definitions_from_xml
37
- extract_array_of 'property_definitions', :from => @xml_model.project
38
- end
39
-
40
- def load_all_card_types_property_definitions_from_xml
41
- load_all_card_types_from_xml.collect do |card_type_xml|
42
- card_types_property_definitions = OpenStruct.new(card_type_xml).card_types_property_definitions
43
- [card_types_property_definitions['card_type_property_definition']].flatten if card_types_property_definitions
44
- end.compact.flatten.compact
45
- end
46
-
47
- def card_types_property_definitions_by_card_type_id_loader(card_type_id)
48
- LoadCardTypesPropertyDefinitionsByCardTypeId.new(card_type_id, @xml_model)
49
- end
50
-
51
- def card_types_property_definitions_by_property_definition_id_loader(property_definition_id)
52
- LoadCardTypesPropertyDefinitionsByPropertyDefinitionId.new(property_definition_id, @xml_model)
25
+ Hash.from_xml(response.body)
53
26
  end
27
+ end
54
28
 
55
- def values_by_property_definition_id_loader(property_definition_id)
56
- LoadValuesByPropertyDefinitionId.new(property_definition_id, @xml_model)
57
- end
29
+ class MqlExecutor < SimpleDelegator
30
+ include LoaderHelper
58
31
 
59
- def card_type_by_id_loader(card_type_id)
60
- LoadCardTypeById.new(card_type_id, @xml_model)
32
+ def initialize(resource, error_handler, delegator)
33
+ super(delegator)
34
+ @uri = URI.parse(resource)
35
+ @error_handler = error_handler
36
+ @version = /(\/api\/([^\/]*))\//.match(@uri.request_uri)[2]
61
37
  end
62
-
63
- def property_definition_by_id_loader(property_definition_id)
64
- LoadPropertyDefinitionById.new(property_definition_id, @xml_model)
65
- end
66
-
67
- def card_types_by_project_id_loader
68
- LoadCardTypesByProjectId.new(@xml_model)
69
- end
70
38
 
71
- def property_definitions_by_project_id_loader
72
- LoadPropertyDefinitionsByProjectId.new(@xml_model)
73
- end
39
+ def execute_mql(mql)
40
+ from_xml_data(get(url_for(:action => "execute_mql", :query => "mql=#{mql}")))
41
+ rescue => e
42
+ @error_handler.alert(e.message)
43
+ []
44
+ end
74
45
 
75
- def team_by_project_id_loader
76
- LoadTeamByProjectId.new(@xml_model)
46
+ def can_be_cached?(mql)
47
+ from_xml_data(get(url_for(:action => "can_be_cached", :query => "mql=#{mql}")))
48
+ rescue => e
49
+ @error_handler.alert(e.message)
50
+ []
77
51
  end
78
52
 
79
- def project_variables_by_project_id_loader
80
- LoadProjectVariablesByProjectId.new(@xml_model)
53
+ def format_number_with_project_precision(number)
54
+ from_xml_data(get(url_for(:action => "format_number_to_project_precision", :query => "number=#{number}")))
55
+ rescue => e
56
+ @error_handler.alert(e.message)
57
+ []
81
58
  end
82
59
 
83
- def extract_array_of(container_key, options)
84
- contents_hash = options[:from]
85
- container = contents_hash[container_key]
86
- container ? [container[container_key.singularize]].flatten.compact : []
87
- end
88
- end
60
+ def format_date_with_project_date_format(date)
61
+ from_xml_data(get(url_for(:action => "format_string_to_date_format", :query => "date=#{date}")))
62
+ rescue => e
63
+ @error_handler.alert(e.message)
64
+ []
65
+ end
89
66
 
90
- class ProjectLoader < Base
91
- class MqlExecutionDelegate < SimpleDelegator
92
- def initialize(delegate, mql_executor)
93
- @mql_executor = mql_executor
94
- __setobj__(delegate)
95
- end
96
-
97
- def execute_mql(mql)
98
- @mql_executor.execute_mql(mql, self)
99
- rescue => e
100
- __getobj__.send(:add_alert, e.message)
101
- []
102
- end
103
-
104
- def can_be_cached?(mql)
105
- @mql_executor.can_be_cached?(mql, self)
106
- rescue => e
107
- __getobj__.send(:add_alert, e.message)
108
- []
109
- end
110
-
111
- def format_number_with_project_precision(number)
112
- @mql_executor.format_number_with_project_precision(number, self)
113
- rescue => e
114
- __getobj__.send(:add_alert, e.message)
115
- end
116
-
117
- def format_date_with_project_date_format(date)
118
- @mql_executor.format_date_with_project_date_format(date, self)
119
- rescue => e
120
- __getobj__.send(:add_alert, e.message)
67
+ def url_for(params)
68
+ relative_path = URI.escape("/api/#{@version}/projects/#{identifier}/cards/#{params[:action]}.xml?#{params[:query]}")
69
+ @uri.merge(relative_path).to_s
70
+ end
71
+
72
+ def from_xml_data(data)
73
+ if data.is_a?(Hash) && data.keys.size == 1
74
+ from_xml_data(data.values.first)
75
+ else
76
+ data
121
77
  end
122
- end
78
+ end
79
+ end
123
80
 
124
- def initialize(project_name, macro_context=nil, alert_receiver=nil)
125
- super(project_name)
126
- project_name =~ /(\/api\/([^\/]*))\//
127
- @api_version_name = $1
128
- @macro_context = macro_context
129
- @alert_receiver = alert_receiver
81
+ class ProjectLoader
82
+ include LoaderHelper
83
+
84
+ def initialize(resource, error_handler)
85
+ @resource = resource
86
+ @error_handler = error_handler
130
87
  end
131
-
132
- def load_project_from_xml
133
- @xml_model.project
134
- end
135
88
 
136
89
  def project
137
- project = MqlExecutionDelegate.new(Mingle::Project.new(OpenStruct.new(load_project_from_xml), :alert_receiver => @alert_receiver), self)
138
- project.card_types_loader = card_types_by_project_id_loader
139
- project.property_definitions_loader = property_definitions_by_project_id_loader
140
- project.team_loader = team_by_project_id_loader
141
- project.project_variables_loader = project_variables_by_project_id_loader
142
- project
90
+ @project ||= load
143
91
  end
144
92
 
145
- def execute_mql(mql, project)
146
- from_xml_data(
147
- Hash.from_xml(
148
- get(
149
- build_request_url(:path => url_for("execute_mql"), :query => "mql=#{mql}"))))
93
+ private
94
+
95
+ def load
96
+ proj = OpenStruct.new(get(@resource)).project
97
+ project = MqlExecutor.new(@resource, @error_handler, Mingle::Project.new(OpenStruct.new(proj), nil))
98
+ project.card_types_loader = CardTypesLoader.new(proj)
99
+ project.property_definitions_loader = PropertyDefinitionsLoader.new(proj)
100
+ project.team_loader = TeamLoader.new(proj)
101
+ project.project_variables_loader = ProjectVariablesLoader.new(proj)
102
+ project
150
103
  end
104
+
105
+ end
106
+
107
+ class CardTypesLoader
108
+ include LoaderHelper
151
109
 
152
- def can_be_cached?(mql, project)
153
- from_xml_data(
154
- Hash.from_xml(
155
- get(
156
- build_request_url(:path => url_for("can_be_cached"), :query => "mql=#{mql}"))))
110
+ def initialize(project)
111
+ @project = project
157
112
  end
158
113
 
159
- def format_number_with_project_precision(number, project)
160
- from_xml_data(
161
- Hash.from_xml(
162
- get(
163
- build_request_url(:path => url_for("format_number_to_project_precision"), :query => "number=#{number}"))))
114
+ def load
115
+ extract('card_types', @project).collect { |ct| CardTypeLoader.new(@project, ct) }.sort_by { |loader| loader.card_type.position }
164
116
  end
117
+ end
118
+
119
+ class CardTypeLoader
165
120
 
166
- def format_date_with_project_date_format(date, project)
167
- from_xml_data(
168
- Hash.from_xml(
169
- get(
170
- build_request_url(:path => url_for("format_string_to_date_format"), :query => "date=#{date}"))))
121
+ def initialize(project, ct)
122
+ @project = project
123
+ @ct = ct
171
124
  end
172
125
 
173
- def url_for(action)
174
- "#{@api_version_name}/projects/#{project.identifier}/cards/#{action}.xml"
126
+ def card_type
127
+ @card_type ||= load
175
128
  end
176
129
 
177
- def build_request_url(params)
178
- url = URI.parse(@project_name)
179
- request_class = if url.scheme == 'http'
180
- URI::HTTP
181
- elsif
182
- URI::HTTPS
183
- else
184
- raise "Unknown protocol used to access project resource #{@project_name}. Supported protocols are HTTP & HTTPS"
185
- end
186
- default_params = {:userinfo => "#{url.user}:#{url.password}", :host => url.host, :port => url.port.to_s}
187
- request_class.build2(default_params.merge(params))
188
- end
189
-
190
- def from_xml_data(data)
191
- if data.is_a?(Hash) && data.keys.size == 1
192
- from_xml_data(data.values.first)
193
- else
194
- data
195
- end
196
- end
197
- end
198
-
199
- class LoadCardTypesByProjectId < Base
200
130
  def load
201
- load_all_card_types_from_xml.collect do |ct|
202
- card_type = Mingle::CardType.new(OpenStruct.new(ct))
203
- card_type.card_types_property_definitions_loader = card_types_property_definitions_by_card_type_id_loader(ct['id'])
204
- card_type
205
- end.sort_by(&:position)
206
- end
131
+ card_type = Mingle::CardType.new(OpenStruct.new(@ct))
132
+ card_type.card_types_property_definitions_loader = CardTypesPropertyDefinitionsLoader.new(@project, 'card_type_id' => @ct['id'])
133
+ card_type
134
+ end
207
135
  end
208
136
 
209
- class LoadPropertyDefinitionsByProjectId < Base
137
+ class PropertyDefinitionsLoader
138
+ include LoaderHelper
139
+
140
+ def initialize(project)
141
+ @project = project
142
+ end
143
+
210
144
  def load
211
- load_all_property_definitions_from_xml.collect do |pd|
212
- property_definition = Mingle::PropertyDefinition.new(OpenStruct.new(pd))
213
- property_definition.card_types_property_definitions_loader = card_types_property_definitions_by_property_definition_id_loader(pd['id'])
214
- property_definition.values_loader = values_by_property_definition_id_loader(pd['id'])
215
- property_definition
216
- end
217
- end
145
+ extract('property_definitions', @project).collect { |pd| PropertyDefinitionLoader.new(@project, pd) }
146
+ end
218
147
  end
219
-
220
- class LoadCardTypesPropertyDefinitionsByCardTypeId < Base
221
- def initialize(card_type_id, fixture_file_name)
222
- super(fixture_file_name)
223
- @card_type_id = card_type_id
224
- end
225
148
 
149
+ class PropertyDefinitionLoader
150
+ def initialize(project, pd)
151
+ @project = project
152
+ @pd = pd
153
+ end
154
+
155
+ def property_definition
156
+ @property_definition ||= load
157
+ end
158
+
226
159
  def load
227
- load_all_card_types_property_definitions_from_xml.collect do |ctpd|
228
- next unless ctpd['card_type_id'] == @card_type_id
229
- card_type_property_definition = Mingle::CardTypePropertyDefinition.new(OpenStruct.new(ctpd))
230
- card_type_property_definition.card_type_loader = card_type_by_id_loader(ctpd['card_type_id'])
231
- card_type_property_definition.property_definition_loader = property_definition_by_id_loader(ctpd['property_definition_id'])
232
- card_type_property_definition
233
- end.compact.sort_by(&:position).compact
234
- end
235
- end
236
-
237
- class LoadCardTypesPropertyDefinitionsByPropertyDefinitionId < Base
238
- def initialize(property_definition_id, fixture_file_name)
239
- super(fixture_file_name)
240
- @property_definition_id = property_definition_id
241
- end
242
-
243
- def load
244
- load_all_card_types_property_definitions_from_xml.collect do |ctpd|
245
- next unless ctpd['property_definition_id'] == @property_definition_id
246
- card_type_property_definition = Mingle::CardTypePropertyDefinition.new(OpenStruct.new(ctpd))
247
- card_type_property_definition.card_type_loader = card_type_by_id_loader(ctpd['card_type_id'])
248
- card_type_property_definition.property_definition_loader = property_definition_by_id_loader(ctpd['property_definition_id'])
249
- card_type_property_definition
250
- end.compact.sort_by(&:position).compact
251
- end
160
+ @property_definition = Mingle::PropertyDefinition.new(OpenStruct.new(@pd))
161
+ @property_definition.card_types_property_definitions_loader = CardTypesPropertyDefinitionsLoader.new(@project, 'property_definition_id' => @pd['id'])
162
+ @property_definition.values_loader = PropertyValuesLoader.new(@pd)
163
+ @property_definition
164
+ end
252
165
  end
253
-
254
- class LoadCardTypeById < Base
255
- def initialize(card_type_id, fixture_file_name)
256
- super(fixture_file_name)
257
- @card_type_id = card_type_id
258
- end
259
-
260
- def load
261
- yaml = load_all_card_types_from_xml.detect { |ct| ct['id'] == @card_type_id }
262
- ct = Mingle::CardType.new(OpenStruct.new(yaml))
263
- ct.card_types_property_definitions_loader = card_types_property_definitions_by_card_type_id_loader(yaml['id'])
264
- ct
265
- end
266
- end
267
-
268
- class LoadPropertyDefinitionById < Base
269
- def initialize(property_definition_id, fixture_file_name)
270
- super(fixture_file_name)
271
- @property_definition_id = property_definition_id
166
+
167
+ class PropertyValuesLoader
168
+ include LoaderHelper
169
+
170
+ def initialize(property_definition)
171
+ @property_definition = property_definition
272
172
  end
273
-
173
+
274
174
  def load
275
- yaml = load_all_property_definitions_from_xml.detect { |pd| pd['id'] == @property_definition_id }
276
- pd = Mingle::PropertyDefinition.new(OpenStruct.new(yaml))
277
- pd.card_types_property_definitions_loader = card_types_property_definitions_by_property_definition_id_loader(yaml['id'])
278
- pd.values_loader = values_by_property_definition_id_loader(yaml['id'])
279
- pd
280
- end
281
- end
282
-
283
- class LoadValuesByPropertyDefinitionId < Base
284
- def initialize(property_definition_id, fixture_file_name)
285
- super(fixture_file_name)
286
- @property_definition_id = property_definition_id
175
+ extract('values', @property_definition).collect {|value| Mingle::PropertyValue.new(OpenStruct.new(value))}
287
176
  end
177
+
178
+ end
288
179
 
180
+ class CardTypesPropertyDefinitionsLoader
181
+ include LoaderHelper
182
+
183
+ def initialize(project, params)
184
+ @project = project
185
+ @params = params
186
+ end
187
+
289
188
  def load
290
- property_definition = load_all_property_definitions_from_xml.detect { |property_definition_xml| property_definition_xml['id'] == @property_definition_id }
291
- return unless property_definition
292
- extract_array_of('values', :from => property_definition).collect do |property_value_xml|
293
- property_value = Mingle::PropertyValue.new(OpenStruct.new(property_value_xml))
294
- property_value.property_definition_loader = property_definition_by_id_loader(property_value_xml['property_definition_id'])
295
- property_value
189
+ mappings = extract('card_types', @project).collect do |card_type|
190
+ mapping = card_type['card_types_property_definitions'].values
191
+ end.flatten
192
+
193
+ pds = extract('property_definitions', @project)
194
+ cts = extract('card_types', @project)
195
+ mappings.collect do |mapping|
196
+ if (match?(mapping))
197
+ pd = pds.find { |pd| pd['id'] && pd['id'] == mapping['property_definition_id'] }
198
+ ct = cts.find { |ct| ct['id'] == mapping['card_type_id'] }
199
+ OpenStruct.new(:card_type => CardTypeLoader.new(@project, ct).load, :property_definition => PropertyDefinitionLoader.new(@project, pd).load)
200
+ end
296
201
  end.compact
297
- end
202
+ end
203
+
204
+ private
205
+ def match?(mapping)
206
+ @params.all? { |key, value| value == mapping[key] }
207
+ end
298
208
  end
299
209
 
300
- class LoadTeamByProjectId < Base
210
+ class TeamLoader
211
+ include LoaderHelper
212
+
213
+ def initialize(project)
214
+ @project = project
215
+ end
216
+
301
217
  def load
302
- extract_array_of('users', :from => @xml_model.project).collect { |user| Mingle::User.new(OpenStruct.new(user)) }
303
- end
218
+ (extract('users', @project) || []).collect{ |user| Mingle::User(OpenStruct.new(user))}
219
+ end
304
220
  end
305
-
306
- class LoadProjectVariablesByProjectId < Base
221
+
222
+ class ProjectVariablesLoader
223
+ include LoaderHelper
224
+
225
+ def initialize(project)
226
+ @project = project
227
+ end
228
+
307
229
  def load
308
- extract_array_of('project_variables', :from => @xml_model.project).collect { |pv| Mingle::ProjectVariable.new(OpenStruct.new(pv)) }
309
- end
230
+ extract('project_variables', @project).collect {|pv| Mingle::ProjectVariable.new(OpenStruct.new(pv))}
231
+ end
310
232
  end
311
- end
233
+ end