contentful-management 0.2.1 → 0.3.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 (59) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +37 -8
  3. data/Gemfile +0 -1
  4. data/README.md +130 -0
  5. data/Rakefile +0 -1
  6. data/contentful-management.gemspec +0 -1
  7. data/examples/blog.rb +1 -1
  8. data/lib/contentful/management.rb +0 -1
  9. data/lib/contentful/management/array.rb +0 -1
  10. data/lib/contentful/management/asset.rb +65 -26
  11. data/lib/contentful/management/client.rb +19 -9
  12. data/lib/contentful/management/content_type.rb +43 -28
  13. data/lib/contentful/management/dynamic_entry.rb +15 -15
  14. data/lib/contentful/management/entry.rb +73 -40
  15. data/lib/contentful/management/error.rb +6 -1
  16. data/lib/contentful/management/field.rb +63 -8
  17. data/lib/contentful/management/file.rb +0 -1
  18. data/lib/contentful/management/http_client.rb +0 -1
  19. data/lib/contentful/management/link.rb +0 -1
  20. data/lib/contentful/management/locale.rb +11 -3
  21. data/lib/contentful/management/location.rb +0 -1
  22. data/lib/contentful/management/request.rb +5 -7
  23. data/lib/contentful/management/resource.rb +3 -3
  24. data/lib/contentful/management/resource/array_like.rb +0 -1
  25. data/lib/contentful/management/resource/fields.rb +1 -2
  26. data/lib/contentful/management/resource/refresher.rb +3 -3
  27. data/lib/contentful/management/resource/system_properties.rb +0 -1
  28. data/lib/contentful/management/resource_builder.rb +16 -27
  29. data/lib/contentful/management/response.rb +36 -32
  30. data/lib/contentful/management/space.rb +16 -6
  31. data/lib/contentful/management/space_webhook_methods_factory.rb +0 -4
  32. data/lib/contentful/management/support.rb +0 -1
  33. data/lib/contentful/management/validation.rb +34 -0
  34. data/lib/contentful/management/version.rb +1 -2
  35. data/lib/contentful/management/webhook.rb +17 -10
  36. data/spec/fixtures/vcr_cassettes/content_type/validation/in.yml +588 -0
  37. data/spec/fixtures/vcr_cassettes/content_type/validation/in_add.yml +620 -0
  38. data/spec/fixtures/vcr_cassettes/content_type/validation/in_update.yml +668 -0
  39. data/spec/fixtures/vcr_cassettes/content_type/validation/link_content_type.yml +749 -0
  40. data/spec/fixtures/vcr_cassettes/content_type/validation/link_field.yml +770 -0
  41. data/spec/fixtures/vcr_cassettes/content_type/validation/link_mimetype_group.yml +766 -0
  42. data/spec/fixtures/vcr_cassettes/content_type/validation/multiple_add.yml +854 -0
  43. data/spec/fixtures/vcr_cassettes/content_type/validation/present.yml +808 -0
  44. data/spec/fixtures/vcr_cassettes/content_type/validation/range.yml +683 -0
  45. data/spec/fixtures/vcr_cassettes/content_type/validation/range_update.yml +697 -0
  46. data/spec/fixtures/vcr_cassettes/content_type/validation/regexp.yml +720 -0
  47. data/spec/fixtures/vcr_cassettes/content_type/validation/size.yml +791 -0
  48. data/spec/fixtures/vcr_cassettes/entry/service_unavailable.yml +52 -0
  49. data/spec/fixtures/vcr_cassettes/space/webhook/create.yml +439 -0
  50. data/spec/lib/contentful/management/asset_spec.rb +16 -16
  51. data/spec/lib/contentful/management/client_spec.rb +0 -1
  52. data/spec/lib/contentful/management/content_type_spec.rb +143 -1
  53. data/spec/lib/contentful/management/entry_spec.rb +10 -4
  54. data/spec/lib/contentful/management/locale_spec.rb +0 -1
  55. data/spec/lib/contentful/management/space_spec.rb +9 -1
  56. data/spec/lib/contentful/management/webhook_spec.rb +0 -1
  57. data/spec/spec_helper.rb +0 -1
  58. data/spec/support/vcr.rb +0 -1
  59. metadata +63 -62
