ticket-replicator 1.1.0 → 1.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c470d25146cd29253a36275e23181f072d7131333f271f748ebe3560d9849825
4
- data.tar.gz: 72393510381f5d4abdd4aa3bccafbec872fc30f585ddee5fdf7a8ae4e0f28f2e
3
+ metadata.gz: '018e90cb8cce2dd15fdb8b766ec815f875a502b2404da79d5c11265b12d00ff9'
4
+ data.tar.gz: 5746059539911a89e647a9ef0b0de23dd060476ebaa55d5d79543354786fb25c
5
5
  SHA512:
6
- metadata.gz: d1460f599c98ef9bdd4c0852cbf9c0b3734a7cba3453c96c4ab25e7dbac7a6e3303cf3b051b2d54ac80f7296113e383fafd1236eb62b8c4b57fe1d1cf190dc12
7
- data.tar.gz: d641aa5a31b2efab1f6028383d534caf55336eeb49f43ceae446044df1cd0073873d6ca5f0b835d0581de049a402813a50a03478fdd81b9914259af079fbdadc
6
+ metadata.gz: 2f25722b36fb72167dd32f6d2a13a3855a8f3eaec98aa40ba2ac65b643ee4b318671d5236029856ebb89acf343bd9fdfa2588ddf387e8b91a71ea432ab84cc00
7
+ data.tar.gz: d301a6fe343826713be40f74cddc41fcd51a3b005009c16990b87e5e708e7312385a0925ed349668583556d50b8be2d45319fe407de5cf1ba0ca7328fb44c5c5
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.4.3
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # Ticket::Replicator
2
2
 
