cloudinary 1.9.1 → 1.20.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 (72) hide show
  1. checksums.yaml +5 -5
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +42 -0
  3. data/.github/ISSUE_TEMPLATE/feature_request.md +21 -0
  4. data/.github/pull_request_template.md +24 -0
  5. data/.gitignore +7 -1
  6. data/.travis.yml +15 -8
  7. data/CHANGELOG.md +261 -0
  8. data/README.md +3 -0
  9. data/Rakefile +3 -45
  10. data/cloudinary.gemspec +27 -20
  11. data/lib/active_storage/blob_key.rb +20 -0
  12. data/lib/active_storage/service/cloudinary_service.rb +249 -0
  13. data/lib/cloudinary.rb +53 -63
  14. data/lib/cloudinary/account_api.rb +231 -0
  15. data/lib/cloudinary/account_config.rb +30 -0
  16. data/lib/cloudinary/api.rb +228 -71
  17. data/lib/cloudinary/auth_token.rb +10 -4
  18. data/lib/cloudinary/base_api.rb +79 -0
  19. data/lib/cloudinary/base_config.rb +70 -0
  20. data/lib/cloudinary/cache.rb +38 -0
  21. data/lib/cloudinary/cache/breakpoints_cache.rb +31 -0
  22. data/lib/cloudinary/cache/key_value_cache_adapter.rb +25 -0
  23. data/lib/cloudinary/cache/rails_cache_adapter.rb +34 -0
  24. data/lib/cloudinary/cache/storage/rails_cache_storage.rb +5 -0
  25. data/lib/cloudinary/carrier_wave.rb +4 -2
  26. data/lib/cloudinary/carrier_wave/remote.rb +3 -2
  27. data/lib/cloudinary/carrier_wave/storage.rb +2 -1
  28. data/lib/cloudinary/{controller.rb → cloudinary_controller.rb} +3 -5
  29. data/lib/cloudinary/config.rb +43 -0
  30. data/lib/cloudinary/helper.rb +77 -7
  31. data/lib/cloudinary/migrator.rb +3 -1
  32. data/lib/cloudinary/railtie.rb +7 -3
  33. data/lib/cloudinary/responsive.rb +111 -0
  34. data/lib/cloudinary/uploader.rb +67 -15
  35. data/lib/cloudinary/utils.rb +324 -54
  36. data/lib/cloudinary/version.rb +1 -1
  37. data/lib/cloudinary/video_helper.rb +96 -22
  38. data/lib/tasks/cloudinary/fetch_assets.rake +48 -0
  39. data/lib/tasks/{cloudinary.rake → cloudinary/sync_static.rake} +0 -0
  40. data/tools/allocate_test_cloud.sh +9 -0
  41. data/tools/get_test_cloud.sh +9 -0
  42. data/tools/update_version +220 -0
  43. data/vendor/assets/javascripts/cloudinary/jquery.cloudinary.js +51 -13
  44. data/vendor/assets/javascripts/cloudinary/jquery.fileupload.js +24 -4
  45. data/vendor/assets/javascripts/cloudinary/jquery.ui.widget.js +741 -561
  46. data/vendor/assets/javascripts/cloudinary/load-image.all.min.js +1 -1
  47. metadata +92 -67
  48. data/spec/access_control_spec.rb +0 -99
  49. data/spec/api_spec.rb +0 -545
  50. data/spec/archive_spec.rb +0 -129
  51. data/spec/auth_token_spec.rb +0 -79
  52. data/spec/cloudinary_helper_spec.rb +0 -190
  53. data/spec/cloudinary_spec.rb +0 -32
  54. data/spec/data/sync_static/app/assets/javascripts/1.coffee +0 -1
  55. data/spec/data/sync_static/app/assets/javascripts/1.js +0 -1
  56. data/spec/data/sync_static/app/assets/stylesheets/1.css +0 -3
  57. data/spec/docx.docx +0 -0
  58. data/spec/favicon.ico +0 -0
  59. data/spec/logo.png +0 -0
  60. data/spec/rake_spec.rb +0 -160
  61. data/spec/sample_asset_file.tsv +0 -4
  62. data/spec/search_spec.rb +0 -109
  63. data/spec/spec_helper.rb +0 -245
  64. data/spec/storage_spec.rb +0 -44
  65. data/spec/streaminig_profiles_api_spec.rb +0 -74
  66. data/spec/support/helpers/temp_file_helpers.rb +0 -22
  67. data/spec/support/shared_contexts/rake.rb +0 -19
  68. data/spec/uploader_spec.rb +0 -363
  69. data/spec/utils_methods_spec.rb +0 -54
  70. data/spec/utils_spec.rb +0 -906
  71. data/spec/video_tag_spec.rb +0 -251
  72. data/spec/video_url_spec.rb +0 -164
