Class: Readiness::Repos::Articles

Inherits:
Client
  • Object
show all
Defined in:
lib/support_readiness/repos/articles.rb

Overview

Defines the class Articles within the module Readiness::Repos.

Author:

  • Jason Colyer

Since:

  • 1.0.12

Class Method Summary collapse

Methods inherited from Client

auth_error, bad_request_error, convert_actions, convert_conditions, convert_standard_names_to_ids, convert_ticket_form_agent_conditions, convert_ticket_form_brands, convert_ticket_form_end_user_conditions, convert_ticket_form_names_to_ids, convert_view_names_to_ids, convert_view_restrictions, covert_ticket_form_field_ids, create_package!, erb_renderer, handle_request_error, not_found_error, not_processible_error, put_into_archive, recursively_deflate_directory, timestamp_filename, to_clean_json, to_clean_json_with_key, to_hash, to_nearly_clean_json, to_nearly_clean_json_with_key, to_param_string, write_entries

Class Method Details

.compare(zd_client, gl_client, instance, location = 'data', verbose = false) ⇒ Object

Since:

  • 1.0.12



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/support_readiness/repos/articles.rb', line 13

def self.compare(zd_client, gl_client, instance, location = 'data', verbose = false)
  diffs = {
    updates: [],
    creates: []
  }
  from_repo = gather(gl_client, instance, location)
  from_zendesk = Zendesk::Articles.list(zd_client)
  from_repo.each do |repo|
    zd = from_zendesk.detect { |z| z.title == repo.title }
    if zd.nil?
      diffs[:creates].push(repo)
    else
      comparable = zd.dup
      comparable.id = nil
      diffs[:updates].push(update_object(repo, zd)) if to_clean_json(repo) != to_clean_json(comparable)
      detailed_diff(repo, comparable) if verbose && to_clean_json(repo) != to_clean_json(comparable)
    end
  end
  report_diffs(diffs) if verbose
  diffs
end

.data_object_dom(object) ⇒ String

Returns the data_object_dom String

Parameters:

  • object (Hash)

Returns:

  • (String)

Author:

  • Jason Colyer

Since:

  • 1.0.12



206
207
208
209
210
211
# File 'lib/support_readiness/repos/articles.rb', line 206

def self.data_object_dom(object)
  path = path_from_instance(object).split("#{@location}/managed_content/").last
  <<~STRING
    <p id="data_object_dom" style="display: none;" data-path="#{path}">&nbsp;</p>
  STRING
end

.detailed_diff(repo, zendesk) ⇒ Object

Outputs a comparison report

Parameters:

Author:

  • Jason Colyer

Since:

  • 1.0.12



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/support_readiness/repos/articles.rb', line 54

def self.detailed_diff(repo, zendesk)
  puts "Detailed diff of #{repo.title}"
  repo.instance_variables.each do |v|
    next if v == :@id

    if repo.instance_variable_get(v) != zendesk.instance_variable_get(v)
      if v == :@body
        puts "- #{v} Repo and #{v} Zendesk differ"
      else
        puts "- #{v} Repo:"
        pp repo.instance_variable_get(v)
        puts "  #{v} Zendesk:"
        pp zendesk.instance_variable_get(v)
      end
    end
  end
end

.determine_anchor(parts) ⇒ String

Determiens the correct anchor for a String

Parameters:

  • parts (Array)

Returns:

  • (String)

Author:

  • Jason Colyer

Since:

  • 1.0.12



251
252
253
254
255
# File 'lib/support_readiness/repos/articles.rb', line 251

def self.determine_anchor(parts)
  return '' if parts.count == 1

  "##{parts.last}"
end

.determine_raw_yamlArray

Function to set the default value for the @raw_yaml global variable

Returns:

  • (Array)

Author:

  • Jason Colyer

Since:

  • 1.0.12



273
274
275
276
277
278
279
# File 'lib/support_readiness/repos/articles.rb', line 273

def self.determine_raw_yaml
  array = []
  Dir["#{@location}/articles/**/*.yaml"].each do |f|
    array.push(YAML.safe_load_file(f))
  end
  array
end

.find_file(path) ⇒ Hash

Locates the correct YAML content

Parameters:

  • path (String)

Returns:

  • (Hash)

Author:

  • Jason Colyer

Since:

  • 1.0.12



240
241
242
# File 'lib/support_readiness/repos/articles.rb', line 240

