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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +1 -1
  3. data/.gitignore +5 -0
  4. data/.rubocop.yml +3 -0
  5. data/CHANGELOG.md +8 -1
  6. data/Guardfile +23 -1
  7. data/app/controllers/wcc/contentful/application_controller.rb +7 -0
  8. data/app/controllers/wcc/contentful/webhook_controller.rb +30 -0
  9. data/app/jobs/wcc/contentful/delayed_sync_job.rb +14 -0
  10. data/bin/rails +14 -0
  11. data/config/initializers/mime_types.rb +3 -0
  12. data/config/routes.rb +5 -0
  13. data/lib/generators/wcc/menu_generator.rb +4 -4
  14. data/lib/generators/wcc/templates/contentful_shell_wrapper +109 -68
  15. data/lib/generators/wcc/templates/menu/menu.rb +4 -6
  16. data/lib/generators/wcc/templates/menu/menu_button.rb +4 -6
  17. data/lib/generators/wcc/templates/release +2 -2
  18. data/lib/generators/wcc/templates/wcc_contentful.rb +0 -1
  19. data/lib/wcc/contentful/client_ext.rb +1 -1
  20. data/lib/wcc/contentful/configuration.rb +76 -35
  21. data/lib/wcc/contentful/engine.rb +13 -0
  22. data/lib/wcc/contentful/exceptions.rb +6 -0
  23. data/lib/wcc/contentful/graphql/builder.rb +8 -3
  24. data/lib/wcc/contentful/helpers.rb +6 -0
  25. data/lib/wcc/contentful/indexed_representation.rb +31 -0
  26. data/lib/wcc/contentful/model.rb +82 -1
  27. data/lib/wcc/contentful/model_builder.rb +18 -8
  28. data/lib/wcc/contentful/model_validators.rb +69 -18
  29. data/lib/wcc/contentful/simple_client/http_adapter.rb +15 -0
  30. data/lib/wcc/contentful/simple_client/typhoeus_adapter.rb +30 -0
  31. data/lib/wcc/contentful/simple_client.rb +67 -18
  32. data/lib/wcc/contentful/store/base.rb +89 -0
  33. data/lib/wcc/contentful/store/cdn_adapter.rb +13 -19
  34. data/lib/wcc/contentful/store/lazy_cache_store.rb +76 -0
  35. data/lib/wcc/contentful/store/memory_store.rb +17 -23
  36. data/lib/wcc/contentful/store/postgres_store.rb +32 -19
  37. data/lib/wcc/contentful/store.rb +62 -0
  38. data/lib/wcc/contentful/version.rb +1 -1
  39. data/lib/wcc/contentful.rb +113 -24
  40. data/wcc-contentful.gemspec +4 -0
  41. metadata +75 -2
@@ -1,17 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'http'
4
-
5
3
  require_relative 'simple_client/response'
6
4
 
7
5
  module WCC::Contentful
6
+ ##
7
+ # The SimpleClient accesses the Contentful CDN to get JSON responses,
8
+ # returning the raw JSON data as a parsed hash.
9
+ # It can be configured to access any API url and exposes only a single method,
10
+ # `get`. This method returns a WCC::Contentful::SimpleClient::Response
11
+ # that handles paging automatically.
12
+ #
13
+ # The SimpleClient by default uses 'http' to perform the gets, but any HTTP
14
+ # client can be injected by passing a proc as the `adapter:` option.
8
15
  class SimpleClient
9
16
  def initialize(api_url:, space:, access_token:, **options)
10
17
  @api_url = URI.join(api_url, '/spaces/', space + '/')
11
18
  @space = space
12
19
  @access_token = access_token
13
20
 
14
- @get_http = options[:override_get_http] if options[:override_get_http].present?
21
+ @get_http = SimpleClient.load_adapter(options[:adapter])
15
22
 
16
23
  @options = options
17
24
  @query_defaults = {}
@@ -26,6 +33,39 @@ module WCC::Contentful
26
33
  get_http(url, query))
27
34
  end
28
35
 
