uploadcare-ruby 1.2.2 → 3.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/gem-push.yml +20 -0
  3. data/.github/workflows/ruby.yml +52 -0
  4. data/.gitignore +13 -5
  5. data/.rspec +3 -0
  6. data/.rubocop.yml +20 -0
  7. data/.yardopts +4 -0
  8. data/CHANGELOG.md +26 -26
  9. data/DEVELOPMENT.md +18 -0
  10. data/Gemfile +2 -0
  11. data/LICENSE +1 -1
  12. data/README.md +191 -519
  13. data/Rakefile +6 -4
  14. data/bin/console +15 -0
  15. data/bin/setup +8 -0
  16. data/lib/uploadcare.rb +36 -31
  17. data/lib/uploadcare/api/api.rb +25 -0
  18. data/lib/uploadcare/client/file_client.rb +44 -0
  19. data/lib/uploadcare/client/file_list_client.rb +46 -0
  20. data/lib/uploadcare/client/group_client.rb +45 -0
  21. data/lib/uploadcare/client/multipart_upload/chunks_client.rb +46 -0
  22. data/lib/uploadcare/client/multipart_upload_client.rb +62 -0
  23. data/lib/uploadcare/client/project_client.rb +18 -0
  24. data/lib/uploadcare/client/rest_client.rb +73 -0
  25. data/lib/uploadcare/client/rest_group_client.rb +23 -0
  26. data/lib/uploadcare/client/upload_client.rb +35 -0
  27. data/lib/uploadcare/client/uploader_client.rb +93 -0
  28. data/lib/uploadcare/client/webhook_client.rb +43 -0
  29. data/lib/uploadcare/concern/error_handler.rb +54 -0
  30. data/lib/uploadcare/concern/throttle_handler.rb +25 -0
  31. data/lib/uploadcare/concern/upload_error_handler.rb +32 -0
  32. data/lib/uploadcare/entity/decorator/paginator.rb +81 -0
  33. data/lib/uploadcare/entity/entity.rb +18 -0
  34. data/lib/uploadcare/entity/file.rb +81 -0
  35. data/lib/uploadcare/entity/file_list.rb +30 -0
  36. data/lib/uploadcare/entity/group.rb +41 -0
  37. data/lib/uploadcare/entity/group_list.rb +24 -0
  38. data/lib/uploadcare/entity/project.rb +13 -0
  39. data/lib/uploadcare/entity/uploader.rb +73 -0
  40. data/lib/uploadcare/entity/webhook.rb +14 -0
  41. data/lib/uploadcare/exception/request_error.rb +9 -0
  42. data/lib/uploadcare/exception/throttle_error.rb +14 -0
  43. data/lib/uploadcare/param/authentication_header.rb +25 -0
  44. data/lib/uploadcare/param/param.rb +10 -0
  45. data/lib/uploadcare/param/secure_auth_header.rb +37 -0
  46. data/lib/uploadcare/param/simple_auth_header.rb +14 -0
  47. data/lib/uploadcare/param/upload/signature_generator.rb +24 -0
  48. data/lib/uploadcare/param/upload/upload_params_generator.rb +23 -0
  49. data/lib/uploadcare/param/user_agent.rb +21 -0
  50. data/lib/uploadcare/ruby/version.rb +5 -0
  51. data/uploadcare-ruby.gemspec +50 -36
  52. metadata +112 -96
  53. data/.travis.yml +0 -12
  54. data/lib/uploadcare/api.rb +0 -24
  55. data/lib/uploadcare/api/file_api.rb +0 -7
  56. data/lib/uploadcare/api/file_list_api.rb +0 -8
  57. data/lib/uploadcare/api/group_api.rb +0 -38
  58. data/lib/uploadcare/api/group_list_api.rb +0 -8
  59. data/lib/uploadcare/api/project_api.rb +0 -9
  60. data/lib/uploadcare/api/raw_api.rb +0 -44
  61. data/lib/uploadcare/api/uploading_api.rb +0 -110
  62. data/lib/uploadcare/errors/errors.rb +0 -64
  63. data/lib/uploadcare/resources/file.rb +0 -164
  64. data/lib/uploadcare/resources/file_list.rb +0 -41
  65. data/lib/uploadcare/resources/group.rb +0 -115
  66. data/lib/uploadcare/resources/group_list.rb +0 -31
  67. data/lib/uploadcare/resources/project.rb +0 -13
  68. data/lib/uploadcare/rest/auth/auth.rb +0 -31
  69. data/lib/uploadcare/rest/auth/secure.rb +0 -43
  70. data/lib/uploadcare/rest/auth/simple.rb +0 -16
  71. data/lib/uploadcare/rest/connections/api_connection.rb +0 -32
  72. data/lib/uploadcare/rest/connections/upload_connection.rb +0 -21
  73. data/lib/uploadcare/rest/middlewares/auth_middleware.rb +0 -24
  74. data/lib/uploadcare/rest/middlewares/parse_json_middleware.rb +0 -33
  75. data/lib/uploadcare/rest/middlewares/raise_error_middleware.rb +0 -21
  76. data/lib/uploadcare/utils/parser.rb +0 -71
  77. data/lib/uploadcare/utils/user_agent.rb +0 -44
  78. data/lib/uploadcare/version.rb +0 -3
  79. data/spec/api/raw_api_spec.rb +0 -25
  80. data/spec/resources/file_list_spec.rb +0 -64
  81. data/spec/resources/file_spec.rb +0 -222
  82. data/spec/resources/group_list_spec.rb +0 -30
  83. data/spec/resources/group_spec.rb +0 -101
  84. data/spec/resources/operations_spec.rb +0 -59
  85. data/spec/resources/project_spec.rb +0 -21
  86. data/spec/rest/api_connection_spec.rb +0 -68
  87. data/spec/rest/auth/secure_spec.rb +0 -66
  88. data/spec/rest/auth/simple_spec.rb +0 -31
  89. data/spec/rest/errors_spec.rb +0 -75
  90. data/spec/rest/upload_connection_spec.rb +0 -19
  91. data/spec/spec_helper.rb +0 -41
  92. data/spec/uploadcare_spec.rb +0 -43
  93. data/spec/uploading/uploading_multiple_spec.rb +0 -43
  94. data/spec/uploading/uploading_spec.rb +0 -40
  95. data/spec/utils/parser_spec.rb +0 -87
  96. data/spec/utils/user_agent_spec.rb +0 -46
  97. data/spec/view.png +0 -0
  98. data/spec/view2.jpg +0 -0
