jirametrics 2.17 → 2.17.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fe52b964cb227f3dac580d4648a325d61fed7c342a7dd7d7bcb62dddd571af61
4
- data.tar.gz: 59eec793880fafb6c5e53b7daf87feb78b53b7624390adfa86ed6ca0be8125f6
3
+ metadata.gz: 4e9534fd6ca22944557cfe40c63ebf5c30ff111458ec52dd3e35617246315ce8
4
+ data.tar.gz: 2feb93d3ae826f133902664751f3cee4e54de3512daaaa882ae78fed47605e65
5
5
  SHA512:
6
- metadata.gz: a98b06c724a2fe57511382a952093d73a7dad414c493c5298c8d35c8e889117252a2a3a6d69eadd274ca6dd90ca03cf0231e23da7b18041752d60bbad9b2fc82
7
- data.tar.gz: 90fc55a7e86358636797f00e6cd4fa4eb5268b3a993ed48ff3bc8aecd0e3ae7629d721a814f5e520b58feed72075858613d11faccdea1a0548ac4a7c19a31479
6
+ metadata.gz: 84942ea82c68aa66325299e5db0e5f5a4fe4af95eb909d1fde004435b53898c60ee7dde2251fec0e075f4b2db7ba2f1eade384795370f3b90d7230926e79d2b9
7
+ data.tar.gz: 9015a7c5ef2b60726dca272a0b5b07de9f62d76e076038e4c8d45cbae13facf37f7e8f45dac82639c72615bbee549de756d3fb4792d21345fb4d3dc3fb17a32e
@@ -2,11 +2,12 @@
2
2
 
3
3
  require 'random-word'
4
4
 
5
- class Anonymizer
5
+ class Anonymizer < ChartBase
6
6
  # needed for testing
7
7
  attr_reader :project_config, :issues
8
8
 
9
9
  def initialize project_config:, date_adjustment: -200
10
+ super()
10
11
  @project_config = project_config
11
12
  @issues = @project_config.issues
12
13
  @all_boards = @project_config.all_boards
@@ -130,18 +131,19 @@ class Anonymizer
130
131
  end
131
132
  end
132
133
 
133
- def shift_all_dates
134
- @file_system.log "Shifting all dates by #{@date_adjustment} days"
134
+ def shift_all_dates date_adjustment: @date_adjustment
135
+ adjustment_in_seconds = 60 * 60 * 24 * date_adjustment
136
+ @file_system.log "Shifting all dates by #{label_days date_adjustment}"
135
137
  @issues.each do |issue|
136
138
  issue.changes.each do |change|
137
- change.time = change.time + @date_adjustment
139
+ change.time = change.time + adjustment_in_seconds
138
140
  end
139
141
 
140
- issue.raw['fields']['updated'] = (issue.updated + @date_adjustment).to_s
142
+ issue.raw['fields']['updated'] = (issue.updated + adjustment_in_seconds).to_s
141
143
  end
142
144
 
143
145
  range = @project_config.time_range
144
- @project_config.time_range = (range.begin + @date_adjustment)..(range.end + @date_adjustment)
146
+ @project_config.time_range = (range.begin + date_adjustment)..(range.end + date_adjustment)
145
147
  end
146
148
 
147
149
  def random_name
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class ChangeItem
4
- attr_reader :field, :value_id, :old_value_id, :raw, :time, :author_raw
5
- attr_accessor :value, :old_value
4
+ attr_reader :field, :value_id, :old_value_id, :raw, :author_raw
5
+ attr_accessor :value, :old_value, :time
6
6
 
7
7
  def initialize raw:, author_raw:, time:, artificial: false
8
8
  @raw = raw
@@ -350,32 +350,6 @@ class Downloader
350
350
  )
351
351
  end
352
352
 
353
- def bulk_fetch_issues issue_datas:, board:, in_initial_query:
354
- log " Downloading #{issue_datas.size} issues", both: true
355
- payload = {
356
- 'expand' => [
357
- 'changelog'
358
- ],
359
- 'fields' => ['*all'],
360
- 'issueIdsOrKeys' => issue_datas.collect(&:key)
361
- }
362
- response = @jira_gateway.post_request(
363
- relative_url: issue_bulk_fetch_api,
364
- payload: JSON.generate(payload)
365
- )
366
- response['issues'].each do |issue_json|
367
- issue_json['exporter'] = {
368
- 'in_initial_query' => in_initial_query
369
- }
370
- issue = Issue.new(raw: issue_json, board: board)
371
- data = issue_datas.find { |d| d.key == issue.key }
372
- data.up_to_date = true
373
- data.last_modified = issue.updated
374
- data.issue = issue
375
- end
376
- issue_datas
377
- end
378
-
379
353
  def delete_issues_from_cache_that_are_not_in_server issue_data_hash:, path:
380
354
  # The gotcha with deleted issues is that they just stop being returned in queries
