delivery-sdk-ruby 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 849e51845ec99d8e4e044b61fa4e66e490d05b8a269ceb92e1000f6cd60b79bd
4
+ data.tar.gz: e66ea8b3abefad47d8064144bd59d74c21357e73133ee4019ac9f080810a19f5
5
+ SHA512:
6
+ metadata.gz: 99af342265d241b6f0c48dcc6f75ecc0e2640b8fe2f5f5c2ce0af56804b47eb74d101223a1566b17c8404a117b36b60370ddb1375fef26f852a441170da2d456
7
+ data.tar.gz: b0e24d2fb3e7accd44f4603e30c7b317aa5f03d3e09889c19b988a096d725337a7fabaec5134f212c3f32c49a5dcaf0e4d7beeb5a0733a85012134b5c043436e
data/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 Eric Dugre
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,323 @@
1
+
2
+ [![Forums](https://img.shields.io/badge/chat-on%20forums-orange.svg)](https://forums.kenticocloud.com) [![Join the chat at https://kentico-community.slack.com](https://img.shields.io/badge/join-slack-E6186D.svg)](https://kentico-community.slack.com) [![Version](https://img.shields.io/badge/version-0.10.0-green.svg)](https://github.com/Kentico/delivery-sdk-ruby/blob/master/lib/delivery/version.rb)
3
+
4
+ # Delivery Ruby SDK
5
+
6
+ The Delivery Ruby SDK can be used in Ruby/Rails projects to retrieve content from Kentico Cloud. This is a community project and not an official Kentico SDK. If you find a bug in the SDK or have a feature request, please submit a GitHub issue.
7
+
8
+ ## Installation
9
+
10
+ To create the gem, clone this repo and run `rake build`, or download the gem from the /pkg directory. Then install the gem and its dependencies as usual:
11
+
12
+ ```ruby
13
+ gem install delivery-sdk-ruby-0.6.1.gem
14
+ gem install rest-client
15
+ gem install nokogiri
16
+ ```
17
+
18
+ Or, if you have a Gemfile, you can link the application directly to this repository:
19
+
20
+ ```ruby
21
+ gem 'delivery-sdk-ruby', :git => 'https://github.com/Kentico/delivery-sdk-ruby.git'
22
+ gem 'rest-client'
23
+ gem 'nokogiri'
24
+ ```
25
+
26
+ Then run `bundle install`. To use the SDK in an `.rb` file, you need to require it:
27
+
28
+ ```ruby
29
+ require 'delivery-sdk-ruby'
30
+ ```
31
+
32
+ ## Creating a client
33
+
34
+ You will use `Delivery::DeliveryClient` to obtain content from Kentico Cloud. Create an instance of the client and pass your project ID:
35
+
36
+ ```ruby
37
+ delivery_client = Delivery::DeliveryClient.new project_id: '<your-project-id>'
38
+ ```
39
+
40
+ ### Previewing unpublished content
41
+
42
+ To enable [preview](https://developer.kenticocloud.com/docs/previewing-content-in-a-separate-environment "preview"), pass the Preview API Key to the constructor:
43
+
44
+ ```ruby
45
+ delivery_client = Delivery::DeliveryClient.new project_id: '<your-project-id>',
46
+ preview_key: '<your-preview-key>'
47
+ ```
48
+
49
+ This enables preview, but you can toggle preview at any time by setting the `use_preview` attribute of DeliveryClient which is propogated to all queries created by the client, _or_ per-query by setting it's `use_preview` attribute:
50
+
51
+ ```ruby
52
+ # For all queries created by client
53
+ delivery_client.use_preview = false
54
+
55
+ # Per-query
56
+ query = delivery_client.items
57
+ query.use_preview = false
58
+ query.execute do |response|
59
+ # Do something
60
+ end
61
+ ```
62
+
63
+ ### Making secure requests
64
+
65
+ If you've [secured access](https://developer.kenticocloud.com/docs/securing-public-access "Securing public access") to your project, you need to provide the DeliveryClient with the primary or secondary key:
66
+
67
+ ```ruby
68
+ Delivery::DeliveryClient.new project_id: '<your-project-id>',
69
+ secure_key: '<your-secure-key>'
70
+ ```
71
+
72
+ ## Listing items
73
+
74
+
75
+ Use `.item` or `.items` to create a `Delivery::DeliveryQuery`, then call `.execute` to perform the request.
76
+
77
+ ```ruby
78
+ delivery_client.items.execute do |response|
79
+ response.items.each do |item|
80
+ # Do something
81
+ end
82
+ end
83
+ ```
84
+
85
+ ### Filtering
86
+
87
+ You can use [filtering](https://developer.kenticocloud.com/v1/reference#content-filtering "filtering") to retrieve particular items. The filtering methods are applied directly to a string and the available methods are:
88
+
89
+ |Method|Example|REST equivalent|
90
+ |--|--|--|
91
+ |all|`'elements.product_status'.all %w[bestseller on_sale]`|?elements.product_status[all]=bestseller,on_sale|
92
+ |any|`'elements.processing'.any %w[dry__natural_ semi_dry]`|?elements.processing[any]=dry__natural_,semi_dry|
93
+ |contains|`'elements.related_articles'.contains 'on_roasts'`|?elements.related_articles[contains]=on_roasts|
94
+ |eq|`'system.type'.eq 'grinder'`|?system.type=grinder|
95
+ |gt|`'elements.price'.gt 20`|?elements.price[gt]=20|
96
+ |gt_or_eq|`'elements.price'.gt_or_eq 20`|?elements.price[gte]=20|
97
+ |in|`'system.type'.in %w[coffee brewer]`|?system.type[in]=coffee,brewer|
98
+ |lt|`'elements.price'.lt 20`|?elements.price[lt]=20|
99
+ |lt_or_eq|`'elements.price'.lt_or_eq 20`|?elements.price[lte]=20|
100
+ |range|`'system.last_modified'.range %w[2018-02-01 2018-03-31]`|?system.last_modified[range]=2018-02-01,2018-03-31|
101
+
102
+ You can pass a single filter or multiple filters in the DeliveryClient methods. For example:
103
+
104
+ ```ruby
105
+ # Single filter
106
+ delivery_client.items('elements.price'.gt 20)
107
+
108
+ # Multiple filters
109
+ delivery_client.items [
110
+ ('elements.price'.gt 20),
111
+ ('system.type'.eq 'grinder')
112
+ ]
113
+ ```
114
+
115
+ ### Parameters
116
+
117
+ The `.item` and `.items` methods return a `Delivery::DeliveryQuery` object which you can further configure before executing. The methods you can call are:
118
+
119
+ |Method|Example|REST equivalent
120
+ |--|--|--|
121
+ |[order_by](https://developer.kenticocloud.com/v1/reference#content-ordering "order_by")|`order_by 'system.last_modified' '[desc]'`|?order=system.last_modified[desc]
122
+ |[skip](https://developer.kenticocloud.com/v1/reference#listing-response-paging "skip")|`skip 5`|?skip=5
123
+ |[limit](https://developer.kenticocloud.com/v1/reference#listing-response-paging "limit")|`limit 5`|?limit=5
124
+ |[elements](https://developer.kenticocloud.com/v1/reference#projection "elements")|`elements %w[price product_name image]`|?elements=price,product_name,image
125
+ |[depth](https://developer.kenticocloud.com/v1/reference#linked-content "depth")|`depth 0`|?depth=0
126
+ |[language](https://developer.kenticocloud.com/docs/understanding-language-fallbacks "language")|`language 'en'`|?language=en
127
+
128
+ For example:
129
+
130
+ ```ruby
131
+ delivery_client.items('system.type'.eq 'coffee')
132
+ .depth(0)
133
+ .limit(5)
134
+ .elements(%W[price product_name])
135
+ .execute do |response|
136
+ # Do something
137
+ end
138
+ ```
139
+
140
+ ### Custom URLs
141
+
142
+ When you have a URL (i.e. `next_page` for paging, for testing purposes, or if you prefer to build it on your own) and still want to leverage SDK functionality such as rich text resolving, use the .url method:
143
+
144
+ ```ruby
145
+ delivery_client.items
146
+ .url('https://deliver.kenticocloud.com/<your-project-id>/items?system.type=grinder')
147
+ .execute do |response|
148
+ # Do something
149
+ end
150
+ ```
151
+
152
+ ### Responses
153
+
154
+ All responses from the `.execute` method will be/extend the `Delivery::Responses::ResponseBase` class which contains an `http_code` attribute and a friendly message that can be displayed by calling `.to_s`. You can check the code to determine if the request was successful:
155
+
156
+ ```ruby
157
+ delivery_client.items.execute do |response|
158
+ case response.http_code
159
+ when 200
160
+ # Success!
161
+ when 401
162
+ # Did you forget the secure key?
163
+ else
164
+ puts response.to_s
165
+ end
166
+ end
167
+ ```
168
+
169
+ For successful content item queries, you will get either `DeliveryItemResponse` for single item queries, or `DeliveryItemListingResponse` for multiple item queries. You can access the returned content item(s) at `.item` or `.items` respectively.
170
+
171
+ The `ContentItem` object gives you access to all system elements and content type elements at the `.system` and `.elements` properies. These are dynamic objects, so you can simply type the name of the element you need:
172
+
173
+ ```ruby
174
+ price = response.item.elements.price.value
175
+ ```
176
+
177
+ ### Assets
178
+
179
+ You can use `.get_assets(code_name)` to get one or more assets from the specified element. This method will always return an array, so use `.first` to get the first asset:
180
+
181
+ ```ruby
182
+ url = response.item.get_assets('teaser_image').first.url
183
+ ```
184
+
185
+ ### Linked items
186
+
187
+ You can get a simple array of code names by accessing the element's value:
188
+
189
+ ```ruby
190
+ links = response.item.elements.facts.value
191
+ ```
192
+
193
+ The `.get_links(element)` method will return an array of ContentItems instead:
194
+
195
+ ```ruby
196
+ response.item.get_links('facts').each do |link|
197
+ title = link.elements.title.value
198
+ end
199
+ ```
200
+
201
+ ### Pagination
202
+
203
+ The `DeliveryItemListingResponse` also contains a `pagination` attribute to access the [paging](https://developer.kenticocloud.com/v1/reference#listing-response-paging "paging") data for the Delivery query. This object contains the following attributes:
204
+
205
+ - **skip**
206
+ - **limit**
207
+ - **count**
208
+ - **next_page**
209
+
210
+ For example, to access the next page URL you can use:
211
+
212
+ ```ruby
213
+ delivery_client.items
214
+ .skip(0)
215
+ .limit(5)
216
+ .execute do |response|
217
+ next_page_url = response.pagination.next_page
218
+ end
219
+ ```
220
+
221
+ You can then request the secure published content in your project. Be sure to not expose the key if the file(s) it appears in are publicly-available.
222
+
223
+ ## Retrieving content types
224
+
225
+ You can use the `.type` and `.types` methods to request your content types from Kentico Cloud:
226
+
227
+ ```ruby
228
+ delivery_client.types.execute do |response|
229
+ # Do something
230
+ end
231
+ delivery_client.type('coffee').execute do |response|
232
+ # Do something
233
+ end
234
+ ```
235
+
236
+ ### Responses
237
+
238
+ As with content item queries, all content type queries will return a `Delivery::Responses::ResponseBase` of the class `DeliveryTypeResponse` or `DeliveryTypeListingResponse` for single and multiple type queries, respectively.
239
+
240
+ For multiple type queries, you can access the array of `ContentType` objects at `.types`, and at `.type` for singe type queries. You can access information about the type(s) dynamically:
241
+
242
+ ```ruby
243
+ delivery_client.type('coffee').execute do |response|
244
+ field_type = response.type.elements.product_status.type # taxonomy
245
+ end
246
+ ```
247
+
248
+ The DeliveryTypeListingResponse also contains pagination data, similar to DeliveryItemListingResponse.
249
+
250
+ ## Resolving links
251
+
252
+ If a rich text element contains links to other content items, you will need to generate the URLs to those items. You can do this by registering a `Delivery::Resolvers::ContentLinkResolver` when you instantiate the DeliveryClient. When you create a ContentLinkResolver, you must pass a method that will return the URL:
253
+
254
+ ```ruby
255
+ link_resolver = Delivery::Resolvers::ContentLinkResolver.new(lambda do |link|
256
+ return "/coffees/#{link.url_slug}" if link.type == 'coffee'
257
+ return "/brewers/#{link.url_slug}" if link.type == 'brewer'
258
+ end)
259
+ delivery_client = Delivery::DeliveryClient.new project_id: '<your-project-id>',
260
+ content_link_url_resolver: link_resolver
261
+ ```
262
+
263
+ You can also build the logic for your resolver in a separate class and register an instance of that class in the DeliveryClient. The class must extend `Delivery::Resolvers::ContentLinkResolver` and contain a `resolve_link(link)` method. For example, you can create `MyLinkResolver.rb`:
264
+
265
+ ```ruby
266
+ class MyLinkResolver < Delivery::Resolvers::ContentLinkResolver
267
+ def resolve_link(link)
268
+ return "/coffees/#{link.url_slug}" if link.type == 'coffee'
269
+ return "/brewers/#{link.url_slug}" if link.type == 'brewer'
270
+ end
271
+ end
272
+ ```
273
+
274
+ Then create an object of this class when instantiating the DeliveryClient:
275
+
276
+ ```ruby
277
+ delivery_client = Delivery::DeliveryClient.new project_id: '<your-project-id>',
278
+ content_link_url_resolver: MyLinkResolver.new
279
+ ```
280
+
281
+ You can pass a `ContentLinkResolver` to the DeliveryQuery instead of the client if you only want to resolve links for that query, or they should be resolved differently:
282
+
283
+ ```ruby
284
+ delivery_client = Delivery::DeliveryClient.new project_id: '<your-project-id>'
285
+ # Client doesn't use ContentLinkResolver, but query below will
286
+ delivery_client.items
287
+ .with_link_resolver MyLinkResolver.new
288
+ ```
289
+
290
+ The `ContentLink` object that is passed to your resolver contains the following attributes:
291
+
292
+ - **id**: the system.id of the linked content item
293
+ - **code_name**: the system.codename of the linked content item
294
+ - **type**: the content type of the linked content item
295
+ - **url_slug**: the URL slug of the linked content item, or nil if there is none
296
+
297
+ To resolve links in rich text elements, you must retrieve the text using `get_string`:
298
+
299
+ ```ruby
300
+ lambda_resolver = Delivery::Resolvers::ContentLinkResolver.new(lambda do |link|
301
+ return "/coffees/#{link.url_slug}" if link.type == 'coffee'
302
+ return "/brewers/#{link.url_slug}" if link.type == 'brewer'
303
+ end)
304
+ delivery_client = Delivery::DeliveryClient.new project_id: '<your-project-id>',
305
+ content_link_url_resolver: lambda_resolver
306
+ delivery_client.item('coffee_processing_techniques').execute do |response|
307
+ text = response.item.get_string 'body_copy'
308
+ end
309
+ ```
310
+
311
+ ## Feedback & Contributing
312
+
313
+ Check out the [contributing](https://github.com/Kentico/delivery-sdk-ruby/blob/master/CONTRIBUTING.md) page to see the best places to file issues, start discussions, and begin contributing.
314
+
315
+ ## License
316
+
317
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
318
+
319
+ ## Code of Conduct
320
+
321
+ Everyone interacting in the Delivery project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/Kentico/delivery-sdk-net/blob/master/CODE_OF_CONDUCT.md).
322
+
323
+ ![Analytics](https://kentico-ga-beacon.azurewebsites.net/api/UA-69014260-4/Kentico/delivery-sdk-ruby?pixel)
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "delivery"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,58 @@
1
+ require 'delivery/client/delivery_query'
2
+ require 'delivery/responses/delivery_item_listing_response'
3
+ require 'delivery/responses/delivery_item_response'
4
+ require 'json'
5
+
6
+ module Delivery
7
+ QUERY_TYPE_TYPES = 'QUERY_TYPE_TYPES'.freeze
8
+ QUERY_TYPE_ITEMS = 'QUERY_TYPE_ITEMS'.freeze
9
+
10
+ # Executes requests against the Kentico Cloud Delivery API.
11
+ class DeliveryClient
12
+ attr_accessor :use_preview
13
+
14
+ def initialize(config)
15
+ @project_id = config.fetch(:project_id)
16
+ @preview_key = config.fetch(:preview_key, nil)
17
+ @secure_key = config.fetch(:secure_key, nil)
18
+ @content_link_url_resolver = config.fetch(:content_link_url_resolver, nil)
19
+ self.use_preview = !@preview_key.nil?
20
+ end
21
+
22
+ def types
23
+ DeliveryQuery.new project_id: @project_id,
24
+ secure_key: @secure_key,
25
+ query_type: QUERY_TYPE_TYPES
26
+ end
27
+
28
+ def type(code_name)
29
+ DeliveryQuery.new project_id: @project_id,
30
+ secure_key: @secure_key,
31
+ code_name: code_name,
32
+ query_type: QUERY_TYPE_TYPES
33
+ end
34
+
35
+ def items(query_parameters = [])
36
+ q = DeliveryQuery.new project_id: @project_id,
37
+ secure_key: @secure_key,
38
+ qp: query_parameters,
39
+ content_link_url_resolver: @content_link_url_resolver,
40
+ query_type: QUERY_TYPE_ITEMS
41
+ q.use_preview = use_preview
42
+ q.preview_key = @preview_key
43
+ q
44
+ end
45
+
46
+ def item(code_name, query_parameters = [])
47
+ q = DeliveryQuery.new project_id: @project_id,
48
+ secure_key: @secure_key,
49
+ code_name: code_name,
50
+ qp: query_parameters,
51
+ content_link_url_resolver: @content_link_url_resolver,
52
+ query_type: QUERY_TYPE_ITEMS
53
+ q.use_preview = use_preview
54
+ q.preview_key = @preview_key
55
+ q
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,166 @@
1
+ require 'rest-client'
2
+ require 'delivery/client/url_provider'
3
+
4
+ module Delivery
5
+ # Responsible for translating query parameters into the
6
+ # corresponding REST request to Kentico Cloud.
7
+ class DeliveryQuery
8
+ ERROR_PREVIEW = 'Preview is enabled for the query, but the key is null. '\
9
+ 'You can set the preview_key attribute of the query, or '\
10
+ 'when you initialize the client. See '\
11
+ 'https://github.com/Kentico/delivery-sdk-ruby#previewing-unpublished-content'.freeze
12
+ ERROR_PARAMS = 'Only filters may be passed in the .item or .items methods'\
13
+ '. See https://github.com/Kentico/delivery-sdk-ruby#filtering'.freeze
14
+ attr_accessor :use_preview,
15
+ :preview_key,
16
+ :project_id,
17
+ :code_name,
18
+ :params,
19
+ :secure_key,
20
+ :content_link_url_resolver,
21
+ :query_type
22
+
23
+ # Setter for url, returns self for chaining
24
+ # .url represents *manually* configured urls, otherwise final url is
25
+ # generated in .execute and this will return nil
26
+ def url(url = nil)
27
+ @url = url unless url.nil?
28
+ self
29
+ end
30
+
31
+ def initialize(config)
32
+ # Map each hash value to attr with corresponding key
33
+ # from https://stackoverflow.com/a/2681014/5656214
34
+ config.each do |k, v|
35
+ instance_variable_set("@#{k}", v) unless v.nil?
36
+ end
37
+ return if config.fetch(:qp, nil).nil?
38
+
39
+ # Query parameters were passed, parse and validate
40
+ validate_params config.fetch(:qp)
41
+ end
42
+
43
+ def execute
44
+ provide_url
45
+ begin
46
+ resp = execute_rest
47
+ rescue RestClient::ExceptionWithResponse => err
48
+ yield Delivery::Responses::ResponseBase.new err.http_code, err.response
49
+ rescue RestClient::SSLCertificateNotVerified => err
50
+ yield Delivery::Responses::ResponseBase.new 500, err
51
+ rescue SocketError => err
52
+ yield Delivery::Responses::ResponseBase.new 500, err.message
53
+ else
54
+ yield make_response resp
55
+ end
56
+ end
57
+
58
+ def with_link_resolver(resolver)
59
+ self.content_link_url_resolver = resolver
60
+ self
61
+ end
62
+
63
+ def order_by(value, sort = '[asc]')
64
+ set_param('order', value + sort)
65
+ self
66
+ end
67
+
68
+ def skip(value)
69
+ set_param('skip', value)
70
+ self
71
+ end
72
+
73
+ def language(value)
74
+ set_param('language', value)
75
+ end
76
+
77
+ def limit(value)
78
+ set_param('limit', value)
79
+ self
80
+ end
81
+
82
+ def elements(value)
83
+ set_param('elements', value)
84
+ self
85
+ end
86
+
87
+ def depth(value)
88
+ set_param('depth', value)
89
+ self
90
+ end
91
+
92
+ private
93
+
94
+ def provide_url
95
+ @url = Delivery::UrlProvider.provide_url self if @url.nil?
96
+ Delivery::UrlProvider.validate_url @url
97
+ end
98
+
99
+ def validate_params(query_parameters)
100
+ self.params = if query_parameters.is_a? Array
101
+ query_parameters
102
+ else
103
+ [query_parameters]
104
+ end
105
+ params.each do |p|
106
+ unless p.is_a? Delivery::QueryParameters::Filter
107
+ raise ArgumentError, ERROR_PARAMS
108
+ end
109
+ end
110
+ end
111
+
112
+ def set_param(key, value)
113
+ self.params = [] if params.nil?
114
+ remove_existing_param key
115
+ params << Delivery::QueryParameters::ParameterBase.new(key, '', value)
116
+ end
117
+
118
+ # Returns true if this query should use preview mode. Raises an error if
119
+ # preview is enabled, but the key is nil
120
+ def should_preview
121
+ raise ERROR_PREVIEW if use_preview && preview_key.nil?
122
+
123
+ use_preview && !preview_key.nil?
124
+ end
125
+
126
+ def execute_rest
127
+ if should_preview
128
+ RestClient.get @url, Authorization: 'Bearer ' + preview_key
129
+ else
130
+ if secure_key.nil?
131
+ RestClient.get @url
132
+ else
133
+ RestClient.get @url, Authorization: 'Bearer ' + secure_key
134
+ end
135
+ end
136
+ end
137
+
138
+ def make_response(response)
139
+ case query_type
140
+ when Delivery::QUERY_TYPE_ITEMS
141
+ if code_name.nil?
142
+ Delivery::Responses::DeliveryItemListingResponse.new(
143
+ JSON.parse(response),
144
+ content_link_url_resolver
145
+ )
146
+ else
147
+ Delivery::Responses::DeliveryItemResponse.new(
148
+ JSON.parse(response),
149
+ content_link_url_resolver
150
+ )
151
+ end
152
+ when Delivery::QUERY_TYPE_TYPES
153
+ if code_name.nil?
154
+ Delivery::Responses::DeliveryTypeListingResponse.new JSON.parse(response)
155
+ else
156
+ Delivery::Responses::DeliveryTypeResponse.new JSON.parse(response)
157
+ end
158
+ end
159
+ end
160
+
161
+ # Remove existing parameter from @params if key exists
162
+ def remove_existing_param(key)
163
+ params.delete_if { |i| i.key.eql? key } unless params.nil?
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,64 @@
1
+ module Delivery
2
+ # Generates the URL required for Delivery REST API
3
+ class UrlProvider
4
+ URL_TEMPLATE_BASE = 'https://deliver.kenticocloud.com/%s'.freeze
5
+ URL_TEMPLATE_PREVIEW = 'https://preview-deliver.kenticocloud.com/%s'.freeze
6
+ URL_TEMPLATE_ITEM = '/items/%s'.freeze
7
+ URL_TEMPLATE_ITEMS = '/items'.freeze
8
+ URL_TEMPLATE_TYPE = '/types/%s'.freeze
9
+ URL_TEMPLATE_TYPES = '/types'.freeze
10
+ URL_TEMPLATE_ELEMENTS = '/types/%s/elements/%s'.freeze
11
+ URL_TEMPLATE_TAXONOMY = '/taxonomies/%s'.freeze
12
+ URL_TEMPLATE_TAXONOMIES = '/taxonomies'.freeze
13
+
14
+ URL_MAX_LENGTH = 65_519
15
+ MSG_LONG_QUERY = 'The request url is too long. Split your query into multiple calls.'.freeze
16
+
17
+ class << self
18
+ def provide_url(query)
19
+ url = provide_base_url(query)
20
+ url += provide_path_part(query)
21
+
22
+ if query.params.nil?
23
+ url
24
+ else
25
+ # Map each parameter to the result of a method and separate with &
26
+ url + '?' + query.params.map(&:provide_query_string_parameter).join('&')
27
+ end
28
+ end
29
+
30
+ def validate_url(url)
31
+ raise UriFormatException, MSG_LONG_QUERY if url.length > URL_MAX_LENGTH
32
+ end
33
+
34
+ private
35
+
36
+ # Returns relative path part of URL depending on query type
37
+ def provide_path_part(query)
38
+ case query.query_type
39
+ when Delivery::QUERY_TYPE_ITEMS
40
+ if query.code_name.nil?
41
+ URL_TEMPLATE_ITEMS
42
+ else
43
+ format(URL_TEMPLATE_ITEM, query.code_name)
44
+ end
45
+ when Delivery::QUERY_TYPE_TYPES
46
+ if query.code_name.nil?
47
+ URL_TEMPLATE_TYPES
48
+ else
49
+ format(URL_TEMPLATE_TYPE, query.code_name)
50
+ end
51
+ end
52
+ end
53
+
54
+ # Returns the protocol and domain with project ID
55
+ def provide_base_url(query)
56
+ if query.use_preview
57
+ format(URL_TEMPLATE_PREVIEW, query.project_id)
58
+ else
59
+ format(URL_TEMPLATE_BASE, query.project_id)
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,72 @@
1
+ require 'ostruct'
2
+ require 'nokogiri'
3
+
4
+ module Delivery
5
+ # JSON data of a content item parsed as OpenStruct objects for dynamic use
6
+ class ContentItem
7
+ attr_accessor :content_link_url_resolver
8
+
9
+ def elements
10
+ @elements unless @elements.nil?
11
+ @elements = JSON.parse(
12
+ JSON.generate(@source['elements']),
13
+ object_class: OpenStruct
14
+ )
15
+ end
16
+
17
+ def system
18
+ @system unless @system.nil?
19
+ @system = JSON.parse(
20
+ JSON.generate(@source['system']),
21
+ object_class: OpenStruct
22
+ )
23
+ end
24
+
25
+ def initialize(source, content_link_url_resolver)
26
+ @source =
27
+ if source['item'].nil?
28
+ source
29
+ else
30
+ source['item']
31
+ end
32
+ @link_source = source['modular_content']
33
+ self.content_link_url_resolver = content_link_url_resolver
34
+ end
35
+
36
+ def get_string(code_name)
37
+ element = get_element code_name
38
+
39
+ # Resolve content links if there are any and we have a resolver
40
+ return content_link_url_resolver.resolve element['value'], element['links'] if should_resolve element
41
+
42
+ element['value'].to_s
43
+ end
44
+
45
+ # Returns an array of assets inserted into the specified element
46
+ def get_assets(code_name)
47
+ element = get_element code_name
48
+ element['value'].map { |n| OpenStruct.new(n) }
49
+ end
50
+
51
+ # Returns an array of ContentItems by comparing code names stored in the
52
+ # element with items from request's link_source
53
+ def get_links(code_name)
54
+ element = get_element code_name
55
+ filtered = @link_source.values.select { |item| element['value'].include?(item['system']['codename']) }
56
+ filtered.map { |n| ContentItem.new JSON.parse(JSON.generate(n)), content_link_url_resolver }
57
+ end
58
+
59
+ private
60
+
61
+ def should_resolve(element)
62
+ element['type'] == 'rich_text' && !element['links'].nil? && !content_link_url_resolver.nil?
63
+ end
64
+
65
+ def get_element(code_name)
66
+ raise ArgumentError, "Argument 'code_name' cannot be null" if code_name.nil?
67
+ raise ArgumentError, "Argument 'code_name' is not a string" unless code_name.is_a? String
68
+
69
+ @source['elements'][code_name]
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,26 @@
1
+ require 'ostruct'
2
+
3
+ module Delivery
4
+ # JSON data of a content type parsed as OpenStruct objects for dynamic use
5
+ class ContentType
6
+ def elements
7
+ @elements unless @elements.nil?
8
+ @elements = JSON.parse(
9
+ JSON.generate(@source['elements']),
10
+ object_class: OpenStruct
11
+ )
12
+ end
13
+
14
+ def system
15
+ @system unless @system.nil?
16
+ @system = JSON.parse(
17
+ JSON.generate(@source['system']),
18
+ object_class: OpenStruct
19
+ )
20
+ end
21
+
22
+ def initialize(source)
23
+ @source = source
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,13 @@
1
+ module Delivery
2
+ # Holds pagination data from a DeliveryItemListingResponse
3
+ class Pagination
4
+ attr_accessor :skip, :limit, :count, :next_page
5
+
6
+ def initialize(json)
7
+ self.skip = json['skip']
8
+ self.limit = json['limit']
9
+ self.count = json['count']
10
+ self.next_page = json['next_page']
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,87 @@
1
+ require 'delivery/query_parameters/parameter_base'
2
+
3
+ module Delivery
4
+ module QueryParameters
5
+ # Provides the base class for filter implementations.
6
+ class Filter < ParameterBase
7
+ def initialize(key, operator, values)
8
+ super(key, operator, values)
9
+ end
10
+ end
11
+ end
12
+ end
13
+
14
+ # Extend String class to allow semantic typing of filters
15
+ class String
16
+ # Represents a filter that matches a content item if the specified content
17
+ # element or system attribute has a value that contains all the specified
18
+ # values. This filter is applicable to array values only, such as sitemap
19
+ # location or value of Linked Items, Taxonomy and Multiple choice content elements.
20
+ def all(*args)
21
+ Delivery::QueryParameters::Filter.new(self, '[all]', *args)
22
+ end
23
+
24
+ # Represents a filter that matches a content item if the specified content
25
+ # element or system attribute has a value that contains any the specified
26
+ # values. This filter is applicable to array values only, such as sitemap
27
+ # location or value of Linked Items, Taxonomy and Multiple choice content elements.
28
+ def any(*args)
29
+ Delivery::QueryParameters::Filter.new(self, '[any]', *args)
30
+ end
31
+
32
+ # Represents a filter that matches a content item if the specified content element
33
+ # or system attribute has a value that contains the specified value.
34
+ # This filter is applicable to array values only, such as sitemap location or value
35
+ # of Linked Items, Taxonomy and Multiple choice content elements.
36
+ def contains(*args)
37
+ Delivery::QueryParameters::Filter.new(self, '[contains]', *args)
38
+ end
39
+
40
+ # Represents a filter that matches a content item if the specified
41
+ # content element or system attribute has the specified value.
42
+ def eq(*args)
43
+ Delivery::QueryParameters::Filter.new(self, '', *args)
44
+ end
45
+
46
+ # Represents a filter that matches a content item if the specified content
47
+ # element or system attribute has a value that is greater than the
48
+ # specified value.
49
+ def gt(*args)
50
+ Delivery::QueryParameters::Filter.new(self, '[gt]', *args)
51
+ end
52
+
53
+ # Represents a filter that matches a content item if the specified content
54
+ # element or system attribute has a value that is greater than or equal to
55
+ # the specified value.
56
+ def gt_or_eq(*args)
57
+ Delivery::QueryParameters::Filter.new(self, '[gte]', *args)
58
+ end
59
+
60
+ # Represents a filter that matches a content item if the specified
61
+ # content element or system attribute has a value that matches a
62
+ # value in the specified list.
63
+ def in(*args)
64
+ Delivery::QueryParameters::Filter.new(self, '[in]', *args)
65
+ end
66
+
67
+ # Represents a filter that matches a content item if the specified content
68
+ # element or system attribute has a value that is less than the
69
+ # specified value.
70
+ def lt(*args)
71
+ Delivery::QueryParameters::Filter.new(self, '[lt]', *args)
72
+ end
73
+
74
+ # Represents a filter that matches a content item if the specified content
75
+ # element or system attribute has a value that is less than or equal to
76
+ # the specified value.
77
+ def lt_or_eq(*args)
78
+ Delivery::QueryParameters::Filter.new(self, '[lte]', *args)
79
+ end
80
+
81
+ # Represents a filter that matches a content item if the specified
82
+ # content element or system attribute has a value that falls within
83
+ # the specified range of values (both inclusive).
84
+ def range(*args)
85
+ Delivery::QueryParameters::Filter.new(self, '[range]', *args)
86
+ end
87
+ end
@@ -0,0 +1,30 @@
1
+ module Delivery
2
+ # Contains static methods for adding parameters to a DeliveryQuery
3
+ # as well as the Filter class.
4
+ module QueryParameters
5
+ # Base class for all parameters added to a DeliveryQuery using the
6
+ # .parameters method. All parameters appear in the query string.
7
+ class ParameterBase
8
+ attr_accessor :key
9
+ SEPARATOR = CGI.escape(',')
10
+
11
+ def initialize(key, operator, values)
12
+ self.key = key
13
+ values = [values] unless values.respond_to? :each
14
+ @values = values
15
+ @operator = operator
16
+ end
17
+
18
+ def provide_query_string_parameter
19
+ escaped_values = []
20
+ @values.each { |n| escaped_values << CGI.escape(n.to_s) }
21
+ format(
22
+ '%<k>s%<o>s=%<v>s',
23
+ k: CGI.escape(key),
24
+ o: CGI.escape(@operator),
25
+ v: escaped_values.join(SEPARATOR)
26
+ )
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,62 @@
1
+ require 'nokogiri'
2
+
3
+ module Delivery
4
+ module Resolvers
5
+ # Locates <a data-item-id=""> tags in content and calls a user-defined method
6
+ # to supply the href for content item links
7
+ class ContentLinkResolver
8
+ def initialize(callback = nil)
9
+ @callback = callback
10
+ end
11
+
12
+ # Resolves all links in the content
13
+ # @param [String] content The string value stored in the element
14
+ # @param [Array] links The collection of source links from the JSON response
15
+ def resolve(content, links)
16
+ doc = Nokogiri::HTML.parse(content).xpath('//body')
17
+ links = links.map { |link| ContentLink.new link }
18
+ tags = doc.xpath('//a[@data-item-id]')
19
+ # This line performs the link resolving and replaces the tags in doc
20
+ tags.map { |tag| resolve_tag tag, links }
21
+ doc.inner_html
22
+ end
23
+
24
+ private
25
+
26
+ # Accepts a tag found in the content and tries to locate matching
27
+ # source link from JSON response. If found, resolves URL and returns
28
+ # the tag with generated HREF
29
+ def resolve_tag(tag, links)
30
+ matches = links.select { |link| link.id == tag['data-item-id'].to_s }
31
+ url = provide_url matches
32
+ tag['href'] = url
33
+ tag
34
+ end
35
+
36
+ # Returns a url if a link was found in source links, otherwise returns 404
37
+ def provide_url(matches)
38
+ if !matches.empty?
39
+ if @callback.nil?
40
+ resolve_link matches[0]
41
+ else
42
+ @callback.call matches[0]
43
+ end
44
+ else
45
+ '/404'
46
+ end
47
+ end
48
+ end
49
+
50
+ # Model for links from the JSON response
51
+ class ContentLink
52
+ attr_accessor :code_name, :type, :url_slug, :id
53
+
54
+ def initialize(link)
55
+ self.id = link[0]
56
+ self.code_name = link[1]['codename']
57
+ self.type = link[1]['type']
58
+ self.url_slug = link[1]['url_slug']
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,33 @@
1
+ require 'delivery/models/content_item'
2
+ require 'delivery/models/pagination'
3
+ require 'delivery/responses/response_base'
4
+
5
+ module Delivery
6
+ module Responses
7
+ # Returned by DeliveryClient.items with an enumerable of ContentItems
8
+ class DeliveryItemListingResponse < ResponseBase
9
+ def pagination
10
+ @pagination unless @pagination.nil?
11
+ @pagination = Pagination.new @response['pagination']
12
+ end
13
+
14
+ def items
15
+ @items unless @items.nil?
16
+ items = []
17
+ @response['items'].each do |n|
18
+ items << Delivery::ContentItem.new(
19
+ n,
20
+ @content_link_url_resolver
21
+ )
22
+ end
23
+ @items = items
24
+ end
25
+
26
+ def initialize(response, content_link_url_resolver)
27
+ @response = response
28
+ @content_link_url_resolver = content_link_url_resolver
29
+ super 200, "Success, #{items.length} items returned"
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,20 @@
1
+ require 'delivery/models/content_item'
2
+ require 'delivery/responses/response_base'
3
+
4
+ module Delivery
5
+ module Responses
6
+ # Returned by DeliveryClient.item containing a single ContentItem
7
+ class DeliveryItemResponse < ResponseBase
8
+ def item
9
+ @item unless @item.nil?
10
+ @item = Delivery::ContentItem.new(@response, @content_link_url_resolver)
11
+ end
12
+
13
+ def initialize(response, content_link_url_resolver)
14
+ @response = response
15
+ @content_link_url_resolver = content_link_url_resolver
16
+ super 200, "Success, '#{item.system.code_name}' returned"
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,29 @@
1
+ require 'delivery/models/content_type'
2
+ require 'delivery/models/pagination'
3
+ require 'delivery/responses/response_base'
4
+
5
+ module Delivery
6
+ module Responses
7
+ # Returned by DeliveryClient.types with an enumerable of ContentTypes
8
+ class DeliveryTypeListingResponse < ResponseBase
9
+ def pagination
10
+ @pagination unless @pagination.nil?
11
+ @pagination = Pagination.new @response['pagination']
12
+ end
13
+
14
+ def types
15
+ @types unless @types.nil?
16
+ types = []
17
+ @response['types'].each do |n|
18
+ types << Delivery::ContentType.new(n)
19
+ end
20
+ @types = types
21
+ end
22
+
23
+ def initialize(response)
24
+ @response = response
25
+ super 200, "Success, #{types.length} types returned"
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,19 @@
1
+ require 'delivery/models/content_type'
2
+ require 'delivery/responses/response_base'
3
+
4
+ module Delivery
5
+ module Responses
6
+ # Returned by DeliveryClient.types with an enumerable of ContentTypes
7
+ class DeliveryTypeResponse < ResponseBase
8
+ def type
9
+ @type unless @type.nil?
10
+ @type = Delivery::ContentType.new(@response)
11
+ end
12
+
13
+ def initialize(response)
14
+ @response = response
15
+ super 200, "Success, type '#{type.system.codename}' returned"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,18 @@
1
+ module Delivery
2
+ module Responses
3
+ # Base class for all responses from DeliveryQuery.execute
4
+ class ResponseBase
5
+ attr_accessor :http_code,
6
+ :message
7
+
8
+ def initialize(http_code, message)
9
+ self.http_code = http_code
10
+ self.message = message
11
+ end
12
+
13
+ def to_s
14
+ "Response is status code #{http_code} with message:\n#{message}"
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ module Delivery
2
+ VERSION = '0.10.0'.freeze
3
+ end
@@ -0,0 +1,10 @@
1
+ require File.dirname(__FILE__) + '/delivery/client/delivery_client'
2
+ require File.dirname(__FILE__) + '/delivery/client/delivery_query'
3
+ require File.dirname(__FILE__) + '/delivery/models/content_item'
4
+ require File.dirname(__FILE__) + '/delivery/models/content_type'
5
+ require File.dirname(__FILE__) + '/delivery/query_parameters/filters'
6
+ require File.dirname(__FILE__) + '/delivery/responses/delivery_item_listing_response'
7
+ require File.dirname(__FILE__) + '/delivery/responses/delivery_item_response'
8
+ require File.dirname(__FILE__) + '/delivery/responses/delivery_type_listing_response'
9
+ require File.dirname(__FILE__) + '/delivery/responses/delivery_type_response'
10
+ require File.dirname(__FILE__) + '/delivery/resolvers/content_link_resolver'
metadata ADDED
@@ -0,0 +1,138 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: delivery-sdk-ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.10.0
5
+ platform: ruby
6
+ authors:
7
+ - Eric Dugre
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-02-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nokogiri
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.10.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.10.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: rest-client
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 2.0.2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 2.0.2
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ description: Kentico Cloud Delivery SDK for Ruby
84
+ email:
85
+ - EricD@kentico.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - LICENSE.md
91
+ - README.md
92
+ - bin/console
93
+ - bin/setup
94
+ - lib/delivery-sdk-ruby.rb
95
+ - lib/delivery/client/delivery_client.rb
96
+ - lib/delivery/client/delivery_query.rb
97
+ - lib/delivery/client/url_provider.rb
98
+ - lib/delivery/models/content_item.rb
99
+ - lib/delivery/models/content_type.rb
100
+ - lib/delivery/models/pagination.rb
101
+ - lib/delivery/query_parameters/filters.rb
102
+ - lib/delivery/query_parameters/parameter_base.rb
103
+ - lib/delivery/resolvers/content_link_resolver.rb
104
+ - lib/delivery/responses/delivery_item_listing_response.rb
105
+ - lib/delivery/responses/delivery_item_response.rb
106
+ - lib/delivery/responses/delivery_type_listing_response.rb
107
+ - lib/delivery/responses/delivery_type_response.rb
108
+ - lib/delivery/responses/response_base.rb
109
+ - lib/delivery/version.rb
110
+ homepage: https://github.com/Kentico/delivery-sdk-ruby
111
+ licenses:
112
+ - MIT
113
+ metadata:
114
+ allowed_push_host: https://rubygems.org
115
+ homepage_uri: https://github.com/Kentico/delivery-sdk-ruby
116
+ source_code_uri: https://github.com/Kentico/delivery-sdk-ruby
117
+ changelog_uri: https://github.com/Kentico/delivery-sdk-ruby
118
+ post_install_message:
119
+ rdoc_options: []
120
+ require_paths:
121
+ - lib
122
+ required_ruby_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ required_rubygems_version: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ requirements: []
133
+ rubyforge_project:
134
+ rubygems_version: 2.7.6
135
+ signing_key:
136
+ specification_version: 4
137
+ summary: Kentico Cloud Delivery SDK for Ruby
138
+ test_files: []