uploadcare-ruby 1.2.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -1
  3. data/.rspec +1 -0
  4. data/.travis.yml +19 -5
  5. data/CHANGELOG.md +12 -30
  6. data/README.md +149 -72
  7. data/Rakefile +1 -1
  8. data/UPGRADE_NOTES.md +36 -0
  9. data/lib/uploadcare.rb +16 -22
  10. data/lib/uploadcare/api.rb +3 -1
  11. data/lib/uploadcare/api/file_list_api.rb +15 -4
  12. data/lib/uploadcare/api/file_storage_api.rb +34 -0
  13. data/lib/uploadcare/api/group_list_api.rb +13 -4
  14. data/lib/uploadcare/api/raw_api.rb +10 -16
  15. data/lib/uploadcare/api/uploading_api.rb +45 -84
  16. data/lib/uploadcare/api/uploading_api/upload_params.rb +72 -0
  17. data/lib/uploadcare/api/validators/file_list_options_validator.rb +73 -0
  18. data/lib/uploadcare/api/validators/group_list_options_validator.rb +49 -0
  19. data/lib/uploadcare/resources/file_list.rb +6 -33
  20. data/lib/uploadcare/resources/group_list.rb +6 -23
  21. data/lib/uploadcare/resources/resource_list.rb +83 -0
  22. data/lib/uploadcare/rest/connections/api_connection.rb +25 -3
  23. data/lib/uploadcare/rest/connections/upload_connection.rb +3 -2
  24. data/lib/uploadcare/version.rb +1 -1
  25. data/spec/api/file_list_api_spec.rb +95 -0
  26. data/spec/api/file_storage_api_spec.rb +88 -0
  27. data/spec/api/group_list_api_spec.rb +59 -0
  28. data/spec/api/raw_api_spec.rb +12 -12
  29. data/spec/api/uploading_api/upload_params_spec.rb +99 -0
  30. data/spec/api/uploading_api_spec.rb +59 -0
  31. data/spec/resources/file_list_spec.rb +13 -52
  32. data/spec/resources/file_spec.rb +6 -3
  33. data/spec/resources/group_list_spec.rb +15 -20
  34. data/spec/rest/api_connection_spec.rb +1 -1
  35. data/spec/shared/resource_list.rb +188 -0
  36. data/spec/spec_helper.rb +11 -1
  37. data/spec/uploadcare_spec.rb +9 -32
  38. data/spec/utils/parser_spec.rb +34 -36
  39. data/uploadcare-ruby.gemspec +7 -6
  40. metadata +52 -37
  41. data/lib/uploadcare/utils/user_agent.rb +0 -44
  42. data/spec/uploading/uploading_multiple_spec.rb +0 -43
  43. data/spec/uploading/uploading_spec.rb +0 -40
  44. data/spec/utils/user_agent_spec.rb +0 -46
@@ -2,36 +2,30 @@ require 'faraday'
2
2
  require 'json'
3
3
  require 'ostruct'
4
4
 
5
- require 'uploadcare/api'
6
- require 'uploadcare/version'
5
+ require_relative 'uploadcare/api'
6
+ require_relative 'uploadcare/version'
7
7
 
8
8
  module Uploadcare
9
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
- }
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.5',
16
+ cache_files: true,
17
+ autostore: :auto,
18
+ auth_scheme: :secure,
19
+ }
20
+
21
+ USER_AGENT = "uploadcare-ruby/#{Gem.ruby_version}/#{Uploadcare::VERSION}"
19
22
 
20
23
  def self.default_settings
21
24
  DEFAULT_SETTINGS
22
25
  end
23
26
 
24
27
  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
28
-
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
28
+ return options[:user_agent].to_s if options[:user_agent]
29
+ [USER_AGENT, options[:public_key]].join('/')
36
30
  end
37
31
  end
@@ -5,6 +5,7 @@ Dir[File.dirname(__FILE__) + '/errors/*.rb'].each {|file| require file }
5
5
  Dir[File.dirname(__FILE__) + '/rest/middlewares/*.rb'].each {|file| require file }
