gitlab_support_readiness 1.0.11 → 1.0.12

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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/lib/support_readiness/calendly/client.rb +64 -0
  3. data/lib/support_readiness/calendly/configuration.rb +45 -0
  4. data/lib/support_readiness/calendly/organization_memberships.rb +67 -0
  5. data/lib/support_readiness/calendly/organizations.rb +123 -0
  6. data/lib/support_readiness/calendly/users.rb +105 -0
  7. data/lib/support_readiness/calendly.rb +16 -0
  8. data/lib/support_readiness/client.rb +88 -1
  9. data/lib/support_readiness/dates.rb +90 -0
  10. data/lib/support_readiness/gitlab/configuration.rb +0 -1
  11. data/lib/support_readiness/gitlab/groups.rb +35 -0
  12. data/lib/support_readiness/gitlab/markdown.rb +37 -0
  13. data/lib/support_readiness/gitlab/merge_requests.rb +127 -0
  14. data/lib/support_readiness/gitlab/namespaces.rb +22 -0
  15. data/lib/support_readiness/gitlab/projects.rb +36 -1
  16. data/lib/support_readiness/gitlab/repositories.rb +174 -0
  17. data/lib/support_readiness/gitlab/users.rb +24 -0
  18. data/lib/support_readiness/gitlab.rb +2 -0
  19. data/lib/support_readiness/google_calendar/client.rb +59 -0
  20. data/lib/support_readiness/google_calendar/configuration.rb +39 -0
  21. data/lib/support_readiness/google_calendar/events.rb +169 -0
  22. data/lib/support_readiness/google_calendar.rb +14 -0
  23. data/lib/support_readiness/mailgun/bounces.rb +103 -0
  24. data/lib/support_readiness/mailgun/client.rb +65 -0
  25. data/lib/support_readiness/mailgun/configuration.rb +46 -0
  26. data/lib/support_readiness/mailgun/emails.rb +95 -0
  27. data/lib/support_readiness/mailgun.rb +15 -0
  28. data/lib/support_readiness/pagerduty/escalation_policies.rb +1 -1
  29. data/lib/support_readiness/pagerduty/schedules.rb +27 -3
  30. data/lib/support_readiness/pagerduty/services.rb +1 -1
  31. data/lib/support_readiness/pagerduty/users.rb +197 -0
  32. data/lib/support_readiness/pagerduty.rb +1 -0
  33. data/lib/support_readiness/repos/articles.rb +335 -0
  34. data/lib/support_readiness/repos/automations.rb +247 -0
  35. data/lib/support_readiness/repos/groups.rb +188 -0
  36. data/lib/support_readiness/repos/macros.rb +224 -0
  37. data/lib/support_readiness/repos/organization_fields.rb +193 -0
  38. data/lib/support_readiness/repos/sla_policies.rb +192 -0
  39. data/lib/support_readiness/repos/ticket_fields.rb +200 -0
  40. data/lib/support_readiness/repos/ticket_forms.rb +200 -0
  41. data/lib/support_readiness/repos/triggers.rb +255 -0
  42. data/lib/support_readiness/repos/user_fields.rb +201 -0
  43. data/lib/support_readiness/repos/views.rb +362 -0
  44. data/lib/support_readiness/repos.rb +22 -0
  45. data/lib/support_readiness/salesforce/accounts.rb +109 -0
  46. data/lib/support_readiness/salesforce/cases.rb +109 -0
  47. data/lib/support_readiness/salesforce/client.rb +64 -0
  48. data/lib/support_readiness/salesforce/configuration.rb +49 -0
  49. data/lib/support_readiness/salesforce/queries.rb +62 -0
  50. data/lib/support_readiness/salesforce.rb +16 -0
  51. data/lib/support_readiness/slack/client.rb +63 -0
  52. data/lib/support_readiness/slack/configuration.rb +43 -0
  53. data/lib/support_readiness/slack/messages.rb +37 -0
  54. data/lib/support_readiness/slack.rb +14 -0
  55. data/lib/support_readiness/zendesk/app_job_statuses.rb +140 -0
  56. data/lib/support_readiness/zendesk/apps.rb +209 -0
  57. data/lib/support_readiness/zendesk/automations.rb +1 -2
  58. data/lib/support_readiness/zendesk/macros.rb +1 -3
  59. data/lib/support_readiness/zendesk/organization_fields.rb +1 -1
  60. data/lib/support_readiness/zendesk/theme_job_statuses.rb +136 -0
  61. data/lib/support_readiness/zendesk/themes.rb +303 -0
  62. data/lib/support_readiness/zendesk/ticket_field_options.rb +110 -0
  63. data/lib/support_readiness/zendesk/ticket_fields.rb +85 -16
  64. data/lib/support_readiness/zendesk/ticket_forms.rb +65 -2
  65. data/lib/support_readiness/zendesk/tickets.rb +77 -0
  66. data/lib/support_readiness/zendesk/triggers.rb +1 -2
  67. data/lib/support_readiness/zendesk/user_field_options.rb +110 -0
  68. data/lib/support_readiness/zendesk/user_fields.rb +257 -0
  69. data/lib/support_readiness/zendesk/views.rb +49 -2
  70. data/lib/support_readiness/zendesk.rb +7 -0
  71. data/lib/support_readiness.rb +16 -0
  72. metadata +120 -2
