Class: Apadmi::Grout::JiraBoardService

Inherits:
BoardService show all
Defined in:
lib/apadmi/grout/service/board_service/jira_board_service.rb

Overview

Provides a layer of abstraction on top of the Jira api

Instance Method Summary collapse

Constructor Details

#initialize(jira_config, network_service) ⇒ JiraBoardService

Returns a new instance of JiraBoardService.

Parameters:



11
12
13
14
15
16
17
18
19
20
21
22
# File 'lib/apadmi/grout/service/board_service/jira_board_service.rb', line 11

def initialize(jira_config, network_service)
  @options = {
    username: jira_config.username,
    password: jira_config.api_token,
    site: jira_config.base_url,
    context_path: jira_config.context_path,
    auth_type: :basic
  }
  @project = jira_config.project_key
  @network_service = network_service
  @jira_client = JIRA::Client.new(@options)
end

Instance Method Details

#add_comment(key, comment) ⇒ Object

Parameters:

  • key (String)
  • comment (String)


92
93
94
95
# File 'lib/apadmi/grout/service/board_service/jira_board_service.rb', line 92

def add_comment(key, comment)
  payload = "{\"issueKeys\":[\"#{key}\"],\"commentVisibility\":\"\",\"comment\":\"#{comment}\"}"
  @network_service.do_post("/rest/greenhopper/1.0/xboard/issue/flag/flag.json", payload)
end

Adds a related work link to a Jira version, skipping if a link with the same URL already exists.

Parameters:

  • version_name (String)
  • url (String)
  • title (String)


248
249
250
251
252
253
254
255
256
257
258
# File 'lib/apadmi/grout/service/board_service/jira_board_service.rb', line 248

def add_version_related_work(version_name, url, title)
  version = create_or_get_versions([version_name]).find { |v| v.name == version_name }
  raise "Version '#{version_name}' not found" if version.nil?

  response = @network_service.do_get("/rest/api/3/version/#{version.id}/relatedwork")
  existing_links = JSON.parse(response.body) || []
  return if existing_links.any? { |link| link["url"] == url }

  payload = { category: "link", url: url, title: title }.to_json
  @network_service.do_post("/rest/api/3/version/#{version.id}/relatedwork", payload)
end

#all_versionsArray<JIRA::Resource::Version>

Returns:

  • (Array<JIRA::Resource::Version>)


179
180
181
# File 'lib/apadmi/grout/service/board_service/jira_board_service.rb', line 179

def all_versions
  @jira_client.Project.find(@project).versions
end

#assign_fixversion_to_tickets(issues, version_names) ⇒ Object

Assigns one or more fix versions to all given issues that are missing them. Filters in-memory from the already-fetched issue objects — no extra network calls. Each issue gets a single PUT containing only the versions it is missing.

Parameters:



203
204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/apadmi/grout/service/board_service/jira_board_service.rb', line 203

def assign_fixversion_to_tickets(issues, version_names)
  return if issues.empty? || version_names.empty?

  versions = create_or_get_versions(version_names)

  issues.each do |issue|
    existing_ids = issue.raw_object.fixVersions.map(&:id)
    missing = versions.reject { |v| existing_ids.include?(v.id) }
    next if missing.empty?

    adds = missing.map { |v| "{\"add\": {\"id\": \"#{v.id}\"}}" }.join(", ")
    @network_service.do_put("/rest/api/2/issue/#{issue.key}", "{\"update\": {\"fixVersions\": [#{adds}]}}")
  end
end

#assign_fixversions(key, version_strings) ⇒ Object

Parameters:

  • key (String)
  • version_strings (Array<String>)


220
221
222
223
224
225
# File 'lib/apadmi/grout/service/board_service/jira_board_service.rb', line 220

def assign_fixversions(key, version_strings)
  versions = create_or_get_versions(version_strings)
  fixversions = versions.map { |v| "{\"id\": \"#{v.id}\"}" }.join(", ")
  payload = "{\"fields\" : {\"fixVersions\": [#{fixversions}] }}"
  @network_service.do_put("/rest/api/2/issue/#{key}", payload)
end

#create_version(release_date, name) ⇒ JIRA::Resource::Version

Parameters:

  • release_date (String)
  • name (String)

Returns:

  • (JIRA::Resource::Version)


160
161
162
163
164
165
166
167
168
# File 'lib/apadmi/grout/service/board_service/jira_board_service.rb', line 160

def create_version(release_date, name)
  version = @jira_client.Version.build
  version.save!({
                  project: @project,
                  name: name,
                  startDate: release_date
                })
  version
end

#delete_version(version_id, move_version_id) ⇒ Object

Parameters:

  • version_id (int)
  • move_version_id (int)


172
173
174
175
176
# File 'lib/apadmi/grout/service/board_service/jira_board_service.rb', line 172