@@ -1,4 +1,3 @@
1
- # -*- encoding: utf-8 -*-
2
1
  require 'contentful/management'
3
2
  require 'contentful/management/response'
4
3
  require 'contentful/management/resource_builder'
@@ -9,30 +8,39 @@ require 'contentful/management/http_client'
9
8
  require_relative 'request'
10
9
  require 'http'
11
10
  require 'json'
11
+ require 'logger'
12
12
 
13
13
  module Contentful
14
14
  module Management
15
15
  class Client
16
16
  extend Contentful::Management::HTTPClient
17
17
 
18
- attr_reader :access_token, :configuration
18
+ attr_reader :access_token, :configuration, :logger
19
19
  attr_accessor :organization_id, :version, :zero_length, :content_type_id, :dynamic_entry_cache
20
20
 
21
21
  DEFAULT_CONFIGURATION = {
22
- api_url: 'api.contentful.com',
23
- api_version: '1',
24
- secure: true,
25
- default_locale: 'en-US',
26
- gzip_encoded: false
22
+ api_url: 'api.contentful.com',
23
+ api_version: '1',
24
+ secure: true,
25
+ default_locale: 'en-US',
26
+ gzip_encoded: false,
27
+ logger: false,
28
+ log_level: Logger::INFO
27
29
  }
28
30
 
29
31
  def initialize(access_token = nil, configuration = {})
30
32
  @configuration = default_configuration.merge(configuration)
33
+ setup_logger
31
34
  @access_token = access_token
32
35
  @dynamic_entry_cache = {}
33
36
  Thread.current[:client] = self
34
37
  end
35
38
 
39
+ def setup_logger
40
+ @logger = configuration[:logger]
41
+ logger.level = configuration[:log_level] if logger
42
+ end
43
+
36
44
  def update_dynamic_entry_cache_for_spaces!(spaces)
37
45
  spaces.each do |space|
38
46
  update_dynamic_entry_cache_for_space!(space)
@@ -49,8 +57,8 @@ module Contentful
49
57
  @dynamic_entry_cache = Hash[
50
58
  content_types.map do |ct|
51
59
  [
52
- ct.id.to_sym,
53
- DynamicEntry.create(ct)
60
+ ct.id.to_sym,
61
+ DynamicEntry.create(ct)
54
62
  ]
55
63
  end
56
64
  ]
@@ -75,7 +83,9 @@ module Contentful
75
83
  def execute_request(request)
76
84
  request_url = request.url
77
85
  url = request.absolute? ? request_url : base_url + request_url
86
+ logger.info(request: {url: url, query: request.query ,header: request_headers}) if logger
78
87
  raw_response = yield(url)
88
+ logger.debug(response: raw_response) if logger
79
89
  clear_headers
80
90
  Response.new(raw_response, request)
81
91
  end
@@ -1,7 +1,8 @@
1
- # -*- encoding: utf-8 -*-
2
1
  require_relative 'resource'
3
2
  require_relative 'field'
3
+ require_relative 'validation'
4
4
  require_relative 'content_type_entry_methods_factory'
5
+ require_relative 'support'
5
6
 
6
7
  module Contentful
7
8
  module Management
@@ -9,16 +10,16 @@ module Contentful
9
10
  # https://www.contentful.com/developers/documentation/content-management-api/#resources-content-types
10
11
  class ContentType
11
12
  FIELD_TYPES = [
12
- SYMBOL = 'Symbol',
13
- TEXT = 'Text',
14
- INTEGER = 'Integer',
15
- FLOAT = 'Number',
16
- DATE = 'Date',
17
- BOOLEAN = 'Boolean',
18
- LINK = 'Link',
19
- ARRAY = 'Array',
20
- OBJECT = 'Object',
21
- LOCATION = 'Location'
13
+ SYMBOL = 'Symbol',
14
+ TEXT = 'Text',
15
+ INTEGER = 'Integer',
16
+ FLOAT = 'Number',
17
+ DATE = 'Date',
18
+ BOOLEAN = 'Boolean',
19
+ LINK = 'Link',
20
+ ARRAY = 'Array',
21
+ OBJECT = 'Object',
22
+ LOCATION = 'Location'
22
23
  ]
23
24
 
24
25
  include Contentful::Management::Resource