data/Rakefile CHANGED
@@ -1,6 +1,8 @@
1
- #!/usr/bin/env rake
2
- require "bundler/gem_tasks"
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
3
4
  require 'rspec/core/rake_task'
4
5
 
5
- task :default => :spec
6
- RSpec::Core::RakeTask.new
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'uploadcare/ruby'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require 'irb'
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/lib/uploadcare.rb CHANGED
@@ -1,37 +1,42 @@
1
- require 'faraday'
2
- require 'json'
3
- require 'ostruct'
1
+ # frozen_string_literal: true
4
2
 
5
- require 'uploadcare/api'
6
- require 'uploadcare/version'
3
+ # Gem version
4
+ require 'ruby/version'
7
5
 
8
- module Uploadcare
9
- DEFAULT_SETTINGS = {
10
- public_key: 'demopublickey',
11
- private_key: 'demoprivatekey',
12
- upload_url_base: 'https://upload.uploadcare.com',
13
- api_url_base: 'https://api.uploadcare.com',
14
- static_url_base: 'https://ucarecdn.com',
15
- api_version: '0.3',
16
- cache_files: true,
17
- auth_scheme: :secure
18
- }
6
+ # Exceptions
7
+ require 'exception/throttle_error'
8
+ require 'exception/request_error'
19
9
 
20
- def self.default_settings
21
- DEFAULT_SETTINGS
22
- end
10
+ # Entities
11
+ require 'entity/entity'
12
+ require 'entity/file'
13
+ require 'entity/file_list'
14
+ require 'entity/group'
15
+ require 'entity/group_list'
16
+ require 'entity/project'
17
+ require 'entity/uploader'
18
+ require 'entity/webhook'
23
19
 
