guidepost 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +46 -18
- data/lib/generators/guidepost/migrate_generator.rb +19 -0
- data/lib/generators/guidepost/models_generator.rb +35 -0
- data/lib/generators/guidepost/templates/migrations.rb +95 -0
- data/lib/generators/guidepost/templates/zendesk_guide.rake +70 -0
- data/lib/generators/guidepost/templates/zendesk_guide_article.rb +95 -0
- data/lib/generators/guidepost/templates/zendesk_guide_article_attachment.rb +47 -0
- data/lib/generators/guidepost/templates/zendesk_guide_category.rb +51 -0
- data/lib/generators/guidepost/templates/zendesk_guide_permission_group.rb +42 -0
- data/lib/generators/guidepost/templates/zendesk_guide_section.rb +62 -0
- data/lib/generators/guidepost/templates/zendesk_guide_user_segment.rb +43 -0
- data/lib/guidepost/provider/zendesk.rb +208 -16
- data/lib/guidepost/storage/s3.rb +1 -1
- metadata +16 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 07224025b0722a20214c83ebac582ad2598712479e6b0bf70d42865a1e565128
|
4
|
+
data.tar.gz: fa6d4711fcd19040e8bf34c2c61387eea7439e96990ff546443cc0bb4e414d55
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f404c59049be377e6c220ac4e9cd14333573f10334b0613fd7d7ee65952fdd9514433576a9a21a2fd8b6d46b0d8dbfda4f61e3149b0c0448a2c4806132b4eb79
|
7
|
+
data.tar.gz: c69162e260001336487daf9c15f17f5f2bea96c65c2d97233c8783edda667e96816f65a262924e4d67d1cbb30780a8c09e9e897bdfdee2f7e0b946175530090d
|
data/README.md
CHANGED
@@ -1,17 +1,24 @@
|
|
1
1
|
# Guidepost
|
2
2
|
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/guidepost.svg)](https://badge.fury.io/rb/guidepost)
|
4
|
+
|
3
5
|
## Purpose
|
4
6
|
|
5
|
-
Your knowledge base is
|
7
|
+
Your knowledge base is an incredibly important component of your company, providing value to your customers, employees, and other stakeholders! The purpose of this project is to help you gain more control over it.
|
8
|
+
|
9
|
+
## Current Use Cases
|
10
|
+
|
11
|
+
* [Back up your knowledge base to S3](#back-up-your-knowledge-base-to-s3)
|
12
|
+
* [Import your knowledge base into your application](#import-your-knowledge-base-into-your-application)
|
6
13
|
|
7
14
|
## Table of Contents
|
8
15
|
|
9
16
|
* [Prerequisites/Requirements](#prerequisites-requirements)
|
10
|
-
* [
|
11
|
-
* [
|
17
|
+
* [Knowledge Base Providers](#knowledge-base-providers)
|
18
|
+
* [Storage Services](#storage-services)
|
12
19
|
* [Installation](#installation)
|
13
20
|
* [Environment Variables](#environment-variables)
|
14
|
-
* [
|
21
|
+
* [Usage](#usage)
|
15
22
|
* [Contact](#contact)
|
16
23
|
* [Roadmap](#roadmap)
|
17
24
|
* [License](#license)
|
@@ -21,18 +28,14 @@ Your knowledge base is a hugely important component of your company! The purpose
|
|
21
28
|
* Rails 4.2+
|
22
29
|
* Ruby 2.5+
|
23
30
|
|
24
|
-
###
|
31
|
+
### Knowledge Base Providers
|
25
32
|
|
26
33
|
* Zendesk
|
27
34
|
|
28
|
-
|
29
|
-
|
30
|
-
### Current Storage Services
|
35
|
+
### Storage Services
|
31
36
|
|
32
37
|
* Amazon Simple Storage Service (Amazon S3)
|
33
38
|
|
34
|
-
If you're looking to use this gem, but work with other storage services like Google Cloud Storage, feel free to reach out!
|
35
|
-
|
36
39
|
### Installation
|
37
40
|
|
38
41
|
Add this line to your application's Gemfile:
|
@@ -68,27 +71,52 @@ ENV["#{YOUR_PROJECT_NAME}_GUIDEPOST_AWS_ACCESS_KEY_ID"]
|
|
68
71
|
# The secret key associated with your AWS account
|
69
72
|
ENV["#{YOUR_PROJECT_NAME}_GUIDEPOST_AWS_SECRET_ACCESS_KEY"]
|
70
73
|
|
74
|
+
# The AWS region associated with this project
|
75
|
+
ENV["#{YOUR_PROJECT_NAME}_GUIDEPOST_AWS_REGION"]
|
76
|
+
|
71
77
|
# The name of the S3 bucket you want to upload your backups to
|
72
78
|
ENV["#{YOUR_PROJECT_NAME}_GUIDEPOST_S3_BUCKET_NAME"]
|
73
79
|
```
|
74
80
|
|
75
|
-
###
|
76
|
-
|
77
|
-
* Back up your knowledge base to S3
|
81
|
+
### Usage
|
78
82
|
|
79
83
|
#### Back up your knowledge base to S3
|
80
84
|
|
85
|
+
##### Implement the backup functionality in your code
|
86
|
+
|
81
87
|
```ruby
|
82
|
-
subdomain
|
83
|
-
|
88
|
+
# The subdomain of your Zendesk account
|
89
|
+
subdomain = "spotify"
|
90
|
+
|
91
|
+
# The name you want to give to your project (name your environment variables accordingly)
|
92
|
+
project_name = "employee-portal"
|
84
93
|
|
85
94
|
zendesk = Guidepost::Provider::Zendesk.new(subdomain: subdomain, project_name: project_name)
|
86
|
-
zendesk.backup_all_articles
|
95
|
+
zendesk.backup_all_articles(sideload: true)
|
87
96
|
```
|
88
97
|
|
98
|
+
##### Run a script outside of your codebase
|
99
|
+
|
100
|
+
$ rake zendesk_guide:backup_articles[spotify,employee-portal]
|
101
|
+
|
102
|
+
#### Import your knowledge base into your application
|
103
|
+
|
104
|
+
|
105
|
+
##### Generate all of the necessary model and migration files
|
106
|
+
|
107
|
+
$ rails g guidepost:models
|
108
|
+
|
109
|
+
##### Perform all of the schema migrations
|
110
|
+
|
111
|
+
$ rails g guidepost:migrate
|
112
|
+
|
113
|
+
##### Run a script outside of your codebase
|
114
|
+
|
115
|
+
$ rake zendesk_guide:import_guides_into_database[spotify,employee-portal]
|
116
|
+
|
89
117
|
## Contact
|
90
118
|
|
91
|
-
If you find and want to address any security issues with the project, email [
|
119
|
+
If you find and want to address any security issues with the project, email [me](mailto:srinitude@gmail.com.com)! For anything else, like bug identifications or feature requests, feel free to file a Github issue or tweet me [@srinitude](https://twitter.com/srinitude).
|
92
120
|
|
93
121
|
## Roadmap
|
94
122
|
|
@@ -98,4 +126,4 @@ If you find and want to address any security issues with the project, email [Kir
|
|
98
126
|
|
99
127
|
## License
|
100
128
|
|
101
|
-
Guidepost is released under the [MIT License](LICENSE).
|
129
|
+
Guidepost is released under the [MIT License](LICENSE).
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "rails/generators/base"
|
2
|
+
|
3
|
+
module Guidepost
|
4
|
+
module Generators
|
5
|
+
|
6
|
+
class MigrateGenerator < Rails::Generators::Base
|
7
|
+
desc "This generator performs the schema and data migrations associated with your current Zendesk guides"
|
8
|
+
|
9
|
+
def self.default_generator_root
|
10
|
+
File.dirname(__FILE__)
|
11
|
+
end
|
12
|
+
|
13
|
+
def schema_migration
|
14
|
+
`rake db:migrate`
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require "rails/generators/base"
|
2
|
+
|
3
|
+
module Guidepost
|
4
|
+
module Generators
|
5
|
+
|
6
|
+
class ModelsGenerator < Rails::Generators::Base
|
7
|
+
desc "This generator creates the migrations, models, and tests associated with your current Zendesk guides"
|
8
|
+
|
9
|
+
def self.default_generator_root
|
10
|
+
File.dirname(__FILE__)
|
11
|
+
end
|
12
|
+
|
13
|
+
def migrations
|
14
|
+
timestamp = Time.now.strftime('%Y%m%d%H%M%S')
|
15
|
+
template "migrations.rb", "db/migrate/#{timestamp}_create_zendesk_guide_models.rb"
|
16
|
+
template "zendesk_guide.rake", "lib/tasks/zendesk_guide.rake"
|
17
|
+
end
|
18
|
+
|
19
|
+
def models
|
20
|
+
model_templates = [
|
21
|
+
"zendesk_guide_category.rb",
|
22
|
+
"zendesk_guide_section.rb",
|
23
|
+
"zendesk_guide_article.rb",
|
24
|
+
"zendesk_guide_article_attachment.rb",
|
25
|
+
"zendesk_guide_permission_group.rb",
|
26
|
+
"zendesk_guide_user_segment.rb"
|
27
|
+
]
|
28
|
+
model_templates.each do |t|
|
29
|
+
template t, "app/models/#{t}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
class CreateZendeskGuideModels < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :zendesk_guide_categories do |t|
|
4
|
+
t.integer :category_id, limit: 8
|
5
|
+
t.string :name, null: false, default: "General"
|
6
|
+
t.string :description
|
7
|
+
t.string :locale, null: false, default: "en-us"
|
8
|
+
t.string :source_locale
|
9
|
+
t.string :url
|
10
|
+
t.string :html_url
|
11
|
+
t.boolean :outdated
|
12
|
+
t.integer :position
|
13
|
+
t.timestamp :category_created_at
|
14
|
+
t.timestamp :category_updated_at
|
15
|
+
end
|
16
|
+
|
17
|
+
create_table :zendesk_guide_sections do |t|
|
18
|
+
t.integer :zendesk_guide_category_id
|
19
|
+
t.integer :category_id, limit: 8
|
20
|
+
t.integer :section_id, limit: 8
|
21
|
+
t.string :name, null: false, default: "General"
|
22
|
+
t.string :description
|
23
|
+
t.string :locale, null: false, default: "en-us"
|
24
|
+
t.string :source_locale
|
25
|
+
t.string :url
|
26
|
+
t.string :html_url
|
27
|
+
t.boolean :outdated
|
28
|
+
t.integer :position
|
29
|
+
t.timestamp :section_created_at
|
30
|
+
t.timestamp :section_updated_at
|
31
|
+
end
|
32
|
+
|
33
|
+
create_table :zendesk_guide_user_segments do |t|
|
34
|
+
t.integer :user_segment_id, limit: 8
|
35
|
+
t.string :name
|
36
|
+
t.string :user_type
|
37
|
+
t.integer :group_ids, array: true, default: [], limit: 8
|
38
|
+
t.integer :organization_ids, array: true, default: [], limit: 8
|
39
|
+
t.string :tags, array: true, default: []
|
40
|
+
t.timestamp :user_segment_created_at
|
41
|
+
t.timestamp :user_segment_updated_at
|
42
|
+
end
|
43
|
+
|
44
|
+
create_table :zendesk_guide_permission_groups do |t|
|
45
|
+
t.integer :permission_group_id, limit: 8
|
46
|
+
t.string :name
|
47
|
+
t.integer :edit, array: true, default: [], limit: 8
|
48
|
+
t.integer :publish, array: true, default: [], limit: 8
|
49
|
+
t.timestamp :permission_group_created_at
|
50
|
+
t.timestamp :permission_group_updated_at
|
51
|
+
t.boolean :built_in
|
52
|
+
end
|
53
|
+
|
54
|
+
create_table :zendesk_guide_articles do |t|
|
55
|
+
t.integer :zendesk_guide_section_id
|
56
|
+
t.integer :section_id, limit: 8
|
57
|
+
t.integer :article_id, limit: 8
|
58
|
+
t.string :url
|
59
|
+
t.string :html_url
|
60
|
+
t.string :title, null: false, default: "FAQ"
|
61
|
+
t.string :body
|
62
|
+
t.string :locale, null: false, default: "en-us"
|
63
|
+
t.string :source_locale
|
64
|
+
t.integer :author_id, limit: 8
|
65
|
+
t.boolean :comments_disabled, default: false
|
66
|
+
t.string :outdated_locales, array: true
|
67
|
+
t.string :label_names, array: true, default: []
|
68
|
+
t.boolean :draft, default: false
|
69
|
+
t.boolean :promoted, default: false
|
70
|
+
t.integer :position, default: 0
|
71
|
+
t.integer :vote_sum
|
72
|
+
t.integer :vote_count
|
73
|
+
t.integer :user_segment_id, limit: 8
|
74
|
+
t.integer :zendesk_guide_user_segment_id
|
75
|
+
t.integer :permission_group_id, limit: 8
|
76
|
+
t.integer :zendesk_guide_permission_group_id
|
77
|
+
t.timestamp :article_created_at
|
78
|
+
t.timestamp :article_edited_at
|
79
|
+
t.timestamp :article_updated_at
|
80
|
+
end
|
81
|
+
|
82
|
+
create_table :zendesk_guide_article_attachments do |t|
|
83
|
+
t.integer :zendesk_guide_article_id
|
84
|
+
t.integer :article_id, limit: 8
|
85
|
+
t.integer :article_attachment_id, limit: 8
|
86
|
+
t.string :url
|
87
|
+
t.string :file_name
|
88
|
+
t.string :content_url
|
89
|
+
t.integer :size, limit: 8
|
90
|
+
t.boolean :inline, default: false
|
91
|
+
t.timestamp :article_attachment_created_at
|
92
|
+
t.timestamp :article_attachment_updated_at
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
namespace :zendesk_guide do
|
2
|
+
|
3
|
+
desc "Backup articles from knowledge base provider (i.e. Zendesk currently) [customize this task if you want to add more functionality]"
|
4
|
+
task :backup_articles, [:subdomain, :project_name] => :environment do |t, args|
|
5
|
+
begin
|
6
|
+
subdomain = args[:subdomain]
|
7
|
+
project_name = args[:project_name]
|
8
|
+
|
9
|
+
if subdomain.nil? || project_name.nil?
|
10
|
+
raise "Subdomain and/or project_name arguments are missing" if Rails.env.development?
|
11
|
+
end
|
12
|
+
|
13
|
+
zendesk = Guidepost::Provider::Zendesk.new(subdomain: subdomain, project_name: project_name)
|
14
|
+
zendesk.backup_all_articles(sideload: true)
|
15
|
+
rescue => e
|
16
|
+
raise e if Rails.env.development?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
desc "Import guides and respective models into database [customize this task if you want to add more functionality]"
|
21
|
+
task :import_guides_into_database, [:subdomain, :project_name] => :environment do |t, args|
|
22
|
+
begin
|
23
|
+
subdomain = args[:subdomain]
|
24
|
+
project_name = args[:project_name]
|
25
|
+
|
26
|
+
if subdomain.nil? || project_name.nil?
|
27
|
+
raise "Subdomain and/or project_name arguments are missing" if Rails.env.development?
|
28
|
+
end
|
29
|
+
|
30
|
+
zendesk = Guidepost::Provider::Zendesk.new(subdomain: subdomain, project_name: project_name)
|
31
|
+
articles_sections_and_categories = zendesk.retrieve_all_articles(sideload: true)
|
32
|
+
|
33
|
+
categories = articles_sections_and_categories[:categories]
|
34
|
+
category_objects = []
|
35
|
+
ZendeskGuideCategory.find_or_create_categories(categories: categories, category_objects: category_objects)
|
36
|
+
|
37
|
+
sections = articles_sections_and_categories[:sections]
|
38
|
+
section_objects = []
|
39
|
+
ZendeskGuideSection.find_or_create_sections(
|
40
|
+
sections: sections,
|
41
|
+
section_objects: section_objects,
|
42
|
+
category_objects: category_objects
|
43
|
+
)
|
44
|
+
|
45
|
+
user_segments = zendesk.retrieve_all_user_segments
|
46
|
+
user_segment_objects = []
|
47
|
+
ZendeskGuideUserSegment.find_or_create_user_segments(user_segments: user_segments, user_segment_objects: user_segment_objects)
|
48
|
+
|
49
|
+
permission_groups = zendesk.retrieve_all_permission_groups
|
50
|
+
permission_group_objects = []
|
51
|
+
ZendeskGuidePermissionGroup.find_or_create_permission_groups(permission_groups: permission_groups, permission_group_objects: permission_group_objects)
|
52
|
+
|
53
|
+
articles = articles_sections_and_categories[:articles]
|
54
|
+
article_objects = []
|
55
|
+
ZendeskGuideArticle.find_or_create_articles(
|
56
|
+
articles: articles,
|
57
|
+
article_objects: article_objects,
|
58
|
+
section_objects: section_objects,
|
59
|
+
user_segment_objects: user_segment_objects,
|
60
|
+
permission_group_objects: permission_group_objects
|
61
|
+
)
|
62
|
+
|
63
|
+
article_attachments = zendesk.retrieve_all_article_attachments(articles: articles)
|
64
|
+
ZendeskGuideArticleAttachment.find_or_create_article_attachments(article_attachments: article_attachments, article_objects: article_objects)
|
65
|
+
rescue => e
|
66
|
+
raise e if Rails.env.development?
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
class ZendeskGuideArticle < ActiveRecord::Base
|
2
|
+
belongs_to :zendesk_guide_section
|
3
|
+
belongs_to :zendesk_guide_user_segment
|
4
|
+
belongs_to :zendesk_guide_permission_group
|
5
|
+
|
6
|
+
has_many :zendesk_guide_article_attachments
|
7
|
+
|
8
|
+
validates :title, presence: true
|
9
|
+
validates :locale, presence: true
|
10
|
+
|
11
|
+
def self.find_or_create_articles(options={})
|
12
|
+
articles = options[:articles]
|
13
|
+
article_objects = options[:article_objects]
|
14
|
+
section_objects = options[:section_objects]
|
15
|
+
user_segment_objects = options[:user_segment_objects]
|
16
|
+
permission_group_objects = options[:permission_group_objects]
|
17
|
+
allowed_attributes = [
|
18
|
+
:section_id,
|
19
|
+
:article_id,
|
20
|
+
:url,
|
21
|
+
:html_url,
|
22
|
+
:title,
|
23
|
+
:body,
|
24
|
+
:locale,
|
25
|
+
:source_locale,
|
26
|
+
:author_id,
|
27
|
+
:comments_disabled,
|
28
|
+
:outdated_locales,
|
29
|
+
:label_names,
|
30
|
+
:draft,
|
31
|
+
:promoted,
|
32
|
+
:position,
|
33
|
+
:vote_sum,
|
34
|
+
:vote_count,
|
35
|
+
:user_segment_id,
|
36
|
+
:permission_group_id,
|
37
|
+
:article_created_at,
|
38
|
+
:article_edited_at,
|
39
|
+
:article_updated_at
|
40
|
+
]
|
41
|
+
|
42
|
+
articles.each do |a|
|
43
|
+
article_hash = a.clone
|
44
|
+
|
45
|
+
article_hash[:article_id] = article_hash["id"]
|
46
|
+
article_hash.delete("id")
|
47
|
+
|
48
|
+
article_hash[:article_created_at] = article_hash["created_at"]
|
49
|
+
article_hash.delete("created_at")
|
50
|
+
|
51
|
+
article_hash[:article_updated_at] = article_hash["updated_at"]
|
52
|
+
article_hash.delete("updated_at")
|
53
|
+
|
54
|
+
article_hash.symbolize_keys!
|
55
|
+
|
56
|
+
article_hash.each_key do |k|
|
57
|
+
article_hash.delete(k) if !allowed_attributes.include?(k)
|
58
|
+
end
|
59
|
+
|
60
|
+
article = ZendeskGuideArticle.where(article_id: article_hash[:article_id]).first
|
61
|
+
article.update(article_hash) if !article.nil?
|
62
|
+
article = ZendeskGuideArticle.create(article_hash) if article.nil?
|
63
|
+
|
64
|
+
changed = false
|
65
|
+
|
66
|
+
section_objects.each do |so|
|
67
|
+
is_correct_section = (article.section_id == so.section_id)
|
68
|
+
if is_correct_section
|
69
|
+
article.zendesk_guide_section = so
|
70
|
+
changed = true
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
user_segment_objects.each do |us|
|
75
|
+
is_correct_user_segment = (article.user_segment_id == us.user_segment_id)
|
76
|
+
if is_correct_user_segment
|
77
|
+
article.zendesk_guide_user_segment = us
|
78
|
+
changed = true
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
permission_group_objects.each do |pg|
|
83
|
+
is_correct_permission_group = (article.permission_group_id == pg.permission_group_id)
|
84
|
+
if is_correct_permission_group
|
85
|
+
article.zendesk_guide_permission_group = pg
|
86
|
+
changed = true
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
article.save if changed
|
91
|
+
|
92
|
+
article_objects << article
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
class ZendeskGuideArticleAttachment < ActiveRecord::Base
|
2
|
+
belongs_to :zendesk_guide_article
|
3
|
+
|
4
|
+
def self.find_or_create_article_attachments(options={})
|
5
|
+
article_attachments = options[:article_attachments]
|
6
|
+
article_objects = options[:article_objects]
|
7
|
+
allowed_attributes = [
|
8
|
+
:article_id,
|
9
|
+
:article_attachment_id,
|
10
|
+
:url,
|
11
|
+
:file_name,
|
12
|
+
:content_url,
|
13
|
+
:size,
|
14
|
+
:inline,
|
15
|
+
:article_attachment_created_at,
|
16
|
+
:article_attachment_updated_at
|
17
|
+
]
|
18
|
+
|
19
|
+
article_attachments.each do |aa|
|
20
|
+
article_attachment_hash = aa.clone
|
21
|
+
|
22
|
+
article_attachment_hash[:article_attachment_id] = article_attachment_hash["id"]
|
23
|
+
article_attachment_hash.delete("id")
|
24
|
+
|
25
|
+
article_attachment_hash[:article_attachment_created_at] = article_attachment_hash["created_at"]
|
26
|
+
article_attachment_hash.delete("created_at")
|
27
|
+
|
28
|
+
article_attachment_hash[:article_attachment_updated_at] = article_attachment_hash["updated_at"]
|
29
|
+
article_attachment_hash.delete("updated_at")
|
30
|
+
|
31
|
+
article_attachment_hash.symbolize_keys!
|
32
|
+
|
33
|
+
article_attachment = ZendeskGuideArticleAttachment.where(article_attachment_id: article_attachment_hash[:article_attachment_id]).first
|
34
|
+
article_attachment.update(article_attachment_hash) if !article_attachment.nil?
|
35
|
+
article_attachment = ZendeskGuideArticleAttachment.create(article_attachment_hash) if article_attachment.nil?
|
36
|
+
|
37
|
+
article_objects.each do |a|
|
38
|
+
is_correct_article = (article_attachment.article_id == a.article_id)
|
39
|
+
if is_correct_article
|
40
|
+
article_attachment.zendesk_guide_article = co
|
41
|
+
article_attachment.save
|
42
|
+
break
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
class ZendeskGuideCategory < ActiveRecord::Base
|
2
|
+
has_many :zendesk_guide_sections
|
3
|
+
|
4
|
+
validates :name, presence: true
|
5
|
+
validates :locale, presence: true
|
6
|
+
|
7
|
+
def self.find_or_create_categories(options={})
|
8
|
+
categories = options[:categories]
|
9
|
+
category_objects = options[:category_objects]
|
10
|
+
|
11
|
+
allowed_attributes = [
|
12
|
+
:category_id,
|
13
|
+
:name,
|
14
|
+
:description,
|
15
|
+
:locale,
|
16
|
+
:source_locale,
|
17
|
+
:url,
|
18
|
+
:html_url,
|
19
|
+
:outdated,
|
20
|
+
:position,
|
21
|
+
:category_created_at,
|
22
|
+
:category_updated_at
|
23
|
+
]
|
24
|
+
|
25
|
+
categories.each do |c|
|
26
|
+
category_hash = c.clone
|
27
|
+
|
28
|
+
category_hash[:category_id] = category_hash["id"]
|
29
|
+
category_hash.delete("id")
|
30
|
+
|
31
|
+
category_hash[:category_created_at] = category_hash["created_at"]
|
32
|
+
category_hash.delete("created_at")
|
33
|
+
|
34
|
+
category_hash[:category_updated_at] = category_hash["updated_at"]
|
35
|
+
category_hash.delete("updated_at")
|
36
|
+
|
37
|
+
category_hash.symbolize_keys!
|
38
|
+
|
39
|
+
category_hash.each_key do |k|
|
40
|
+
category_hash.delete(k) if !allowed_attributes.include?(k)
|
41
|
+
end
|
42
|
+
|
43
|
+
category = ZendeskGuideCategory.where(category_id: category_hash[:category_id]).first
|
44
|
+
category.update(category_hash) if !category.nil?
|
45
|
+
category = ZendeskGuideCategory.create(category_hash) if category.nil?
|
46
|
+
|
47
|
+
category_objects << category
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
class ZendeskGuidePermissionGroup < ActiveRecord::Base
|
2
|
+
has_many :zendesk_guide_articles
|
3
|
+
|
4
|
+
def self.find_or_create_permission_groups(options={})
|
5
|
+
permission_groups = options[:permission_groups]
|
6
|
+
permission_group_objects = options[:permission_group_objects]
|
7
|
+
allowed_attributes = [
|
8
|
+
:permission_group_id,
|
9
|
+
:name,
|
10
|
+
:edit,
|
11
|
+
:publish,
|
12
|
+
:permission_group_created_at,
|
13
|
+
:permission_group_updated_at,
|
14
|
+
:built_in
|
15
|
+
]
|
16
|
+
|
17
|
+
permission_groups.each do |p|
|
18
|
+
permission_group_hash = p.clone
|
19
|
+
|
20
|
+
permission_group_hash[:permission_group_id] = permission_group_hash["id"]
|
21
|
+
permission_group_hash.delete("id")
|
22
|
+
|
23
|
+
permission_group_hash[:permission_group_created_at] = permission_group_hash["created_at"]
|
24
|
+
permission_group_hash.delete("created_at")
|
25
|
+
|
26
|
+
permission_group_hash[:permission_group_updated_at] = permission_group_hash["updated_at"]
|
27
|
+
permission_group_hash.delete("updated_at")
|
28
|
+
|
29
|
+
permission_group_hash.symbolize_keys!
|
30
|
+
|
31
|
+
permission_group_hash.each_key do |k|
|
32
|
+
permission_group_hash.delete(k) if !allowed_attributes.include?(k)
|
33
|
+
end
|
34
|
+
|
35
|
+
permission_group = ZendeskGuidePermissionGroup.where(permission_group_id: permission_group_hash[:permission_group_id]).first
|
36
|
+
permission_group.update(permission_group_hash) if !permission_group.nil?
|
37
|
+
permission_group = ZendeskGuidePermissionGroup.create(permission_group_hash) if permission_group.nil?
|
38
|
+
|
39
|
+
permission_group_objects << permission_group
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
class ZendeskGuideSection < ActiveRecord::Base
|
2
|
+
belongs_to :zendesk_guide_category
|
3
|
+
has_many :zendesk_guide_articles
|
4
|
+
|
5
|
+
validates :name, presence: true
|
6
|
+
validates :locale, presence: true
|
7
|
+
|
8
|
+
def self.find_or_create_sections(options={})
|
9
|
+
sections = options[:sections]
|
10
|
+
section_objects = options[:section_objects]
|
11
|
+
category_objects = options[:category_objects]
|
12
|
+
|
13
|
+
allowed_attributes = [
|
14
|
+
:category_id,
|
15
|
+
:section_id,
|
16
|
+
:name,
|
17
|
+
:description,
|
18
|
+
:locale,
|
19
|
+
:source_locale,
|
20
|
+
:url,
|
21
|
+
:html_url,
|
22
|
+
:outdated,
|
23
|
+
:position,
|
24
|
+
:section_created_at,
|
25
|
+
:section_updated_at
|
26
|
+
]
|
27
|
+
|
28
|
+
sections.each do |s|
|
29
|
+
section_hash = s.clone
|
30
|
+
|
31
|
+
section_hash[:section_id] = section_hash["id"]
|
32
|
+
section_hash.delete("id")
|
33
|
+
|
34
|
+
section_hash[:section_created_at] = section_hash["created_at"]
|
35
|
+
section_hash.delete("created_at")
|
36
|
+
|
37
|
+
section_hash[:section_updated_at] = section_hash["updated_at"]
|
38
|
+
section_hash.delete("updated_at")
|
39
|
+
|
40
|
+
section_hash.symbolize_keys!
|
41
|
+
|
42
|
+
section_hash.each_key do |k|
|
43
|
+
section_hash.delete(k) if !allowed_attributes.include?(k)
|
44
|
+
end
|
45
|
+
|
46
|
+
section = ZendeskGuideSection.where(section_id: section_hash[:section_id]).first
|
47
|
+
section.update(section_hash) if !section.nil?
|
48
|
+
section = ZendeskGuideSection.create(section_hash) if section.nil?
|
49
|
+
|
50
|
+
category_objects.each do |co|
|
51
|
+
is_correct_category = (section.category_id == co.category_id)
|
52
|
+
if is_correct_category
|
53
|
+
section.zendesk_guide_category = co
|
54
|
+
section.save
|
55
|
+
break
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
section_objects << section
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class ZendeskGuideUserSegment < ActiveRecord::Base
|
2
|
+
has_many :zendesk_guide_articles
|
3
|
+
|
4
|
+
def self.find_or_create_user_segments(options={})
|
5
|
+
user_segments = options[:user_segments]
|
6
|
+
user_segment_objects = options[:user_segment_objects]
|
7
|
+
allowed_attributes = [
|
8
|
+
:user_segment_id,
|
9
|
+
:name,
|
10
|
+
:user_type,
|
11
|
+
:group_ids,
|
12
|
+
:organization_ids,
|
13
|
+
:tags,
|
14
|
+
:user_segment_created_at,
|
15
|
+
:user_segment_updated_at
|
16
|
+
]
|
17
|
+
|
18
|
+
user_segments.each do |s|
|
19
|
+
user_segment_hash = s.clone
|
20
|
+
|
21
|
+
user_segment_hash[:user_segment_id] = user_segment_hash["id"]
|
22
|
+
user_segment_hash.delete("id")
|
23
|
+
|
24
|
+
user_segment_hash[:user_segment_created_at] = user_segment_hash["created_at"]
|
25
|
+
user_segment_hash.delete("created_at")
|
26
|
+
|
27
|
+
user_segment_hash[:user_segment_updated_at] = user_segment_hash["updated_at"]
|
28
|
+
user_segment_hash.delete("updated_at")
|
29
|
+
|
30
|
+
user_segment_hash.symbolize_keys!
|
31
|
+
|
32
|
+
user_segment_hash.each_key do |k|
|
33
|
+
user_segment_hash.delete(k) if !allowed_attributes.include?(k)
|
34
|
+
end
|
35
|
+
|
36
|
+
user_segment = ZendeskGuideUserSegment.where(user_segment_id: user_segment_hash[:user_segment_id]).first
|
37
|
+
user_segment.update(user_segment_hash) if !user_segment.nil?
|
38
|
+
user_segment = ZendeskGuideUserSegment.create(user_segment_hash) if user_segment.nil?
|
39
|
+
|
40
|
+
user_segment_objects << user_segment
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
module Guidepost
|
2
2
|
module Provider
|
3
3
|
class Zendesk
|
4
|
-
|
5
|
-
|
4
|
+
attr_reader :subdomain
|
5
|
+
attr_reader :project_name
|
6
6
|
|
7
7
|
def initialize(options={})
|
8
8
|
@subdomain = options[:subdomain]
|
@@ -17,31 +17,112 @@ module Guidepost
|
|
17
17
|
@password = ENV["#{@project_name}_GUIDEPOST_ZENDESK_PASSWORD_TOKEN"]
|
18
18
|
end
|
19
19
|
|
20
|
-
def backup_all_articles
|
20
|
+
def backup_all_articles(options={})
|
21
21
|
# Get all articles (with pagination)
|
22
|
-
|
22
|
+
sideload = options[:sideload] || false
|
23
|
+
articles = self.retrieve_all_articles(sideload: sideload)
|
23
24
|
|
24
25
|
# Upload to S3
|
25
26
|
timestamp = Time.now.strftime('%Y%m%d%H%M%S')
|
26
|
-
|
27
|
+
|
28
|
+
filename = "#{timestamp}"
|
29
|
+
filename += "_with_sideload" if sideload
|
30
|
+
@storage.upload_file(path: "zendesk/article_backups/#{filename}.json", string_content: articles.to_json)
|
27
31
|
|
28
32
|
articles.count
|
29
33
|
end
|
30
34
|
|
31
|
-
def retrieve_all_articles
|
32
|
-
|
35
|
+
def retrieve_all_articles(options={})
|
36
|
+
sideload = options[:sideload] || false
|
33
37
|
page_next = nil
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
38
|
+
articles = []
|
39
|
+
article_attachments = nil
|
40
|
+
|
41
|
+
if !sideload
|
42
|
+
while true
|
43
|
+
page_articles, page_next = self.retrieve_articles(url: page_next)
|
44
|
+
break if page_articles.nil? || page_articles.empty?
|
45
|
+
articles += page_articles
|
46
|
+
break if page_next.nil?
|
47
|
+
end
|
48
|
+
|
49
|
+
article_attachments = self.retrieve_all_article_attachments(articles: articles)
|
50
|
+
|
51
|
+
return {
|
52
|
+
articles: articles,
|
53
|
+
article_count: articles.count,
|
54
|
+
article_attachments: article_attachments,
|
55
|
+
article_attachment_count: article_attachments.count
|
56
|
+
}
|
57
|
+
else
|
58
|
+
sections = []
|
59
|
+
categories = []
|
60
|
+
|
61
|
+
section_urls = Hash.new
|
62
|
+
category_urls = Hash.new
|
63
|
+
|
64
|
+
while true
|
65
|
+
page, page_next = self.retrieve_articles(url: page_next, sideload: true)
|
66
|
+
|
67
|
+
articles_from_page = page["articles"]
|
68
|
+
sections_from_page = page["sections"]
|
69
|
+
categories_from_page = page["categories"]
|
70
|
+
|
71
|
+
no_more_articles = articles_from_page.nil? || articles_from_page.empty?
|
72
|
+
no_more_sections = sections_from_page.nil? || sections_from_page.empty?
|
73
|
+
no_more_categories = categories_from_page.nil? || categories_from_page.empty?
|
74
|
+
|
75
|
+
break if no_more_articles && no_more_sections && no_more_categories
|
76
|
+
|
77
|
+
articles += articles_from_page
|
78
|
+
|
79
|
+
sections_from_page.each do |s|
|
80
|
+
url = s["url"]
|
81
|
+
if !section_urls.has_key?(url)
|
82
|
+
section_urls[url] = 1
|
83
|
+
else
|
84
|
+
section_urls[url] += 1
|
85
|
+
end
|
86
|
+
sections << s if section_urls[url] == 1
|
87
|
+
end
|
88
|
+
|
89
|
+
categories_from_page.each do |c|
|
90
|
+
url = c["url"]
|
91
|
+
if !category_urls.has_key?(url)
|
92
|
+
category_urls[url] = 1
|
93
|
+
else
|
94
|
+
category_urls[url] += 1
|
95
|
+
end
|
96
|
+
categories << c if category_urls[url] == 1
|
97
|
+
end
|
98
|
+
|
99
|
+
break if page_next.nil?
|
100
|
+
end
|
101
|
+
|
102
|
+
article_attachments = self.retrieve_all_article_attachments(articles: articles)
|
103
|
+
|
104
|
+
return {
|
105
|
+
categories: categories,
|
106
|
+
category_count: categories.count,
|
107
|
+
sections: sections,
|
108
|
+
section_count: sections.count,
|
109
|
+
articles: articles,
|
110
|
+
article_count: articles.count,
|
111
|
+
article_attachments: article_attachments,
|
112
|
+
article_attachment_count: article_attachments.count
|
113
|
+
}
|
39
114
|
end
|
40
|
-
articles
|
41
115
|
end
|
42
116
|
|
43
|
-
def retrieve_articles(
|
44
|
-
url =
|
117
|
+
def retrieve_articles(options={})
|
118
|
+
url = options[:url]
|
119
|
+
sideload = options[:sideload] || false
|
120
|
+
|
121
|
+
if !sideload
|
122
|
+
url = "#{self.base_api_url}/help_center/articles.json?per_page=25&page=1" if url.nil?
|
123
|
+
else
|
124
|
+
url = "#{self.base_api_url}/help_center/articles.json?include=sections,categories&per_page=25&page=1" if url.nil?
|
125
|
+
end
|
45
126
|
|
46
127
|
uri = URI.parse(url)
|
47
128
|
|
@@ -56,7 +137,118 @@ module Guidepost
|
|
56
137
|
body = response.body.force_encoding("UTF-8")
|
57
138
|
|
58
139
|
j_body = JSON.parse(body)
|
59
|
-
|
140
|
+
|
141
|
+
if !sideload
|
142
|
+
return j_body['articles'], j_body['next_page']
|
143
|
+
else
|
144
|
+
return j_body, j_body['next_page']
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def retrieve_all_user_segments(options={})
|
149
|
+
user_segments = []
|
150
|
+
next_page = nil
|
151
|
+
|
152
|
+
while true
|
153
|
+
segments, next_page = self.retrieve_user_segments(url: next_page)
|
154
|
+
break if segments.nil? || segments.empty?
|
155
|
+
user_segments += segments
|
156
|
+
break if next_page.nil?
|
157
|
+
end
|
158
|
+
|
159
|
+
user_segments
|
160
|
+
end
|
161
|
+
|
162
|
+
def retrieve_user_segments(options={})
|
163
|
+
url = options[:url]
|
164
|
+
url = "#{self.base_api_url}/help_center/user_segments.json?per_page=25&page=1" if url.nil?
|
165
|
+
uri = URI.parse(url)
|
166
|
+
|
167
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
168
|
+
http.use_ssl = true
|
169
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
170
|
+
|
171
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
172
|
+
request.basic_auth(@email, @password)
|
173
|
+
response = http.request(request)
|
174
|
+
|
175
|
+
body = response.body.force_encoding("UTF-8")
|
176
|
+
|
177
|
+
j_body = JSON.parse(body)
|
178
|
+
|
179
|
+
return j_body["user_segments"], j_body['next_page']
|
180
|
+
end
|
181
|
+
|
182
|
+
def retrieve_all_permission_groups(options={})
|
183
|
+
permission_groups = []
|
184
|
+
next_page = nil
|
185
|
+
|
186
|
+
while true
|
187
|
+
groups, next_page = self.retrieve_permission_groups(url: next_page)
|
188
|
+
break if groups.nil? || groups.empty?
|
189
|
+
permission_groups += groups
|
190
|
+
break if next_page.nil?
|
191
|
+
end
|
192
|
+
|
193
|
+
permission_groups
|
194
|
+
end
|
195
|
+
|
196
|
+
def retrieve_permission_groups(options={})
|
197
|
+
url = options[:url]
|
198
|
+
url = "#{self.base_api_url}/guide/permission_groups.json?per_page=25&page=1" if url.nil?
|
199
|
+
uri = URI.parse(url)
|
200
|
+
|
201
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
202
|
+
http.use_ssl = true
|
203
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
204
|
+
|
205
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
206
|
+
request.basic_auth(@email, @password)
|
207
|
+
response = http.request(request)
|
208
|
+
|
209
|
+
body = response.body.force_encoding("UTF-8")
|
210
|
+
|
211
|
+
j_body = JSON.parse(body)
|
212
|
+
|
213
|
+
return j_body["permission_groups"], j_body['next_page']
|
214
|
+
end
|
215
|
+
|
216
|
+
def retrieve_all_article_attachments(options={})
|
217
|
+
article_attachments = []
|
218
|
+
next_page = nil
|
219
|
+
|
220
|
+
articles = options[:articles]
|
221
|
+
articles.each do |article|
|
222
|
+
while true
|
223
|
+
attachments, next_page = self.retrieve_article_attachments(for_article: article)
|
224
|
+
break if attachments.nil? || attachments.empty?
|
225
|
+
article_attachments += attachments
|
226
|
+
break if next_page.nil?
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
article_attachments
|
231
|
+
end
|
232
|
+
|
233
|
+
def retrieve_article_attachments(options={})
|
234
|
+
article = options[:for_article]
|
235
|
+
|
236
|
+
url = "#{self.base_api_url}/help_center/articles/#{article["id"]}/attachments.json?per_page=25&page=1" if url.nil?
|
237
|
+
uri = URI.parse(url)
|
238
|
+
|
239
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
240
|
+
http.use_ssl = true
|
241
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
242
|
+
|
243
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
244
|
+
request.basic_auth(@email, @password)
|
245
|
+
response = http.request(request)
|
246
|
+
|
247
|
+
body = response.body.force_encoding("UTF-8")
|
248
|
+
|
249
|
+
j_body = JSON.parse(body)
|
250
|
+
|
251
|
+
return j_body["article_attachments"], j_body["next_page"]
|
60
252
|
end
|
61
253
|
|
62
254
|
def base_api_url
|
data/lib/guidepost/storage/s3.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: guidepost
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kiren Srinivasan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-05-
|
11
|
+
date: 2019-05-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk
|
@@ -24,18 +24,28 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '2.0'
|
27
|
-
description: Harness your knowledge base in your
|
28
|
-
email:
|
27
|
+
description: Harness your knowledge base in your Rails applications
|
28
|
+
email: srinitude@gmail.com
|
29
29
|
executables: []
|
30
30
|
extensions: []
|
31
31
|
extra_rdoc_files: []
|
32
32
|
files:
|
33
33
|
- LICENSE
|
34
34
|
- README.md
|
35
|
+
- lib/generators/guidepost/migrate_generator.rb
|
36
|
+
- lib/generators/guidepost/models_generator.rb
|
37
|
+
- lib/generators/guidepost/templates/migrations.rb
|
38
|
+
- lib/generators/guidepost/templates/zendesk_guide.rake
|
39
|
+
- lib/generators/guidepost/templates/zendesk_guide_article.rb
|
40
|
+
- lib/generators/guidepost/templates/zendesk_guide_article_attachment.rb
|
41
|
+
- lib/generators/guidepost/templates/zendesk_guide_category.rb
|
42
|
+
- lib/generators/guidepost/templates/zendesk_guide_permission_group.rb
|
43
|
+
- lib/generators/guidepost/templates/zendesk_guide_section.rb
|
44
|
+
- lib/generators/guidepost/templates/zendesk_guide_user_segment.rb
|
35
45
|
- lib/guidepost.rb
|
36
46
|
- lib/guidepost/provider/zendesk.rb
|
37
47
|
- lib/guidepost/storage/s3.rb
|
38
|
-
homepage:
|
48
|
+
homepage: https://github.com/srinitude/guidepost
|
39
49
|
licenses:
|
40
50
|
- MIT
|
41
51
|
metadata: {}
|
@@ -58,5 +68,5 @@ rubyforge_project:
|
|
58
68
|
rubygems_version: 2.7.6
|
59
69
|
signing_key:
|
60
70
|
specification_version: 4
|
61
|
-
summary: Harness your knowledge base in your
|
71
|
+
summary: Harness your knowledge base in your Rails applications
|
62
72
|
test_files: []
|