rb_splash 1.0.0
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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +1024 -0
- data/lib/rb_splash/http_client.rb +81 -0
- data/lib/rb_splash/response.rb +13 -0
- data/lib/rb_splash/url_config.rb +49 -0
- data/lib/rb_splash/version.rb +5 -0
- data/lib/rb_splash/wrap_splash_api.rb +404 -0
- data/lib/rb_splash/wrap_splash_error.rb +14 -0
- data/lib/rb_splash.rb +11 -0
- metadata +137 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'faraday'
|
|
4
|
+
require 'json'
|
|
5
|
+
|
|
6
|
+
require_relative 'response'
|
|
7
|
+
|
|
8
|
+
module RbSplash
|
|
9
|
+
class HttpClient
|
|
10
|
+
RETRYABLE_ERRORS = [
|
|
11
|
+
Faraday::TimeoutError,
|
|
12
|
+
Faraday::ConnectionFailed,
|
|
13
|
+
Net::OpenTimeout,
|
|
14
|
+
Net::ReadTimeout,
|
|
15
|
+
Net::ProtocolError,
|
|
16
|
+
IOError,
|
|
17
|
+
Errno::ECONNRESET,
|
|
18
|
+
Errno::ECONNREFUSED,
|
|
19
|
+
Errno::ETIMEDOUT
|
|
20
|
+
].freeze
|
|
21
|
+
|
|
22
|
+
def initialize(headers: {}, timeout: 10_000, retries: 2, retry_delay_ms: 100)
|
|
23
|
+
@headers = headers
|
|
24
|
+
@timeout = timeout
|
|
25
|
+
@retries = retries
|
|
26
|
+
@retry_delay_ms = retry_delay_ms
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def make_request(url, method, query_parameters: {}, body: nil)
|
|
30
|
+
raise ArgumentError, 'URL required' if url.nil? || url.empty?
|
|
31
|
+
|
|
32
|
+
last_error = nil
|
|
33
|
+
|
|
34
|
+
(0..@retries).each do |attempt|
|
|
35
|
+
return execute_request(url, method, query_parameters, body)
|
|
36
|
+
rescue *RETRYABLE_ERRORS => e
|
|
37
|
+
last_error = e
|
|
38
|
+
sleep(@retry_delay_ms / 1000.0) if @retry_delay_ms.positive? && attempt < @retries
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
raise last_error
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def execute_request(url, method, query_parameters, body)
|
|
47
|
+
conn = Faraday.new do |f|
|
|
48
|
+
f.adapter Faraday.default_adapter
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
response = conn.send(method.downcase.to_sym, url) do |req|
|
|
52
|
+
req.headers.merge!(@headers)
|
|
53
|
+
req.params.merge!(query_parameters.transform_keys(&:to_s)) if query_parameters.any?
|
|
54
|
+
req.body = body.to_json if body
|
|
55
|
+
req.options.timeout = @timeout / 1000.0
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
data = response.body.to_s.empty? ? {} : parse_json(response.body)
|
|
59
|
+
|
|
60
|
+
result = Response.new(
|
|
61
|
+
status: response.status,
|
|
62
|
+
status_text: response.reason_phrase,
|
|
63
|
+
data: data
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
unless response.status.between?(200, 299)
|
|
67
|
+
error_message = "HTTP #{response.status}: #{response.reason_phrase}"
|
|
68
|
+
error_message += " - #{response.body}" if response.body && !response.body.empty?
|
|
69
|
+
raise StandardError, error_message
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
result
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def parse_json(body)
|
|
76
|
+
JSON.parse(body)
|
|
77
|
+
rescue JSON::ParserError
|
|
78
|
+
{ raw: body }
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RbSplash
|
|
4
|
+
module UrlConfig
|
|
5
|
+
API_LOCATION = 'https://api.unsplash.com/'
|
|
6
|
+
BEARER_TOKEN_URL = 'https://unsplash.com/oauth/token'
|
|
7
|
+
|
|
8
|
+
USERS_PUBLIC_PROFILE = 'users/'
|
|
9
|
+
USERS_PORTFOLIO = 'users/:username/portfolio'
|
|
10
|
+
USERS_PHOTOS = 'users/:username/photos'
|
|
11
|
+
USERS_LIKED_PHOTOS = 'users/:username/likes'
|
|
12
|
+
USERS_COLLECTIONS = 'users/:username/collections'
|
|
13
|
+
USERS_STATISTICS = 'users/:username/statistics'
|
|
14
|
+
|
|
15
|
+
LIST_PHOTOS = 'photos'
|
|
16
|
+
LIST_CURATED_PHOTOS = 'photos/curated'
|
|
17
|
+
GET_A_PHOTO = 'photos/:id'
|
|
18
|
+
GET_A_RANDOM_PHOTO = 'photos/random'
|
|
19
|
+
GET_A_PHOTO_STATISTICS = 'photos/:id/statistics'
|
|
20
|
+
GET_A_PHOTO_DOWNLOAD_LINK = 'photos/:id/download'
|
|
21
|
+
UPDATE_A_PHOTO = 'photos/:id'
|
|
22
|
+
LIKE_A_PHOTO = 'photos/:id/like'
|
|
23
|
+
UNLIKE_A_PHOTO = 'photos/:id/like'
|
|
24
|
+
|
|
25
|
+
SEARCH_PHOTOS = 'search/photos'
|
|
26
|
+
SEARCH_COLLECTIONS = 'search/collections'
|
|
27
|
+
SEARCH_USERS = 'search/users'
|
|
28
|
+
|
|
29
|
+
CURRENT_USER_PROFILE = 'me'
|
|
30
|
+
UPDATE_CURRENT_USER_PROFILE = 'me'
|
|
31
|
+
|
|
32
|
+
STATS_TOTALS = 'stats/total'
|
|
33
|
+
STATS_MONTH = 'stats/month'
|
|
34
|
+
|
|
35
|
+
LIST_COLLECTIONS = 'collections'
|
|
36
|
+
LIST_FEATURED_COLLECTIONS = 'collections/featured'
|
|
37
|
+
LIST_CURATED_COLLECTIONS = 'collections/curated'
|
|
38
|
+
GET_COLLECTION = 'collections/:id'
|
|
39
|
+
GET_CURATED_COLLECTION = 'collections/curated/:id'
|
|
40
|
+
GET_COLLECTION_PHOTOS = 'collections/:id/photos'
|
|
41
|
+
GET_CURATED_COLLECTION_PHOTOS = 'collections/curated/:id/photos'
|
|
42
|
+
LIST_RELATED_COLLECTION = 'collections/:id/related'
|
|
43
|
+
CREATE_NEW_COLLECTION = 'collections'
|
|
44
|
+
UPDATE_EXISTING_COLLECTION = 'collections/:id'
|
|
45
|
+
DELETE_COLLECTION = 'collections/:id'
|
|
46
|
+
ADD_PHOTO_TO_COLLECTION = 'collections/:collection_id/add'
|
|
47
|
+
REMOVE_PHOTO_FROM_COLLECTION = 'collections/:collection_id/remove'
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'digest'
|
|
4
|
+
require 'concurrent/promises'
|
|
5
|
+
|
|
6
|
+
module RbSplash
|
|
7
|
+
class WrapSplashApi
|
|
8
|
+
AVAILABLE_ORDERS = %w[latest oldest popular].freeze
|
|
9
|
+
AVAILABLE_ORIENTATIONS = %w[landscape portrait squarish].freeze
|
|
10
|
+
|
|
11
|
+
def initialize
|
|
12
|
+
@api_location = UrlConfig::API_LOCATION
|
|
13
|
+
@bearer_token_url = UrlConfig::BEARER_TOKEN_URL
|
|
14
|
+
@access_key = ''
|
|
15
|
+
@secret_key = ''
|
|
16
|
+
@redirect_uri = ''
|
|
17
|
+
@code = ''
|
|
18
|
+
@grant_type = 'authorization_code'
|
|
19
|
+
@bearer_token = ''
|
|
20
|
+
@timeout = 10_000
|
|
21
|
+
@retries = 2
|
|
22
|
+
@retry_delay_ms = 100
|
|
23
|
+
@headers = {
|
|
24
|
+
'Content-type' => 'application/json',
|
|
25
|
+
'X-Requested-With' => 'WrapSplash'
|
|
26
|
+
}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def init(options = {})
|
|
30
|
+
raise WrapSplashError, 'Initialisation parameters required!' unless options.is_a?(Hash) && !options.empty?
|
|
31
|
+
|
|
32
|
+
@timeout = options[:timeout].is_a?(Numeric) && options[:timeout].positive? ? options[:timeout] : 10_000
|
|
33
|
+
@retries = options[:retries].is_a?(Numeric) && options[:retries] >= 0 ? options[:retries] : 2
|
|
34
|
+
raw_timeout = options[:retry_delay_ms]
|
|
35
|
+
valid_timeout = raw_timeout.is_a?(Numeric) && raw_timeout >= 0
|
|
36
|
+
@retry_delay_ms = valid_timeout ? raw_timeout : 100
|
|
37
|
+
@bearer_token = options[:bearer_token] || ''
|
|
38
|
+
|
|
39
|
+
@headers = {
|
|
40
|
+
'Content-type' => 'application/json',
|
|
41
|
+
'X-Requested-With' => 'WrapSplash'
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if options[:bearer_token]
|
|
45
|
+
@headers['Authorization'] = "Bearer #{options[:bearer_token]}"
|
|
46
|
+
@headers['X-WrapSplash-Header'] = compute_hash(options[:bearer_token])
|
|
47
|
+
return
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
@access_key = options[:access_key] || raise(WrapSplashError, 'Access Key missing!')
|
|
51
|
+
@secret_key = options[:secret_key] || raise(WrapSplashError, 'Secret Key missing!')
|
|
52
|
+
@redirect_uri = options[:redirect_uri] || raise(WrapSplashError, 'Redirect URI missing!')
|
|
53
|
+
@code = options[:code] || raise(WrapSplashError, 'Authorization Code missing!')
|
|
54
|
+
|
|
55
|
+
@headers['Authorization'] = "Client-ID #{options[:access_key]}"
|
|
56
|
+
@headers['X-WrapSplash-Header'] = compute_hash(options[:access_key])
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def generate_bearer_token
|
|
60
|
+
validate_required(@access_key, 'access_key')
|
|
61
|
+
validate_required(@secret_key, 'secret_key')
|
|
62
|
+
validate_required(@redirect_uri, 'redirect_uri')
|
|
63
|
+
validate_required(@code, 'code')
|
|
64
|
+
|
|
65
|
+
result = fetch_url(@bearer_token_url, 'POST', {}, {
|
|
66
|
+
client_id: @access_key,
|
|
67
|
+
client_secret: @secret_key,
|
|
68
|
+
redirect_uri: @redirect_uri,
|
|
69
|
+
code: @code,
|
|
70
|
+
grant_type: @grant_type
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
clear_credentials!
|
|
74
|
+
|
|
75
|
+
result
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def get_current_user_profile
|
|
79
|
+
fetch_url(@api_location + UrlConfig::CURRENT_USER_PROFILE, 'GET')
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def update_current_user_profile(username: nil, first_name: nil, last_name: nil, email: nil, url: nil,
|
|
83
|
+
location: nil, bio: nil, instagram_username: nil)
|
|
84
|
+
fetch_url(@api_location + UrlConfig::UPDATE_CURRENT_USER_PROFILE, 'PUT', {
|
|
85
|
+
username: username, first_name: first_name, last_name: last_name,
|
|
86
|
+
email: email, url: url, location: location, bio: bio,
|
|
87
|
+
instagram_username: instagram_username
|
|
88
|
+
})
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def get_public_profile(username, width: nil, height: nil)
|
|
92
|
+
validate_required(username, 'username')
|
|
93
|
+
fetch_url(@api_location + UrlConfig::USERS_PUBLIC_PROFILE + username, 'GET', { w: width, h: height })
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def get_user_portfolio(username)
|
|
97
|
+
validate_required(username, 'username')
|
|
98
|
+
fetch_url(@api_location + UrlConfig::USERS_PORTFOLIO.gsub(':username', username), 'GET')
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def get_user_photos(username, page: 1, per_page: 10, stats: false, resolution: 'days', quantity: 30,
|
|
102
|
+
order_by: 'latest')
|
|
103
|
+
validate_required(username, 'username')
|
|
104
|
+
validate_supported_value(order_by, AVAILABLE_ORDERS, 'order_by')
|
|
105
|
+
fetch_url(@api_location + UrlConfig::USERS_PHOTOS.gsub(':username', username), 'GET', {
|
|
106
|
+
page: page, per_page: per_page, order_by: order_by,
|
|
107
|
+
stats: stats, resolution: resolution, quantity: quantity
|
|
108
|
+
})
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def get_user_liked_photos(username, page: 1, per_page: 10, order_by: 'latest')
|
|
112
|
+
validate_required(username, 'username')
|
|
113
|
+
validate_supported_value(order_by, AVAILABLE_ORDERS, 'order_by')
|
|
114
|
+
fetch_url(@api_location + UrlConfig::USERS_LIKED_PHOTOS.gsub(':username', username), 'GET', {
|
|
115
|
+
page: page, per_page: per_page, order_by: order_by
|
|
116
|
+
})
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def get_user_collections(username, page: 1, per_page: 10)
|
|
120
|
+
validate_required(username, 'username')
|
|
121
|
+
fetch_url(@api_location + UrlConfig::USERS_COLLECTIONS.gsub(':username', username), 'GET', {
|
|
122
|
+
page: page, per_page: per_page
|
|
123
|
+
})
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def get_user_statistics(username, resolution: 'days', quantity: 30)
|
|
127
|
+
validate_required(username, 'username')
|
|
128
|
+
fetch_url(@api_location + UrlConfig::USERS_STATISTICS.gsub(':username', username), 'GET', {
|
|
129
|
+
resolution: resolution, quantity: quantity
|
|
130
|
+
})
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def list_photos(page: 1, per_page: 10, order_by: 'latest')
|
|
134
|
+
validate_supported_value(order_by, AVAILABLE_ORDERS, 'order_by')
|
|
135
|
+
fetch_url(@api_location + UrlConfig::LIST_PHOTOS, 'GET', {
|
|
136
|
+
page: page, per_page: per_page, order_by: order_by
|
|
137
|
+
})
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def list_curated_photos(page: 1, per_page: 10, order_by: 'latest')
|
|
141
|
+
validate_supported_value(order_by, AVAILABLE_ORDERS, 'order_by')
|
|
142
|
+
fetch_url(@api_location + UrlConfig::LIST_CURATED_PHOTOS, 'GET', {
|
|
143
|
+
page: page, per_page: per_page, order_by: order_by
|
|
144
|
+
})
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def get_a_photo(id, width: nil, height: nil, rect: nil)
|
|
148
|
+
validate_required(id, 'id')
|
|
149
|
+
fetch_url(@api_location + UrlConfig::GET_A_PHOTO.gsub(':id', id), 'GET', {
|
|
150
|
+
w: width, h: height, rect: rect
|
|
151
|
+
})
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
alias get_photo get_a_photo
|
|
155
|
+
|
|
156
|
+
def get_a_random_photo(collections: nil, featured: false, username: nil, query: nil, width: nil, height: nil,
|
|
157
|
+
orientation: 'landscape', count: 1)
|
|
158
|
+
validate_supported_value(orientation, AVAILABLE_ORIENTATIONS, 'orientation')
|
|
159
|
+
fetch_url(@api_location + UrlConfig::GET_A_RANDOM_PHOTO, 'GET', {
|
|
160
|
+
collections: collections&.to_s, featured: featured, username: username,
|
|
161
|
+
query: query, width: width, height: height, orientation: orientation, count: count
|
|
162
|
+
})
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
alias get_random_photo get_a_random_photo
|
|
166
|
+
|
|
167
|
+
def get_photo_statistics(id, resolution: 'days', quantity: 30)
|
|
168
|
+
validate_required(id, 'id')
|
|
169
|
+
fetch_url(@api_location + UrlConfig::GET_A_PHOTO_STATISTICS.gsub(':id', id), 'GET', {
|
|
170
|
+
resolution: resolution, quantity: quantity
|
|
171
|
+
})
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def get_photo_link(id)
|
|
175
|
+
validate_required(id, 'id')
|
|
176
|
+
fetch_url(@api_location + UrlConfig::GET_A_PHOTO_DOWNLOAD_LINK.gsub(':id', id), 'GET')
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def update_photo(id, location: {}, exif: {})
|
|
180
|
+
validate_required(id, 'id')
|
|
181
|
+
params = {}
|
|
182
|
+
params['location[latitude]'] = location[:latitude] if location[:latitude]
|
|
183
|
+
params['location[longitude]'] = location[:longitude] if location[:longitude]
|
|
184
|
+
params['location[name]'] = location[:name] if location[:name]
|
|
185
|
+
params['location[city]'] = location[:city] if location[:city]
|
|
186
|
+
params['location[country]'] = location[:country] if location[:country]
|
|
187
|
+
params['location[confidential]'] = location[:confidential] if location[:confidential]
|
|
188
|
+
params['exif[make]'] = exif[:make] if exif[:make]
|
|
189
|
+
params['exif[model]'] = exif[:model] if exif[:model]
|
|
190
|
+
params['exif[exposure_time]'] = exif[:exposure_time] if exif[:exposure_time]
|
|
191
|
+
params['exif[aperture_value]'] = exif[:aperture_value] if exif[:aperture_value]
|
|
192
|
+
params['exif[focal_length]'] = exif[:focal_length] if exif[:focal_length]
|
|
193
|
+
params['exif[iso_speed_ratings]'] = exif[:iso_speed_ratings] if exif[:iso_speed_ratings]
|
|
194
|
+
fetch_url(@api_location + UrlConfig::UPDATE_A_PHOTO.gsub(':id', id), 'PUT', params)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def like_photo(id)
|
|
198
|
+
validate_required(id, 'id')
|
|
199
|
+
fetch_url(@api_location + UrlConfig::LIKE_A_PHOTO.gsub(':id', id), 'POST')
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def unlike_photo(id)
|
|
203
|
+
validate_required(id, 'id')
|
|
204
|
+
fetch_url(@api_location + UrlConfig::UNLIKE_A_PHOTO.gsub(':id', id), 'DELETE')
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def search(query, page: 1, per_page: 10, collections: nil, orientation: nil)
|
|
208
|
+
validate_required(query, 'query')
|
|
209
|
+
validate_supported_value(orientation, AVAILABLE_ORIENTATIONS, 'orientation')
|
|
210
|
+
fetch_url(@api_location + UrlConfig::SEARCH_PHOTOS, 'GET', {
|
|
211
|
+
query: query, page: page, per_page: per_page,
|
|
212
|
+
collections: collections&.to_s, orientation: orientation
|
|
213
|
+
})
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def search_collections(query, page: 1, per_page: 10)
|
|
217
|
+
validate_required(query, 'query')
|
|
218
|
+
fetch_url(@api_location + UrlConfig::SEARCH_COLLECTIONS, 'GET', {
|
|
219
|
+
query: query, page: page, per_page: per_page
|
|
220
|
+
})
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def search_users(query, page: 1, per_page: 10)
|
|
224
|
+
validate_required(query, 'query')
|
|
225
|
+
fetch_url(@api_location + UrlConfig::SEARCH_USERS, 'GET', {
|
|
226
|
+
query: query, page: page, per_page: per_page
|
|
227
|
+
})
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def get_stats_totals
|
|
231
|
+
fetch_url(@api_location + UrlConfig::STATS_TOTALS, 'GET')
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def get_stats_month
|
|
235
|
+
fetch_url(@api_location + UrlConfig::STATS_MONTH, 'GET')
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def list_collections(page: 1, per_page: 10)
|
|
239
|
+
fetch_url(@api_location + UrlConfig::LIST_COLLECTIONS, 'GET', {
|
|
240
|
+
page: page, per_page: per_page
|
|
241
|
+
})
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def list_featured_collections(page: 1, per_page: 10)
|
|
245
|
+
fetch_url(@api_location + UrlConfig::LIST_FEATURED_COLLECTIONS, 'GET', {
|
|
246
|
+
page: page, per_page: per_page
|
|
247
|
+
})
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def list_curated_collections(page: 1, per_page: 10)
|
|
251
|
+
fetch_url(@api_location + UrlConfig::LIST_CURATED_COLLECTIONS, 'GET', {
|
|
252
|
+
page: page, per_page: per_page
|
|
253
|
+
})
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def get_collection(id)
|
|
257
|
+
validate_required(id, 'id')
|
|
258
|
+
fetch_url(@api_location + UrlConfig::GET_COLLECTION.gsub(':id', id), 'GET')
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def get_curated_collection(id)
|
|
262
|
+
validate_required(id, 'id')
|
|
263
|
+
fetch_url(@api_location + UrlConfig::GET_CURATED_COLLECTION.gsub(':id', id), 'GET')
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def get_collection_photos(id, page: 1, per_page: 10)
|
|
267
|
+
validate_required(id, 'id')
|
|
268
|
+
fetch_url(@api_location + UrlConfig::GET_COLLECTION_PHOTOS.gsub(':id', id), 'GET', {
|
|
269
|
+
page: page, per_page: per_page
|
|
270
|
+
})
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def get_curated_collection_photos(id, page: 1, per_page: 10)
|
|
274
|
+
validate_required(id, 'id')
|
|
275
|
+
fetch_url(@api_location + UrlConfig::GET_CURATED_COLLECTION_PHOTOS.gsub(':id', id), 'GET', {
|
|
276
|
+
page: page, per_page: per_page
|
|
277
|
+
})
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def list_related_collections(id)
|
|
281
|
+
validate_required(id, 'id')
|
|
282
|
+
fetch_url(@api_location + UrlConfig::LIST_RELATED_COLLECTION.gsub(':id', id), 'GET')
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def create_new_collection(title, description: nil, private_collection: false)
|
|
286
|
+
validate_required(title, 'title')
|
|
287
|
+
fetch_url(@api_location + UrlConfig::CREATE_NEW_COLLECTION, 'POST', {
|
|
288
|
+
title: title, description: description, private: private_collection
|
|
289
|
+
})
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
alias create_collection create_new_collection
|
|
293
|
+
|
|
294
|
+
def update_existing_collection(id, title, description: nil, private_collection: false)
|
|
295
|
+
validate_required(id, 'id')
|
|
296
|
+
validate_required(title, 'title')
|
|
297
|
+
fetch_url(@api_location + UrlConfig::UPDATE_EXISTING_COLLECTION.gsub(':id', id), 'PUT', {
|
|
298
|
+
title: title, description: description, private: private_collection
|
|
299
|
+
})
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
alias update_collection update_existing_collection
|
|
303
|
+
|
|
304
|
+
def delete_collection(id)
|
|
305
|
+
validate_required(id, 'id')
|
|
306
|
+
fetch_url(@api_location + UrlConfig::DELETE_COLLECTION.gsub(':id', id), 'DELETE')
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
def add_photo_to_collection(collection_id, photo_id)
|
|
310
|
+
validate_required(collection_id, 'collection_id')
|
|
311
|
+
validate_required(photo_id, 'photo_id')
|
|
312
|
+
fetch_url(@api_location + UrlConfig::ADD_PHOTO_TO_COLLECTION.gsub(':collection_id', collection_id), 'POST', {
|
|
313
|
+
photo_id: photo_id
|
|
314
|
+
})
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
def remove_photo_from_collection(collection_id, photo_id)
|
|
318
|
+
validate_required(collection_id, 'collection_id')
|
|
319
|
+
validate_required(photo_id, 'photo_id')
|
|
320
|
+
url = UrlConfig::REMOVE_PHOTO_FROM_COLLECTION.gsub(':collection_id', collection_id)
|
|
321
|
+
fetch_url(@api_location + url, 'DELETE', { photo_id: photo_id })
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
private
|
|
325
|
+
|
|
326
|
+
def compute_hash(value)
|
|
327
|
+
Digest::SHA256.hexdigest(value)
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def validate_required(value, field_name)
|
|
331
|
+
return if value && !value.to_s.empty?
|
|
332
|
+
|
|
333
|
+
message = case field_name
|
|
334
|
+
when 'id' then 'Parameter : id is required!'
|
|
335
|
+
when 'query' then 'Parameter : query is missing!'
|
|
336
|
+
else "Parameter : #{field_name} is required and cannot be empty!"
|
|
337
|
+
end
|
|
338
|
+
raise WrapSplashError, message
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
def validate_supported_value(value, allowed_values, field_name)
|
|
342
|
+
return if value.nil?
|
|
343
|
+
return if allowed_values.include?(value)
|
|
344
|
+
|
|
345
|
+
raise WrapSplashError, "Parameter : #{field_name} has an unsupported value!"
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
def build_query_parameters(params)
|
|
349
|
+
params.select { |_, v| !v.nil? && !v.to_s.empty? }
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
def fetch_url(url, method, query_parameters = {}, body = nil)
|
|
353
|
+
Concurrent::Promises.future do
|
|
354
|
+
http = HttpClient.new(
|
|
355
|
+
headers: @headers,
|
|
356
|
+
timeout: @timeout,
|
|
357
|
+
retries: @retries,
|
|
358
|
+
retry_delay_ms: @retry_delay_ms
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
response = http.make_request(url, method, query_parameters: build_query_parameters(query_parameters),
|
|
362
|
+
body: body)
|
|
363
|
+
|
|
364
|
+
if response.status == 204
|
|
365
|
+
{ status: 204, status_text: response.status_text, message: 'Content Deleted' }
|
|
366
|
+
elsif response.status == 403
|
|
367
|
+
{ status: 403, status_text: response.status_text, message: 'Rate Limit Exceeded' }
|
|
368
|
+
else
|
|
369
|
+
response.data
|
|
370
|
+
end
|
|
371
|
+
end.rescue do |e|
|
|
372
|
+
raise create_wrap_splash_error(e)
|
|
373
|
+
end
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
def create_wrap_splash_error(error)
|
|
377
|
+
return error if error.is_a?(WrapSplashError)
|
|
378
|
+
|
|
379
|
+
status_code = error.respond_to?(:response) && error.response&.dig(:status)
|
|
380
|
+
status_text = error.respond_to?(:response) && error.response&.dig(:status_text)
|
|
381
|
+
|
|
382
|
+
WrapSplashError.new(
|
|
383
|
+
get_error_message(error),
|
|
384
|
+
status_code: status_code,
|
|
385
|
+
status_text: status_text,
|
|
386
|
+
cause: error
|
|
387
|
+
)
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
def get_error_message(error)
|
|
391
|
+
return error.message if error.is_a?(StandardError)
|
|
392
|
+
return error if error.is_a?(String)
|
|
393
|
+
|
|
394
|
+
'Request failed'
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
def clear_credentials!
|
|
398
|
+
@access_key = nil
|
|
399
|
+
@secret_key = nil
|
|
400
|
+
@redirect_uri = nil
|
|
401
|
+
@code = nil
|
|
402
|
+
end
|
|
403
|
+
end
|
|
404
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RbSplash
|
|
4
|
+
class WrapSplashError < StandardError
|
|
5
|
+
attr_reader :status_code, :status_text, :cause_error
|
|
6
|
+
|
|
7
|
+
def initialize(message, status_code: nil, status_text: nil, cause: nil)
|
|
8
|
+
super(message)
|
|
9
|
+
@status_code = status_code
|
|
10
|
+
@status_text = status_text
|
|
11
|
+
@cause_error = cause
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
data/lib/rb_splash.rb
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'rb_splash/version'
|
|
4
|
+
require_relative 'rb_splash/url_config'
|
|
5
|
+
require_relative 'rb_splash/wrap_splash_error'
|
|
6
|
+
require_relative 'rb_splash/response'
|
|
7
|
+
require_relative 'rb_splash/http_client'
|
|
8
|
+
require_relative 'rb_splash/wrap_splash_api'
|
|
9
|
+
|
|
10
|
+
module RbSplash
|
|
11
|
+
end
|