6
6
  Dir[File.dirname(__FILE__) + '/rest/connections/*.rb'].each {|file| require file }
7
7
  Dir[File.dirname(__FILE__) + '/rest/auth/*.rb'].sort.each {|file| require file }
8
+ Dir[File.dirname(__FILE__) + '/api/validators/*.rb'].each {|file| require file }
8
9
  Dir[File.dirname(__FILE__) + '/api/*.rb'].each {|file| require file }
9
10
  Dir[File.dirname(__FILE__) + '/resources/*.rb'].each {|file| require file }
10
11
 
@@ -20,5 +21,6 @@ module Uploadcare
20
21
  include Uploadcare::FileListApi
21
22
  include Uploadcare::GroupApi
22
23
  include Uploadcare::GroupListApi
24
+ include Uploadcare::FileStorageApi
23
25
  end
24
- end
26
+ end
@@ -1,8 +1,19 @@
1
1
  module Uploadcare
2
2
  module FileListApi
3
- def file_list page=1
4
- data = get '/files/', {page: page}
5
- list = Uploadcare::Api::FileList.new self, data
3
+ # Available options:
4
+ #
5
+ # limit -- a number of objects retrieved per request. Default: 100
6
+ # ordering -- sorting order of files in a list. Default: datetime_uploaded
7
+ # from -- a starting point for filtering files.
8
+ # stored -- true to include only stored files, false to exclude.
9
+ # removed -- true to include only removed files, false to exclude. Default: false
10
+ #
11
+ # Documentation: http://uploadcare.com/documentation/rest/#file-files
12
+ def file_list options={}
13
+ Validators::FileListOptionsValidator.new(options).validate
14
+
15
+ data = get '/files/', options
16
+ Uploadcare::Api::FileList.new self, data, options
6
17
  end
7
18
  end
8
- end
19
+ end
@@ -0,0 +1,34 @@
1
+ module Uploadcare
2
+ module FileStorageApi
3
+ MAX_BATCH_SIZE = 100
4
+ UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/
5
+
6
+ def store_files(objects)
7
+ if objects.size > MAX_BATCH_SIZE
8
+ raise ArgumentError, "Up to #{MAX_BATCH_SIZE} files are supported per request, #{objects.size} given"
9
+ end
10
+
11
+ put "/files/storage/", to_uuids(objects)
12
+ end
13
+
14
+ def delete_files(objects)
15
+ if objects.size > MAX_BATCH_SIZE
16
+ raise ArgumentError, "Up to #{MAX_BATCH_SIZE} files are supported per request, #{objects.size} given"
17
+ end
18
+
19
+ delete "/files/storage/", to_uuids(objects)
20
+ end
21
+
22
+ private
23
+
24
+ def to_uuids(objects)
25
+ objects.map do |object|
26
+ case object
27
+ when Uploadcare::Api::File then object.uuid
28
+ when UUID_REGEX then object
29
+ else raise(ArgumentError, "Unable to convert object to uuid: #{object}")
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -1,8 +1,17 @@
1
1
  module Uploadcare
2
2
  module GroupListApi
3
- def group_list from=nil, limit=nil
4
- data = get '/groups/', {from: from, limit: limit}.reject{|_,v| v.nil?}
5
- list = Uploadcare::Api::GroupList.new self, data
3
+ # Available options:
4
+ #
5
+ # limit -- a number of objects retrieved per request. Default: 100
6
+ # ordering -- sorting order of groups in a list. Default: datetime_creataed
7
+ # from -- a starting point for filtering groups.
8
+ #
9
+ # Documentation: http://uploadcare.com/documentation/rest/#group-groups
10
+ def group_list options={}
11
+ Validators::GroupListOptionsValidator.new(options).validate
12
+
13
+ data = get '/groups/', options
14
+ list = Uploadcare::Api::GroupList.new self, data, options
6
15
  end
7
16
  end