36
+ ADAPTERS = {
37
+ http: ['http', '> 1.0', '< 3.0'],
38
+ typhoeus: ['typhoeus', '~> 1.0']
39
+ }.freeze
40
+
41
+ def self.load_adapter(adapter)
42
+ case adapter
43
+ when nil
44
+ ADAPTERS.each do |a, spec|
45
+ begin
46
+ gem(*spec)
47
+ return load_adapter(a)
48
+ rescue Gem::LoadError
49
+ next
50
+ end
51
+ end
52
+ raise ArgumentError, 'Unable to load adapter! Please install one of '\
53
+ "#{ADAPTERS.values.map(&:join).join(',')}"
54
+ when :http
55
+ require_relative 'simple_client/http_adapter'
56
+ HttpAdapter.new
57
+ when :typhoeus
58
+ require_relative 'simple_client/typhoeus_adapter'
59
+ TyphoeusAdapter.new
60
+ else
61
+ unless adapter.respond_to?(:call)
62
+ raise ArgumentError, "Adapter #{adapter} is not invokeable! Please "\
63
+ "pass a proc or use one of #{ADAPTERS.keys}"
64
+ end
65
+ adapter
66
+ end
67
+ end
68
+
29
69
  private
30
70
 
31
71
  def get_http(url, query, headers = {}, proxy = {})
@@ -36,27 +76,19 @@ module WCC::Contentful
36
76
  q = @query_defaults.dup
37
77
  q = q.merge(query) if query
38
78
 
39
- resp =
40
- if @get_http
41
- @get_http.call(url, q, headers, proxy)
42
- else
43
- default_get_http(url, q, headers, proxy)
44
- end
79
+ resp = @get_http.call(url, q, headers, proxy)
80
+
45
81
  if [301, 302, 307].include?(resp.code) && !@options[:no_follow_redirects]
46
82
  resp = get_http(resp.headers['location'], nil, headers, proxy)
47
83
  end
48
84
  resp
49
85
  end
50
86
 
51
- def default_get_http(url, query, headers = {}, proxy = {})
52
- if proxy[:host]
53
- HTTP[headers].via(proxy[:host], proxy[:port], proxy[:username], proxy[:password])
54
- .get(url, params: query)
55
- else
56
- HTTP[headers].get(url, params: query)
57
- end
58
- end
59
-
87
+ ##
88
+ # The CDN SimpleClient accesses 'https://cdn.contentful.com' to get raw
89
+ # JSON responses. It exposes methods to query entries, assets, and content_types.
90
+ # The responses are instances of WCC::Contentful::SimpleClient::Response
91
+ # which handles paging automatically.
60
92
  class Cdn < SimpleClient
61
93
  def initialize(space:, access_token:, **options)
62
94
  super(
@@ -67,31 +99,48 @@ module WCC::Contentful
67
99
  )
68
100
  end
69
101
 
102
+ ##
103
+ # Gets an entry by ID
70
104
  def entry(key, query = {})
71
105
  resp = get("entries/#{key}", query)
72
106
  resp.assert_ok!
73
107
  end
74
108
 
109
+ ##
110
+ # Queries entries with optional query parameters
75
111
  def entries(query = {})
76
112
  resp = get('entries', query)
77
113
  resp.assert_ok!
78
114
  end
79
115
 
116
+ ##
117
+ # Gets an asset by ID
80
118
  def asset(key, query = {})
81
119
  resp = get("assets/#{key}", query)
82
120
  resp.assert_ok!
83
121
  end
84
122
 
123
+ ##
124
+ # Queries assets with optional query parameters
85
125
  def assets(query = {})
86
126
  resp = get('assets', query)
87
127
  resp.assert_ok!
88
128
  end
89
129
 
130
+ ##
131
+ # Queries content types with optional query parameters
90
132
  def content_types(query = {})
91
133
  resp = get('content_types', query)
92
134
  resp.assert_ok!
93
135
  end
94
136
 
137
+ ##
138
+ # Accesses the Sync API to get a list of items that have changed since
139
+ # the last sync.
140
+ #
141
+ # If `sync_token` is nil, an initial sync is performed.
142
+ # Returns a WCC::Contentful::SimpleClient::SyncResponse
143
+ # which handles paging automatically.
95
144
  def sync(sync_token: nil, **query)
96
145
  sync_token =
97
146
  if sync_token
