transloadit 3.1.1 → 4.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 +4 -4
- data/.gitattributes +44 -2
- data/.github/workflows/ci.yml +18 -15
- data/.gitignore +1 -1
- data/.standard.yml +2 -0
- data/CHANGELOG.md +14 -0
- data/CONTRIBUTING.md +7 -4
- data/Dockerfile +1 -1
- data/MIGRATING_TO_V4.md +136 -0
- data/README.md +3 -0
- data/chameleon.jpg +0 -0
- data/lib/transloadit/assembly.rb +13 -4
- data/lib/transloadit/exception.rb +14 -6
- data/lib/transloadit/request.rb +54 -22
- data/lib/transloadit/response.rb +36 -34
- data/lib/transloadit/version.rb +1 -1
- data/lib/transloadit.rb +1 -1
- data/test/test_helper.rb +26 -0
- data/test/unit/test_transloadit.rb +1 -1
- data/test/unit/transloadit/test_assembly.rb +91 -8
- data/test/unit/transloadit/test_request.rb +1 -19
- data/test/unit/transloadit/test_response.rb +33 -31
- data/test/unit/transloadit/test_transport_compatibility.rb +222 -0
- data/transloadit.gemspec +5 -4
- metadata +33 -14
data/test/test_helper.rb
CHANGED
|
@@ -72,3 +72,29 @@ module TransloaditCliHelpers
|
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
Minitest::Test.include(TransloaditCliHelpers)
|
|
75
|
+
|
|
76
|
+
module SingletonMethodOverrideHelpers
|
|
77
|
+
def with_singleton_method(target, name, implementation)
|
|
78
|
+
eigenclass = target.singleton_class
|
|
79
|
+
|
|
80
|
+
backup = "__orig_#{name}_for_test__"
|
|
81
|
+
had_method = eigenclass.method_defined?(name) || eigenclass.private_method_defined?(name)
|
|
82
|
+
eigenclass.send(:alias_method, backup, name) if had_method
|
|
83
|
+
|
|
84
|
+
target.define_singleton_method(name, &implementation)
|
|
85
|
+
yield
|
|
86
|
+
ensure
|
|
87
|
+
eigenclass = target.singleton_class
|
|
88
|
+
if eigenclass.method_defined?(name) || eigenclass.private_method_defined?(name)
|
|
89
|
+
eigenclass.send(:remove_method, name)
|
|
90
|
+
end
|
|
91
|
+
if had_method
|
|
92
|
+
eigenclass.send(:alias_method, name, backup)
|
|
93
|
+
if eigenclass.method_defined?(backup) || eigenclass.private_method_defined?(backup)
|
|
94
|
+
eigenclass.send(:remove_method, backup)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
Minitest::Test.include(SingletonMethodOverrideHelpers)
|
|
@@ -109,7 +109,7 @@ describe Transloadit do
|
|
|
109
109
|
|
|
110
110
|
it "must produce Transloadit-compatible JSON output" do
|
|
111
111
|
fixed_time = Time.utc(2025, 10, 28, 0, 0, 0)
|
|
112
|
-
Time
|
|
112
|
+
with_singleton_method(Time, :now, proc { fixed_time }) do
|
|
113
113
|
_(@transloadit.to_json).must_equal MultiJson.dump(@transloadit.to_hash)
|
|
114
114
|
end
|
|
115
115
|
end
|
|
@@ -64,9 +64,13 @@ describe Transloadit::Assembly do
|
|
|
64
64
|
|
|
65
65
|
it "must send the signature before any file" do
|
|
66
66
|
transloadit = Transloadit.new(key: "", secret: "foo")
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
File.open("lib/transloadit/version.rb") do |file|
|
|
68
|
+
Transloadit::Assembly.new(
|
|
69
|
+
transloadit
|
|
70
|
+
).create! file
|
|
71
|
+
|
|
72
|
+
_(file.closed?).must_equal false
|
|
73
|
+
end
|
|
70
74
|
|
|
71
75
|
assert_requested(:post, "https://api2.transloadit.com/assemblies") do |req|
|
|
72
76
|
position_params = req.body.index 'name="params"'
|
|
@@ -76,6 +80,7 @@ describe Transloadit::Assembly do
|
|
|
76
80
|
_(position_params).wont_be_nil
|
|
77
81
|
_(position_signature).wont_be_nil
|
|
78
82
|
_(position_file).wont_be_nil
|
|
83
|
+
_(req.body).must_include 'filename="version.rb"'
|
|
79
84
|
|
|
80
85
|
_(position_params < position_signature).must_equal true
|
|
81
86
|
_(position_signature < position_file).must_equal true
|
|
@@ -146,12 +151,14 @@ describe Transloadit::Assembly do
|
|
|
146
151
|
it "must call the create! method with the same parameters" do
|
|
147
152
|
VCR.use_cassette "submit_assembly" do
|
|
148
153
|
file = open("lib/transloadit/version.rb")
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
154
|
+
create_arg = nil
|
|
155
|
+
with_singleton_method(@assembly, :create!, proc { |arg|
|
|
156
|
+
create_arg = arg
|
|
157
|
+
nil
|
|
158
|
+
}) do
|
|
152
159
|
@assembly.submit!(file)
|
|
153
160
|
end
|
|
154
|
-
|
|
161
|
+
_(create_arg).must_equal file
|
|
155
162
|
end
|
|
156
163
|
end
|
|
157
164
|
end
|
|
@@ -171,9 +178,12 @@ describe Transloadit::Assembly do
|
|
|
171
178
|
@assembly.options[:tries] = 1
|
|
172
179
|
|
|
173
180
|
VCR.use_cassette "rate_limit_succeed" do
|
|
174
|
-
assert_raises Transloadit::Exception::RateLimitReached do
|
|
181
|
+
error = assert_raises Transloadit::Exception::RateLimitReached do
|
|
175
182
|
@assembly.create! open("lib/transloadit/version.rb")
|
|
176
183
|
end
|
|
184
|
+
|
|
185
|
+
_(error.response["error"]).must_equal "RATE_LIMIT_REACHED"
|
|
186
|
+
_(error.message).must_equal "Transloadit Rate Limit Reached. Retry in 0 seconds"
|
|
177
187
|
end
|
|
178
188
|
end
|
|
179
189
|
|
|
@@ -211,6 +221,79 @@ describe Transloadit::Assembly do
|
|
|
211
221
|
end
|
|
212
222
|
end
|
|
213
223
|
|
|
224
|
+
describe "with multiple hash steps" do
|
|
225
|
+
it "must wrap its steps into one hash" do
|
|
226
|
+
hash_1 = {encode: {robot: "/video/encode"}, resize: {robot: "/image/resize"}}
|
|
227
|
+
hash_2 = {thumbs: {robot: "/video/thumbs"}}
|
|
228
|
+
|
|
229
|
+
assembly = Transloadit::Assembly.new @transloadit,
|
|
230
|
+
steps: [hash_1, hash_2]
|
|
231
|
+
(hash_1.keys + hash_2.keys).each do |key|
|
|
232
|
+
_(assembly.to_hash[:steps].keys).must_include key
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
it "must not allow duplicate steps" do
|
|
237
|
+
thumbs = {thumbs: {robot: "/video/thumbs"}}
|
|
238
|
+
thumbs_duplicate = {thumbs: {robot: "/video/encode"}}
|
|
239
|
+
assembly = Transloadit::Assembly.new @transloadit,
|
|
240
|
+
steps: [thumbs, thumbs_duplicate]
|
|
241
|
+
assert_raises ArgumentError do
|
|
242
|
+
assembly.create! open("lib/transloadit/version.rb")
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
describe "with mixture of hashes and steps" do
|
|
248
|
+
it "must wrap its steps into one hash" do
|
|
249
|
+
hash = {encode: {robot: "/video/encode"}, resize: {robot: "/image/resize"}}
|
|
250
|
+
step = @transloadit.step "thumbs", "/video/thumbs"
|
|
251
|
+
|
|
252
|
+
assembly = Transloadit::Assembly.new @transloadit,
|
|
253
|
+
steps: [hash, step]
|
|
254
|
+
|
|
255
|
+
hash.keys.each do |key|
|
|
256
|
+
_(assembly.to_hash[:steps].keys).must_include key
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
_(assembly.to_hash[:steps].keys).must_include step.name
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
it "must not allow duplicate steps" do
|
|
263
|
+
hash = {thumbs: {robot: "/video/thumbs"}}
|
|
264
|
+
step = @transloadit.step "thumbs", "/video/thumbs"
|
|
265
|
+
|
|
266
|
+
assembly = Transloadit::Assembly.new @transloadit,
|
|
267
|
+
steps: [hash, step]
|
|
268
|
+
|
|
269
|
+
assert_raises ArgumentError do
|
|
270
|
+
assembly.create! open("lib/transloadit/version.rb")
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
describe "with unsupported step class" do
|
|
276
|
+
it "raises error" do
|
|
277
|
+
assembly = Transloadit::Assembly.new @transloadit,
|
|
278
|
+
steps: Class.new
|
|
279
|
+
|
|
280
|
+
assert_raises ArgumentError do
|
|
281
|
+
assembly.create! open("lib/transloadit/version.rb")
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
describe "with unsupported array element" do
|
|
287
|
+
it "raises error" do
|
|
288
|
+
assembly = Transloadit::Assembly.new @transloadit,
|
|
289
|
+
steps: [Class.new]
|
|
290
|
+
|
|
291
|
+
assert_raises ArgumentError do
|
|
292
|
+
assembly.create! open("lib/transloadit/version.rb")
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
|
|
214
297
|
describe "using assembly API methods" do
|
|
215
298
|
include WebMock::API
|
|
216
299
|
|
|
@@ -83,25 +83,6 @@ describe Transloadit::Request do
|
|
|
83
83
|
lib_path = File.expand_path("../../../lib", __dir__)
|
|
84
84
|
|
|
85
85
|
Dir.mktmpdir do |stub_dir|
|
|
86
|
-
File.write(File.join(stub_dir, "rest-client.rb"), <<~RUBY)
|
|
87
|
-
module RestClient
|
|
88
|
-
class Response; end
|
|
89
|
-
|
|
90
|
-
class Resource
|
|
91
|
-
def initialize(*); end
|
|
92
|
-
def [](*); self; end
|
|
93
|
-
def get(*); Response.new; end
|
|
94
|
-
def post(*); Response.new; end
|
|
95
|
-
def put(*); Response.new; end
|
|
96
|
-
def delete(*); Response.new; end
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
module Exceptions
|
|
100
|
-
class OpenTimeout < StandardError; end
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
RUBY
|
|
104
|
-
|
|
105
86
|
File.write(File.join(stub_dir, "multi_json.rb"), <<~RUBY)
|
|
106
87
|
require "json"
|
|
107
88
|
|
|
@@ -123,6 +104,7 @@ describe Transloadit::Request do
|
|
|
123
104
|
begin
|
|
124
105
|
require "transloadit/request"
|
|
125
106
|
Transloadit::Request.new("/")
|
|
107
|
+
raise "RestClient was loaded" if defined?(RestClient)
|
|
126
108
|
rescue StandardError => e
|
|
127
109
|
warn e.full_message
|
|
128
110
|
exit 1
|
|
@@ -3,17 +3,36 @@ require "test_helper"
|
|
|
3
3
|
describe Transloadit::Response do
|
|
4
4
|
request_uri = "https://api2.jane.transloadit.com/assemblies/76fe5df1c93a0a530f3e583805cf98b4"
|
|
5
5
|
|
|
6
|
-
it "must allow
|
|
7
|
-
response = Transloadit::Response.new("
|
|
6
|
+
it "must allow initialization" do
|
|
7
|
+
response = Transloadit::Response.new(body: "{}", headers: {}, status: 200)
|
|
8
8
|
_(response.class).must_equal Transloadit::Response
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
+
it "must replace body, headers, and status together" do
|
|
12
|
+
response = Transloadit::Response.new(
|
|
13
|
+
body: '{"ok":"ASSEMBLY_EXECUTING"}',
|
|
14
|
+
headers: {"X-Request-Id" => "old-request"},
|
|
15
|
+
status: 202
|
|
16
|
+
)
|
|
17
|
+
replacement = Transloadit::Response.new(
|
|
18
|
+
body: '{"ok":"ASSEMBLY_COMPLETED"}',
|
|
19
|
+
headers: {"X-Request-Id" => "new-request"},
|
|
20
|
+
status: 200
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
returned = response.replace(replacement)
|
|
24
|
+
|
|
25
|
+
_(returned).must_be_same_as response
|
|
26
|
+
_(response["ok"]).must_equal "ASSEMBLY_COMPLETED"
|
|
27
|
+
_(response.headers).must_equal x_request_id: "new-request"
|
|
28
|
+
_(response.code).must_equal 200
|
|
29
|
+
_(response.status).must_equal 200
|
|
30
|
+
end
|
|
31
|
+
|
|
11
32
|
describe "when initialized" do
|
|
12
33
|
before do
|
|
13
34
|
VCR.use_cassette "fetch_assembly_ok" do
|
|
14
|
-
@response = Transloadit::
|
|
15
|
-
RestClient::Resource.new(request_uri).get
|
|
16
|
-
)
|
|
35
|
+
@response = Transloadit::Request.new(request_uri).get
|
|
17
36
|
end
|
|
18
37
|
end
|
|
19
38
|
|
|
@@ -41,9 +60,7 @@ describe Transloadit::Response do
|
|
|
41
60
|
describe "when extended as an assembly" do
|
|
42
61
|
before do
|
|
43
62
|
VCR.use_cassette "fetch_assembly_ok" do
|
|
44
|
-
@response = Transloadit::
|
|
45
|
-
RestClient::Resource.new(request_uri).get
|
|
46
|
-
).extend!(Transloadit::Response::Assembly)
|
|
63
|
+
@response = Transloadit::Request.new(request_uri).get.extend!(Transloadit::Response::Assembly)
|
|
47
64
|
end
|
|
48
65
|
end
|
|
49
66
|
|
|
@@ -56,9 +73,6 @@ describe Transloadit::Response do
|
|
|
56
73
|
# TODO: can this be tested better?
|
|
57
74
|
it "must allow reloading the assembly" do
|
|
58
75
|
VCR.use_cassette "fetch_assembly_ok", allow_playback_repeats: true do
|
|
59
|
-
_(@response.send(:__getobj__))
|
|
60
|
-
.wont_be_same_as @response.reload!.send(:__getobj__)
|
|
61
|
-
|
|
62
76
|
_(@response.object_id)
|
|
63
77
|
.must_equal @response.reload!.object_id
|
|
64
78
|
end
|
|
@@ -79,9 +93,7 @@ describe Transloadit::Response do
|
|
|
79
93
|
describe "long-running assembly" do
|
|
80
94
|
before do
|
|
81
95
|
VCR.use_cassette "fetch_assembly_executing" do
|
|
82
|
-
@response = Transloadit::
|
|
83
|
-
RestClient::Resource.new(request_uri).get
|
|
84
|
-
).extend!(Transloadit::Response::Assembly)
|
|
96
|
+
@response = Transloadit::Request.new(request_uri).get.extend!(Transloadit::Response::Assembly)
|
|
85
97
|
end
|
|
86
98
|
end
|
|
87
99
|
|
|
@@ -109,9 +121,7 @@ describe Transloadit::Response do
|
|
|
109
121
|
describe "statuses" do
|
|
110
122
|
it "must allow checking for upload" do
|
|
111
123
|
VCR.use_cassette "fetch_assembly_uploading" do
|
|
112
|
-
@response = Transloadit::
|
|
113
|
-
RestClient::Resource.new(request_uri).get
|
|
114
|
-
).extend!(Transloadit::Response::Assembly)
|
|
124
|
+
@response = Transloadit::Request.new(request_uri).get.extend!(Transloadit::Response::Assembly)
|
|
115
125
|
end
|
|
116
126
|
|
|
117
127
|
_(@response.finished?).must_equal false
|
|
@@ -121,9 +131,7 @@ describe Transloadit::Response do
|
|
|
121
131
|
|
|
122
132
|
it "must allow to check for executing" do
|
|
123
133
|
VCR.use_cassette "fetch_assembly_executing" do
|
|
124
|
-
@response = Transloadit::
|
|
125
|
-
RestClient::Resource.new(request_uri).get
|
|
126
|
-
).extend!(Transloadit::Response::Assembly)
|
|
134
|
+
@response = Transloadit::Request.new(request_uri).get.extend!(Transloadit::Response::Assembly)
|
|
127
135
|
end
|
|
128
136
|
|
|
129
137
|
_(@response.finished?).must_equal false
|
|
@@ -133,11 +141,9 @@ describe Transloadit::Response do
|
|
|
133
141
|
|
|
134
142
|
it "must allow to check for replaying" do
|
|
135
143
|
VCR.use_cassette "replay_assembly" do
|
|
136
|
-
@response = Transloadit::
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
).post({})
|
|
140
|
-
).extend!(Transloadit::Response::Assembly)
|
|
144
|
+
@response = Transloadit::Request.new(
|
|
145
|
+
"https://api2.transloadit.com/assemblies/55c965a063a311e6ba2d379ef10b28f7/replay"
|
|
146
|
+
).post.extend!(Transloadit::Response::Assembly)
|
|
141
147
|
end
|
|
142
148
|
|
|
143
149
|
_(@response.finished?).must_equal false
|
|
@@ -147,9 +153,7 @@ describe Transloadit::Response do
|
|
|
147
153
|
|
|
148
154
|
it "must allow to check for aborted" do
|
|
149
155
|
VCR.use_cassette "fetch_assembly_aborted" do
|
|
150
|
-
@response = Transloadit::
|
|
151
|
-
RestClient::Resource.new(request_uri).get
|
|
152
|
-
).extend!(Transloadit::Response::Assembly)
|
|
156
|
+
@response = Transloadit::Request.new(request_uri).get.extend!(Transloadit::Response::Assembly)
|
|
153
157
|
end
|
|
154
158
|
|
|
155
159
|
_(@response.finished?).must_equal true
|
|
@@ -158,9 +162,7 @@ describe Transloadit::Response do
|
|
|
158
162
|
|
|
159
163
|
it "must allow to check for errors" do
|
|
160
164
|
VCR.use_cassette "fetch_assembly_errors" do
|
|
161
|
-
@response = Transloadit::
|
|
162
|
-
RestClient::Resource.new(request_uri).get
|
|
163
|
-
).extend!(Transloadit::Response::Assembly)
|
|
165
|
+
@response = Transloadit::Request.new(request_uri).get.extend!(Transloadit::Response::Assembly)
|
|
164
166
|
end
|
|
165
167
|
|
|
166
168
|
_(@response.error?).must_equal true
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
require "stringio"
|
|
3
|
+
require "tempfile"
|
|
4
|
+
|
|
5
|
+
describe "Faraday transport compatibility" do
|
|
6
|
+
include WebMock::API
|
|
7
|
+
|
|
8
|
+
before do
|
|
9
|
+
WebMock.reset!
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
after do
|
|
13
|
+
WebMock.reset!
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "sends signed GET parameters and the client header to a selected host" do
|
|
17
|
+
endpoint = "https://api2.example.test/assemblies/assembly-id"
|
|
18
|
+
stub_request(:get, /\A#{Regexp.escape(endpoint)}/)
|
|
19
|
+
.to_return(status: 200, body: '{"ok":"ASSEMBLY_COMPLETED"}')
|
|
20
|
+
|
|
21
|
+
response = Transloadit::Request.new(endpoint, "secret").get(wait: true)
|
|
22
|
+
|
|
23
|
+
_(response["ok"]).must_equal "ASSEMBLY_COMPLETED"
|
|
24
|
+
assert_requested(:get, /\A#{Regexp.escape(endpoint)}/) do |request|
|
|
25
|
+
query = Addressable::URI.parse(request.uri.to_s).query_values
|
|
26
|
+
_(MultiJson.load(query.fetch("params"))).must_equal "wait" => true
|
|
27
|
+
_(query.fetch("signature")).must_match(/\Asha384:[0-9a-f]{96}\z/)
|
|
28
|
+
_(request.headers.fetch("Transloadit-Client")).must_equal "ruby-sdk:#{Transloadit::VERSION}"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "form-encodes signed POST payloads" do
|
|
33
|
+
endpoint = "https://api2.transloadit.com/assemblies"
|
|
34
|
+
stub_request(:post, endpoint)
|
|
35
|
+
.to_return(status: 200, body: '{"ok":"ASSEMBLY_COMPLETED"}')
|
|
36
|
+
|
|
37
|
+
Transloadit::Request.new("/assemblies", "secret").post(params: {template_id: "template-id"})
|
|
38
|
+
|
|
39
|
+
assert_requested(:post, endpoint) do |request|
|
|
40
|
+
form = URI.decode_www_form(request.body).to_h
|
|
41
|
+
_(MultiJson.load(form.fetch("params"))).must_equal "template_id" => "template-id"
|
|
42
|
+
_(form.fetch("signature")).must_match(/\Asha384:[0-9a-f]{96}\z/)
|
|
43
|
+
_(request.headers.fetch("Content-Type")).must_match(/\Aapplication\/x-www-form-urlencoded/)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "form-encodes signed PUT payloads" do
|
|
48
|
+
endpoint = "https://api2.transloadit.com/templates/template-id"
|
|
49
|
+
stub_request(:put, endpoint)
|
|
50
|
+
.to_return(status: 200, body: '{"ok":"TEMPLATE_UPDATED"}')
|
|
51
|
+
|
|
52
|
+
Transloadit::Request.new("/templates/template-id", "secret").put(params: {name: "Updated"})
|
|
53
|
+
|
|
54
|
+
assert_requested(:put, endpoint) do |request|
|
|
55
|
+
form = URI.decode_www_form(request.body).to_h
|
|
56
|
+
_(MultiJson.load(form.fetch("params"))).must_equal "name" => "Updated"
|
|
57
|
+
_(form.fetch("signature")).must_match(/\Asha384:[0-9a-f]{96}\z/)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it "form-encodes signed DELETE payloads" do
|
|
62
|
+
endpoint = "https://api2.transloadit.com/templates/template-id"
|
|
63
|
+
stub_request(:delete, endpoint)
|
|
64
|
+
.to_return(status: 200, body: '{"ok":"TEMPLATE_DELETED"}')
|
|
65
|
+
|
|
66
|
+
Transloadit::Request.new("/templates/template-id", "secret").delete(params: {reason: "cleanup"})
|
|
67
|
+
|
|
68
|
+
assert_requested(:delete, endpoint) do |request|
|
|
69
|
+
form = URI.decode_www_form(request.body).to_h
|
|
70
|
+
_(MultiJson.load(form.fetch("params"))).must_equal "reason" => "cleanup"
|
|
71
|
+
_(form.fetch("signature")).must_match(/\Asha384:[0-9a-f]{96}\z/)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it "returns non-success responses with SDK-owned status, headers, and body access" do
|
|
76
|
+
endpoint = "https://api2.example.test/assemblies/missing"
|
|
77
|
+
stub_request(:get, endpoint).to_return(
|
|
78
|
+
status: 422,
|
|
79
|
+
headers: {"Retry-After" => "5", "X-Request-Id" => "request-id"},
|
|
80
|
+
body: '{"error":"ASSEMBLY_NOT_FOUND"}'
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
response = Transloadit::Request.new(endpoint).get
|
|
84
|
+
|
|
85
|
+
_(response).must_be_kind_of Transloadit::Response
|
|
86
|
+
_(response).wont_be_kind_of Faraday::Response
|
|
87
|
+
_(response.code).must_equal 422
|
|
88
|
+
_(response.status).must_equal 422
|
|
89
|
+
_(response.headers).must_equal retry_after: "5", x_request_id: "request-id"
|
|
90
|
+
_(response["error"]).must_equal "ASSEMBLY_NOT_FOUND"
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it "returns redirects without following them" do
|
|
94
|
+
endpoint = "https://api2.example.test/assemblies"
|
|
95
|
+
redirect = "https://uploads.example.test/assemblies"
|
|
96
|
+
stub_request(:post, endpoint).to_return(
|
|
97
|
+
status: 302,
|
|
98
|
+
headers: {"Location" => redirect},
|
|
99
|
+
body: "{}"
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
response = Transloadit::Request.new(endpoint).post
|
|
103
|
+
|
|
104
|
+
_(response.code).must_equal 302
|
|
105
|
+
_(response.headers[:location]).must_equal redirect
|
|
106
|
+
assert_not_requested(:post, redirect)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it "uploads multiple files after params and signature without closing caller-owned files" do
|
|
110
|
+
endpoint = "https://api2.transloadit.com/assemblies"
|
|
111
|
+
stub_request(:post, endpoint)
|
|
112
|
+
.to_return(status: 200, body: '{"ok":"ASSEMBLY_COMPLETED"}')
|
|
113
|
+
|
|
114
|
+
Tempfile.create(["first", ".txt"]) do |first|
|
|
115
|
+
Tempfile.create(["second", ".json"]) do |second|
|
|
116
|
+
first.write("first upload")
|
|
117
|
+
first.flush
|
|
118
|
+
second.write('{"second":"upload"}')
|
|
119
|
+
second.flush
|
|
120
|
+
|
|
121
|
+
Transloadit::Request.new("/assemblies", "secret").post(
|
|
122
|
+
params: {steps: {}},
|
|
123
|
+
file_0: first,
|
|
124
|
+
file_1: second
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
_(first.closed?).must_equal false
|
|
128
|
+
_(second.closed?).must_equal false
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
assert_requested(:post, endpoint) do |request|
|
|
133
|
+
params_position = request.body.index('name="params"')
|
|
134
|
+
signature_position = request.body.index('name="signature"')
|
|
135
|
+
first_position = request.body.index('name="file_0"')
|
|
136
|
+
second_position = request.body.index('name="file_1"')
|
|
137
|
+
|
|
138
|
+
_(request.headers.fetch("Content-Type")).must_match(/\Amultipart\/form-data; boundary=/)
|
|
139
|
+
_(params_position).wont_be_nil
|
|
140
|
+
_(signature_position).wont_be_nil
|
|
141
|
+
_(first_position).wont_be_nil
|
|
142
|
+
_(second_position).wont_be_nil
|
|
143
|
+
_(params_position < signature_position).must_equal true
|
|
144
|
+
_(signature_position < first_position).must_equal true
|
|
145
|
+
_(first_position < second_position).must_equal true
|
|
146
|
+
_(request.body).must_include "Content-Type: text/plain"
|
|
147
|
+
_(request.body).must_include "Content-Type: application/json"
|
|
148
|
+
_(request.body).must_include "first upload"
|
|
149
|
+
_(request.body).must_include '{"second":"upload"}'
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
it "preserves filename and content type from Rails-style uploaded files" do
|
|
154
|
+
endpoint = "https://api2.transloadit.com/assemblies"
|
|
155
|
+
stub_request(:post, endpoint)
|
|
156
|
+
.to_return(status: 200, body: '{"ok":"ASSEMBLY_COMPLETED"}')
|
|
157
|
+
|
|
158
|
+
Tempfile.create(["rails-upload", ".bin"]) do |file|
|
|
159
|
+
file.write("csv contents")
|
|
160
|
+
file.flush
|
|
161
|
+
upload = Object.new
|
|
162
|
+
upload.define_singleton_method(:read) { |*arguments| file.read(*arguments) }
|
|
163
|
+
upload.define_singleton_method(:path) { file.path }
|
|
164
|
+
upload.define_singleton_method(:original_filename) { "report.csv" }
|
|
165
|
+
upload.define_singleton_method(:content_type) { "text/csv" }
|
|
166
|
+
|
|
167
|
+
Transloadit::Request.new("/assemblies").post(params: {steps: {}}, file_0: upload)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
assert_requested(:post, endpoint) do |request|
|
|
171
|
+
_(request.body).must_include 'filename="report.csv"'
|
|
172
|
+
_(request.body).must_include "Content-Type: text/csv"
|
|
173
|
+
_(request.body).must_include "csv contents"
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
it "uses safe metadata defaults for in-memory uploads" do
|
|
178
|
+
endpoint = "https://api2.transloadit.com/assemblies"
|
|
179
|
+
stub_request(:post, endpoint)
|
|
180
|
+
.to_return(status: 200, body: '{"ok":"ASSEMBLY_COMPLETED"}')
|
|
181
|
+
upload = StringIO.new("memory upload")
|
|
182
|
+
|
|
183
|
+
Transloadit::Request.new("/assemblies").post(params: {steps: {}}, file_0: upload)
|
|
184
|
+
|
|
185
|
+
_(upload.closed?).must_equal false
|
|
186
|
+
assert_requested(:post, endpoint) do |request|
|
|
187
|
+
_(request.body).must_include 'filename="upload"'
|
|
188
|
+
_(request.body).must_include "Content-Type: application/octet-stream"
|
|
189
|
+
_(request.body).must_include "memory upload"
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
it "uses the binary content type when a file extension is unknown" do
|
|
194
|
+
endpoint = "https://api2.transloadit.com/assemblies"
|
|
195
|
+
stub_request(:post, endpoint)
|
|
196
|
+
.to_return(status: 200, body: '{"ok":"ASSEMBLY_COMPLETED"}')
|
|
197
|
+
|
|
198
|
+
Tempfile.create(["upload", ".transloadit-unknown"]) do |upload|
|
|
199
|
+
upload.write("unknown upload")
|
|
200
|
+
upload.flush
|
|
201
|
+
|
|
202
|
+
Transloadit::Request.new("/assemblies").post(params: {steps: {}}, file_0: upload)
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
assert_requested(:post, endpoint) do |request|
|
|
206
|
+
_(request.body).must_include "Content-Type: application/octet-stream"
|
|
207
|
+
_(request.body).must_include "unknown upload"
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
it "wraps adapter failures and retains the original error as the cause" do
|
|
212
|
+
endpoint = "https://api2.example.test/assemblies/assembly-id"
|
|
213
|
+
stub_request(:get, endpoint).to_timeout
|
|
214
|
+
|
|
215
|
+
error = assert_raises Transloadit::Exception::RequestFailed do
|
|
216
|
+
Transloadit::Request.new(endpoint).get
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
_(error.message).must_equal "Transloadit request failed"
|
|
220
|
+
_(error.cause).must_be_kind_of Faraday::ConnectionFailed
|
|
221
|
+
end
|
|
222
|
+
end
|
data/transloadit.gemspec
CHANGED
|
@@ -16,21 +16,22 @@ Gem::Specification.new do |gem|
|
|
|
16
16
|
gem.description = "The transloadit gem allows you to automate uploading files through the Transloadit REST API"
|
|
17
17
|
|
|
18
18
|
gem.required_rubygems_version = ">= 2.2.0"
|
|
19
|
-
gem.required_ruby_version = ">= 3.
|
|
19
|
+
gem.required_ruby_version = ">= 3.1.0"
|
|
20
20
|
|
|
21
21
|
gem.files = `git ls-files`.split("\n")
|
|
22
22
|
gem.require_paths = %w[lib]
|
|
23
23
|
|
|
24
|
-
gem.add_dependency "
|
|
24
|
+
gem.add_dependency "faraday", ">= 2.0", "< 3.0"
|
|
25
|
+
gem.add_dependency "faraday-multipart", "~> 1.0"
|
|
25
26
|
gem.add_dependency "multi_json"
|
|
26
27
|
gem.add_dependency "mime-types"
|
|
27
28
|
|
|
28
29
|
gem.add_development_dependency "rake"
|
|
29
30
|
gem.add_development_dependency "minitest"
|
|
30
31
|
gem.add_development_dependency "simplecov"
|
|
31
|
-
gem.add_development_dependency "standard"
|
|
32
|
+
gem.add_development_dependency "standard", ">= 1.54"
|
|
32
33
|
|
|
33
|
-
gem.add_development_dependency "vcr"
|
|
34
|
+
gem.add_development_dependency "vcr", ">= 6.4"
|
|
34
35
|
gem.add_development_dependency "webmock"
|
|
35
36
|
|
|
36
37
|
gem.add_development_dependency "yard"
|