@@ -0,0 +1,231 @@
1
+ class Cloudinary::AccountApi
2
+ extend Cloudinary::BaseApi
3
+
4
+ # Creates a new sub-account. Any users that have access to all sub-accounts will also automatically have access to the
5
+ # new sub-account.
6
+ # @param [String] name The display name as shown in the management console
7
+ # @param [String] cloud_name A case-insensitive cloud name comprised of alphanumeric and underscore characters.
8
+ # Generates an error if the specified cloud name is not unique across all Cloudinary accounts.
9
+ # Note: Once created, the name can only be changed for accounts with fewer than 1000 assets.
10
+ # @param [Object] custom_attributes Any custom attributes you want to associate with the sub-account
11
+ # @param [Boolean] enabled Whether to create the account as enabled (default is enabled)
12
+ # @param [String] base_account ID of sub-account from which to copy settings
13
+ # @param [Object] options additional options
14
+ def self.create_sub_account(name, cloud_name = nil, custom_attributes = {}, enabled = nil, base_account = nil, options = {})
15
+ params = {
16
+ name: name,
17
+ cloud_name: cloud_name,
18
+ custom_attributes: custom_attributes,
19
+ enabled: enabled,
20
+ base_sub_account_id: base_account
21
+ }
22
+
23
+ call_account_api(:post, 'sub_accounts', params, options.merge(content_type: :json))
24
+ end
25
+
26
+ # Updates the specified details of the sub-account.
27
+ # @param [String] sub_account_id The ID of the sub-account.
28
+ # @param [String] name The display name as shown in the management console
29
+ # @param [String] cloud_name A case-insensitive cloud name comprised of alphanumeric and underscore characters.
30
+ # Generates an error if the specified cloud name is not unique across all Cloudinary accounts.
31
+ # Note: Once created, the name can only be changed for accounts with fewer than 1000 assets.
32
+ # @param [Object] custom_attributes Any custom attributes you want to associate with the sub-account, as a map/hash
33
+ # of key/value pairs.
34
+ # @param [Boolean] enabled Whether the sub-account is enabled.
35
+ # @param [Object] options additional options
36
+ def self.update_sub_account(sub_account_id, name = nil, cloud_name = nil, custom_attributes = nil, enabled = nil, options = {})
37
+ params = {
38
+ name: name,
39
+ cloud_name: cloud_name,
40
+ custom_attributes: custom_attributes,
41
+ enabled: enabled
42
+ }
43
+
44
+ call_account_api(:put, ['sub_accounts', sub_account_id], params, options.merge(content_type: :json))
45
+ end
46
+
47
+ # Lists sub-accounts.
48
+ # @param [Boolean] enabled Whether to only return enabled sub-accounts (true) or disabled accounts (false).
49
+ # Default: all accounts are returned (both enabled and disabled).
50
+ # @param [Array<String>] ids A list of up to 100 sub-account IDs. When provided, other parameters are ignored.
51
+ # @param [String] prefix Returns accounts where the name begins with the specified case-insensitive string.
52
+ # @param [Object] options additional options
53
+ def self.sub_accounts(enabled = nil, ids = [], prefix = nil, options = {})
54
+ params = {
55
+ enabled: enabled,
56
+ ids: ids,
57
+ prefix: prefix
58
+ }
59
+
60
+ call_account_api(:get, 'sub_accounts', params, options.merge(content_type: :json))
61
+ end
62
+
63
+ # Retrieves the details of the specified sub-account.
64
+ # @param [String] sub_account_id The ID of the sub-account.
65
+ # @param [Object] options additional options
66
+ def self.sub_account(sub_account_id, options = {})
67
+ call_account_api(:get, ['sub_accounts', sub_account_id], {}, options.merge(content_type: :json))
68
+ end
69
+
70
+ # Deletes the specified sub-account. Supported only for accounts with fewer than 1000 assets.
71
+ # @param [String] sub_account_id The ID of the sub-account.
72
+ # @param [Object] options additional options
73
+ def self.delete_sub_account(sub_account_id, options = {})
74
+ call_account_api(:delete, ['sub_accounts', sub_account_id], {}, options)
75
+ end
76
+
77
+ # Creates a new user in the account.
78
+ # @param [String] name The name of the user.
79
+ # @param [String] email A unique email address, which serves as the login name and notification address.
80
+ # @param [String] role The role to assign. Possible values: master_admin, admin, billing, technical_admin, reports,
81
+ # media_library_admin, media_library_user
82
+ # @param [Array<String>] sub_account_ids The list of sub-account IDs that this user can access.
83
+ # Note: This parameter is ignored if the role is specified as master_admin.
84
+ # @param [Object] options additional options
85
+ def self.create_user(name, email, role, sub_account_ids = [], options = {})
86
+ params = {
87
+ name: name,
88
+ email: email,
89
+ role: role,
90
+ sub_account_ids: sub_account_ids
91
+ }
92
+
93
+ call_account_api(:post, 'users', params, options.merge(content_type: :json))
94
+ end
95
+
96
+ # Deletes an existing user.
97
+ # @param [String] user_id The ID of the user to delete.
98
+ # @param [Object] options additional options
99
+ def self.delete_user(user_id, options = {})
100
+ call_account_api(:delete, ['users', user_id], {}, options)
101
+ end
102
+
103
+ # Updates the details of the specified user.
104
+ # @param [String] user_id The ID of the user to update.
105
+ # @param [String] name The name of the user.
106
+ # @param [String] email A unique email address, which serves as the login name and notification address.
107
+ # @param [String] role The role to assign. Possible values: master_admin, admin, billing, technical_admin, reports,
108
+ # media_library_admin, media_library_user
109
+ # @param [Array<String>] sub_account_ids The list of sub-account IDs that this user can access.
110
+ # Note: This parameter is ignored if the role is specified as master_admin.
111
+ # @param [Object] options additional options
112
+ def self.update_user(user_id, name = nil, email = nil, role = nil, sub_account_ids = nil, options = {})
113
+ params = {
114
+ name: name,
115
+ email: email,
116
+ role: role,
117
+ sub_account_ids: sub_account_ids
118
+ }
119
+
120
+ call_account_api(:put, ['users', user_id], params, options.merge(content_type: :json))
121
+ end
122
+
123
+ # Returns the user with the specified ID.
124
+ # @param [String] user_id The ID of the user.
125
+ # @param [Object] options additional options
126
+ def self.user(user_id, options = {})
127
+ call_account_api(:get, ['users', user_id], {}, options.merge(content_type: :json))
128
+ end
129
+
130
+ # Lists users in the account.
131
+ # @param [Boolean] pending Whether to only return pending users. Default: all users
132
+ # @param [Array<String>] user_ids A list of up to 100 user IDs. When provided, other parameters are ignored.
133
+ # @param [String] prefix Returns users where the name or email address begins with the specified case-insensitive string.
134
+ # @param [String] sub_account_id Only returns users who have access to the specified account.
135
+ # @param [Object] options additional options
136
+ def self.users(pending = nil, user_ids = [], prefix = nil, sub_account_id = nil, options = {})
137
+ params = if user_ids && user_ids.count > 0
138
+ {
139
+ ids: user_ids
140
+ }
141
+ else
142
+ {
143
+ prefix: prefix,
144
+ sub_account_id: sub_account_id,
145
+ pending: pending
146
+ }
147
+ end
148
+
149
+ call_account_api(:get, 'users', params, options.merge(content_type: :json))
150
+ end
151
+
152
+ # Creates a new user group.
153
+ # @param [String] name The name for the user group.
154
+ # @param [Object] options additional options
155
+ def self.create_user_group(name, options = {})
156
+ params = {
157
+ name: name
158
+ }
159
+
160
+ call_account_api(:post, 'user_groups', params, options.merge(content_type: :json))
161
+ end
162
+
163
+ # Updates the specified user group.
164
+ # @param [String] group_id The ID of the user group to update.
165
+ # @param [String] name The name for the user group.
166
+ # @param [Object] options additional options
167
+ def self.update_user_group(group_id, name, options = {})
168
+ params = {
169
+ name: name
170
+ }
171
+
172
+ call_account_api(:put, ['user_groups', group_id], params, options.merge(content_type: :json))
173
+ end
174
+
175
+ # Adds a user to a group with the specified ID.
176
+ # @param [String] group_id The ID of the user group.
177
+ # @param [String] user_id The ID of the user.
178
+ # @param [Object] options additional options
179
+ def self.add_user_to_group(group_id, user_id, options = {})
180
+ call_account_api(:post, ['user_groups', group_id, 'users', user_id], {}, options.merge(content_type: :json))
181
+ end
182
+
183
+ # Removes a user from a group with the specified ID.
184
+ # @param [String] group_id The ID of the user group.
185
+ # @param [String] user_id The ID of the user.
186
+ # @param [Object] options additional options
187
+ def self.remove_user_from_group(group_id, user_id, options = {})
188
+ call_account_api(:delete, ['user_groups', group_id, 'users', user_id], {}, options.merge(content_type: :json))
189
+ end
190
+
191
+ # Deletes the user group with the specified ID.
192
+ # @param [String] group_id The ID of the user group to delete.
193
+ # @param [Object] options additional options
194
+ def self.delete_user_group(group_id, options = {})
195
+ call_account_api(:delete, ['user_groups', group_id], {}, options)
196
+ end
197
+
198
+ # Lists user groups in the account.
199
+ # @param [Object] options additional options
200
+ def self.user_groups(options = {})
201
+ call_account_api(:get, 'user_groups', {}, options.merge(content_type: :json))
202
+ end
203
+
204
+ # Retrieves the details of the specified user group.
205
+ # @param [String] group_id The ID of the user group to retrieve.
206
+ # @param [Object] options additional options
207
+ def self.user_group(group_id, options = {})
208
+ call_account_api(:get, ['user_groups', group_id], {}, options.merge(content_type: :json))
209
+ end
210
+
211
+ # Lists users in the specified user group.
212
+ # @param [String] group_id The ID of the user group.
213
+ # @param [Object] options additional options
214
+ def self.user_group_users(group_id, options = {})
215
+ call_account_api(:get, ['user_groups', group_id, 'users'], {}, options.merge(content_type: :json))
216
+ end
217
+
218
+ def self.call_account_api(method, uri, params, options)
219
+ account_id = options[:account_id] || Cloudinary.account_config.account_id || raise('Must supply account_id')
220
+ api_key = options[:provisioning_api_key] || Cloudinary.account_config.provisioning_api_key || raise('Must supply provisioning api_key')
221
+ api_secret = options[:provisioning_api_secret] || Cloudinary.account_config.provisioning_api_secret || raise('Must supply provisioning api_secret')
222
+
223
+ params.reject! { |_, v| v.nil? }
224
+
225
+ call_cloudinary_api(method, uri, api_key, api_secret, params, options) do |cloudinary, inner_uri|
226
+ [cloudinary, 'v1_1', 'provisioning', 'accounts', account_id, inner_uri]
227
+ end
228
+ end
229
+
230
+ private_class_method :call_account_api
231
+ end
@@ -0,0 +1,30 @@
1
+ module Cloudinary
2
+ module AccountConfig
3
+ include BaseConfig
4
+
5
+ ENV_URL = "CLOUDINARY_ACCOUNT_URL"
6
+ SCHEME = "account"
7
+
8
+ def load_config_from_env
9
+ load_from_url(ENV[ENV_URL]) if ENV[ENV_URL]
10
+ end
11
+
12
+ private
13
+
14
+ def env_url
15
+ ENV_URL
16
+ end
17
+
18
+ def expected_scheme
19
+ SCHEME
20
+ end
21
+
22
+ def config_from_parsed_url(parsed_url)
23
+ {
24
+ "account_id" => parsed_url.host,
25
+ "provisioning_api_key" => parsed_url.user,
26
+ "provisioning_api_secret" => parsed_url.password
27
+ }
28
+ end
29
+ end
30
+ end
@@ -1,37 +1,28 @@
1
- require 'rest_client'
2
- require 'json'
3
-
4
1
  class Cloudinary::Api