24
- def self.user_agent(options={})
25
- warn '[DEPRECATION] `Uploadcare::user_agent` method is deprecated and will be removed in version 3.0'
26
- UserAgent.new.call(options)
27
- end
20
+ # General api
21
+ require 'api/api'
28
22
 
29
- def self.const_missing(name)
30
- if name == :USER_AGENT
31
- warn '[DEPRECATION] `Uploadcare::USER_AGENT` constant is deprecated and will be removed in version 3.0'
32
- "uploadcare-ruby/#{Gem.ruby_version}/#{Uploadcare::VERSION}"
33
- else
34
- super
35
- end
36
- end
23
+ # Ruby wrapper for Uploadcare API
24
+ #
25
+ # @see https://uploadcare.com/docs/api_reference
26
+ module Uploadcare
27
+ extend Dry::Configurable
28
+ setting :public_key, ENV.fetch('UPLOADCARE_PUBLIC_KEY')
29
+ setting :secret_key, ENV.fetch('UPLOADCARE_SECRET_KEY')
30
+ setting :auth_type, 'Uploadcare'
31
+ setting :multipart_size_threshold, 100 * 1024 * 1024
32
+ setting :rest_api_root, 'https://api.uploadcare.com'
33
+ setting :upload_api_root, 'https://upload.uploadcare.com'
34
+ setting :max_request_tries, 100
35
+ setting :base_request_sleep, 1 # seconds
36
+ setting :max_request_sleep, 60.0 # seconds
37
+ setting :sign_uploads, false
38
+ setting :upload_signature_lifetime, 30 * 60 # seconds
39
+ setting :max_throttle_attempts, 5
40
+ setting :upload_threads, 2 # used for multiupload only ATM
41
+ setting :framework_data, ''
37
42
  end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ Gem.find_files('client/**/*.rb').each { |path| require path }
