mingle-macro-development-toolkit 1.3.2 → 1.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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