381
355
  # and we have no way to know that they should be removed from our local cache.
@@ -44,7 +44,71 @@ class DownloaderForCloud < Downloader
44
44
  hash
45
45
  end
46
46
 
47
- def issue_bulk_fetch_api
48
- '/rest/api/3/issue/bulkfetch'
47
+ def bulk_fetch_issues issue_datas:, board:, in_initial_query:
48
+ # We used to use the expand option to pull in the changelog directly. Unfortunately
49
+ # that only returns the "recent" changes, not all of them. So now we get the issue
50
+ # without changes and then make a second call for that changes. Then we insert it
51
+ # into the raw issue as if it had been there all along.
52
+ log " Downloading #{issue_datas.size} issues", both: true
53
+ payload = {
54
+ 'fields' => ['*all'],
55
+ 'issueIdsOrKeys' => issue_datas.collect(&:key)
56
+ }
57
+ response = @jira_gateway.post_request(
58
+ relative_url: '/rest/api/3/issue/bulkfetch',
59
+ payload: JSON.generate(payload)
60
+ )
61
+
62
+ attach_changelog_to_issues issue_datas: issue_datas, issue_jsons: response['issues']
63
+
64
+ response['issues'].each do |issue_json|
65
+ issue_json['exporter'] = {
66
+ 'in_initial_query' => in_initial_query
67
+ }
68
+ issue = Issue.new(raw: issue_json, board: board)
69
+ data = issue_datas.find { |d| d.key == issue.key }
70
+ data.up_to_date = true
71
+ data.last_modified = issue.updated
72
+ data.issue = issue
73
+ end
74
+
75
+ issue_datas
76
+ end
77
+
78
+ def attach_changelog_to_issues issue_datas:, issue_jsons:
79
+ max_results = 10_000 # The max jira accepts is 10K
80
+ payload = {
81
+ 'issueIdsOrKeys' => issue_datas.collect(&:key),
82
+ 'maxResults' => max_results
83
+ }
84
+ loop do
85
+ response = @jira_gateway.post_request(
86
+ relative_url: '/rest/api/3/changelog/bulkfetch',
87
+ payload: JSON.generate(payload)
88
+ )
89
+
90
+ response['issueChangeLogs'].each do |issue_change_log|
91
+ issue_id = issue_change_log['issueId']
92
+ json = issue_jsons.find { |json| json['id'] == issue_id }
93
+
94
+ unless json['changelog']
95
+ # If this is our first time in, there won't be a changelog section
96
+ json['changelog'] = {
97
+ 'startAt' => 0,
98
+ 'maxResults' => max_results,
99
+ 'total' => 0,
100
+ 'histories' => []
101
+ }
102
+ end
103
+
104
+ new_changes = issue_change_log['changeHistories']
105
+ json['changelog']['total'] += new_changes.size
106
+ json['changelog']['histories'] += new_changes
107
+ end
108
+
109
+ next_page_token = response['nextPageToken']
110
+ payload['nextPageToken'] = next_page_token
111
+ break if next_page_token.nil?
112
+ end
49
113
  end
50
114
  end
@@ -40,7 +40,29 @@ class DownloaderForDataCenter < Downloader
40
40
  hash
41
41
  end
42
42
 
43
- def issue_bulk_fetch_api
44
- '/rest/api/2/issue/bulkfetch'
43
+ def bulk_fetch_issues issue_datas:, board:, in_initial_query:
44
+ log " Downloading #{issue_datas.size} issues", both: true
45
+ payload = {
46
+ 'expand' => [
47
+ 'changelog'
48
+ ],
49
+ 'fields' => ['*all'],
50
+ 'issueIdsOrKeys' => issue_datas.collect(&:key)
51
+ }
52
+ response = @jira_gateway.post_request(
53
+ relative_url: '/rest/api/2/issue/bulkfetch',
54
+ payload: JSON.generate(payload)
55
+ )
56
+ response['issues'].each do |issue_json|
57
+ issue_json['exporter'] = {
58
+ 'in_initial_query' => in_initial_query
59
+ }
60
+ issue = Issue.new(raw: issue_json, board: board)
61
+ data = issue_datas.find { |d| d.key == issue.key }
62
+ data.up_to_date = true
63
+ data.last_modified = issue.updated
64
+ data.issue = issue
65
+ end
66
+ issue_datas
45
67
  end
46
68
  end
@@ -65,14 +65,14 @@ class Exporter
65
65
  puts "Full output from downloader in #{file_system.logfile_name}"
66
66
  end
67
67
 
68
- def info keys, name_filter:
68
+ def info key, name_filter:
69
69
  selected = []
70
70
  each_project_config(name_filter: name_filter) do |project|
71
71
  project.evaluate_next_level
72
72
 
73
73
  project.run load_only: true
74
74
  project.issues.each do |issue|
