uploadcare-ruby 2.1.1 → 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 (116) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/gem-push.yml +20 -0
  3. data/.github/workflows/ruby.yml +52 -0
  4. data/.gitignore +13 -6
  5. data/.rspec +2 -0
  6. data/.rubocop.yml +32 -0
  7. data/.yardopts +4 -0
  8. data/CHANGELOG.md +33 -34
  9. data/DEVELOPMENT.md +18 -0
  10. data/Gemfile +2 -0
  11. data/LICENSE +1 -1
  12. data/README.md +369 -546
  13. data/Rakefile +5 -5
  14. data/bin/console +15 -0
  15. data/bin/setup +8 -0
  16. data/lib/uploadcare.rb +36 -26
  17. data/lib/uploadcare/api/api.rb +25 -0
  18. data/lib/uploadcare/client/conversion/base_conversion_client.rb +54 -0
  19. data/lib/uploadcare/client/conversion/document_conversion_client.rb +38 -0
  20. data/lib/uploadcare/client/conversion/video_conversion_client.rb +42 -0
  21. data/lib/uploadcare/client/file_client.rb +44 -0
  22. data/lib/uploadcare/client/file_list_client.rb +46 -0
  23. data/lib/uploadcare/client/group_client.rb +45 -0
  24. data/lib/uploadcare/client/multipart_upload/chunks_client.rb +48 -0
  25. data/lib/uploadcare/client/multipart_upload_client.rb +67 -0
  26. data/lib/uploadcare/client/project_client.rb +18 -0
  27. data/lib/uploadcare/client/rest_client.rb +74 -0
  28. data/lib/uploadcare/client/rest_group_client.rb +23 -0
  29. data/lib/uploadcare/client/upload_client.rb +36 -0
  30. data/lib/uploadcare/client/uploader_client.rb +109 -0
  31. data/lib/uploadcare/client/webhook_client.rb +47 -0
  32. data/lib/uploadcare/concern/error_handler.rb +54 -0
  33. data/lib/uploadcare/concern/throttle_handler.rb +25 -0
  34. data/lib/uploadcare/concern/upload_error_handler.rb +32 -0
  35. data/lib/uploadcare/entity/decorator/paginator.rb +79 -0
  36. data/lib/uploadcare/entity/document_converter.rb +26 -0
  37. data/lib/uploadcare/entity/entity.rb +18 -0
  38. data/lib/uploadcare/entity/file.rb +81 -0
  39. data/lib/uploadcare/entity/file_list.rb +31 -0
  40. data/lib/uploadcare/entity/group.rb +40 -0
  41. data/lib/uploadcare/entity/group_list.rb +24 -0
  42. data/lib/uploadcare/entity/project.rb +13 -0
  43. data/lib/uploadcare/entity/uploader.rb +75 -0
  44. data/lib/uploadcare/entity/video_converter.rb +26 -0
  45. data/lib/uploadcare/entity/webhook.rb +14 -0
  46. data/lib/uploadcare/exception/conversion_error.rb +8 -0
  47. data/lib/uploadcare/exception/request_error.rb +9 -0
  48. data/lib/uploadcare/exception/throttle_error.rb +16 -0
  49. data/lib/uploadcare/param/authentication_header.rb +25 -0
  50. data/lib/uploadcare/param/conversion/document/processing_job_url_builder.rb +39 -0
  51. data/lib/uploadcare/param/conversion/video/processing_job_url_builder.rb +64 -0
  52. data/lib/uploadcare/param/param.rb +10 -0
  53. data/lib/uploadcare/param/secure_auth_header.rb +37 -0
  54. data/lib/uploadcare/param/simple_auth_header.rb +14 -0
  55. data/lib/uploadcare/param/upload/signature_generator.rb +24 -0
  56. data/lib/uploadcare/param/upload/upload_params_generator.rb +23 -0
  57. data/lib/uploadcare/param/user_agent.rb +21 -0
  58. data/lib/uploadcare/ruby/version.rb +5 -0
  59. data/uploadcare-ruby.gemspec +50 -37
  60. metadata +109 -115
  61. data/.travis.yml +0 -26
  62. data/UPGRADE_NOTES.md +0 -36
  63. data/lib/uploadcare/api.rb +0 -26
  64. data/lib/uploadcare/api/file_api.rb +0 -7
  65. data/lib/uploadcare/api/file_list_api.rb +0 -19
  66. data/lib/uploadcare/api/file_storage_api.rb +0 -34
  67. data/lib/uploadcare/api/group_api.rb +0 -38
  68. data/lib/uploadcare/api/group_list_api.rb +0 -17
  69. data/lib/uploadcare/api/project_api.rb +0 -9
  70. data/lib/uploadcare/api/raw_api.rb +0 -38
  71. data/lib/uploadcare/api/uploading_api.rb +0 -71
  72. data/lib/uploadcare/api/uploading_api/upload_params.rb +0 -72
  73. data/lib/uploadcare/api/validators/file_list_options_validator.rb +0 -73
  74. data/lib/uploadcare/api/validators/group_list_options_validator.rb +0 -49
  75. data/lib/uploadcare/errors/errors.rb +0 -64
  76. data/lib/uploadcare/resources/file.rb +0 -164
  77. data/lib/uploadcare/resources/file_list.rb +0 -14
  78. data/lib/uploadcare/resources/group.rb +0 -115
  79. data/lib/uploadcare/resources/group_list.rb +0 -14
  80. data/lib/uploadcare/resources/project.rb +0 -13
  81. data/lib/uploadcare/resources/resource_list.rb +0 -83
  82. data/lib/uploadcare/rest/auth/auth.rb +0 -31
  83. data/lib/uploadcare/rest/auth/secure.rb +0 -43
  84. data/lib/uploadcare/rest/auth/simple.rb +0 -16
  85. data/lib/uploadcare/rest/connections/api_connection.rb +0 -53
  86. data/lib/uploadcare/rest/connections/upload_connection.rb +0 -22
  87. data/lib/uploadcare/rest/middlewares/auth_middleware.rb +0 -24
  88. data/lib/uploadcare/rest/middlewares/parse_json_middleware.rb +0 -33
  89. data/lib/uploadcare/rest/middlewares/raise_error_middleware.rb +0 -21
  90. data/lib/uploadcare/utils/parser.rb +0 -71
  91. data/lib/uploadcare/utils/user_agent.rb +0 -44
  92. data/lib/uploadcare/version.rb +0 -3
  93. data/spec/api/file_list_api_spec.rb +0 -95
  94. data/spec/api/file_storage_api_spec.rb +0 -88
  95. data/spec/api/group_list_api_spec.rb +0 -59
  96. data/spec/api/raw_api_spec.rb +0 -25
  97. data/spec/api/uploading_api/upload_params_spec.rb +0 -99
  98. data/spec/api/uploading_api_spec.rb +0 -59
  99. data/spec/resources/file_list_spec.rb +0 -25
  100. data/spec/resources/file_spec.rb +0 -223
  101. data/spec/resources/group_list_spec.rb +0 -25
  102. data/spec/resources/group_spec.rb +0 -101
  103. data/spec/resources/operations_spec.rb +0 -59
  104. data/spec/resources/project_spec.rb +0 -21
  105. data/spec/rest/api_connection_spec.rb +0 -68
  106. data/spec/rest/auth/secure_spec.rb +0 -66
  107. data/spec/rest/auth/simple_spec.rb +0 -31
  108. data/spec/rest/errors_spec.rb +0 -75
  109. data/spec/rest/upload_connection_spec.rb +0 -19
  110. data/spec/shared/resource_list.rb +0 -188
  111. data/spec/spec_helper.rb +0 -54
  112. data/spec/uploadcare_spec.rb +0 -16
  113. data/spec/utils/parser_spec.rb +0 -85
  114. data/spec/utils/user_agent_spec.rb +0 -46
  115. data/spec/view.png +0 -0
  116. data/spec/view2.jpg +0 -0
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'client/multipart_upload/chunks_client'
4
+ require_relative 'upload_client'
5
+
6
+ module Uploadcare
7
+ module Client
8
+ # Client for multipart uploads
9
+ #
10
+ # @see https://uploadcare.com/api-refs/upload-api/#tag/Upload
11
+ class MultipartUploaderClient < UploadClient
12
+ include MultipartUpload
13
+
14
+ # Upload a big file by splitting it into parts and sending those parts into assigned buckets
15
+ # object should be File
16
+ def upload(object, store: false)
17
+ response = upload_start(object, store: store)
18
+ return response unless response.success[:parts] && response.success[:uuid]
19
+
20
+ links = response.success[:parts]
21
+ uuid = response.success[:uuid]
22
+ ChunksClient.new.upload_chunks(object, links)
23
+ upload_complete(uuid)
24
+ end
25
+
26
+ # Asks Uploadcare server to create a number of storage bin for uploads
27
+ def upload_start(object, store: false)
28
+ body = HTTP::FormData::Multipart.new(
29
+ Param::Upload::UploadParamsGenerator.call(store).merge(multiupload_metadata(object))
30
+ )
31
+ post(path: 'multipart/start/',
32
+ headers: { 'Content-type': body.content_type },
33
+ body: body)
34
+ end
35
+
36
+ # When every chunk is uploaded, ask Uploadcare server to finish the upload
37
+ def upload_complete(uuid)
38
+ body = HTTP::FormData::Multipart.new(
39
+ {
40
+ 'UPLOADCARE_PUB_KEY': Uploadcare.config.public_key,
41
+ 'uuid': uuid
42
+ }
43
+ )
44
+ post(path: 'multipart/complete/', body: body, headers: { 'Content-type': body.content_type })
45
+ end
46
+
47
+ private
48
+
49
+ def multiupload_metadata(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)
54
+ {
55
+ filename: file.filename,
56
+ size: file.size,
57
+ content_type: file.content_type
58
+ }
59
+ end
60
+
61
+ alias api_struct_post post
62
+ def post(**args)
63
+ handle_throttling { api_struct_post(**args) }
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uploadcare
4
+ module Client
5
+ # API client for getting project info
6
+ # @see https://uploadcare.com/docs/api_reference/rest/handling_projects/
7
+ class ProjectClient < RestClient
8
+ # get information about current project
9
+ # current project is determined by public and private key combination
10
+ # @see https://uploadcare.com/api-refs/rest-api/v0.5.0/#tag/Project
11
+ def show
12
+ get(uri: '/project/')
13
+ end
14
+
15
+ alias project show
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'rest_client'
4
+ require 'api_struct'
5
+ require 'uploadcare/concern/error_handler'
6
+ require 'uploadcare/concern/throttle_handler'
7
+ require 'param/authentication_header'
8
+
9
+ module Uploadcare
10
+ module Client
11
+ # @abstract
12
+ # General client for signed REST requests
13
+ class RestClient < ApiStruct::Client
14
+ include Uploadcare::Concerns::ErrorHandler
15
+ include Uploadcare::Concerns::ThrottleHandler
16
+ include Exception
17
+
18
+ alias api_struct_delete delete
19
+ alias api_struct_get get
20
+ alias api_struct_post post
21
+ alias api_struct_put put
22
+
23
+ # Send request with authentication header
24
+ #
25
+ # Handle throttling as well
26
+ def request(uri:, method: 'GET', **options)
27
+ request_headers = Param::AuthenticationHeader.call(method: method.upcase, uri: uri,
28
+ content_type: headers[:'Content-type'], **options)
29
+ handle_throttling do
30
+ send("api_struct_#{method.downcase}", path: remove_trailing_slash(uri),
31
+ headers: request_headers, body: options[:content])
32
+ end
33
+ end
34
+
35
+ def get(**options)
36
+ request(method: 'GET', **options)
37
+ end
38
+
39
+ def post(**options)
40
+ request(method: 'POST', **options)
41
+ end
42
+
43
+ def put(**options)
44
+ request(method: 'PUT', **options)
45
+ end
46
+
47
+ def delete(**options)
48
+ request(method: 'DELETE', **options)
49
+ end
50
+
51
+ def api_root
52
+ Uploadcare.config.rest_api_root
53
+ end
54
+
55
+ def headers
56
+ {
57
+ 'Content-type': 'application/json',
58
+ 'Accept': 'application/vnd.uploadcare-v0.5+json',
59
+ 'User-Agent': Uploadcare::Param::UserAgent.call
60
+ }
61
+ end
62
+
63
+ private
64
+
65
+ def remove_trailing_slash(str)
66
+ str.gsub(%r{^/}, '')
67
+ end
68
+
69
+ def default_params
70
+ {}
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'rest_client'
4
+
5
+ module Uploadcare
6
+ module Client
7
+ # @see https://uploadcare.com/api-refs/rest-api/v0.5.0/#tag/Group/paths/~1groups~1%3Cuuid%3E~1storage~1/put
8
+ class RestGroupClient < RestClient
9
+ # store all files in a group
10
+ # @see https://uploadcare.com/api-refs/rest-api/v0.5.0/#tag/Group/paths/~1groups~1%3Cuuid%3E~1storage~1/put
11
+ def store(uuid)
12
+ put(uri: "/groups/#{uuid}/storage/")
13
+ end
14
+
15
+ # return paginated list of groups
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)}"
19
+ get(uri: "/groups/#{query}")
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'api_struct'
4
+ require 'param/user_agent'
5
+ require 'uploadcare/concern/error_handler'
6
+ require 'uploadcare/concern/throttle_handler'
7
+
8
+ module Uploadcare
9
+ module Client
10
+ # @abstract
11
+ #
12
+ # Headers and helper methods for clients working with upload API
13
+ # @see https://uploadcare.com/docs/api_reference/upload/
14
+ class UploadClient < ApiStruct::Client
15
+ include Concerns::ErrorHandler
16
+ include Concerns::ThrottleHandler
17
+ include Exception
18
+
19
+ def api_root
20
+ Uploadcare.config.upload_api_root
21
+ end
22
+
23
+ def headers
24
+ {
25
+ 'User-Agent': Uploadcare::Param::UserAgent.call
26
+ }
27
+ end
28
+
29
+ private
30
+
31
+ def default_params
32
+ {}
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'upload_client'
4
+ require 'retries'
5
+ require 'param/upload/upload_params_generator'
6
+
7
+ module Uploadcare
8
+ module Client
9
+ # This is client for general uploads
10
+ #
11
+ # @see https://uploadcare.com/api-refs/upload-api/#tag/Upload
12
+ class UploaderClient < UploadClient
13
+ # @see https://uploadcare.com/api-refs/upload-api/#operation/baseUpload
14
+
15
+ def upload_many(arr, **options)
16
+ body = upload_many_body(arr, **options)
17
+ post(path: 'base/',
18
+ headers: { 'Content-type': body.content_type },
19
+ body: body)
20
+ end
21
+
22
+ # syntactic sugar for upload_many
23
+ # There is actual upload method for one file, but it is redundant
24
+
25
+ def upload(file, **options)
26
+ upload_many([file], **options)
27
+ end
28
+
29
+ # Upload files from url
30
+ # @see https://uploadcare.com/api-refs/upload-api/#operation/fromURLUpload
31
+ # options:
32
+ # - check_URL_duplicates
33
+ # - filename
34
+ # - save_URL_duplicates
35
+ # - async - returns upload token instead of upload data
36
+ def upload_from_url(url, **options)
37
+ body = upload_from_url_body(url, **options)
38
+ token_response = post(path: 'from_url/', headers: { 'Content-type': body.content_type }, body: body)
39
+ return token_response if options[:async]
40
+
41
+ uploaded_response = poll_upload_response(token_response.success[:token])
42
+ return uploaded_response if uploaded_response.success[:status] == 'error'
43
+
44
+ Dry::Monads::Success(files: [uploaded_response.success])
45
+ end
46
+
47
+ private
48
+
49
+ alias api_struct_post post
50
+ def post(**args)
51
+ handle_throttling { api_struct_post(**args) }
52
+ end
53
+
54
+ def poll_upload_response(token)
55
+ with_retries(max_tries: Uploadcare.config.max_request_tries,
56
+ base_sleep_seconds: Uploadcare.config.base_request_sleep,
57
+ max_sleep_seconds: Uploadcare.config.max_request_sleep) do
58
+ response = get_status_response(token)
59
+ raise RequestError if %w[progress waiting unknown].include?(response.success[:status])
60
+
61
+ response
62
+ end
63
+ end
64
+
65
+ # Check upload status
66
+ #
67
+ # @see https://uploadcare.com/api-refs/upload-api/#operation/fromURLUploadStatus
68
+ def get_status_response(token)
69
+ query_params = { token: token }
70
+ get(path: 'from_url/status/', params: query_params)
71
+ end
72
+
73
+ # Prepares body for upload_many method
74
+ def upload_many_body(arr, **options)
75
+ files_formdata = arr.map do |file|
76
+ [HTTP::FormData::File.new(file).filename,
77
+ form_data_for(file)]
78
+ end.to_h
79
+ p Param::Upload::UploadParamsGenerator.call(options[:store]).merge(files_formdata)
80
+ HTTP::FormData::Multipart.new(
81
+ Param::Upload::UploadParamsGenerator.call(options[:store]).merge(files_formdata)
82
+ )
83
+ end
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
+
97
+ # Prepare upload_from_url initial request body
98
+ def upload_from_url_body(url, **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
+ )
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'rest_client'
4
+
5
+ module Uploadcare
6
+ module Client
7
+ # client for webhook management
8
+ # @see https://uploadcare.com/api-refs/rest-api/v0.5.0/#tag/Webhook
9
+ class WebhookClient < RestClient
10
+ # Create webhook
11
+ # @see https://uploadcare.com/docs/api_reference/rest/webhooks/#subscribe
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
18
+ post(uri: '/webhooks/', content: body)
19
+ end
20
+
21
+ # Returns array (not paginated list) of webhooks
22
+ # @see https://uploadcare.com/docs/api_reference/rest/webhooks/#get-list
23
+ def list
24
+ get(uri: '/webhooks/')
25
+ end
26
+
27
+ # Permanently deletes subscription
28
+ # @see https://uploadcare.com/docs/api_reference/rest/webhooks/#unsubscribe
29
+ def delete(target_url)
30
+ body = { 'target_url': target_url }.to_json
31
+ post(uri: '/webhooks/unsubscribe/', content: body)
32
+ end
33
+
34
+ # Updates webhook
35
+ # @see https://uploadcare.com/docs/api_reference/rest/webhooks/#subscribe-update
36
+ def update(id, options = {})
37
+ body = options.to_json
38
+ post(uri: "/webhooks/#{id}/", content: body)
39
+ end
40
+
41
+ alias create_webhook create
42
+ alias list_webhooks list
43
+ alias delete_webhook delete
44
+ alias update_webhook update
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uploadcare
4
+ module Concerns
5
+ # Wrapper for responses
6
+ # raises errors instead of returning monads
7
+ module ErrorHandler
8
+ include Exception
9
+
10
+ # Extension of ApiStruct's failure method
11
+ #
12
+ # Raises errors instead of returning falsey objects
13
+ # @see https://github.com/rubygarage/api_struct/blob/master/lib/api_struct/client.rb#L55
14
+ def failure(response)
15
+ catch_upload_errors(response)
16
+ parsed_response = JSON.parse(response.body.to_s)
17
+ raise RequestError, parsed_response['detail'] || parsed_response.map { |k, v| "#{k}: #{v}" }.join('; ')
18
+ rescue JSON::ParserError
19
+ raise RequestError, response.body.to_s
20
+ end
21
+
22
+ # Extension of ApiStruct's wrap method
23
+ #
24
+ # Catches throttling errors and Upload API errors
25
+ #
26
+ # @see https://github.com/rubygarage/api_struct/blob/master/lib/api_struct/client.rb#L45
27
+ def wrap(response)
28
+ raise_throttling_error(response) if response.status == 429
29
+ return failure(response) if response.status >= 300
30
+
31
+ catch_upload_errors(response)
32
+ success(response)
33
+ end
34
+
35
+ private
36
+
37
+ # Raise ThrottleError. Also, tells in error when server will be ready for next request
38
+ def raise_throttling_error(response)
39
+ retry_after = response.headers['Retry-After'].to_i + 1 || 11
40
+ raise ThrottleError.new(retry_after), "Response throttled, retry #{retry_after} seconds later"
41
+ end
42
+
43
+ # Upload API returns its errors with code 200, and stores its actual code and details within response message
44
+ # This methods detects that and raises apropriate error
45
+ def catch_upload_errors(response)
46
+ return unless response.code == 200
47
+
48
+ parsed_response = JSON.parse(response.body.to_s)
49
+ error = parsed_response['error'] if parsed_response.is_a?(Hash)
50
+ raise RequestError, error if error
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uploadcare
4
+ module Concerns
5
+ # This module lets clients send request multiple times if request is throttled
6
+ module ThrottleHandler
7
+ # call given block. If ThrottleError is returned, it will wait and attempt again 4 more times
8
+ # @yield executable block (HTTP request that may be throttled)
9
+ def handle_throttling
10
+ (Uploadcare.config.max_throttle_attempts - 1).times do
11
+ # rubocop:disable Style/RedundantBegin
12
+ begin
13
+ return yield
14
+ rescue(Exception::ThrottleError) => e
15
+ wait_time = e.timeout
16
+ sleep(wait_time)
17
+ next
18
+ end
19
+ # rubocop:enable Style/RedundantBegin
20
+ end
21
+ yield
22
+ end
23
+ end
24
+ end
25
+ end