def self.find_file(path)
  raw_yaml.detect { |y| y['content_path'] =~ /#{path}/ }
end

.full_conversion(object) ⇒ String

Performs a full conversion on the article’s body

Parameters:

  • object (Hash)

Returns:

  • (String)

Author:

  • Jason Colyer

Since:

  • 1.0.12



161
162
163
164
165
166
167
# File 'lib/support_readiness/repos/articles.rb', line 161

def self.full_conversion(object)
  html = markdown_to_html(object)
  @doc = Nokogiri::HTML.fragment(html)
  rearrange_anchors
  replace_modifiers
  @doc.to_html
end

.gather(gl_client, instance = 'Global', location = 'data') ⇒ Array

Parses repo article files

Examples:

require 'support_readiness'
gl_config = Readiness::GitLab::Configuration.new
gl_config.token = ENV.fetch('GL_TOKEN')
gl_client = Readiness::GitLab::Client.new(gl_config)
repo = Readiness::Repos::Articles.gather(gl_client, 'Global', 'automations/data')
pp repo.count
# => 15

Parameters:

  • gl_client (Object)

    An instance of GitLab::Client

  • instance (String) (defaults to: 'Global')

    The Zendesk instance being worked with. Accepted values are:

    • Global

    • Global Sandbox

    • US Government

    • US Government Sandbox

  • location (String) (defaults to: 'data')

    The location (relative or absolute) of the repo’s data folder

Returns:

  • (Array)

Author:

  • Jason Colyer

Since:

  • 1.0.12



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/support_readiness/repos/articles.rb', line 107

def self.gather(gl_client, instance = 'Global', location = 'data')
  @errors = []
  @gl_client = gl_client
  @instance = instance
  @location = location
  array = []
  Dir["#{@location}/articles/**/*.yaml"].each do |f|
    object = YAML.safe_load_file(f)
    object['body'] = full_conversion(object)
    validity_check(f, object)
    object['id'] = nil
    array.push(Zendesk::Articles.new(object))
  end
  repo_check(array)
  report_errors unless @errors.count.zero?
  array
end

.markdown_to_html(object) ⇒ String

Converts the manasged content file’s markdown to html

Parameters:

  • object (Hash)

Returns:

  • (String)

Author:

  • Jason Colyer

Since:

  • 1.0.12



176
177
178
179
180
181
# File 'lib/support_readiness/repos/articles.rb', line 176

def self.markdown_to_html(object)
  markdown = File.readlines(path_from_instance(object))
  body = replace_internal_links(markdown, object)
  html = Readiness::GitLab::Markdown.convert(@gl_client, body, true, 'gitlab-com/support/support-pages' )
  data_object_dom(object) + html
end

.path_from_instance(object) ⇒ String

Determines the path based off the instance and object information

Parameters:

  • object (Hash)

Returns:

  • (String)

Author:

  • Jason Colyer

Since:

  • 1.0.15



190
191
192
193
194
195
196
197
# File 'lib/support_readiness/repos/articles.rb', line 190

def self.path_from_instance(object)
  return "#{@location}/managed_content/support-pages/#{object['content_path']}" if @instance == 'Global' && object['section_id'] == 360004459140
  return "#{@location}/managed_content/knowledge-base/#{object['content_path']}" if @instance == 'Global' && object['section_id'] == 15215649512604
  return "#{@location}/managed_content/support-pages/#{object['content_path']}" if @instance == 'US Government' && object['section_id'] == 10593044624020
  return "#{@location}/managed_content/knowledge-base/#{object['content_path']}" if @instance == 'US Government' && object['section_id'] == 29015014994068

  ''
end

.raw_yamlArray

Placeholder functiont to set the global variable @raw_yaml

Returns:

  • (Array)

Author:

  • Jason Colyer

Since:

  • 1.0.12



263
264
265
# File 'lib/support_readiness/repos/articles.rb', line 263

def self.raw_yaml
  @raw_yaml ||= determine_raw_yaml
end

.rearrange_anchorsObject

Rearranges anchors into the correct format

Author:

  • Jason Colyer

Since:

  • 1.0.12



304
305
306
307
308
309
310
311
312
313
314
315
316
# File 'lib/support_readiness/repos/articles.rb', line 304

def self.rearrange_anchors
  %w[h1 h2 h3 h4 h5 h6].each do |h|
    headers = @doc.css(h)
    next if headers.count.zero?

    headers.each do |header|
      anchor = header.children.at_css '.anchor'
      anchor['name'] = anchor['href'].gsub('#', '')
      text = header.children.last
      text.add_next_sibling(anchor)
    end
  end
end

Replaces internal links with their external values

Parameters:

  • object (Hash)

Returns:

  • (String)

Author:

  • Jason Colyer

Since:

  • 1.0.12



220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/support_readiness/repos/articles.rb', line 220

def self.replace_internal_links(lines, object)
  lines.each_with_index do |line, index|
    line.scan(/{{LINK:\ ([^}]+)}}/).each do |matches|
      matches.each do |m|
        parts = m.split('#')
        anchor = determine_anchor(parts)
        lines[index] = line.gsub("{{LINK: #{m}}}", support_url("#{find_file(parts.first)['id']}#{anchor}", object))
      end
    end
  end
  lines.join
