contentful-management 0.8.0 → 0.9.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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +12 -29
  3. data/.rubocop_todo.yml +24 -0
  4. data/.travis.yml +1 -1
  5. data/.yardopts +4 -0
  6. data/CHANGELOG.md +12 -0
  7. data/Gemfile +1 -0
  8. data/Guardfile +39 -34
  9. data/README.md +42 -1
  10. data/Rakefile +2 -0
  11. data/contentful-management.gemspec +2 -1
  12. data/lib/contentful/management/api_key.rb +65 -0
  13. data/lib/contentful/management/array.rb +3 -2
  14. data/lib/contentful/management/asset.rb +124 -58
  15. data/lib/contentful/management/client.rb +60 -19
  16. data/lib/contentful/management/content_type.rb +104 -49
  17. data/lib/contentful/management/content_type_entry_methods_factory.rb +21 -0
  18. data/lib/contentful/management/dynamic_entry.rb +22 -18
  19. data/lib/contentful/management/entry.rb +138 -80
  20. data/lib/contentful/management/error.rb +23 -23
  21. data/lib/contentful/management/field.rb +16 -13
  22. data/lib/contentful/management/http_client.rb +29 -0
  23. data/lib/contentful/management/link.rb +3 -3
  24. data/lib/contentful/management/locale.rb +55 -26
  25. data/lib/contentful/management/location.rb +1 -1
  26. data/lib/contentful/management/request.rb +11 -12
  27. data/lib/contentful/management/resource.rb +27 -12
  28. data/lib/contentful/management/resource/array_like.rb +1 -1
  29. data/lib/contentful/management/resource/asset_fields.rb +1 -0
  30. data/lib/contentful/management/resource/entry_fields.rb +2 -0
  31. data/lib/contentful/management/resource/field_aware.rb +16 -5
  32. data/lib/contentful/management/resource/fields.rb +16 -8
  33. data/lib/contentful/management/resource/refresher.rb +1 -0
  34. data/lib/contentful/management/resource/system_properties.rb +13 -6
  35. data/lib/contentful/management/resource_builder.rb +43 -40
  36. data/lib/contentful/management/space.rb +67 -28
  37. data/lib/contentful/management/space_api_key_methods_factory.rb +15 -0
  38. data/lib/contentful/management/space_asset_methods_factory.rb +4 -0
  39. data/lib/contentful/management/space_association_all_published_method_factory.rb +11 -0
  40. data/lib/contentful/management/space_association_methods_factory.rb +4 -2
  41. data/lib/contentful/management/space_content_type_methods_factory.rb +4 -0
  42. data/lib/contentful/management/space_entry_methods_factory.rb +4 -0
  43. data/lib/contentful/management/space_locale_methods_factory.rb +2 -0
  44. data/lib/contentful/management/space_webhook_methods_factory.rb +2 -0
  45. data/lib/contentful/management/validation.rb +3 -2
  46. data/lib/contentful/management/version.rb +4 -1
  47. data/lib/contentful/management/webhook.rb +42 -21
  48. data/spec/fixtures/vcr_cassettes/api_key/all_for_space.yml +194 -0
  49. data/spec/fixtures/vcr_cassettes/api_key/create_for_space.yml +113 -0
  50. data/spec/fixtures/vcr_cassettes/api_key/find.yml +113 -0
  51. data/spec/fixtures/vcr_cassettes/api_key/find_for_space_not_found.yml +71 -0
  52. data/spec/fixtures/vcr_cassettes/asset/all_public.yml +112 -0
  53. data/spec/fixtures/vcr_cassettes/content_type/all_public.yml +106 -0
  54. data/spec/fixtures/vcr_cassettes/entry/all_public.yml +102 -0
  55. data/spec/fixtures/vcr_cassettes/locale/destroy.yml +330 -0
  56. data/spec/fixtures/vcr_cassettes/locale/update_both.yml +306 -0
  57. data/spec/fixtures/vcr_cassettes/locale/update_code.yml +306 -0
  58. data/spec/fixtures/vcr_cassettes/locale/update_name.yml +306 -0
  59. data/spec/fixtures/vcr_cassettes/space/api_key/all.yml +410 -0
  60. data/spec/fixtures/vcr_cassettes/space/api_key/create.yml +329 -0
  61. data/spec/fixtures/vcr_cassettes/space/api_key/find.yml +329 -0
  62. data/spec/fixtures/vcr_cassettes/space/asset/all_public.yml +328 -0
  63. data/spec/fixtures/vcr_cassettes/space/content_type/all_public.yml +322 -0
  64. data/spec/fixtures/vcr_cassettes/space/entry/all_public.yml +318 -0
  65. data/spec/lib/contentful/management/api_key_spec.rb +55 -0
  66. data/spec/lib/contentful/management/asset_spec.rb +10 -0
  67. data/spec/lib/contentful/management/content_type_spec.rb +10 -0
  68. data/spec/lib/contentful/management/entry_spec.rb +10 -0
  69. data/spec/lib/contentful/management/locale_spec.rb +58 -0
  70. data/spec/lib/contentful/management/space_spec.rb +53 -0
  71. metadata +58 -9
