contentful-management 1.6.0 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -0
  3. data/.rubocop_todo.yml +3 -3
  4. data/.travis.yml +4 -3
  5. data/CHANGELOG.md +5 -0
  6. data/Gemfile +0 -8
  7. data/Guardfile +3 -54
  8. data/README.md +45 -0
  9. data/Rakefile +0 -5
  10. data/contentful-management.gemspec +8 -5
  11. data/lib/contentful/management/client.rb +44 -14
  12. data/lib/contentful/management/client_upload_methods_factory.rb +19 -0
  13. data/lib/contentful/management/content_type.rb +19 -19
  14. data/lib/contentful/management/dynamic_entry.rb +1 -1
  15. data/lib/contentful/management/entry.rb +57 -50
  16. data/lib/contentful/management/http_client.rb +7 -1
  17. data/lib/contentful/management/request.rb +14 -9
  18. data/lib/contentful/management/resource.rb +7 -7
  19. data/lib/contentful/management/resource/array_like.rb +2 -2
  20. data/lib/contentful/management/resource/system_properties.rb +2 -1
  21. data/lib/contentful/management/resource_builder.rb +6 -3
  22. data/lib/contentful/management/resource_requester.rb +3 -3
  23. data/lib/contentful/management/support.rb +1 -1
  24. data/lib/contentful/management/upload.rb +42 -0
  25. data/lib/contentful/management/version.rb +1 -1
  26. data/spec/fixtures/pixel.jpg +0 -0
  27. data/spec/fixtures/vcr_cassettes/upload/associate_with_asset.yml +426 -0
  28. data/spec/fixtures/vcr_cassettes/upload/create_file.yml +85 -0
  29. data/spec/fixtures/vcr_cassettes/upload/create_path.yml +85 -0
  30. data/spec/fixtures/vcr_cassettes/upload/destroy.yml +133 -0
  31. data/spec/fixtures/vcr_cassettes/upload/find.yml +82 -0
  32. data/spec/fixtures/vcr_cassettes/upload/find_not_found.yml +66 -0
  33. data/spec/lib/contentful/management/client_spec.rb +25 -0
  34. data/spec/lib/contentful/management/upload_spec.rb +92 -0
  35. data/spec/spec_helper.rb +9 -1
  36. metadata +91 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 59a0b72970e30e8e4fa850361c2ed6c52603e6b0
4
- data.tar.gz: b1f2d446a426a052a8ca164e1f9ca3a9b15f8212
3
+ metadata.gz: c880b5d6e4f5f9b5cf11599a5177de92c8cfb673
4
+ data.tar.gz: 57ed6666b81a6767b4c7d5b1b315d2bf64f3e7e2
5
5
  SHA512:
6
- metadata.gz: d245df3cd7ab483f306fd8fb7823cfbe41bb3b93c4fa1798c8833ac6fcfdbbba4eb62b72c0ef5be78bcc963528ac0983436594c79005309262d83340eca1a199
7
- data.tar.gz: 03b149bc58082a045ccda45820f07c590616a50c96ce3af8777ada574f6ab7f453b57f8197b754f687ea60f679f7c162020f5973b064ad783b47fa157ca42a3b
6
+ metadata.gz: e590866845d60f709bfd6b525f2a7a4ef779373e78e57881e375237f52ea54e3b66a52d221052cab7a572c3a609127cfbb8aa41a0cd3cddaca9a01e447dce278
7
+ data.tar.gz: f115b17339749173bdbd4c883b8d3cc0d99ceaa398f3b30639959a31ebcfc5751d01a9b61198deae79f5fd019d2c91b883836a2f06f95f8895ec1c4bc2c437af
data/.rubocop.yml CHANGED
@@ -21,3 +21,6 @@ Metrics/LineLength:
21
21
 
22
22
  Metrics/ClassLength:
23
23
  Max: 250
24
+
25
+ Style/SignalException:
26
+ EnforcedStyle: 'semantic'
data/.rubocop_todo.yml CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  # Offense count: 12
10
10
  Metrics/AbcSize:
11
- Max: 41
11
+ Max: 45
12
12
 
13
13
  # Offense count: 4
14
14
  Metrics/CyclomaticComplexity:
@@ -17,8 +17,8 @@ Metrics/CyclomaticComplexity:
17
17
  # Offense count: 18
18
18
  # Configuration parameters: CountComments.
19
19
  Metrics/MethodLength:
20
- Max: 24
20
+ Max: 25
21
21
 
22
22
  # Offense count: 2
23
23
  Metrics/PerceivedComplexity:
