storyblok 2.0.8 → 3.0.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 15b954d3cf0dc7ca6346d490dfe34ed0b676cbd3eef222d7091860d31c7103b4
4
- data.tar.gz: f5aaa23eac80dba3e1129c729c96aa3f57a4fcda746a6bdd14a6e914a25523fb
3
+ metadata.gz: a0a390c17a7c90bad28b7a799a7f151486d55d38977a98257cf67a1cd539e9dd
4
+ data.tar.gz: 54f9aaad47ad4d435cd40292fc969e9250c5f934c278475880f83a077e49b7cc
5
5
  SHA512:
6
- metadata.gz: 69cdd57edaab0f030cd3018a0e27caa4d28dde70ca6e4b75d00bd773d71838d3e8de9346e05b53c1b8885f273037787c09a08937de6564193bde954831e91941
7
- data.tar.gz: 4680ba3b6697ad2e25bd50cb0039dc3b3a4c3652df680045e862a80a8d9625d057a388d3d0b448bf5c17c042c2cea158672f2144fde359311b8ece9eb68dc58e
6
+ metadata.gz: 179c3cc2405a6370ab2cc924fa925df5df0a2737b18cd76ac6c6183e3c980889dd1782a4b3cb6f3f0e963124e138639864b6ed04fd293286c647fcf03a092424
7
+ data.tar.gz: aff00d8502c0816aac6078e68d4060db5a8041249c695f5e55c2c2ea8825ebf63efd788fd408acb9f9408ad57ce9f375eb098a5cb05253f8c820b1b3cd8c25aa
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- storyblok (2.0.6)
4
+ storyblok (2.0.8)
5
5
  rest-client (>= 1.8.0, < 3)
6
6
  storyblok-richtext-renderer (>= 0.0.4, < 1)
7
7
 
@@ -16,7 +16,7 @@ GEM
16
16
  domain_name (~> 0.5)
17
17
  mime-types (3.3)
18
18
  mime-types-data (~> 3.2015)
19
- mime-types-data (3.2019.0904)
19
+ mime-types-data (3.2019.1009)
20
20
  netrc (0.11.0)
21
21
  redis (4.1.0)
22
22
  rest-client (2.1.0)