@@ -33,7 +34,10 @@ module Contentful
33
34
  # Takes an id of space and an optional hash of query options
34
35
  # Returns a Contentful::Management::Array of Contentful::Management::ContentType.
35
36
  def self.all(space_id, query = {})
36
- request = Request.new("/#{ space_id }/content_types", query)
37
+ request = Request.new(
38
+ "/#{ space_id }/content_types",
39
+ query
40
+ )
37
41
  response = request.get
38
42
  result = ResourceBuilder.new(response, {}, {})
39
43
  content_types = result.run
@@ -69,7 +73,12 @@ module Contentful
69
73
  # Activates a content type.
70
74
  # Returns a Contentful::Management::ContentType.
71
75
  def activate
72
- request = Request.new("/#{ space.id }/content_types/#{ id }/published", {}, id = nil, version: sys[:version])
76
+ request = Request.new(
77
+ "/#{ space.id }/content_types/#{ id }/published",
78
+ {},
79
+ id = nil,
80
+ version: sys[:version]
81
+ )
73
82
  response = request.put
74
83
  result = ResourceBuilder.new(response, {}, {}).run
75
84
  refresh_data(result)
@@ -88,7 +97,7 @@ module Contentful
88
97
  # Checks if a content type is active.
89
98
  # Returns true if active.
90
99
  def active?
91
- !sys[:publishedAt].nil?
100
+ sys[:publishedAt] ? true : false
92
101
  end
93
102
 
94
103
  # Creates a content type.
@@ -96,9 +105,12 @@ module Contentful
96
105
  # Returns a Contentful::Management::ContentType.
97
106
  def self.create(space_id, attributes)
98
107
  fields = fields_to_nested_properties_hash(attributes[:fields] || [])
99
- request = Request.new("/#{ space_id }/content_types/#{ attributes[:id] || ''}", name: attributes.fetch(:name),
100
- description: attributes[:description],
101
- fields: fields)
108
+ request = Request.new(
109
+ "/#{ space_id }/content_types/#{ attributes[:id]}",
110
+ name: attributes.fetch(:name),
111
+ description: attributes[:description],
112
+ fields: fields
113
+ )
102
114
  response = attributes[:id].nil? ? request.post : request.put
103
115
  result = ResourceBuilder.new(response, {}, {}).run
104
116
  client.register_dynamic_entry(result.id, DynamicEntry.create(result)) if result.is_a?(self.class)
@@ -114,7 +126,12 @@ module Contentful
114
126
  parameters.merge!(name: (attributes[:name] || name))
115
127
  parameters.merge!(description: (attributes[:description] || description))
116
128
  parameters.merge!(fields: self.class.fields_to_nested_properties_hash(attributes[:fields] || fields))
117
- request = Request.new("/#{ space.id }/content_types/#{ id }", parameters, id = nil, version: sys[:version])
129
+ request = Request.new(
130
+ "/#{ space.id }/content_types/#{ id }",
131
+ parameters,
132
+ id = nil,
133
+ version: sys[:version]
134
+ )
118
135
  response = request.put
119
136
  result = ResourceBuilder.new(response, {}, {}).run
120
137
  refresh_data(result)
@@ -123,11 +140,11 @@ module Contentful
123
140
  # If a content type is a new object gets created in the Contentful, otherwise the existing entry gets updated.
124
141
  # See README for details.
125
142
  def save
126
- if id.nil?
143
+ if id
144
+ update(@properties)
145
+ else
127
146
  new_instance = self.class.create(space.id, @properties)
128
147
  refresh_data(new_instance)
129
- else
130
- update(@properties)
131
148
  end
132
149
  end
133
150
 
@@ -160,13 +177,11 @@ module Contentful
160
177
 
161
178
  fields.define_singleton_method(:create) do |params|
162
179
  field = Contentful::Management::Field.new
163
- field.id = params.fetch(:id)
164
- field.name = params[:name] if params[:name]
165
- field.type = params[:type] if params[:type]
166
- field.link_type = params[:link_type] if params[:link_type]
167
- field.required = params[:required] if params[:required]
168
- field.localized = params[:localized] if params[:localized]
169
- field.items = params[:items] if params[:items]
180
+ Field.property_coercions.each do |key, _value|
181
+ snakify_key = Support.snakify(key)
182
+ param = params[snakify_key.to_sym]
183
+ field.send("#{snakify_key}=", param) if param
184
+ end
170
185
  content_type.update(fields: content_type.merged_fields(field))