24
- Max: 9
24
+ Max: 10
data/.travis.yml CHANGED
@@ -1,13 +1,14 @@
1
1
  language: ruby
2
2
  rvm:
3
3
  - 2.2.3
4
+ - 2.3.1
5
+ - 2.4.0
4
6
  - 2.1.1
5
7
  - 2.1
6
8
  - 2.0.0
7
- - 1.9.3
8
- - jruby-19mode
9
+ - jruby-head
9
10
 
10
- before_install: gem install bundler -v 1.10.6
11
+ before_install: gem install bundler
11
12
  script: bundle exec rake rspec_rubocop
12
13
  notifications:
13
14
  slack:
data/CHANGELOG.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  ## Master
4
4
 
5
+ ## 1.7.0
6
+
7
+ ### Added
8
+ * Added support for Upload API
9
+
5
10
  ## 1.6.0
6
11
 
7
12
  ### Added
data/Gemfile CHANGED
@@ -4,13 +4,5 @@ source 'https://rubygems.org'
4
4
  gemspec
5
5
 
6
6
  group :development do
7
- gem 'guard'
8
- gem 'listen', '3.0.0'
9
- gem 'guard-rspec'
10
- gem 'guard-rubocop'
11
7
  gem 'pry'
12
8
  end
13
-
14
- group :test do
15
- gem 'simplecov', require: false
16
- end
data/Guardfile CHANGED
@@ -1,29 +1,3 @@
1
- # A sample Guardfile
2
- # More info at https://github.com/guard/guard#readme
3
-
4
- ## Uncomment and set this to only include directories you want to watch
5
- # directories %w(app lib config test spec features) \
6
- # .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
7
-
8
- ## Note: if you are using the `directories` clause above and you are not
9
- ## watching the project directory ('.'), then you will want to move
10
- ## the Guardfile to a watched dir and symlink it back, e.g.
11
- #
12
- # $ mkdir config
13
- # $ mv Guardfile config/
14
- # $ ln -s config/Guardfile .
15
- #
16
- # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
17
-
18
- # Note: The cmd option is now required due to the increasing number of ways
19
- # rspec may be run, below are examples of the most common uses.
20
- # * bundler: 'bundle exec rspec'
21
- # * bundler binstubs: 'bin/rspec'
22
- # * spring: 'bin/rspec' (This will use spring if running and you have
23
- # installed the spring binstubs per the docs)
24
- # * zeus: 'zeus rspec' (requires the server to be started separately)
25
- # * 'just' rspec: 'rspec'
26
-
27
1
  group :green_red_refactor, halt_on_fail: true do
28
2
  guard :rspec, cmd: "bundle exec rspec --format documentation" do
29
3
  require "guard/rspec/dsl"
@@ -40,36 +14,11 @@ group :green_red_refactor, halt_on_fail: true do
40
14
  # Ruby files
41
15
  ruby = dsl.ruby
42
16
  dsl.watch_spec_files_for(ruby.lib_files)
43
-
44
- # Rails files
45
- rails = dsl.rails(view_extensions: %w(erb haml slim))
46
- dsl.watch_spec_files_for(rails.app_files)
47
- dsl.watch_spec_files_for(rails.views)
48
-
49
- watch(rails.controllers) do |m|
50
- [
51
- rspec.spec.("routing/#{m[1]}_routing"),
52
- rspec.spec.("controllers/#{m[1]}_controller"),
53
- rspec.spec.("acceptance/#{m[1]}")
54
- ]
55
- end
56
-
57
- # Rails config changes
58
- watch(rails.spec_helper) { rspec.spec_dir }
59
- watch(rails.routes) { "#{rspec.spec_dir}/routing" }
60
- watch(rails.app_controller) { "#{rspec.spec_dir}/controllers" }
61
-
62
- # Capybara features specs
63
- watch(rails.view_dirs) { |m| rspec.spec.("features/#{m[1]}") }
64
- watch(rails.layouts) { |m| rspec.spec.("features/#{m[1]}") }
65
-
66
- # Turnip features and steps
67
- watch(%r{^spec/acceptance/(.+)\.feature$})
68
- watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) do |m|
69
- Dir[File.join("**/#{m[1]}.feature")][0] || "spec/acceptance"
70
- end
71
17
  end
72
18
 
73
19
  guard :rubocop, cmd: "rubocop" do
74
20
  end
21
+
22
+ guard :yard, cmd: "yard doc" do
23
+ end
75
24
  end
data/README.md CHANGED
@@ -196,6 +196,51 @@ Checking if an asset is published:
196
196
  my_image_asset.published?