@@ -0,0 +1,89 @@
1
+
2
+ # frozen_string_literal: true
3
+
4
+ module WCC::Contentful::Store
5
+ class Base
6
+ def find(_id)
7
+ raise NotImplementedError, "#{self.class} does not implement #find"
8
+ end
9
+
10
+ def set(_id, _value)
11
+ raise NotImplementedError, "#{self.class} does not implement #set"
12
+ end
13
+
14
+ def delete(_id)
15
+ raise NotImplementedError, "#{self.class} does not implement #delete"
16
+ end
17
+
18
+ def index(json)
19
+ # Subclasses can override to do this in a more performant thread-safe way.
20
+ # Example: postgres_store could do this in a stored procedure for speed
21
+ mutex.with_write_lock do
22
+ prev =
23
+ case type = json.dig('sys', 'type')
24
+ when 'DeletedEntry', 'DeletedAsset'
25
+ delete(json.dig('sys', 'id'))
26
+ else
27
+ set(json.dig('sys', 'id'), json)
28
+ end
29
+
30
+ if (prev_rev = prev&.dig('sys', 'revision')) && (next_rev = json.dig('sys', 'revision'))
31
+ if next_rev < prev_rev
32
+ # Uh oh! we overwrote an entry with a prior revision. Put the previous back.
33
+ return index(prev)
34
+ end
35
+ end
36
+
37
+ case type
38
+ when 'DeletedEntry', 'DeletedAsset'
39
+ nil
40
+ else
41
+ json
42
+ end
43
+ end
44
+ end
45
+
46
+ def find_by(content_type:, filter: nil)
47
+ # default implementation - can be overridden
48
+ q = find_all(content_type: content_type)
49
+ q = q.apply(filter) if filter
50
+ q.first
51
+ end
52
+
53
+ # rubocop:disable Lint/UnusedMethodArgument
54
+ def find_all(content_type:)
55
+ raise NotImplementedError, "#{self.class} does not implement find_all"
56
+ end
57
+ # rubocop:enable Lint/UnusedMethodArgument
58
+
59
+ def initialize
60
+ @mutex = Concurrent::ReentrantReadWriteLock.new
61
+ end
62
+
63
+ protected
64
+
65
+ attr_reader :mutex
66
+
67
+ class Query
68
+ delegate :first, to: :result
69
+ delegate :map, to: :result
70
+ delegate :count, to: :result
71
+
72
+ def result
73
+ raise NotImplementedError
74
+ end
75
+
76
+ def apply(filter, context = nil)
77
+ filter.reduce(self) do |query, (field, value)|
78
+ if value.is_a?(Hash)
79
+ k = value.keys.first
80
+ raise ArgumentError, "Filter not implemented: #{value}" unless query.respond_to?(k)
81
+ query.public_send(k, field, value[k], context)
82
+ else
83
+ query.eq(field.to_s, value)
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -4,7 +4,10 @@ module WCC::Contentful::Store
4
4
  class CDNAdapter
5
5
  attr_reader :client
6
6
 
7
+ # Intentionally not implementing write methods
8
+
7
9
  def initialize(client)
10
+ super()
8
11
  @client = client
9
12
  end
10
13
 
@@ -16,29 +19,26 @@ module WCC::Contentful::Store
16
19
  client.asset(key, locale: '*')
17
20
  end
18
21
  entry&.raw
22
+ rescue WCC::Contentful::SimpleClient::NotFoundError
23
+ nil
19
24
  end
20
25
 
21
- def find_all
22
- raise ArgumentError, 'use find_by content type instead'
26
+ def find_by(content_type:, filter: nil)
27
+ # default implementation - can be overridden
28
+ q = find_all(content_type: content_type)
29
+ q = q.apply(filter) if filter
30
+ q.first
23
31
  end
24
32
 
25
- def find_by(content_type:)
33
+ def find_all(content_type:)
26
34
  Query.new(@client, content_type: content_type)
27
35
  end
28
36
 
29
- class Query
37
+ class Query < Base::Query
30
38
  delegate :count, to: :resolve
31
39
 
32
- def first
33
- resolve.items.first
34
- end
35
-
36
- def map(&block)
37
- resolve.items.map(&block)
38
- end
39
-
40
40
  def result
41
- raise ArgumentError, 'Not Implemented'
41
+ resolve.items
42
42
  end
43
43
 
44
44
  def initialize(client, relation)
@@ -48,12 +48,6 @@ module WCC::Contentful::Store
48
48
  @relation = relation