171
186
  end
172
187
 
@@ -1,4 +1,3 @@
1
- # -*- encoding: utf-8 -*-
2
1
  require_relative 'resource'
3
2
  require_relative 'resource/fields'
4
3
  require_relative 'location'
@@ -7,14 +6,14 @@ module Contentful
7
6
  module Management
8
7
  class DynamicEntry < Contentful::Management::Entry
9
8
  KNOWN_TYPES = {
10
- 'String' => :string,
11
- 'Text' => :string,
12
- 'Symbol' => :string,
13
- 'Integer' => :integer,
14
- 'Float' => :float,
15
- 'Boolean' => :boolean,
16
- 'Date' => :date,
17
- 'Location' => Location
9
+ 'String' => :string,
10
+ 'Text' => :string,
11
+ 'Symbol' => :string,
12
+ 'Integer' => :integer,
13
+ 'Float' => :float,
14
+ 'Boolean' => :boolean,
15
+ 'Date' => :date,
16
+ 'Location' => Location
18
17
  }
19
18
 
20
19
  def self.create(content_type)
@@ -22,7 +21,6 @@ module Contentful
22
21
  content_type = ContentType.new(content_type)
23
22
  end
24
23
 
25
- # TODO no support for an empty content type (undefined method map for Field)
26
24
  fields_coercions = Hash[
27
25
  content_type.fields.map do |field|
28
26
  [field.id.to_sym, KNOWN_TYPES[field.type]]
@@ -39,14 +37,12 @@ module Contentful
39
37
  fields_for_query[field.id.to_sym]
40
38
  end
41
39
  define_method "#{ accessor_name }=" do |value|
42
- if field.localized || default_locale == locale
43
- fields[field.id.to_sym] = value
44
- end
40
+ fields[field.id.to_sym] = value if localized_or_default_locale(field, locale)
45
41
  end
46
42
  define_method "#{ accessor_name }_with_locales=" do |values|
47
43
  values.each do |locale, value|
48
- if field.localized || default_locale == locale
49
- @fields[locale] = {} if @fields[locale].nil?
44
+ if localized_or_default_locale(field, locale)
45
+ @fields[locale] = {} unless @fields[locale]
50
46
  @fields[locale][field.id.to_sym] = value
51
47
  end
52
48
  end
@@ -70,6 +66,10 @@ module Contentful
70
66
  end
71
67
  end
72
68
  end
69
+
70
+ def localized_or_default_locale(field, locale)
71
+ field.localized || default_locale == locale
72
+ end
73
73
  end
74
74
  end
75
75
  end
@@ -1,4 +1,3 @@
1
- # -*- encoding: utf-8 -*-
2
1
  require_relative 'resource'
3
2
  require_relative 'resource/entry_fields'
4
3
  require_relative 'resource/fields'
@@ -20,7 +19,10 @@ module Contentful
20
19
  # Takes an id of space and hash of parameters with optional content_type_id.
21
20
  # Returns a Contentful::Management::Array of Contentful::Management::Entry.
22
21
  def self.all(space_id, parameters = {})
23
- request = Request.new("/#{ space_id }/entries", parameters)
22
+ request = Request.new(
23
+ "/#{ space_id }/entries",
24
+ parameters
25
+ )
24
26
  response = request.get
25
27
  result = ResourceBuilder.new(response, {}, {})
26
28
  result.run
@@ -40,7 +42,7 @@ module Contentful
40
42
  # Takes a content type object and hash with attributes of content type.
41
43
  # Returns a Contentful::Management::Entry.
42
44
  def self.create(content_type, attributes)
43
- custom_id = attributes[:id] || ''
45
+ custom_id = attributes[:id]
44
46
  locale = attributes[:locale]
45
47
  fields_for_create = if attributes[:fields] # create from initialized dynamic entry via save
46
48
  tmp_entry = new
@@ -50,8 +52,13 @@ module Contentful
50
52
  fields_with_locale content_type, attributes
51
53
  end
52
54
 
53
- request = Request.new("/#{ content_type.sys[:space].id }/entries/#{ custom_id }", {fields: fields_for_create}, nil, content_type_id: content_type.id)
54
- response = custom_id.empty? ? request.post : request.put
55
+ request = Request.new(
56
+ "/#{ content_type.sys[:space].id }/entries/#{ custom_id }",
57
+ {fields: fields_for_create},
58
+ nil,
59
+ content_type_id: content_type.id
60
+ )
61
+ response = custom_id.nil? ? request.post : request.put
55
62
  result = ResourceBuilder.new(response, {}, {})
56
63
  client.register_dynamic_entry(content_type.id, DynamicEntry.create(content_type))
57
64
  entry = result.run
@@ -65,7 +72,12 @@ module Contentful
65
72
  def update(attributes)
66
73
  fields_for_update = Contentful::Management::Support.deep_hash_merge(fields_for_query, fields_from_attributes(attributes))
67
74
 
68
- request = Request.new("/#{ space.id }/entries/#{ id }", {fields: fields_for_update}, id = nil, version: sys[:version])
75
+ request = Request.new(
76
+ "/#{ space.id }/entries/#{ id }",
77
+ {fields: fields_for_update},
78
+ id = nil,
79
+ version: sys[:version]
80
+ )
69
81
  response = request.put
70
82
  result = ResourceBuilder.new(response, {}, {}).run
71
83
  refresh_data(result)
@@ -74,18 +86,23 @@ module Contentful
74
86
  # If an entry is a new object gets created in the Contentful, otherwise the existing entry gets updated.
75
87
  # See README for details.
76
88
  def save
77
- if id.nil?
89
+ if id
90
+ update({})
91
+ else
78
92
  new_instance = Contentful::Management::Entry.create(content_type, fields: instance_variable_get(:@fields))
79
93
  refresh_data(new_instance)
80
- else
81
- update({})
82
94
  end
83
95
  end
84
96
 
85
97
  # Publishes an entry.
86
98
  # Returns a Contentful::Management::Entry.
87
99
  def publish
88
- request = Request.new("/#{ space.id }/entries/#{ id }/published", {}, id = nil, version: sys[:version])
100
+ request = Request.new(
101
+ "/#{ space.id }/entries/#{ id }/published",
102
+ {},
103
+ id = nil,
104
+ version: sys[:version]
105
+ )
89
106
  response = request.put
90
107
  result = ResourceBuilder.new(response, {}, {}).run
91
108
  refresh_data(result)
@@ -94,7 +111,12 @@ module Contentful
94
111
  # Unpublishes an entry.
95
112
  # Returns a Contentful::Management::Entry.
96
113
  def unpublish
97
- request = Request.new("/#{ space.id }/entries/#{ id }/published", {}, id = nil, version: sys[:version])
114
+ request = Request.new(
115
+ "/#{ space.id }/entries/#{ id }/published",
116
+ {},
117
+ id = nil,
118
+ version: sys[:version]
119
+ )
98
120
  response = request.delete
99
121
  result = ResourceBuilder.new(response, {}, {}).run
100
122
  refresh_data(result)
@@ -103,7 +125,12 @@ module Contentful
103
125
  # Archives an entry.
104
126
  # Returns a Contentful::Management::Entry.
105
127
  def archive
106
- request = Request.new("/#{ space.id }/entries/#{ id }/archived", {}, id = nil, version: sys[:version])
128
+ request = Request.new(
129
+ "/#{ space.id }/entries/#{ id }/archived",
130
+ {},
131
+ id = nil,
132
+ version: sys[:version]
133
+ )
107
134
  response = request.put
108
135
  result = ResourceBuilder.new(response, {}, {}).run
109
136
  refresh_data(result)
@@ -112,7 +139,12 @@ module Contentful
112
139
  # Unarchives an entry.
113
140
  # Returns a Contentful::Management::Entry.
114
141
  def unarchive
115
- request = Request.new("/#{ space.id }/entries/#{ id }/archived", {}, id = nil, version: sys[:version])
142
+ request = Request.new(
143
+ "/#{ space.id }/entries/#{ id }/archived",
144
+ {},
145
+ id = nil,
146
+ version: sys[:version]
147
+ )
116
148
  response = request.delete
117
149
  result = ResourceBuilder.new(response, {}, {}).run
118
150
  refresh_data(result)
@@ -134,13 +166,13 @@ module Contentful
134
166
  # Checks if an entry is published.
135
167
  # Returns true if published.
136
168
  def published?
137
- !sys[:publishedAt].nil?
169
+ sys[:publishedAt] ? true : false
138
170
  end
139
171
 
140
172
  # Checks if an entry is archived.
141
173
  # Returns true if published.
142
174
  def archived?
143
- !sys[:archivedAt].nil?
175
+ sys[:archivedAt] ? true : false
144
176
  end
145
177
 
146
178
  # Returns the currently supported local.
@@ -155,7 +187,7 @@ module Contentful
155
187
  fields_names = raw_fields.first[1].keys
156
188
  fields_names.each_with_object({}) do |field_name, results|
157
189
  results[field_name] = raw_fields.each_with_object({}) do |(locale, fields), field_results|
158
- field_results[locale] = parse_update_attribute(fields[field_name]) unless fields[field_name].nil?
190
+ field_results[locale] = parse_update_attribute(fields[field_name]) if fields[field_name]
159
191
  end
160
192
  end
161
193
  end
@@ -182,35 +214,36 @@ module Contentful
182
214
  end
183
215
 
184
216
  def parse_update_attribute(attribute)
185
- if attribute.is_a? Asset
186
- {sys: {type: 'Link', linkType: 'Asset', id: attribute.id}}
187
- elsif attribute.is_a? Entry
188
- {sys: {type: 'Link', linkType: 'Entry', id: attribute.id}}
189
- elsif attribute.is_a? Location
190
- {lat: attribute.properties[:lat], lon: attribute.properties[:lon]}
191
- elsif attribute.is_a? ::Array
192
- self.class.parse_fields_array(attribute)
193
- else
194
- attribute
217
+ case attribute
218
+ when Asset
219
+ self.class.hash_with_link_object('Asset', attribute)
220
+ when Entry
221
+ self.class.hash_with_link_object('Entry', attribute)
222
+ when Location
223
+ {lat: attribute.properties[:lat], lon: attribute.properties[:lon]}
224
+ when ::Array
225
+ self.class.parse_fields_array(attribute)
226
+ else
227
+ attribute
195
228
  end
196
229
  end
197
230
 
231
+ def self.hash_with_link_object(type, attribute)
232
+ {sys: {type: 'Link', linkType: type, id: attribute.id}}
233
+ end
234
+
198
235
  def self.parse_fields_array(attributes)
199
- type = attributes.first.class.to_s
200
- if type == 'String'
201
- attributes
202
- else
203
- parse_objects_array(attributes, type)
204
- end
236
+ type = attributes.first.class
237
+ type == String ? attributes : parse_objects_array(attributes)
205
238
  end
206
239
 
207
- def self.parse_objects_array(attributes, type)
240
+ def self.parse_objects_array(attributes)
208
241
  attributes.each_with_object([]) do |attr, arr|
209
- arr << case type
210
- when /Entry/ then
211
- {sys: {type: 'Link', linkType: 'Entry', id: attr.id}}
212
- when /Asset/ then
213
- {sys: {type: 'Link', linkType: 'Asset', id: attr.id}}
242
+ arr << case attr
243
+ when Entry then
244
+ hash_with_link_object('Entry', attr)
245
+ when Asset then
246
+ hash_with_link_object('Asset', attr)
214
247
  end
215
248
  end
216
249
  end
@@ -218,11 +251,11 @@ module Contentful
218
251
  def self.fields_with_locale(content_type, attributes)
219
252
  locale = attributes[:locale] || content_type.sys[:space].default_locale
220
253
  fields = content_type.properties[:fields]
221
- field_names = fields.map { |f| f.id.to_sym }
254
+ field_names = fields.map { |field| field.id.to_sym }
222
255
  attributes.keep_if { |key| field_names.include?(key) }
223
256
 
224
257
  attributes.each do |id, value|
225
- field = fields.select { |f| f.id.to_sym == id.to_sym }.first
258
+ field = fields.select { |field| field.id.to_sym == id.to_sym }.first
226
259
  attributes[id] = {locale => parse_attribute_with_field(value, field)}
227
260
  end
228
261
  end