8
- end
17
+ end
@@ -2,43 +2,37 @@ require 'json'
2
2
 
3
3
  module Uploadcare
4
4
  module RawApi
5
-
6
- def initialize options={}
5
+ def initialize(options = {})
7
6
  @options = Uploadcare::default_settings.merge(options)
8
7
  @api_connection = Uploadcare::Connections::ApiConnection.new(@options)
9
8
  @upload_connection = Uploadcare::Connections::UploadConnection.new(@options)
10
9
  end
11
10
 
12
-
13
- # basic request method
14
- def request method = :get, path = "/files/", params = {}
15
- response = @api_connection.send method, path, params
11
+ # basic request method
12
+ def request(method = :get, path = '/files/', params = {})
13
+ response = @api_connection.send method, path, params
16
14
  response.body
17
15
  end
18
16
  alias_method :api_request, :request
19
17
 
20
-
21
18
  # request with GET verb
22
- def get path= "/files/", params={}
19
+ def get(path = '/files/', params = {})
23
20
  request :get, path, params
24
21
  end
25
22
 
26
-
27
23
  # request with POST verb
28
- def post path= "/files/", params={}
29
- request :post, path, params
24
+ def post(path = '/files/', params = {})
25
+ request :post, path, params
30
26
  end
31
27
 
32
-
33
28
  # request with PUT verb
34
- def put path= "/files/", params={}
29
+ def put(path = '/files/', params = {})
35
30
  request :put, path, params
36
31
  end
37
32
 
38
-
39
33
  # request with DELETE verb
40
- def delete path= "/files/", params={}
34
+ def delete(path = '/files/', params = {})
41
35
  request :delete, path, params
42
36
  end
43
37
  end
44
- end
38
+ end
@@ -1,110 +1,71 @@
1
- require "uri"
2
- require 'mime/types'
1
+ require_relative 'uploading_api/upload_params'
3
2
 
4
3
  module Uploadcare
5
4
  module UploadingApi
6
- # intelegent guess for file or url uploading
7
- def upload object
8
- # if object is file - uploading it as file
9
- if object.kind_of?(File)
10
- upload_file(object)
11
-
12
- # if a string - try to upload as url
13
- elsif object.kind_of?(String)
14
- upload_url(object)
15
-
16
- # array of files
17
- elsif object.kind_of?(Array)
18
- upload_files(object)
19
-
5
+ # intelegent guess for file or URL uploading
6
+ def upload(object, options = {})
7
+ case object
8
+ when File then upload_file(object, options)
9
+ when Array then upload_files(object, options)
10
+ # if object is a string, try to upload it as an URL
11
+ when String then upload_url(object, options)
20
12
  else
21
- raise ArgumentError.new "you should give File object, array of files or valid url string"
13
+ raise ArgumentError, "Expected `object` to be an Uploadcare::Api::File, "\
14
+ "an Array or a valid URL string, received: `#{object}`"
22
15
  end
23
16
  end
24
17
 
18
+ # Upload multiple files
19
+ def upload_files(files, options = {})
20
+ data = upload_params(options).for_file_upload(files)
25
21
 
26
- def upload_files files
27
- raise ArgumentError.new "one or more of given files is not actually files" if files.select {|f| !f.kind_of?(File)}.any?
28
-
29
- data = {UPLOADCARE_PUB_KEY: @options[:public_key]}
22
+ response = @upload_connection.post('/base/', data)
30
23
 
31
- files.each_with_index do |file, i|
32
- data["file[#{i}]"] = build_upload_io(file)
33
- end
34
-
35
- response = @upload_connection.send :post, '/base/', data
36
- uuids = response.body
37
-
38
- files = uuids.values.map! {|f| Uploadcare::Api::File.new self, f }
24
+ response.body.values.map! { |f| Uploadcare::Api::File.new(self, f) }
39
25
  end
40
26
 
