contentful-management 0.0.1.pre

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 (188) hide show
  1. data/.gitignore +24 -0
  2. data/.reek +3 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +16 -0
  5. data/.travis.yml +9 -0
  6. data/CHANGELOG.md +2 -0
  7. data/Gemfile +13 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +408 -0
  10. data/Rakefile +37 -0
  11. data/bin/cma-console +8 -0
  12. data/contentful-management.gemspec +33 -0
  13. data/examples/blog.rb +82 -0
  14. data/examples/content_types.rb +45 -0
  15. data/examples/create_space.rb +35 -0
  16. data/lib/contentful/management.rb +12 -0
  17. data/lib/contentful/management/array.rb +35 -0
  18. data/lib/contentful/management/asset.rb +163 -0
  19. data/lib/contentful/management/client.rb +160 -0
  20. data/lib/contentful/management/content_type.rb +180 -0
  21. data/lib/contentful/management/dynamic_entry.rb +71 -0
  22. data/lib/contentful/management/entry.rb +213 -0
  23. data/lib/contentful/management/error.rb +62 -0
  24. data/lib/contentful/management/field.rb +34 -0
  25. data/lib/contentful/management/file.rb +15 -0
  26. data/lib/contentful/management/http_client.rb +22 -0
  27. data/lib/contentful/management/link.rb +23 -0
  28. data/lib/contentful/management/locale.rb +48 -0
  29. data/lib/contentful/management/location.rb +15 -0
  30. data/lib/contentful/management/request.rb +82 -0
  31. data/lib/contentful/management/resource.rb +166 -0
  32. data/lib/contentful/management/resource/array_like.rb +37 -0
  33. data/lib/contentful/management/resource/fields.rb +69 -0
  34. data/lib/contentful/management/resource/refresher.rb +21 -0
  35. data/lib/contentful/management/resource/system_properties.rb +56 -0
  36. data/lib/contentful/management/resource_builder.rb +269 -0
  37. data/lib/contentful/management/response.rb +72 -0
  38. data/lib/contentful/management/space.rb +159 -0
  39. data/lib/contentful/management/support.rb +24 -0
  40. data/lib/contentful/management/version.rb +6 -0
  41. data/spec/fixtures/vcr_cassettes/array_page_1.yml +1337 -0
  42. data/spec/fixtures/vcr_cassettes/asset/all.yml +611 -0
  43. data/spec/fixtures/vcr_cassettes/asset/archive.yml +228 -0
  44. data/spec/fixtures/vcr_cassettes/asset/archive_published.yml +177 -0
  45. data/spec/fixtures/vcr_cassettes/asset/archived_false.yml +228 -0
  46. data/spec/fixtures/vcr_cassettes/asset/archived_true.yml +228 -0
  47. data/spec/fixtures/vcr_cassettes/asset/create.yml +150 -0
  48. data/spec/fixtures/vcr_cassettes/asset/create_with_already_used_id.yml +56 -0
  49. data/spec/fixtures/vcr_cassettes/asset/create_with_custom_id.yml +150 -0
  50. data/spec/fixtures/vcr_cassettes/asset/destroy.yml +159 -0
  51. data/spec/fixtures/vcr_cassettes/asset/destroy_published.yml +175 -0
  52. data/spec/fixtures/vcr_cassettes/asset/find.yml +108 -0
  53. data/spec/fixtures/vcr_cassettes/asset/find_not_found.yml +64 -0
  54. data/spec/fixtures/vcr_cassettes/asset/image_url.yml +133 -0
  55. data/spec/fixtures/vcr_cassettes/asset/locale.yml +110 -0
  56. data/spec/fixtures/vcr_cassettes/asset/publish.yml +228 -0
  57. data/spec/fixtures/vcr_cassettes/asset/publish_already_published.yml +176 -0
  58. data/spec/fixtures/vcr_cassettes/asset/published_false.yml +228 -0
  59. data/spec/fixtures/vcr_cassettes/asset/published_true.yml +226 -0
  60. data/spec/fixtures/vcr_cassettes/asset/save_update.yml +242 -0
  61. data/spec/fixtures/vcr_cassettes/asset/set_locale.yml +110 -0
  62. data/spec/fixtures/vcr_cassettes/asset/unarchive.yml +228 -0
  63. data/spec/fixtures/vcr_cassettes/asset/unarchive_already_unarchive.yml +168 -0
  64. data/spec/fixtures/vcr_cassettes/asset/unarchive_already_unarchived.yml +168 -0
  65. data/spec/fixtures/vcr_cassettes/asset/unpublish.yml +228 -0
  66. data/spec/fixtures/vcr_cassettes/asset/unpublish_already_unpublished.yml +168 -0
  67. data/spec/fixtures/vcr_cassettes/asset/update_file.yml +207 -0
  68. data/spec/fixtures/vcr_cassettes/asset/update_to_specified_locale.yml +224 -0
  69. data/spec/fixtures/vcr_cassettes/asset/update_with_default_locale_without_file.yml +214 -0
  70. data/spec/fixtures/vcr_cassettes/content_type/activate.yml +246 -0
  71. data/spec/fixtures/vcr_cassettes/content_type/activate_with_invalid_version.yml +186 -0
  72. data/spec/fixtures/vcr_cassettes/content_type/activated_false.yml +246 -0
  73. data/spec/fixtures/vcr_cassettes/content_type/activated_true.yml +257 -0
  74. data/spec/fixtures/vcr_cassettes/content_type/all.yml +190 -0
  75. data/spec/fixtures/vcr_cassettes/content_type/create.yml +87 -0
  76. data/spec/fixtures/vcr_cassettes/content_type/create_content_type_with_id.yml +87 -0
  77. data/spec/fixtures/vcr_cassettes/content_type/create_with_Array_field.yml +94 -0
  78. data/spec/fixtures/vcr_cassettes/content_type/create_with_Boolean_field.yml +94 -0
  79. data/spec/fixtures/vcr_cassettes/content_type/create_with_Date_field.yml +94 -0
  80. data/spec/fixtures/vcr_cassettes/content_type/create_with_Integer_field.yml +94 -0
  81. data/spec/fixtures/vcr_cassettes/content_type/create_with_Link_field.yml +95 -0
  82. data/spec/fixtures/vcr_cassettes/content_type/create_with_Location_field.yml +94 -0
  83. data/spec/fixtures/vcr_cassettes/content_type/create_with_Number_field.yml +94 -0
  84. data/spec/fixtures/vcr_cassettes/content_type/create_with_Object_field.yml +94 -0
  85. data/spec/fixtures/vcr_cassettes/content_type/create_with_Symbol_field.yml +94 -0
  86. data/spec/fixtures/vcr_cassettes/content_type/create_with_Text_field.yml +94 -0
  87. data/spec/fixtures/vcr_cassettes/content_type/deactivate.yml +246 -0
  88. data/spec/fixtures/vcr_cassettes/content_type/deactivate_already_deactivated.yml +176 -0
  89. data/spec/fixtures/vcr_cassettes/content_type/deactivate_with_entries.yml +236 -0
  90. data/spec/fixtures/vcr_cassettes/content_type/deactivate_with_version_change.yml +248 -0
  91. data/spec/fixtures/vcr_cassettes/content_type/destroy.yml +148 -0
  92. data/spec/fixtures/vcr_cassettes/content_type/destroy_activated.yml +164 -0
  93. data/spec/fixtures/vcr_cassettes/content_type/entry/create.yml +1344 -0
  94. data/spec/fixtures/vcr_cassettes/content_type/entry/create_with_camel_case_id_to_multiple_locales.yml +235 -0
  95. data/spec/fixtures/vcr_cassettes/content_type/entry/create_with_entries.yml +398 -0
  96. data/spec/fixtures/vcr_cassettes/content_type/entry/create_with_entries_for_multiple_locales.yml +3157 -0
  97. data/spec/fixtures/vcr_cassettes/content_type/entry/create_with_multiple_locales.yml +230 -0
  98. data/spec/fixtures/vcr_cassettes/content_type/fields/add.yml +345 -0
  99. data/spec/fixtures/vcr_cassettes/content_type/fields/create.yml +353 -0
  100. data/spec/fixtures/vcr_cassettes/content_type/fields/create_array_with_params.yml +228 -0
  101. data/spec/fixtures/vcr_cassettes/content_type/fields/create_with_params.yml +221 -0
  102. data/spec/fixtures/vcr_cassettes/content_type/fields/destroy.yml +349 -0
  103. data/spec/fixtures/vcr_cassettes/content_type/fields/update_field.yml +358 -0
  104. data/spec/fixtures/vcr_cassettes/content_type/find.yml +180 -0
  105. data/spec/fixtures/vcr_cassettes/content_type/find_not_found.yml +64 -0
  106. data/spec/fixtures/vcr_cassettes/content_type/save_new.yml +876 -0
  107. data/spec/fixtures/vcr_cassettes/content_type/save_updated.yml +345 -0
  108. data/spec/fixtures/vcr_cassettes/content_type/save_with_added_field.yml +217 -0
  109. data/spec/fixtures/vcr_cassettes/content_type/update.yml +175 -0
  110. data/spec/fixtures/vcr_cassettes/content_type/update_change_field_name.yml +222 -0
  111. data/spec/fixtures/vcr_cassettes/content_type/update_remove_field.yml +231 -0
  112. data/spec/fixtures/vcr_cassettes/content_type/update_with_fields.yml +222 -0
  113. data/spec/fixtures/vcr_cassettes/content_type/update_with_one_new_field.yml +251 -0
  114. data/spec/fixtures/vcr_cassettes/entry/all.yml +139 -0
  115. data/spec/fixtures/vcr_cassettes/entry/archive.yml +222 -0
  116. data/spec/fixtures/vcr_cassettes/entry/archive_published.yml +187 -0
  117. data/spec/fixtures/vcr_cassettes/entry/archived_false.yml +178 -0
  118. data/spec/fixtures/vcr_cassettes/entry/archived_true.yml +248 -0
  119. data/spec/fixtures/vcr_cassettes/entry/create.yml +245 -0
  120. data/spec/fixtures/vcr_cassettes/entry/create_test.yml +150 -0
  121. data/spec/fixtures/vcr_cassettes/entry/create_with_asset.yml +403 -0
  122. data/spec/fixtures/vcr_cassettes/entry/create_with_assets.yml +419 -0
  123. data/spec/fixtures/vcr_cassettes/entry/create_with_entries.yml +418 -0
  124. data/spec/fixtures/vcr_cassettes/entry/create_with_entry.yml +493 -0
  125. data/spec/fixtures/vcr_cassettes/entry/create_with_location.yml +259 -0
  126. data/spec/fixtures/vcr_cassettes/entry/create_with_symbols.yml +271 -0
  127. data/spec/fixtures/vcr_cassettes/entry/destory_published.yml +172 -0
  128. data/spec/fixtures/vcr_cassettes/entry/destroy.yml +156 -0
  129. data/spec/fixtures/vcr_cassettes/entry/find.yml +129 -0
  130. data/spec/fixtures/vcr_cassettes/entry/find_not_found.yml +64 -0
  131. data/spec/fixtures/vcr_cassettes/entry/publish.yml +248 -0
  132. data/spec/fixtures/vcr_cassettes/entry/publish_already_published.yml +186 -0
  133. data/spec/fixtures/vcr_cassettes/entry/published_false.yml +248 -0
  134. data/spec/fixtures/vcr_cassettes/entry/published_true.yml +257 -0
  135. data/spec/fixtures/vcr_cassettes/entry/save_update.yml +211 -0
  136. data/spec/fixtures/vcr_cassettes/entry/unarchive.yml +248 -0
  137. data/spec/fixtures/vcr_cassettes/entry/unarchive_already_unarchived.yml +178 -0
  138. data/spec/fixtures/vcr_cassettes/entry/unpublish.yml +248 -0
  139. data/spec/fixtures/vcr_cassettes/entry/unpublish_already_unpublished.yml +178 -0
  140. data/spec/fixtures/vcr_cassettes/entry/update.yml +626 -0
  141. data/spec/fixtures/vcr_cassettes/entry/update_unlocalized_field.yml +308 -0
  142. data/spec/fixtures/vcr_cassettes/entry/update_with_custom_locale.yml +332 -0
  143. data/spec/fixtures/vcr_cassettes/get_request.yml +37 -0
  144. data/spec/fixtures/vcr_cassettes/locale/all_for_space.yml +321 -0
  145. data/spec/fixtures/vcr_cassettes/locale/create_for_space.yml +95 -0
  146. data/spec/fixtures/vcr_cassettes/locale/find.yml +95 -0
  147. data/spec/fixtures/vcr_cassettes/locale/find_for_space_not_found.yml +67 -0
  148. data/spec/fixtures/vcr_cassettes/space/all.yml +1201 -0
  149. data/spec/fixtures/vcr_cassettes/space/asset/all.yml +475 -0
  150. data/spec/fixtures/vcr_cassettes/space/asset/assets.yml +826 -0
  151. data/spec/fixtures/vcr_cassettes/space/asset/create.yml +508 -0
  152. data/spec/fixtures/vcr_cassettes/space/asset/create_with_multiple_locales.yml +1062 -0
  153. data/spec/fixtures/vcr_cassettes/space/asset/find.yml +634 -0
  154. data/spec/fixtures/vcr_cassettes/space/content_type/all.yml +470 -0
  155. data/spec/fixtures/vcr_cassettes/space/content_type/content_types.yml +341 -0
  156. data/spec/fixtures/vcr_cassettes/space/content_type/create.yml +425 -0
  157. data/spec/fixtures/vcr_cassettes/space/content_type/find.yml +493 -0
  158. data/spec/fixtures/vcr_cassettes/space/create.yml +85 -0
  159. data/spec/fixtures/vcr_cassettes/space/create_when_limit_has_been_reached.yml +67 -0
  160. data/spec/fixtures/vcr_cassettes/space/create_with_unknown_organization.yml +65 -0
  161. data/spec/fixtures/vcr_cassettes/space/create_without_organization.yml +83 -0
  162. data/spec/fixtures/vcr_cassettes/space/destory.yml +196 -0
  163. data/spec/fixtures/vcr_cassettes/space/entry/all.yml +634 -0
  164. data/spec/fixtures/vcr_cassettes/space/entry/entries.yml +498 -0
  165. data/spec/fixtures/vcr_cassettes/space/entry/find.yml +624 -0
  166. data/spec/fixtures/vcr_cassettes/space/find.yml +270 -0
  167. data/spec/fixtures/vcr_cassettes/space/find_not_found.yml +67 -0
  168. data/spec/fixtures/vcr_cassettes/space/locale/all.yml +690 -0
  169. data/spec/fixtures/vcr_cassettes/space/locale/create.yml +572 -0
  170. data/spec/fixtures/vcr_cassettes/space/locale/create_with_the_same_code.yml +585 -0
  171. data/spec/fixtures/vcr_cassettes/space/locale/find.yml +572 -0
  172. data/spec/fixtures/vcr_cassettes/space/locale/find_not_found.yml +580 -0
  173. data/spec/fixtures/vcr_cassettes/space/locale/locales.yml +480 -0
  174. data/spec/fixtures/vcr_cassettes/space/locale/update.yml +700 -0
  175. data/spec/fixtures/vcr_cassettes/space/save_new_space.yml +67 -0
  176. data/spec/fixtures/vcr_cassettes/space/save_update_space.yml +352 -0
  177. data/spec/fixtures/vcr_cassettes/space/update.yml +350 -0
  178. data/spec/fixtures/vcr_cassettes/space/update_with_the_same_data.yml +350 -0
  179. data/spec/lib/contentful/management/array_spec.rb +59 -0
  180. data/spec/lib/contentful/management/asset_spec.rb +321 -0
  181. data/spec/lib/contentful/management/client_spec.rb +80 -0
  182. data/spec/lib/contentful/management/content_type_spec.rb +455 -0
  183. data/spec/lib/contentful/management/entry_spec.rb +308 -0
  184. data/spec/lib/contentful/management/locale_spec.rb +53 -0
  185. data/spec/lib/contentful/management/space_spec.rb +317 -0
  186. data/spec/spec_helper.rb +10 -0
  187. data/spec/support/vcr.rb +17 -0
  188. metadata +554 -0