@@ -12,24 +12,37 @@ require 'logger'
12
12
 
13
13
  module Contentful
14
14
  module Management
15
+ # Client for interacting with the Contentful Management API
16
+ # @see _ https://www.contentful.com/developers/docs/references/content-management-api/
15
17
  class Client
16
18
  extend Contentful::Management::HTTPClient
17
19
 
18
20
  attr_reader :access_token, :configuration, :logger
19
21
  attr_accessor :organization_id, :version, :zero_length, :content_type_id, :dynamic_entry_cache
20
22
 
23
+ # Default configuration for Contentful::Management::Client
21
24
  DEFAULT_CONFIGURATION = {
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,
29
- raise_errors: false,
30
- dynamic_entries: []
25
+ api_url: 'api.contentful.com',
26
+ api_version: '1',
27
+ secure: true,
28
+ default_locale: 'en-US',
29
+ gzip_encoded: false,
30
+ logger: false,
31
+ log_level: Logger::INFO,
32
+ raise_errors: false,
33
+ dynamic_entries: []
31
34
  }
32
35
 
36
+ # @param [String] access_token
37
+ # @param [Hash] configuration
38
+ # @option configuration [String] :api_url
39
+ # @option configuration [String] :api_version
40
+ # @option configuration [String] :default_locale
41
+ # @option configuration [Boolean] :gzip_encoded
42
+ # @option configuration [false, ::Logger] :logger
43
+ # @option configuration [::Logger::DEBUG, ::Logger::INFO, ::Logger::WARN, ::Logger::ERROR] :log_level
44
+ # @option configuration [Boolean] :raise_errors
45
+ # @option configuration [::Array<String>] :dynamic_entries
33
46
  def initialize(access_token = nil, configuration = {})
34
47
  @configuration = default_configuration.merge(configuration)
35
48
  setup_logger
@@ -39,18 +52,21 @@ module Contentful
39
52
  update_all_dynamic_entry_cache!
40
53
  end
41
54
 
55
+ # @private
42
56
  def setup_logger
43
57
  @logger = configuration[:logger]
44
58
  logger.level = configuration[:log_level] if logger
45
59
  end
46
60
 
61
+ # @private
47
62
  def update_all_dynamic_entry_cache!
48
- if !configuration[:dynamic_entries].empty?
49
- spaces = configuration[:dynamic_entries].map { |space_id| ::Contentful::Management::Space.find(space_id) }
50
- update_dynamic_entry_cache_for_spaces!(spaces)
51
- end
63
+ return if configuration[:dynamic_entries].empty?
64
+
65
+ spaces = configuration[:dynamic_entries].map { |space_id| ::Contentful::Management::Space.find(space_id) }
66
+ update_dynamic_entry_cache_for_spaces!(spaces)
52
67
  end
53
68
 
69
+ # @private
54
70
  def update_dynamic_entry_cache_for_spaces!(spaces)
55
71
  spaces.each do |space|
56
72
  update_dynamic_entry_cache_for_space!(space)
@@ -59,36 +75,43 @@ module Contentful
59
75
 