41
-
42
- # upload file to servise
43
- def upload_file file
44
- raise ArgumentError.new 'expecting File object' unless file.kind_of?(File)
45
-
46
- response = @upload_connection.send :post, '/base/', {
47
- UPLOADCARE_PUB_KEY: @options[:public_key],
48
- file: build_upload_io(file)
49
- }
50
-
51
- uuid = response.body["file"]
52
-
53
- Uploadcare::Api::File.new self, uuid
27
+ # Upload single file
28
+ def upload_file(file, options = {})
29
+ upload_files([file], options).first
54
30
  end
55
-
56
- # create file is the same as uplaod file
57
31
  alias_method :create_file, :upload_file
58
32
 
33
+ # Upload from an URL
34
+ def upload_url(url, options = {})
35
+ params = upload_params(options).for_url_upload(url)
36
+ token = request_file_upload(params)
59
37
 
60
- #upload from url
61
- def upload_url url
62
- uri = URI.parse(url)
63
-
64
- raise ArgumentError.new 'invalid url was given' unless uri.kind_of?(URI::HTTP)
65
-
66
- token = get_token(url)
67
-
68
- while !['success', 'error'].include?((response = get_status_response(token))['status'])
69
- sleep 0.5
38
+ upload_status = poll_upload_result(token)
39
+ if upload_status['status'] == 'error'
40
+ raise ArgumentError.new(upload_status['error'])
70
41
  end
71
-
72
- raise ArgumentError.new(response['error']) if response['status'] == 'error'
73
- uuid = response['file_id']
74
- Uploadcare::Api::File.new self, uuid
42
+
43
+ Uploadcare::Api::File.new(self, upload_status['file_id'])
75
44
  end
76
45
  alias_method :upload_from_url, :upload_url
77
46
 
78
-
79
-
80
-
81
- protected
82
- # DEPRECATRED but still works
83
- def upload_request method, path, params = {}
84
- response = @upload_connection.send method, path, params
85
- end
86
-
87
- def build_upload_io file
88
- Faraday::UploadIO.new file.path, extract_mime_type(file)
89
- end
90
-
91
-
92
47
  private
93
- def get_status_response token
94
- response = @upload_connection.send :post, '/from_url/status/', {token: token}
95
- response.body
96
- end
97
-
98
48
 
99
- def get_token url
100
- response = @upload_connection.send :post, '/from_url/', { source_url: url, pub_key: @options[:public_key] }
101
- token = response.body["token"]
102
- end
49
+ def get_status_response(token)
50
+ response = @upload_connection.post('/from_url/status/', {token: token})
51
+ response.body
52
+ end
103
53
 
54
+ def request_file_upload(upload_params)
55
+ response = @upload_connection.post('/from_url/', upload_params)
56
+ token = response.body['token']
57
+ end
104
58
 
105
- def extract_mime_type file
106
- types = MIME::Types.of(file.path)
107
- types[0].content_type
59
+ def poll_upload_result(token)
60
+ while true
61
+ response = get_status_response(token)
62
+ break(response) if ['success', 'error'].include?(response['status'])
63
+ sleep 0.5
108
64
  end
65
+ end
66
+
67
+ def upload_params(request_options)
68
+ UploadParams.new(@options, request_options)
69
+ end
109
70
  end
110
71
  end