def delete_version(version_id, move_version_id)
  payload = "{\"moveFixIssuesTo\": #{move_version_id}, \"moveAffectedIssuesTo\": #{move_version_id}, " \
            "\"customFieldReplacementList\": []}"
  @network_service.do_post("/rest/api/2/version/#{version_id}/removeAndSwap", payload)
end

#find_issues_by_keys(keys) ⇒ Array<Apadmi::Grout::Issue>

Parameters:

  • keys (String[])

Returns:



26
27
28
29
30
31
32
# File 'lib/apadmi/grout/service/board_service/jira_board_service.rb', line 26

def find_issues_by_keys(keys)
  return [] if keys.length <= 0

  jql_search = "project = '#{@project}' AND issue IN (#{keys.join(", ")})"
  issues = @jira_client.Issue.jql(jql_search, { max_results: 1000, fields: ["*navigable"] }).uniq
  convert(issues)
end

#flag_ticket(key, comment) ⇒ Object

Parameters:

  • key (String)
  • comment (String)


69
70
71
72
73
# File 'lib/apadmi/grout/service/board_service/jira_board_service.rb', line 69

def flag_ticket(key, comment)
  payload = "{\"flag\":true,\"issueKeys\":[\"#{key}\"],\"commentVisibility\":\"\",\"comment\":\"#{comment}\"}"
  @network_service.do_post("/rest/greenhopper/1.0/xboard/issue/flag/flag.json", payload)
  sleep 2 # Seems to take a while for flagging and unflagging to register
end

#flagged?(key) ⇒ Boolean

Returns bool.

Parameters:

  • key (String)

Returns:

  • (Boolean)

    bool



84
85
86
87
88
# File 'lib/apadmi/grout/service/board_service/jira_board_service.rb', line 84

def flagged?(key)
  jql_search = "project = '#{@project}' AND issue IN (#{key}) AND Flagged is not EMPTY"
  response = @jira_client.Issue.jql(jql_search, { max_results: 1000, fields: ["*navigable"] }).uniq
  response != []
end

#get_ticket_fixversions(key) ⇒ Array<String>

Parameters:

  • key (String)

Returns:

  • (Array<String>)


239
240
241
242
# File 'lib/apadmi/grout/service/board_service/jira_board_service.rb', line 239

def get_ticket_fixversions(key)
  response = @network_service.do_get("/rest/api/2/issue/#{key}")
  JSON.parse(response.body)["fields"]["fixVersions"].map { |v| v["name"] } || []
end

#get_ticket_prs(issue) ⇒ Array<PullRequest>

Parameters:

Returns:



134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/apadmi/grout/service/board_service/jira_board_service.rb', line 134

def get_ticket_prs(issue)
  query_string = "issueId=#{issue.raw_object.id}&applicationType=bitbucket&dataType=pullrequest"
  response = @network_service.do_get("/rest/dev-status/latest/issue/details?#{query_string}")

  parsed_detail = JSON.parse(response.body)["detail"]

  return [] if parsed_detail.empty?
  return [] unless parsed_detail[0].key?("pullRequests")

  parsed_detail[0]["pullRequests"].map do |pr|
    PullRequest.new(pr["id"], pr["name"], pr["status"].to_s == "MERGED", pr["status"].to_s == "DECLINED")
  end
end

#get_ticket_status(key) ⇒ String

Parameters:

  • key (String)

Returns:

  • (String)


127
128
129
130
# File 'lib/apadmi/grout/service/board_service/jira_board_service.rb', line 127

def get_ticket_status(key)
  issue = @jira_client.Issue.find(key)
  issue.status.name
end

#get_ticket_subtask(keys, component = nil) ⇒ Array<Apadmi::Grout::Issue>

Parameters:

  • keys (String)
  • component (String) (defaults to: nil)

Returns:



151
152
153
154
155
# File 'lib/apadmi/grout/service/board_service/jira_board_service.rb', line 151

def get_ticket_subtask(keys, component = nil)
  jql_search = "project = '#{@project}' AND parent IN #{keys.to_s.gsub("[", "(").gsub("]", ")")}" \
                + (component.nil? ? "" : " AND component IN ('#{component}')")
  convert(@jira_client.Issue.jql(jql_search, { max_results: 1000, fields: ["*navigable"] }).uniq)
end

#get_tickets_by_component(component_key) ⇒ Array<Apadmi::Grout::Issue>

Parameters:

  • component_key (String)

Returns:



114
115
116
117
# File 'lib/apadmi/grout/service/board_service/jira_board_service.rb', line 114

def get_tickets_by_component(component_key)
  jql_search = "project = '#{@project}' AND component IN ('#{component_key}')"
  convert(@jira_client.Issue.jql(jql_search, { max_results: 1000, fields: ["*navigable"] }).uniq)
end

#get_top_comment(key) ⇒ JIRA::Resource::Comment

Parameters:

  • key (String)

Returns:

  • (JIRA::Resource::Comment)


