skylab_studio 1.0.5 → 1.0.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 74d45652e950eb110f6d634681b5caad2830017e50da39a7610a3c2e148f5c8f
4
- data.tar.gz: 71f434342a60a6e1604b5d2258c00f2766d571a4d81959a494c6d9bc7e07cacd
3
+ metadata.gz: 549e0ebc3cedf74a848a299346f93ce8b77c6d8813383f7993c82e755fbf4d88
4
+ data.tar.gz: 32825398b0428f26bf8548b40a4eae238d24b97709ce408b021c343901a99835
5
5
  SHA512:
6
- metadata.gz: a2be823c425b7c9754d3c92e0777429fd5ad32bc795ac5dd1e1964f3299913fe527a805c856f6bab2eba787aafc5df26869126e95113a8b65b5acfd6320413d1
7
- data.tar.gz: 7223682dfcb1368c03e493b57b53377ff7f9085d9ba0a71e7f40a85894e0a9d8e3efe916eb289ae9f89adc7b0949c88ac4d6ce23e09aa7855f9477d81c43f460
6
+ metadata.gz: af7021375c6030fcf977ff50fd85f1a55947ff7d6a0f4c122b0d8dd55ed4265dc5cccfcb020f04636cda76310910fd09fc1dbcda2b0477937b761185d36f7e63
7
+ data.tar.gz: c5cac2e61a900dd0460e477c6c8ecf5a83d2ae3e69fb0503b780e71550f3c79f25e67e83cc49fdfd6dd7663ac4e558da0521eea7e2bd9d9b17f52aa8f92baf5f
@@ -1,4 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'error'
4
+ require 'fileutils'
5
+ require 'digest'
6
+ require 'open-uri'
7
+ require 'base64'
8
+ require 'net/http'
9
+ require 'vips'
10
+ require 'concurrent'
2
11
 
3
12
  module SkylabStudio
4
13
  class ClientNilArgument < Error; end
@@ -25,109 +34,364 @@ module SkylabStudio
25
34
  @configuration = SkylabStudio::Config.new(settings)
26
35
  end
27
36
 
28
- def list_jobs(options = {})
29
- SkylabStudio::Request.new(@configuration).get(:jobs, payload: options)
37
+ def list_jobs()
38
+ SkylabStudio::Request.new(@configuration).get(:jobs)
30
39
  end
31
40
 
32
41
  def create_job(options = {})
33
- validate_argument_presence options, :job
42
+ validate_argument_presence options, :name
43
+ validate_argument_presence options, :profile_id
34
44
 
35
- SkylabStudio::Request.new(@configuration).post(:jobs, payload: options)
45
+ SkylabStudio::Request.new(@configuration).post(:jobs, options)
36
46
  end
37
47
 
38
- def get_job(options = {})
39
- validate_argument_presence options, :id
48
+ def get_job(job_id)
49
+ validate_argument_presence nil, :job_id
40
50
 
41
- SkylabStudio::Request.new(@configuration).get("jobs/#{options[:id]}", payload: options)
51
+ SkylabStudio::Request.new(@configuration).get("jobs/#{job_id}")
42
52
  end
43
53
 
44
- def update_job(options = {})
45
- validate_argument_presence options, :id
46
- validate_argument_presence options, :job
54
+ def get_job_by_name(options = {})
55
+ validate_argument_presence options, :name
47
56
 
48
- SkylabStudio::Request.new(@configuration).patch("jobs/#{options[:id]}", payload: options)
57
+ SkylabStudio::Request.new(@configuration).get('jobs/find_by_name', options)
49
58
  end
50
59
 
51
- def delete_job(options = {})
52
- validate_argument_presence options, :id
60
+ def update_job(job_id, options = {})
61
+ validate_argument_presence nil, :job_id
53
62
 
54
- SkylabStudio::Request.new(@configuration).delete("jobs/#{options[:id]}")
63
+ SkylabStudio::Request.new(@configuration).patch("jobs/#{job_id}", options)
55
64
  end
56
65
 
57
- def process_job(options = {})
66
+ def queue_job(options = {})
58
67
  validate_argument_presence options, :id
59
68
 
60
- SkylabStudio::Request.new(@configuration).post("jobs/#{options[:id]}/process", payload: options)
69
+ SkylabStudio::Request.new(@configuration).post("jobs/#{options[:id]}/queue", options)
61
70
  end
62
71
 
63
- def cancel_job(options = {})
64
- validate_argument_presence options, :id
72
+ def fetch_jobs_in_front(job_id)
73
+ SkylabStudio::Request.new(@configuration).get("jobs/#{job_id}/jobs_in_front")
74
+ end
75
+
76
+ def delete_job(job_id)
77
+ validate_argument_presence nil, :job_id
65
78
 
66
- SkylabStudio::Request.new(@configuration).post("jobs/#{options[:id]}/cancel", payload: options)
79
+ SkylabStudio::Request.new(@configuration).delete("jobs/#{job_id}")
80
+ end
81
+
82
+ def cancel_job(job_id)
83
+ validate_argument_presence nil, :job_id
84
+
85
+ SkylabStudio::Request.new(@configuration).post("jobs/#{job_id}/cancel", nil)
67
86
  end