49
49
  end
50
50
 
51
- def apply(filter, context = nil)
52
- return eq(filter[:field], filter[:eq], context) if filter[:eq]
53
-
54
- raise ArgumentError, "Filter not implemented: #{filter}"
55
- end
56
-
57
51
  def eq(field, expected, context = nil)
58
52
  locale = context[:locale] if context.present?
59
53
  locale ||= 'en-US'
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WCC::Contentful::Store
4
+ class LazyCacheStore
5
+ delegate :find_all, to: :@cdn
6
+
7
+ # TODO: https://zube.io/watermarkchurch/development/c/2265
8
+ # figure out how to cache the results of a find_by query, ex:
9
+ # `find_by('slug' => '/about')`
10
+ delegate :find_by, to: :@cdn
11
+
12
+ def initialize(client, cache: nil)
13
+ @cdn = CDNAdapter.new(client)
14
+ @cache = cache || ActiveSupport::Cache::MemoryStore.new
15
+ end
16
+
17
+ def find(key)
18
+ found =
19
+ @cache.fetch(key) do
20
+ # if it's not a contentful ID don't hit the API.
21
+ # Store a nil object if we can't find the object on the CDN.
22
+ (@cdn.find(key) || nil_obj(key)) if key =~ /^\w+$/
23
+ end
24
+
25
+ case found.try(:dig, 'sys', 'type')
26
+ when 'Nil', 'DeletedEntry', 'DeletedAsset'
27
+ nil
28
+ else
29
+ found
30
+ end
31
+ end
32
+
33
+ # #index is called whenever the sync API comes back with more data.
34
+ def index(json)
35
+ id = json.dig('sys', 'id')
36
+ return unless prev = @cache.read(id)
37
+
38
+ if (prev_rev = prev&.dig('sys', 'revision')) && (next_rev = json.dig('sys', 'revision'))
39
+ return prev if next_rev < prev_rev
40
+ end
41
+
42
+ # we also set deletes in the cache - no need to go hit the API when we know
43
+ # this is a nil object
44
+ @cache.write(id, json)
45
+
46
+ case json.dig('sys', 'type')
47
+ when 'DeletedEntry', 'DeletedAsset'
48
+ nil
49
+ else
50
+ json
51
+ end
52
+ end
53
+
54
+ def set(key, value)
55
+ old = @cache.read(key)
56
+ @cache.write(key, value)
57
+ old
58
+ end
59
+
60
+ def delete(key)
61
+ old = @cache.read(key)
62
+ @cache.delete(key)
63
+ old
64
+ end
65
+
66
+ def nil_obj(id)
67
+ {
68
+ 'sys' => {
69
+ 'id' => id,
70
+ 'type' => 'Nil',
71
+ 'revision' => 1
72
+ }
73
+ }
74
+ end
75
+ end
76
+ end
@@ -1,35 +1,39 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module WCC::Contentful::Store
4
- class MemoryStore
4
+ class MemoryStore < Base
5
5
  def initialize
6
+ super
6
7
  @hash = {}
7
- @mutex = Mutex.new
8
8
  end
9
9
 
10
- def index(key, value)
10
+ def set(key, value)
11
11
  value = value.deep_dup.freeze
12
- @mutex.synchronize do
12
+ mutex.with_write_lock do
13
+ old = @hash[key]
13
14
  @hash[key] = value
15
+ old
16
+ end
17
+ end
18
+
19
+ def delete(key)
20
+ mutex.with_write_lock do
21
+ @hash.delete(key)
14
22
  end
15
23
  end
16
24
 
17
25
  def keys
18
- @mutex.synchronize { @hash.keys }
26
+ mutex.with_read_lock { @hash.keys }
19
27
  end
20
28
 
21
29
  def find(key)
22
- @mutex.synchronize do
30
+ mutex.with_read_lock do
23
31
  @hash[key]
24
32
  end
25
33
  end
26
34
 
27
- def find_all
28
- Query.new(@mutex.synchronize { @hash.values })
29
- end
30
-
31
- def find_by(content_type:)
32
- relation = @mutex.synchronize { @hash.values }
35
+ def find_all(content_type:)
36
+ relation = mutex.with_read_lock { @hash.values }
33
37
 
34
38
  relation =
35
39
  relation.reject do |v|