data/README.md CHANGED
@@ -1,5 +1,11 @@
1
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/76e7fcc8524d4fadeeee/test_coverage)](https://codeclimate.com/github/storyblok/storyblok-ruby/test_coverage)
2
+ ![GitHub Workflow Status (branch)](https://img.shields.io/github/workflow/status/storyblok/storyblok-ruby/RSpec%20Tests/master)
3
+ [![Ruby Gems Downloads](https://img.shields.io/gem/dt/storyblok)](https://rubygems.org/gems/storyblok)
4
+ [![Inline docs](https://inch-ci.org/github/storyblok/storyblok-ruby.svg?branch=master)](https://www.rubydoc.info/gems/storyblok)
5
+ [![Maintainability](https://api.codeclimate.com/v1/badges/76e7fcc8524d4fadeeee/maintainability)](https://codeclimate.com/github/storyblok/storyblok-ruby/maintainability)
6
+
1
7
  # About
2
- This is the Storyblok ruby client for easy access of the management and content delivery api.
8
+ This is the official [Storyblok](https://www.storyblok.com/) ruby client for easy access of the management and content delivery api.
3
9
 
4
10
  ## Install
5
11
 
@@ -120,7 +126,7 @@ client.flush
120
126
  client = Storyblok::Client.new(oauth_token: 'YOUR_OAUTH_TOKEN')
121
127
 
122
128
  # Get your spaces
123
- client.get('spaces')
129
+ client.get('spaces/')
124
130
  ```
125
131
 
126
132
  ### Create a story
@@ -158,12 +164,12 @@ Storyblok's richtext field also let's you insert content blocks. To render these
158
164
  ```ruby
159
165
  # Option 1: Define the resolver when initializing
160
166
  client = Storyblok::Client.new(
161
- component_resolver: ->(component, data) => {
167
+ component_resolver: ->(component, data) {
162
168
  case component
163
169
  when 'button'
164
170
  "<button>#{data['text']}</button>"
165
171
  when 'your_custom_component'
166
- "<div class="welcome">#{data['welcome_text']}</div>"
172
+ "<div class='welcome'>#{data['welcome_text']}</div>"
167
173
  end
168
174
  }
169
175
  )
@@ -183,6 +189,32 @@ gem build storyblok.gemspec
183
189
  gem push storyblok-2.0.X.gem
184
190
  ~~~
185
191
 
192
+ ### Running Tests
193
+ We use [RSpec](http://rspec.info/) for testing.
194
+
195
+ #### To run the whole test suite you will need export the environment variables, ATTENTION when running the test suit with the variable `REDIS_URL` exported, the test suite will remove the keys with this pattern `storyblok:*` from the redis database defined by `REDIS_URL`
196
+
197
+ ```bash
198
+ export REDIS_URL="redis://localhost:6379"
199
+ ```
200
+
201
+ Optionally you can generate the test report coverage by setting the environment variable
202
+
203
+ ```bash
204
+ export COVERAGE=true
205
+ ```
206
+
207
+ To run the whole test suite use the following command:
208
+
209
+ ```bash
210
+ rspec
211
+ ```
212
+
213
+ To run tests without redis cache tests (for when you don't have redis, or to avoid the test suite to remove the keys with this pattern `storyblok:*` ):
214
+
215
+ ```bash
216
+ rspec --tag ~redis_cache:true
217
+ ```
186
218
 
187
219
  ### License
188
220
 
data/examples/renderer.rb CHANGED
@@ -1,23 +1,31 @@
1
1
  # bundle exec ruby examples/renderer.rb
2
2
 
3
3
  require_relative '../lib/storyblok'
4
+ require 'redis'
4
5
 
5
6
  logger = Logger.new(STDOUT)
6
7
 
8
+ redis = Redis.new(url: 'redis://localhost:6379')
9
+ cache = Storyblok::Cache::Redis.new(redis: redis)
10
+
7
11
  client = Storyblok::Client.new(
8
12
  token: '6HMYdAjBoONyuS6GIf5PdAtt',
9
13
  logger: logger,
10
14
  component_resolver: ->(component, data) {
11
15
  "Placeholder for #{component}: #{data['text']}"
12
- }
16
+ },
17
+ api_url: 'api-testing.storyblok.com',
18
+ api_version: 2,
19
+ cache: cache
13
20
  )
14
21
 
15
- puts client.render({'type' => 'doc', 'content' => [
16
- {'type' => 'paragraph', 'content' => [{'text' => 'Good', 'type' => 'text'}]},
17
- {'type' => 'blok', 'attrs' => {'body' => [{'component' => 'button', 'text' => 'Click me'}]}}
18
- ]})
19
22
 
20
- res = client.story('article/article-1')
23
+ res = client.flush
24
+ res = client.story('authors/page', {version: 'published'})
25
+ puts client.cache_version
26
+ res = client.story('authors/page', {version: 'published'})
27
+ res = client.story('authors/page', {version: 'published'})
28
+ res = client.story('authors/page', {version: 'published'})
21
29
 
22
- puts res['data']['story']['content']['intro']
23
- puts client.render(res['data']['story']['content']['intro'])
30
+ puts res['data']
31
+ #puts client.render(res['data']['story']['content']['intro'])
@@ -3,7 +3,9 @@ module Storyblok
3
3
  class Redis
4
4
  DEFAULT_CONFIGURATION = {
5
5
  ttl: 60 * 60 * 24
6
- }
6
+ }.freeze
7
+
8
+ attr_reader :redis
7
9
 
8
10
  def initialize(*args)
9
11
  options = args.last.is_a?(::Hash) ? args.pop : {}
@@ -12,16 +12,19 @@ module Storyblok
12
12
  DEFAULT_CONFIGURATION = {
13
13
  secure: true,
14
14
  api_url: 'api.storyblok.com',
15
- api_version: 1,
15
+ api_version: 2,
16
16
  logger: false,
17
17
  log_level: Logger::INFO,
18
18
  version: 'draft',
19
+ # :nocov:
19
20
  component_resolver: ->(component, data) { '' },
21
+ # :nocov:
20
22
  cache_version: Time.now.to_i,
21
23
  cache: nil
22
24
  }
23
25
 
24
26
  attr_reader :configuration, :logger
27
+ attr_accessor :cache_version
25
28
 
26
29
  # @param [Hash] given_configuration
27
30
  # @option given_configuration [String] :token Required if oauth_token is not set
@@ -33,6 +36,7 @@ module Storyblok
33
36
  # @option given_configuration [::Logger::DEBUG, ::Logger::INFO, ::Logger::WARN, ::Logger::ERROR] :log_level
34
37
  def initialize(given_configuration = {})
35
38
  @configuration = default_configuration.merge(given_configuration)
39
+ @cache_version = '0'
36
40
  validate_configuration!
37
41
 
38
42
  if configuration[:oauth_token]
@@ -62,7 +66,7 @@ module Storyblok
62
66
  #
63
67
  # @return [Hash]
64
68
  def space(query = {})
65
- Request.new(self, '/cdn/spaces/me', query).get
69
+ Request.new(self, '/cdn/spaces/me', query, nil, true).get
66
70
  end
67
71
 
68
72
  # Gets a collection of stories
@@ -169,32 +173,34 @@ module Storyblok
169
173
  parse_result(res)
170
174
  end
171
175
 
172
- def cached_get(request)
176
+ def cached_get(request, bypass_cache = false)
173
177
  endpoint = base_url + request.url
178
+ query = request_query(request.query)
179
+ query_string = build_nested_query(query)
174
180
 
175
- if cache.nil?
176
- query = request_query(request.query)
177
- query_string = build_nested_query(query)
181
+ if cache.nil? || bypass_cache || query[:version] == 'draft'
178
182
  result = run_request(endpoint, query_string)
179
183
  else
180
- version = cache.get('storyblok:' + configuration[:token] + ':version') || '0'
181
-
182
- query = query = request_query({ cache_version: version }.merge(request.query))
183
- query_string = build_nested_query(query)
184
-
185
- cache_key = 'storyblok:' + configuration[:token] + ':v:' + version + ':' + request.url + ':' + Base64.encode64(query_string)
184
+ cache_key = 'storyblok:' + configuration[:token] + ':v:' + query[:cv] + ':' + request.url + ':' + Base64.encode64(query_string)
186
185
 
187
186
  result = cache.cache(cache_key) do
188
187
  run_request(endpoint, query_string)
189
188
  end
190
189
  end
191
190
 
192
- JSON.parse(result)
191
+ result = JSON.parse(result)
192
+
193
+ if !result.dig('data', 'story').nil? || !result.dig('data', 'stories').nil?
194
+ result = resolve_stories(result, query)
195
+ end
196
+
197
+ result
193
198
  end
194
199
 
200
+
195
201
  def flush
196
202
  unless cache.nil?
197
- cache.set('storyblok:' + configuration[:token] + ':version', Time.now.to_i.to_s)
203
+ cache.set('storyblok:' + configuration[:token] + ':version', space['data']['space']['version'])
198
204
  end
199
205
  end
200
206
 
@@ -240,14 +246,27 @@ module Storyblok
240
246
  raise
241
247
  end
242
248
 
243
- {'headers' => res.headers, 'data' => JSON.parse(res.body)}.to_json
249
+ body = JSON.parse(res.body)
250
+ self.cache_version = body['cv'] if body['cv']
251
+
252
+ unless cache.nil?
253
+ cache.set('storyblok:' + configuration[:token] + ':version', cache_version)
254
+ end
255
+
256
+ {'headers' => res.headers, 'data' => body}.to_json
244
257
  end
245
258
 
246
259
  # Patches a query hash with the client configurations for queries
247
260
  def request_query(query)
248
261
  query[:token] = configuration[:token] if query[:token].nil?
249
262
  query[:version] = configuration[:version] if query[:version].nil?
250
- query[:cv] = configuration[:cache_version] if query[:cache_version].nil?
263
+
264
+ unless cache.nil?
265
+ query[:cv] = (cache.get('storyblok:' + configuration[:token] + ':version') or cache_version) if query[:cv].nil?
266
+ else
267
+ query[:cv] = cache_version if query[:cv].nil?
268
+ end
269
+
251
270
  query
252
271
  end
253
272
 
@@ -292,5 +311,87 @@ module Storyblok
292
311
  "#{prefix}=#{URI.encode_www_form_component(value)}"
293
312
  end
294
313
  end
314
+
315
+ def resolve_stories(result, params)
316
+ data = result['data']
317
+ rels = data['rels']
318
+ links = data['links']
319
+ resolve_relations = params[:resolve_relations] || params["resolve_relations"]
320
+
321
+ if data['stories'].nil?
322
+ find_and_fill_relations(data.dig('story', 'content'), resolve_relations, rels)
323
+ find_and_fill_links(data.dig('story', 'content'), links)
324
+ else
325
+ data['stories'].each do |story|
326
+ find_and_fill_relations(story['content'], resolve_relations, rels)
327
+ find_and_fill_links(story['content'], links)
328
+ end
329
+ end
330
+
331
+ result
332
+ end
333
+
334
+ def find_and_fill_links(content, links)
335
+ return if content.nil? || links.nil? || links.size.zero?
336
+
337
+ if content.is_a? Array
338
+ content.each do |item|
339
+ find_and_fill_links(item, links)
340
+ end
341
+ elsif content.is_a? Hash
342
+ content['story'] = nil
343
+ content.each do |_k, value|
344
+ if !content['fieldtype'].nil?
345
+ if content['fieldtype'] == 'multilink' && content['linktype'] == 'story'
346
+ id =
347
+ if content['id'].is_a? String
348
+ content['id']
349
+ elsif content['uuid'].is_a? String
350
+ content['uuid']
351
+ end
352
+
353
+ links.each do |link|
354
+ if link['uuid'] == id
355
+ content['story'] = link
356
+ break
357
+ end
358
+ end
359
+ end
360
+ end
361
+
362
+ find_and_fill_links(value, links)
363
+ end
364
+ content.delete('story') if content['story'].nil?
365
+ end
366
+ end
367
+
368
+ def find_and_fill_relations(content, relation_params, rels)
369
+ return if content.nil? || rels.nil? || rels.size.zero?
370
+
371
+ if content.is_a? Array
372
+ content.each do |item|
373
+ find_and_fill_relations(item, relation_params, rels)
374
+ end
375
+ elsif content.is_a? Hash
376
+ content.each do |_k, value|
377
+ if !content['component'].nil? && !content['_uid'].nil?
378
+ relation_params.split(',').each do |relation|
379
+ component, field_name = relation.split('.')
380
+
381
+ if (content['component'] == component) && !content[field_name].nil?
382
+ rels.each do |rel|
383
+ index = content[field_name].index(rel['uuid'])
384
+ if !index.nil?
385
+ content[field_name][index] = rel
386
+ end
387
+ end
388
+ end
389
+ end
390
+ end
391
+
392
+ find_and_fill_relations(value, relation_params, rels)
393
+ end
394
+ end
395
+ end
295
396
  end
296
397
  end
@@ -5,14 +5,15 @@ module Storyblok
5
5
  class Request
6
6
  attr_reader :client, :type, :query, :id, :endpoint
7
7
 
8
- def initialize(client, endpoint, query = {}, id = nil)
8
+ def initialize(client, endpoint, query = {}, id = nil, bypass_cache = false)
9
9
  @client = client
10
10
  @endpoint = endpoint
11
11
  @query = query
12
+ @bypass_cache = bypass_cache
12
13
 
13
14
  if id
14
15
  @type = :single
15
- @id = URI.escape(id)
16
+ @id = id
16
17
  else
17
18
  @type = :multi
18
19
  @id = nil
@@ -26,7 +27,7 @@ module Storyblok
26
27
 
27
28
  # Delegates the actual HTTP work to the client
28
29
  def get
29
- client.cached_get(self)
30
+ client.cached_get(self, @bypass_cache)
30
31
  end
31
32
 
32
33
  # Returns a new Request object with the same data
@@ -1,4 +1,4 @@
1
1
  module Storyblok
2
2
  # Gem Version
3
- VERSION = '2.0.8'
3
+ VERSION = '3.0.1'
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: storyblok
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.8
4
+ version: 3.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Storyblok (Alexander Feiglstorfer)
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-17 00:00:00.000000000 Z
11
+ date: 2021-12-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rest-client
@@ -64,6 +64,20 @@ dependencies:
64
64
  - - "~>"
65
65
  - !ruby/object:Gem::Version
66
66
  version: '1.5'
67
+ - !ruby/object:Gem::Dependency
68
+ name: hashdiff
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: 1.0.1
74
+ type: :development
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: 1.0.1
67
81
  - !ruby/object:Gem::Dependency
68
82
  name: rspec
69
83
  requirement: !ruby/object:Gem::Requirement
@@ -78,6 +92,48 @@ dependencies:
78
92
  - - "~>"
79
93
  - !ruby/object:Gem::Version
80
94
  version: '3'
95
+ - !ruby/object:Gem::Dependency
96
+ name: webmock
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - '='
100
+ - !ruby/object:Gem::Version
101
+ version: 3.14.0
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - '='
107
+ - !ruby/object:Gem::Version
108
+ version: 3.14.0
109
+ - !ruby/object:Gem::Dependency
110
+ name: vcr
111
+ requirement: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - '='
114
+ - !ruby/object:Gem::Version
115
+ version: 6.0.0
116
+ type: :development
117
+ prerelease: false
118
+ version_requirements: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - '='
121
+ - !ruby/object:Gem::Version
122
+ version: 6.0.0
123
+ - !ruby/object:Gem::Dependency
124
+ name: simplecov
125
+ requirement: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - "<"
128
+ - !ruby/object:Gem::Version
129
+ version: 0.18.0
130
+ type: :development
131
+ prerelease: false
132
+ version_requirements: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - "<"
135
+ - !ruby/object:Gem::Version
136
+ version: 0.18.0
81
137
  description: Ruby client for the https://www.storyblok.com management and content
82
138
  delivery API
83
139
  email: it@storyblok.com
@@ -105,7 +161,6 @@ files:
105
161
  - lib/storyblok/links.rb
106
162
  - lib/storyblok/request.rb
107
163
  - lib/storyblok/version.rb
108
- - storyblok.gemspec
109
164
  homepage: https://github.com/storyblok/storyblok-ruby
110
165
  licenses:
111
166
  - MIT
data/storyblok.gemspec DELETED
@@ -1,23 +0,0 @@
1
- require File.expand_path('../lib/storyblok/version', __FILE__)
2
-
3
- Gem::Specification.new do |gem|
4
- gem.name = 'storyblok'
5
- gem.version = Storyblok::VERSION
6
- gem.summary = 'storyblok'
7
- gem.description = 'Ruby client for the https://www.storyblok.com management and content delivery API'
8
- gem.license = 'MIT'
9
- gem.authors = ['Storyblok (Alexander Feiglstorfer)']
10
- gem.email = 'it@storyblok.com'
11
- gem.homepage = 'https://github.com/storyblok/storyblok-ruby'
12
-
13
- gem.files = Dir['{**/}{.*,*}'].select { |path| File.file?(path) && !path.start_with?('pkg') && !path.end_with?('.gem') }
14
- gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
15
- gem.test_files = gem.files.grep(%r{^spec/})
16
- gem.require_paths = ['lib']
17
-
18
- gem.add_dependency 'rest-client', '>= 1.8.0', '< 3'
19
- gem.add_dependency 'storyblok-richtext-renderer', '>= 0.0.4', '< 1'
20
-
21
- gem.add_development_dependency 'bundler', '~> 1.5'
22
- gem.add_development_dependency 'rspec', '~> 3'
23
- end