uploadcare-ruby 3.0.5 → 3.1.0.pre.rc1

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 (31) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +12 -0
  3. data/CHANGELOG.md +12 -0
  4. data/README.md +248 -11
  5. data/lib/uploadcare/client/conversion/base_conversion_client.rb +54 -0
  6. data/lib/uploadcare/client/conversion/document_conversion_client.rb +38 -0
  7. data/lib/uploadcare/client/conversion/video_conversion_client.rb +42 -0
  8. data/lib/uploadcare/client/file_list_client.rb +4 -4
  9. data/lib/uploadcare/client/multipart_upload/chunks_client.rb +3 -1
  10. data/lib/uploadcare/client/multipart_upload_client.rb +8 -3
  11. data/lib/uploadcare/client/rest_client.rb +4 -3
  12. data/lib/uploadcare/client/rest_group_client.rb +2 -2
  13. data/lib/uploadcare/client/upload_client.rb +1 -0
  14. data/lib/uploadcare/client/uploader_client.rb +22 -6
  15. data/lib/uploadcare/client/webhook_client.rb +9 -5
  16. data/lib/uploadcare/concern/error_handler.rb +2 -2
  17. data/lib/uploadcare/entity/decorator/paginator.rb +4 -6
  18. data/lib/uploadcare/entity/document_converter.rb +26 -0
  19. data/lib/uploadcare/entity/file.rb +3 -3
  20. data/lib/uploadcare/entity/file_list.rb +1 -0
  21. data/lib/uploadcare/entity/group.rb +1 -2
  22. data/lib/uploadcare/entity/uploader.rb +2 -0
  23. data/lib/uploadcare/entity/video_converter.rb +26 -0
  24. data/lib/uploadcare/exception/conversion_error.rb +8 -0
  25. data/lib/uploadcare/exception/throttle_error.rb +2 -0
  26. data/lib/uploadcare/param/conversion/document/processing_job_url_builder.rb +39 -0
  27. data/lib/uploadcare/param/conversion/video/processing_job_url_builder.rb +64 -0
  28. data/lib/uploadcare/param/user_agent.rb +1 -1
  29. data/lib/uploadcare/ruby/version.rb +1 -1
  30. data/uploadcare-ruby.gemspec +2 -2
  31. metadata +15 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e78e5ae5b01f3ca9834186dbd80f7498a1ebec2491740e4b4d5a0aa2845e34e5
4
- data.tar.gz: 84a34cf0fdc9cda596cc54b1b60df014d4d4b3f3882e8f3dad7344ae230c7c2f
3
+ metadata.gz: b37d6357bdceca37eb0175201e52847b253c701771e3bed959e7e945e4b384b7
4
+ data.tar.gz: c70b33bed6660c649df6e93a5bb7e8b3cb5a4c79884a7c146086f0658da690a3
5
5
  SHA512:
6
- metadata.gz: 1efbec0c9e65cf94a769c206490090244628dd16952db2218e7dae5f004ed8b29c9b02855e48d7048006f125c4f019c17c03f40df1dc0db1d2fbd3cdfebf5e75
7
- data.tar.gz: aa3ac3a9e392c105d8be4d405323e645fe07406fe14bf9cc579b4dc3852a2853f15c2dc18d492831284f8cb6343a84f3ffb34f028b3d822d9cd5f738ecb67f17
6
+ metadata.gz: 47b8ead48824c45759a89a62e170374afe3cc0e8a39690ba4851c04785ee52096769a4415fea2e7ecffb62a864a5fae5f6fb1e95bf450442141973b0412990e7
7
+ data.tar.gz: a7aa262e0982825592487187f99018f5276582dffbfa3ef412abc06a3c9d723906d64d2443c79c20eb0dce137ae1cec4384c40b54e692170915289c8191032b0
data/.rubocop.yml CHANGED
@@ -7,6 +7,15 @@ Metrics/LineLength:
7
7
  IneffectiveAccessModifier:
8
8
  Enabled: false
9
9
 
10
+ Style/HashTransformKeys:
11
+ Exclude:
12
+ - 'lib/uploadcare/entity/decorator/paginator.rb'
13
+ - 'lib/uploadcare/client/conversion/video_conversion_client.rb'
14
+
15
+ Gemspec/RequiredRubyVersion:
16
+ Exclude:
17
+ - 'uploadcare-ruby.gemspec'
18
+
10
19
  Metrics/BlockLength:
11
20
  Exclude:
12
21
  - 'bin/'
@@ -18,3 +27,6 @@ Metrics/MethodLength:
18
27
 
19
28
  Style/Documentation:
20
29
  Enabled: false
30
+
31
+ Style/OptionalBooleanParameter:
32
+ AllowedMethods: ['create']
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.1.0-rc1 2021-08-11
4
+
5
+ ### Added
6
+ - Add video conversion
7
+ - Add document conversion
8
+ - Add new attributes to Uploadcare::File (variations, video_info, source, rekognition_info)
9
+
10
+ ### Fixed
11
+
12
+ - Fix the `uninitialized constant Uploadcare::Client::ApiStruct (NameError)` error
13
+
3
14
  ## 3.0.5 2021-04-15
4
15
 
5
16
  - Replace Travis-CI with Github Actions
@@ -12,6 +23,7 @@
12
23
  - Added CI
13
24
 
14
25
  ## 3.0.3-dev 2020-03-13
26
+
15
27
  - Added better pagination and iterators for GroupList & FileList
16
28
 
17
29
  ## 3.0.2-dev 2020-03-11
data/README.md CHANGED
@@ -12,12 +12,24 @@
12
12
  [stack-img]: https://img.shields.io/badge/tech-stack-0690fa.svg?style=flat
13
13
  [stack]: https://stackshare.io/uploadcare/stacks/
14
14
 
15
-
16
15
  Uploadcare Ruby integration handles uploads and further operations with files by
17
16
  wrapping Upload and REST APIs.
18
17
 