68
87
 
69
88
  def list_profiles(options = {})
70
- SkylabStudio::Request.new(@configuration).get(:profiles, payload: options)
89
+ SkylabStudio::Request.new(@configuration).get(:profiles, options)
71
90
  end
72
91
 
73
92
  def create_profile(options = {})
74
- validate_argument_presence options, :profile
93
+ validate_argument_presence options, :name
75
94
 
76
- SkylabStudio::Request.new(@configuration).post(:profiles, payload: options)
95
+ SkylabStudio::Request.new(@configuration).post(:profiles, options)
77
96
  end
78
97
 
79
- def get_profile(options = {})
80
- validate_argument_presence options, :id
98
+ def get_profile(profile_id)
99
+ validate_argument_presence nil, :profile_id
81
100
 
82
- SkylabStudio::Request.new(@configuration).get("profiles/#{options[:id]}", payload: options)
101
+ SkylabStudio::Request.new(@configuration).get("profiles/#{profile_id}")
83
102
  end
84
103
 
85
- def update_profile(options = {})
86
- validate_argument_presence options, :id
87
- validate_argument_presence options, :profile
104
+ def update_profile(profile_id, options = {})
105
+ validate_argument_presence nil, :profile_id
88
106
 
89
- SkylabStudio::Request.new(@configuration).patch("profiles/#{options[:id]}", payload: options)
107
+ SkylabStudio::Request.new(@configuration).patch("profiles/#{profile_id}", options)
90
108
  end
91
109
 
92
- def delete_profile(options = {})
93
- validate_argument_presence options, :id
110
+ def upload_job_photo(photo_path = nil, job_id = nil)
111
+ validate_argument_presence nil, job_id
112
+ validate_argument_presence nil, photo_path
94
113
 
95
- SkylabStudio::Request.new(@configuration).delete("profiles/#{options[:id]}")
114
+ upload_photo(photo_path, job_id, 'job')
96
115
  end
97
116
 
98
- def list_photos(options = {})
99
- SkylabStudio::Request.new(@configuration).get(:photos, payload: options)
117
+ def upload_profile_photo(photo_path = nil, profile_id = nil)
118
+ validate_argument_presence nil, :photo_path
119
+ validate_argument_presence nil, :profile_id
120
+
121
+ upload_photo(photo_path, profile_id, 'profile')
100
122
  end
101
123
 
102
- def create_photo(options = {})
103
- validate_argument_presence options, :photo
124
+ def get_photo(photo_id)
125
+ validate_argument_presence nil, :photo_id
104
126
 
105
- SkylabStudio::Request.new(@configuration).post(:photos, payload: options)
127
+ SkylabStudio::Request.new(@configuration).get("photos/#{photo_id}")
106
128
  end
107
129
 
108
- def get_photo(options = {})
109
- validate_argument_presence options, :id
110
-
111
- SkylabStudio::Request.new(@configuration).get("photos/#{options[:id]}", payload: options)
130
+ def get_job_photos(job_id)
131
+ SkylabStudio::Request.new(@configuration).get('photos/list_for_job', { job_id: job_id })
112
132
  end
113
133
 
114
- def update_photo(options = {})
115
- validate_argument_presence options, :id
116
- validate_argument_presence options, :photo
134
+ def delete_photo(photo_id)
135
+ validate_argument_presence nil, :photo_id
117
136
 
118
- SkylabStudio::Request.new(@configuration).patch("photos/#{options[:id]}", payload: options)
137
+ SkylabStudio::Request.new(@configuration).delete("photos/#{photo_id}")
119
138
  end
120
139
 
121
- def delete_photo(options = {})
122
- validate_argument_presence options, :id
140
+ def download_photo(photo_id, output_path, profile: nil, options: {})
141
+ file_name = ''
142
+
143
+ unless Dir.exist?(output_path)
144
+ # Must be a file path - separate output_path and file_name
145
+ file_name = File.basename(output_path)
146
+ output_path = File.dirname(output_path) || ''
147
+ end
148
+
149
+ begin
150
+ photo = get_photo(photo_id)
151
+ profile_id = photo['job']['profileId']
152
+
153
+ file_name = photo['name'] if file_name.empty?
154
+
155
+ profile ||= get_profile(profile_id)
156
+
157
+ is_extract = profile['enableExtract'].to_s == 'true'
158
+ replace_background = profile['replaceBackground'].to_s == 'true'
159
+ is_dual_file_output = profile['dualFileOutput'].to_s == 'true'
160
+ profile['enableStripPngMetadata'].to_s
161
+ bgs = options[:bgs]
162
+
163
+ # Load output image
164
+ image_buffer = download_image_async(photo['retouchedUrl'])
165
+ image = Vips::Image.new_from_buffer(image_buffer, '')
166
+
167
+ if is_extract # Output extract image
168
+ png_file_name = "#{File.basename(file_name, '.*')}.png"
123
169
 