60
76
  # Use this method together with the client's :dynamic_entries configuration.
61
77
  # See README for details.
78
+ # @private
62
79
  def update_dynamic_entry_cache_for_space!(space)
63
80
  update_dynamic_entry_cache!(space.content_types.all)
64
81
  end
65
82
 
83
+ # @private
66
84
  def update_dynamic_entry_cache!(content_types)
67
85
  content_types.each do |ct|
68
86
  @dynamic_entry_cache[ct.id.to_sym] = DynamicEntry.create(ct)
69
87
  end
70
88
  end
71
89
 
90
+ # @private
72
91
  def api_version
73
92
  configuration[:api_version]
74
93
  end
75
94
 
95
+ # @private
76
96
  def gzip_encoded
77
97
  configuration[:gzip_encoded]
78
98
  end
79
99
 
100
+ # @private
80
101
  def default_configuration
81
102
  DEFAULT_CONFIGURATION.dup
82
103
  end
83
104
 
105
+ # @private
84
106
  def register_dynamic_entry(key, klass)
85
107
  @dynamic_entry_cache[key.to_sym] = klass
86
108
  end
87
109
 
110
+ # @private
88
111
  def execute_request(request)
89
112
  request_url = request.url
90
113
  url = request.absolute? ? request_url : base_url + request_url
91
- logger.info(request: {url: url, query: request.query, header: request_headers}) if logger
114
+ logger.info(request: { url: url, query: request.query, header: request_headers }) if logger
92
115
  raw_response = yield(url)
93
116
  logger.debug(response: raw_response) if logger
94
117
  clear_headers
@@ -97,81 +120,98 @@ module Contentful
97
120
  result
98
121
  end
99
122
 
123
+ # @private
100
124
  def clear_headers
101
125
  self.content_type_id = nil
102
126
  self.version = nil
103
127
  self.organization_id = nil
104
128
  end
105
129
 
130
+ # @private
106
131
  def delete(request)
107
132
  execute_request(request) do |url|
108
133
  self.class.delete_http(url, {}, request_headers)
109
134
  end
110
135
  end
111
136
 
137
+ # @private
112
138
  def get(request)
113
139
  execute_request(request) do |url|
114
140
  self.class.get_http(url, request.query, request_headers)
115
141
  end
116
142
  end
117
143
 
144
+ # @private
118
145
  def post(request)
119
146
  execute_request(request) do |url|
120
147
  self.class.post_http(url, request.query, request_headers)
121
148
  end
122
149
  end
123
150
 
151
+ # @private
124
152
  def put(request)
125
153
  execute_request(request) do |url|
126
154
  self.class.put_http(url, request.query, request_headers)
127
155
  end
128
156
  end
129
157
 
158
+ # @private
130
159
  def base_url
131
- "#{ protocol }://#{ configuration[:api_url]}/spaces"
160
+ "#{protocol}://#{configuration[:api_url]}/spaces"
132
161
  end
133
162
 
163
+ # @private
134
164
  def default_locale
135
165
  configuration[:default_locale]
136
166
  end
137
167
 
168
+ # @private
138
169
  def protocol
139
170
  configuration[:secure] ? 'https' : 'http'
140
171
  end
141
172
 
173
+ # @private
142
174
  def authentication_header
143
- Hash['Authorization', "Bearer #{ access_token }"]
175
+ Hash['Authorization', "Bearer #{access_token}"]
144
176
  end
145
177
 
178
+ # @private
146
179
  def api_version_header
147
- Hash['Content-Type', "application/vnd.contentful.management.v#{ api_version }+json"]
180
+ Hash['Content-Type', "application/vnd.contentful.management.v#{api_version}+json"]
148
181
  end
149
182
 
183
+ # @private
150
184
  def user_agent
151
- Hash['User-Agent', "RubyContentfulManagementGem/#{ Contentful::Management::VERSION }"]
185
+ Hash['User-Agent', "RubyContentfulManagementGem/#{Contentful::Management::VERSION}"]
152
186
  end
153
187
 
188
+ # @private
154
189
  def organization_header(organization_id)
155
190
  Hash['X-Contentful-Organization', organization_id]
