ticket-replicator 1.0.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/.rubocop.yml +5 -0
- data/.ruby-version +1 -0
- data/README.md +9 -9
- data/config/examples/ticket-replicator.mappings.yml +3 -1
- data/features/load_tickets_in_jira.feature +82 -69
- data/features/setup_ticket_replicator.feature +3 -1
- data/features/transform-solution-manager-tickets-into-jira-loadable-tickets.feature +86 -32
- data/features/transform_and_load_extracted_ticket_queue.feature +18 -14
- data/lib/ticket/replicator/file_transformer.rb +1 -0
- data/lib/ticket/replicator/row_loader.rb +73 -16
- data/lib/ticket/replicator/row_transformer.rb +47 -4
- data/lib/ticket/replicator/ticket.rb +17 -0
- data/lib/ticket/replicator/version.rb +1 -1
- data/spec/ticket/replicator/file_transformer_spec.rb +5 -6
- data/spec/ticket/replicator/row_loader_spec.rb +270 -40
- data/spec/ticket/replicator/row_transformer_spec.rb +280 -185
- data/spec/ticket/replicator/ticket_spec.rb +50 -1
- metadata +2 -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
|
@@ -20,6 +27,7 @@ module Ticket
|
|
20
27
|
|
21
28
|
def run
|
22
29
|
save_ticket
|
30
|
+
update_source_ticket_remote_link
|
23
31
|
transition_ticket_to_the_expected_status
|
24
32
|
end
|
25
33
|
|
@@ -27,23 +35,35 @@ module Ticket
|
|
27
35
|
jira_project.replicated_tickets.key?(id)
|
28
36
|
end
|
29
37
|
|
30
|
-
# rubocop:disable Metrics/MethodLength
|
31
38
|
def save_ticket
|
32
39
|
return unless ticket_fields_need_to_be_updated?
|
33
40
|
|
34
|
-
ticket.jira_ticket.save!(
|
35
|
-
fields: {
|
36
|
-
project: { key: jira_project.project_key },
|
37
|
-
issuetype: { name: jira_project.ticket_type_name },
|
38
|
-
resolution: jira_resolution_value,
|
39
|
-
priority: { name: priority },
|
40
|
-
summary: summary
|
41
|
-
}
|
42
|
-
})
|
41
|
+
ticket.jira_ticket.save!(attributes_for_save)
|
43
42
|
|
44
43
|
ticket.jira_ticket.fetch
|
45
44
|
end
|
46
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
|
+
|
47
67
|
def jira_resolution_value
|
48
68
|
return if resolution.blank?
|
49
69
|
return { name: resolution } if jira_project.resolutions.key?(resolution)
|
@@ -56,8 +76,6 @@ module Ticket
|
|
56
76
|
nil
|
57
77
|
end
|
58
78
|
|
59
|
-
# rubocop:enable Metrics/MethodLength
|
60
|
-
|
61
79
|
def ticket_fields_need_to_be_updated?
|
62
80
|
!ticket_previously_replicated? || ticket_fields_changed?
|
63
81
|
end
|
@@ -67,7 +85,47 @@ module Ticket
|
|
67
85
|
end
|
68
86
|
|
69
87
|
def fields_to_save
|
70
|
-
%i[summary priority]
|
88
|
+
%i[summary priority] + (exclude_resolution? ? [] : %i[resolution])
|
89
|
+
end
|
90
|
+
|
91
|
+
def update_source_ticket_remote_link
|
92
|
+
return unless source_ticket_link_needs_update?
|
93
|
+
|
94
|
+
ticket.source_ticket_link&.delete
|
95
|
+
|
96
|
+
ticket.jira_ticket.remotelink.build.save!(ticket_link_attributes)
|
97
|
+
end
|
98
|
+
|
99
|
+
# rubocop:disable Metrics/MethodLength
|
100
|
+
def source_ticket_link_needs_update?
|
101
|
+
existing_ticket_link_attributes = ticket.source_ticket_link&.attrs
|
102
|
+
|
103
|
+
object = existing_ticket_link_attributes&.[]("object")
|
104
|
+
existing_url = object&.[]("url")
|
105
|
+
existing_title = object&.[]("title")
|
106
|
+
|
107
|
+
filtered_existing_ticket_link_attributes = ticket_link_attributes(existing_url, existing_title)
|
108
|
+
|
109
|
+
log.debug do
|
110
|
+
<<~EOLOG
|
111
|
+
|
112
|
+
existing_ticket_link_attributes = #{existing_ticket_link_attributes.inspect}
|
113
|
+
filtered_existing_ticket_link_attributes = #{filtered_existing_ticket_link_attributes}
|
114
|
+
ticket_link_attributes = #{ticket_link_attributes.inspect}
|
115
|
+
EOLOG
|
116
|
+
end
|
117
|
+
|
118
|
+
filtered_existing_ticket_link_attributes != ticket_link_attributes
|
119
|
+
end
|
120
|
+
|
121
|
+
# rubocop:enable Metrics/MethodLength
|
122
|
+
|
123
|
+
def source_ticket_link_title
|
124
|
+
"Source Ticket #{id}"
|
125
|
+
end
|
126
|
+
|
127
|
+
def ticket_link_attributes(url = source_ticket_url, title = source_ticket_link_title)
|
128
|
+
self.class.build_ticket_link_attributes(url, title)
|
71
129
|
end
|
72
130
|
|
73
131
|
def transition_ticket_to_the_expected_status
|
@@ -91,8 +149,6 @@ module Ticket
|
|
91
149
|
Replicator::Ticket.new(jira_project.jira_auto_tool, jira_project.jira_client.Issue.build)
|
92
150
|
end
|
93
151
|
|
94
|
-
private
|
95
|
-
|
96
152
|
def method_missing(name, *args)
|
97
153
|
if field_to_load?(name)
|
98
154
|
row.fetch(name) { |key| raise "No value found for #{key.inspect} in #{row.inspect}" }
|
@@ -115,7 +171,8 @@ module Ticket
|
|
115
171
|
|
116
172
|
class << self
|
117
173
|
def fields_to_load
|
118
|
-
@fields_to_load ||=
|
174
|
+
@fields_to_load ||=
|
175
|
+
RowTransformer.fields_to_transform.collect { |field| field.to_s.parameterize.underscore.to_sym }
|
119
176
|
end
|
120
177
|
end
|
121
178
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "ticket/replicator/replicated_summary"
|
4
|
+
require "ticket/replicator/file_transformer"
|
5
|
+
require "erb"
|
4
6
|
|
5
7
|
module Ticket
|
6
8
|
class Replicator
|
@@ -18,11 +20,44 @@ module Ticket
|
|
18
20
|
end
|
19
21
|
|
20
22
|
def run
|
21
|
-
fields_to_transform.collect { |field| send("transformed_#{field.to_s.
|
23
|
+
fields_to_transform.collect { |field| send("transformed_#{field.to_s.parameterize.underscore}") }
|
22
24
|
end
|
23
25
|
|
24
26
|
def transformed_id
|
25
|
-
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
|
47
|
+
end
|
48
|
+
|
49
|
+
public
|
50
|
+
|
51
|
+
def transformed_source_ticket_url
|
52
|
+
source_ticket_url_builder(transformed_id)
|
53
|
+
end
|
54
|
+
|
55
|
+
def source_ticket_url_builder(source_ticket_id)
|
56
|
+
ERB.new(ticket_replicator_source_ticket_url).result(binding)
|
57
|
+
end
|
58
|
+
|
59
|
+
def ticket_replicator_source_ticket_url
|
60
|
+
ENV.fetch("TICKET_REPLICATOR_SOURCE_TICKET_URL") { |name| raise "#{name}: not set in environment!" }
|
26
61
|
end
|
27
62
|
|
28
63
|
def transformed_status
|
@@ -42,7 +77,7 @@ module Ticket
|
|
42
77
|
end
|
43
78
|
|
44
79
|
def transformed_summary
|
45
|
-
ReplicatedSummary.build(
|
80
|
+
ReplicatedSummary.build(transformed_id,
|
46
81
|
remapped_field_extracted_value_for(:summary)).to_s
|
47
82
|
end
|
48
83
|
|
@@ -50,7 +85,15 @@ module Ticket
|
|
50
85
|
self.class.fields_to_transform
|
51
86
|
end
|
52
87
|
|
88
|
+
def fields_to_remap
|
89
|
+
self.class.fields_to_remap
|
90
|
+
end
|
91
|
+
|
53
92
|
def self.fields_to_transform
|
93
|
+
fields_to_remap + ["Source Ticket URL"]
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.fields_to_remap
|
54
97
|
%i[ID Status Resolution Priority Summary]
|
55
98
|
end
|
56
99
|
|
@@ -77,7 +120,7 @@ module Ticket
|
|
77
120
|
end
|
78
121
|
|
79
122
|
def remapped_field_extracted_row
|
80
|
-
@remapped_field_extracted_row ||=
|
123
|
+
@remapped_field_extracted_row ||= fields_to_remap.to_h do |field|
|
81
124
|
mapped_field_key = remapped_field_key(field)
|
82
125
|
[mapped_field_key, extracted_row_value_for(mapped_field_key)]
|
83
126
|
end
|
@@ -36,8 +36,25 @@ module Ticket
|
|
36
36
|
ReplicatedSummary.parse(summary).source_id
|
37
37
|
end
|
38
38
|
|
39
|
+
SOURCE_TICKET_REMOTE_LINK_APPLICATION = "Ticket Source"
|
40
|
+
|
41
|
+
def source_ticket_link
|
42
|
+
@source_ticket_link ||=
|
43
|
+
jira_ticket.remotelink.all.find do |link|
|
44
|
+
source_ticket_link_attributes(link)&.[]("application")&.[]("name") == SOURCE_TICKET_REMOTE_LINK_APPLICATION
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def source_ticket_url
|
49
|
+
source_ticket_link_attributes(source_ticket_link)&.[]("object")&.[]("url")
|
50
|
+
end
|
51
|
+
|
39
52
|
private
|
40
53
|
|
54
|
+
def source_ticket_link_attributes(link)
|
55
|
+
link&.attrs
|
56
|
+
end
|
57
|
+
|
41
58
|
def cmp_values(object)
|
42
59
|
object&.source_id
|
43
60
|
end
|
@@ -43,7 +43,8 @@ module Ticket
|
|
43
43
|
|
44
44
|
describe "#transformed_headers" do
|
45
45
|
it "returns the headers" do
|
46
|
-
expect(transformer.transformed_headers)
|
46
|
+
expect(transformer.transformed_headers)
|
47
|
+
.to eq(%w[ID Status Resolution Priority Summary] + ["Source Ticket URL"])
|
47
48
|
end
|
48
49
|
end
|
49
50
|
|
@@ -69,11 +70,9 @@ module Ticket
|
|
69
70
|
end
|
70
71
|
|
71
72
|
it "raises an error message including the file name and line number of the row that was processed" do
|
72
|
-
expect { transformer.transformed_rows }
|
73
|
-
|
74
|
-
|
75
|
-
:another_row
|
76
|
-
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/)
|
77
76
|
end
|
78
77
|
end
|
79
78
|
end
|