@@ -39,11 +43,7 @@ module WCC::Contentful::Store
39
43
  Query.new(relation)
40
44
  end
41
45
 
42
- class Query
43
- delegate :first, to: :@relation
44
- delegate :map, to: :@relation
45
- delegate :count, to: :@relation
46
-
46
+ class Query < Base::Query
47
47
  def result
48
48
  @relation.dup
49
49
  end
@@ -52,12 +52,6 @@ module WCC::Contentful::Store
52
52
  @relation = relation
53
53
  end
54
54
 
55
- def apply(filter, context = nil)
56
- return eq(filter[:field], filter[:eq], context) if filter[:eq]
57
-
58
- raise ArgumentError, "Filter not implemented: #{filter}"
59
- end
60
-
61
55
  def eq(field, expected, context = nil)
62
56
  locale = context[:locale] if context.present?
63
57
  locale ||= 'en-US'
@@ -4,16 +4,19 @@ gem 'pg', '~> 1.0'
4
4
  require 'pg'
5
5
 
6
6
  module WCC::Contentful::Store
7
- class PostgresStore
7
+ class PostgresStore < Base
8
8
  def initialize(connection_options = nil)
9
+ super()
9
10
  connection_options ||= { dbname: 'postgres' }
10
11
  @conn = PG.connect(connection_options)
11
12
  PostgresStore.ensure_schema(@conn)
12
13
  end
13
14
 
14
- def index(key, value)
15
- @conn.exec_prepared('index_entry', [key, value.to_json])
16
- true
15
+ def set(key, value)
16
+ result = @conn.exec_prepared('upsert_entry', [key, value.to_json])
17
+ return if result.num_tuples == 0
18
+ val = result.getvalue(0, 0)
19
+ JSON.parse(val) if val
17
20
  end
18
21
 
19
22
  def keys
@@ -23,17 +26,19 @@ module WCC::Contentful::Store
23
26
  arr
24
27
  end
25
28
 
26
- def find(key)
27
- result = @conn.exec_prepared('select_entry', [key])
29
+ def delete(key)
30
+ result = @conn.exec_prepared('delete_by_id', [key])
28
31
  return if result.num_tuples == 0
29
32
  JSON.parse(result.getvalue(0, 1))
30
33
  end
31
34
 
32
- def find_all
33
- Query.new(@conn)
35
+ def find(key)
36
+ result = @conn.exec_prepared('select_entry', [key])
37
+ return if result.num_tuples == 0
38
+ JSON.parse(result.getvalue(0, 1))
34
39
  end
35
40
 
36
- def find_by(content_type:)
41
+ def find_all(content_type:)
37
42
  statement = "WHERE data->'sys'->'contentType'->'sys'->>'id' = $1"
38
43
  Query.new(
39
44
  @conn,
@@ -42,7 +47,7 @@ module WCC::Contentful::Store
42
47
  )
43
48
  end
44
49
 
45
- class Query
50
+ class Query < Base::Query
46
51
  def initialize(conn, statement = nil, params = nil)
47
52
  @conn = conn
48
53
  @statement = statement ||
@@ -50,12 +55,6 @@ module WCC::Contentful::Store
50
55
  @params = params || []
51
56
  end
52
57
 
53
- def apply(filter, context = nil)
54
- return eq(filter[:field], filter[:eq], context) if filter[:eq]
55
-
56
- raise ArgumentError, "Filter not implemented: #{filter}"
57
- end
58
-
59
58
  def eq(field, expected, context = nil)
60
59
  locale = context[:locale] if context.present?
61
60
  locale ||= 'en-US'
@@ -115,18 +114,32 @@ module WCC::Contentful::Store
115
114
  def self.ensure_schema(conn)