4
+ Gem.find_files('entity/**/*.rb').each { |path| require path }
5
+
6
+ module Uploadcare
7
+ # End-user interface
8
+ #
9
+ # It delegates methods to other classes:
10
+ # * To class methods of Entity objects
11
+ # * To instance methods of Client objects
12
+ # @see Uploadcare::Entity
13
+ # @see Uploadcare::Client
14
+ class Api
15
+ extend Forwardable
16
+ include Entity
17
+
18
+ def_delegator File, :file
19
+ def_delegators FileList, :file_list, :store_files, :delete_files
20
+ def_delegators Group, :group
21
+ def_delegators Project, :project
22
+ def_delegators Uploader, :upload, :upload_files, :upload_url
23
+ def_delegators Webhook, :create_webhook, :list_webhooks, :delete_webhook, :update_webhook
24
+ end
25
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'rest_client'
4
+
5
+ module Uploadcare
6
+ module Client
7
+ # API client for handling single files
8
+ # @see https://uploadcare.com/docs/api_reference/rest/accessing_files/
9
+ # @see https://uploadcare.com/api-refs/rest-api/v0.5.0/#tag/File
10
+ class FileClient < RestClient
11
+ # Gets list of files without pagination fields
12
+ def index
13
+ response = get(uri: '/files/')
14
+ response.fmap { |i| i[:results] }
15
+ end
16
+
17
+ # Acquire file info
18
+ # @see https://uploadcare.com/api-refs/rest-api/v0.5.0/#operation/fileInfo
19
+ def info(uuid)
20
+ get(uri: "/files/#{uuid}/")
21
+ end
22
+ alias file info
23
+
24
+ # 'copy' method is used to copy original files or their modified versions to default storage.
25
+ # Source files MAY either be stored or just uploaded and MUST NOT be deleted.
26
+ # @see https://uploadcare.com/api-refs/rest-api/v0.5.0/#operation/copyFile
27
+ def copy(**options)
28
+ body = options.compact.to_json
29
+ post(uri: '/files/', content: body)
30
+ end
31
+
32
+ # @see https://uploadcare.com/api-refs/rest-api/v0.5.0/#operation/deleteFile
33
+ def delete(uuid)
34
+ request(method: 'DELETE', uri: "/files/#{uuid}/")
35
+ end
36
+
37
+ # Store a single file, preventing it from being deleted in 2 weeks
38
+ # @see https://uploadcare.com/api-refs/rest-api/v0.5.0/#operation/storeFile
39
+ def store(uuid)
40
+ put(uri: "/files/#{uuid}/storage/")
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'rest_client'
4
+
5
+ module Uploadcare
6
+ module Client
7
+ # API client for handling file lists
8
+ class FileListClient < RestClient
9
+ # Returns a pagination json of files stored in project
10
+ # @see https://uploadcare.com/api-refs/rest-api/v0.5.0/#operation/filesList
11
+ #
12
+ # valid options:
13
+ # removed: [true|false]
14
+ # stored: [true|false]
15
+ # limit: (1..1000)
16
+ # ordering: ["datetime_uploaded"|"-datetime_uploaded"|"size"|"-size"]
17
+ # from: number of files skipped
18
+ def file_list(**options)
19
+ query = options.empty? ? '' : '?' + URI.encode_www_form(options)
20
+ get(uri: "/files/#{query}")
21
+ end
22
+
23
+ # Make a set of files "stored". This will prevent them from being deleted automatically
24
+ # @see https://uploadcare.com/api-refs/rest-api/v0.5.0/#operation/filesStoring
25
+ # uuids: Array
26
+ def batch_store(uuids)
27
+ body = uuids.to_json
28
+ put(uri: '/files/storage/', body: body)
29
+ end
30
+
31
+ alias request_delete delete
32
+
33
+ # Delete several files by list of uids
34
+ # @see https://uploadcare.com/api-refs/rest-api/v0.5.0/#operation/filesDelete
35
+ # uuids: Array
36
+ def batch_delete(uuids)
37
+ body = uuids.to_json
38
+ request_delete(uri: '/files/storage/', body: body)
39
+ end
40
+
41
+ alias store_files batch_store
42
+ alias delete_files batch_delete
43
+ alias list file_list
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'upload_client'
4
+
5
+ module Uploadcare
6
+ module Client
7
+ # Groups serve a purpose of better organizing files in your Uploadcare projects.
8
+ # You can create one from a set of files by using their UUIDs.
9
+ # @see https://uploadcare.com/docs/api_reference/upload/groups/
10
+ class GroupClient < UploadClient
11
+ # Create files group from a set of files by using their UUIDs.
12
+ # @see https://uploadcare.com/api-refs/upload-api/#operation/createFilesGroup
13
+ def create(file_list, **options)
14
+ body_hash = group_body_hash(file_list, **options)
15
+ body = HTTP::FormData::Multipart.new(body_hash)
16
+ post(path: 'group/',
17
+ headers: { 'Content-type': body.content_type },
18
+ body: body)
19
+ end
20
+
21
+ # Get group info
22
+ # @see https://uploadcare.com/api-refs/upload-api/#operation/filesGroupInfo
23
+ def info(group_id)
24
+ get(path: 'group/info/', params: { 'pub_key': Uploadcare.config.public_key, 'group_id': group_id })
25
+ end
26
+
27
+ private
28
+
29
+ def file_params(file_ids)
30
+ ids = (0...file_ids.size).map { |i| "files[#{i}]" }
31
+ ids.zip(file_ids).to_h
32
+ end
33
+
34
+ def group_body_hash(file_list, **options)
35
+ { pub_key: Uploadcare.config.public_key }.merge(file_params(parse_file_list(file_list))).merge(**options)
36
+ end
37
+
38
+ # API accepts only list of ids, but some users may want to upload list of files
39
+ # @return [Array] of [String]
40
+ def parse_file_list(file_list)
41
+ file_list.map { |file| file.methods.include?(:uuid) ? file.uuid : file }
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'parallel'
4
+
5
+ module Uploadcare
6
+ module Client
7
+ module MultipartUpload
8
+ # This class splits file into chunks of set chunk_size
9
+ # and uploads them into cloud storage.
10
+ # Used for multipart uploads
11
+ # @see https://uploadcare.com/api-refs/upload-api/#tag/Upload/paths/https:~1~1uploadcare.s3-accelerate.amazonaws.com~1%3C%3Cpresigned-url%3E/put
12
+ class ChunksClient < ApiStruct::Client
13
+ CHUNK_SIZE = 5_242_880
14
+
15
+ # In multiple threads, split file into chunks and upload those chunks into respective Amazon links
16
+ # @param object [File]
17
+ # @param links [Array] of strings; by default list of Amazon storage urls
18
+ def upload_chunks(object, links)
19
+ Parallel.each(0...links.count, in_threads: Uploadcare.config.upload_threads) do |link_id|
20
+ offset = link_id * CHUNK_SIZE
21
+ chunk = IO.read(object, CHUNK_SIZE, offset)
22
+ upload_chunk(chunk, links[link_id])
23
+ end
24
+ end
25
+
26
+ def api_root
27
+ ''
28
+ end
29
+
30
+ def headers
31
+ {}
32
+ end
33
+
34
+ private
35
+
36
+ def upload_chunk(chunk, link)
37
+ put(path: link, body: chunk, headers: { 'Content-type': 'application/octet-stream' })
38
+ end
39
+
40
+ def default_params
41
+ {}
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,62 @@
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
+ 'UPLOADCARE_PUB_KEY': Uploadcare.config.public_key,
40
+ 'uuid': uuid
41
+ )
42
+ post(path: 'multipart/complete/', body: body, headers: { 'Content-type': body.content_type })
43
+ end
44
+
45
+ private
46
+
47
+ def multiupload_metadata(file)
48
+ file = HTTP::FormData::File.new(file)
49
+ {
50
+ filename: file.filename,
51
+ size: file.size,
52
+ content_type: file.content_type
53
+ }
54
+ end
55
+
56
+ alias api_struct_post post
57
+ def post(**args)
58
+ handle_throttling { api_struct_post(**args) }
59
+ end
60
+ end
61
+ end
62
+ 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,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'rest_client'
4
+ require 'uploadcare/concern/error_handler'
5
+ require 'uploadcare/concern/throttle_handler'
6
+ require 'param/authentication_header'
7
+
8
+ module Uploadcare
9
+ module Client
10
+ # @abstract
11
+ # General client for signed REST requests
12
+ class RestClient < ApiStruct::Client
13
+ include Uploadcare::Concerns::ErrorHandler
14
+ include Uploadcare::Concerns::ThrottleHandler
15
+ include Exception
16
+
17
+ alias api_struct_delete delete
18
+ alias api_struct_get get
19
+ alias api_struct_post post
20
+ alias api_struct_put put
21
+
22
+ # Send request with authentication header
23
+ #
24
+ # Handle throttling as well
25
+ def request(method: 'GET', uri:, **options)
26
+ request_headers = Param::AuthenticationHeader.call(method: method.upcase, uri: uri,
27
+ content_type: headers[:'Content-type'], **options)
28
+ handle_throttling do
29
+ send('api_struct_' + method.downcase, path: remove_trailing_slash(uri),
30
+ headers: request_headers, body: options[:content])
31
+ end
32
+ end
33
+
34
+ def get(**options)
35
+ request(method: 'GET', **options)
36
+ end
37
+
38
+ def post(**options)
39
+ request(method: 'POST', **options)
40
+ end
41
+
42
+ def put(**options)
43
+ request(method: 'PUT', **options)
44
+ end
45
+
46
+ def delete(**options)
47
+ request(method: 'DELETE', **options)
48
+ end
49
+
50
+ def api_root
51
+ Uploadcare.config.rest_api_root
52
+ end
53
+
54
+ def headers
55
+ {
56
+ 'Content-type': 'application/json',
57
+ 'Accept': 'application/vnd.uploadcare-v0.5+json',
58
+ 'User-Agent': Uploadcare::Param::UserAgent.call
59
+ }
60
+ end
61
+
62
+ private
63
+
64
+ def remove_trailing_slash(str)
65
+ str.gsub(%r{^\/}, '')
66
+ end
67
+
68
+ def default_params
69
+ {}
70
+ end
71
+ end
72
+ end
73
+ end