@@ -0,0 +1,72 @@
1
+ require 'uri'
2
+ require 'mime/types'
3
+
4
+ module Uploadcare
5
+ module UploadingApi
6
+ class UploadParams
7
+ def initialize(global_options, request_options)
8
+ @global_options = global_options
9
+ @request_options = request_options
10
+ end
11
+
12
+ def for_url_upload(url)
13
+ {
14
+ source_url: parse_url(url),
15
+ pub_key: public_key,
16
+ store: store
17
+ }.reject { |k, v| v.nil? }
18
+ end
19
+
20
+ def for_file_upload(files)
21
+ {
22
+ UPLOADCARE_PUB_KEY: public_key,
23
+ UPLOADCARE_STORE: store
24
+ }.reject { |k, v| v.nil? }.merge(file_params(files))
25
+ end
26
+
27
+ private
28
+
29
+ attr_reader :global_options, :request_options
30
+
31
+ def public_key
32
+ global_options[:public_key]
33
+ end
34
+
35
+ def store
36
+ mapping = { true => 1, false => 0, auto: 'auto' }
37
+
38
+ global_value = global_options[:autostore]
39
+ per_request_value = request_options[:store]
40
+
41
+ mapping[per_request_value] || mapping[global_value]
42
+ end
43
+
44
+ def file_params(files)
45
+ Hash[files.map.with_index { |file, i| ["file[#{i}]", build_upload_io(file)] }]
46
+ end
47
+
48
+ def parse_url(url)
49
+ uri = URI.parse(url)
50
+
51
+ unless uri.is_a?(URI::HTTP) # will also be true for https
52
+ raise ArgumentError, 'invalid url was given'
53
+ end
54
+
55
+ uri
56
+ end
57
+
58
+ def build_upload_io(file)
59
+ unless file.is_a?(File)
60
+ raise ArgumentError, "expected File object, #{file} given"
61
+ end
62
+
63
+ Faraday::UploadIO.new file.path, extract_mime_type(file)
64
+ end
65
+
66
+ def extract_mime_type file
67
+ types = MIME::Types.of(file.path)
68
+ types[0].content_type
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,73 @@
1
+ module Uploadcare
2
+ module Validators
3
+
4
+ class FileListOptionsValidator
5
+ SUPPORTED_KEYS = [:from, :ordering, :limit, :stored, :removed]
6
+
7
+ def initialize(options)
8
+ @options = options
9
+ end
10
+
11
+ def validate
12
+ check_for_unsupported_keys(@options)
13
+
14
+ validate_limit(@options[:limit])
15
+ validate_stored(@options[:stored])
16
+ validate_removed(@options[:removed])
17
+ validate_ordering_and_from(@options[:ordering], @options[:from])
18
+ end
19
+
20
+ private
21
+
22
+ def check_for_unsupported_keys(options)
23
+ unsupported_keys = options.keys.reject{|k,_| SUPPORTED_KEYS.include?(k)}
24
+ error("Unknown options: #{unsupported_keys}") if unsupported_keys.any?
25
+ end
26
+
27
+ def validate_ordering_and_from(ordering, from)
28
+ case ordering
29
+ when nil, /^-?datetime_uploaded$/
30
+ validate_from_as_date(from)
31
+ when /^-?size$/
32
+ validate_from_as_size(from)
33
+ else
34
+ error("Unknown value for :ordering option: #{ordering.inspect}")
35
+ end
36
+ end
37
+
38
+ def validate_from_as_date(from)
39
+ return if from.nil? || from.to_s =~ /^\d{4}-\d{2}-\d{2}T\d{2}.*/
40
+ error(":from value should be a DateTime or an iso8601 string when "\
41
+ "ordering is `datetime_uploaded` or `-datetime_uploaded`, "\
42
+ "#{from.inspect} given")
43
+ end
44
+
45
+ def validate_from_as_size(from)
46
+ return if from.nil? || (from.is_a?(Integer) && from >= 0)
47
+ error(":from value should be a positive integer when ordering is "\
48
+ "`size` or `-size`, #{from.inspect} given")
49
+ end
50
+
51
+ def validate_limit(limit)
52
+ return if limit.nil? || (limit.is_a?(Integer) && (1..1000).include?(limit))
53
+ error(":limit should be a positive integer from 1 to 1000, "\
54
+ "#{limit.inspect} given")
55
+ end
56
+
57
+ def validate_stored(stored)
58
+ return if [nil, true, false].include?(stored)
59
+ error(":stored can be true or false, #{stored.inspect} given")
60
+ end
61
+
62
+ def validate_removed(removed)
63
+ return if [nil, true, false].include?(removed)
64
+ error(":removed can be true or false, #{removed.inspect} given")
65
+ end
66
+
67
+ def error(message)
68
+ raise ArgumentError, message
69
+ end
70
+ end
71
+
72
+ end
73
+ end