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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -1
- data/lib/apadmi/grout/actions/find_tickets_to_move_action/find_tickets_to_move_action.rb +24 -0
- data/lib/apadmi/grout/actions/find_tickets_to_move_action/find_tickets_to_move_ado_action.rb +91 -0
- data/lib/apadmi/grout/actions/find_tickets_to_move_action/find_tickets_to_move_jira_action.rb +114 -0
- data/lib/apadmi/grout/actions/generate_release_notes_action/generate_release_notes_action.rb +48 -0
- data/lib/apadmi/grout/actions/generate_release_notes_action/issue_classifier.rb +51 -0
- data/lib/apadmi/grout/{release_notes/actions → actions}/issues_from_changelog_action.rb +11 -8
- data/lib/apadmi/grout/actions/move_tickets_action.rb +34 -0
- data/lib/apadmi/grout/di.rb +54 -9
- data/lib/apadmi/grout/models/ado_config.rb +10 -0
- data/lib/apadmi/grout/models/find_tickets_options.rb +44 -0
- data/lib/apadmi/grout/models/flag_messages.rb +25 -0
- data/lib/apadmi/grout/models/issue.rb +49 -0
- data/lib/apadmi/grout/{jira/models → models}/pull_request.rb +0 -0
- data/lib/apadmi/grout/models/release_notes_config.rb +47 -0
- data/lib/apadmi/grout/{release_notes/models → models}/release_notes_templates.rb +6 -6
- data/lib/apadmi/grout/service/board_service/ado_board_service.rb +199 -0
- data/lib/apadmi/grout/service/board_service/board_service.rb +59 -0
- data/lib/apadmi/grout/{jira/wrapper/jira_wrapper.rb → service/board_service/jira_board_service.rb} +68 -107
- data/lib/apadmi/grout/utils/filename_utils.rb +40 -0
- data/lib/apadmi/grout/utils/git_utils.rb +57 -0
- data/lib/apadmi/grout/utils/network_service.rb +88 -0
- data/lib/apadmi/grout/version.rb +1 -1
- data/lib/apadmi_grout.rb +3 -18
- metadata +23 -13
- data/lib/apadmi/grout/jira/actions/find_tickets_to_move_action.rb +0 -76
- data/lib/apadmi/grout/jira/actions/move_jira_tickets_action.rb +0 -58
- data/lib/apadmi/grout/jira/models/flag_messages.rb +0 -8
- data/lib/apadmi/grout/jira/models/version.rb +0 -23
- data/lib/apadmi/grout/release_notes/actions/generate_release_notes_action.rb +0 -39
- 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
|
-
|
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 }}]({{
|
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 }}]({{
|
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 }}]({{
|
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 }}]({{
|
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 }}]({{
|
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
|
data/lib/apadmi/grout/{jira/wrapper/jira_wrapper.rb → service/board_service/jira_board_service.rb}
RENAMED
@@ -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
|
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
|
-
|
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<
|
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
|
-
# @
|
50
|
-
|
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)
|
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("
|
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("
|
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("
|
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("
|
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<
|
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 [
|
134
|
+
# @param [Apadmi::Grout::Issue] issue
|
144
135
|
# @return [Array<PullRequest>]
|
145
136
|
def get_ticket_prs(issue)
|
146
|
-
|
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<
|
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("
|
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]
|
191
|
-
# @param [Array<
|
192
|
-
def assign_fixversions(
|
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("
|
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]
|
213
|
-
# @return [
|
214
|
-
def
|
215
|
-
|
216
|
-
response
|
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
|
-
|
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]
|
242
|
-
# @return [
|
243
|
-
def
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
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
|
-
# @
|
251
|
-
|
252
|
-
|
253
|
-
|
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
|