@@ -0,0 +1,160 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'contentful/management'
3
+ require 'contentful/management/response'
4
+ require 'contentful/management/resource_builder'
5
+
6
+ require 'contentful/management/version'
7
+ require 'contentful/management/http_client'
8
+
9
+ require_relative 'request'
10
+ require 'http'
11
+ require 'json'
12
+
13
+ module Contentful
14
+ module Management
15
+ class Client
16
+ extend Contentful::Management::HTTPClient
17
+
18
+ attr_reader :access_token, :configuration
19
+ attr_accessor :organization_id, :version, :zero_length, :content_type_id, :dynamic_entry_cache
20
+
21
+ DEFAULT_CONFIGURATION = {
22
+ api_url: 'api.contentful.com',
23
+ api_version: '1',
24
+ secure: true,
25
+ default_locale: 'en-US',
26
+ }
27
+
28
+ def initialize(access_token = nil, configuration = {})
29
+ @configuration = default_configuration.merge(configuration)
30
+ @access_token = access_token
31
+ @dynamic_entry_cache = {}
32
+ Thread.current[:client] = self
33
+ end
34
+
35
+
36
+ def update_dynamic_entry_cache_for_spaces!(spaces)
37
+ spaces.each do |space|
38
+ update_dynamic_entry_cache_for_space!(space)
39
+ end
40
+ end
41
+
42
+ # Use this method together with the client's :dynamic_entries configuration.
43
+ # See README for details.
44
+ def update_dynamic_entry_cache_for_space!(space)
45
+ update_dynamic_entry_cache!(space.content_types)
46
+ end
47
+
48
+ def update_dynamic_entry_cache!(content_types)
49
+ @dynamic_entry_cache = Hash[
50
+ content_types.map do |ct|
51
+ [
52
+ ct.id.to_sym,
53
+ DynamicEntry.create(ct)
54
+ ]
55
+ end
56
+ ]
57
+ end
58
+
59
+ def api_version
60
+ configuration[:api_version]
61
+ end
62
+
63
+ def default_configuration
64
+ DEFAULT_CONFIGURATION.dup
65
+ end
66
+
67
+ def register_dynamic_entry(key, klass)
68
+ @dynamic_entry_cache[key.to_sym] = klass
69
+ end
70
+
71
+ def execute_request(request)
72
+ request_url = request.url
73
+ url = request.absolute? ? request_url : base_url + request_url
74
+ raw_response = yield(url)
75
+ Response.new(raw_response, request)
76
+ end
77
+
78
+ def delete(request)
79
+ execute_request(request) do |url|
80
+ self.class.delete_http(url, {}, request_headers)
81
+ end
82
+ end
83
+
84
+ def get(request)
85
+ execute_request(request) do |url|
86
+ self.class.get_http(url, {}, request_headers)
87
+ end
88
+ end
89
+
90
+ def post(request)
91
+ execute_request(request) do |url|
92
+ self.class.post_http(url, request.query, request_headers)
93
+ end
94
+ end
95
+
96
+ def put(request)
97
+ execute_request(request) do |url|
98
+ self.class.put_http(url, request.query, request_headers)
99
+ end
100
+ end
101
+
102
+ def base_url
103
+ "#{ protocol }://#{ configuration[:api_url]}/spaces"
104
+ end
105
+
106
+ def default_locale
107
+ configuration[:default_locale]
108
+ end
109
+
110
+ def protocol
111
+ configuration[:secure] ? 'https' : 'http'
112
+ end
113
+
114
+ def authentication_header
115
+ Hash['Authorization', "Bearer #{ access_token }"]
116
+ end
117
+
118
+ def api_version_header
119
+ Hash['Content-Type', "application/vnd.contentful.management.v#{ api_version }+json"]
120
+ end
121
+
122
+ def user_agent
123
+ Hash['User-Agent', "RubyContenfulManagementGem/#{ Contentful::Management::VERSION }"]
124
+ end
125
+
126
+ def organization_header(organization_id)
127
+ Hash['X-Contentful-Organization', organization_id]
128
+ end
129
+
130
+ def version_header(version)
131
+ Hash['X-Contentful-Version', version]
132
+ end
133
+
134
+ def content_type_header(content_type_id)
135
+ Hash['X-Contentful-Content-Type', content_type_id]
136
+ end
137
+
138
+ def zero_length_header
139
+ Hash['Content-Length', 0]
140
+ end
141
+
142
+ # XXX: headers should be supplied differently, maybe through the request object.
143
+ def request_headers
144
+ headers = {}
145
+ headers.merge! user_agent
146
+ headers.merge! authentication_header
147
+ headers.merge! api_version_header
148
+ headers.merge! organization_header(organization_id) if organization_id
149
+ headers.merge! version_header(version) if version
150
+ headers.merge! zero_length_header if zero_length
151
+ headers.merge! content_type_header(content_type_id) if content_type_id
152
+ headers
153
+ end
154
+
155
+ def self.shared_instance
156
+ Thread.current[:client]
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,180 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require_relative 'resource'
3
+ require_relative 'field'
4
+
5
+ module Contentful
6
+ module Management
7
+ class ContentType
8
+
9
+ FIELD_TYPES = [
10
+ SYMBOL = 'Symbol',
11
+ TEXT = 'Text',
12
+ INTEGER = 'Integer',
13
+ FLOAT = 'Number',
14
+ DATE = 'Date',
15
+ BOOLEAN = 'Boolean',
16
+ LINK = 'Link',
17
+ ARRAY = 'Array',
18
+ OBJECT = 'Object',
19
+ LOCATION = 'Location'
20
+ ]
21
+
22
+ include Contentful::Management::Resource
23
+ include Contentful::Management::Resource::SystemProperties
24
+ include Contentful::Management::Resource::Refresher
25
+
26
+ property :name, :string
27
+ property :description, :string
28
+ property :fields, Field
29
+ property :displayField, :string
30
+
31
+ def self.all(space_id)
32
+ request = Request.new("/#{ space_id }/content_types")
33
+ response = request.get
34
+ result = ResourceBuilder.new(self, response, {}, {})
35
+ content_types = result.run
36
+ Contentful::Management::Client.shared_instance.update_dynamic_entry_cache!(content_types)
37
+ content_types
38
+ end
39
+
40
+ def self.find(space_id, content_type_id)
41
+ request = Request.new("/#{ space_id }/content_types/#{ content_type_id }")
42
+ response = request.get
43
+ result = ResourceBuilder.new(self, response, {}, {})
44
+ content_type = result.run
45
+ Contentful::Management::Client.shared_instance.register_dynamic_entry(content_type.id, DynamicEntry.create(content_type)) if content_type.is_a?(self)
46
+ content_type
47
+ end
48
+
49
+ def destroy
50
+ request = Request.new("/#{ space.id }/content_types/#{ id }")
51
+ response = request.delete
52
+ if response.status == :no_content
53
+ return true
54
+ else
55
+ result = ResourceBuilder.new(self, response, {}, {})
56
+ result.run
57
+ end
58
+ end
59
+
60
+ def activate
61
+ request = Request.new("/#{ space.id }/content_types/#{ id }/published", {}, id = nil, version: sys[:version])
62
+ response = request.put
63
+ result = ResourceBuilder.new(self, response, {}, {}).run
64
+ refresh_data(result)
65
+ end
66
+
67
+ def deactivate
68
+ request = Request.new("/#{ space.id }/content_types/#{ id }/published")
69
+ response = request.delete
70
+ result = ResourceBuilder.new(self, response, {}, {}).run
71
+ refresh_data(result)
72
+ end
73
+
74
+ def active?
75
+ !sys[:publishedAt].nil?
76
+ end
77
+
78
+ def self.create(space_id, attributes)
79
+ fields = fields_to_nested_properties_hash(attributes[:fields] || [])
80
+ request = Request.new("/#{ space_id }/content_types/#{ attributes[:id] || ''}", { name: attributes.fetch(:name),
81
+ description: attributes[:description],
82
+ fields: fields })
83
+ response = attributes[:id].nil? ? request.post : request.put
84
+ result = ResourceBuilder.new(self, response, {}, {}).run
85
+ Contentful::Management::Client.shared_instance.register_dynamic_entry(result.id, DynamicEntry.create(result)) if result.is_a?(self.class)
86
+ result
87
+ end
88
+
89
+ def update(attributes)
90
+ parameters = {}
91
+ parameters.merge!(name: (attributes[:name] || name))
92
+ parameters.merge!(description: (attributes[:description] || description))
93
+ parameters.merge!(fields: self.class.fields_to_nested_properties_hash(attributes[:fields] || fields))
94
+ request = Request.new("/#{ space.id }/content_types/#{ id }", parameters, id = nil, version: sys[:version])
95
+ response = request.put
96
+ result = ResourceBuilder.new(self, response, {}, {}).run
97
+ refresh_data(result)
98
+ end
99
+
100
+ def save
101
+ if id.nil?
102
+ new_instance = self.class.create(space.id, @properties)
103
+ refresh_data(new_instance)
104
+ else
105
+ update(@properties)
106
+ end
107
+ end
108
+
109
+ def merged_fields(new_field)
110
+ field_ids = []
111
+ merged_fields = fields.each_with_object([]) do |field, fields|
112
+ field.deep_merge!(new_field) if field.id == new_field.id
113
+ fields << field
114
+ field_ids << field.id
115
+ end
116
+ merged_fields << new_field unless field_ids.include?(new_field.id)
117
+ merged_fields
118
+ end
119
+
120
+ alias_method :orig_fields, :fields
121
+
122
+ def fields
123
+ fields = orig_fields
124
+
125
+ fields.instance_exec(self) do |content_type|
126
+
127
+ fields.define_singleton_method(:add) do |field|
128
+ content_type.update(fields: content_type.merged_fields(field))
129
+ end
130
+
131
+ fields.define_singleton_method(:create) do |params|
132
+ field = Contentful::Management::Field.new
133
+ field.id = params.fetch(:id)
134
+ field.name = params[:name] if params[:name]
135
+ field.type = params[:type] if params[:type]
136
+ field.link_type = params[:link_type] if params[:link_type]
137
+ field.required = params[:required] if params[:required]
138
+ field.localized = params[:localized] if params[:localized]
139
+ field.items = params[:items] if params[:items]
140
+ content_type.update(fields: content_type.merged_fields(field))
141
+ end
142
+
143
+ fields.define_singleton_method(:destroy) do |id|
144
+ fields = content_type.fields.select { |field| field.id != id }
145
+ content_type.update(fields: fields)
146
+ end
147
+
148
+ end
149
+
150
+ fields
151
+ end
152
+
153
+ #TODO refactor and move
154
+ def entries
155
+ entries = []
156
+ entries.instance_exec(self) do |content_type|
157
+ define_singleton_method(:create) do |params|
158
+ Entry.create(content_type, params)
159
+ end
160
+ define_singleton_method(:new) do
161
+ dynamic_entry_class = Contentful::Management::Client.shared_instance.register_dynamic_entry(content_type.id, DynamicEntry.create(content_type))
162
+ dynamic_entry = dynamic_entry_class.new
163
+ dynamic_entry.content_type = content_type
164
+ dynamic_entry
165
+ end
166
+ end
167
+ entries
168
+ end
169
+
170
+ private
171
+
172
+ def self.fields_to_nested_properties_hash(fields)
173
+ fields.map do |field|
174
+ field.properties.replace(field.properties_to_hash)
175
+ end
176
+ end
177
+
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,71 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require_relative 'resource'
3
+ require_relative 'resource/fields'
4
+ require_relative 'location'
5
+
6
+ module Contentful
7
+ module Management
8
+ class DynamicEntry < Contentful::Management::Entry
9
+ 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
18
+ }
19
+
20
+ def self.create(content_type)
21
+ unless content_type.is_a? ContentType
22
+ content_type = ContentType.new(content_type)
23
+ end
24
+
25
+ # TODO no support for an empty content type (undefined method map for Field)
26
+ fields_coercions = Hash[
27
+ content_type.fields.map do |field|
28
+ [field.id.to_sym, KNOWN_TYPES[field.type]]
29
+ end
30
+ ]
31
+
32
+ Class.new DynamicEntry do
33
+ content_type.fields.each do |field|
34
+ accessor_name = Support.snakify(field.id)
35
+ define_method accessor_name do
36
+ fields[field.id.to_sym]
37
+ end
38
+ define_method "#{ accessor_name }_with_locales" do
39
+ fields_for_query[field.id.to_sym]
40
+ end
41
+ define_method "#{ accessor_name }=" do |value|
42
+ fields[field.id.to_sym] = value
43
+ end
44
+ define_method "#{ accessor_name }_with_locales=" do |values|
45
+ values.each do |locale, value|
46
+ @fields[locale] = {} if @fields[locale].nil?
47
+ @fields[locale][field.id.to_sym] = value
48
+ end
49
+ end
50
+ end
51
+
52
+ define_singleton_method :fields_coercions do
53
+ fields_coercions
54
+ end
55
+
56
+ define_singleton_method :content_type do
57
+ content_type
58
+ end
59
+
60
+ define_singleton_method :to_s do
61
+ "Contentful::Management::DynamicEntry[#{ content_type.id }]"
62
+ end
63
+
64
+ define_singleton_method :inspect do
65
+ "Contentful::Management::DynamicEntry[#{ content_type.id }]"
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,213 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require_relative 'resource'
3
+ require_relative 'resource/fields'
4
+
5
+ module Contentful
6
+ module Management
7
+ class Entry
8
+
9
+ def self.fields_coercions
10
+ {}
11
+ end
12
+
13
+ include Contentful::Management::Resource
14
+ include Contentful::Management::Resource::SystemProperties
15
+ include Contentful::Management::Resource::Refresher
16
+ include Contentful::Management::Resource::Fields
17
+
18
+ attr_accessor :content_type
19
+
20
+ def self.all(space_id)
21
+ request = Request.new("/#{ space_id }/entries")
22
+ response = request.get
23
+ result = ResourceBuilder.new(Contentful::Management::Client.shared_instance, response, {}, {})
24
+ result.run
25
+ end
26
+
27
+ def self.find(space_id, entry_id)
28
+ request = Request.new("/#{ space_id }/entries/#{ entry_id }")
29
+ response = request.get
30
+ result = ResourceBuilder.new(Contentful::Management::Client.shared_instance, response, {}, {})
31
+ result.run
32
+ end
33
+
34
+ def self.create(content_type, attributes)
35
+ fields_for_create = if attributes[:fields] #create from initialized dynamic entry via save
36
+ tmp_entry = self.new
37
+ tmp_entry.instance_variable_set(:@fields, attributes.delete(:fields) || {})
38
+ Contentful::Management::Support.deep_hash_merge(tmp_entry.fields_for_query, tmp_entry.fields_from_attributes(attributes))
39
+ else
40
+ fields_with_locale content_type, attributes
41
+ end
42
+
43
+ request = Request.new("/#{ content_type.sys[:space].id }/entries/#{ attributes[:id] || ''}", { fields: fields_for_create }, nil, content_type_id: content_type.id)
44
+
45
+ response = attributes[:id].nil? ? request.post : request.put
46
+ result = ResourceBuilder.new(Contentful::Management::Client.shared_instance, response, {}, {})
47
+ result.run
48
+ end
49
+
50
+ def update(attributes)
51
+ fields_for_update = Contentful::Management::Support.deep_hash_merge(fields_for_query, fields_from_attributes(attributes))
52
+
53
+ request = Request.new("/#{ space.id }/entries/#{ id }", { fields: fields_for_update }, id = nil, version: sys[:version])
54
+ response = request.put
55
+ result = ResourceBuilder.new(Contentful::Management::Client.shared_instance, response, {}, {}).run
56
+ refresh_data(result)
57
+ end
58
+
59
+ def save
60
+ if id.nil?
61
+ new_instance = Contentful::Management::Entry.create(content_type, { fields: instance_variable_get(:@fields) })
62
+ refresh_data(new_instance)
63
+ else
64
+ update({})
65
+ end
66
+ end
67
+
68
+ def publish
69
+ request = Request.new("/#{ space.id }/entries/#{ id }/published", {}, id = nil, version: sys[:version])
70
+ response = request.put
71
+ result = ResourceBuilder.new(Contentful::Management::Client.shared_instance, response, {}, {}).run
72
+ refresh_data(result)
73
+ end
74
+
75
+ def unpublish
76
+ request = Request.new("/#{ space.id }/entries/#{ id }/published", {}, id = nil, version: sys[:version])
77
+ response = request.delete
78
+ result = ResourceBuilder.new(Contentful::Management::Client.shared_instance, response, {}, {}).run
79
+ refresh_data(result)
80
+ end
81
+
82
+ def archive
83
+ request = Request.new("/#{ space.id }/entries/#{ id }/archived", {}, id = nil, version: sys[:version])
84
+ response = request.put
85
+ result = ResourceBuilder.new(Contentful::Management::Client.shared_instance, response, {}, {}).run
86
+ refresh_data(result)
87
+ end
88
+
89
+ def unarchive
90
+ request = Request.new("/#{ space.id }/entries/#{ id }/archived", {}, id = nil, version: sys[:version])
91
+ response = request.delete
92
+ result = ResourceBuilder.new(Contentful::Management::Client.shared_instance, response, {}, {}).run
93
+ refresh_data(result)
94
+ end
95
+
96
+ def destroy
97
+ request = Request.new("/#{ space.id }/entries/#{ id }")
98
+ response = request.delete
99
+ if response.status == :no_content
100
+ return true
101
+ else
102
+ result = ResourceBuilder.new(Contentful::Management::Client.shared_instance, response, {}, {})
103
+ result.run
104
+ end
105
+ end
106
+
107
+ def published?
108
+ !sys[:publishedAt].nil?
109
+ end
110
+
111
+ def archived?
112
+ !sys[:archivedAt].nil?
113
+ end
114
+
115
+ def locale
116
+ sys[:locale] || default_locale
117
+ end
118
+
119
+ def fields_for_query
120
+ raw_fields = self.instance_variable_get(:@fields)
121
+ fields_names = raw_fields.first[1].keys
122
+ fields_names.each_with_object({}) do |field_name, results|
123
+ results[field_name] = raw_fields.each_with_object({}) do |(locale, fields), field_results|
124
+ # field_results[locale] = fields[field_name]
125
+ field_results[locale] = parse_update_attribute(fields[field_name])
126
+ end
127
+ end
128
+ end
129
+
130
+ def fields_from_attributes(attributes)
131
+ attributes.each do |id, value|
132
+ attributes[id] = { locale => parse_update_attribute(value) }
133
+ end
134
+ end
135
+
136
+ private
137
+
138
+ def self.parse_attribute_with_field(attribute, field)
139
+ case field.type
140
+ when ContentType::LINK then
141
+ { sys: { type: field.type, linkType: field.link_type, id: attribute.id }}
142
+ when ContentType::ARRAY then
143
+ parse_fields_array(attribute)
144
+ when ContentType::LOCATION then
145
+ { lat: attribute.properties[:lat], lon: attribute.properties[:lon]}
146
+ else
147
+ attribute
148
+ end
149
+ end
150
+
151
+ #TODO refactor
152
+ def parse_update_attribute(attribute)
153
+ if attribute.is_a? Asset
154
+ { sys: { type: 'Link', linkType: 'Asset', id: attribute.id } }
155
+ elsif attribute.is_a? Entry
156
+ { sys: { type: 'Link', linkType: 'Entry', id: attribute.id } }
157
+ elsif attribute.is_a? Location
158
+ { lat: attribute.properties[:lat], lon: attribute.properties[:lon] }
159
+ elsif attribute.is_a? ::Array
160
+ parse_update_fields_array(attribute)
161
+ else
162
+ attribute
163
+ end
164
+ end
165
+
166
+ #TODO refactor
167
+ def parse_update_fields_array(attributes)
168
+ type = attributes.first.class.to_s
169
+ unless type == 'String'
170
+ attributes.each_with_object([]) do |attr, arr|
171
+ arr << case type
172
+ when /Entry/ then
173
+ { sys: { type: 'Link', linkType: 'Entry', id: attr.id } }
174
+ when /Asset/ then
175
+ { sys: { type: 'Link', linkType: 'Asset', id: attr.id } }
176
+ end
177
+ end
178
+ else
179
+ attributes
180
+ end
181
+ end
182
+
183
+ def self.parse_fields_array(attributes)
184
+ if attributes.is_a? ::Array
185
+ type = attributes.first.class
186
+ attributes.each_with_object([]) do |attr, arr|
187
+ arr << case type.to_s
188
+ when /Entry/ then
189
+ { sys: { type: 'Link', linkType: 'Entry', id: attr.id } }
190
+ when /Asset/ then
191
+ { sys: { type: 'Link', linkType: 'Asset', id: attr.id } }
192
+ end
193
+ end
194
+ else
195
+ [attributes]
196
+ end
197
+ end
198
+
199
+ def self.fields_with_locale(content_type, attributes)
200
+ locale = content_type.sys[:space].default_locale
201
+ fields = content_type.properties[:fields]
202
+ field_names = fields.map { |f| f.id.to_sym }
203
+ attributes.keep_if { |key| field_names.include?(key) }
204
+
205
+ attributes.each do |id, value|
206
+ field = fields.select { |f| f.id.to_sym == id.to_sym }.first
207
+ attributes[id] = { locale => parse_attribute_with_field(value, field) }
208
+ end
209
+ end
210
+
211
+ end
212
+ end
213
+ end