5
- class Error < CloudinaryException; end
6
- class NotFound < Error; end
7
- class NotAllowed < Error; end
8
- class AlreadyExists < Error; end
9
- class RateLimited < Error; end
10
- class BadRequest < Error; end
11
- class GeneralError < Error; end
12
- class AuthorizationRequired < Error; end
13
-
14
- class Response < Hash
15
- attr_reader :rate_limit_reset_at, :rate_limit_remaining, :rate_limit_allowed
16
-
17
- def initialize(response=nil)
18
- if response
19
- # This sets the instantiated self as the response Hash
20
- update Cloudinary::Api.parse_json_response response
21
-
22
- @rate_limit_allowed = response.headers[:x_featureratelimit_limit].to_i
23
- @rate_limit_reset_at = Time.parse(response.headers[:x_featureratelimit_reset])
24
- @rate_limit_remaining = response.headers[:x_featureratelimit_remaining].to_i
25
- end
26
- end
27
- end
2
+ extend Cloudinary::BaseApi
28
3
 
29
4
  def self.ping(options={})
30
5
  call_api(:get, "ping", {}, options)
31
6
  end
32
7
 
8
+ # Gets account usage details
9
+ #
10
+ # Get a report on the status of your Cloudinary account usage details, including
11
+ # storage, bandwidth, requests, number of resources, and add-on usage.
12
+ # Note that numbers are updated periodically.
13
+ #
14
+ # @see https://cloudinary.com/documentation/admin_api#get_account_usage_details Get account usage details
15
+ #
16
+ # @param [Hash] options Additional options
17
+ # @return [Cloudinary::Api::Response]
18
+ # @raise [Cloudinary::Api:Error]
33
19
  def self.usage(options={})
