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.
@@ -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 ||= RowTransformer.fields_to_transform.collect { |field| field.to_s.downcase.to_sym }
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.downcase}") }
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(remapped_field_extracted_value_for(:id),
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 ||= fields_to_transform.to_h do |field|
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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Ticket
4
4
  class Replicator
5
- VERSION = "1.0.0"
5
+ VERSION = "1.2.0"
6
6
  end
7
7
  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).to eq(%w[ID Status Resolution Priority Summary])
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 }.to raise_error(TransformError, <<~EOEXPECTEDERRORMSG)
73
- source.csv:3: error while transforming row:
74
- transformation error:
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