From 27899c5896bab0c2e842469d29fe46e5169b88f2 Mon Sep 17 00:00:00 2001 From: Vipul A M Date: Fri, 23 Feb 2024 15:31:50 -0500 Subject: [PATCH 1/4] Fix documentation about original file url on simple file upload. (#150) * Fix documentation about original file url on simple file upload. Closes #148 * Fix rubocop style suggestion * Fix missing paran in file upload in README --- README.md | 6 +++--- lib/uploadcare.rb | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 75bc1a9..468a413 100644 --- a/README.md +++ b/README.md @@ -101,8 +101,8 @@ Using Uploadcare is simple, and here are the basics of handling files. # => "dc99200d-9bd6-4b43-bfa9-aa7bfaefca40" # URL for the file, can be used with your website or app right away -@uc_file.url -# => "https://ucarecdn.com/dc99200d-9bd6-4b43-bfa9-aa7bfaefca40/" +@uc_file.original_file_url +# => "https://ucarecdn.com/dc99200d-9bd6-4b43-bfa9-aa7bfaefca40/your-file.png" ``` Your might then want to store or delete the uploaded file. Storing files could @@ -134,7 +134,7 @@ Uploadcare::Uploader.upload("https://placekitten.com/96/139") There are explicit ways to select upload type: ```ruby -files = [File.open("1.jpg"), File.open("1.jpg"] +files = [File.open("1.jpg"), File.open("1.jpg")] Uploadcare::Uploader.upload_files(files) Uploadcare::Uploader.upload_from_url("https://placekitten.com/96/139") diff --git a/lib/uploadcare.rb b/lib/uploadcare.rb index e063613..b42f226 100644 --- a/lib/uploadcare.rb +++ b/lib/uploadcare.rb @@ -39,7 +39,7 @@ module Uploadcare # deprecation warnings, we override the dry-configurable's `setting` DSL method. def self.setting(name, default:, **options, &block) if RUBY_VERSION < '2.6' - super name, default, &block + super(name, default, &block) else super end From 0eda04b2588f4c21bbac1dc369093f4a28a9503f Mon Sep 17 00:00:00 2001 From: Vipul A M Date: Sat, 24 Feb 2024 14:21:38 -0500 Subject: [PATCH 2/4] feat: Support params in Rest client and in file info method. (#151) * feat: Support params in Rest client and in file info method. - Fix Uploadcare::Param::SecureAuthHeader to allow accepting query params that can be passed down to http requests - Add make_uri method to support the same, with or without query params options being passed down - Group and move call method to class << self instead - In RestClient start passing down params when calling api struct request - Start passing down params from info method to get - Cover with new specs and docs --- README.md | 4 +- lib/uploadcare/client/file_client.rb | 4 +- lib/uploadcare/client/rest_client.rb | 7 ++- lib/uploadcare/param/secure_auth_header.rb | 40 ++++++++----- .../fixtures/vcr_cassettes/rest_file_info.yml | 58 ++++++++++++++++++- spec/uploadcare/client/file_client_spec.rb | 9 +++ 6 files changed, 102 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 468a413..49a9be9 100644 --- a/README.md +++ b/README.md @@ -208,10 +208,10 @@ Entities are representations of objects in Uploadcare cloud. #### File -File entity contains its metadata. +File entity contains its metadata. It also supports `include` param to include additional fields to the file object, such as: "appdata". ```ruby -@file = Uploadcare::File.file("FILE_ID_IN_YOUR_PROJECT") +@file = Uploadcare::File.file("FILE_ID_IN_YOUR_PROJECT", include: "appdata") { "datetime_removed"=>nil, "datetime_stored"=>"2018-11-26T12:49:10.477888Z", diff --git a/lib/uploadcare/client/file_client.rb b/lib/uploadcare/client/file_client.rb index 9a4f27e..69f14bf 100644 --- a/lib/uploadcare/client/file_client.rb +++ b/lib/uploadcare/client/file_client.rb @@ -16,8 +16,8 @@ def index # Acquire file info # @see https://uploadcare.com/api-refs/rest-api/v0.7.0/#operation/fileInfo - def info(uuid) - get(uri: "/files/#{uuid}/") + def info(uuid, params = {}) + get(uri: "/files/#{uuid}/", params: params) end alias file info diff --git a/lib/uploadcare/client/rest_client.rb b/lib/uploadcare/client/rest_client.rb index e9bbafb..a7ae803 100644 --- a/lib/uploadcare/client/rest_client.rb +++ b/lib/uploadcare/client/rest_client.rb @@ -27,8 +27,11 @@ def request(uri:, method: 'GET', **options) request_headers = Param::AuthenticationHeader.call(method: method.upcase, uri: uri, content_type: headers[:'Content-Type'], **options) handle_throttling do - send("api_struct_#{method.downcase}", path: remove_trailing_slash(uri), - headers: request_headers, body: options[:content]) + send("api_struct_#{method.downcase}", + path: remove_trailing_slash(uri), + headers: request_headers, + body: options[:content], + params: options[:params]) end end diff --git a/lib/uploadcare/param/secure_auth_header.rb b/lib/uploadcare/param/secure_auth_header.rb index e94d553..fb5db9d 100644 --- a/lib/uploadcare/param/secure_auth_header.rb +++ b/lib/uploadcare/param/secure_auth_header.rb @@ -1,26 +1,28 @@ # frozen_string_literal: true require 'digest/md5' +require 'addressable/uri' module Uploadcare module Param # This object returns headers needed for authentication # This authentication method is more secure, but more tedious class SecureAuthHeader - # @see https://uploadcare.com/docs/api_reference/rest/requests_auth/#auth-uploadcare - def self.call(options = {}) - @method = options[:method] - @body = options[:content] || '' - @content_type = options[:content_type] - @uri = options[:uri] - @date_for_header = timestamp - { - Date: @date_for_header, - Authorization: "Uploadcare #{Uploadcare.config.public_key}:#{signature}" - } - end - class << self + # @see https://uploadcare.com/docs/api_reference/rest/requests_auth/#auth-uploadcare + def call(options = {}) + @method = options[:method] + @body = options[:content] || '' + @content_type = options[:content_type] + @uri = make_uri(options) + + @date_for_header = timestamp + { + Date: @date_for_header, + Authorization: "Uploadcare #{Uploadcare.config.public_key}:#{signature}" + } + end + def signature content_md5 = Digest::MD5.hexdigest(@body) sign_string = [@method, content_md5, @content_type, @date_for_header, @uri].join("\n") @@ -31,6 +33,18 @@ def signature def timestamp Time.now.gmtime.strftime('%a, %d %b %Y %H:%M:%S GMT') end + + private + + def make_uri(options) + if options[:params] && !options[:params].empty? + uri = Addressable::URI.parse options[:uri] + uri.query_values = uri.query_values(Array).to_a.concat(options[:params].to_a) + uri.to_s + else + options[:uri] + end + end end end end diff --git a/spec/fixtures/vcr_cassettes/rest_file_info.yml b/spec/fixtures/vcr_cassettes/rest_file_info.yml index 10f49c3..352b303 100644 --- a/spec/fixtures/vcr_cassettes/rest_file_info.yml +++ b/spec/fixtures/vcr_cassettes/rest_file_info.yml @@ -59,4 +59,60 @@ http_interactions: string: '{"datetime_removed":null,"datetime_stored":null,"datetime_uploaded":"2022-09-19T12:29:59.461061Z","is_image":true,"is_ready":true,"mime_type":"application/octet-stream","original_file_url":"https://ucarecdn.com/2e17f5d1-d423-4de6-8ee5-6773cc4a7fa6/kitten.jpeg","original_filename":"kitten.jpeg","size":1290,"url":"https://api.uploadcare.com/files/2e17f5d1-d423-4de6-8ee5-6773cc4a7fa6/","uuid":"2e17f5d1-d423-4de6-8ee5-6773cc4a7fa6","variations":null,"content_info":{"mime":{"mime":"image/jpeg","type":"image","subtype":"jpeg"},"image":{"dpi":[96,96],"width":50,"format":"JPEG","height":50,"sequence":false,"color_mode":"RGB","orientation":null,"geo_location":null,"datetime_original":null}},"metadata":{"subsystem":"new test value"}}' recorded_at: Mon, 19 Sep 2022 17:27:32 GMT -recorded_with: VCR 6.1.0 +- request: + method: get + uri: https://api.uploadcare.com/files/640fe4b7-7352-42ca-8d87-0e4387957157/?include=appdata + body: + encoding: ASCII-8BIT + string: '' + headers: + Content-Type: + - application/json + Accept: + - application/vnd.uploadcare-v0.7+json + User-Agent: + - UploadcareRuby/4.3.6/5d5bb5639e3f2df33674 (Ruby/3.3.0) + Date: + - Sat, 24 Feb 2024 18:59:02 GMT + Authorization: + - Uploadcare 5d5bb5639e3f2df33674:0cd5a6181fa53bc946d83db616e619fa75672634 + Connection: + - close + Host: + - api.uploadcare.com + response: + status: + code: 200 + message: OK + headers: + Date: + - Sat, 24 Feb 2024 18:59:03 GMT + Content-Type: + - application/vnd.uploadcare-v0.7+json + Content-Length: + - '2292' + Connection: + - close + Server: + - nginx + Access-Control-Allow-Origin: + - https://uploadcare.com + Vary: + - Accept + Allow: + - DELETE, GET, HEAD, OPTIONS + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Uploadcare-Request-Id: + - c9e3fc1d-c435-4212-a3ef-0c142f4d2054 + X-Frame-Options: + - SAMEORIGIN + X-Robots-Tag: + - noindex, nofollow, nosnippet, noarchive + body: + encoding: ASCII-8BIT + string: '{"datetime_removed":null,"datetime_stored":"2024-02-16T14:44:29.637342Z","datetime_uploaded":"2024-02-16T14:44:29.395043Z","is_image":true,"is_ready":true,"mime_type":"image/jpeg","original_file_url":"https://ucarecdn.com/640fe4b7-7352-42ca-8d87-0e4387957157/samplefile.jpeg","original_filename":"sample-file.jpeg","size":3518420,"url":"https://api.uploadcare.com/files/640fe4b7-7352-42ca-8d87-0e4387957157/","uuid":"640fe4b7-7352-42ca-8d87-0e4387957157","variations":null,"content_info":{"mime":{"mime":"image/jpeg","type":"image","subtype":"jpeg"},"image":{"dpi":[72,72],"width":3011,"format":"JPEG","height":4516,"sequence":false,"color_mode":"RGB","orientation":1,"geo_location":null,"datetime_original":"2023-03-10T16:23:15"}},"metadata":{},"appdata":{"aws_rekognition_detect_labels":{"data":{"Labels":[{"Name":"Accessories","Parents":[],"Instances":[],"Confidence":99.99993133544922},{"Name":"Strap","Parents":[{"Name":"Accessories"}],"Instances":[],"Confidence":99.99993133544922},{"Name":"Path","Parents":[],"Instances":[],"Confidence":99.9738998413086},{"Name":"Road","Parents":[],"Instances":[],"Confidence":99.55068969726562},{"Name":"Street","Parents":[{"Name":"City"},{"Name":"Road"},{"Name":"Urban"}],"Instances":[],"Confidence":97.583984375},{"Name":"Dog","Parents":[{"Name":"Animal"},{"Name":"Canine"},{"Name":"Mammal"},{"Name":"Pet"}],"Instances":[{"Confidence":97.21764373779297,"BoundingBox":{"Top":0.22813057899475098,"Left":0.20908746123313904,"Width":0.603455662727356,"Height":0.5941042304039001}}],"Confidence":97.41680908203125},{"Name":"Husky","Parents":[{"Name":"Animal"},{"Name":"Canine"},{"Name":"Dog"},{"Name":"Mammal"},{"Name":"Pet"}],"Instances":[],"Confidence":97.41680908203125},{"Name":"Leash","Parents":[],"Instances":[],"Confidence":95.52120971679688},{"Name":"Tarmac","Parents":[{"Name":"Road"}],"Instances":[],"Confidence":93.37832641601562},{"Name":"Walkway","Parents":[{"Name":"Path"}],"Instances":[],"Confidence":90.72362518310547}],"LabelModelVersion":"3.0"},"datetime_created":"2024-02-24T14:00:23.545995Z","datetime_updated":"2024-02-24T14:07:10.598091Z","version":"2016-06-27"},"uc_clamav_virus_scan":{"data":{"infected":false},"datetime_created":"2024-02-16T14:44:29.676188Z","datetime_updated":"2024-02-16T14:44:29.676207Z","version":"1.0.1"}}}' + recorded_at: Sat, 24 Feb 2024 18:59:03 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/uploadcare/client/file_client_spec.rb b/spec/uploadcare/client/file_client_spec.rb index f30fa8a..573b462 100644 --- a/spec/uploadcare/client/file_client_spec.rb +++ b/spec/uploadcare/client/file_client_spec.rb @@ -16,6 +16,15 @@ module Client end end + it 'supports extra params like include' do + VCR.use_cassette('rest_file_info') do + uuid = '640fe4b7-7352-42ca-8d87-0e4387957157' + file = subject.info(uuid, { include: 'appdata' }) + expect(file.value![:uuid]).to eq(uuid) + expect(file.value![:appdata]).not_to be_empty + end + end + it 'shows nothing on invalid file' do VCR.use_cassette('rest_file_info_fail') do uuid = 'nonexistent' From 990071c539284e6fa48337ac3a519b89f07e8687 Mon Sep 17 00:00:00 2001 From: Vipul A M Date: Sun, 25 Feb 2024 09:44:25 -0500 Subject: [PATCH 3/4] fix: Start returning proper error message when raising RequestError in poll_upload_response, to hit to users what is going on. Fixes #141 (#153) --- lib/uploadcare/client/uploader_client.rb | 5 ++++- spec/uploadcare/entity/uploader_spec.rb | 9 +++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/uploadcare/client/uploader_client.rb b/lib/uploadcare/client/uploader_client.rb index 76f5e5d..51b04a4 100644 --- a/lib/uploadcare/client/uploader_client.rb +++ b/lib/uploadcare/client/uploader_client.rb @@ -77,7 +77,10 @@ def poll_upload_response(token) base_sleep_seconds: Uploadcare.config.base_request_sleep, max_sleep_seconds: Uploadcare.config.max_request_sleep) do response = get_upload_from_url_status(token) - raise RequestError if %w[progress waiting unknown].include?(response.success[:status]) + + if %w[progress waiting unknown].include?(response.success[:status]) + raise RequestError, 'Upload is taking longer than expected. Try increasing the max_request_tries config if you know your file uploads will take more time.' # rubocop:disable Layout/LineLength + end response end diff --git a/spec/uploadcare/entity/uploader_spec.rb b/spec/uploadcare/entity/uploader_spec.rb index 395ed14..c475ca1 100644 --- a/spec/uploadcare/entity/uploader_spec.rb +++ b/spec/uploadcare/entity/uploader_spec.rb @@ -53,6 +53,15 @@ module Entity expect(upload[0]).to be_kind_of(Uploadcare::Entity::File) end end + + it 'raises error with information if file upload takes time' do + Uploadcare.config.max_request_tries = 1 + VCR.use_cassette('upload_upload_from_url') do + url = 'https://placekitten.com/2250/2250' + error_str = 'Upload is taking longer than expected. Try increasing the max_request_tries config if you know your file uploads will take more time.' # rubocop:disable Layout/LineLength + expect { subject.upload(url) }.to raise_error(RequestError, error_str) + end + end end describe 'multipart_upload' do From fc50f5df6052db8521bdc2eb820b89399967e0cb Mon Sep 17 00:00:00 2001 From: Vipul A M Date: Sat, 9 Mar 2024 11:47:31 -0500 Subject: [PATCH 4/4] Drop support for Ruby < 3x (#154) * Unlock and upgrade all development dependencies. Remove Ruby 2.6 specific code. Upgrade Rubocop target version to 3x. Update ruby matrix in specs. Update readme and gemspec with required ruby versions. * Remove rubocop-rspec for now since there are lots of violations --- .github/workflows/ruby.yml | 7 +++---- .rubocop.yml | 6 +----- Gemfile | 12 ++++++------ README.md | 2 +- lib/uploadcare.rb | 12 ------------ spec/spec_helper.rb | 2 +- uploadcare-ruby.gemspec | 2 ++ 7 files changed, 14 insertions(+), 29 deletions(-) diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index bc822eb..dcfadc1 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -16,17 +16,17 @@ jobs: strategy: matrix: ruby-version: - - 2.7 - 3.0 - 3.1 - 3.2 + - 3.3 steps: - uses: actions/checkout@v3 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: - bundler: 2.4 + bundler: latest ruby-version: ${{ matrix.ruby-version }} bundler-cache: true - name: Run tests @@ -41,8 +41,7 @@ jobs: strategy: matrix: ruby-version: - - 2.7 - - 3.2 + - 3.3 steps: - uses: actions/checkout@v3 - name: Set up Ruby diff --git a/.rubocop.yml b/.rubocop.yml index f767bd1..4cc3b33 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,6 +1,6 @@ AllCops: NewCops: enable - TargetRubyVersion: 2.7 + TargetRubyVersion: 3.0 Layout/LineLength: Max: 120 @@ -14,10 +14,6 @@ Style/HashTransformKeys: - 'lib/uploadcare/client/conversion/video_conversion_client.rb' - 'lib/uploadcare/entity/file.rb' -Gemspec/RequiredRubyVersion: - Exclude: - - 'uploadcare-ruby.gemspec' - Metrics/BlockLength: Exclude: - 'bin/' diff --git a/Gemfile b/Gemfile index daf8bb8..d3f8c60 100644 --- a/Gemfile +++ b/Gemfile @@ -2,12 +2,12 @@ source 'https://rubygems.org' -gem 'byebug', '~> 11.1' -gem 'rake', '~> 13.0' -gem 'rspec', '~> 3.0' -gem 'rubocop', '~> 1.48' -gem 'vcr', '~> 6.1' -gem 'webmock', '~> 3.18' +gem 'byebug' +gem 'rake' +gem 'rspec' +gem 'rubocop' +gem 'vcr' +gem 'webmock' # Specify your gem's dependencies in uploadcare-ruby.gemspec gemspec diff --git a/README.md b/README.md index 49a9be9..c277815 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ wrapping Upload and REST APIs. * [Useful links](#useful-links) ## Requirements -* ruby 2.7+ +* ruby 3.0+ ## Compatibility diff --git a/lib/uploadcare.rb b/lib/uploadcare.rb index b42f226..72db20e 100644 --- a/lib/uploadcare.rb +++ b/lib/uploadcare.rb @@ -33,18 +33,6 @@ module Uploadcare extend Dry::Configurable - # NOTE: The dry-configurable gem has introduced the `default` keyword argument - # and deprecated the positional default argument in v0.13.0, which requires - # Ruby >= 2.6.0. In order to provide backwards compatibility and not disable - # deprecation warnings, we override the dry-configurable's `setting` DSL method. - def self.setting(name, default:, **options, &block) - if RUBY_VERSION < '2.6' - super(name, default, &block) - else - super - end - end - setting :public_key, default: ENV.fetch('UPLOADCARE_PUBLIC_KEY', '') setting :secret_key, default: ENV.fetch('UPLOADCARE_SECRET_KEY', '') setting :auth_type, default: 'Uploadcare' diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 5c41ab0..60ed59b 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -6,7 +6,7 @@ require 'byebug' require 'webmock/rspec' require 'uploadcare' -Dir[File.expand_path(File.join(File.dirname(__FILE__), 'support', '**', '*.rb'))].sort.each { |f| require f } +Dir[File.expand_path(File.join(File.dirname(__FILE__), 'support', '**', '*.rb'))].each { |f| require f } RSpec.configure do |config| include Uploadcare::Exception diff --git a/uploadcare-ruby.gemspec b/uploadcare-ruby.gemspec index 5ee8fca..4937600 100644 --- a/uploadcare-ruby.gemspec +++ b/uploadcare-ruby.gemspec @@ -43,6 +43,8 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ['lib', 'lib/uploadcare', 'lib/uploadcare/rest'] + spec.required_ruby_version = '>= 3.0' + spec.add_dependency 'mimemagic', '~> 0.4' spec.add_dependency 'parallel', '~> 1.22' spec.add_dependency 'retries', '~> 0.0'