124
- SkylabStudio::Request.new(@configuration).delete("photos/#{options[:id]}")
170
+ # Dual File Output will provide an image in the format specified in the outputFileType field
171
+ # and an extracted image as a PNG.
172
+ image.write_to_file(File.join(output_path, png_file_name)) if is_dual_file_output
173
+
174
+ download_replaced_background_image(file_name, image, output_path, profile: profile, bgs: bgs) if replace_background
175
+
176
+ # Regular Extract output
177
+ image.write_to_file(File.join(output_path, png_file_name)) unless is_dual_file_output || replace_background
178
+ else # Non-extracted regular image output
179
+ image.write_to_file(File.join(output_path, file_name))
180
+ end
181
+
182
+ puts "Successfully downloaded: #{file_name}"
183
+ [file_name, true]
184
+ rescue StandardError => e
185
+ error_msg = "Failed to download photo id: #{photo_id} - #{e}"
186
+ raise error_msg if options[:return_on_error].nil?
187
+
188
+ [file_name, false]
189
+ end
190
+ end
191
+
192
+ def download_all_photos(photos_list, profile, output_path)
193
+ raise 'Invalid output path' unless Dir.exist?(output_path)
194
+
195
+ success_photos = []
196
+ errored_photos = []
197
+ bgs = []
198
+
199
+ begin
200
+ profile = get_profile(profile['id'])
201
+ bgs = download_bg_images(profile) if profile && profile['photos']&.any?
202
+
203
+ photo_ids = photos_list.map { |photo| photo['id'].to_s }.compact
204
+
205
+ pool = Concurrent::FixedThreadPool.new(@configuration.settings[:max_download_concurrency])
206
+ download_tasks = []
207
+ photo_options = { return_on_error: true, bgs: bgs }
208
+ photo_ids.each do |photo_id|
209
+ download_tasks << Concurrent::Future.execute(executor: pool) do
210
+ download_photo(photo_id.to_i, output_path, profile: profile, options: photo_options)
211
+ end
212
+ end
213
+
214
+ # Wait for all download tasks to complete
215
+ results = download_tasks.map(&:value)
216
+ results.each do |result|
217
+ if result[1]
218
+ success_photos << result[0]
219
+ else
220
+ errored_photos << result[0]
221
+ end
222
+ end
223
+
224
+ { success_photos: success_photos, errored_photos: errored_photos }
225
+ rescue StandardError => e
226
+ warn e
227
+
228
+ { success_photos: success_photos, errored_photos: errored_photos }
229
+ end
125
230
  end
126
231
 
127
232
  private
128
233
 