156
191
  end
157
192
 
193
+ # @private
158
194
  def version_header(version)
159
195
  Hash['X-Contentful-Version', version]
160
196
  end
161
197
 
198
+ # @private
162
199
  def content_type_header(content_type_id)
163
200
  Hash['X-Contentful-Content-Type', content_type_id]
164
201
  end
165
202
 
203
+ # @private
166
204
  def zero_length_header
167
205
  Hash['Content-Length', 0]
168
206
  end
169
207
 
208
+ # @private
170
209
  def accept_encoding_header(encoding)
171
210
  Hash['Accept-Encoding', encoding]
172
211
  end
173
212
 
174
- # XXX: headers should be supplied differently, maybe through the request object.
213
+ # @todo headers should be supplied differently, maybe through the request object.
214
+ # @private
175
215
  def request_headers
176
216
  headers = {}
177
217
  headers.merge! user_agent
@@ -185,6 +225,7 @@ module Contentful
185
225
  headers
186
226
  end
187
227
 
228
+ # @private
188
229
  def self.shared_instance
189
230
  Thread.current[:client]
190
231
  end
@@ -7,19 +7,20 @@ require_relative 'support'
7
7
  module Contentful
8
8
  module Management
9
9
  # Resource class for ContentType.
10
- # https://www.contentful.com/developers/documentation/content-management-api/#resources-content-types
10
+ # @see _ https://www.contentful.com/developers/documentation/content-management-api/#resources-content-types
11
11
  class ContentType
12
+ # Shortcuts for Contentful Field Types
12
13
  FIELD_TYPES = [
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'
14
+ SYMBOL = 'Symbol',
15
+ TEXT = 'Text',
16
+ INTEGER = 'Integer',
17
+ FLOAT = 'Number',
18
+ DATE = 'Date',
19
+ BOOLEAN = 'Boolean',
20
+ LINK = 'Link',
21
+ ARRAY = 'Array',
22
+ OBJECT = 'Object',
23
+ LOCATION = 'Location'
23
24
  ]
24
25
 
25
26
  include Contentful::Management::Resource
@@ -32,12 +33,20 @@ module Contentful
32
33
  property :displayField, :string
33
34
 
34
35
  # Gets a collection of content types.
35
- # Takes an id of space and an optional hash of query options
36
- # Returns a Contentful::Management::Array of Contentful::Management::ContentType.
36
+ #
37
+ # @param [String] space_id
38
+ # @param [Hash] query Search Options
39
+ # @see _ For complete option list: http://docs.contentfulcda.apiary.io/#reference/search-parameters
40
+ # @option query [String] 'sys.id' Content Type ID
41
+ # @option query [String] :name Kind of Content Type
42
+ # @option query [Integer] :limit
43
+ # @option query [Integer] :skip
44
+ #
45
+ # @return [Contentful::Management::Array<Contentful::Management::ContentType>]
37
46
  def self.all(space_id, query = {})
38
47
  request = Request.new(
39
- "/#{ space_id }/content_types",
40
- query
48
+ "/#{space_id}/content_types",
49
+ query
41
50
  )
42
51
  response = request.get
43
52
  result = ResourceBuilder.new(response, {}, {})
@@ -46,11 +55,37 @@ module Contentful
46
55
  content_types
47
56
  end
48
57
 
49
- # Gets a specific entry.
50
- # Takes an id of space and content type.
51
- # Returns a Contentful::Management::ContentType.
58
+ # Gets a collection of published content types.
59
+ #
60
+ # @param [String] space_id
61
+ # @param [Hash] query Search Options
62
+ # @see _ For complete option list: http://docs.contentfulcda.apiary.io/#reference/search-parameters
63
+ # @option query [String] 'sys.id' Content Type ID
64
+ # @option query [String] :name Kind of Content Type
65
+ # @option query [Integer] :limit
66
+ # @option query [Integer] :skip
67
+ #
68
+ # @return [Contentful::Management::Array<Contentful::Management::ContentType>]
69
+ def self.all_published(space_id, query = {})
70
+ request = Request.new(
71
+ "/#{space_id}/public/content_types",
72
+ query
73
+ )
74
+ response = request.get
75
+ result = ResourceBuilder.new(response, {}, {})
76
+ content_types = result.run
77
+ client.update_dynamic_entry_cache!(content_types)
78
+ content_types
79
+ end
80
+
81
+ # Gets a specific content type.
82
+ #
83
+ # @param [String] space_id
84
+ # @param [String] content_type_id
85
+ #
86
+ # @return [Contentful::Management::ContentType]
52
87
  def self.find(space_id, content_type_id)