19
18
  * [Installation](#installation)
20
19
  * [Usage](#usage)
20
+ * [Uploading files](#uploading-files)
21
+ * [Uploading and storing a single file](#uploading-and-storing-a-single-file)
22
+ * [Multiple ways to upload files](#multiple-ways-to-upload-files)
23
+ * [Uploading options](#uploading-options)
24
+ * [File management](#file-management)
25
+ * [File](#file)
26
+ * [FileList](#filelist)
27
+ * [Pagination](#pagination)
28
+ * [Group](#group)
29
+ * [GroupList](#grouplist)
30
+ * [Webhook](#webhook)
31
+ * [Project](#project)
32
+ * [Conversion](#conversion)
21
33
  * [Useful links](#useful-links)
22
34
 
23
35
  ## Requirements
@@ -25,7 +37,7 @@ wrapping Upload and REST APIs.
25
37
 
26
38
  ## Compatibility
27
39
 
28
- Note that `uploadcare-ruby` **3.x** is not backward compativble with
40
+ Note that `uploadcare-ruby` **3.x** is not backward compatible with
29
41
  **[2.x](https://github.com/uploadcare/uploadcare-ruby/tree/v2.x)**.
30
42
 
31
43
  ## Installation
@@ -65,7 +77,8 @@ This section contains practical usage examples. Please note, everything that
65
77
  follows gets way more clear once you've looked through our
66
78
  [docs](https://uploadcare.com/docs/?utm_source=github&utm_medium=referral&utm_campaign=uploadcare-ruby).
67
79
 
68
- ### Uploading and storing a single file
80
+ ### Uploading files
81
+ #### Uploading and storing a single file
69
82
 
70
83
  Using Uploadcare is simple, and here are the basics of handling files.
71
84
 
@@ -97,7 +110,7 @@ within a 24-hour period.
97
110
  # => #<Uploadcare::Api::File ...
98
111
  ```
99
112
 
100
- ### Uploads
113
+ #### Multiple ways to upload files
101
114
 
102
115
  Uploadcare supports multiple ways to upload files:
103
116
 
@@ -118,7 +131,7 @@ Uploadcare::Uploader.upload_from_url('https://placekitten.com/96/139')
118
131
  Uploadcare::Uploader.multipart_upload(File.open('big_file.bin'))
119
132
  ```
120
133
 
121
- ### Upload options
134
+ #### Uploading options
122
135
 
123
136
  You can override global [`:autostore`](#initialization) option for each upload request:
124
137
 
@@ -127,15 +140,13 @@ You can override global [`:autostore`](#initialization) option for each upload r
127
140
  @api.upload_from_url(url, store: :auto)
128
141
  ```
129
142
 
130
- ### Api
143
+ ### File management
131
144
  Most methods are also available through `Uploadcare::Api` object:
132
145
  ```ruby
133
146
  # Same as Uploadcare::Uploader.upload
134
147
  Uploadcare::Api.upload('https://placekitten.com/96/139')
135
148
  ```
136
149
 
137
- ### Entity object
138
-
139
150
  Entities are representations of objects in Uploadcare cloud.
140
151
 
141
152
  #### File
@@ -168,6 +179,8 @@ File entity contains its metadata.
168
179
  "https://api.uploadcare.com/files/FILE_ID_IN_YOUR_PROJECT/",
169
180
  "uuid"=>"8f64f313-e6b1-4731-96c0-6751f1e7a50a"}
170
181
 
182
+ @file.store # copies file, returns a new (copied) file metadata
183
+
171
184
  @file.store # stores file, returns updated metadata
172
185
 
173
186
  @file.delete #deletes file. Returns updated metadata
@@ -226,7 +239,7 @@ To simply get all associated objects:
226
239
  @list.all # => returns Array of Files
227
240
  ```
228
241
 
229
- ##### Pagination
242
+ #### Pagination
230
243
 
231
244
  Initially, `FileList` is a paginated collection. It can be navigated using following methods:
232
245
  ```ruby
@@ -251,10 +264,15 @@ assigned UUID. Note, group UUIDs include a `~#{files_count}` part at the end.
251
264
  That's a requirement of our API.
252
265
 
253
266
  ```ruby
254
- # group can be created from an array of Uploadcare files
267
+ # group can be created from an array of Uploadcare files (UUIDs)
268
+ @file = '134dc30c-093e-4f48-a5b9-966fe9cb1d01'
269
+ @file2 = '134dc30c-093e-4f48-a5b9-966fe9cb1d02'
255
270
  @files_ary = [@file, @file2]
256
271
  @files = Uploadcare::Uploader.upload @files_ary
257
272
  @group = Uploadcare::Group.create @files
273
+
274
+ # group can be stored by group ID. It means that all files of a group will be stored on Uploadcare servers permanently
275
+ Uploadcare::Group.store(group.id)
258
276
  ```
259
277
 
260
278
  #### GroupList
@@ -275,7 +293,10 @@ You can use webhooks to provide notifications about your uploads to target urls.
275
293
  This gem lets you create and manage webhooks.
276
294
 
277
295
  ```ruby
278
- Uploadcare::Webhook.create('example.com/listen', event: 'file.uploaded')
296
+ Uploadcare::Webhook.create(target_url: 'https://example.com/listen', event: 'file.uploaded', is_active: true)
297
+ Uploadcare::Webhook.update(<webhook_id>, target_url: 'https://newexample.com/listen/new', event: 'file.uploaded', is_active: true)
298
+ Uploadcare::Webhook.delete('https://example.com/listen')
299
+ Uploadcare::Webhook.list
279
300
  ```
280
301
 
281
302
  #### Project
@@ -297,6 +318,222 @@ object is also an Hashie::Mash, so every methods out of
297
318
  # [{"email": collaborator@gmail.com, "name": "Collaborator"}, {"email": collaborator@gmail.com, "name": "Collaborator"}]
298
319
  ```
299
320
 
321
+ #### Conversion
322
+
323
+ ##### Video
324
+
325
+ Uploadcare can encode video files from all popular formats, adjust their quality, format and dimensions, cut out a video fragment, and generate thumbnails via [REST API](https://uploadcare.com/api-refs/rest-api/v0.6.0/).
326
+
327
+ After each video file upload you obtain a file identifier in UUID format.
328
+ Then you can use this file identifier to convert your video in multiple ways:
329
+
330
+ ```ruby
331
+ Uploadcare::VideoConverter.convert(
332
+ [
333
+ {
334
+ uuid: "dc99200d-9bd6-4b43-bfa9-aa7bfaefca40",
335
+ size: { resize_mode: 'change_ratio', width: '600', height: '400' },
336
+ quality: 'best',
337
+ format: 'ogg',
338
+ cut: { start_time: '0:0:0.0', length: '0:0:1.0' },
339
+ thumbs: { N: 2, number: 1 }
340
+ }
341
+ ], store: false
342
+ )
343
+ ```
344
+ This method accepts options to set properties of an output file:
345
+
346
+ - **uuid** — the file UUID-identifier.
347
+ - **size**:
348
+ - **resize_mode** - size operation to apply to a video file. Can be `preserve_ratio (default)`, `change_ratio`, `scale_crop` or `add_padding`.
349
+ - **width** - width for a converted video.
350
+ - **height** - height for a converted video.
351
+
352
+ ```
353
+ NOTE: you can choose to provide a single dimension (width OR height).
354
+ The value you specify for any of the dimensions should be a non-zero integer divisible by 4
355
+ ```
356
+
357
+ - **quality** - sets the level of video quality that affects file sizes and hence loading times and volumes of generated traffic. Can be `normal (default)`, `better`, `best`, `lighter`, `lightest`.
358
+ - **format** - format for a converted video. Can be `mp4 (default)`, `webm`, `ogg`.
359
+ - **cut**:
360
+ - **start_time** - defines the starting point of a fragment to cut based on your input file timeline.
361
+ - **length** - defines the duration of that fragment.
362
+ - **thumbs**:
363
+ - **N** - quantity of thumbnails for your video - non-zero integer ranging from 1 to 50; defaults to 1.
364
+ - **number** - zero-based index of a particular thumbnail in a created set, ranging from 1 to (N - 1).
365
+ - **store** - a flag indicating if Uploadcare should store your transformed outputs.
366
+
367
+ ```ruby
368
+ # Response
369
+ {
370
+ :result => [
371
+ {
372
+ :original_source=>"dc99200d-9bd6-4b43-bfa9-aa7bfaefca40/video/-/size/600x400/change_ratio/-/quality/best/-/format/ogg/-/cut/0:0:0.0/0:0:1.0/-/thumbs~2/1/",
373
+ :token=>911933811,
374
+ :uuid=>"6f9b88bd-625c-4d60-bfde-145fa3813d95",
375
+ :thumbnails_group_uuid=>"cf34c5a1-8fcc-4db2-9ec5-62c389e84468~2"
376
+ }
377
+ ],
378
+ :problems=>{}
379
+ }
380
+ ```
381
+ Params in the response:
382
+ - **result** - info related to your transformed output(-s):
383
+ - **original_source** - built path for a particular video with all the conversion operations and parameters.
384
+ - **token** - a processing job token that can be used to get a [job status](https://uploadcare.com/docs/transformations/video-encoding/#status) (see below).
385
+ - **uuid** - UUID of your processed video file.
386
+ - **thumbnails_group_uuid** - holds :uuid-thumb-group, a UUID of a [file group](https://uploadcare.com/api-refs/rest-api/v0.5.0/#operation/groupsList) with thumbnails for an output video, based on the thumbs [operation](https://uploadcare.com/docs/transformations/video-encoding/#operation-thumbs) parameters.
387
+ - **problems** - problems related to your processing job, if any.
388
+
389
+ To convert multiple videos just add params as a hash for each video to the first argument of the `Uploadcare::VideoConverter#convert` method:
390
+
391
+ ```ruby
392
+ Uploadcare::VideoConverter.convert(
393
+ [
394
+ { video_one_params }, { video_two_params }, ...
395
+ ], store: false
396
+ )
397
+ ```
398
+
399
+
400
+ To check a status of a video processing job you can simply use appropriate method of `Uploadcare::VideoConverter`:
401
+
402
+ ```ruby
403
+ token = 911933811
404
+ Uploadcare::VideoConverter.status(token)
405
+ ```
406
+ `token` here is a processing job token, obtained in a response of a convert video request.
407
+
408
+ ```ruby
409
+ # Response
410
+ {
411
+ :status => "finished",
412
+ :error => nil,
413
+ :result => {
414
+ :uuid => "dc99200d-9bd6-4b43-bfa9-aa7bfaefca40",
415
+ :thumbnails_group_uuid => "0f181f24-7551-42e5-bebc-14b15d9d3838~2"
416
+ }
417
+ }
418
+ ```
419
+
420
+ Params in the response:
421
+ - **status** - processing job status, can have one of the following values:
422
+ - *pending* — video file is being prepared for conversion.
423
+ - *processing* — video file processing is in progress.
424
+ - *finished* — the processing is finished.
425
+ - *failed* — we failed to process the video, see error for details.
426
+ - *canceled* — video processing was canceled.
427
+ - **error** - holds a processing error if we failed to handle your video.
428
+ - **result** - repeats the contents of your processing output.
429
+ - **thumbnails_group_uuid** - holds :uuid-thumb-group, a UUID of a file group with thumbnails for an output video, based on the thumbs operation parameters.
430
+ - **uuid** - a UUID of your processed video file.
431
+
432
+ More examples and options can be found [here](https://uploadcare.com/docs/transformations/video-encoding/#video-encoding)
433
+
434
+ ##### Document
435
+
436
+ Uploadcare allows converting documents to the following target formats: DOC, DOCX, XLS, XLSX, ODT, ODS, RTF, TXT, PDF, JPG, ENHANCED JPG, PNG. Document Conversion works via our [REST API](https://uploadcare.com/api-refs/rest-api/v0.6.0/).
437
+
438
+ After each document file upload you obtain a file identifier in UUID format.
439
+ Then you can use this file identifier to convert your document to a new format:
440
+
441
+ ```ruby
442
+ Uploadcare::DocumentConverter.convert(
443
+ [
444
+ {
445
+ uuid: "dc99200d-9bd6-4b43-bfa9-aa7bfaefca40",
446
+ format: 'pdf'
447
+ }
448
+ ], store: false
449
+ )
450
+ ```
451
+ or create an image of a particular page (if using image format):
452
+ ```ruby
453
+ Uploadcare::DocumentConverter.convert(
454
+ [
455
+ {
456
+ uuid: "a4b9db2f-1591-4f4c-8f68-94018924525d",
457
+ format: 'png',
458
+ page: 1
459
+ }
460
+ ], store: false
461
+ )
462
+ ```
463
+
464
+ This method accepts options to set properties of an output file:
465
+
466
+ - **uuid** — the file UUID-identifier.
467
+ - **format** - defines the target format you want a source file converted to. The supported values are: `pdf (default)`, `doc`, `docx`, `xls`, `xlsx`, `odt`, `ods`, `rtf`, `txt`, `jpg`, `enhanced.jpg`, `png`. In case the format operation was not found, your input document will be converted to `pdf`.
468
+ - **page** - a page number of a multi-paged document to either `jpg` or `png`. The method will not work for any other target formats.
469
+
470
+ ```
471
+ NOTE: Use an enhanced.jpg output format for PDF documents with inline fonts.
472
+ When converting multi-page documents to an image format (jpg or png), the output will be a zip archive with one image per page.
473
+ ```
474
+
475
+ ```ruby
476
+ # Response
477
+ {
478
+ :result => [
479
+ {
480
+ :original_source=>"a4b9db2f-1591-4f4c-8f68-94018924525d/document/-/format/png/-/page/1/",
481
+ :token=>21120220
482
+ :uuid=>"88fe5ada-90f1-422a-a233-3a0f3a7cf23c"
483
+ }
484
+ ],
485
+ :problems=>{}
486
+ }
487
+ ```
488
+ Params in the response:
489
+ - **result** - info related to your transformed output(-s):
490
+ - **original_source** - source file identifier including a target format, if present.
491
+ - **token** - a processing job token that can be used to get a [job status](https://uploadcare.com/docs/transformations/document-conversion/#status) (see below).
492
+ - **uuid** - UUID of your processed document file.
493
+ - **problems** - problems related to your processing job, if any.
494
+
495
+ To convert multiple documents just add params as a hash for each document to the first argument of the `Uploadcare::DocumentConverter#convert` method:
496
+
497
+ ```ruby
498
+ Uploadcare::DocumentConverter.convert(
499
+ [
500
+ { doc_one_params }, { doc_two_params }, ...
501
+ ], store: false
502
+ )
503
+ ```
504
+
505
+ To check a status of a document processing job you can simply use appropriate method of `Uploadcare::DocumentConverter`:
506
+
507
+ ```ruby
508
+ token = 21120220
509
+ Uploadcare::DocumentConverter.status(token)
510
+ ```
511
+ `token` here is a processing job token, obtained in a response of a convert document request.
512
+
513
+ ```ruby
514
+ # Response
515
+ {
516
+ :status => "finished",
517
+ :error => nil,
518
+ :result => {
519
+ :uuid => "a4b9db2f-1591-4f4c-8f68-94018924525d"
520
+ }
521
+ }
522
+ ```
523
+
524
+ Params in the response:
525
+ - **status** - processing job status, can have one of the following values:
526
+ - *pending* — document file is being prepared for conversion.
527
+ - *processing* — document file processing is in progress.
528
+ - *finished* — the processing is finished.
529
+ - *failed* — we failed to process the document, see error for details.
530
+ - *canceled* — document processing was canceled.
531
+ - **error** - holds a processing error if we failed to handle your document.
532
+ - **result** - repeats the contents of your processing output.
533
+ - **uuid** - a UUID of your processed document file.
534
+
535
+ More examples and options can be found [here](https://uploadcare.com/docs/transformations/document-conversion/#document-conversion)
536
+
300
537
  ## Useful links
301
538
 
302
539
  * [Development](https://github.com/uploadcare/uploadcare-ruby/blob/main/DEVELOPMENT.md)
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../rest_client'
4
+ require 'exception/conversion_error'
5
+
6
+ module Uploadcare
7
+ module Client
8
+ module Conversion
9
+ # This is a base client for conversion operations
10
+ #
11
+ # @see https://uploadcare.com/api-refs/rest-api/v0.6.0/#tag/Conversion
12
+ class BaseConversionClient < RestClient
13
+ def headers
14
+ {
15
+ 'Content-type': 'application/json',
16
+ 'Accept': 'application/vnd.uploadcare-v0.6+json',
17
+ 'User-Agent': Uploadcare::Param::UserAgent.call
18
+ }
19
+ end
20
+
21
+ private
22
+
23
+ def success(response)
24
+ body = response.body.to_s
25
+ result = extract_result(body)
26
+
27
+ Dry::Monads::Success(result)
28
+ end
29
+
30
+ def extract_result(response_body)
31
+ return nil if response_body.nil? || response_body.empty?
32
+
33
+ parsed_body = JSON.parse(response_body, symbolize_names: true)
34
+ errors = parsed_body[:error] || parsed_body[:problems]
35
+ raise ConversionError, errors unless errors.nil? || errors.empty?
36
+
37
+ parsed_body
38
+ end
39
+
40
+ # Prepares body for convert_many method
41
+ def build_body_for_many(arr, options, url_builder_class)
42
+ {
43
+ "paths": arr.map do |params|
44
+ url_builder_class.call(
45
+ **build_paths_body(params)
46
+ )
47
+ end,
48
+ "store": options[:store]
49
+ }.compact.to_json
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'client/conversion/base_conversion_client'
4
+ require 'param/conversion/document/processing_job_url_builder'
5
+
6
+ module Uploadcare
7
+ module Client
8
+ module Conversion
9
+ # This is client for document conversion
10
+ #
11
+ # @see https://uploadcare.com/api-refs/rest-api/v0.6.0/#operation/documentConvert
12
+ class DocumentConversionClient < BaseConversionClient
13
+ def convert_many(
14
+ arr,
15
+ options = {},
16
+ url_builder_class = Uploadcare::Param::Conversion::Document::ProcessingJobUrlBuilder
17
+ )
18
+ body = build_body_for_many(arr, options, url_builder_class)
19
+ post(uri: '/convert/document/', content: body)
20
+ end
21
+
22
+ def get_conversion_status(token)
23
+ get(uri: "/convert/document/status/#{token}/")
24
+ end
25
+
26
+ private
27
+
28
+ def build_paths_body(params)
29
+ {
30
+ uuid: params[:uuid],
31
+ format: params[:format],
32
+ page: params[:page]
33
+ }.compact
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'client/conversion/base_conversion_client'
4
+ require 'param/conversion/video/processing_job_url_builder'
5
+ require 'exception/conversion_error'
6
+
7
+ module Uploadcare
8
+ module Client
9
+ module Conversion
10
+ # This is client for video conversion
11
+ #
12
+ # @see https://uploadcare.com/api-refs/rest-api/v0.6.0/#operation/videoConvert
13
+ class VideoConversionClient < BaseConversionClient
14
+ def convert_many(
15
+ arr,
16
+ options = {},
17
+ url_builder_class = Uploadcare::Param::Conversion::Video::ProcessingJobUrlBuilder
18
+ )
19
+ body = build_body_for_many(arr, options, url_builder_class)
20
+ post(uri: '/convert/video/', content: body)
21
+ end
22
+
23
+ def get_conversion_status(token)
24
+ get(uri: "/convert/video/status/#{token}/")
25
+ end
26
+
27
+ private
28
+
29
+ def build_paths_body(params)
30
+ {
31
+ uuid: params[:uuid],
32
+ quality: params[:quality],
33
+ format: params[:format],
34
+ size: params[:size],
35
+ cut: params[:cut],
36
+ thumbs: params[:thumbs]
37
+ }.compact
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -15,8 +15,8 @@ module Uploadcare
15
15
  # limit: (1..1000)
16
16
  # ordering: ["datetime_uploaded"|"-datetime_uploaded"|"size"|"-size"]
17
17
  # from: number of files skipped
18
- def file_list(**options)
19
- query = options.empty? ? '' : '?' + URI.encode_www_form(options)
18
+ def file_list(options = {})
19
+ query = options.empty? ? '' : "?#{URI.encode_www_form(options)}"
20
20
  get(uri: "/files/#{query}")
21
21
  end
22
22
 
@@ -25,7 +25,7 @@ module Uploadcare
25
25
  # uuids: Array
26
26
  def batch_store(uuids)
27
27
  body = uuids.to_json
28
- put(uri: '/files/storage/', body: body)
28
+ put(uri: '/files/storage/', content: body)
29
29
  end
30
30
 
31
31
  alias request_delete delete
@@ -35,7 +35,7 @@ module Uploadcare
35
35
  # uuids: Array
36
36
  def batch_delete(uuids)
37
37
  body = uuids.to_json
38
- request_delete(uri: '/files/storage/', body: body)
38
+ request_delete(uri: '/files/storage/', content: body)
39
39
  end
40
40
 
41
41
  alias store_files batch_store
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'parallel'
4
+ require 'api_struct'
4
5
 
5
6
  module Uploadcare
6
7
  module Client
@@ -17,9 +18,10 @@ module Uploadcare
17
18
  # @param links [Array] of strings; by default list of Amazon storage urls
18
19
  def upload_chunks(object, links)
19
20
  Parallel.each(0...links.count, in_threads: Uploadcare.config.upload_threads) do |link_id|
21
+ client = self.class.new
20
22
  offset = link_id * CHUNK_SIZE
21
23
  chunk = IO.read(object, CHUNK_SIZE, offset)
22
- upload_chunk(chunk, links[link_id])
24
+ client.send(:upload_chunk, chunk, links[link_id])
23
25
  end
24
26
  end
25
27
 
@@ -36,8 +36,10 @@ module Uploadcare
36
36
  # When every chunk is uploaded, ask Uploadcare server to finish the upload
37
37
  def upload_complete(uuid)
38
38
  body = HTTP::FormData::Multipart.new(
39
- 'UPLOADCARE_PUB_KEY': Uploadcare.config.public_key,
40
- 'uuid': uuid
39
+ {
40
+ 'UPLOADCARE_PUB_KEY': Uploadcare.config.public_key,
41
+ 'uuid': uuid
42
+ }
41
43
  )
42
44
  post(path: 'multipart/complete/', body: body, headers: { 'Content-type': body.content_type })
43
45
  end
@@ -45,7 +47,10 @@ module Uploadcare
45
47
  private
46
48
 
47
49
  def multiupload_metadata(file)
48
- file = HTTP::FormData::File.new(file)
50
+ filename = file.original_filename if file.respond_to?(:original_filename)
51
+ mime_type = file.content_type if file.respond_to?(:content_type)
52
+ options = { filename: filename, content_type: mime_type }.compact
53
+ file = HTTP::FormData::File.new(file, options)
49
54
  {
50
55
  filename: file.filename,
51
56
  size: file.size,
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'rest_client'
4
+ require 'api_struct'
4
5
  require 'uploadcare/concern/error_handler'
5
6
  require 'uploadcare/concern/throttle_handler'
6
7
  require 'param/authentication_header'
@@ -22,11 +23,11 @@ module Uploadcare
22
23
  # Send request with authentication header
23
24
  #
24
25
  # Handle throttling as well
25
- def request(method: 'GET', uri:, **options)
26
+ def request(uri:, method: 'GET', **options)
26
27
  request_headers = Param::AuthenticationHeader.call(method: method.upcase, uri: uri,
27
28
  content_type: headers[:'Content-type'], **options)
28
29
  handle_throttling do
29
- send('api_struct_' + method.downcase, path: remove_trailing_slash(uri),
30
+ send("api_struct_#{method.downcase}", path: remove_trailing_slash(uri),
30
31
  headers: request_headers, body: options[:content])
31
32
  end
32
33
  end
@@ -62,7 +63,7 @@ module Uploadcare
62
63
  private
63
64
 
64
65
  def remove_trailing_slash(str)
65
- str.gsub(%r{^\/}, '')
66
+ str.gsub(%r{^/}, '')
66
67
  end
67
68
 
68
69
  def default_params
@@ -14,8 +14,8 @@ module Uploadcare
14
14
 
15
15
  # return paginated list of groups
16
16
  # @see https://uploadcare.com/api-refs/rest-api/v0.5.0/#operation/groupsList
17
- def list(**options)
18
- query = options.empty? ? '' : '?' + URI.encode_www_form(options)
17
+ def list(options = {})
18
+ query = options.empty? ? '' : "?#{URI.encode_www_form(options)}"
19
19
  get(uri: "/groups/#{query}")
20
20
  end
21
21
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'api_struct'
3
4
  require 'param/user_agent'
4
5
  require 'uploadcare/concern/error_handler'
5
6
  require 'uploadcare/concern/throttle_handler'
@@ -74,19 +74,35 @@ module Uploadcare
74
74
  def upload_many_body(arr, **options)
75
75
  files_formdata = arr.map do |file|
76
76
  [HTTP::FormData::File.new(file).filename,
77
- HTTP::FormData::File.new(file)]
78
- end .to_h
77
+ form_data_for(file)]
78
+ end.to_h
79
+ p Param::Upload::UploadParamsGenerator.call(options[:store]).merge(files_formdata)
79
80
  HTTP::FormData::Multipart.new(
80
81
  Param::Upload::UploadParamsGenerator.call(options[:store]).merge(files_formdata)
81
82
  )
82
83
  end
83
84
 
85
+ def form_data_for(file)
86
+ filename = file.original_filename if file.respond_to?(:original_filename)
87
+ mime_type = file.content_type if file.respond_to?(:content_type)
88
+ options = { filename: filename, content_type: mime_type }.compact
89
+ HTTP::FormData::File.new(file, options)
90
+ end
91
+
92
+ STORE_VALUES = {
93
+ true => '1',
94
+ false => '0'
95
+ }.freeze
96
+
84
97
  # Prepare upload_from_url initial request body
85
98
  def upload_from_url_body(url, **options)
86
- HTTP::FormData::Multipart.new({
87
- 'pub_key': Uploadcare.config.public_key,
88
- 'source_url': url
89
- }.merge(**options))
99
+ HTTP::FormData::Multipart.new(
100
+ options.merge(
101
+ 'pub_key' => Uploadcare.config.public_key,
102
+ 'source_url' => url,
103
+ 'store' => STORE_VALUES[options[:store]]
104
+ )
105
+ )
90
106
  end
91
107
  end
92
108
  end
@@ -9,8 +9,12 @@ module Uploadcare
9
9
  class WebhookClient < RestClient
10
10
  # Create webhook
11
11
  # @see https://uploadcare.com/docs/api_reference/rest/webhooks/#subscribe
12
- def create(target_url, event: 'file.uploaded', is_active: true)
13
- body = { 'target_url': target_url, 'event': event, 'is_active': is_active }.to_json
12
+ def create(options = {})
13
+ body = {
14
+ 'target_url': options[:target_url],
15
+ 'event': options[:event] || 'file.uploaded',
16
+ 'is_active': options[:is_active] || true
17
+ }.to_json
14
18
  post(uri: '/webhooks/', content: body)
15
19
  end
16
20
 
@@ -22,14 +26,14 @@ module Uploadcare
22
26
 
23
27
  # Permanently deletes subscription
24
28
  # @see https://uploadcare.com/docs/api_reference/rest/webhooks/#unsubscribe
25
- def delete(name)
26
- body = { 'name': name }.to_json
29
+ def delete(target_url)
30
+ body = { 'target_url': target_url }.to_json
27
31
  post(uri: '/webhooks/unsubscribe/', content: body)
28
32
  end
29
33
 
30
34
  # Updates webhook
31
35
  # @see https://uploadcare.com/docs/api_reference/rest/webhooks/#subscribe-update
32
- def update(id, **options)
36
+ def update(id, options = {})
33
37
  body = options.to_json
34
38
  post(uri: "/webhooks/#{id}/", content: body)
35
39
  end
@@ -14,9 +14,9 @@ module Uploadcare
14
14
  def failure(response)
15
15
  catch_upload_errors(response)
16
16
  parsed_response = JSON.parse(response.body.to_s)
17
- raise RequestError, parsed_response['detail']
17
+ raise RequestError, parsed_response['detail'] || parsed_response.map { |k, v| "#{k}: #{v}" }.join('; ')
18
18
  rescue JSON::ParserError
19
- raise RequestError, response.status
19
+ raise RequestError, response.body.to_s
20
20
  end
21
21
 
22
22
  # Extension of ApiStruct's wrap method
@@ -26,7 +26,7 @@ module Uploadcare
26
26
  return unless url
27
27
 
28
28
  query = URI.decode_www_form(URI(url).query).to_h
29
- query = Hash[query.map { |k, v| [k.to_sym, v] }]
29
+ query = query.map { |k, v| [k.to_sym, v] }.to_h
30
30
  self.class.list(**query)
31
31
  end
32
32
 
@@ -36,7 +36,7 @@ module Uploadcare
36
36
  return unless url
37
37
 
38
38
  query = URI.decode_www_form(URI(url).query).to_h
39
- query = Hash[query.map { |k, v| [k.to_sym, v] }]
39
+ query = query.map { |k, v| [k.to_sym, v] }.to_h
40
40
  self.class.list(**query)
41
41
  end
42
42
 
@@ -59,12 +59,10 @@ module Uploadcare
59
59
  # iterate through pages, starting with current one
60
60
  #
61
61
  # @yield [Block]
62
- def each
62
+ def each(&block)
63
63
  current_page = self
64
64
  while current_page
65
- current_page.results.each do |obj|
66
- yield obj
67
- end
65
+ current_page.results.each(&block)
68
66
  current_page = current_page.next_page
69
67
  end
70
68
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uploadcare
4
+ module Entity
5
+ # This serializer lets a user convert uploaded documents
6
+ # @see https://uploadcare.com/api-refs/rest-api/v0.6.0/#operation/documentConvert
7
+ class DocumentConverter < Entity
8
+ client_service Conversion::DocumentConversionClient
9
+ # Converts documents
10
+ #
11
+ # @param doc_params [Array] of hashes with params or [Hash]
12
+ # @option options [Boolean] :store (false) whether to store file on servers.
13
+ def self.convert(doc_params, **options)
14
+ params = doc_params.is_a?(Hash) ? [doc_params] : doc_params
15
+ Conversion::DocumentConversionClient.new.convert_many(params, **options)
16
+ end
17
+
18
+ # Returns a status of document conversion job
19
+ #
20
+ # @param token [Integer, String] token obtained from a server in convert method
21
+ def self.status(token)
22
+ Conversion::DocumentConversionClient.new.get_conversion_status(token)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -9,7 +9,8 @@ module Uploadcare
9
9
  client_service FileClient
10
10
 
11
11
  attr_entity :datetime_removed, :datetime_stored, :datetime_uploaded, :image_info, :is_image, :is_ready,
12
- :mime_type, :original_file_url, :original_filename, :size, :url, :uuid
12
+ :mime_type, :original_file_url, :original_filename, :size, :url, :uuid, :variations, :video_info,
13
+ :source, :rekognition_info
13
14
 
14
15
  # gets file's uuid - even if it's only initialized with url
15
16
  # @return [String]
@@ -17,8 +18,7 @@ module Uploadcare
17
18
  return @entity.uuid if @entity.uuid
18
19
 
19
20
  uuid = @entity.url.gsub('https://ucarecdn.com/', '')
20
- uuid = uuid.gsub(%r{\/.*}, '')
21
- uuid
21
+ uuid.gsub(%r{/.*}, '')
22
22
  end
23
23
 
24
24
  # loads file metadata, if it's initialized with url or uuid
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'uploadcare/entity/file'
4
4
  require 'uploadcare/entity/decorator/paginator'
5
+ require 'api_struct'
5
6
 
6
7
  module Uploadcare
7
8
  module Entity
@@ -28,8 +28,7 @@ module Uploadcare
28
28
  return @entity.id if @entity.id
29
29
 
30
30
  id = @entity.cdn_url.gsub('https://ucarecdn.com/', '')
31
- id = id.gsub(%r{\/.*}, '')
32
- id
31
+ id.gsub(%r{/.*}, '')
33
32
  end
34
33
 
35
34
  # loads group metadata, if it's initialized with url or id
@@ -52,6 +52,8 @@ module Uploadcare
52
52
  # @param url [String]
53
53
  def self.upload_from_url(url, **options)
54
54
  response = UploaderClient.new.upload_from_url(url, **options)
55
+ return response.success[:token] unless response.success[:files]
56
+
55
57
  response.success[:files].map { |file_data| Uploadcare::Entity::File.new(file_data) }
56
58
  end
57
59
 
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uploadcare
4
+ module Entity
5
+ # This serializer lets a user convert uploaded videos, and usually returns an array of results
6
+ # @see https://uploadcare.com/api-refs/rest-api/v0.6.0/#operation/videoConvert
7
+ class VideoConverter < Entity
8
+ client_service Conversion::VideoConversionClient
9
+ # Converts video files
10
+ #
11
+ # @param doc_params [Array] of hashes with params or [Hash]
12
+ # @option options [Boolean] :store (false) whether to store file on servers.
13
+ def self.convert(video_params, **options)
14
+ params = video_params.is_a?(Hash) ? [video_params] : video_params
15
+ Conversion::VideoConversionClient.new.convert_many(params, **options)
16
+ end
17
+
18
+ # Returns a status of video conversion job
19
+ #
20
+ # @param token [Integer, String] token obtained from a server in convert method
21
+ def self.status(token)
22
+ Conversion::VideoConversionClient.new.get_conversion_status(token)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uploadcare
4
+ module Exception
5
+ # Standard error for invalid API conversion responses
6
+ class ConversionError < StandardError; end
7
+ end
8
+ end
@@ -5,8 +5,10 @@ module Uploadcare
5
5
  # Exception for throttled requests
6
6
  class ThrottleError < StandardError
7
7
  attr_reader :timeout
8
+
8
9
  # @param timeout [Float] Amount of seconds the request have been throttled for
9
10
  def initialize(timeout = 10.0)
11
+ super
10
12
  @timeout = timeout
11
13
  end
12
14
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uploadcare
4
+ module Param
5
+ module Conversion
6
+ module Document
7
+ class ProcessingJobUrlBuilder
8
+ class << self
9
+ def call(uuid:, format: nil, page: nil)
10
+ [
11
+ uuid_part(uuid),
12
+ format_part(format),
13
+ page_part(page)
14
+ ].compact.join('-')
15
+ end
16
+
17
+ private
18
+
19
+ def uuid_part(uuid)
20
+ "#{uuid}/document/"
21
+ end
22
+
23
+ def format_part(format)
24
+ return if format.nil?
25
+
26
+ "/format/#{format}/"
27
+ end
28
+
29
+ def page_part(page)
30
+ return if page.nil?
31
+
32
+ "/page/#{page}/"
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uploadcare
4
+ module Param
5
+ module Conversion
6
+ module Video
7
+ class ProcessingJobUrlBuilder
8
+ class << self
9
+ # rubocop:disable Metrics/ParameterLists
10
+ def call(uuid:, size: {}, quality: nil, format: nil, cut: {}, thumbs: {})
11
+ [
12
+ uuid_part(uuid),
13
+ size_part(size),
14
+ quality_part(quality),
15
+ format_part(format),
16
+ cut_part(cut),
17
+ thumbs_part(thumbs)
18
+ ].compact.join('-')
19
+ end
20
+ # rubocop:enable Metrics/ParameterLists
21
+
22
+ private
23
+
24
+ def uuid_part(uuid)
25
+ "#{uuid}/video/"
26
+ end
27
+
28
+ def size_part(size)
29
+ return if size.empty?
30
+
31
+ dimensions = "#{size[:width]}x#{size[:height]}" if size[:width] || size[:height]
32
+ resize_mode = (size[:resize_mode]).to_s
33
+ "/size/#{dimensions}/#{resize_mode}/".squeeze('/')
34
+ end
35
+
36
+ def quality_part(quality)
37
+ return if quality.nil?
38
+
39
+ "/quality/#{quality}/"
40
+ end
41
+
42
+ def format_part(format)
43
+ return if format.nil?
44
+
45
+ "/format/#{format}/"
46
+ end
47
+
48
+ def cut_part(cut)
49
+ return if cut.empty?
50
+
51
+ "/cut/#{cut[:start_time]}/#{cut[:length]}/"
52
+ end
53
+
54
+ def thumbs_part(thumbs)
55
+ return if thumbs.empty?
56
+
57
+ "/thumbs~#{thumbs[:N]}/#{thumbs[:number]}/".squeeze('/')
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -12,7 +12,7 @@ module Uploadcare
12
12
  # UploadcareRuby/3.0.0-dev/Pubkey_(Ruby/2.6.3;UploadcareRuby)
13
13
  def self.call
14
14
  framework_data = Uploadcare.config.framework_data || ''
15
- framework_data_string = '; ' + Uploadcare.config.framework_data unless framework_data.empty?
15
+ framework_data_string = "; #{Uploadcare.config.framework_data}" unless framework_data.empty?
16
16
  public_key = Uploadcare.config.public_key
17
17
  "UploadcareRuby/#{VERSION}/#{public_key} (Ruby/#{RUBY_VERSION}#{framework_data_string})"
18
18
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Uploadcare
4
- VERSION = '3.0.5'
4
+ VERSION = '3.1.0-rc1'
5
5
  end
@@ -7,8 +7,8 @@ require 'uploadcare/ruby/version'
7
7
  Gem::Specification.new do |spec|
8
8
  spec.name = 'uploadcare-ruby'
9
9
  spec.version = Uploadcare::VERSION
10
- spec.authors = ['Stepan Redka']
11
- spec.email = ['stepan.redka@railsmuffin.com']
10
+ spec.authors = ['Stepan Redka, Dmitrij Ivanchenko']
11
+ spec.email = ['stepan.redka@railsmuffin.com', 'dmitrij.ivanchenko@gmail.com']
12
12
 
13
13
  spec.summary = 'Ruby wrapper for uploadcare API'
14
14
  spec.description = spec.summary
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uploadcare-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.5
4
+ version: 3.1.0.pre.rc1
5
5
  platform: ruby
6
6
  authors:
7
- - Stepan Redka
7
+ - Stepan Redka, Dmitrij Ivanchenko
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-04-15 00:00:00.000000000 Z
11
+ date: 2021-08-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: api_struct
@@ -153,6 +153,7 @@ dependencies:
153
153
  description: Ruby wrapper for uploadcare API
154
154
  email:
155
155
  - stepan.redka@railsmuffin.com
156
+ - dmitrij.ivanchenko@gmail.com
156
157
  executables: []
157
158
  extensions: []
158
159
  extra_rdoc_files: []
@@ -173,6 +174,9 @@ files:
173
174
  - bin/setup
174
175
  - lib/uploadcare.rb
175
176
  - lib/uploadcare/api/api.rb
177
+ - lib/uploadcare/client/conversion/base_conversion_client.rb
178
+ - lib/uploadcare/client/conversion/document_conversion_client.rb
179
+ - lib/uploadcare/client/conversion/video_conversion_client.rb
176
180
  - lib/uploadcare/client/file_client.rb
177
181
  - lib/uploadcare/client/file_list_client.rb
178
182
  - lib/uploadcare/client/group_client.rb
@@ -188,6 +192,7 @@ files:
188
192
  - lib/uploadcare/concern/throttle_handler.rb
189
193
  - lib/uploadcare/concern/upload_error_handler.rb
190
194
  - lib/uploadcare/entity/decorator/paginator.rb
195
+ - lib/uploadcare/entity/document_converter.rb
191
196
  - lib/uploadcare/entity/entity.rb
192
197
  - lib/uploadcare/entity/file.rb
193
198
  - lib/uploadcare/entity/file_list.rb
@@ -195,10 +200,14 @@ files:
195
200
  - lib/uploadcare/entity/group_list.rb
196
201
  - lib/uploadcare/entity/project.rb
197
202
  - lib/uploadcare/entity/uploader.rb
203
+ - lib/uploadcare/entity/video_converter.rb
198
204
  - lib/uploadcare/entity/webhook.rb
205
+ - lib/uploadcare/exception/conversion_error.rb
199
206
  - lib/uploadcare/exception/request_error.rb
200
207
  - lib/uploadcare/exception/throttle_error.rb
201
208
  - lib/uploadcare/param/authentication_header.rb
209
+ - lib/uploadcare/param/conversion/document/processing_job_url_builder.rb
210
+ - lib/uploadcare/param/conversion/video/processing_job_url_builder.rb
202
211
  - lib/uploadcare/param/param.rb
203
212
  - lib/uploadcare/param/secure_auth_header.rb
204
213
  - lib/uploadcare/param/simple_auth_header.rb
@@ -228,11 +237,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
228
237
  version: '0'
229
238
  required_rubygems_version: !ruby/object:Gem::Requirement
230
239
  requirements:
231
- - - ">="
240
+ - - ">"
232
241
  - !ruby/object:Gem::Version
233
- version: '0'
242
+ version: 1.3.1
234
243
  requirements: []
235
- rubygems_version: 3.0.1
244
+ rubygems_version: 3.2.22
236
245
  signing_key:
237
246
  specification_version: 4
238
247
  summary: Ruby wrapper for uploadcare API