234
+ def get_upload_url(options = { use_cache_upload: false })
235
+ SkylabStudio::Request.new(@configuration).get('photos/upload_url', options)
236
+ end
237
+
238
+ def create_photo(options = {})
239
+ SkylabStudio::Request.new(@configuration).post(:photos, options)
240
+ end
241
+
242
+ def upload_photo(photo_path, id, model = 'job')
243
+ valid_exts_to_check = %w[.jpg .jpeg .png .webp]
244
+
245
+ raise 'Invalid file type: must be of type jpg/jpeg/png/webp' unless valid_exts_to_check.any? { |ext| photo_path.downcase.end_with?(ext) }
246
+
247
+ file_size = File.size(photo_path)
248
+
249
+ raise 'Invalid file size: must be no larger than 27MB' if file_size > 27 * 1024 * 1024
250
+
251
+ photo_name = File.basename(photo_path)
252
+ photo_ext = File.extname(photo_name)
253
+ headers = {}
254
+ md5hash = ''
255
+
256
+ # Read file contents to binary
257
+ data = nil
258
+ File.open(photo_path, 'rb') do |file|
259
+ data = file.read
260
+ md5hash = Digest::MD5.hexdigest(data)
261
+ end
262
+
263
+ # model - either job or profile (job_id/profile_id)
264
+ photo_data = { "#{model}_id": id, name: "#{photo_name}#{photo_ext}", path: photo_path, 'use_cache_upload': false }
265
+
266
+ if model == 'job'
267
+ job_type = get_job(id)['type']
268
+
269
+ headers = { 'X-Amz-Tagging' => 'job=photo&api=true' } if job_type == 'regular'
270
+ end
271
+
272
+ # Ask studio to create the photo record
273
+ photo_response_json = create_photo(photo_data)
274
+
275
+ unless photo_response_json
276
+ raise 'Unable to create the photo object, if creating profile photo, ensure enable_extract and replace_background is set to: True'
277
+ end
278
+
279
+ photo_id = photo_response_json['id']
280
+
281
+ b64md5 = Base64.strict_encode64([md5hash].pack('H*'))
282
+ payload = {
283
+ 'use_cache_upload' => false,
284
+ 'photo_id' => photo_id,
285
+ 'content_md5' => b64md5
286
+ }
287
+
288
+ # Ask studio for a presigned url
289
+ upload_url_resp = get_upload_url(payload)
290
+ upload_url = upload_url_resp['url']
291
+
292
+ # PUT request to presigned url with image data
293
+ headers['Content-MD5'] = b64md5
294
+
295
+ begin
296
+ uri = URI(upload_url)
297
+ request = Net::HTTP::Put.new(uri, headers)
298
+ request.body = data
299
+ upload_photo_resp = Net::HTTP.start(uri.hostname) { |http| http.request(request) }
300
+
301
+ unless upload_photo_resp
302
+ puts 'First upload attempt failed, retrying...'
303
+ retry_count = 0
304
+ # Retry upload
305
+
306
+ while retry_count < 3
307
+ upload_photo_resp = Net::HTTP.start(uri.hostname) { |http| http.request(request) }
308
+ if upload_photo_resp
309
+ break # Upload was successful, exit the loop
310
+ elsif retry_count == 2 # Check if retry count is 2 (0-based indexing)
311
+ raise 'Unable to upload to the bucket after retrying.'
312
+ else
313
+ sleep(1) # Wait for a moment before retrying
314
+ retry_count += 1
315
+ end
316
+ end
317
+ end
318
+ rescue StandardError => e
319
+ puts "An exception of type #{e.class} occurred: #{e.message}"
320
+ puts 'Deleting created, but unuploaded photo...'
321
+ delete_photo({ id: photo_id }) if photo_id
322
+ end
323
+
324
+ photo_response_json
325
+ end
326
+
327
+ def download_image_async(image_url)
328
+ raise "Invalid retouchedUrl: \"#{image_url}\" - Please ensure the job is complete" unless image_url.start_with?('http', 'https')
329
+
330
+ begin
331
+ uri = URI(image_url)
332
+ response = Net::HTTP.get_response(uri)
333
+
334
+ if response.is_a?(Net::HTTPSuccess)
335
+ response.body
336
+
337
+ else
338
+ puts "Error downloading image: #{response.message}"
339
+ nil
340
+ end
341
+ rescue StandardError => e
342
+ puts "Error downloading image: #{e.message}"
343
+ nil
344
+ end
345
+ end
346
+
347
+ def download_bg_images(profile)
348
+ temp_bgs = []
349
+
350
+ bg_photos = profile['photos'].select { |photo| photo['jobId'].nil? }
351
+
352
+ bg_photos.each do |bg|
353
+ bg_buffer = download_image_async(bg['originalUrl'])
354
+ bg_image = Vips::Image.new_from_buffer(bg_buffer, '')
355
+ temp_bgs << bg_image
356
+ end
357
+
358
+ temp_bgs
359
+ end
360
+
361
+ def download_replaced_background_image(file_name, input_image, output_path, profile: nil, bgs: nil)
362
+ output_file_type = profile&.[]('outputFileType') || 'png'
363
+
364
+ bgs = download_bg_images(profile) if bgs.nil? && !profile.nil? && profile&.[]('photos')&.any?
365
+
366
+ alpha_channel = input_image[3]
367
+ rgb_channel = input_image[0..2]
368
+ rgb_cutout = rgb_channel.bandjoin(alpha_channel)
369
+
370
+ if bgs&.any?
371
+ bgs.each_with_index do |bg_image, i|
372
+ new_file_name = if i.zero?
373
+ "#{File.basename(file_name,
374
+ '.*')}.#{output_file_type}"
375
+ else
376
+ "#{File.basename(file_name,
377
+ '.*')} (#{i + 1}).#{output_file_type}"
378
+ end
379
+ resized_bg_image = bg_image.thumbnail_image(input_image.width, height: input_image.height, crop: :centre)
380
+ result_image = resized_bg_image.composite2(rgb_cutout, :over)
381
+ result_image.write_to_file(File.join(output_path, new_file_name))
382
+ end
383
+ end
384
+
385
+ true
386
+ rescue StandardError => e
387
+ error_msg = "Error downloading background image: #{e.message}"
388
+ raise error_msg
389
+ end
390
+
129
391
  def validate_argument_presence(options, key)
130
- raise SkylabStudio::ClientNilArgument, "#{key} cannot be nil" if options[key].nil?
392
+ raise SkylabStudio::ClientNilArgument, "#{key} cannot be nil" if options.is_a?(Hash) && options[key].nil?
393
+
394
+ raise SkylabStudio::ClientNilArgument, "#{key} cannot be nil" if key.nil?
131
395
  end
132
396
  end
133
397
  end
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'version'
2
4
 
3
5
  module SkylabStudio
4
6
  class Config
5
7
  attr_accessor :settings
6
8
 
7
- DEFAULT_URL = 'https://studio.skylabtech.ai'.freeze
9
+ DEFAULT_URL = 'https://studio.skylabtech.ai'
8
10
 
9
11
  def self.defaults
10
12
  source = URI.parse(DEFAULT_URL)
@@ -17,7 +19,8 @@ module SkylabStudio
17
19
  port: source.port,
18
20
  api_version: 'v1',
19
21
  debug: true,
20
- client_stub: "ruby-#{VERSION}"
22
+ client_stub: "ruby-#{VERSION}",
23
+ max_download_concurrency: 5
21
24
  }