34
- call_api(:get, "usage", {}, options)
20
+ uri = 'usage'
21
+ date = options[:date]
22
+
23
+ uri += "/#{Cloudinary::Utils.to_usage_api_date_format(date)}" unless date.nil?
24
+
25
+ call_api(:get, uri, {}, options)
35
26
  end
36
27
 
37
28
  def self.resource_types(options={})
@@ -78,14 +69,29 @@ class Cloudinary::Api
78
69
  resource_type = options[:resource_type] || "image"
79
70
  type = options[:type] || "upload"
80
71
  uri = "resources/#{resource_type}/#{type}/#{public_id}"
81
- call_api(:get, uri, only(options, :colors, :exif, :faces, :image_metadata, :pages, :phash, :coordinates, :max_results), options)
72
+ call_api(:get, uri,
73
+ only(options,
74
+ :cinemagraph_analysis,
75
+ :colors,
76
+ :coordinates,
77
+ :exif,
78
+ :faces,
79
+ :image_metadata,
80
+ :max_results,
81
+ :pages,
82
+ :phash,
83
+ :quality_analysis,
84
+ :derived_next_cursor,
85
+ :accessibility_analysis,
86
+ :versions
87
+ ), options)
82
88
  end
83
89
 
84
90
  def self.restore(public_ids, options={})