53
- request = Request.new("/#{ space_id }/content_types/#{ content_type_id }")
88
+ request = Request.new("/#{space_id}/content_types/#{content_type_id}")
54
89
  response = request.get
55
90
  result = ResourceBuilder.new(response, {}, {})
56
91
  content_type = result.run
@@ -59,9 +94,10 @@ module Contentful
59
94
  end
60
95
 
61
96
  # Destroys a content type.
62
- # Returns true if succeed.
97
+ #
98
+ # @return [true, Contentful::Management::Error] success
63
99
  def destroy
64
- request = Request.new("/#{ space.id }/content_types/#{ id }")
100
+ request = Request.new("/#{space.id}/content_types/#{id}")
65
101
  response = request.delete
66
102
  if response.status == :no_content
67
103
  return true
@@ -75,10 +111,10 @@ module Contentful
75
111
  # Returns a Contentful::Management::ContentType.
76
112
  def activate
77
113
  request = Request.new(
78
- "/#{ space.id }/content_types/#{ id }/published",
79
- {},
80
- id = nil,
81
- version: sys[:version]
114
+ "/#{space.id}/content_types/#{id}/published",
115
+ {},
116
+ nil,
117
+ version: sys[:version]
82
118
  )
83
119
  response = request.put
84
120
  result = ResourceBuilder.new(response, {}, {}).run
@@ -87,34 +123,42 @@ module Contentful
87
123
 
88
124
  # Deactivates a content type.
89
125
  # Only content type that has no entries can be deactivated.
90
- # Returns a Contentful::Management::ContentType.
126
+ #
127
+ # @return [Contentful::Management::ContentType]
91
128
  def deactivate
92
- request = Request.new("/#{ space.id }/content_types/#{ id }/published")
129
+ request = Request.new("/#{space.id}/content_types/#{id}/published")
93
130
  response = request.delete
94
131
  result = ResourceBuilder.new(response, {}, {}).run
95
132
  refresh_data(result)
96
133
  end
97
134
 
98
135
  # Checks if a content type is active.
99
- # Returns true if active.
136
+ #
137
+ # @return [Boolean]
100
138
  def active?
101
139
  sys[:publishedAt] ? true : false
102
140
  end
103
141
 
104
142
  # Creates a content type.
105
- # Takes an id of space and hash with attributes.
106
- # Returns a Contentful::Management::ContentType.
143
+ #
144
+ # @param [String] space_id
145
+ # @param [Hash] attributes
146
+ # @option attributes [String] :id
147
+ # @option attributes [String] :name
148
+ # @option attributes [::Array<Contentful::Management::Field>] :fields
149
+ #
150
+ # @return [Contentful::Management::ContentType]
107
151
  def self.create(space_id, attributes)
108
152
  fields = fields_to_nested_properties_hash(attributes[:fields] || [])
109
153
 
110
154
  params = attributes.clone
111
155
  params[:fields] = fields
112
156
  params.delete(:id)
113
- params = params.delete_if { |k, v| v.nil? }
157
+ params = params.delete_if { |_, v| v.nil? }
114
158
 
115
159
  request = Request.new(
116
- "/#{ space_id }/content_types/#{ attributes[:id]}",
117
- params
160
+ "/#{space_id}/content_types/#{attributes[:id]}",
161
+ params
118
162
  )
119
163
  response = attributes[:id].nil? ? request.post : request.put
120
164
  result = ResourceBuilder.new(response, {}, {}).run
@@ -122,6 +166,7 @@ module Contentful
122
166
  result
123
167
  end
124
168
 
169
+ # @private
125
170
  def display_field_value(attributes)
