agave-client 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.travis.yml +14 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/CONTRIBUTORS.md +34 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +29 -0
- data/README.md +41 -0
- data/Rakefile +7 -0
- data/agave-client.gemspec +63 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/exe/agave +10 -0
- data/lib/agave.rb +11 -0
- data/lib/agave/api_client.rb +97 -0
- data/lib/agave/api_error.rb +23 -0
- data/lib/agave/cli.rb +93 -0
- data/lib/agave/dump/dsl/add_to_data_file.rb +14 -0
- data/lib/agave/dump/dsl/create_data_file.rb +14 -0
- data/lib/agave/dump/dsl/create_post.rb +33 -0
- data/lib/agave/dump/dsl/directory.rb +30 -0
- data/lib/agave/dump/dsl/root.rb +37 -0
- data/lib/agave/dump/format.rb +28 -0
- data/lib/agave/dump/format/json.rb +18 -0
- data/lib/agave/dump/format/toml.rb +31 -0
- data/lib/agave/dump/format/yaml.rb +55 -0
- data/lib/agave/dump/operation/add_to_data_file.rb +41 -0
- data/lib/agave/dump/operation/create_data_file.rb +28 -0
- data/lib/agave/dump/operation/create_post.rb +34 -0
- data/lib/agave/dump/operation/directory.rb +34 -0
- data/lib/agave/dump/operation/root.rb +27 -0
- data/lib/agave/dump/runner.rb +47 -0
- data/lib/agave/dump/ssg_detector.rb +36 -0
- data/lib/agave/json_api_deserializer.rb +38 -0
- data/lib/agave/json_api_serializer.rb +145 -0
- data/lib/agave/local/entities_repo.rb +46 -0
- data/lib/agave/local/field_type/boolean.rb +12 -0
- data/lib/agave/local/field_type/color.rb +63 -0
- data/lib/agave/local/field_type/date.rb +12 -0
- data/lib/agave/local/field_type/date_time.rb +12 -0
- data/lib/agave/local/field_type/file.rb +65 -0
- data/lib/agave/local/field_type/float.rb +12 -0
- data/lib/agave/local/field_type/gallery.rb +23 -0
- data/lib/agave/local/field_type/global_seo.rb +56 -0
- data/lib/agave/local/field_type/image.rb +82 -0
- data/lib/agave/local/field_type/integer.rb +12 -0
- data/lib/agave/local/field_type/json.rb +13 -0
- data/lib/agave/local/field_type/lat_lon.rb +30 -0
- data/lib/agave/local/field_type/link.rb +12 -0
- data/lib/agave/local/field_type/links.rb +21 -0
- data/lib/agave/local/field_type/rich_text.rb +21 -0
- data/lib/agave/local/field_type/seo.rb +33 -0
- data/lib/agave/local/field_type/slug.rb +12 -0
- data/lib/agave/local/field_type/string.rb +12 -0
- data/lib/agave/local/field_type/text.rb +12 -0
- data/lib/agave/local/field_type/theme.rb +43 -0
- data/lib/agave/local/field_type/video.rb +77 -0
- data/lib/agave/local/item.rb +179 -0
- data/lib/agave/local/items_repo.rb +210 -0
- data/lib/agave/local/json_api_entity.rb +78 -0
- data/lib/agave/local/loader.rb +60 -0
- data/lib/agave/local/site.rb +73 -0
- data/lib/agave/paginator.rb +33 -0
- data/lib/agave/repo.rb +93 -0
- data/lib/agave/site/client.rb +24 -0
- data/lib/agave/upload/file.rb +92 -0
- data/lib/agave/upload/image.rb +8 -0
- data/lib/agave/utils/favicon_tags_builder.rb +85 -0
- data/lib/agave/utils/locale_value.rb +15 -0
- data/lib/agave/utils/meta_tags/article_modified_time.rb +15 -0
- data/lib/agave/utils/meta_tags/article_publisher.rb +18 -0
- data/lib/agave/utils/meta_tags/base.rb +55 -0
- data/lib/agave/utils/meta_tags/description.rb +24 -0
- data/lib/agave/utils/meta_tags/image.rb +35 -0
- data/lib/agave/utils/meta_tags/og_locale.rb +15 -0
- data/lib/agave/utils/meta_tags/og_site_name.rb +18 -0
- data/lib/agave/utils/meta_tags/og_type.rb +18 -0
- data/lib/agave/utils/meta_tags/robots.rb +14 -0
- data/lib/agave/utils/meta_tags/title.rb +46 -0
- data/lib/agave/utils/meta_tags/twitter_card.rb +14 -0
- data/lib/agave/utils/meta_tags/twitter_site.rb +18 -0
- data/lib/agave/utils/seo_tags_builder.rb +45 -0
- data/lib/agave/version.rb +4 -0
- data/lib/agave/watch/site_change_watcher.rb +37 -0
- metadata +504 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
module Agave
|
5
|
+
module Dump
|
6
|
+
module Operation
|
7
|
+
class Directory
|
8
|
+
attr_reader :context, :path
|
9
|
+
|
10
|
+
def initialize(context, path)
|
11
|
+
@context = context
|
12
|
+
@path = File.join(context.path, path)
|
13
|
+
@operations = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def add(operation)
|
17
|
+
@operations << operation
|
18
|
+
end
|
19
|
+
|
20
|
+
def perform
|
21
|
+
FileUtils.remove_dir(path) if Dir.exist?(path)
|
22
|
+
|
23
|
+
FileUtils.mkdir_p(path)
|
24
|
+
|
25
|
+
operations.each(&:perform)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
attr_reader :operations
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Agave
|
3
|
+
module Dump
|
4
|
+
module Operation
|
5
|
+
class Root
|
6
|
+
attr_reader :path
|
7
|
+
|
8
|
+
def initialize(path)
|
9
|
+
@operations = []
|
10
|
+
@path = path
|
11
|
+
end
|
12
|
+
|
13
|
+
def add(operation)
|
14
|
+
@operations << operation
|
15
|
+
end
|
16
|
+
|
17
|
+
def perform
|
18
|
+
operations.each(&:perform)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_reader :operations
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'agave/dump/dsl/root'
|
3
|
+
require 'agave/dump/operation/root'
|
4
|
+
require 'agave/dump/ssg_detector'
|
5
|
+
require 'agave/local/loader'
|
6
|
+
|
7
|
+
module Agave
|
8
|
+
module Dump
|
9
|
+
class Runner
|
10
|
+
attr_reader :config_path, :client, :destination_path, :preview_mode
|
11
|
+
|
12
|
+
def initialize(config_path, client, preview_mode, destination_path = Dir.pwd)
|
13
|
+
@config_path = config_path
|
14
|
+
@preview_mode = preview_mode
|
15
|
+
@client = client
|
16
|
+
@destination_path = destination_path
|
17
|
+
end
|
18
|
+
|
19
|
+
def run
|
20
|
+
print 'Fetching content from AgaveCMS... '
|
21
|
+
|
22
|
+
loader.load
|
23
|
+
|
24
|
+
I18n.available_locales = loader.items_repo.available_locales
|
25
|
+
I18n.locale = I18n.available_locales.first
|
26
|
+
|
27
|
+
Dsl::Root.new(
|
28
|
+
File.read(config_path),
|
29
|
+
loader.items_repo,
|
30
|
+
operation
|
31
|
+
)
|
32
|
+
|
33
|
+
operation.perform
|
34
|
+
|
35
|
+
puts "\e[32m✓\e[0m Done!"
|
36
|
+
end
|
37
|
+
|
38
|
+
def operation
|
39
|
+
@operation ||= Operation::Root.new(destination_path)
|
40
|
+
end
|
41
|
+
|
42
|
+
def loader
|
43
|
+
@loader ||= Agave::Local::Loader.new(client, preview_mode)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'toml'
|
3
|
+
require 'json'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
module Agave
|
7
|
+
module Dump
|
8
|
+
class SsgDetector
|
9
|
+
attr_reader :path
|
10
|
+
|
11
|
+
RUBY = %w(jekyll).freeze
|
12
|
+
|
13
|
+
def initialize(path)
|
14
|
+
@path = path
|
15
|
+
end
|
16
|
+
|
17
|
+
def detect
|
18
|
+
ruby_generator ||
|
19
|
+
'unknown'
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def ruby_generator
|
25
|
+
gemfile_path = File.join(path, 'Gemfile')
|
26
|
+
return unless File.exist?(gemfile_path)
|
27
|
+
|
28
|
+
gemfile = File.read(gemfile_path)
|
29
|
+
|
30
|
+
RUBY.find do |generator|
|
31
|
+
gemfile =~ /('#{generator}'|"#{generator}")/
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Agave
|
3
|
+
class JsonApiDeserializer
|
4
|
+
def deserialize(data)
|
5
|
+
data = data[:data]
|
6
|
+
|
7
|
+
if data.is_a? Array
|
8
|
+
data.map { |resource| deserialize_resource(resource) }
|
9
|
+
else
|
10
|
+
deserialize_resource(data)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def deserialize_resource(data)
|
15
|
+
result = { id: data[:id] }
|
16
|
+
|
17
|
+
if data[:attributes]
|
18
|
+
result.merge!(data[:attributes])
|
19
|
+
end
|
20
|
+
|
21
|
+
relationships = data.delete(:relationships)
|
22
|
+
|
23
|
+
if relationships
|
24
|
+
relationships.each do |key, handle|
|
25
|
+
handle_data = handle['data']
|
26
|
+
value = if handle_data.is_a? Array
|
27
|
+
handle_data.map { |ref| ref['id'] }
|
28
|
+
elsif handle_data.is_a? Hash
|
29
|
+
handle_data[:id]
|
30
|
+
end
|
31
|
+
result[key] = value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
result.with_indifferent_access
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Agave
|
3
|
+
class JsonApiSerializer
|
4
|
+
attr_reader :link, :type
|
5
|
+
|
6
|
+
def initialize(type, link)
|
7
|
+
@link = link
|
8
|
+
@type = type
|
9
|
+
end
|
10
|
+
|
11
|
+
def serialize(resource, id = nil)
|
12
|
+
resource = resource.with_indifferent_access
|
13
|
+
data = {}
|
14
|
+
|
15
|
+
data[:id] = id || resource[:id] if id || resource[:id]
|
16
|
+
|
17
|
+
data[:type] = type
|
18
|
+
data[:attributes] = serialized_attributes(resource)
|
19
|
+
|
20
|
+
if relationships.any?
|
21
|
+
data[:relationships] = serialized_relationships(resource)
|
22
|
+
end
|
23
|
+
|
24
|
+
{ data: data }
|
25
|
+
end
|
26
|
+
|
27
|
+
def serialized_attributes(resource)
|
28
|
+
result = {}
|
29
|
+
|
30
|
+
attributes(resource).each do |attribute|
|
31
|
+
if resource.key? attribute
|
32
|
+
result[attribute] = resource[attribute]
|
33
|
+
elsif required_attributes.include? attribute
|
34
|
+
throw "Required attribute: #{attribute}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
result
|
39
|
+
end
|
40
|
+
|
41
|
+
def serialized_relationships(resource)
|
42
|
+
result = {}
|
43
|
+
|
44
|
+
relationships.each do |relationship, meta|
|
45
|
+
if resource.key? relationship
|
46
|
+
value = resource[relationship]
|
47
|
+
|
48
|
+
data = if value
|
49
|
+
if meta[:collection]
|
50
|
+
value.map do |id|
|
51
|
+
{ type: meta[:type], id: id.to_s }
|
52
|
+
end
|
53
|
+
else
|
54
|
+
{ type: meta[:type], id: value.to_s }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
result[relationship] = { data: data }
|
58
|
+
|
59
|
+
elsif required_relationships.include?(relationship)
|
60
|
+
throw "Required attribute: #{relationship}"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
result
|
65
|
+
end
|
66
|
+
|
67
|
+
def attributes(resource)
|
68
|
+
if type == "item"
|
69
|
+
return resource.keys.map(&:to_sym) - [
|
70
|
+
:item_type,
|
71
|
+
:id,
|
72
|
+
:created_at,
|
73
|
+
:updated_at,
|
74
|
+
:is_valid,
|
75
|
+
:published_version,
|
76
|
+
:current_version
|
77
|
+
]
|
78
|
+
end
|
79
|
+
|
80
|
+
link_attributes["properties"].keys.map(&:to_sym)
|
81
|
+
end
|
82
|
+
|
83
|
+
def required_attributes
|
84
|
+
if type == "item"
|
85
|
+
return []
|
86
|
+
end
|
87
|
+
|
88
|
+
(link_attributes.required || []).map(&:to_sym)
|
89
|
+
end
|
90
|
+
|
91
|
+
def relationships
|
92
|
+
if type == "item"
|
93
|
+
if link.rel == :create
|
94
|
+
return { item_type: { collection: false, type: 'item_type' } }
|
95
|
+
else
|
96
|
+
{}
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
if !link_relationships
|
101
|
+
return {}
|
102
|
+
end
|
103
|
+
|
104
|
+
link_relationships.properties.reduce({}) do |acc, (relationship, schema)|
|
105
|
+
is_collection = schema.properties["data"].type.first == 'array'
|
106
|
+
|
107
|
+
definition = if is_collection
|
108
|
+
schema.properties['data'].items
|
109
|
+
elsif schema.properties['data'].type.first == 'object'
|
110
|
+
schema.properties['data']
|
111
|
+
else
|
112
|
+
schema.properties['data'].any_of.find do |option|
|
113
|
+
option.type.first == 'object'
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
type = definition.properties['type']
|
118
|
+
.pattern.source.gsub(/(^\^|\$$)/, '')
|
119
|
+
|
120
|
+
acc[relationship.to_sym] = {
|
121
|
+
collection: is_collection,
|
122
|
+
type: type,
|
123
|
+
}
|
124
|
+
|
125
|
+
acc
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def required_relationships
|
130
|
+
if type == "item"
|
131
|
+
return %i(item_type)
|
132
|
+
end
|
133
|
+
|
134
|
+
(link_relationships.required || []).map(&:to_sym)
|
135
|
+
end
|
136
|
+
|
137
|
+
def link_attributes
|
138
|
+
link.schema.properties["data"].properties["attributes"]
|
139
|
+
end
|
140
|
+
|
141
|
+
def link_relationships
|
142
|
+
link.schema.properties["data"].properties["relationships"]
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'agave/local/json_api_entity'
|
3
|
+
|
4
|
+
module Agave
|
5
|
+
module Local
|
6
|
+
class EntitiesRepo
|
7
|
+
attr_reader :entities
|
8
|
+
|
9
|
+
def initialize(*payloads)
|
10
|
+
@entities = {}
|
11
|
+
|
12
|
+
payloads.each do |payload|
|
13
|
+
EntitiesRepo.payload_entities(payload).each do |entity_payload|
|
14
|
+
object = JsonApiEntity.new(entity_payload, self)
|
15
|
+
@entities[object.type] ||= {}
|
16
|
+
@entities[object.type][object.id] = object
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def find_entities_of_type(type)
|
22
|
+
entities.fetch(type, {}).values
|
23
|
+
end
|
24
|
+
|
25
|
+
def find_entity(type, id)
|
26
|
+
entities.fetch(type, {}).fetch(id, nil)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.payload_entities(payload)
|
30
|
+
acc = []
|
31
|
+
|
32
|
+
if payload[:data]
|
33
|
+
acc = if payload[:data].is_a? Array
|
34
|
+
acc + payload[:data]
|
35
|
+
else
|
36
|
+
acc + [payload[:data]]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
acc += payload[:included] if payload[:included]
|
41
|
+
|
42
|
+
acc
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
module Agave
|
4
|
+
module Local
|
5
|
+
module FieldType
|
6
|
+
class Color
|
7
|
+
attr_reader :red, :green, :blue, :alpha
|
8
|
+
|
9
|
+
def self.parse(value, _repo)
|
10
|
+
value && new(
|
11
|
+
value[:red],
|
12
|
+
value[:green],
|
13
|
+
value[:blue],
|
14
|
+
value[:alpha]
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(red, green, blue, alpha)
|
19
|
+
@red = red
|
20
|
+
@green = green
|
21
|
+
@blue = blue
|
22
|
+
@alpha = alpha / 255.0
|
23
|
+
end
|
24
|
+
|
25
|
+
def rgb
|
26
|
+
if alpha == 1.0
|
27
|
+
"rgb(#{red}, #{green}, #{blue})"
|
28
|
+
else #
|
29
|
+
"rgba(#{red}, #{green}, #{blue}, #{alpha})"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def hex
|
34
|
+
r = red.to_s(16)
|
35
|
+
g = green.to_s(16)
|
36
|
+
b = blue.to_s(16)
|
37
|
+
a = (alpha * 255).to_i.to_s(16)
|
38
|
+
|
39
|
+
r = "0#{r}" if r.length == 1
|
40
|
+
g = "0#{g}" if g.length == 1
|
41
|
+
b = "0#{b}" if b.length == 1
|
42
|
+
a = "0#{a}" if a.length == 1
|
43
|
+
|
44
|
+
hex = '#' + r + g + b
|
45
|
+
|
46
|
+
hex += a if a != 'ff'
|
47
|
+
|
48
|
+
hex
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_hash(*_args)
|
52
|
+
{
|
53
|
+
red: red,
|
54
|
+
green: green,
|
55
|
+
blue: blue,
|
56
|
+
rgb: rgb,
|
57
|
+
hex: hex
|
58
|
+
}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|