3
3
  [![Ruby](https://github.com/cbroult/ticket-replicator/actions/workflows/main.yml/badge.svg)](https://github.com/cbroult/ticket-replicator/actions/workflows/main.yml)
4
+ [![Dependabot Updates](https://github.com/cbroult/ticket-replicator/actions/workflows/dependabot/dependabot-updates/badge.svg)](https://github.com/cbroult/ticket-replicator/actions/workflows/dependabot/dependabot-updates)
4
5
 
5
6
  ## Purpose
6
7
 
@@ -50,7 +51,7 @@ Optional environment variables:
50
51
  - `JIRA_HTTP_DEBUG` - Enable HTTP debug logging (set to "true" or "false").
51
52
  - `JAT_RATE_INTERVAL_IN_SECONDS` - Interval for rate limiting in seconds (e.g., "1").
52
53
  - `JAT_RATE_LIMIT_PER_INTERVAL` - Rate limit per interval for Jira API calls (e.g., "1")
53
-
54
+ - `TICKET_REPLICATOR_DISABLE_RESOLUTION_LOADING` - To deal with tickets not having a resolution field.
54
55
 
55
56
  ### Setup Queue Folder And Field Mapping
56
57
 
@@ -4,12 +4,14 @@
4
4
  ### WARNING
5
5
  field_mapping:
6
6
  id: Defect
7
- summary: Defect (2)
7
+ summary: Defect
8
8
  priority: Defect Priority
9
9
  resolution: Defect Status
10
10
  status: Defect Status
11
11
  team: Defect Support Team (2)
12
12
 
13
+ id_extraction_regex: "\\((?<id>\\d+)\\)$"
14
+
13
15
  priority_mapping:
14
16
  "1: Critical": "Highest"
15
17
  "2: High": "High"
@@ -8,9 +8,6 @@ Feature: Load tickets into Jira
8
8
  | name |
9
9
  | TICKET_REPLICATOR_JIRA_PROJECT_KEY |
10
10
  | TICKET_REPLICATOR_JIRA_TICKET_TYPE_NAME |
11
- # And the following environment variables have been set:
12
- # | name | value |
13
- # | TICKET_REPLICATOR_SOURCE_TICKET_URL | http://url/to/source/ticket/<%= source_ticket_id %> |
14
11
  And the project has no tickets
15
12
 
16
13
  Scenario: Load tickets in Jira
@@ -40,6 +37,26 @@ Feature: Load tickets into Jira
40
37
  | Open | | Highest | SMAN-10008 \| Memory leak in caching implementation (10008) | http://url/to/source/ticket/10008 |
41
38
  | Wait on External | | Medium | SMAN-10009 \| Inconsistent button styles across modules (10009) | http://url/to/source/ticket/10009 |
42
39
 
40
+
41
+ Scenario: Load tickets in Jira w/o their resolution
42
+ Given the following environment variables have been set:
43
+ | name | value |
44
+ | TICKET_REPLICATOR_DISABLE_RESOLUTION_LOADING | true |
45
+ Given a file named "queue/20.transformed/sap_solution_manager_defects.csv" with:
46
+ """
47
+ "ID","Status","Resolution","Priority","Summary","Source Ticket URL"
48
+ "10001","Open","","Highest","SMAN-10001 | Login page randomly fails to load CSS assets (10001)","http://url/to/source/ticket/10001"
49
+ "10003","Confirmed","Done","Medium","SMAN-10003 | Invalid date format in SOAP response (10003)","http://url/to/source/ticket/10003"
50
+ "10007","Withdrawn","Won't Do","Low","SMAN-10007 | Test data missing edge case scenarios (10007)","http://url/to/source/ticket/10007"
51
+ """
52
+ When I successfully run `ticket-replicator --load`
53
+ Then the Jira project should only have the following tickets:
54
+ | status | resolution | priority | summary | source_ticket_url |
55
+ | Open | | Highest | SMAN-10001 \| Login page randomly fails to load CSS assets (10001) | http://url/to/source/ticket/10001 |
56
+ | Confirmed | | Medium | SMAN-10003 \| Invalid date format in SOAP response (10003) | http://url/to/source/ticket/10003 |
57
+ | Withdrawn | | Low | SMAN-10007 \| Test data missing edge case scenarios (10007) | http://url/to/source/ticket/10007 |
58
+
59
+
43
60
  Scenario: Loading the ticket information twice in Jira does not create additional tickets
44
61
  Given a file named "queue/20.transformed/sap_solution_manager_defects.csv" with:
45
62
  """
@@ -20,12 +20,14 @@ Feature: Setup Ticket Replicator
20
20
  ### WARNING
21
21
  field_mapping:
22
22
  id: Defect
23
- summary: Defect (2)
23
+ summary: Defect
24
24
  priority: Defect Priority
25
25
  resolution: Defect Status
26
26
  status: Defect Status
27
27
  team: Defect Support Team (2)
28
28
 
29
+ id_extraction_regex: "\\((?<id>\\d+)\\)$"
30
+
29
31
  priority_mapping:
30
32
  "1: Critical": "Highest"
31
33
  "2: High": "High"
@@ -5,12 +5,13 @@ Feature: Transform SAP tickets to Jira format
5
5
 
6
6
  Background:
7
7
  Given the following environment variables have been set:
8
- | name | value |
8
+ | name | value |
9
9
  | TICKET_REPLICATOR_SOURCE_TICKET_URL | http://url/to/source/ticket/<%= source_ticket_id %> |
10
10
 
11
11
  Scenario: Transform valid ticket CSV
12
12
  Given a file named "config/ticket-replicator.mappings.yml" with:
13
13
  """
14
+ ---
14
15
  field_mapping:
15
16
  id: ID
16
17
  summary: Summary
@@ -77,6 +78,7 @@ Feature: Transform SAP tickets to Jira format
77
78
  Scenario: Transform valid ticket Excel
78
79
  Given a file named "config/ticket-replicator.mappings.yml" with:
79
80
  """
81
+ ---
80
82
  field_mapping:
81
83
  id: Defect
82
84
  summary: Defect (2)
@@ -146,9 +148,60 @@ Feature: Transform SAP tickets to Jira format
146
148
  "3000018423","No Error","","Medium","SMAN-3000018423 | Summary","http://url/to/source/ticket/3000018423"
147
149
  """
148
150
 
151
+ Scenario: Transform with ID originally embedded into the summary
152
+ Given a file named "config/ticket-replicator.mappings.yml" with:
153
+ """
154
+ ---
155
+ field_mapping:
156
+ id: Defect (2)
157
+ summary: Defect (2)
158
+ priority: Defect Priority
159
+ resolution: Defect Status
160
+ status: Defect Status
161
+
162
+ id_extraction_regex: "\\((?<id>\\d+)\\)$"
163
+
164
+ priority_mapping:
165
+ "1: Critical": "Highest"
166
+ "2: High": "High"
167
+ "3: Medium": "Medium"
168
+ "4: Low": "Low"
169
+
170
+ status_mapping:
171
+ defaults_to: keep_original_value
172
+
173
+ resolution_mapping:
174
+ defaults_to: blank_value
175
+ "Fixed": "Done"
176
+ "Closed": "Done"
177
+ "Rejected": "Won't Do"
178
+ "Resolved": "Done"
179
+ "Withdrawn": "Cannot Reproduce"
180
+ """
181
+ And an Excel file named "queue/10.extracted/sap_solution_manager_defects.xlsx"
182
+ And it has a tab named "SAP Document Export" with the following rows:
183
+ | Defect (2) | Defect Priority | Defect Status |
184
+ | Summary (3000017049) | 3: Medium | Closed |
185
+ | Summary (9400011377) | 4: Low | Confirmed |
186
+ | Summary (3000016618) | 3: Medium | Defect Correction in Process |
187
+ | Summary (3000016617) | 3: Medium | No Error |
188
+ | Summary (3000017667) | 3: Medium | Closed |
189
+ Then a file named "queue/10.extracted/sap_solution_manager_defects.xlsx" should exist
190
+ When I successfully run `ticket-replicator --transform`
191
+ Then a file named "queue/20.transformed/sap_solution_manager_defects.csv" should contain exactly:
192
+ """
193
+ "ID","Status","Resolution","Priority","Summary","Source Ticket URL"
194
+ "3000017049","Closed","Done","Medium","SMAN-3000017049 | Summary (3000017049)","http://url/to/source/ticket/3000017049"
195
+ "9400011377","Confirmed","","Low","SMAN-9400011377 | Summary (9400011377)","http://url/to/source/ticket/9400011377"
196
+ "3000016618","Defect Correction in Process","","Medium","SMAN-3000016618 | Summary (3000016618)","http://url/to/source/ticket/3000016618"
197
+ "3000016617","No Error","","Medium","SMAN-3000016617 | Summary (3000016617)","http://url/to/source/ticket/3000016617"
198
+ "3000017667","Closed","Done","Medium","SMAN-3000017667 | Summary (3000017667)","http://url/to/source/ticket/3000017667"
199
+ """
200
+
149
201
  Scenario: Processing an invalid Excel file generates an error with relevant information
150
202
  Given a file named "config/ticket-replicator.mappings.yml" with:
151
203
  """
204
+ ---
152
205
  field_mapping:
153
206
  id: Defect
154
207
  summary: Defect (2)
@@ -184,6 +237,7 @@ Feature: Transform SAP tickets to Jira format
184
237
  Scenario: Processing a file with missing columns generates an error with relevant information
185
238
  Given a file named "config/ticket-replicator.mappings.yml" with:
186
239
  """
240
+ ---
187
241
  field_mapping:
188
242
  id: Defect
189
243
  summary: Defect (2)
@@ -223,6 +277,7 @@ Feature: Transform SAP tickets to Jira format
223
277
  Scenario: Processing a file with an unexpected priority generates an error with relevant information
224
278
  Given a file named "config/ticket-replicator.mappings.yml" with:
225
279
  """
280
+ ---
226
281
  field_mapping:
227
282
  id: Defect
228
283
  summary: Defect (2)
@@ -56,6 +56,7 @@ module Ticket
56
56
  #{extracted_path}:#{row_index}: error while transforming row:
57
57
  #{e.message}:
58
58
  #{row.inspect}
59
+ #{e.backtrace.join("\n")}
59
60
  EOERRORMSG
60
61
  ensure
61
62
  row_index += 1
@@ -9,6 +9,13 @@ module Ticket
9
9
  new(jira_project, row).run
10
10
  end
11
11
 
12
+ def self.build_ticket_link_attributes(
13
+ url, title, application_name = Ticket::SOURCE_TICKET_REMOTE_LINK_APPLICATION
14
+ )
15
+ { "object" => { "url" => url, "title" => title } }
16
+ .merge(application_name ? { "application" => { "name" => application_name } } : {})
17
+ end
18
+
12
19
  private_class_method :new
13
20
 
14
21
  attr_reader :jira_project, :row
@@ -28,23 +35,35 @@ module Ticket
28
35
  jira_project.replicated_tickets.key?(id)
29
36
  end
30
37
 
31
- # rubocop:disable Metrics/MethodLength
32
38
  def save_ticket
33
39
  return unless ticket_fields_need_to_be_updated?
34
40
 
35
- ticket.jira_ticket.save!({
36
- fields: {
37
- project: { key: jira_project.project_key },
38
- issuetype: { name: jira_project.ticket_type_name },
39
- resolution: jira_resolution_value,
40
- priority: { name: priority },
41
- summary: summary
42
- }
43
- })
41
+ ticket.jira_ticket.save!(attributes_for_save)
44
42
 
45
43
  ticket.jira_ticket.fetch
46
44
  end
47
45
 
46
+ private
47
+
48
+ def attributes_for_save
49
+ {
50
+ fields: field_values
51
+ }
52
+ end
53
+
54
+ def field_values
55
+ {
56
+ project: { key: jira_project.project_key },
57
+ issuetype: { name: jira_project.ticket_type_name },
58
+ priority: { name: priority },
59
+ summary: summary
60
+ }.merge(exclude_resolution? ? {} : { resolution: jira_resolution_value })
61
+ end
62
+
63
+ def exclude_resolution?
64
+ ENV["TICKET_REPLICATOR_DISABLE_RESOLUTION_LOADING"] == "true"
65
+ end
66
+
48
67
  def jira_resolution_value
49
68
  return if resolution.blank?
50
69
  return { name: resolution } if jira_project.resolutions.key?(resolution)
@@ -57,8 +76,6 @@ module Ticket
57
76
  nil
58
77
  end
59
78
 
60
- # rubocop:enable Metrics/MethodLength
61
-
62
79
  def ticket_fields_need_to_be_updated?
63
80
  !ticket_previously_replicated? || ticket_fields_changed?
64
81
  end
@@ -68,7 +85,7 @@ module Ticket
68
85
  end
69
86
 
70
87
  def fields_to_save
71
- %i[summary priority]
88
+ %i[summary priority] + (exclude_resolution? ? [] : %i[resolution])
72
89
  end
73
90
 
74
91
  def update_source_ticket_remote_link
@@ -94,7 +111,7 @@ module Ticket
94
111
 
95
112
  existing_ticket_link_attributes = #{existing_ticket_link_attributes.inspect}
96
113
  filtered_existing_ticket_link_attributes = #{filtered_existing_ticket_link_attributes}
97
- ticket_link_attributes: #{ticket_link_attributes.inspect}
114
+ ticket_link_attributes = #{ticket_link_attributes.inspect}
98
115
  EOLOG
99
116
  end
100
117
 
@@ -111,13 +128,6 @@ module Ticket
111
128
  self.class.build_ticket_link_attributes(url, title)
112
129
  end
113
130
 
114
- def self.build_ticket_link_attributes(
115
- url, title, application_name = Ticket::SOURCE_TICKET_REMOTE_LINK_APPLICATION
116
- )
117
- { "object" => { "url" => url, "title" => title } }
118
- .merge(application_name ? { "application" => { "name" => application_name } } : {})
119
- end
120
-
121
131
  def transition_ticket_to_the_expected_status
122
132
  ticket.transition_to(status)
123
133
  end
@@ -139,8 +149,6 @@ module Ticket
139
149
  Replicator::Ticket.new(jira_project.jira_auto_tool, jira_project.jira_client.Issue.build)
140
150
  end
141
151
 
142
- private
143
-
144
152
  def method_missing(name, *args)
145
153
  if field_to_load?(name)
146
154
  row.fetch(name) { |key| raise "No value found for #{key.inspect} in #{row.inspect}" }
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "ticket/replicator/replicated_summary"
4
+ require "ticket/replicator/file_transformer"
4
5
  require "erb"
5
6
 
6
7
  module Ticket
@@ -23,9 +24,30 @@ module Ticket
23
24
  end
24
25
 
25
26
  def transformed_id
26
- remapped_field_extracted_value_for :id
27
+ remapped_id = remapped_field_extracted_value_for :id
28
+
29
+ return remapped_id unless id_extraction_regex
30
+
31
+ match_data = id_extraction_regex.match(remapped_id)
32
+
33
+ return match_data[:id] if match_data
34
+
35
+ raise FileTransformer::TransformError,
36
+ "No match found for :id in #{remapped_id.inspect} using #{id_extraction_regex.inspect}"
37
+ end
38
+
39
+ private
40
+
41
+ def id_extraction_regex
42
+ @id_extraction_regex ||=
43
+ begin
44
+ regex_string = mappings["id_extraction_regex"]
45
+ Regexp.new(regex_string) if regex_string
46
+ end
27
47
  end
28
48
 
49
+ public
50
+
29
51
  def transformed_source_ticket_url
30
52
  source_ticket_url_builder(transformed_id)
31
53
  end
@@ -55,7 +77,7 @@ module Ticket
55
77
  end
56
78
 
57
79
  def transformed_summary
58
- ReplicatedSummary.build(remapped_field_extracted_value_for(:id),
80
+ ReplicatedSummary.build(transformed_id,
59
81
  remapped_field_extracted_value_for(:summary)).to_s
60
82
  end
61
83
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Ticket
4
4
  class Replicator
5
- VERSION = "1.1.0"
5
+ VERSION = "1.2.0"
6
6
  end
7
7
  end
@@ -70,11 +70,9 @@ module Ticket
70
70
  end
71
71
 
72
72
  it "raises an error message including the file name and line number of the row that was processed" do
73
- expect { transformer.transformed_rows }.to raise_error(TransformError, <<~EOEXPECTEDERRORMSG)
74
- source.csv:3: error while transforming row:
75
- transformation error:
76
- :another_row
77
- EOEXPECTEDERRORMSG
73
+ expect { transformer.transformed_rows }
74
+ .to raise_error(TransformError,
75
+ /source\.csv:3: error while transforming row:\ntransformation error:\n:another_row\n/)
78
76
  end
79
77
  end
80
78
  end