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 +4 -4
- data/.ruby-version +1 -0
- data/README.md +2 -1
- data/config/examples/ticket-replicator.mappings.yml +3 -1
- data/features/load_tickets_in_jira.feature +20 -3
- data/features/setup_ticket_replicator.feature +3 -1
- data/features/transform-solution-manager-tickets-into-jira-loadable-tickets.feature +56 -1
- data/lib/ticket/replicator/file_transformer.rb +1 -0
- data/lib/ticket/replicator/row_loader.rb +31 -23
- data/lib/ticket/replicator/row_transformer.rb +24 -2
- data/lib/ticket/replicator/version.rb +1 -1
- data/spec/ticket/replicator/file_transformer_spec.rb +3 -5
- data/spec/ticket/replicator/row_loader_spec.rb +126 -39
- data/spec/ticket/replicator/row_transformer_spec.rb +268 -228
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '018e90cb8cce2dd15fdb8b766ec815f875a502b2404da79d5c11265b12d00ff9'
|
4
|
+
data.tar.gz: 5746059539911a89e647a9ef0b0de23dd060476ebaa55d5d79543354786fb25c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
[](https://github.com/cbroult/ticket-replicator/actions/workflows/main.yml)
|
4
|
+
[](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
|
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
|
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)
|
@@ -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
|
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(
|
80
|
+
ReplicatedSummary.build(transformed_id,
|
59
81
|
remapped_field_extracted_value_for(:summary)).to_s
|
60
82
|
end
|
61
83
|
|
@@ -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 }
|
74
|
-
|
75
|
-
|
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
|