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,200 @@
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 TicketForms within the module {Readiness::Repos}.
9
+ #
10
+ # @author Jason Colyer
11
+ # @since 1.0.12
12
+ class TicketForms < Readiness::Client
13
+ ##
14
+ # Compares the repo ticket forms files to the Zendesk instance ticket forms
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::TicketForms.compare(client, 'tickets/forms-and-fields/data/forms', 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::TicketForms.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 = zd.dup
63
+ comparable_repo = repo.dup
64
+ comparable_zd.id = nil
65
+ comparable_zd.agent_conditions.sort_by! { |a| [a['parent_field_id'], a['value']] }
66
+ comparable_zd.agent_conditions.map { |a| a['child_fields'].sort_by! { |c| c['id'] }}
67
+ comparable_zd.end_user_conditions.sort_by! { |a| [a['parent_field_id'], a['value']] }
68
+ comparable_zd.end_user_conditions.map { |a| a['child_fields'].sort_by! { |c| c['id'] }}
69
+ comparable_repo.agent_conditions.sort_by! { |a| [a['parent_field_id'], a['value']] }
70
+ comparable_repo.agent_conditions.map { |a| a['child_fields'].sort_by! { |c| c['id'] }}
71
+ comparable_repo.end_user_conditions.sort_by! { |a| [a['parent_field_id'], a['value']] }
72
+ comparable_repo.end_user_conditions.map { |a| a['child_fields'].sort_by! { |c| c['id'] }}
73
+ diffs[:updates].push(update_object(repo, zd)) if to_clean_json(comparable_repo) != to_clean_json(comparable_zd)
74
+ detailed_diff(comparable_repo, comparable_zd) if verbose && to_clean_json(comparable_repo) != to_clean_json(comparable_zd)
75
+ end
76
+ end
77
+ report_diffs(diffs) if verbose
78
+ diffs
79
+ end
80
+
81
+ ##
82
+ # Outputs a comparison report
83
+ #
84
+ # @author Jason Colyer
85
+ # @since 1.0.12
86
+ # @param diffs [Hash] The returned value of {compare}
87
+ def self.report_diffs(diffs)
88
+ puts 'Compare report:'
89
+ puts "- Creates: #{diffs[:creates].count}"
90
+ puts "- Updates: #{diffs[:updates].count}"
91
+ end
92
+
93
+ ##
94
+ # Outputs a comparison report
95
+ #
96
+ # @author Jason Colyer
97
+ # @since 1.0.12
98
+ # @param repo [Object] An instance of {Readiness::Zendesk::TicketForms}
99
+ # @param zendesk [Object] An instance of {Readiness::Zendesk::TicketForms}
100
+ def self.detailed_diff(repo, zendesk)
101
+ puts "Detailed diff of #{repo.name}"
102
+ repo.instance_variables.each do |v|
103
+ next if v == :@id
104
+
105
+ if repo.instance_variable_get(v) != zendesk.instance_variable_get(v)
106
+ puts "- #{v} Repo:"
107
+ pp repo.instance_variable_get(v)
108
+ puts " #{v} Zendesk:"
109
+ pp zendesk.instance_variable_get(v)
110
+ end
111
+ end
112
+ end
113
+
114
+ ##
115
+ # Creates an instance of {Readiness::Zendesk::TicketForms} to use for updates
116
+ #
117
+ # @author Jason Colyer
118
+ # @since 1.0.12
119
+ # @param repo [Object] An instance of {Readiness::Zendesk::TicketForms}
120
+ # @param zendesk [Object] An instance of {Readiness::Zendesk::TicketForms}
121
+ # @return [Object]
122
+ def self.update_object(repo, zendesk)
123
+ object = repo
124
+ object.id = zendesk.id
125
+ object
126
+ end
127
+
128
+ ##
129
+ # Parses repo ticket form files
130
+ #
131
+ # @author Jason Colyer
132
+ # @since 1.0.12
133
+ # @param location [String] The location (relative or absolute) of the repo's data folder
134
+ # @return [Array]
135
+ # @example
136
+ # require 'support_readiness'
137
+ # repo = Readiness::Repos::TicketForms.gather('groups/data')
138
+ # pp repo.count
139
+ # # => 35
140
+ def self.gather(location = 'data')
141
+ @errors = []
142
+ @location = location
143
+ array = []
144
+ Dir["#{@location}/**/*.yaml"].each do |f|
145
+ object = YAML.safe_load_file(f)
146
+ validity_check(f, object)
147
+ object['id'] = nil
148
+ array.push(Zendesk::TicketForms.new(object))
149
+ end
150
+ repo_check(array)
151
+ report_errors unless @errors.count.zero?
152
+ array
153
+ end
154
+
155
+ ##
156
+ # Outputs an error report and exits with a status code of 1
157
+ #
158
+ # @author Jason Colyer
159
+ # @since 1.0.12
160
+ def self.report_errors
161
+ puts 'The following errors were found in the repo files:'
162
+ @errors.each do |e|
163
+ puts "- #{e}"
164
+ end
165
+ puts 'Rectify the errors and retry. We cannot proceed with those errors occurring.'
166
+ exit 1
167
+ end
168
+
169
+ ##
170
+ # Performs basic checks on a repo file
171
+ #
172
+ # @author Jason Colyer
173
+ # @since 1.0.12
174
+ # @param file [String] The path to the repo file
175
+ # @param object [Hash] The Hash derived from parsing a YAML file
176
+ def self.validity_check(file, object)
177
+ folder = file.split("#{@location}/").last.split('/').first
178
+ @errors.push("Missing name: #{file}") if object['name'].nil?
179
+ @errors.push("Missing position: #{file}") if object['position'].nil?
180
+ @errors.push("Inactive form in active folder: #{file}") if folder == 'active' && !object['active']
181
+ @errors.push("Active form in inactive folder: #{file}") if folder == 'inactive' && object['active']
182
+ end
183
+
184
+ ##
185
+ # Performs basic checks on the repo
186
+ #
187
+ # @author Jason Colyer
188
+ # @since 1.0.12
189
+ # @param objects [Array] The Array of Hashes derived from parsing the repo files
190
+ def self.repo_check(objects)
191
+ duplicate_names = objects.group_by { |o| o.name }.select { |k, v| v.size > 1 }.map(&:first)
192
+ unless duplicate_names.count.zero?
193
+ duplicate_names.each do |d|
194
+ @errors.push("The name '#{d}' is used in multiple files")
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
@@ -0,0 +1,255 @@
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 Triggers within the module {Readiness::Repos}.
9
+ #
10
+ # @author Jason Colyer
11
+ # @since 1.0.12
12
+ class Triggers < Readiness::Client
13
+ ##
14
+ # Compares the repo trigger files to the Zendesk instance triggers
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::Triggers.compare(client, 'triggers/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::Triggers.compare(client, 'triggers/data', true)
40
+ # # => Detailed diff of Cleanup followup tickets
41
+ # # => - @position Repo:
42
+ # # => 13
43
+ # # => @position Zendesk:
44
+ # # => 12
45
+ # # => Detailed diff of New Billing ticket created via Webform
46
+ # # => - @position Repo:
47
+ # # => 11
48
+ # # => @position Zendesk:
49
+ # # => 10
50
+ # # => Compare report:
51
+ # # => - Creates: 0
52
+ # # => - Updates: 2
53
+ # pp diffs[:updates.count]
54
+ # # => 2
55
+ def self.compare(zendesk_client, location = 'data', verbose = false)
56
+ diffs = {
57
+ updates: [],
58
+ creates: []
59
+ }
60
+ from_repo = gather(location)
61
+ from_zendesk = Zendesk::Triggers.list(zendesk_client)
62
+ from_repo.each do |repo|
63
+ zd = from_zendesk.detect { |z| z.title == repo.title }
64
+ if zd.nil?
65
+ diffs[:creates].push(repo)
66
+ else
67
+ comparable = zd.dup
68
+ comparable.id = nil
69
+ diffs[:updates].push(update_object(repo, zd)) if to_clean_json(repo) != to_clean_json(comparable)
70
+ detailed_diff(repo, comparable) if verbose && to_clean_json(repo) != to_clean_json(comparable)
71
+ end
72
+ end
73
+ report_diffs(diffs) if verbose
74
+ diffs
75
+ end
76
+
77
+ ##
78
+ # Outputs a comparison report
79
+ #
80
+ # @author Jason Colyer
81
+ # @since 1.0.12
82
+ # @param diffs [Hash] The returned value of {compare}
83
+ def self.report_diffs(diffs)
84
+ puts 'Compare report:'
85
+ puts "- Creates: #{diffs[:creates].count}"
86
+ puts "- Updates: #{diffs[:updates].count}"
87
+ end
88
+
89
+ ##
90
+ # Outputs a comparison report
91
+ #
92
+ # @author Jason Colyer
93
+ # @since 1.0.12
94
+ # @param repo [Object] An instance of {Readiness::Zendesk::Triggers}
95
+ # @param zendesk [Object] An instance of {Readiness::Zendesk::Triggers}
96
+ def self.detailed_diff(repo, zendesk)
97
+ puts "Detailed diff of #{repo.title}"
98
+ repo.instance_variables.each do |v|
99
+ next if v == :@id
100
+
101
+ if repo.instance_variable_get(v) != zendesk.instance_variable_get(v)
102
+ puts "- #{v} Repo:"
103
+ pp repo.instance_variable_get(v)
104
+ puts " #{v} Zendesk:"
105
+ pp zendesk.instance_variable_get(v)
106
+ end
107
+ end
108
+ end
109
+
110
+ ##
111
+ # Creates an instance of {Readiness::Zendesk::Triggers} to use for updates
112
+ #
113
+ # @author Jason Colyer
114
+ # @since 1.0.12
115
+ # @param repo [Object] An instance of {Readiness::Zendesk::Triggers}
116
+ # @param zendesk [Object] An instance of {Readiness::Zendesk::Triggers}
117
+ # @return [Object]
118
+ def self.update_object(repo, zendesk)
119
+ object = repo
120
+ object.id = zendesk.id
121
+ object
122
+ end
123
+
124
+ ##
125
+ # Parses repo trigger files
126
+ #
127
+ # @author Jason Colyer
128
+ # @since 1.0.12
129
+ # @param location [String] The location (relative or absolute) of the repo's data folder
130
+ # @return [Array]
131
+ # @example
132
+ # require 'support_readiness'
133
+ # repo = Readiness::Repos::Triggers.gather('triggers/data')
134
+ # pp repo.count
135
+ # # => 35
136
+ def self.gather(location = 'data')
137
+ @errors = []
138
+ @location = location
139
+ array = []
140
+ Dir["#{@location}/**/*.yaml"].each do |f|
141
+ object = YAML.safe_load_file(f)
142
+ object = convert_managed_content(object, f) if object['contains_managed_content']
143
+ object['default'] = false
144
+ #object = convert_managed_webhook(object, f) if object['contains_managed_webhook']
145
+ validity_check(f, object)
146
+ object['id'] = nil
147
+ array.push(Zendesk::Triggers.new(object))
148
+ end
149
+ repo_check(array)
150
+ report_errors unless @errors.count.zero?
151
+ array
152
+ end
153
+
154
+ ##
155
+ # Outputs an error report and exits with a status code of 1
156
+ #
157
+ # @author Jason Colyer
158
+ # @since 1.0.12
159
+ def self.report_errors
160
+ puts 'The following errors were found in the repo files:'
161
+ @errors.each do |e|
162
+ puts "- #{e}"
163
+ end
164
+ puts 'Rectify the errors and retry. We cannot proceed with those errors occurring.'
165
+ exit 1
166
+ end
167
+
168
+ ##
169
+ # Performs basic checks on a repo file
170
+ #
171
+ # @author Jason Colyer
172
+ # @since 1.0.12
173
+ # @param file [String] The path to the repo file
174
+ # @param object [Hash] The Hash derived from parsing a YAML file
175
+ def self.validity_check(file, object)
176
+ folder = file.split("#{@location}/").last.split('/').first
177
+ @errors.push("Missing position: #{file}") if object['position'].nil?
178
+ @errors.push("Missing title: #{file}") if object['title'].nil?
179
+ @errors.push("Inactive trigger in active folder: #{file}") if folder == 'active' && !object['active']
180
+ @errors.push("Active trigger in inactive folder: #{file}") if folder == 'inactive' && object['active']
181
+ end
182
+
183
+ ##
184
+ # Performs basic checks on the repo
185
+ #
186
+ # @author Jason Colyer
187
+ # @since 1.0.12
188
+ # @param objects [Array] The Array of Hashes derived from parsing the repo files
189
+ def self.repo_check(objects)
190
+ duplicate_names = objects.group_by { |o| o.title }.select { |k, v| v.size > 1 }.map(&:first)
191
+ unless duplicate_names.count.zero?
192
+ duplicate_names.each do |d|
193
+ @errors.push("The title '#{d}' is used in multiple files")
194
+ end
195
+ end
196
+ end
197
+
198
+ ##
199
+ # Performs conversions for a repo file if using managed content
200
+ #
201
+ # @author Jason Colyer
202
+ # @since 1.0.12
203
+ # @param object [Hash] The Hash derived from parsing a YAML file
204
+ def self.convert_managed_content(object, file)
205
+ category = file.split("#{@location}/#{object['active'] ? '' : 'in' }active/").last.split('/').first
206
+ path = "#{@location}/managed_content/#{category}/#{object['title']}.md"
207
+ handle_request_error(1, 'Repos', 404, { action: 'Get managed content', id: object['title'] }) unless File.exist?(path)
208
+ object['actions'].each_with_index do |action, index|
209
+ if action['value'].is_a?(String)
210
+ if action['value'] =~ /MANAGED_CONTENT/
211
+ if action['value'] == 'MANAGED_CONTENT'
212
+ object['actions'][index]['value'] = action['value'].gsub('MANAGED_CONTENT', File.read(path).chomp)
213
+ else
214
+ object['actions'][index]['value'] = action['value'].gsub('MANAGED_CONTENT', File.read(path).chomp.gsub("\n", '\\n'))
215
+ end
216
+ end
217
+ elsif action['value'].is_a?(Array)
218
+ action['value'].each_with_index do |value, sub_index|
219
+ if value =~ /MANAGED_CONTENT/
220
+ if value == 'MANAGED_CONTENT'
221
+ object['actions'][index]['value'][sub_index] = value.gsub('MANAGED_CONTENT', File.read(path).chomp)
222
+ else
223
+ object['actions'][index]['value'][sub_index] = value.gsub('MANAGED_CONTENT', File.read(path).chomp.gsub("\n", '\\n'))
224
+ end
225
+ end
226
+ end
227
+ end
228
+ end
229
+ object
230
+ end
231
+
232
+ ##
233
+ # Performs conversions for a repo file if using managed webhooks
234
+ #
235
+ # @author Jason Colyer
236
+ # @since 1.0.12
237
+ # @param object [Hash] The Hash derived from parsing a YAML file
238
+ def self.convert_managed_webhook(object, file)
239
+ category = file.split("#{@location}/#{object['active'] ? '' : 'in' }active/").last.split('/').first
240
+ path = "#{@location}/managed_content/#{category}/#{object['title']}.webhook"
241
+ handle_request_error(1, 'Repos', 404, { action: 'Get managed webhook', id: object['title'] }) unless File.exist?(path)
242
+ object['actions'].each_with_index do |action, index|
243
+ next unless action['value'].is_a?(Array)
244
+
245
+ action['value'].each_with_index do |value, sub_index|
246
+ next unless value == 'MANAGED_WEBHOOK'
247
+
248
+ object['actions'][index]['value'][sub_index] = JSON.parse(File.read(path))
249
+ end
250
+ end
251
+ object
252
+ end
253
+ end
254
+ end
255
+ end
@@ -0,0 +1,201 @@
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 UserFields within the module {Readiness::Repos}.
9
+ #
10
+ # @author Jason Colyer
11
+ # @since 1.0.12
12
+ class UserFields < Readiness::Client
13
+ ##
14
+ # Compares the repo user fields files to the Zendesk instance user fields
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
+ def self.compare(zendesk_client, location = 'data', verbose = false)
23
+ @from_repo = gather(zendesk_client, location)
24
+ @from_zendesk = Zendesk::UserFields.list(zendesk_client)
25
+ @diffs = {
26
+ field_updates: [],
27
+ field_creates: [],
28
+ option_updates: [],
29
+ option_deletes: []
30
+ }
31
+ @from_repo.each do |repo|
32
+ zd = @from_zendesk.detect { |z| z.title == repo.title }
33
+ if zd.nil?
34
+ pp @from_zendesk
35
+ pp repo
36
+ @diffs[:field_creates].push(repo)
37
+ unless repo.custom_field_options.nil?
38
+ repo.custom_field_options.each do |o|
39
+ @diffs[:option_updates].push({ "#{repo.title}" => o })
40
+ end
41
+ end
42
+ else
43
+ comparable_zd = zd.dup
44
+ comparable_repo = repo.dup
45
+ comparable_zd.id = nil
46
+ comparable_zd.custom_field_options = nil
47
+ comparable_repo.custom_field_options = nil
48
+ @diffs[:field_updates].push(update_object(repo, zd)) if to_clean_json(comparable_zd) != to_clean_json(comparable_repo)
49
+ detailed_diff(comparable_repo, comparable_zd) if verbose && to_clean_json(comparable_repo) != to_clean_json(comparable_zd)
50
+ next if repo.custom_field_options.nil?
51
+
52
+ zd_options = @from_zendesk.detect { |z| z.title == repo.title }.custom_field_options
53
+ repo.custom_field_options.map! { |o| Zendesk::UserFieldOptions.new(o) } unless repo.custom_field_options.first.is_a? Object
54
+ repo.custom_field_options.each do |repo_option|
55
+ repo_option.position = repo.custom_field_options.index(repo_option) if repo_option.position.nil?
56
+ zd_option = zd_options.detect { |zd_o| zd_o.name == repo_option.name }
57
+ if zd_option.nil? || to_clean_json(zd_option) != to_clean_json(repo_option)
58
+ @diffs[:option_updates].push({ "#{repo.title}" => repo_option })
59
+ end
60
+ end
61
+ zd_options.each do |zd_option|
62
+ repo_option = repo.custom_field_options.detect { |rp_o| rp_o.name == zd_option.name }
63
+ if repo_option.nil?
64
+ @diffs[:option_deletes].push({ "#{repo.title}" => zd_option })
65
+ end
66
+ end
67
+ end
68
+ end
69
+ report_diffs if verbose
70
+ @diffs
71
+ end
72
+
73
+ ##
74
+ # Outputs a comparison report
75
+ #
76
+ # @author Jason Colyer
77
+ # @since 1.0.12
78
+ def self.report_diffs
79
+ puts 'Compare report:'
80
+ puts "- Field Creates: #{@diffs[:field_creates].count}"
81
+ puts "- Field Updates: #{@diffs[:field_updates].count}"
82
+ puts "- Option Updates: #{@diffs[:option_updates].count}"
83
+ puts "- Option Deletes: #{@diffs[:option_deletes].count}"
84
+ end
85
+
86
+ ##
87
+ # Outputs a comparison report
88
+ #
89
+ # @author Jason Colyer
90
+ # @since 1.0.12
91
+ # @param repo [Object] An instance of {Readiness::Zendesk::UserFields}
92
+ # @param zendesk [Object] An instance of {Readiness::Zendesk::UserFields}
93
+ def self.detailed_diff(repo, zendesk)
94
+ puts "Detailed diff of #{repo.title}"
95
+ repo.instance_variables.each do |v|
96
+ next if v == :@id
97
+
98
+ if repo.instance_variable_get(v) != zendesk.instance_variable_get(v)
99
+ puts "- #{v} Repo:"
100
+ pp repo.instance_variable_get(v)
101
+ puts " #{v} Zendesk:"
102
+ pp zendesk.instance_variable_get(v)
103
+ end
104
+ end
105
+ end
106
+
107
+ ##
108
+ # Creates an instance of {Readiness::Zendesk::UserFields} to use for updates
109
+ #
110
+ # @author Jason Colyer
111
+ # @since 1.0.12
112
+ # @param repo [Object] An instance of {Readiness::Zendesk::UserFields}
113
+ # @param zendesk [Object] An instance of {Readiness::Zendesk::UserFields}
114
+ # @return [Object]
115
+ def self.update_object(repo, zendesk)
116
+ object = repo
117
+ object.id = zendesk.id
118
+ object
119
+ end
120
+
121
+ ##
122
+ # Parses repo user field files
123
+ #
124
+ # @author Jason Colyer
125
+ # @since 1.0.12
126
+ # @param client [Object] An instance of {Readiness::Zendesk::Client}
127
+ # @param location [String] The location (relative or absolute) of the repo's data folder
128
+ # @return [Array]
129
+ # @example
130
+ # require 'support_readiness'
131
+ # config = Readiness::Zendesk::Configuration.new
132
+ # config.username = 'alice@example.com'
133
+ # config.token = 'test123abc'
134
+ # config.url = 'https://example.zendesk.com/api/v2'
135
+ # client = Readiness::Zendesk::Client.new(config)
136
+ # repo = Readiness::Repos::UserFields.gather(client, 'users/fields/data')
137
+ # pp repo.count
138
+ # # => 170
139
+ def self.gather(client, location = 'data')
140
+ @errors = []
141
+ @location = location
142
+ array = []
143
+ Dir["#{@location}/**/*.yaml"].each do |f|
144
+ object = YAML.safe_load_file(f)
145
+ validity_check(f, object)
146
+ object['id'] = nil
147
+ object['title_in_portal'] = (object['visible_in_portal'] ? object['title_in_portal'] : object['title'])
148
+ array.push(Zendesk::UserFields.new(client, object))
149
+ end
150
+ repo_check(array)
151
+ report_errors unless @errors.count.zero?
152
+ array
153
+ end
154
+
155
+ ##
156
+ # Outputs an error report and exits with a status code of 1
157
+ #
158
+ # @author Jason Colyer
159
+ # @since 1.0.12
160
+ def self.report_errors
161
+ puts 'The following errors were found in the repo files:'
162
+ @errors.each do |e|
163
+ puts "- #{e}"
164
+ end
165
+ puts 'Rectify the errors and retry. We cannot proceed with those errors occurring.'
166
+ exit 1
167
+ end
168
+
169
+ ##
170
+ # Performs basic checks on a repo file
171
+ #
172
+ # @author Jason Colyer
173
+ # @since 1.0.12
174
+ # @param file [String] The path to the repo file
175
+ # @param object [Hash] The Hash derived from parsing a YAML file
176
+ def self.validity_check(file, object)
177
+ folder = file.split("#{@location}/").last.split('/').first
178
+ @errors.push("Missing type: #{file}") if object['type'].nil?
179
+ @errors.push("Missing title: #{file}") if object['title'].nil?
180
+ @errors.push("Missing position: #{file}") if object['position'].nil?
181
+ @errors.push("Inactive user field in active folder: #{file}") if folder == 'active' && !object['active']
182
+ @errors.push("Active user field in inactive folder: #{file}") if folder == 'inactive' && object['active']
183
+ end
184
+
185
+ ##
186
+ # Performs basic checks on the repo
187
+ #
188
+ # @author Jason Colyer
189
+ # @since 1.0.12
190
+ # @param objects [Array] The Array of Hashes derived from parsing the repo files
191
+ def self.repo_check(objects)
192
+ duplicate_names = objects.group_by { |o| o.title }.select { |k, v| v.size > 1 }.map(&:first)
193
+ unless duplicate_names.count.zero?
194
+ duplicate_names.each do |d|
195
+ @errors.push("The title '#{d}' is used in multiple files")
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
201
+ end