85
91
  resource_type = options[:resource_type] || "image"
86
92
  type = options[:type] || "upload"
87
93
  uri = "resources/#{resource_type}/#{type}/restore"
88
- call_api(:post, uri, { :public_ids => public_ids }, options)
94
+ call_api(:post, uri, { :public_ids => public_ids, :versions => options[:versions] }, options)
89
95
  end
90
96
 
91
97
  def self.update(public_id, options={})
@@ -94,19 +100,20 @@ class Cloudinary::Api
94
100
  uri = "resources/#{resource_type}/#{type}/#{public_id}"
95
101
  update_options = {
96
102
  :access_control => Cloudinary::Utils.json_array_param(options[:access_control]),
97
- :tags => options[:tags] && Cloudinary::Utils.build_array(options[:tags]).join(","),
103
+ :auto_tagging => options[:auto_tagging] && options[:auto_tagging].to_f,
104
+ :background_removal => options[:background_removal],
105
+ :categorization => options[:categorization],
98
106
  :context => Cloudinary::Utils.encode_context(options[:context]),
99
- :face_coordinates => Cloudinary::Utils.encode_double_array(options[:face_coordinates]),
100
107
  :custom_coordinates => Cloudinary::Utils.encode_double_array(options[:custom_coordinates]),
108
+ :detection => options[:detection],
109
+ :face_coordinates => Cloudinary::Utils.encode_double_array(options[:face_coordinates]),
101
110
  :moderation_status => options[:moderation_status],
102
- :raw_convert => options[:raw_convert],
111
+ :notification_url => options[:notification_url],
112
+ :quality_override => options[:quality_override],
103
113
  :ocr => options[:ocr],
104
- :categorization => options[:categorization],
105
- :detection => options[:detection],
114
+ :raw_convert => options[:raw_convert],
106
115
  :similarity_search => options[:similarity_search],
107
- :background_removal => options[:background_removal],
108
- :auto_tagging => options[:auto_tagging] && options[:auto_tagging].to_f,
109
- :notification_url => options[:notification_url]
116
+ :tags => options[:tags] && Cloudinary::Utils.build_array(options[:tags]).join(",")
110
117
  }