116
115
  conn.exec(<<~HEREDOC
117
116
  CREATE TABLE IF NOT EXISTS contentful_raw (
118
- id char(22) PRIMARY KEY,
117
+ id varchar PRIMARY KEY,
119
118
  data jsonb
120
119
  );
121
120
  CREATE INDEX IF NOT EXISTS contentful_raw_value_type ON contentful_raw ((data->'sys'->>'type'));
122
121
  CREATE INDEX IF NOT EXISTS contentful_raw_value_content_type ON contentful_raw ((data->'sys'->'contentType'->'sys'->>'id'));
122
+
123
+ DROP FUNCTION IF EXISTS "upsert_entry";
124
+ CREATE FUNCTION "upsert_entry"(_id varchar, _data jsonb) RETURNS jsonb AS $$
125
+ DECLARE
126
+ prev jsonb;
127
+ BEGIN
128
+ SELECT data FROM contentful_raw WHERE id = _id INTO prev;
129
+ INSERT INTO contentful_raw (id, data) values (_id, _data)
130
+ ON CONFLICT (id) DO
131
+ UPDATE
132
+ SET data = _data;
133
+ RETURN prev;
134
+ END;
135
+ $$ LANGUAGE 'plpgsql';
123
136
  HEREDOC
124
137
  )
125
138
 
126
- conn.prepare('index_entry', 'INSERT INTO contentful_raw (id, data) values ($1, $2) ' \
127
- 'ON CONFLICT (id) DO UPDATE SET data = $2')
139
+ conn.prepare('upsert_entry', 'SELECT * FROM upsert_entry($1,$2)')
128
140
  conn.prepare('select_entry', 'SELECT * FROM contentful_raw WHERE id = $1')
129
141
  conn.prepare('select_ids', 'SELECT id FROM contentful_raw')
142
+ conn.prepare('delete_by_id', 'DELETE FROM contentful_raw WHERE id = $1 RETURNING *')
130
143
  end
131
144
  end
132
145
  end
@@ -1,8 +1,70 @@
1
1
 
2
2
  # frozen_string_literal: true
3
3
 
4
+ require_relative 'store/base'
4
5
  require_relative 'store/memory_store'
6
+ require_relative 'store/lazy_cache_store'
5
7
  require_relative 'store/cdn_adapter'
6
8
 
7
9
  # required dynamically if they select the 'postgres' store option
8
10
  # require_relative 'store/postgres_store'
11
+
12
+ module WCC::Contentful::Store
13
+ SYNC_STORES = {
14
+ memory: ->(_config) { WCC::Contentful::Store::MemoryStore.new },
15
+ postgres: ->(_config) {
16
+ require_relative 'store/postgres_store'
17
+ WCC::Contentful::Store::PostgresStore.new(ENV['POSTGRES_CONNECTION'])
18
+ }
19
+ }.freeze
20
+
21
+ CDN_METHODS = %i[
22
+ eager_sync
23
+ lazy_sync
24
+ direct
25
+ ].freeze
26
+
27
+ Factory =
28
+ Struct.new(:config, :cdn_method, :content_delivery_params) do
29
+ def build_sync_store
30
+ unless respond_to?("build_#{cdn_method}")
31
+ raise ArgumentError, "Don't know how to build content delivery method #{cdn_method}"
32
+ end
33
+
34
+ public_send("build_#{cdn_method}", config, *content_delivery_params)
35
+ end
36
+
37
+ def validate!
38
+ unless CDN_METHODS.include?(cdn_method)
39
+ raise ArgumentError, "Please use one of #{CDN_METHODS} for 'content_delivery'"
40
+ end
41
+
42
+ return unless respond_to?("validate_#{cdn_method}")
43
+ public_send("validate_#{cdn_method}", config, *content_delivery_params)
44
+ end
45
+
46
+ def build_eager_sync(config, store = nil, *_options)
47
+ puts "store: #{store}"
48
+ store = SYNC_STORES[store].call(config) if store.is_a?(Symbol)
49
+ store || MemoryStore.new
50
+ end
51
+
52
+ def build_lazy_sync(config, *options)
53
+ WCC::Contentful::Store::LazyCacheStore.new(
54
+ config.client,
55
+ cache: ActiveSupport::Cache.lookup_store(*options)
56
+ )
57
+ end
58
+
59
+ def build_direct(config, *_options)
60
+ CDNAdapter.new(config.client)
61
+ end
62
+
63
+ def validate_eager_sync(_config, store = nil, *_options)
64
+ return unless store.is_a?(Symbol)
65
+
66
+ return if SYNC_STORES.keys.include?(store)
67
+ raise ArgumentError, "Please use one of #{SYNC_STORES.keys}"
68
+ end
69
+ end
70
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module WCC
4
4
  module Contentful
5
- VERSION = '0.1.0'
5
+ VERSION = '0.2.0'
6
6
  end
7
7
  end