skylab_studio 1.0.5 → 1.0.7

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 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