111
118
  call_api(:post, uri, update_options, options)
112
119
  end
@@ -214,11 +221,21 @@ class Cloudinary::Api
214
221
  end
215
222
 
216
223
  def self.root_folders(options={})
217
- call_api(:get, "folders", {}, options)
224
+ params = only(options, :max_results, :next_cursor)
225
+ call_api(:get, "folders", params, options)
218
226
  end
219
227
 
220
228
  def self.subfolders(of_folder_path, options={})
221
- call_api(:get, "folders/#{of_folder_path}", {}, options)
229
+ params = only(options, :max_results, :next_cursor)
230
+ call_api(:get, "folders/#{of_folder_path}", params, options)
231
+ end
232
+
233
+ def self.delete_folder(path, options={})
234
+ call_api(:delete, "folders/#{path}", {}, options)
235
+ end
236
+
237
+ def self.create_folder(folder_name, options={})
238
+ call_api(:post, "folders/#{folder_name}", {}, options)
222
239
  end
223
240
 
224
241
  def self.upload_mappings(options={})
@@ -311,40 +328,165 @@ class Cloudinary::Api
311
328
  update_resources_access_mode(access_mode, :public_ids, public_ids, options)
312
329
  end
313
330
 
331
+ def self.get_breakpoints(public_id, options)
332
+ local_options = options.clone
333
+ base_transformation = Cloudinary::Utils.generate_transformation_string(local_options)
334
+ srcset = local_options[:srcset]
335
+ breakpoints = [:min_width, :max_width, :bytes_step, :max_images].map {|k| srcset[k]}.join('_')
336
+
337
+
338
+ local_options[:transformation] = [base_transformation, width: "auto:breakpoints_#{breakpoints}:json"]
339
+ json_url = Cloudinary::Utils.cloudinary_url public_id, local_options
340
+ call_json_api('GET', json_url, {}, 60, {})
341
+ end
342
+
343
+ # Returns a list of all metadata field definitions.
344
+ #
345
+ # @see https://cloudinary.com/documentation/admin_api#get_metadata_fields Get metadata fields API reference
346
+ #
347
+ # @param [Hash] options Additional options
348
+ # @return [Cloudinary::Api::Response]
349
+ # @raise [Cloudinary::Api::Error]
350
+ def self.list_metadata_fields(options = {})
351
+ call_metadata_api(:get, [], {}, options)
352
+ end
353
+
354
+ # Gets a metadata field by external id.
355
+ #
356
+ # @see https://cloudinary.com/documentation/admin_api#get_a_metadata_field_by_external_id Get metadata field by external ID API reference
357
+ #
358
+ # @param [String] field_external_id The ID of the metadata field to retrieve
359
+ # @param [Hash] options Additional options
360
+ # @return [Cloudinary::Api::Response]
361
+ # @raise [Cloudinary::Api::Error]
362
+ def self.metadata_field_by_field_id(field_external_id, options = {})
363
+ uri = [field_external_id]
364
+
365
+ call_metadata_api(:get, uri, {}, options)
366
+ end
367
+
368
+ # Creates a new metadata field definition.
369
+ #
370
+ # @see https://cloudinary.com/documentation/admin_api#create_a_metadata_field Create metadata field API reference
371
+ #
372
+ # @param [Hash] field The field to add
373
+ # @param [Hash] options Additional options
374
+ # @return [Cloudinary::Api::Response]
375
+ # @raise [Cloudinary::Api::Error]
376
+ def self.add_metadata_field(field, options = {})
377
+ params = only(field, :type, :external_id, :label, :mandatory, :default_value, :validation, :datasource)
378
+
379
+ call_metadata_api(:post, [], params, options)
380
+ end
381
+
382
+ # Updates a metadata field by external id.
383
+ #
384
+ # Updates a metadata field definition (partially, no need to pass the entire object) passed as JSON data.
385
+ # See https://cloudinary.com/documentation/admin_api#generic_structure_of_a_metadata_field for the generic structure
386
+ # of a metadata field.
387
+ #
388
+ # @see https://cloudinary.com/documentation/admin_api#update_a_metadata_field_by_external_id Update metadata field API reference
389
+ #
390
+ # @param [String] field_external_id The id of the metadata field to update
391
+ # @param [Hash] field The field definition
392
+ # @param [Hash] options Additional options
393
+ # @return [Cloudinary::Api::Response]
394
+ # @raise [Cloudinary::Api::Error]
395
+ def self.update_metadata_field(field_external_id, field, options = {})
396
+ uri = [field_external_id]
397
+ params = only(field, :label, :mandatory, :default_value, :validation)
398
+
399
+ call_metadata_api(:put, uri, params, options)
400
+ end
401
+
402
+ # Deletes a metadata field definition.
403
+ #
404
+ # The field should no longer be considered a valid candidate for all other endpoints.
405
+ #
406
+ # @see https://cloudinary.com/documentation/admin_api#delete_a_metadata_field_by_external_id Delete metadata field API reference
407
+ #
408
+ # @param [String] field_external_id The external id of the field to delete
409
+ # @param [Hash] options Additional options
410
+ # @return [Cloudinary::Api::Response] A hash with a "message" key. "ok" value indicates a successful deletion
411
+ # @raise [Cloudinary::Api::Error]
412
+ def self.delete_metadata_field(field_external_id, options = {})
413
+ uri = [field_external_id]
414
+
415
+ call_metadata_api(:delete, uri, {}, options)
416
+ end
417
+
418
+ # Deletes entries in a metadata field datasource.
419
+ #
420
+ # Deletes (blocks) the datasource entries for a specified metadata field definition. Sets the state of the
421
+ # entries to inactive. This is a soft delete, the entries still exist under the hood and can be activated
422
+ # again with the restore datasource entries method.
423
+ #
424
+ # @see https://cloudinary.com/documentation/admin_api#delete_entries_in_a_metadata_field_datasource Delete entries in a metadata field datasource API reference
425
+ #
426
+ # @param [String] field_external_id The id of the field to update
427
+ # @param [Array] entries_external_id The ids of all the entries to delete from the datasource
428
+ # @param [Hash] options Additional options
429
+ # @return [Cloudinary::Api::Response] The remaining datasource entries
430
+ # @raise [Cloudinary::Api::Error]
431
+ def self.delete_datasource_entries(field_external_id, entries_external_id, options = {})
432
+ uri = [field_external_id, "datasource"]
433
+ params = {:external_ids => entries_external_id }
434
+
435
+ call_metadata_api(:delete, uri, params, options)
436
+ end
437
+
438
+ # Updates a metadata field datasource.
439
+ #
440
+ # Updates the datasource of a supported field type (currently only enum and set), passed as JSON data. The
441
+ # update is partial: datasource entries with an existing external_id will be updated and entries with new
442
+ # external_id’s (or without external_id’s) will be appended.
443
+ #
444
+ # @see https://cloudinary.com/documentation/admin_api#update_a_metadata_field_datasource Update a metadata field datasource API reference
445
+ #
446
+ # @param [String] field_external_id The external id of the field to update
447
+ # @param [Array] entries_external_id
448
+ # @param [Hash] options Additional options
449
+ # @return [Cloudinary::Api::Response]
450
+ # @raise [Cloudinary::Api::Error]
451
+ def self.update_metadata_field_datasource(field_external_id, entries_external_id, options = {})
452
+ uri = [field_external_id, "datasource"]
453
+
454
+ params = entries_external_id.each_with_object({:values => [] }) do |item, hash|
455
+ item = only(item, :external_id, :value)
456
+ hash[:values ] << item if item.present?
457
+ end
458
+
459
+ call_metadata_api(:put, uri, params, options)
460
+ end
461
+
462
+ # Restores entries in a metadata field datasource.
463
+ #
464
+ # Restores (unblocks) any previously deleted datasource entries for a specified metadata field definition.
465
+ # Sets the state of the entries to active.
466
+ #
467
+ # @see https://cloudinary.com/documentation/admin_api#restore_entries_in_a_metadata_field_datasource Restore entries in a metadata field datasource API reference
468
+ #
469
+ # @param [String] field_external_id The ID of the metadata field
470
+ # @param [Array] entries_external_ids An array of IDs of datasource entries to restore (unblock)
471
+ # @param [Hash] options Additional options
472
+ # @return [Cloudinary::Api::Response]
473
+ # @raise [Cloudinary::Api::Error]
474
+ def self.restore_metadata_field_datasource(field_external_id, entries_external_ids, options = {})
475
+ uri = [field_external_id, "datasource_restore"]
476
+ params = {:external_ids => entries_external_ids }
477
+
478
+ call_metadata_api(:post, uri, params, options)
479
+ end
480
+
314
481
  protected