99
100
101
102
103
104
# File 'lib/apadmi/grout/service/board_service/jira_board_service.rb', line 99

def get_top_comment(key)
  jql_search = "project = '#{@project}' AND issue IN (#{key})"
  response = @jira_client.Issue.jql(jql_search, { fields: ["comment"], max_results: 1000 }).uniq
  comments = response[0].comments.reverse
  comments.empty? ? nil : comments[0]
end

#get_transitions(key) ⇒ Object

Parameters:

  • key (String)


120
121
122
123
# File 'lib/apadmi/grout/service/board_service/jira_board_service.rb', line 120

def get_transitions(key)
  issue = @jira_client.Issue.find(key)
  @jira_client.Transition.all(issue: issue)
end

#remove_comment(key, comment_id) ⇒ Object

Parameters:

  • key (String)
  • comment_id (String)


108
109
110
# File 'lib/apadmi/grout/service/board_service/jira_board_service.rb', line 108

def remove_comment(key, comment_id)
  @network_service.do_delete("/rest/api/2/issue/#{key}/comment/#{comment_id}")
end

#remove_fixversions(key, version_names) ⇒ Object

Removes specific fix versions from a ticket using the update.remove operation.

Parameters:

  • key (String)
  • version_names (Array<String>)


230
231
232
233
234
235
# File 'lib/apadmi/grout/service/board_service/jira_board_service.rb', line 230

def remove_fixversions(key, version_names)
  return if version_names.empty?

  removes = version_names.map { |name| "{\"remove\": {\"name\": \"#{name}\"}}" }.join(", ")
  @network_service.do_put("/rest/api/2/issue/#{key}", "{\"update\": {\"fixVersions\": [#{removes}]}}")
end

#search_unblocked_issues(component, status, ticket_types = [], options = nil) ⇒ Array<Apadmi::Grout::Issue>

Parameters:

Returns:



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/apadmi/grout/service/board_service/jira_board_service.rb', line 39

def search_unblocked_issues(component, status, ticket_types = [], options = nil)
  allow_no_sprint = options&.include_no_sprint_tickets || false
  component_filter = (" AND component = '#{component}' " unless component.empty?) || ""
  status_filter = (" AND status = '#{status}' " unless status.empty?) || ""
  type_filter = ("AND (#{ticket_types.map { |type| "type = #{type}" }.join("OR ")})" unless ticket_types.empty?) || ""
  empty_sprint_condition = ("OR sprint is EMPTY" if allow_no_sprint) || ""

  jql_search = %{
    project = '#{@project}'
    #{status_filter} #{component_filter} #{type_filter}
    AND (sprint in openSprints() #{empty_sprint_condition}) AND (labels not in(Blocked) or labels is EMPTY)
    AND Flagged is EMPTY
  }

  issues = @jira_client.Issue.jql(jql_search, { max_results: 1000, fields: ["*navigable"] }).uniq
  convert(issues)
end

#transition_issue(issue, state_name) ⇒ Object

Parameters:



59
60
61
62
63
64
65
# File 'lib/apadmi/grout/service/board_service/jira_board_service.rb', line 59

def transition_issue(issue, state_name)
  transitions = @jira_client.Transition.all(issue: issue.raw_object)
  transition = transitions.find { |elem| elem.name.downcase == state_name.downcase }

  trans = issue.raw_object.transitions.build
  trans.save!("transition" => { "id" => transition.id })
end

#un_flag_ticket(key) ⇒ Object

Parameters:

  • key (String)


76
77
78
79
80
# File 'lib/apadmi/grout/service/board_service/jira_board_service.rb', line 76

def un_flag_ticket(key)
  payload = "{\"flag\":false,\"issueKeys\":[\"#{key}\"],\"commentVisibility\":\"\",\"comment\":\"Unflagged by CI\"}"
  @network_service.do_post("/rest/greenhopper/1.0/xboard/issue/flag/flag.json", payload)
  sleep 2 # Seems to take a while for flagging and unflagging to register
end

#update_version(version_name, released: nil, description: nil) ⇒ Object

Parameters:

  • version_name (String)
  • released (Boolean, nil) (defaults to: nil)

    omit to leave unchanged

  • description (String, nil) (defaults to: nil)

    omit to leave unchanged

Raises:

  • (ArgumentError)


186
187
188
189
190
191
192
193
194
195
196
# File 'lib/apadmi/grout/service/board_service/jira_board_service.rb', line 186

def update_version(version_name, released: nil, description: nil)
  raise ArgumentError, "Must provide at least one of: released, description" if released.nil? && description.nil?

  version = create_or_get_versions([version_name]).first
  raise "Version '#{version_name}' not found" if version.nil?

  fields = {}
  fields[:released] = released unless released.nil?
  fields[:description] = description unless description.nil?
  @network_service.do_put("/rest/api/2/version/#{version.id}", fields.to_json)
end