22
25
  end
23
26
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SkylabStudio
2
4
  class Error < StandardError; end
3
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'error'
2
4
 
3
5
  module SkylabStudio
@@ -37,7 +39,7 @@ module SkylabStudio
37
39
  private
38
40
 
39
41
  def request_path(end_point)
40
- "/api/#{@configuration.api_version}/#{end_point}"
42
+ "/api/public/#{@configuration.api_version}/#{end_point}"
41
43
  end
42
44
 
43
45
  def use_ssl?
@@ -64,19 +66,19 @@ module SkylabStudio
64
66
  end
65
67
 
66
68
  def handle_response(response)
67
- case @response
68
- when Net::HTTPUnauthorized then
69
+ case response
70
+ when Net::HTTPUnauthorized
69
71
  raise SkylabStudio::ClientInvalidKey, 'Invalid api key'
70
- when Net::HTTPForbidden then
72
+ when Net::HTTPForbidden
71
73
  raise SkylabStudio::ClientInvalidKey, 'Invalid api key'
72
- when Net::HTTPNotFound then
74
+ when Net::HTTPNotFound
73
75
  raise SkylabStudio::ClientInvalidEndpoint, 'Resource not found'
74
- when Net::HTTPBadRequest then
76
+ when Net::HTTPBadRequest
75
77
  raise SkylabStudio::ClientBadRequest, 'There was an error processing your request'
76
- when Net::HTTPTooManyRequests then
78
+ when Net::HTTPTooManyRequests
77
79
  raise SkylabStudio::ClientBadRequest, 'The rate limit has been met'
78
80
  when Net::HTTPSuccess
79
- response
81
+ JSON.parse(response.body)
80
82
  else
81
83
  raise SkylabStudio::ClientUnknownError, 'An error has occurred'
82
84
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SkylabStudio
2
- VERSION = '1.0.5'.freeze
4
+ VERSION = '1.0.7'
3
5
  end
data/lib/skylab_studio.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SkylabStudio
2
4
  end
3
5
 
@@ -7,7 +9,7 @@ require 'net/https'
7
9
  require 'uri'
8
10
  require 'json'
9
11
 
10
- require 'skylab_studio/request'
11
- require 'skylab_studio/client'
12
- require 'skylab_studio/config'
13
- require 'skylab_studio/version' unless defined?(Skylab::VERSION)
12
+ require_relative 'skylab_studio/request'
13
+ require_relative 'skylab_studio/client'
14
+ require_relative 'skylab_studio/config'
15
+ require_relative 'skylab_studio/version' unless defined?(Skylab::VERSION)
@@ -1,11 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require File.expand_path('../../../lib/skylab_studio.rb', __dir__)
2
4
 
3
5
  RSpec.describe SkylabStudio::Client do
4
- before(:each) do
5
- @client = SkylabStudio::Client.new
6
- end
6
+ let(:client) { SkylabStudio::Client.new }
7
7
 
8
- subject { @client }
8
+ subject { client }
9
9
 
10
10
  it { should respond_to(:list_jobs) }
11
11
  it { should respond_to(:get_job) }
@@ -34,7 +34,7 @@ RSpec.describe SkylabStudio::Client do
34
34
  it 'should return response' do
35
35
  SkylabStudio::Request.any_instance.stub(:get).and_return(true)
36
36
 
37
- @client.list_jobs.should eq(true)
37
+ subject.list_jobs.should eq(true)
38
38
  end
39
39
  end
40
40
 
@@ -42,23 +42,19 @@ RSpec.describe SkylabStudio::Client do
42
42
  it 'should return response' do
43
43
  SkylabStudio::Request.any_instance.stub(:post).and_return(true)
44
44
 
45
- @client.create_job(
46
- job: {
47
- profile_id: 1
48
- }
49
- ).should eq(true)
45
+ subject.create_job({ name: 'test-job', profile_id: 1 }).should eq(true)
50
46
  end
51
47
  end
52
48
 
53
49
  describe '#get_job' do
54
50
  it 'should raise error with no id' do
55
- expect { @client.get_job }.to raise_error(SkylabStudio::ClientNilArgument)
51
+ expect { subject.get_job }.to raise_error(ArgumentError)
56
52
  end
57
53
 
58
54
  it 'should return response' do
59
55
  SkylabStudio::Request.any_instance.stub(:get).and_return(true)
60
56
 
61
- @client.get_job(id: 123).should eq(true)
57
+ subject.get_job(123).should eq(true)
62
58
  end
63
59
  end
64
60
 
@@ -66,12 +62,7 @@ RSpec.describe SkylabStudio::Client do
66
62
  it 'should return response' do
67
63
  SkylabStudio::Request.any_instance.stub(:patch).and_return(true)
68
64
 
69
- @client.update_job(
70
- id: 1,
71
- job: {
72
- profile_id: 2
73
- }
74
- ).should eq(true)
65
+ subject.update_job(1, { profile_id: 2 }).should eq(true)
75
66
  end