315
482
 
316
483
  def self.call_api(method, uri, params, options)
317
- cloudinary = options[:upload_prefix] || Cloudinary.config.upload_prefix || "https://api.cloudinary.com"
318
- cloud_name = options[:cloud_name] || Cloudinary.config.cloud_name || raise("Must supply cloud_name")
319
- api_key = options[:api_key] || Cloudinary.config.api_key || raise("Must supply api_key")
320
- api_secret = options[:api_secret] || Cloudinary.config.api_secret || raise("Must supply api_secret")
321
- timeout = options[:timeout] || Cloudinary.config.timeout || 60
322
- api_url = [cloudinary, "v1_1", cloud_name, uri].join("/")
323
- # Add authentication
324
- api_url.sub!(%r(^(https?://)), "\\1#{api_key}:#{api_secret}@")
325
-
326
- headers = { "User-Agent" => Cloudinary::USER_AGENT }
327
- if options[:content_type]== :json
328
- payload = params.to_json
329
- headers.merge!("Content-Type"=> 'application/json', "Accept"=> 'application/json')
330
- else
331
- payload = params.reject { |k, v| v.nil? || v=="" }
332
- end
333
- RestClient::Request.execute(:method => method, :url => api_url, :payload => payload, :timeout => timeout, :headers => headers) do
334
- |response, request, tmpresult|
335
- return Response.new(response) if response.code == 200
336
- exception_class = case response.code
337
- when 400 then BadRequest
338
- when 401 then AuthorizationRequired
339
- when 403 then NotAllowed
340
- when 404 then NotFound
341
- when 409 then AlreadyExists
342
- when 420 then RateLimited
343
- when 500 then GeneralError
344
- else raise GeneralError.new("Server returned unexpected status code - #{response.code} - #{response.body}")
345
- end
346
- json = parse_json_response(response)
347
- raise exception_class.new(json["error"]["message"])
484
+ cloud_name = options[:cloud_name] || Cloudinary.config.cloud_name || raise('Must supply cloud_name')
485
+ api_key = options[:api_key] || Cloudinary.config.api_key || raise('Must supply api_key')
486
+ api_secret = options[:api_secret] || Cloudinary.config.api_secret || raise('Must supply api_secret')
487
+
488
+ call_cloudinary_api(method, uri, api_key, api_secret, params, options) do |cloudinary, inner_uri|
489
+ [cloudinary, 'v1_1', cloud_name, inner_uri]
348
490
  end
349
491
  end
350
492
 
@@ -355,6 +497,22 @@ class Cloudinary::Api
355
497
  raise GeneralError.new("Error parsing server response (#{response.code}) - #{response.body}. Got - #{e}")
356
498
  end
357
499
 
500
+ # Protected function that assists with performing an API call to the metadata_fields part of the Admin API.
501
+ #
502
+ # @protected
503
+ # @param [Symbol] method The HTTP method. Valid methods: get, post, put, delete
504
+ # @param [Array] uri REST endpoint of the API (without 'metadata_fields')
505
+ # @param [Hash] params Query/body parameters passed to the method
506
+ # @param [Hash] options Additional options. Can be an override of the configuration, headers, etc.
507
+ # @return [Cloudinary::Api::Response]
508
+ # @raise [Cloudinary::Api::Error]
509
+ def self.call_metadata_api(method, uri, params, options)
510
+ options[:content_type] = :json
511
+ uri = ["metadata_fields", uri].reject(&:empty?).join("/")
512
+
513
+ call_api(method, uri, params, options)
514
+ end
515
+
358
516
  def self.only(hash, *keys)
359
517
  result = {}
360
518
  keys.each do |key|
@@ -398,5 +556,4 @@ class Cloudinary::Api
398
556
  params[by_key] = value
399
557
  call_api("post", "resources/#{resource_type}/#{type}/update_access_mode", params, options)
400
558
  end
401
-
402
559
  end