wcc-contentful 0.1.0 → 0.2.0
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.
- checksums.yaml +4 -4
- data/.circleci/config.yml +1 -1
- data/.gitignore +5 -0
- data/.rubocop.yml +3 -0
- data/CHANGELOG.md +8 -1
- data/Guardfile +23 -1
- data/app/controllers/wcc/contentful/application_controller.rb +7 -0
- data/app/controllers/wcc/contentful/webhook_controller.rb +30 -0
- data/app/jobs/wcc/contentful/delayed_sync_job.rb +14 -0
- data/bin/rails +14 -0
- data/config/initializers/mime_types.rb +3 -0
- data/config/routes.rb +5 -0
- data/lib/generators/wcc/menu_generator.rb +4 -4
- data/lib/generators/wcc/templates/contentful_shell_wrapper +109 -68
- data/lib/generators/wcc/templates/menu/menu.rb +4 -6
- data/lib/generators/wcc/templates/menu/menu_button.rb +4 -6
- data/lib/generators/wcc/templates/release +2 -2
- data/lib/generators/wcc/templates/wcc_contentful.rb +0 -1
- data/lib/wcc/contentful/client_ext.rb +1 -1
- data/lib/wcc/contentful/configuration.rb +76 -35
- data/lib/wcc/contentful/engine.rb +13 -0
- data/lib/wcc/contentful/exceptions.rb +6 -0
- data/lib/wcc/contentful/graphql/builder.rb +8 -3
- data/lib/wcc/contentful/helpers.rb +6 -0
- data/lib/wcc/contentful/indexed_representation.rb +31 -0
- data/lib/wcc/contentful/model.rb +82 -1
- data/lib/wcc/contentful/model_builder.rb +18 -8
- data/lib/wcc/contentful/model_validators.rb +69 -18
- data/lib/wcc/contentful/simple_client/http_adapter.rb +15 -0
- data/lib/wcc/contentful/simple_client/typhoeus_adapter.rb +30 -0
- data/lib/wcc/contentful/simple_client.rb +67 -18
- data/lib/wcc/contentful/store/base.rb +89 -0
- data/lib/wcc/contentful/store/cdn_adapter.rb +13 -19
- data/lib/wcc/contentful/store/lazy_cache_store.rb +76 -0
- data/lib/wcc/contentful/store/memory_store.rb +17 -23
- data/lib/wcc/contentful/store/postgres_store.rb +32 -19
- data/lib/wcc/contentful/store.rb +62 -0
- data/lib/wcc/contentful/version.rb +1 -1
- data/lib/wcc/contentful.rb +113 -24
- data/wcc-contentful.gemspec +4 -0
- metadata +75 -2
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'http'
|
4
|
-
|
5
3
|
class WCC::Contentful::Configuration
|
6
4
|
ATTRIBUTES = %i[
|
7
5
|
access_token
|
@@ -9,47 +7,79 @@ class WCC::Contentful::Configuration
|
|
9
7
|
space
|
10
8
|
default_locale
|
11
9
|
content_delivery
|
12
|
-
|
10
|
+
http_adapter
|
11
|
+
sync_cache_store
|
12
|
+
webhook_username
|
13
|
+
webhook_password
|
13
14
|
].freeze
|
14
15
|
attr_accessor(*ATTRIBUTES)
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
17
|
+
##
|
18
|
+
# Defines the method by which content is downloaded from the Contentful CDN.
|
19
|
+
#
|
20
|
+
# [:direct] `config.content_delivery = :direct`
|
21
|
+
# with the `:direct` method, all queries result in web requests to
|
22
|
+
# 'https://cdn.contentful.com' via the
|
23
|
+
# {SimpleClient}[rdoc-ref:WCC::Contentful::SimpleClient::Cdn]
|
24
|
+
#
|
25
|
+
# [:eager_sync] `config.content_delivery = :eager_sync, [sync_store], [options]`
|
26
|
+
# with the `:eager_sync` method, the entire content of the Contentful
|
27
|
+
# space is downloaded locally and stored in the
|
28
|
+
# {Sync Store}[rdoc-ref:WCC::Contentful.store]. The application is responsible
|
29
|
+
# to periodically call `WCC::Contentful.sync!` to keep the store updated.
|
30
|
+
# Alternatively, the provided {Engine}[WCC::Contentful::Engine]
|
31
|
+
# can be mounted to receive a webhook from the Contentful space
|
32
|
+
# on publish events:
|
33
|
+
# mount WCC::Contentful::Engine, at: '/wcc/contentful'
|
34
|
+
#
|
35
|
+
# [:lazy_sync] `config.content_delivery = :lazy_sync, [cache]`
|
36
|
+
# The `:lazy_sync` method is a hybrid between the other two methods.
|
37
|
+
# Frequently accessed data is stored in an ActiveSupport::Cache implementation
|
38
|
+
# and is kept up-to-date via the Sync API. Any data that is not present
|
39
|
+
# in the cache is fetched from the CDN like in the `:direct` method.
|
40
|
+
# The application is still responsible to periodically call `sync!`
|
41
|
+
# or to mount the provided Engine.
|
42
|
+
#
|
43
|
+
def content_delivery=(params)
|
44
|
+
cd, *cd_params = params
|
45
|
+
unless cd.is_a? Symbol
|
46
|
+
raise ArgumentError, 'content_delivery must be a symbol, use store= to '\
|
47
|
+
'directly set contentful CDN access adapter'
|
48
|
+
end
|
21
49
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
}
|
28
|
-
}.freeze
|
50
|
+
WCC::Contentful::Store::Factory.new(
|
51
|
+
self,
|
52
|
+
cd,
|
53
|
+
cd_params
|
54
|
+
).validate!
|
29
55
|
|
30
|
-
|
31
|
-
|
32
|
-
@content_delivery = symbol
|
56
|
+
@content_delivery = cd
|
57
|
+
@content_delivery_params = cd_params
|
33
58
|
end
|
34
59
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
60
|
+
##
|
61
|
+
# Initializes the configured Sync Store.
|
62
|
+
def store
|
63
|
+
@store ||= WCC::Contentful::Store::Factory.new(
|
64
|
+
self,
|
65
|
+
@content_delivery,
|
66
|
+
@content_delivery_params
|
67
|
+
).build_sync_store
|
42
68
|
end
|
43
69
|
|
44
|
-
|
45
|
-
|
46
|
-
|
70
|
+
##
|
71
|
+
# Directly sets the adapter layer for communicating with Contentful
|
72
|
+
def store=(value)
|
73
|
+
@content_delivery = :custom
|
74
|
+
@store = value
|
47
75
|
end
|
48
76
|
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
|
77
|
+
# Sets the adapter which is used to make HTTP requests.
|
78
|
+
# If left unset, the gem attempts to load either 'http' or 'typhoeus'.
|
79
|
+
# You can pass your own adapter which responds to 'call', or even a lambda
|
80
|
+
# that accepts the following parameters:
|
81
|
+
# ->(url, query, headers = {}, proxy = {}) { ... }
|
82
|
+
attr_writer :http_adapter
|
53
83
|
|
54
84
|
def initialize
|
55
85
|
@access_token = ''
|
@@ -57,12 +87,21 @@ class WCC::Contentful::Configuration
|
|
57
87
|
@space = ''
|
58
88
|
@default_locale = nil
|
59
89
|
@content_delivery = :direct
|
60
|
-
@sync_store = :memory
|
61
90
|
end
|
62
91
|
|
92
|
+
##
|
93
|
+
# Gets a {CDN Client}[rdoc-ref:WCC::Contentful::SimpleClient::Cdn] which provides
|
94
|
+
# methods for getting and paging raw JSON data from the Contentful CDN.
|
63
95
|
attr_reader :client
|
64
96
|
attr_reader :management_client
|
65
97
|
|
98
|
+
##
|
99
|
+
# Called by WCC::Contentful.init! to configure the
|
100
|
+
# Contentful clients. This method can be called independently of `init!` if
|
101
|
+
# the application would prefer not to generate all the models.
|
102
|
+
#
|
103
|
+
# If the {contentful.rb}[https://github.com/contentful/contentful.rb] gem is
|
104
|
+
# loaded, it is extended to make use of the `http_adapter` lambda.
|
66
105
|
def configure_contentful
|
67
106
|
@client = nil
|
68
107
|
@management_client = nil
|
@@ -81,13 +120,15 @@ class WCC::Contentful::Configuration
|
|
81
120
|
@client = WCC::Contentful::SimpleClient::Cdn.new(
|
82
121
|
access_token: access_token,
|
83
122
|
space: space,
|
84
|
-
default_locale: default_locale
|
123
|
+
default_locale: default_locale,
|
124
|
+
adapter: http_adapter
|
85
125
|
)
|
86
126
|
return unless management_token.present?
|
87
127
|
@management_client = WCC::Contentful::SimpleClient::Management.new(
|
88
128
|
management_token: management_token,
|
89
129
|
space: space,
|
90
|
-
default_locale: default_locale
|
130
|
+
default_locale: default_locale,
|
131
|
+
adapter: http_adapter
|
91
132
|
)
|
92
133
|
end
|
93
134
|
end
|
@@ -46,7 +46,7 @@ module WCC::Contentful::Graphql
|
|
46
46
|
|
47
47
|
resolve ->(_obj, args, _ctx) {
|
48
48
|
if args['id'].nil?
|
49
|
-
store.find_by(content_type: content_type)
|
49
|
+
store.find_by(content_type: content_type)
|
50
50
|
else
|
51
51
|
store.find(args['id'])
|
52
52
|
end
|
@@ -58,8 +58,13 @@ module WCC::Contentful::Graphql
|
|
58
58
|
argument :filter, Types::FilterType
|
59
59
|
|
60
60
|
resolve ->(_obj, args, ctx) {
|
61
|
-
relation = store.
|
62
|
-
|
61
|
+
relation = store.find_all(content_type: content_type)
|
62
|
+
# TODO: improve this POC
|
63
|
+
if args[:filter]
|
64
|
+
filter = {}
|
65
|
+
filter[args[:filter]['field']] = { eq: args[:filter][:eq] }
|
66
|
+
relation = relation.apply(filter, ctx)
|
67
|
+
end
|
63
68
|
relation.result
|
64
69
|
}
|
65
70
|
end
|
@@ -32,6 +32,16 @@ module WCC::Contentful
|
|
32
32
|
@types.to_json
|
33
33
|
end
|
34
34
|
|
35
|
+
def deep_dup
|
36
|
+
self.class.new(@types.deep_dup)
|
37
|
+
end
|
38
|
+
|
39
|
+
def ==(other)
|
40
|
+
my_keys = keys
|
41
|
+
return false unless my_keys == other.keys
|
42
|
+
my_keys.all? { |k| self[k] == other[k] }
|
43
|
+
end
|
44
|
+
|
35
45
|
class ContentType
|
36
46
|
ATTRIBUTES = %i[
|
37
47
|
name
|
@@ -57,6 +67,18 @@ module WCC::Contentful
|
|
57
67
|
|
58
68
|
hash_or_id.each { |k, v| public_send("#{k}=", v) }
|
59
69
|
end
|
70
|
+
|
71
|
+
def deep_dup
|
72
|
+
dup_hash =
|
73
|
+
ATTRIBUTES.each_with_object({}) do |att, h|
|
74
|
+
h[att] = public_send(att)
|
75
|
+
end
|
76
|
+
self.class.new(dup_hash)
|
77
|
+
end
|
78
|
+
|
79
|
+
def ==(other)
|
80
|
+
ATTRIBUTES.all? { |att| public_send(att) == other.public_send(att) }
|
81
|
+
end
|
60
82
|
end
|
61
83
|
|
62
84
|
class Field
|
@@ -96,6 +118,11 @@ module WCC::Contentful
|
|
96
118
|
return
|
97
119
|
end
|
98
120
|
|
121
|
+
unless hash_or_id.is_a?(Hash)
|
122
|
+
ATTRIBUTES.each { |att| public_send("#{att}=", hash_or_id.public_send(att)) }
|
123
|
+
return
|
124
|
+
end
|
125
|
+
|
99
126
|
if raw_type = hash_or_id.delete('type')
|
100
127
|
raw_type = raw_type.to_sym
|
101
128
|
unless TYPES.include?(raw_type)
|
@@ -106,6 +133,10 @@ module WCC::Contentful
|
|
106
133
|
|
107
134
|
hash_or_id.each { |k, v| public_send("#{k}=", v) }
|
108
135
|
end
|
136
|
+
|
137
|
+
def ==(other)
|
138
|
+
ATTRIBUTES.all? { |att| public_send(att) == other.public_send(att) }
|
139
|
+
end
|
109
140
|
end
|
110
141
|
end
|
111
142
|
end
|
data/lib/wcc/contentful/model.rb
CHANGED
@@ -5,20 +5,101 @@ class WCC::Contentful::Model
|
|
5
5
|
extend WCC::Contentful::Helpers
|
6
6
|
extend WCC::Contentful::ModelValidators
|
7
7
|
|
8
|
+
# The Model base class maintains a registry which is best expressed as a
|
9
|
+
# class var.
|
10
|
+
# rubocop:disable Style/ClassVars
|
11
|
+
|
8
12
|
class << self
|
13
|
+
##
|
14
|
+
# The configured store which executes all model queries against either the
|
15
|
+
# Contentful CDN or a locally-downloaded copy.
|
16
|
+
#
|
17
|
+
# See the {sync_store}[rdoc-ref:WCC::Contentful::Configuration.sync_store] parameter
|
18
|
+
# on the WCC::Contentful::Configuration class.
|
9
19
|
attr_accessor :store
|
20
|
+
|
21
|
+
def const_missing(name)
|
22
|
+
raise WCC::Contentful::ContentTypeNotFoundError,
|
23
|
+
"Content type '#{content_type_from_constant(name)}' does not exist in the space"
|
24
|
+
end
|
10
25
|
end
|
11
26
|
|
27
|
+
@@registry = {}
|
28
|
+
|
12
29
|
def self.all_models
|
30
|
+
# TODO: this needs to use the registry but it's OK for now cause we only
|
31
|
+
# use it in specs
|
13
32
|
WCC::Contentful::Model.constants(false).map { |k| WCC::Contentful::Model.const_get(k) }
|
14
33
|
end
|
15
34
|
|
35
|
+
##
|
36
|
+
# Finds an Entry or Asset by ID in the configured contentful space
|
37
|
+
# and returns an initialized instance of the appropriate model type.
|
38
|
+
#
|
39
|
+
# Makes use of the configured {store}[rdoc-ref:WCC::Contentful::Model.store]
|
40
|
+
# to access the Contentful CDN.
|
16
41
|
def self.find(id, context = nil)
|
17
42
|
return unless raw = store.find(id)
|
18
43
|
|
19
44
|
content_type = content_type_from_raw(raw)
|
20
45
|
|
21
|
-
const =
|
46
|
+
unless const = @@registry[content_type]
|
47
|
+
begin
|
48
|
+
# The app may have defined a model and we haven't loaded it yet
|
49
|
+
const = Object.const_missing(constant_from_content_type(content_type).to_s)
|
50
|
+
rescue NameError
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
end
|
54
|
+
unless const
|
55
|
+
# Autoloading couldn't find their model - we'll register our own.
|
56
|
+
const = WCC::Contentful::Model.const_get(constant_from_content_type(content_type))
|
57
|
+
register_for_content_type(content_type, klass: const)
|
58
|
+
end
|
59
|
+
|
22
60
|
const.new(raw, context)
|
23
61
|
end
|
62
|
+
|
63
|
+
##
|
64
|
+
# Registers a class constant to be instantiated when resolving an instance
|
65
|
+
# of the given content type. This automatically happens for the first subclass
|
66
|
+
# of a generated model type, example:
|
67
|
+
#
|
68
|
+
# class MyMenu < WCC::Contentful::Model::Menu
|
69
|
+
# end
|
70
|
+
#
|
71
|
+
# In the above case, instances of MyMenu will be instantiated whenever a 'menu'
|
72
|
+
# content type is resolved.
|
73
|
+
# The mapping can be made explicit with the optional parameters. Example:
|
74
|
+
#
|
75
|
+
# class MyFoo < WCC::Contentful::Model::Foo
|
76
|
+
# register_for_content_type 'bar' # MyFoo is assumed
|
77
|
+
# end
|
78
|
+
#
|
79
|
+
# # in initializers/wcc_contentful.rb
|
80
|
+
# WCC::Contentful::Model.register_for_content_type('bar', klass: MyFoo)
|
81
|
+
def self.register_for_content_type(content_type = nil, klass: nil)
|
82
|
+
klass ||= self
|
83
|
+
raise ArgumentError, "#{klass} must be a class constant!" unless klass.respond_to?(:new)
|
84
|
+
content_type ||= content_type_from_constant(klass)
|
85
|
+
|
86
|
+
@@registry[content_type] = klass
|
87
|
+
end
|
88
|
+
|
89
|
+
##
|
90
|
+
# Returns the current registry of content type names to constants.
|
91
|
+
def self.registry
|
92
|
+
return {} unless @@registry
|
93
|
+
@@registry.dup.freeze
|
94
|
+
end
|
95
|
+
|
96
|
+
##
|
97
|
+
# Checks if a content type has already been registered to a class and returns
|
98
|
+
# that class. If nil, the generated WCC::Contentful::Model::{content_type} class
|
99
|
+
# will be resolved for this content type.
|
100
|
+
def self.registered?(content_type)
|
101
|
+
@@registry[content_type]
|
102
|
+
end
|
24
103
|
end
|
104
|
+
|
105
|
+
# rubocop:enable Style/ClassVars
|
@@ -40,9 +40,16 @@ module WCC::Contentful
|
|
40
40
|
new(raw, context) if raw.present?
|
41
41
|
end
|
42
42
|
|
43
|
-
define_singleton_method(:find_all) do |context = nil|
|
44
|
-
|
45
|
-
|
43
|
+
define_singleton_method(:find_all) do |filter = nil, context = nil|
|
44
|
+
if filter
|
45
|
+
filter.transform_keys! { |k| k.to_s.camelize(:lower) }
|
46
|
+
bad_fields = filter.keys.reject { |k| fields.include?(k) }
|
47
|
+
raise ArgumentError, "These fields do not exist: #{bad_fields}" unless bad_fields.empty?
|
48
|
+
end
|
49
|
+
|
50
|
+
query = WCC::Contentful::Model.store.find_all(content_type: content_type)
|
51
|
+
query = query.apply(filter) if filter
|
52
|
+
query.map { |r| new(r, context) }
|
46
53
|
end
|
47
54
|
|
48
55
|
define_singleton_method(:find_by) do |filter, context = nil|
|
@@ -50,11 +57,14 @@ module WCC::Contentful
|
|
50
57
|
bad_fields = filter.keys.reject { |k| fields.include?(k) }
|
51
58
|
raise ArgumentError, "These fields do not exist: #{bad_fields}" unless bad_fields.empty?
|
52
59
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
60
|
+
result = WCC::Contentful::Model.store.find_by(content_type: content_type, filter: filter)
|
61
|
+
new(result, context)
|
62
|
+
end
|
63
|
+
|
64
|
+
define_singleton_method(:inherited) do |subclass|
|
65
|
+
# only register if it's not already registered
|
66
|
+
return if WCC::Contentful::Model.registered?(typedef.content_type)
|
67
|
+
WCC::Contentful::Model.register_for_content_type(typedef.content_type, klass: subclass)
|
58
68
|
end
|
59
69
|
|
60
70
|
define_method(:initialize) do |raw, context = nil|
|
@@ -6,43 +6,94 @@ require_relative 'model_validators/dsl'
|
|
6
6
|
|
7
7
|
module WCC::Contentful::ModelValidators
|
8
8
|
def schema
|
9
|
-
return if
|
10
|
-
field_validations = @field_validations
|
9
|
+
return if validations.nil? || validations.empty?
|
11
10
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
# "title": { ... },
|
16
|
-
# "sections": { ... },
|
17
|
-
# ...
|
18
|
-
# }
|
19
|
-
# }
|
11
|
+
all_field_validations =
|
12
|
+
validations.each_with_object({}) do |(content_type, procs), h|
|
13
|
+
next if procs.empty?
|
20
14
|
|
21
|
-
|
22
|
-
|
23
|
-
#
|
24
|
-
#
|
25
|
-
|
15
|
+
# "page": {
|
16
|
+
# "sys": { ... }
|
17
|
+
# "fields": {
|
18
|
+
# "title": { ... },
|
19
|
+
# "sections": { ... },
|
20
|
+
# ...
|
21
|
+
# }
|
22
|
+
# }
|
23
|
+
h[content_type] =
|
24
|
+
Dry::Validation.Schema do
|
25
|
+
# Had to dig through the internals of Dry::Validation to find
|
26
|
+
# this magic incantation
|
27
|
+
procs.each { |dsl| instance_eval(&dsl.to_proc) }
|
28
|
+
end
|
26
29
|
end
|
27
30
|
|
28
31
|
Dry::Validation.Schema do
|
29
|
-
|
32
|
+
all_field_validations.each do |content_type, fields_schema|
|
33
|
+
required(content_type).schema do
|
34
|
+
required('fields').schema(fields_schema)
|
35
|
+
end
|
36
|
+
end
|
30
37
|
end
|
31
38
|
end
|
32
39
|
|
40
|
+
def validations
|
41
|
+
# This needs to be a class variable so that subclasses defined in application
|
42
|
+
# code can add to the total package of model validations
|
43
|
+
# rubocop:disable Style/ClassVars
|
44
|
+
@@validations ||= {}
|
45
|
+
# rubocop:enable Style/ClassVars
|
46
|
+
end
|
47
|
+
|
48
|
+
##
|
49
|
+
# Accepts a block which uses the {dry-validation DSL}[http://dry-rb.org/gems/dry-validation/]
|
50
|
+
# to validate the 'fields' object of a content type.
|
33
51
|
def validate_fields(&block)
|
34
52
|
raise ArgumentError, 'validate_fields requires a block' unless block_given?
|
35
53
|
dsl = ProcDsl.new(Proc.new(&block))
|
36
54
|
|
37
|
-
|
55
|
+
ct = try(:content_type) || name.demodulize.camelize(:lower)
|
56
|
+
(validations[ct] ||= []) << dsl
|
38
57
|
end
|
39
58
|
|
59
|
+
##
|
60
|
+
# Validates a single field is of the expected type.
|
61
|
+
# Type expectations are one of:
|
62
|
+
#
|
63
|
+
# [:String] the field type must be `Symbol` or `Text`
|
64
|
+
# [:Int] the field type must be `Integer`
|
65
|
+
# [:Float] the field type must be `Number`
|
66
|
+
# [:DateTime] the field type must be 'Date'
|
67
|
+
# [:Asset] the field must be a link and the `linkType` must be `Asset`
|
68
|
+
# [:Link] the field must be a link and the `linkType` must be `Entry`.
|
69
|
+
# [:Location] the field type must be `Location`
|
70
|
+
# [:Boolean] the field type must be `Boolean`
|
71
|
+
# [:Json] the field type must be `Json` - a json blob.
|
72
|
+
# [:Array] the field must be a List.
|
73
|
+
#
|
74
|
+
# Additional validation options can be enforced:
|
75
|
+
#
|
76
|
+
# [:required] the 'Required Field' checkbox must be checked
|
77
|
+
# [:optional] the 'Required Field' checkbox must not be checked
|
78
|
+
# [:link_to] (only `:Link` or `:Array` type) the given content type(s) must be
|
79
|
+
# checked in the 'Accept only specified entry type' validations
|
80
|
+
# Example:
|
81
|
+
# validate_field :button, :Link, link_to: ['button', 'altButton']
|
82
|
+
#
|
83
|
+
# [:items] (only `:Array` type) the items of the list must be of the given type.
|
84
|
+
# Example:
|
85
|
+
# validate_field :my_strings, :Array, items: :String
|
86
|
+
#
|
87
|
+
# Examples:
|
88
|
+
# see WCC::Contentful::Model::Menu and WCC::Contentful::Model::MenuButton
|
40
89
|
def validate_field(field, type, *options)
|
41
90
|
dsl = FieldDsl.new(field, type, options)
|
42
91
|
|
43
|
-
|
92
|
+
ct = try(:content_type) || name.demodulize.camelize(:lower)
|
93
|
+
(validations[ct] ||= []) << dsl
|
44
94
|
end
|
45
95
|
|
96
|
+
##
|
46
97
|
# Accepts a content types response from the API and transforms it
|
47
98
|
# to be acceptible for the validator.
|
48
99
|
def self.transform_content_types_for_validation(content_types)
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
gem 'http'
|
4
|
+
require 'http'
|
5
|
+
|
6
|
+
class HttpAdapter
|
7
|
+
def call(url, query, headers = {}, proxy = {})
|
8
|
+
if proxy[:host]
|
9
|
+
HTTP[headers].via(proxy[:host], proxy[:port], proxy[:username], proxy[:password])
|
10
|
+
.get(url, params: query)
|
11
|
+
else
|
12
|
+
HTTP[headers].get(url, params: query)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
gem 'typhoeus'
|
4
|
+
require 'typhoeus'
|
5
|
+
|
6
|
+
class TyphoeusAdapter
|
7
|
+
def call(url, query, headers = {}, proxy = {})
|
8
|
+
raise NotImplementedError, 'Proxying Not Yet Implemented' if proxy[:host]
|
9
|
+
|
10
|
+
TyphoeusAdapter::Response.new(
|
11
|
+
Typhoeus.get(
|
12
|
+
url,
|
13
|
+
params: query,
|
14
|
+
headers: headers
|
15
|
+
)
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
Response =
|
20
|
+
Struct.new(:raw) do
|
21
|
+
delegate :body, to: :raw
|
22
|
+
delegate :to_s, to: :body
|
23
|
+
delegate :code, to: :raw
|
24
|
+
delegate :headers, to: :raw
|
25
|
+
|
26
|
+
def status
|
27
|
+
raw.code
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|