75
- selected << [project, issue] if keys.include? issue.key
75
+ selected << [project, issue] if key == issue.key
76
76
  end
77
77
  rescue => e # rubocop:disable Style/RescueStandardError
78
78
  # This happens when we're attempting to load an aggregated project because it hasn't been
@@ -81,7 +81,7 @@ class Exporter
81
81
  end
82
82
 
83
83
  if selected.empty?
84
- file_system.log "No issues found to match #{keys.collect(&:inspect).join(', ')}"
84
+ file_system.log "No issues found to match #{key.inspect}"
85
85
  else
86
86
  selected.each do |project, issue|
87
87
  file_system.log "\nProject #{project.name}", also_write_to_stderr: true
@@ -212,7 +212,11 @@ class Issue
212
212
  end
213
213
 
214
214
  def parse_time text
215
- Time.parse(text).getlocal(@timezone_offset)
215
+ if text.is_a? String
216
+ Time.parse(text).getlocal(@timezone_offset)
217
+ else
218
+ Time.at(text / 1000).getlocal(@timezone_offset)
219
+ end
216
220
  end
217
221
 
218
222
  def created
@@ -7,7 +7,7 @@ require 'open3'
7
7
 
8
8
  class JiraGateway
9
9
  attr_accessor :ignore_ssl_errors
10
- attr_reader :jira_url, :settings
10
+ attr_reader :jira_url, :settings, :file_system
11
11
 
12
12
  def initialize file_system:, jira_config:, settings:
13
13
  @file_system = file_system
@@ -18,15 +18,23 @@ class JiraGateway
18
18
 
19
19
  def post_request relative_url:, payload:
20
20
  command = make_curl_command url: "#{@jira_url}#{relative_url}", method: 'POST'
21
+ log_entry = " #{command.gsub(/\s+/, ' ')}"
22
+ log_entry = sanitize_message log_entry
23
+ @file_system.log log_entry
24
+
21
25
  stdout, stderr, status = Open3.capture3(command, stdin_data: payload)
22
- @file_system.log "Error: #{stderr}" unless stderr == ''
23
- raise 'no response' if stdout == ''
24
- return parse_response(command: command, result: stdout) if status.success?
26
+ unless status.success?
27
+ @file_system.log "Failed call with exit status #{status.exitstatus}!"
28
+ @file_system.log "Returned (stdout): #{stdout.inspect}"
29
+ @file_system.log "Returned (stderr): #{stderr.inspect}"
30
+ raise "Failed call with exit status #{status.exitstatus}. " \
31
+ "See #{@file_system.logfile_name} for details"
32
+ end
25
33
 
26
- @file_system.log result
27
- @file_system.log "Failed call with exit status #{status.exitstatus}."
28
- raise "Failed call with exit status #{status.exitstatus}. " \
29
- "See #{@file_system.logfile_name} for details"
34
+ @file_system.log "Returned (stderr): #{stderr}" unless stderr == ''
35
+ raise 'no response from curl on stdout' if stdout == ''
36
+
37
+ parse_response(command: command, result: stdout)
30
38
  end
31
39
 
32
40
  def call_url relative_url:
@@ -53,12 +61,12 @@ class JiraGateway
53
61
  token = @jira_api_token || @jira_personal_access_token
54
62
  raise 'Neither Jira API Token or personal access token has been set' unless token
55
63
 
56
- message.gsub(@jira_api_token, '[API_TOKEN]')
64
+ message.gsub(token, '[API_TOKEN]')
57
65
  end
58
66
 
59
67
  def call_command command
60
68
  log_entry = " #{command.gsub(/\s+/, ' ')}"
61
- log_entry = sanitize_message log_entry if @jira_api_token
69
+ log_entry = sanitize_message log_entry
62
70
  @file_system.log log_entry
63
71
 
64
72
  result = `#{command}`
data/lib/jirametrics.rb CHANGED
@@ -47,9 +47,9 @@ class JiraMetrics < Thor
47
47
 
48
48
  option :config
49
49
  desc 'info', 'Dump information about one issue'
50
- def info keys
50
+ def info key
51
51
  load_config options[:config]
52
- Exporter.instance.info(keys, name_filter: options[:name] || '*')
52
+ Exporter.instance.info(key, name_filter: options[:name] || '*')
53
53
  end
54
54
 
55
55
  no_commands do
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jirametrics
3
3
  version: !ruby/object:Gem::Version
4
- version: '2.17'
4
+ version: 2.17.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Bowler
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-09-20 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: random-word
@@ -159,7 +159,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
159
159
  - !ruby/object:Gem::Version
160
160
  version: '0'
161
161
  requirements: []
162
- rubygems_version: 3.6.2
162
+ rubygems_version: 3.7.2
163
163
  specification_version: 4
164
164
  summary: Extract Jira metrics
165
165
  test_files: []