apadmi_grout 1.0.0 → 2.0.0

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.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -1
  3. data/lib/apadmi/grout/actions/find_tickets_to_move_action/find_tickets_to_move_action.rb +24 -0
  4. data/lib/apadmi/grout/actions/find_tickets_to_move_action/find_tickets_to_move_ado_action.rb +91 -0
  5. data/lib/apadmi/grout/actions/find_tickets_to_move_action/find_tickets_to_move_jira_action.rb +114 -0
  6. data/lib/apadmi/grout/actions/generate_release_notes_action/generate_release_notes_action.rb +48 -0
  7. data/lib/apadmi/grout/actions/generate_release_notes_action/issue_classifier.rb +51 -0
  8. data/lib/apadmi/grout/{release_notes/actions → actions}/issues_from_changelog_action.rb +11 -8
  9. data/lib/apadmi/grout/actions/move_tickets_action.rb +34 -0
  10. data/lib/apadmi/grout/di.rb +54 -9
  11. data/lib/apadmi/grout/models/ado_config.rb +10 -0
  12. data/lib/apadmi/grout/models/find_tickets_options.rb +44 -0
  13. data/lib/apadmi/grout/models/flag_messages.rb +25 -0
  14. data/lib/apadmi/grout/models/issue.rb +49 -0
  15. data/lib/apadmi/grout/{jira/models → models}/pull_request.rb +0 -0
  16. data/lib/apadmi/grout/models/release_notes_config.rb +47 -0
  17. data/lib/apadmi/grout/{release_notes/models → models}/release_notes_templates.rb +6 -6
  18. data/lib/apadmi/grout/service/board_service/ado_board_service.rb +199 -0
  19. data/lib/apadmi/grout/service/board_service/board_service.rb +59 -0
  20. data/lib/apadmi/grout/{jira/wrapper/jira_wrapper.rb → service/board_service/jira_board_service.rb} +68 -107
  21. data/lib/apadmi/grout/utils/filename_utils.rb +40 -0
  22. data/lib/apadmi/grout/utils/git_utils.rb +57 -0
  23. data/lib/apadmi/grout/utils/network_service.rb +88 -0
  24. data/lib/apadmi/grout/version.rb +1 -1
  25. data/lib/apadmi_grout.rb +3 -18
  26. metadata +23 -13
  27. data/lib/apadmi/grout/jira/actions/find_tickets_to_move_action.rb +0 -76
  28. data/lib/apadmi/grout/jira/actions/move_jira_tickets_action.rb +0 -58
  29. data/lib/apadmi/grout/jira/models/flag_messages.rb +0 -8
  30. data/lib/apadmi/grout/jira/models/version.rb +0 -23
  31. data/lib/apadmi/grout/release_notes/actions/generate_release_notes_action.rb +0 -39
  32. data/lib/apadmi/grout/release_notes/models/release_notes_config.rb +0 -74
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apadmi
4
+ module Grout
5
+ # @param tasks [Array<Apadmi::Grout::Issue>]
6
+ # @param features [Array<Apadmi::Grout::Issue>] aka stories
7
+ # @param improvements [Array<Apadmi::Grout::Issue>]
8
+ # @param defects [Array<Apadmi::Grout::Issue>] aka bugs
9
+ # @param others [Array<Apadmi::Grout::Issue>] any other non-standard issue types
10
+ ClassifiedIssues = Struct.new(
11
+ :tasks,
12
+ :features,
13
+ :improvements,
14
+ :defects,
15
+ :others
16
+ ) do
17
+ # @return returns true if all categories are empty
18
+ def empty
19
+ tasks.empty? && features.empty? &&
20
+ improvements.empty? && defects.empty? && others.empty?
21
+ end
22
+ end
23
+
24
+ # @param title [String] The title of the document
25
+ # @param app_version [String] The app version pertaining to this release
26
+ # @param min_os_version [String] The min supported os version of this release
27
+ # @param date [String] Today's date
28
+ # @param moved_issues [Array<Apadmi::Grout::Issue>] Issues moved to a new state by this build job
29
+ # @param release_issues [Array<Apadmi::Grout::Issue>] Issues considered part of this release
30
+ # @param commit_hash [String] Commit hash from which release was built
31
+ # @param ci_build_number [String] CI build number which built the release
32
+ # @param ci_build_url [String] Link to CI build job
33
+ # @param templates [Apadmi::Grout::Templates] Mustache templates to use to generate the document
34
+ ReleaseNotesConfig = Struct.new(
35
+ :title,
36
+ :app_version,
37
+ :min_os_version,
38
+ :date,
39
+ :moved_issues,
40
+ :release_issues,
41
+ :commit_hash,
42
+ :ci_build_number,
43
+ :ci_build_url,
44
+ :templates
45
+ )
46
+ end
47
+ end
@@ -9,7 +9,7 @@ module Apadmi
9
9
 