76
67
  end
77
68
 
@@ -79,17 +70,15 @@ RSpec.describe SkylabStudio::Client do
79
70
  it 'should return response' do
80
71
  SkylabStudio::Request.any_instance.stub(:delete).and_return(true)
81
72
 
82
- @client.delete_job(
83
- id: 1
84
- ).should eq(true)
73
+ subject.delete_job(1).should eq(true)
85
74
  end
86
75
  end
87
76
 
88
- describe '#process_job' do
77
+ describe '#queue_job' do
89
78
  it 'should return response' do
90
79
  SkylabStudio::Request.any_instance.stub(:post).and_return(true)
91
80
 
92
- @client.process_job(
81
+ subject.queue_job(
93
82
  id: 1
94
83
  ).should eq(true)
95
84
  end
@@ -99,9 +88,7 @@ RSpec.describe SkylabStudio::Client do
99
88
  it 'should return response' do
100
89
  SkylabStudio::Request.any_instance.stub(:post).and_return(true)
101
90
 
102
- @client.cancel_job(
103
- id: 1
104
- ).should eq(true)
91
+ subject.cancel_job(1).should eq(true)
105
92
  end
106
93
  end
107
94
 
@@ -109,7 +96,7 @@ RSpec.describe SkylabStudio::Client do
109
96
  it 'should return response' do
110
97
  SkylabStudio::Request.any_instance.stub(:get).and_return(true)
111
98
 
112
- @client.list_profiles.should eq(true)
99
+ subject.list_profiles.should eq(true)
113
100
  end
114
101
  end
115
102
 
@@ -117,8 +104,8 @@ RSpec.describe SkylabStudio::Client do
117
104
  it 'should return response' do
118
105
  SkylabStudio::Request.any_instance.stub(:post).and_return(true)
119
106
 
120
- @client.create_profile(
121
- profile: {
107
+ subject.create_profile(
108
+ {
122
109
  name: 'Foo'
123
110
  }
124
111
  ).should eq(true)
@@ -127,13 +114,13 @@ RSpec.describe SkylabStudio::Client do
127
114
 
128
115
  describe '#get_profile' do
129
116
  it 'should raise error with no id' do
130
- expect { @client.get_profile }.to raise_error(SkylabStudio::ClientNilArgument)
117
+ expect { subject.get_profile }.to raise_error(ArgumentError)
131
118
  end
132
119
 
133
120
  it 'should return response' do
134
121
  SkylabStudio::Request.any_instance.stub(:get).and_return(true)
135
122
 
136
- @client.get_profile(id: 123).should eq(true)
123
+ subject.get_profile(123).should eq(true)
137
124
  end
138
125
  end
139
126
 
@@ -141,67 +128,80 @@ RSpec.describe SkylabStudio::Client do
141
128
  it 'should return response' do
142
129
  SkylabStudio::Request.any_instance.stub(:patch).and_return(true)
143
130
 
144
- @client.update_profile(
145
- id: 1,
146
- profile: {
147
- name: 'Bar'
148
- }
149
- ).should eq(true)
131
+ subject.update_profile(1, { name: 'Bar' } ).should eq(true)
150
132
  end
151
133
  end
152
134
 
153
- describe '#delete_profile' do
154
- it 'should return response' do
155
- SkylabStudio::Request.any_instance.stub(:delete).and_return(true)
135
+ describe '#upload_job_photo' do
136
+ before do
137
+ stub_request(:post, 'https://studio.skylabtech.ai/api/public/v1/photos')
138
+ .to_return(status: 200, body: { id: 1, photo: {} }.to_json, headers: {})
156
139
 
157
- @client.delete_profile(
158
- id: 1
159
- ).should eq(true)
140
+ stub_request(:get, 'https://studio.skylabtech.ai/api/public/v1/photos/upload_url')
141
+ .to_return(status: 200, body: { url: 'http://test.test/' }.to_json, headers: {})
142
+
143
+ stub_request(:get, 'https://studio.skylabtech.ai/api/public/v1/jobs/1')
144
+ .to_return(status: 200, body: {}.to_json, headers: {})
145
+
146
+ stub_request(:delete, 'https://studio.skylabtech.ai/api/public/v1/photos/1')
147
+ .to_return(status: 200, body: { id: 1 }.to_json, headers: {})
148
+
149
+ stub_request(:put, /.*/)
150
+ .to_return(status: 200, body: {}.to_json, headers: {})
151
+
152
+ # allow_any_instance_of(Net::HTTPSuccess).to receive(:body) { { id: 1 }.to_json }
160
153
  end
161
- end
162
154
 
163
- describe '#list_photos' do
164
155
  it 'should return response' do
165
- SkylabStudio::Request.any_instance.stub(:get).and_return(true)
156
+ photo_path = "#{File.expand_path('../../', File.dirname(__FILE__))}/test-portrait-1.JPG"
157
+ id = 1
158
+
159
+ expected_response = { id: id, photo: {} }.to_json
166
160
 
167
- @client.list_photos.should eq(true)
161
+ expect(subject.upload_job_photo(photo_path, id).to_json).to eq(expected_response)
168
162
  end
169
163
  end
170
164
 
171
- describe '#create_photo' do
165
+ describe '#upload_profile_photo' do
166
+ before do
167
+ stub_request(:post, 'https://studio.skylabtech.ai/api/public/v1/photos')
168
+ .to_return(status: 200, body: { id: 1, photo: {} }.to_json, headers: {})
169
+
170
+ stub_request(:get, 'https://studio.skylabtech.ai/api/public/v1/photos/upload_url')
171
+ .to_return(status: 200, body: { url: 'http://test.test/' }.to_json, headers: {})
172
+
173
+ stub_request(:get, 'https://studio.skylabtech.ai/api/public/v1/profiles/upload_url')
174
+ .to_return(status: 200, body: { url: 'http://test.test/' }.to_json, headers: {})
175
+
176
+ stub_request(:get, 'https://studio.skylabtech.ai/api/public/v1/jobs/1')
177
+ .to_return(status: 200, body: {}.to_json, headers: {})
178
+
179
+ stub_request(:delete, 'https://studio.skylabtech.ai/api/public/v1/photos/1')
180
+ .to_return(status: 200, body: { id: 1 }.to_json, headers: {})
181
+
182
+ stub_request(:put, /.*/)
183
+ .to_return(status: 200, body: {}.to_json, headers: {})
184
+ end
185
+
172
186
  it 'should return response' do
173
- SkylabStudio::Request.any_instance.stub(:post).and_return(true)
187
+ photo_path = "#{File.expand_path('../../', File.dirname(__FILE__))}/test-portrait-1.JPG"
188
+ id = 1
174
189
 
175
- @client.create_photo(
176
- photo: {
177
- name: 'Foo'
178
- }
179
- ).should eq(true)
190
+ expected_response = { id: id, photo: {} }.to_json
191
+
192
+ expect(subject.upload_profile_photo(photo_path, 1).to_json).to eq(expected_response)
180
193
  end
181
194
  end
182
195
 
183
196
  describe '#get_photo' do
184
197
  it 'should raise error with no id' do
185
- expect { @client.get_photo }.to raise_error(SkylabStudio::ClientNilArgument)
198
+ expect { subject.get_photo }.to raise_error(ArgumentError)
186
199
  end
187
200
 
188
201
  it 'should return response' do
189
202
  SkylabStudio::Request.any_instance.stub(:get).and_return(true)
190
203
 
191
- @client.get_photo(id: 123).should eq(true)
192
- end
193
- end
194
-
195
- describe '#update_photo' do
196
- it 'should return response' do
197
- SkylabStudio::Request.any_instance.stub(:patch).and_return(true)
198
-
199
- @client.update_photo(
200
- id: 1,
201
- photo: {
202
- name: 'Bar'
203
- }
204
- ).should eq(true)
204
+ subject.get_photo(123).should eq(true)
205
205
  end
206
206
  end
207
207
 
@@ -209,9 +209,7 @@ RSpec.describe SkylabStudio::Client do
209
209
  it 'should return response' do
210
210
  SkylabStudio::Request.any_instance.stub(:delete).and_return(true)
211
211
 
212
- @client.delete_photo(
213
- id: 1
214
- ).should eq(true)
212
+ subject.delete_photo(1).should eq(true)
215
213
  end
216
214
  end
217
215
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require File.expand_path('../../../lib/skylab_studio.rb', __dir__)
2
4
 
3
5
  RSpec.describe SkylabStudio::Config do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require File.expand_path('../../../lib/skylab_studio.rb', __dir__)
2
4
 
3
5
  RSpec.describe SkylabStudio::Request do
@@ -12,9 +14,10 @@ RSpec.describe SkylabStudio::Request do
12
14
 
13
15
  describe '#post' do
14
16
  it 'should return success' do
15
- Net::HTTP.any_instance.stub(:request).and_return(Net::HTTPSuccess.new(1.0, 200, 'OK'))
17
+ allow_any_instance_of(Net::HTTP).to receive(:request) { Net::HTTPSuccess.new(1.0, 200, 'OK') }
18
+ allow_any_instance_of(Net::HTTPSuccess).to receive(:body) { {}.to_json }
16
19
 
17
- @request.post(:jobs, {}).should be_instance_of(Net::HTTPSuccess)
20
+ @request.post(:jobs, {}).should be_instance_of(Hash)
18
21
  end
19
22
 
20
23
  it 'should raise error on 401' do
@@ -62,9 +65,10 @@ RSpec.describe SkylabStudio::Request do
62
65
 
63
66
  describe '#get' do
64
67
  it 'should return success' do
65
- Net::HTTP.any_instance.stub(:request).and_return(Net::HTTPSuccess.new(1.0, 200, 'OK'))
68
+ allow_any_instance_of(Net::HTTP).to receive(:request) { Net::HTTPSuccess.new(1.0, 200, 'OK') }
69
+ allow_any_instance_of(Net::HTTPSuccess).to receive(:body) { {}.to_json }
66
70
 
67
- @request.get(:jobs, {}).should be_instance_of(Net::HTTPSuccess)
71
+ @request.get(:jobs, {}).should be_instance_of(Hash)
68
72
  end
69
73
 
70
74
  it 'should raise error on 401' do
@@ -112,9 +116,10 @@ RSpec.describe SkylabStudio::Request do
112
116
 
113
117
  describe '#delete' do
114
118
  it 'should return success' do
115
- Net::HTTP.any_instance.stub(:request).and_return(Net::HTTPSuccess.new(1.0, 200, 'OK'))
119
+ allow_any_instance_of(Net::HTTP).to receive(:request) { Net::HTTPSuccess.new(1.0, 200, 'OK') }
120
+ allow_any_instance_of(Net::HTTPSuccess).to receive(:body) { {}.to_json }
116
121
 
117
- @request.delete(:jobs).should be_instance_of(Net::HTTPSuccess)
122
+ @request.delete(:jobs).should be_instance_of(Hash)
118
123
  end
119
124
 
120
125
  it 'should raise error on 401' do
@@ -162,9 +167,10 @@ RSpec.describe SkylabStudio::Request do
162
167
 
163
168
  describe '#put' do
164
169
  it 'should return success' do
165
- Net::HTTP.any_instance.stub(:request).and_return(Net::HTTPSuccess.new(1.0, 200, 'OK'))
170
+ allow_any_instance_of(Net::HTTP).to receive(:request) { Net::HTTPSuccess.new(1.0, 200, 'OK') }
171
+ allow_any_instance_of(Net::HTTPSuccess).to receive(:body) { {}.to_json }
166
172
 
167
- @request.put(:jobs, {}).should be_instance_of(Net::HTTPSuccess)
173
+ @request.put(:jobs, {}).should be_instance_of(Hash)
168
174
  end
169
175
 
170
176
  it 'should raise error on 401' do
@@ -212,9 +218,10 @@ RSpec.describe SkylabStudio::Request do
212
218
 
213
219
  describe '#patch' do
214
220
  it 'should return success' do
215
- Net::HTTP.any_instance.stub(:request).and_return(Net::HTTPSuccess.new(1.0, 200, 'OK'))
221
+ allow_any_instance_of(Net::HTTP).to receive(:request) { Net::HTTPSuccess.new(1.0, 200, 'OK') }
222
+ allow_any_instance_of(Net::HTTPSuccess).to receive(:body) { {}.to_json }
216
223
 
217
- @request.patch(:jobs, {}).should be_instance_of(Net::HTTPSuccess)
224
+ @request.patch(:jobs, {}).should be_instance_of(Hash)
218
225
  end
219
226
 
220
227
  it 'should raise error on 401' do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe SkylabStudio::VERSION do
2
4
  it 'provides the current version number' do
3
5
  SkylabStudio::VERSION.nil?.should eq(false)
data/spec/spec_helper.rb CHANGED
@@ -1,4 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'simplecov'
4
+ require 'webmock/rspec'
2
5
 
3
6
  SimpleCov.start
4
7
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: skylab_studio
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.5
4
+ version: 1.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paul Lam
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-05-18 00:00:00.000000000 Z
11
+ date: 2024-03-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '3.8'
19
+ version: 3.12.0
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '3.8'
26
+ version: 3.12.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: shoulda-matchers
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - '='
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 3.1.2
33
+ version: 5.3.0
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - '='
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 3.1.2
40
+ version: 5.3.0
41
41
  description: studio.skylabtech.ai ruby client
42
42
  email:
43
43
  - info@skylabtech.ai
@@ -60,7 +60,7 @@ homepage: https://github.com/skylab-tech/studio_client_ruby
60
60
  licenses:
61
61
  - Apache-2.0
62
62
  metadata: {}
63
- post_install_message:
63
+ post_install_message:
64
64
  rdoc_options: []
65
65
  require_paths:
66
66
  - lib
@@ -68,7 +68,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
68
68
  requirements:
69
69
  - - ">="
70
70
  - !ruby/object:Gem::Version
71
- version: '0'
71
+ version: 2.7.0
72
72
  required_rubygems_version: !ruby/object:Gem::Requirement
73
73
  requirements:
74
74
  - - ">="
@@ -76,12 +76,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
76
76
  version: '0'
77
77
  requirements: []
78
78
  rubygems_version: 3.1.6
79
- signing_key:
79
+ signing_key:
80
80
  specification_version: 4
81
81
  summary: A HTTP client for accessing studio.skylabtech.ai services.
82
82
  test_files:
83
83
  - spec/spec_helper.rb
84
- - spec/lib/skylab_studio/config_spec.rb
84
+ - spec/lib/skylab_studio/request_spec.rb
85
85
  - spec/lib/skylab_studio/client_spec.rb
86
+ - spec/lib/skylab_studio/config_spec.rb
86
87
  - spec/lib/skylab_studio/version_spec.rb
87
- - spec/lib/skylab_studio/request_spec.rb