end

.replace_modifiersObject

Replaces internal modifiers with their external values

Author:

  • Jason Colyer

Since:

  • 1.0.12



323
324
325
326
327
328
329
330
331
332
333
334
# File 'lib/support_readiness/repos/articles.rb', line 323

def self.replace_modifiers
  @doc.children.each do |children|
    next unless children.to_s =~ /{{([^}]+)}}/

    children.each do |child|
      current = child.parent.get_attribute('class').to_s.split
      current.push(child.to_s[/{{([^}]+)}}/, 1])
      child.parent.set_attribute('class', current.join(' '))
      child.content = child.to_s.gsub(/{{([^}]+)}}/, '')
    end
  end
end

.repo_check(objects) ⇒ Object

Performs basic checks on the repo

Parameters:

  • objects (Array)

    The Array of Hashes derived from parsing the repo files

Author:

  • Jason Colyer

Since:

  • 1.0.12



145
146
147
148
149
150
151
152
# File 'lib/support_readiness/repos/articles.rb', line 145

def self.repo_check(objects)
  duplicate_names = objects.group_by { |o| o.title }.select { |k, v| v.size > 1 }.map(&:first)
  unless duplicate_names.count.zero?
    duplicate_names.each do |d|
      @errors.push("The title '#{d}' is used in multiple files")
    end
  end
end

.report_diffs(diffs) ⇒ Object

Outputs a comparison report

Parameters:

  • diffs (Hash)

    The returned value of compare

Author:

  • Jason Colyer

Since:

  • 1.0.12



41
42
43
44
45
# File 'lib/support_readiness/repos/articles.rb', line 41

def self.report_diffs(diffs)
  puts 'Compare report:'
  puts "- Creates: #{diffs[:creates].count}"
  puts "- Updates: #{diffs[:updates].count}"
end

.report_errorsObject

Outputs an error report and exits with a status code of 1

Author:

  • Jason Colyer

Since:

  • 1.0.12



130
131
132
133
134
135
136
137
# File 'lib/support_readiness/repos/articles.rb', line 130

def self.report_errors
  puts 'The following errors were found in the repo files:'
  @errors.each do |e|
    puts "- #{e}"
  end
  puts 'Rectify the errors and retry. We cannot proceed with those errors occurring.'
  exit 1
end

.support_url(endpoint, yaml) ⇒ String

Returns the external URL for a link

Parameters:

  • endpoint (String)
  • yaml (Hash)

Returns:

  • (String)

Author:

  • Jason Colyer

Since:

  • 1.0.12



289
290
291
292
293
294
295
296
297
# File 'lib/support_readiness/repos/articles.rb', line 289

def self.support_url(endpoint, yaml)
  return "##{endpoint.split('#').last}" if endpoint.split('#').first.to_i == yaml['id'].to_i
  return "https://support.gitlab.com/hc/en-us/articles/#{endpoint}" if @instance == 'Global'
  return "https://gitlab1707170878.zendesk.com/hc/en-us/articles/#{endpoint}" if @instance == 'Global Sandbox'
  return "https://federal-support.gitlab.com/hc/en-us/articles/#{endpoint}" if @instance == 'US Government'
  return "https://gitlabfederalsupport1585318082.zendesk.com/hc/en-us/articles/#{endpoint}" if @instance == 'US Government Sandbox'

  handle_request_error(1, 'Zendesk', 404, { action: 'Find ZD instance', id: @instance })
end

.update_object(repo, zendesk) ⇒ Object

Creates an instance of Zendesk::Automations to use for updates

Parameters:

Returns:

  • (Object)

Author:

  • Jason Colyer

Since:

  • 1.0.12



80
81
82
83
84
# File 'lib/support_readiness/repos/articles.rb', line 80

def self.update_object(repo, zendesk)
  object = repo
  object.id = zendesk.id
  object
end

.validity_check(file, object) ⇒ Object

Performs basic checks on a repo file

Parameters:

  • file (String)

    The path to the repo file

  • object (Hash)

    The Hash derived from parsing a YAML file

Author:

  • Jason Colyer

Since:

  • 1.0.12



343
344
345
346
347
348
349
# File 'lib/support_readiness/repos/articles.rb', line 343

def self.validity_check(file, object)
  folder = file.split("#{@location}/").last.split('/').first
  @errors.push("Missing position: #{file}") if object['position'].nil?
  @errors.push("Missing title: #{file}") if object['title'].nil?
  @errors.push("Inactive article in active folder: #{file}") if folder == 'active' && object['draft']
  @errors.push("Active article in inactive folder: #{file}") if folder == 'inactive' && !object['draft']
end