10
10
  ### Version
11
11
  {{config.app_version}}
12
- {{config.hello}}
12
+
13
13
  ### Release date
14
14
  {{config.date}}
15
15
 
@@ -30,35 +30,35 @@ Commit Hash: {{config.commit_hash}}
30
30
  %{### New functionality
31
31
  {{# classified_issues.features.first}}
32
32
  {{# classified_issues.features}}
33
- * [{{ key }} - {{ summary }}]({{ base_url }}{{ key }})
33
+ * [{{ key }} - {{ summary }}]({{ url }})
34
34
  {{/ classified_issues.features}}
35
35
 
36
36
  {{/ classified_issues.features.first}}
37
37
  {{# classified_issues.improvements.first}}
38
38
  ### Improvements
39
39
  {{# classified_issues.improvements}}
40
- * [{{ key }} - {{ summary }}]({{ base_url }}{{ key }})
40
+ * [{{ key }} - {{ summary }}]({{ url }})
41
41
  {{/ classified_issues.improvements}}
42
42
 
43
43
  {{/ classified_issues.improvements.first}}
44
44
  {{# classified_issues.tasks.first}}
45
45
  ### Completed Tasks
46
46
  {{# classified_issues.tasks}}
47
- * [{{ key }} - {{ summary }}]({{ base_url }}{{ key }})
47
+ * [{{ key }} - {{ summary }}]({{ url }})
48
48
  {{/ classified_issues.tasks}}
49
49
 
50
50
  {{/ classified_issues.tasks.first}}
51
51
  {{# classified_issues.defects.first}}
52
52
  ### Fixed defects
53
53
  {{# classified_issues.defects}}
54
- * [{{ key }} - {{ summary }}]({{ base_url }}{{ key }})
54
+ * [{{ key }} - {{ summary }}]({{ url }})
55
55
  {{/ classified_issues.defects}}
56
56
 
57
57
  {{/ classified_issues.defects.first}}
58
58
  {{# classified_issues.others.first}}
59
59
  ### Other issue types
60
60
  {{# classified_issues.others}}
61
- * [{{ key }} - {{ summary }}]({{ base_url }}{{ key }})
61
+ * [{{issue_type}} - {{ key }} - {{ summary }}]({{ url }})
62
62
  {{/ classified_issues.others}}
63
63
 
64
64
  {{/ classified_issues.others.first}}}
@@ -0,0 +1,199 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./board_service"
4
+
5
+ module Apadmi
6
+ module Grout
7
+ # Provides a layer of abstraction on top of the ADO api
8
+ class AdoBoardService < BoardService
9
+ API_VERSION_PARAM = "api-version=6.0"
10
+
11
+ # @param [Apadmi::Grout::NetworkService] network_service
12
+ # @param [Apadmi::Grout::AdoConfig] ado_config
13
+ # @param [Logger] logger
14
+ def initialize(network_service, ado_config, logger)
15
+ @network_service = network_service
16
+ @ado_config = ado_config
17
+ @logger = logger
18
+ end
19
+
20
+ # @param [String[]] keys
21
+ # @return [Array<Apadmi::Grout::Issue>]
22
+ def find_issues_by_keys(keys)
23
+ return [] if keys.length <= 0
24
+
25
+ keys = keys.map { |k| sanitise_key(k) } # Allows supporting raw keys, and keys prefixed with a hash which is common ADO practice
26
+
27
+ res = @network_service.do_get("/wit/workitems?ids=#{keys.join(",")}&api-version=7.1-preview.2")
28
+ parsed = JSON.parse(res.body)
29
+ items = parsed["value"]
30
+
31
+ return if items.nil? || items.empty?
32
+
33
+ items.map { |i| Apadmi::Grout::Issue.from_ado_hash(i) }
34
+ end
35
+
36
+ # @param component [String] Included to be consistent with JIRA, this will boil down to a tag in ADO
37
+ # @param status [String]
38
+ # @param ticket_types [String[]]
39
+ # @param options [Apadmi::Grout::AdoFindTicketsOptions] Additional options to filter by
40
+
41
+ # @return [Array<Apadmi::Grout::Issue>]
42
+ def search_unblocked_issues(component, status, ticket_types = [], options = nil)
43
+ tags = (options&.required_tags || []).push(*component).filter { |it| !it.blank? }
44
+ not_tags = (options&.not_tags || []).filter { |it| !it.blank? }
45
+
46
+ raise "Tags can either be required or not required, not both" unless (tags & not_tags).empty?
47
+
48
+ tags_filter = tags.map { |tag| " AND [System.Tags] CONTAINS '#{tag}' " }.join(" ")
49
+ not_tags_filter = not_tags.map { |tag| " AND [System.Tags] NOT CONTAINS '#{tag}'" }.join(" ")
50
+
51
+ status_filter = ("AND [System.State]='#{status}' " unless status.blank?) || ""
52
+ type_filter = ("AND [System.WorkItemType] IN (#{ticket_types.map { |t| "'#{t}'" }.join(", ")})" unless ticket_types.empty?) || ""
53
+ wiql = %(
54
+ Select [System.Id]
55
+ From WorkItems
56
+ Where [System.Id] >= 1
57
+ #{status_filter}
58
+ #{tags_filter}
59
+ #{not_tags_filter}
60
+ #{type_filter}
61
+ )
62
+
63
+ get_issues_by_wiql(wiql)
64
+ end
65
+
66
+ # @param [String] key
67
+ # @param [String] comment
68
+ def flag_ticket(key, comment)
69
+ key = sanitise_key(key)
70
+
71
+ add_tag(key, @ado_config.flag_tag)
72
+ add_comment(key, comment) unless comment.blank?
73
+ end
74
+
75
+ # @param [String] key
76
+ def un_flag_ticket(key)
77
+ key = sanitise_key(key)
78
+
79
+ remove_tag(key, @ado_config.flag_tag)
80
+ end
81
+
82
+ # @param [String] key
83
+ # @param [String] comment
84
+ def add_comment(key, comment)
85
+ key = sanitise_key(key)
86
+
87
+ payload = {
88
+ "text" => comment
89
+ }
90
+ @network_service.do_post("/wit/workitems/#{key}/comments?#{API_VERSION_PARAM}-preview.3", payload.to_json)
91
+ end
92
+
93
+ # @param [String] key
94
+ # @param [String] tag
95
+ def add_tag(key, tag)
96
+ payload = [{
97
+ "op" => "add",
98
+ "path" => "/fields/System.Tags",
99
+ "value" => tag
100
+ }]
101
+ @network_service.do_patch("/wit/workitems/#{key}?#{API_VERSION_PARAM}", payload.to_json)
102
+ end
103
+
104
+ # @param [String] key
105
+ # @param [String] tag
106
+ def remove_tag(key, tag)
107
+ current_tags = get_work_item(key)["fields"]["System.Tags"].split("; ")
108
+ new_tags = current_tags.filter { |t| t != tag }
109
+
110
+ payload = [{
111
+ "op" => "replace",
112
+ "path" => "/fields/System.Tags",
113
+ "value" => new_tags.join("; ")
114
+ }]
115
+ @network_service.do_patch("/wit/workitems/#{key}?#{API_VERSION_PARAM}", payload.to_json)
116
+ end
117
+
118
+ # @param [String] key
119
+ def flagged?(key)
120
+ current_tags = get_work_item(key)["fields"]["System.Tags"].split("; ")
121
+ current_tags.include?(@ado_config.flag_tag)
122
+ end
123
+
124
+ # @param [String] key
125
+ # @return [RawWorkItemHash]
126
+ def get_work_item(key)
127
+ key = sanitise_key(key)
128
+
129
+ res = @network_service.do_get("/wit/workitems/#{key}?#{API_VERSION_PARAM}")
130
+ JSON.parse(res.body)
131
+ end
132
+
133
+ # @param [String] key
134
+ # @param [Array<String>] versions
135
+ def assign_fixversions(key, versions)
136
+ key = sanitise_key(key)
137
+
138
+ payload = [{
139
+ "op" => "replace",
140
+ "path" => "/fields/Microsoft.VSTS.Build.IntegrationBuild",
141
+ "value" => versions.join("; ")
142
+ }]
143
+ @network_service.do_patch("/wit/workitems/#{key}?#{API_VERSION_PARAM}", payload.to_json)
144
+ end
145
+
146
+ # @param [String] key
147
+ # @return [Array<String>] fix versions
148
+ def get_ticket_fixversions(key)
149
+ key = sanitise_key(key)
150
+
151
+ get_work_item(key)["fields"]["Microsoft.VSTS.Build.IntegrationBuild"].split("; ")
152
+ end
153
+
154
+ # @param [Apadmi::Grout::Issue] issue
155
+ # @param [String] state_name
156
+ def transition_issue(issue, state_name)
157
+ key = sanitise_key(issue.key)
158
+
159
+ payload = [{
160
+ "op" => "add",
161
+ "path" => "/fields/System.State",
162
+ "value" => state_name
163
+ }]
164
+ @network_service.do_patch("/wit/workitems/#{key}?#{API_VERSION_PARAM}", payload.to_json)
165
+ end
166
+
167
+ # @param [String] key
168
+ # @return [String]
169
+ def get_ticket_status(key)
170
+ key = sanitise_key(key)
171
+
172
+ get_work_item(key)["fields"]["System.State"]
173
+ end
174
+
175
+ private
176
+
177
+ def sanitise_key(key)
178
+ key = key.to_s.strip.delete_prefix "#"
179
+ raise "Invalid key" if key.blank?
180
+
181
+ key
182
+ end
183
+
184
+ # @param wiql_query [String]
185
+ # @return [Array<Apadmi::Grout::Issue>]
186
+ def get_issues_by_wiql(wiql_query)
187
+ @logger.message("Executing wiql")
188
+ @logger.message(wiql_query)
189
+
190
+ payload = { "query" => wiql_query }
191
+ res = @network_service.do_post("/wit/wiql?#{API_VERSION_PARAM}", payload.to_json)
192
+ parsed = JSON.parse(res.body)
193
+ items = parsed["workItems"] || []
194
+ ids = items.map { |item| item["id"] } || []
195
+ find_issues_by_keys(ids)
196
+ end
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apadmi
4
+ module Grout
5
+ # A generic board service to be implemented by whatever
6
+ # board providers we have e.g. JIRA and ADO
7
+ class BoardService
8
+ # @param [String[]] keys
9
+ # @return [Array<Apadmi::Grout::Issue>]
10
+ def find_issues_by_keys(_keys)
11
+ raise "Unimplemented :("
12
+ end
13
+
14
+ # @param [String] _component
15
+ # @param [String] _status
16
+ # @param [String[]] _ticket_types
17
+ # @param [Apadmi::Grout::JiraFindTicketsOptions|Apadmi::Grout::JiraFindTicketsOptions] _options
18
+ # @return [Array<Apadmi::Grout::Issue>]
19
+ def search_unblocked_issues(_component, _status, _ticket_types = [], _options = nil)
20
+ raise "Unimplemented :("
21
+ end
22
+
23
+ # @param [String] _key
24
+ # @param [String] _comment
25
+ def flag_ticket(_key, _comment)
26
+ raise "Unimplemented :("
27
+ end
28
+
29
+ # @param [String] _key
30
+ def un_flag_ticket(_key)
31
+ raise "Unimplemented :("
32
+ end
33
+
34
+ # @param [String] _key
35
+ # @return bool
36
+ def flagged?(_key)
37
+ raise "Unimplemented :("
38
+ end
39
+
40
+ # @param [String] _key
41
+ # @param [Array<String>] _version_strings
42
+ def assign_fixversions(_key, _version_strings)
43
+ raise "Unimplemented :("
44
+ end
45
+
46
+ # @param [String] _key
47
+ # @return [Array<String>]
48
+ def get_ticket_fixversions(_key)
49
+ raise "Unimplemented :("
50
+ end
51
+
52
+ # @param [Apadmi::Grout::Issue] _issue
53
+ # @param [String] _state_name
54
+ def transition_issue(_issue, _state_name)
55
+ raise "Unimplemented :("
56
+ end
57
+ end
58
+ end
59
+ end
@@ -1,23 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Build tools
4
3
  require "jira-ruby"
5
- require "faraday"
6
4
 
7
5
  module Apadmi
8
6
  module Grout
9
7
  # Provides a layer of abstraction on top of the Jira api
10
- class JiraWrapper
11
- APPLICATION_JSON = "application/json"
12
- CONTENT_TYPE = "Content-Type"
13
- private_constant :CONTENT_TYPE, :APPLICATION_JSON
14
-
8
+ class JiraBoardService < BoardService
15
9
  # @param [String] username
16
10
  # @param [String] token
17
11
  # @param [String] base_url
18
12
  # @param [String] context_path
19
13
  # @param [String] project
20
- def initialize(username, token, base_url, context_path, project)
14
+ # @param [Apadmi::Grout::NetworkService] network_service
15
+ def initialize(username, token, base_url, context_path, project, network_service)
21
16
  @options = {
22
17
  username: username,
23
18
  password: token,
@@ -26,52 +21,64 @@ module Apadmi
26
21
  auth_type: :basic
27
22
  }
28
23
  @project = project
24
+ @network_service = network_service
29
25
  @jira_client = JIRA::Client.new(@options)
30
26
  end
31
27
 
32
- # @return returns the ticket prefix for the given project
33
- def issue_id_prefix
34
- @project
35
- end
36
-
37
28
  # @param [String[]] keys
38
- # @return [Array<JIRA::Resource::Issue>]
29
+ # @return [Array<Apadmi::Grout::Issue>]
39
30
  def find_issues_by_keys(keys)
40
31
  return [] if keys.length <= 0
41
32
 
42
33
  jql_search = "project = '#{@project}' AND issue IN (#{keys.join(", ")})"
43
- @jira_client.Issue.jql(jql_search, { max_results: 1000 }).uniq
34
+ issues = @jira_client.Issue.jql(jql_search, { max_results: 1000 }).uniq
35
+ convert(issues)
44
36
  end
45
37
 
46
38
  # @param [String] component
47
39
  # @param [String] status
48
40
  # @param [String[]] ticket_types
49
- # @return [Array<JIRA::Resource::Issue>]
50
- def search_unblocked_issues(component, status, ticket_types = [])
41
+ # @param [Apadmi::Grout::JiraFindTicketsOptions] options
42
+ # @return [Array<Apadmi::Grout::Issue>]
43
+ def search_unblocked_issues(component, status, ticket_types = [], options = nil)
44
+ allow_no_sprint = options&.include_no_sprint_tickets || false
51
45
  component_filter = (" AND component = '#{component}' " unless component.empty?) || ""
52
46
  status_filter = (" AND status = '#{status}' " unless status.empty?) || ""
53
47
  type_filter = ("AND (#{ticket_types.map { |type| "type = #{type}" }.join("OR ")})" unless ticket_types.empty?) || ""
48
+ empty_sprint_condition = ("OR sprint is EMPTY" if allow_no_sprint) || ""
54
49
 
55
50
  jql_search = %{
56
51
  project = '#{@project}'
57
52
  #{status_filter} #{component_filter} #{type_filter}
58
- AND sprint in openSprints() AND (labels not in(Blocked) or labels is EMPTY) AND Flagged is EMPTY
53
+ AND (sprint in openSprints() #{empty_sprint_condition}) AND (labels not in(Blocked) or labels is EMPTY)
54
+ AND Flagged is EMPTY
59
55
  }
60
56
 
61
- @jira_client.Issue.jql(jql_search, { max_results: 1000 }).uniq
57
+ issues = @jira_client.Issue.jql(jql_search, { max_results: 1000 }).uniq
58
+ convert(issues)
59
+ end
60
+
61
+ # @param [Apadmi::Grout::Issue] issue
62
+ # @param [String] state_name
63
+ def transition_issue(issue, state_name)
64
+ transitions = @jira_client.Transition.all(issue: issue.raw_object)
65
+ transition = transitions.find { |elem| elem.name.downcase == state_name.downcase }
66
+
67
+ trans = issue.raw_object.transitions.build
68
+ trans.save!("transition" => { "id" => transition.id })
62
69
  end
63
70
 
64
71
  # @param [String] key
65
72
  # @param [String] comment
66
73
  def flag_ticket(key, comment)
67
74
  payload = "{\"flag\":true,\"issueKeys\":[\"#{key}\"],\"commentVisibility\":\"\",\"comment\":\"#{comment}\"}"
68
- do_post("#{@options[:site]}/rest/greenhopper/1.0/xboard/issue/flag/flag.json", payload)
75
+ @network_service.do_post("/rest/greenhopper/1.0/xboard/issue/flag/flag.json", payload)
69
76
  end
70
77
 
71
78
  # @param [String] key
72
79
  def un_flag_ticket(key)
73
80
  payload = "{\"flag\":false,\"issueKeys\":[\"#{key}\"],\"commentVisibility\":\"\",\"comment\":\"Unflagged by CI\"}"
74
- do_post("#{@options[:site]}/rest/greenhopper/1.0/xboard/issue/flag/flag.json", payload)
81
+ @network_service.do_post("/rest/greenhopper/1.0/xboard/issue/flag/flag.json", payload)
75
82
  end
76
83
 
77
84
  # @param [String] key
@@ -86,7 +93,7 @@ module Apadmi
86
93
  # @param [String] comment
87
94
  def add_comment(key, comment)
88
95
  payload = "{\"issueKeys\":[\"#{key}\"],\"commentVisibility\":\"\",\"comment\":\"#{comment}\"}"
89
- do_post("#{@options[:site]}/rest/greenhopper/1.0/xboard/issue/flag/flag.json", payload)
96
+ @network_service.do_post("/rest/greenhopper/1.0/xboard/issue/flag/flag.json", payload)
90
97
  end
91
98
 
92
99
  # @param [String] key
@@ -101,14 +108,14 @@ module Apadmi
101
108
  # @param [String] key
102
109
  # @param [String] comment_id
103
110
  def remove_comment(key, comment_id)
104
- do_delete("#{@options[:site]}/rest/api/2/issue/#{key}/comment/#{comment_id}")
111
+ @network_service.do_delete("/rest/api/2/issue/#{key}/comment/#{comment_id}")
105
112
  end
106
113
 
107
114
  # @param [String] component_key
108
- # @return [Array<JIRA::Resource::Issue>]
115
+ # @return [Array<Apadmi::Grout::Issue>]
109
116
  def get_tickets_by_component(component_key)
110
117
  jql_search = "project = '#{@project}' AND component IN ('#{component_key}')"
111
- @jira_client.Issue.jql(jql_search, { max_results: 1000 }).uniq
118
+ convert(@jira_client.Issue.jql(jql_search, { max_results: 1000 }).uniq)
112
119
  end
113
120
 
114
121
  # @param [String] key
@@ -117,33 +124,18 @@ module Apadmi
117
124
  @jira_client.Transition.all(issue: issue)
118
125
  end
119
126
 
120
- # @param [String] ticket
121
- # @param [String] state_name
122
- def transition_ticket(ticket, state_name)
123
- issue = @jira_client.Issue.find(ticket)
124
- transition_issue(issue, state_name)
125
- end
126
-
127
- # @param [JIRA::Resource::Issue] issue
128
- # @param [String] state_name
129
- def transition_issue(issue, state_name)
130
- transitions = @jira_client.Transition.all(issue: issue)
131
- transition = transitions.find { |elem| elem.name == state_name }
132
-
133
- trans = issue.transitions.build
134
- trans.save!("transition" => { "id" => transition.id })
135
- end
136
-
137
127
  # @param [String] key
128
+ # @return [String]
138
129
  def get_ticket_status(key)
139
130
  issue = @jira_client.Issue.find(key)
140
- issue.status
131
+ issue.status.name
141
132
  end
142
133
 
143
- # @param [JIRA::Resource::Issue] issue
134
+ # @param [Apadmi::Grout::Issue] issue
144
135
  # @return [Array<PullRequest>]
145
136
  def get_ticket_prs(issue)
146
- response = do_get("#{@options[:site]}/rest/dev-status/latest/issue/details?issueId=#{issue.id}&applicationType=bitbucket&dataType=pullrequest")
137
+ query_string = "issueId=#{issue.raw_object.id}&applicationType=bitbucket&dataType=pullrequest"
138
+ response = @network_service.do_get("/rest/dev-status/latest/issue/details?#{query_string}")
147
139
 
148
140
  return [] if JSON.parse(response.body)["detail"].empty?
149
141
 
@@ -154,11 +146,11 @@ module Apadmi
154
146
 
155
147
  # @param [String] keys
156
148
  # @param [String] component
157
- # @return [Array<JIRA::Resource::Issue>]
149
+ # @return [Array<Apadmi::Grout::Issue>]
158
150
  def get_ticket_subtask(keys, component = nil)
159
151
  jql_search = "project = '#{@project}' AND parent IN #{keys.to_s.gsub("[", "(").gsub("]", ")")}"\
160
152
  + (component.nil? ? "" : " AND component IN ('#{component}')")
161
- @jira_client.Issue.jql(jql_search, { max_results: 1000 }).uniq
153
+ convert(@jira_client.Issue.jql(jql_search, { max_results: 1000 }).uniq)
162
154
  end
163
155
 
164
156
  # @param [String] release_date
@@ -179,7 +171,7 @@ module Apadmi
179
171
  def delete_version(version_id, move_version_id)
180
172
  payload = "{\"moveFixIssuesTo\": #{move_version_id}, \"moveAffectedIssuesTo\": #{move_version_id}, "\
181
173
  "\"customFieldReplacementList\": []}"
182
- do_post("#{@options[:site]}/rest/api/2/version/#{version_id}/removeAndSwap", payload)
174
+ @network_service.do_post("/rest/api/2/version/#{version_id}/removeAndSwap", payload)
183
175
  end
184
176
 
185
177
  # @return [Array<JIRA::Resource::Version>]
@@ -187,75 +179,44 @@ module Apadmi
187
179
  @jira_client.Project.find(@project).versions
188
180
  end
189
181
 
190
- # @param [String] ticket
191
- # @param [Array<JIRA::Resource::Version>] versions
192
- def assign_fixversions(ticket, versions)
182
+ # @param [String] key
183
+ # @param [Array<String>] version_strings
184
+ def assign_fixversions(key, version_strings)
185
+ versions = create_or_get_versions(version_strings)
193
186
  fixversions = versions.map { |v| "{\"id\": \"#{v.id}\"}" }.join(", ")
194
187
  payload = "{\"fields\" : {\"fixVersions\": [#{fixversions}] }}"
195
- do_put("#{@options[:site]}/rest/api/2/issue/#{ticket}", payload)
196
- end
197
-
198
- # @param [String] ticket
199
- # @return [Array<Version>]
200
- def get_ticket_fixversions(ticket)
201
- response = do_get("#{@options[:site]}/rest/api/2/issue/#{ticket}")
202
- JSON.parse(response.body)["fields"]["fixVersions"].map do |version|
203
- Version.new(version)
204
- end
205
- end
206
-
207
- # @param [Faraday::Response] response
208
- def throw_if_error(response)
209
- raise "Network call failed #{response.status} #{response.body}" unless (200..210).include?(response.status)
188
+ @network_service.do_put("/rest/api/2/issue/#{key}", payload)
210
189
  end
211
190
 
212
- # @param [String] uri
213
- # @return [Faraday::Response]
214
- def do_get(uri)
215
- conn = setup_con
216
- response = conn.get(uri, CONTENT_TYPE => APPLICATION_JSON)
217
- throw_if_error(response)
218
- response
219
- end
220
-
221
- # @param [String] uri
222
- # @param [String] payload
223
- # @return [Faraday::Response]
224
- def do_put(uri, payload)
225
- conn = setup_con
226
- response = conn.put(uri, payload, CONTENT_TYPE => APPLICATION_JSON)
227
- throw_if_error(response)
228
- response
191
+ # @param [String] key
192
+ # @return [Array<String>]
193
+ def get_ticket_fixversions(key)
194
+ response = @network_service.do_get("/rest/api/2/issue/#{key}")
195
+ JSON.parse(response.body)["fields"]["fixVersions"].map { |v| v["name"] } || []
229
196
  end
230
197
 
231
- # @param [String] uri
232
- # @param [String] payload
233
- # @return [Faraday::Response]
234
- def do_post(uri, payload)
235
- conn = setup_con
236
- response = conn.post(uri, payload, CONTENT_TYPE => APPLICATION_JSON)
237
- throw_if_error(response)
238
- response
239
- end
198
+ private
240
199
 
241
- # @param [String] uri
242
- # @return [Faraday::Response]
243
- def do_delete(uri)
244
- conn = setup_con
245
- response = conn.delete(uri, CONTENT_TYPE => APPLICATION_JSON)
246
- throw_if_error(response)
247
- response
200
+ # @param [Array<String>] version_strings
201
+ # @return [Array<JIRA::Resource::Version>]
202
+ def create_or_get_versions(version_strings)
203
+ all = all_versions
204
+ version_strings.map do |version|
205
+ existing_version = all.find { |v| v.name == version }
206
+ if !existing_version.nil?
207
+ existing_version
208
+ else
209
+ date = Time.now.strftime("%Y-%m-%d")
210
+ create_version(date, version)
211
+ end
212
+ end
248
213
  end
249
214
 
250
- # @return [Faraday::Connection]
251
- def setup_con
252
- auth_str = "#{@options[:username]}:#{@options[:password]}"
253
- conn = Faraday.new # create a new Connection with base URL
254
- conn.headers["Authorization"] = "Basic #{Base64.strict_encode64(auth_str)}"
255
- conn
215
+ # @param [Array<JIRA::Resource::Issue>]
216
+ # @return [Array<Apadmi::Grout::Issue>]
217
+ def convert(issues)
218
+ Apadmi::Grout::Issue.from_jira_issues(issues, @options[:site])
256
219
  end
257
-
258
- private :setup_con, :do_delete, :do_get, :do_post, :do_put
259
220
  end
260
221
  end
261
222
  end