197
197
  ```
198
198
 
199
+ ### File Uploads
200
+
201
+ Creating an upload from a file path:
202
+
203
+ ```ruby
204
+ upload = client.uploads.create('space_id', '/path/to/file.md')
205
+ ```
206
+
207
+ Alternatively, create it from an `::IO` object:
208
+
209
+ ```ruby
210
+ File.open('/path/to/file.md', 'rb') do |file|
211
+ upload = client.uploads.create('space_id', file)
212
+ end
213
+ ```
214
+
215
+ Finding an upload:
216
+
217
+ ```ruby
218
+ upload = client.uploads.find('space_id', 'upload_id')
219
+ ```
220
+
221
+ Deleting an upload
222
+
223
+ ```ruby
224
+ upload.destroy
225
+ ```
226
+
227
+ Associating an upload with an asset:
228
+
229
+ ```ruby
230
+ # We find or create an upload:
231
+ upload = client.uploads.find('space_id', 'upload_id')
232
+
233
+ # We create a File object with the associated upload:
234
+ file = Contentful::Management::File.new
235
+ file.properties[:contentType] = 'text/plain'
236
+ file.properties[:fileName] = 'file.md'
237
+ file.properties[:uploadFrom] = upload.to_link_json # We create the Link from the upload.
238
+
239
+ # We create an asset with the associated file:
240
+ asset = client.assets.create('space_id', title: 'My Upload', file: file)
241
+ asset.process_file # We process the file, to generate an URL for our upload.
242
+ ```
243
+
199
244
  ### Entries
200
245
 
201
246
  Retrieving all entries from the space:
data/Rakefile CHANGED
@@ -24,11 +24,6 @@ RSpec::Core::RakeTask.new
24
24
  require 'rubocop/rake_task'
25
25
  RuboCop::RakeTask.new
26
26
 
27
- require 'reek/rake/task'
28
- Reek::Rake::Task.new do |t|
29
- t.fail_on_error = false
30
- end
31
-
32
27
  desc 'Run specs, rubocop and reek'
33
28
  task ci: %w(spec reek rubocop)
34
29
 
@@ -17,19 +17,22 @@ Gem::Specification.new do |spec|
17
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
18
  spec.require_paths = ['lib']
19
19
 
20
- spec.add_dependency 'http', '~> 1.0'
20
+ spec.add_dependency 'http', '> 1.0', '< 3.0'
21
21
  spec.add_dependency 'multi_json', '~> 1'
22
22
  spec.add_dependency 'json', '~> 1.8'
23
23
 
24
24
  spec.add_development_dependency 'bundler', '~> 1.6'
25
25
  spec.add_development_dependency 'rake', '< 11.0'
26
26
  spec.add_development_dependency 'public_suffix', '< 1.5'
27
-
28
27
  spec.add_development_dependency 'rspec', '~> 3'
29
28
  spec.add_development_dependency 'rspec-its'
30
- spec.add_development_dependency 'rubocop'
31
- spec.add_development_dependency 'reek', '~> 2.2.1'
32
- spec.add_development_dependency 'unparser', '0.2.4'
29
+ spec.add_development_dependency 'guard'
30
+ spec.add_development_dependency 'guard-rspec'
31
+ spec.add_development_dependency 'guard-rubocop'
32
+ spec.add_development_dependency 'guard-yard'
33
+ spec.add_development_dependency 'rubocop', '~> 0.41.0'
34
+ spec.add_development_dependency 'listen', '3.0.0'
33
35
  spec.add_development_dependency 'vcr'
34
36
  spec.add_development_dependency 'webmock', '~> 1', '>= 1.17.3'
37
+ spec.add_development_dependency 'simplecov'
35
38
  end
@@ -14,6 +14,7 @@ require 'contentful/management/client_locale_methods_factory'
14
14
  require 'contentful/management/client_role_methods_factory'
15
15
  require 'contentful/management/client_editor_interface_methods_factory'
16
16
  require 'contentful/management/client_webhook_methods_factory'
17
+ require 'contentful/management/client_upload_methods_factory'
17
18
  require 'contentful/management/client_snapshot_methods_factory'
18
19
 
19
20
  require_relative 'request'
@@ -34,6 +35,7 @@ module Contentful
34
35
  # Default configuration for Contentful::Management::Client
35
36
  DEFAULT_CONFIGURATION = {
36
37
  api_url: 'api.contentful.com',
38
+ uploads_url: 'upload.contentful.com',
37
39
  api_version: '1',
38
40
  secure: true,
39
41
  default_locale: 'en-US',
@@ -48,9 +50,10 @@ module Contentful
48
50
  proxy_password: nil,
49
51
  max_rate_limit_retries: 1,
50
52
  max_rate_limit_wait: 60
51
- }
53
+ }.freeze
54
+
52
55
  # Rate Limit Reset Header Key
53
- RATE_LIMIT_RESET_HEADER_KEY = 'x-contentful-ratelimit-reset'
56
+ RATE_LIMIT_RESET_HEADER_KEY = 'x-contentful-ratelimit-reset'.freeze
54
57
 
55
58
  # @param [String] access_token
56
59
  # @param [Hash] configuration
@@ -156,6 +159,15 @@ module Contentful
156
159
  ClientWebhookMethodsFactory.new(self)
157
160
  end
158
161
 
162
+ # Allows manipulation of uploads in context of the current client
163
+ # Allows creating new and finding uploads by id.
164
+ # @see _ README for details.
165
+ #
166
+ # @return [Contentful::Management::ClientUploadMethodsFactory]
167
+ def uploads
168
+ ClientUploadMethodsFactory.new(self)
169
+ end
170
+
159
171
  # Allows manipulation of snapshots in context of the current client
160
172
  # Allows listing all webhooks for client, creating new and finding one by id.
161
173
  # @see _ README for details.
@@ -223,11 +235,11 @@ module Contentful
223
235
  # @private
224
236
  def execute_request(request)
225
237
  retries_left = configuration[:max_rate_limit_retries]
226
- request_url = request.url
227
- url = request.absolute? ? request_url : base_url + request_url
238
+ host = host_url(request)
239
+ url = request.absolute? ? request.url : host + request.url
228
240
 
229
241
  begin
230
- logger.info(request: { url: url, query: request.query, header: request_headers }) if logger
242
+ logger.info(request: { url: url, query: request.query, header: request_headers(request) }) if logger
231
243
  raw_response = yield(url)
232
244
  logger.debug(response: raw_response) if logger
233
245
  clear_headers
@@ -237,10 +249,7 @@ module Contentful
237
249
  reset_time = rate_limit_error.response.raw[RATE_LIMIT_RESET_HEADER_KEY].to_i
238
250
  if should_retry(retries_left, reset_time, configuration[:max_rate_limit_wait])
239
251
  retries_left -= 1
240
- retry_message = 'Contentful Management API Rate Limit Hit! '
241
- retry_message += "Retrying - Retries left: #{retries_left}"
242
- retry_message += "- Time until reset (seconds): #{reset_time}"
243
- logger.info(retry_message) if logger
252
+ logger.info(retry_message(retries_left, reset_time)) if logger
244
253
  sleep(reset_time * Random.new.rand(1.0..1.2))
245
254
  retry
246
255
  end
@@ -251,6 +260,20 @@ module Contentful
251
260
  result
252
261
  end
253
262
 
263
+ # @private
264
+ def host_url(request)
265
+ (%r{^/[\w|-]+/uploads(?:/[\w|-]*)?$} =~ request.url) ? uploads_url : base_url
266
+ end
267
+
268
+ # @private
269
+ def retry_message(retries_left, reset_time)
270
+ retry_message = 'Contentful Management API Rate Limit Hit! '
271
+ retry_message += "Retrying - Retries left: #{retries_left}"
272
+ retry_message += "- Time until reset (seconds): #{reset_time}"
273
+
274
+ retry_message
275
+ end
276
+
254
277
  # @private
255
278
  def should_retry(retries_left, reset_time, max_wait)
256
279
  retries_left > 0 && max_wait > reset_time
@@ -266,28 +289,28 @@ module Contentful
266
289
  # @private
267
290
  def delete(request)
268
291
  execute_request(request) do |url|
269
- self.class.delete_http(url, {}, request_headers, proxy_parameters)
292
+ self.class.delete_http(url, {}, request_headers(request), proxy_parameters)
270
293
  end
271
294
  end
272
295
 
273
296
  # @private
274
297
  def get(request)
275
298
  execute_request(request) do |url|
276
- self.class.get_http(url, request.query, request_headers, proxy_parameters)
299
+ self.class.get_http(url, request.query, request_headers(request), proxy_parameters)
277
300
  end
278
301
  end
279
302
 
280
303
  # @private
281
304
  def post(request)
282
305
  execute_request(request) do |url|
283
- self.class.post_http(url, request.query, request_headers, proxy_parameters)
306
+ self.class.post_http(url, request.query, request_headers(request), proxy_parameters)
284
307
  end
285
308
  end
286
309
 
287
310
  # @private
288
311
  def put(request)
289
312
  execute_request(request) do |url|
290
- self.class.put_http(url, request.query, request_headers, proxy_parameters)
313
+ self.class.put_http(url, request.query, request_headers(request), proxy_parameters)
291
314
  end
292
315
  end
293
316
 
@@ -296,6 +319,11 @@ module Contentful
296
319
  "#{protocol}://#{configuration[:api_url]}/spaces"
297
320
  end
298
321
 
322
+ # @private
323
+ def uploads_url
324
+ "#{protocol}://#{configuration[:uploads_url]}/spaces"
325
+ end
326
+
299
327
  # @private
300
328
  def default_locale
301
329
  configuration[:default_locale]
@@ -348,7 +376,7 @@ module Contentful
348
376
 
349
377
  # @todo headers should be supplied differently, maybe through the request object.
350
378
  # @private
351
- def request_headers
379
+ def request_headers(request = nil)
352
380
  headers = {}
353
381
  headers.merge! user_agent
354
382
  headers.merge! authentication_header
@@ -358,6 +386,8 @@ module Contentful
358
386
  headers.merge! zero_length_header if zero_length
359
387
  headers.merge! content_type_header(content_type_id) if content_type_id
360
388
  headers.merge! accept_encoding_header('gzip') if gzip_encoded
389
+
390
+ headers.merge! request.headers unless request.nil?
361
391
  headers
362
392
  end
363
393
 
@@ -0,0 +1,19 @@
1
+ require_relative 'client_association_methods_factory'
2
+
3
+ module Contentful
4
+ module Management
5
+ # Wrapper for Upload API for usage from within Client
6
+ # @private
7
+ class ClientUploadMethodsFactory
8
+ include Contentful::Management::ClientAssociationMethodsFactory
9
+
10
+ def new(*)
11
+ fail 'Not supported'
12
+ end
13
+
14
+ def all(*)
15
+ fail 'Not supported'
16
+ end
17
+ end
18
+ end
19
+ end
@@ -14,17 +14,17 @@ module Contentful
14
14
  class ContentType
15
15
  # Shortcuts for Contentful Field Types
16
16
  FIELD_TYPES = [
17
- SYMBOL = 'Symbol',
18
- TEXT = 'Text',
19
- INTEGER = 'Integer',
20
- FLOAT = 'Number',
21
- DATE = 'Date',
22
- BOOLEAN = 'Boolean',
23
- LINK = 'Link',
24
- ARRAY = 'Array',
25
- OBJECT = 'Object',
26
- LOCATION = 'Location'
27
- ]
17
+ SYMBOL = 'Symbol'.freeze,
18
+ TEXT = 'Text'.freeze,
19
+ INTEGER = 'Integer'.freeze,
20
+ FLOAT = 'Number'.freeze,
21
+ DATE = 'Date'.freeze,
22
+ BOOLEAN = 'Boolean'.freeze,
23
+ LINK = 'Link'.freeze,
24
+ ARRAY = 'Array'.freeze,
25
+ OBJECT = 'Object'.freeze,
26
+ LOCATION = 'Location'.freeze
27
+ ].freeze
28
28
 
29
29
  include Contentful::Management::Resource
30
30
  include Contentful::Management::Resource::SystemProperties
@@ -42,9 +42,9 @@ module Contentful
42
42
  ClientContentTypeMethodsFactory
43
43
  end
44
44
 
45
- alias_method :activate, :publish
46
- alias_method :deactivate, :unpublish
47
- alias_method :active?, :published?
45
+ alias activate publish
46
+ alias deactivate unpublish
47
+ alias active? published?
48
48
 
49
49
  # @private
50
50
  def self.create_attributes(_client, attributes)
@@ -97,7 +97,7 @@ module Contentful
97
97
  end
98
98
 
99
99
  # @private
100
- alias_method :orig_fields, :fields
100
+ alias orig_fields fields
101
101
 
102
102
  # Use this method only in the context of content type.
103
103
  # Allows you to add and create a field with specified attributes or destroy by pass field id.
@@ -164,10 +164,10 @@ module Contentful
164
164
 
165
165
  def query_attributes(attributes)
166
166
  parameters = {}
167
- parameters.merge!(displayField: display_field_value(attributes))
168
- parameters.merge!(name: (attributes[:name] || name))
169
- parameters.merge!(description: (attributes[:description] || description))
170
- parameters.merge!(fields: self.class.fields_to_nested_properties_hash(attributes[:fields] || fields))
167
+ parameters[:displayField] = display_field_value(attributes)
168
+ parameters[:name] = attributes[:name] || name
169
+ parameters[:description] = attributes[:description] || description
170
+ parameters[:fields] = self.class.fields_to_nested_properties_hash(attributes[:fields] || fields)
171
171
 
172
172
  parameters.delete_if { |_, v| v.nil? }
173
173
  end