@@ -0,0 +1,247 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Defines the module Readiness.
4
+ module Readiness
5
+ # Defines the module Repos
6
+ module Repos
7
+ ##
8
+ # Defines the class Automations within the module {Readiness::Repos}.
9
+ #
10
+ # @author Jason Colyer
11
+ # @since 1.0.12
12
+ class Automations < Readiness::Client
13
+ ##
14
+ # Compares the repo automation files to the Zendesk instance automations
15
+ #
16
+ # @author Jason Colyer
17
+ # @since 1.0.12
18
+ # @param zendesk_client [Object] An instance of {Readiness::Zendesk::Client}
19
+ # @param location [String] The location (relative or absolute) of the repo's data folder
20
+ # @param verbose [Boolean] Whether you want a detailed report or not
21
+ # @return [Hash]
22
+ # @example
23
+ # require 'support_readiness'
24
+ # config = Readiness::Zendesk::Configuration.new
25
+ # config.username = 'alice@example.com'
26
+ # config.token = 'test123abc'
27
+ # config.url = 'https://example.zendesk.com/api/v2'
28
+ # client = Readiness::Zendesk::Client.new(config)
29
+ # diffs = Readiness::Repos::Automations.compare(client, 'automations/data', false)
30
+ # pp diffs[:updates.count]
31
+ # # => 5
32
+ # @example
33
+ # require 'support_readiness'
34
+ # config = Readiness::Zendesk::Configuration.new
35
+ # config.username = 'alice@example.com'
36
+ # config.token = 'test123abc'
37
+ # config.url = 'https://example.zendesk.com/api/v2'
38
+ # client = Readiness::Zendesk::Client.new(config)
39
+ # diffs = Readiness::Repos::Automations.compare(client, 'automations/data', true)
40
+ # # => Detailed diff of Status::Close::Close solved tickets after 7 days
41
+ # # => - @default Repo:
42
+ # # => false
43
+ # # => @default Zendesk:
44
+ # # => true
45
+ # # => Compare report:
46
+ # # => - Creates: 0
47
+ # # => - Updates: 1
48
+ # pp diffs[:updates.count]
49
+ # # => 1
50
+ def self.compare(zendesk_client, location = 'data', verbose = false)
51
+ diffs = {
52
+ updates: [],
53
+ creates: []
54
+ }
55
+ from_repo = gather(location)
56
+ from_zendesk = Zendesk::Automations.list(zendesk_client)
57
+ from_repo.each do |repo|
58
+ zd = from_zendesk.detect { |z| z.title == repo.title }
59
+ if zd.nil?
60
+ diffs[:creates].push(repo)
61
+ else
62
+ comparable = zd.dup
63
+ comparable.id = nil
64
+ diffs[:updates].push(update_object(repo, zd)) if to_clean_json(repo) != to_clean_json(comparable)
65
+ detailed_diff(repo, comparable) if verbose && to_clean_json(repo) != to_clean_json(comparable)
66
+ end
67
+ end
68
+ report_diffs(diffs) if verbose
69
+ diffs
70
+ end
71
+
72
+ ##
73
+ # Outputs a comparison report
74
+ #
75
+ # @author Jason Colyer
76
+ # @since 1.0.12
77
+ # @param diffs [Hash] The returned value of {compare}
78
+ def self.report_diffs(diffs)
79
+ puts 'Compare report:'
80
+ puts "- Creates: #{diffs[:creates].count}"
81
+ puts "- Updates: #{diffs[:updates].count}"
82
+ end
83
+
84
+ ##
85
+ # Outputs a comparison report
86
+ #
87
+ # @author Jason Colyer
88
+ # @since 1.0.12
89
+ # @param repo [Object] An instance of {Readiness::Zendesk::Automations}
90
+ # @param zendesk [Object] An instance of {Readiness::Zendesk::Automations}
91
+ def self.detailed_diff(repo, zendesk)
92
+ puts "Detailed diff of #{repo.title}"
93
+ repo.instance_variables.each do |v|
94
+ next if v == :@id
95
+
96
+ if repo.instance_variable_get(v) != zendesk.instance_variable_get(v)
97
+ puts "- #{v} Repo:"
98
+ pp repo.instance_variable_get(v)
99
+ puts " #{v} Zendesk:"
100
+ pp zendesk.instance_variable_get(v)
101
+ end
102
+ end
103
+ end
104
+
105
+ ##
106
+ # Creates an instance of {Readiness::Zendesk::Automations} to use for updates
107
+ #
108
+ # @author Jason Colyer
109
+ # @since 1.0.12
110
+ # @param repo [Object] An instance of {Readiness::Zendesk::Automations}
111
+ # @param zendesk [Object] An instance of {Readiness::Zendesk::Automations}
112
+ # @return [Object]
113
+ def self.update_object(repo, zendesk)
114
+ object = repo
115
+ object.id = zendesk.id
116
+ object
117
+ end
118
+
119
+ ##
120
+ # Parses repo automation files
121
+ #
122
+ # @author Jason Colyer
123
+ # @since 1.0.12
124
+ # @param location [String] The location (relative or absolute) of the repo's data folder
125
+ # @return [Array]
126
+ # @example
127
+ # require 'support_readiness'
128
+ # repo = Readiness::Repos::Automations.gather('automations/data')
129
+ # pp repo.count
130
+ # # => 35
131
+ def self.gather(location = 'data')
132
+ @errors = []
133
+ @location = location
134
+ array = []
135
+ Dir["#{@location}/**/*.yaml"].each do |f|
136
+ object = YAML.safe_load_file(f)
137
+ object = convert_managed_content(object) if object['contains_managed_content']
138
+ object = convert_managed_webhook(object) if object['contains_managed_webhook']
139
+ validity_check(f, object)
140
+ object['id'] = nil
141
+ array.push(Zendesk::Automations.new(object))
142
+ end
143
+ repo_check(array)
144
+ report_errors unless @errors.count.zero?
145
+ array
146
+ end
147
+
148
+ ##
149
+ # Outputs an error report and exits with a status code of 1
150
+ #
151
+ # @author Jason Colyer
152
+ # @since 1.0.12
153
+ def self.report_errors
154
+ puts 'The following errors were found in the repo files:'
155
+ @errors.each do |e|
156
+ puts "- #{e}"
157
+ end
158
+ puts 'Rectify the errors and retry. We cannot proceed with those errors occurring.'
159
+ exit 1
160
+ end
161
+
162
+ ##
163
+ # Performs basic checks on a repo file
164
+ #
165
+ # @author Jason Colyer
166
+ # @since 1.0.12
167
+ # @param file [String] The path to the repo file
168
+ # @param object [Hash] The Hash derived from parsing a YAML file
169
+ def self.validity_check(file, object)
170
+ folder = file.split("#{@location}/").last.split('/').first
171
+ @errors.push("Missing position: #{file}") if object['position'].nil?
172
+ @errors.push("Missing title: #{file}") if object['title'].nil?
173
+ @errors.push("Inactive automation in active folder: #{file}") if folder == 'active' && !object['active']
174
+ @errors.push("Active automation in inactive folder: #{file}") if folder == 'inactive' && object['active']
175
+ end
176
+
177
+ ##
178
+ # Performs basic checks on the repo
179
+ #
180
+ # @author Jason Colyer
181
+ # @since 1.0.12
182
+ # @param objects [Array] The Array of Hashes derived from parsing the repo files
183
+ def self.repo_check(objects)
184
+ duplicate_names = objects.group_by { |o| o.title }.select { |k, v| v.size > 1 }.map(&:first)
185
+ unless duplicate_names.count.zero?
186
+ duplicate_names.each do |d|
187
+ @errors.push("The title '#{d}' is used in multiple files")
188
+ end
189
+ end
190
+ end
191
+
192
+ ##
193
+ # Performs conversions for a repo file if using managed content
194
+ #
195
+ # @author Jason Colyer
196
+ # @since 1.0.12
197
+ # @param object [Hash] The Hash derived from parsing a YAML file
198
+ def self.convert_managed_content(object)
199
+ path = "#{@location}/managed_content/#{object['title']}.md"
200
+ handle_request_error(1, 'Repos', 404, { action: 'Get managed content', id: object['title'] }) unless File.exist?(path)
201
+ object['actions'].each_with_index do |action, index|
202
+ if action['value'].is_a?(String)
203
+ if action['value'] =~ /MANAGED_CONTENT/
204
+ if action['value'] == 'MANAGED_CONTENT'
205
+ object['actions'][index]['value'] = action['value'].gsub('MANAGED_CONTENT', File.read(path).chomp)
206
+ else
207
+ object['actions'][index]['value'] = action['value'].gsub('MANAGED_CONTENT', File.read(path).chomp.gsub("\n", '\\n'))
208
+ end
209
+ end
210
+ elsif action['value'].is_a?(Array)
211
+ action['value'].each_with_index do |value, sub_index|
212
+ if value =~ /MANAGED_CONTENT/
213
+ if value == 'MANAGED_CONTENT'
214
+ object['actions'][index]['value'][sub_index] = value.gsub('MANAGED_CONTENT', File.read(path).chomp)
215
+ else
216
+ object['actions'][index]['value'][sub_index] = value.gsub('MANAGED_CONTENT', File.read(path).chomp.gsub("\n", '\\n'))
217
+ end
218
+ end
219
+ end
220
+ end
221
+ end
222
+ object
223
+ end
224
+
225
+ ##
226
+ # Performs conversions for a repo file if using managed webhooks
227
+ #
228
+ # @author Jason Colyer
229
+ # @since 1.0.12
230
+ # @param object [Hash] The Hash derived from parsing a YAML file
231
+ def self.convert_managed_webhook(object)
232
+ path = "#{@location}/managed_content/#{object['title']}.webhook"
233
+ handle_request_error(1, 'Repos', 404, { action: 'Get managed webhook', id: object['title'] }) unless File.exist?(path)
234
+ object['actions'].each_with_index do |action, index|
235
+ next unless action['value'].is_a?(Array)
236
+
237
+ action['value'].each_with_index do |value, sub_index|
238
+ next unless value == 'MANAGED_WEBHOOK'
239
+
240
+ object['actions'][index]['value'][sub_index] = JSON.parse(File.read(path))
241
+ end
242
+ end
243
+ object
244
+ end
245
+ end
246
+ end
247
+ end
@@ -0,0 +1,188 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Defines the module Readiness.
4
+ module Readiness
5
+ # Defines the module Repos
6
+ module Repos
7
+ ##
8
+ # Defines the class Groups within the module {Readiness::Repos}.
9
+ #
10
+ # @author Jason Colyer
11
+ # @since 1.0.12
12
+ class Groups < Readiness::Client
13
+ ##
14
+ # Compares the repo automation files to the Zendesk instance groups
15
+ #
16
+ # @author Jason Colyer
17
+ # @since 1.0.12
18
+ # @param zendesk_client [Object] An instance of {Readiness::Zendesk::Client}
19
+ # @param location [String] The location (relative or absolute) of the repo's data folder
20
+ # @param verbose [Boolean] Whether you want a detailed report or not
21
+ # @return [Hash]
22
+ # @example
23
+ # require 'support_readiness'
24
+ # config = Readiness::Zendesk::Configuration.new
25
+ # config.username = 'alice@example.com'
26
+ # config.token = 'test123abc'
27
+ # config.url = 'https://example.zendesk.com/api/v2'
28
+ # client = Readiness::Zendesk::Client.new(config)
29
+ # diffs = Readiness::Repos::Groups.compare(client, 'groups/data', false)
30
+ # pp diffs[:updates.count]
31
+ # # => 1
32
+ # @example
33
+ # require 'support_readiness'
34
+ # config = Readiness::Zendesk::Configuration.new
35
+ # config.username = 'alice@example.com'
36
+ # config.token = 'test123abc'
37
+ # config.url = 'https://example.zendesk.com/api/v2'
38
+ # client = Readiness::Zendesk::Client.new(config)
39
+ # diffs = Readiness::Repos::Groups.compare(client, 'groups/data', true)
40
+ # # => Detailed diff of Support Ops
41
+ # # => - @description Repo:
42
+ # # => "For GitLab Support Operations"
43
+ # # => @description Zendesk:
44
+ # # => "For GitLab Support Ops"
45
+ # # => Compare report:
46
+ # # => - Creates: 0
47
+ # # => - Updates: 1
48
+ # pp diffs[:updates.count]
49
+ # # => 1
50
+ def self.compare(zendesk_client, location = 'data', verbose = false)
51
+ diffs = {
52
+ updates: [],
53
+ creates: []
54
+ }
55
+ from_repo = gather(location)
56
+ from_zendesk = Zendesk::Groups.list(zendesk_client)
57
+ from_repo.each do |repo|
58
+ zd = from_zendesk.detect { |z| z.name == repo.name }
59
+ if zd.nil?
60
+ diffs[:creates].push(repo)
61
+ else
62
+ comparable = zd.dup
63
+ comparable.id = nil
64
+ diffs[:updates].push(update_object(repo, zd)) if to_clean_json(repo) != to_clean_json(comparable)
65
+ detailed_diff(repo, comparable) if verbose && to_clean_json(repo) != to_clean_json(comparable)
66
+ end
67
+ end
68
+ report_diffs(diffs) if verbose
69
+ diffs
70
+ end
71
+
72
+ ##
73
+ # Outputs a comparison report
74
+ #
75
+ # @author Jason Colyer
76
+ # @since 1.0.12
77
+ # @param diffs [Hash] The returned value of {compare}
78
+ def self.report_diffs(diffs)
79
+ puts 'Compare report:'
80
+ puts "- Creates: #{diffs[:creates].count}"
81
+ puts "- Updates: #{diffs[:updates].count}"
82
+ end
83
+
84
+ ##
85
+ # Outputs a comparison report
86
+ #
87
+ # @author Jason Colyer
88
+ # @since 1.0.12
89
+ # @param repo [Object] An instance of {Readiness::Zendesk::Groups}
90
+ # @param zendesk [Object] An instance of {Readiness::Zendesk::Groups}
91
+ def self.detailed_diff(repo, zendesk)
92
+ puts "Detailed diff of #{repo.title}"
93
+ repo.instance_variables.each do |v|
94
+ next if v == :@id
95
+
96
+ if repo.instance_variable_get(v) != zendesk.instance_variable_get(v)
97
+ puts "- #{v} Repo:"
98
+ pp repo.instance_variable_get(v)
99
+ puts " #{v} Zendesk:"
100
+ pp zendesk.instance_variable_get(v)
101
+ end
102
+ end
103
+ end
104
+
105
+ ##
106
+ # Creates an instance of {Readiness::Zendesk::Groups} to use for updates
107
+ #
108
+ # @author Jason Colyer
109
+ # @since 1.0.12
110
+ # @param repo [Object] An instance of {Readiness::Zendesk::Groups}
111
+ # @param zendesk [Object] An instance of {Readiness::Zendesk::Groups}
112
+ # @return [Object]
113
+ def self.update_object(repo, zendesk)
114
+ object = repo
115
+ object.id = zendesk.id
116
+ object
117
+ end
118
+
119
+ ##
120
+ # Parses repo group files
121
+ #
122
+ # @author Jason Colyer
123
+ # @since 1.0.12
124
+ # @param location [String] The location (relative or absolute) of the repo's data folder
125
+ # @return [Array]
126
+ # @example
127
+ # require 'support_readiness'
128
+ # repo = Readiness::Repos::Groups.gather('groups/data')
129
+ # pp repo.count
130
+ # # => 35
131
+ def self.gather(location = 'data')
132
+ @errors = []
133
+ @location = location
134
+ array = []
135
+ Dir["#{@location}/*.yaml"].each do |f|
136
+ object = YAML.safe_load_file(f)
137
+ validity_check(f, object)
138
+ object['id'] = nil
139
+ array.push(Zendesk::Groups.new(object))
140
+ end
141
+ repo_check(array)
142
+ report_errors unless @errors.count.zero?
143
+ array
144
+ end
145
+
146
+ ##
147
+ # Outputs an error report and exits with a status code of 1
148
+ #
149
+ # @author Jason Colyer
150
+ # @since 1.0.12
151
+ def self.report_errors
152
+ puts 'The following errors were found in the repo files:'
153
+ @errors.each do |e|
154
+ puts "- #{e}"
155
+ end
156
+ puts 'Rectify the errors and retry. We cannot proceed with those errors occurring.'
157
+ exit 1
158
+ end
159
+
160
+ ##
161
+ # Performs basic checks on a repo file
162
+ #
163
+ # @author Jason Colyer
164
+ # @since 1.0.12
165
+ # @param file [String] The path to the repo file
166
+ # @param object [Hash] The Hash derived from parsing a YAML file
167
+ def self.validity_check(file, object)
168
+ @errors.push("Missing name: #{file}") if object['name'].nil?
169
+ @errors.push("Deleted group file: #{file}") if object['deleted']
170
+ end
171
+
172
+ ##
173
+ # Performs basic checks on the repo
174
+ #
175
+ # @author Jason Colyer
176
+ # @since 1.0.12
177
+ # @param objects [Array] The Array of Hashes derived from parsing the repo files
178
+ def self.repo_check(objects)
179
+ duplicate_names = objects.group_by { |o| o.name }.select { |k, v| v.size > 1 }.map(&:first)
180
+ unless duplicate_names.count.zero?
181
+ duplicate_names.each do |d|
182
+ @errors.push("The name '#{d}' is used in multiple files")
183
+ end
184
+ end
185
+ end
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,224 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Defines the module Readiness.
4
+ module Readiness
5
+ # Defines the module Repos
6
+ module Repos
7
+ ##
8
+ # Defines the class Macros within the module {Readiness::Repos}.
9
+ #
10
+ # @author Jason Colyer
11
+ # @since 1.0.12
12
+ class Macros < Readiness::Client
13
+ ##
14
+ # Compares the repo automation files to the Zendesk instance macros
15
+ #
16
+ # @author Jason Colyer
17
+ # @since 1.0.12
18
+ # @param zendesk_client [Object] An instance of {Readiness::Zendesk::Client}
19
+ # @param location [String] The location (relative or absolute) of the repo's data folder
20
+ # @param verbose [Boolean] Whether you want a detailed report or not
21
+ # @return [Hash]
22
+ # @example
23
+ # require 'support_readiness'
24
+ # config = Readiness::Zendesk::Configuration.new
25
+ # config.username = 'alice@example.com'
26
+ # config.token = 'test123abc'
27
+ # config.url = 'https://example.zendesk.com/api/v2'
28
+ # client = Readiness::Zendesk::Client.new(config)
29
+ # diffs = Readiness::Repos::Macros.compare(client, 'macros/data', false)
30
+ # pp diffs[:updates.count]
31
+ # # => 5
32
+ # @example
33
+ # require 'support_readiness'
34
+ # config = Readiness::Zendesk::Configuration.new
35
+ # config.username = 'alice@example.com'
36
+ # config.token = 'test123abc'
37
+ # config.url = 'https://example.zendesk.com/api/v2'
38
+ # client = Readiness::Zendesk::Client.new(config)
39
+ # diffs = Readiness::Repos::Macros.compare(client, 'macros/data', true)
40
+ # # => Detailed diff of Status::Close::Close solved tickets after 7 days
41
+ # # => - @default Repo:
42
+ # # => false
43
+ # # => @default Zendesk:
44
+ # # => true
45
+ # # => Compare report:
46
+ # # => - Creates: 0
47
+ # # => - Updates: 1
48
+ # pp diffs[:updates.count]
49
+ # # => 1
50
+ def self.compare(zendesk_client, location = 'data', verbose = false)
51
+ diffs = {
52
+ updates: [],
53
+ creates: []
54
+ }
55
+ from_repo = gather(location)
56
+ from_zendesk = Zendesk::Macros.list(zendesk_client)
57
+ from_repo.each do |repo|
58
+ zd = from_zendesk.detect { |z| z.title == repo.title }
59
+ if zd.nil?
60
+ diffs[:creates].push(repo)
61
+ else
62
+ comparable = zd.dup
63
+ comparable.id = nil
64
+ diffs[:updates].push(update_object(repo, zd)) if to_clean_json(repo) != to_clean_json(comparable)
65
+ detailed_diff(repo, comparable) if verbose && to_clean_json(repo) != to_clean_json(comparable)
66
+ end
67
+ end
68
+ report_diffs(diffs) if verbose
69
+ diffs
70
+ end
71
+
72
+ ##
73
+ # Outputs a comparison report
74
+ #
75
+ # @author Jason Colyer
76
+ # @since 1.0.12
77
+ # @param diffs [Hash] The returned value of {compare}
78
+ def self.report_diffs(diffs)
79
+ puts 'Compare report:'
80
+ puts "- Creates: #{diffs[:creates].count}"
81
+ puts "- Updates: #{diffs[:updates].count}"
82
+ end
83
+
84
+ ##
85
+ # Outputs a comparison report
86
+ #
87
+ # @author Jason Colyer
88
+ # @since 1.0.12
89
+ # @param repo [Object] An instance of {Readiness::Zendesk::Macros}
90
+ # @param zendesk [Object] An instance of {Readiness::Zendesk::Macros}
91
+ def self.detailed_diff(repo, zendesk)
92
+ puts "Detailed diff of #{repo.title}"
93
+ repo.instance_variables.each do |v|
94
+ next if v == :@id
95
+
96
+ if repo.instance_variable_get(v) != zendesk.instance_variable_get(v)
97
+ puts "- #{v} Repo:"
98
+ pp repo.instance_variable_get(v)
99
+ puts " #{v} Zendesk:"
100
+ pp zendesk.instance_variable_get(v)
101
+ end
102
+ end
103
+ end
104
+
105
+ ##
106
+ # Creates an instance of {Readiness::Zendesk::Macros} to use for updates
107
+ #
108
+ # @author Jason Colyer
109
+ # @since 1.0.12
110
+ # @param repo [Object] An instance of {Readiness::Zendesk::Macros}
111
+ # @param zendesk [Object] An instance of {Readiness::Zendesk::Macros}
112
+ # @return [Object]
113
+ def self.update_object(repo, zendesk)
114
+ object = repo
115
+ object.id = zendesk.id
116
+ object
117
+ end
118
+
119
+ ##
120
+ # Parses repo macro files
121
+ #
122
+ # @author Jason Colyer
123
+ # @since 1.0.12
124
+ # @param location [String] The location (relative or absolute) of the repo's data folder
125
+ # @return [Array]
126
+ # @example
127
+ # require 'support_readiness'
128
+ # repo = Readiness::Repos::Macros.gather('macros/data')
129
+ # pp repo.count
130
+ # # => 35
131
+ def self.gather(location = 'data')
132
+ @errors = []
133
+ @location = location
134
+ array = []
135
+ Dir["#{@location}/**/*.yaml"].each do |f|
136
+ object = YAML.safe_load_file(f)
137
+ object = convert_managed_content(object) if object['contains_managed_content']
138
+ validity_check(f, object)
139
+ object['id'] = nil
140
+ array.push(Zendesk::Macros.new(object))
141
+ end
142
+ repo_check(array)
143
+ report_errors unless @errors.count.zero?
144
+ array
145
+ end
146
+
147
+ ##
148
+ # Outputs an error report and exits with a status code of 1
149
+ #
150
+ # @author Jason Colyer
151
+ # @since 1.0.12
152
+ def self.report_errors
153
+ puts 'The following errors were found in the repo files:'
154
+ @errors.each do |e|
155
+ puts "- #{e}"
156
+ end
157
+ puts 'Rectify the errors and retry. We cannot proceed with those errors occurring.'
158
+ exit 1
159
+ end
160
+
161
+ ##
162
+ # Performs basic checks on a repo file
163
+ #
164
+ # @author Jason Colyer
165
+ # @since 1.0.12
166
+ # @param file [String] The path to the repo file
167
+ # @param object [Hash] The Hash derived from parsing a YAML file
168
+ def self.validity_check(file, object)
169
+ folder = file.split("#{@location}/").last.split('/').first
170
+ @errors.push("Missing title: #{file}") if object['title'].nil?
171
+ @errors.push("Inactive macro in active folder: #{file}") if folder == 'active' && !object['active']
172
+ @errors.push("Active macro in inactive folder: #{file}") if folder == 'inactive' && object['active']
173
+ end
174
+
175
+ ##
176
+ # Performs basic checks on the repo
177
+ #
178
+ # @author Jason Colyer
179
+ # @since 1.0.12
180
+ # @param objects [Array] The Array of Hashes derived from parsing the repo files
181
+ def self.repo_check(objects)
182
+ duplicate_names = objects.group_by { |o| o.title }.select { |k, v| v.size > 1 }.map(&:first)
183
+ unless duplicate_names.count.zero?
184
+ duplicate_names.each do |d|
185
+ @errors.push("The title '#{d}' is used in multiple files")
186
+ end
187
+ end
188
+ end
189
+
190
+ ##
191
+ # Performs conversions for a repo file if using managed content
192
+ #
193
+ # @author Jason Colyer
194
+ # @since 1.0.12
195
+ # @param object [Hash] The Hash derived from parsing a YAML file
196
+ def self.convert_managed_content(object)
197
+ path = "#{@location}/managed_content/#{object['active'] ? '' : 'in'}active/#{object['title'].gsub('::', '/')}.md"
198
+ handle_request_error(1, 'Repos', 404, { action: 'Get managed content', id: object['title'] }) unless File.exist?(path)
199
+ object['actions'].each_with_index do |action, index|
200
+ if action['value'].is_a?(String)
201
+ if action['value'] =~ /MANAGED_CONTENT/
202
+ if action['value'] == 'MANAGED_CONTENT'
203
+ object['actions'][index]['value'] = action['value'].gsub('MANAGED_CONTENT', File.read(path).chomp)
204
+ else
205
+ object['actions'][index]['value'] = action['value'].gsub('MANAGED_CONTENT', File.read(path).chomp.gsub("\n", '\\n'))
206
+ end
207
+ end
208
+ elsif action['value'].is_a?(Array)
209
+ action['value'].each_with_index do |value, sub_index|
210
+ if value =~ /MANAGED_CONTENT/
211
+ if value == 'MANAGED_CONTENT'
212
+ object['actions'][index]['value'][sub_index] = value.gsub('MANAGED_CONTENT', File.read(path).chomp)
213
+ else
214
+ object['actions'][index]['value'][sub_index] = value.gsub('MANAGED_CONTENT', File.read(path).chomp.gsub("\n", '\\n'))
215
+ end
216
+ end
217
+ end
218
+ end
219
+ end
220
+ object
221
+ end
222
+ end
223
+ end
224
+ end