126
171
  if attributes[:displayField].nil? && (display_field.nil? || display_field.empty?)
127
172
  nil
@@ -131,8 +176,13 @@ module Contentful
131
176
  end
132
177
 
133
178
  # Updates a content type.
134
- # Takes a hash with attributes.
135
- # Returns a Contentful::Management::ContentType.
179
+ #
180
+ # @param [Hash] attributes
181
+ # @option attributes [String] :id
182
+ # @option attributes [String] :name
183
+ # @option attributes [::Array<Contentful::Management::Field>] :fields
184
+ #
185
+ # @return [Contentful::Management::ContentType]
136
186
  def update(attributes)
137
187
  parameters = {}
138
188
  parameters.merge!(displayField: display_field_value(attributes))
@@ -140,12 +190,12 @@ module Contentful
140
190
  parameters.merge!(description: (attributes[:description] || description))
141
191
  parameters.merge!(fields: self.class.fields_to_nested_properties_hash(attributes[:fields] || fields))
142
192
 
143
- parameters = parameters.delete_if { |k, v| v.nil? }
193
+ parameters = parameters.delete_if { |_, v| v.nil? }
144
194
  request = Request.new(
145
- "/#{ space.id }/content_types/#{ id }",
146
- parameters,
147
- id = nil,
148
- version: sys[:version]
195
+ "/#{space.id}/content_types/#{id}",
196
+ parameters,
197
+ nil,
198
+ version: sys[:version]
149
199
  )
150
200
  response = request.put
151
201
  result = ResourceBuilder.new(response, {}, {}).run
@@ -153,7 +203,9 @@ module Contentful
153
203
  end
154
204
 
155
205
  # If a content type is a new object gets created in the Contentful, otherwise the existing entry gets updated.
156
- # See README for details.
206
+ # @see _ README for details.
207
+ #
208
+ # @return [Contentful::Management::ContentType]
157
209
  def save
158
210
  if id
159
211
  update(@properties)
@@ -164,6 +216,7 @@ module Contentful
164
216
  end
165
217
 
166
218
  # This method merges existing fields with new one, when adding, creating or updating new fields.
219
+ # @private
167
220
  def merged_fields(new_field)
168
221
  field_ids = []
169
222
  merged_fields = fields.each_with_object([]) do |field, fields|
@@ -175,17 +228,18 @@ module Contentful
175
228
  merged_fields
176
229
  end
177
230
 
231
+ # @private
178
232
  alias_method :orig_fields, :fields
179
233
 
180
234
  # Use this method only in the context of content type.
181
235
  # Allows you to add and create a field with specified attributes or destroy by pass field id.
182
- # Returns a Contentful::Management::ContentType.
183
- # See README for details.
236
+ # @see _ README for details.
237
+ #
238
+ # @return [Contentful::Management::ContentType]
184
239
  def fields
185
240
  fields = orig_fields
186
241
 
187
242
  fields.instance_exec(self) do |content_type|
188
-
189
243
  fields.define_singleton_method(:add) do |field|
190
244
  content_type.update(fields: content_type.merged_fields(field))
191
245
  end
@@ -204,7 +258,6 @@ module Contentful
204
258
  fields = content_type.fields.select { |field| field.id != id }
205
259
  content_type.update(fields: fields)
206
260
  end
207
-
208
261
  end
209
262
 
210
263
  fields
@@ -212,14 +265,16 @@ module Contentful
212
265
 
213
266
  # Use this method only in the context of content type.
214
267
  # Allows you to create an entry.
215
- # Returns a Contentful::Management::Entry.
216
- # See README for details.
268
+ # @see _ README for details.
269
+ #
270
+ # @private
271
+ #
272
+ # @return [Contentful::Management::ContentTypeEntryMethodsFactory]
217
273
  def entries
218
274
  Contentful::Management::ContentTypeEntryMethodsFactory.new(self)
219
275
  end
220
276
 
221
- private
222
-
277
+ # @private
223
278
  def self.fields_to_nested_properties_hash(fields)
224
279
  fields.map do |field|
225
280
  field.properties.replace(field.properties_to_hash)