Skip to content

Commit eee346d

Browse files
Merge pull request #30 from appwrite/dev
fix: multipart testing
2 parents ddca8f4 + bec791c commit eee346d

35 files changed

+1021
-759
lines changed

Gemfile

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
source 'https://rubygems.org'
22

3-
gemspec
3+
gem 'mime-types', '~> 3.4.1'
4+
5+
gemspec
6+

appwrite.gemspec

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Gem::Specification.new do |spec|
22

33
spec.name = 'appwrite'
4-
spec.version = '12.1.1'
4+
spec.version = '13.0.0'
55
spec.license = 'BSD-3-Clause'
66
spec.summary = 'Appwrite is an open-source self-hosted backend server that abstract and simplify complex and repetitive development tasks behind a very simple REST API'
77
spec.author = 'Appwrite Team'

docs/examples/functions/create-deployment.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ functions = Functions.new(client)
1111

1212
result = functions.create_deployment(
1313
function_id: '<FUNCTION_ID>',
14-
code: InputFile.from_path('dir/file.png'),
14+
code: Payload.from_file('/path/to/file.png'),
1515
activate: false,
1616
entrypoint: '<ENTRYPOINT>', # optional
1717
commands: '<COMMANDS>' # optional

docs/examples/functions/create-execution.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ functions = Functions.new(client)
1111

1212
result = functions.create_execution(
1313
function_id: '<FUNCTION_ID>',
14-
body: '<BODY>', # optional
14+
body: Payload.from_json({ "x": "y" }), # optional
1515
async: false, # optional
1616
path: '<PATH>', # optional
1717
method: ExecutionMethod::GET, # optional

docs/examples/storage/create-file.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@ storage = Storage.new(client)
1212
result = storage.create_file(
1313
bucket_id: '<BUCKET_ID>',
1414
file_id: '<FILE_ID>',
15-
file: InputFile.from_path('dir/file.png'),
15+
file: Payload.from_file('/path/to/file.png'),
1616
permissions: ["read("any")"] # optional
1717
)

lib/appwrite.rb

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
require_relative 'appwrite/client'
77
require_relative 'appwrite/service'
88
require_relative 'appwrite/exception'
9-
require_relative 'appwrite/input_file'
9+
require_relative 'appwrite/payload'
10+
require_relative 'appwrite/multipart'
1011
require_relative 'appwrite/query'
1112
require_relative 'appwrite/permission'
1213
require_relative 'appwrite/role'

lib/appwrite/client.rb

+48-78
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,17 @@ def initialize
1515
'x-sdk-name'=> 'Ruby',
1616
'x-sdk-platform'=> 'server',
1717
'x-sdk-language'=> 'ruby',
18-
'x-sdk-version'=> '12.1.1',
19-
'X-Appwrite-Response-Format' => '1.6.0'
18+
'x-sdk-version'=> '13.0.0',
19+
'X-Appwrite-Response-Format' => '1.6.0'
2020
}
21+
2122
@endpoint = 'https://cloud.appwrite.io/v1'
2223
end
2324

2425
# Set Project
2526
#
2627
# Your project ID
27-
#
28-
# @param [String] value The value to set for the Project header
28+
# # @param [String] value The value to set for the Project header
2929
#
3030
# @return [self]
3131
def set_project(value)
@@ -37,8 +37,7 @@ def set_project(value)
3737
# Set Key
3838
#
3939
# Your secret API key
40-
#
41-
# @param [String] value The value to set for the Key header
40+
# # @param [String] value The value to set for the Key header
4241
#
4342
# @return [self]
4443
def set_key(value)
@@ -50,8 +49,7 @@ def set_key(value)
5049
# Set JWT
5150
#
5251
# Your secret JSON Web Token
53-
#
54-
# @param [String] value The value to set for the JWT header
52+
# # @param [String] value The value to set for the JWT header
5553
#
5654
# @return [self]
5755
def set_jwt(value)
@@ -74,8 +72,7 @@ def set_locale(value)
7472
# Set Session
7573
#
7674
# The user session to authenticate with
77-
#
78-
# @param [String] value The value to set for the Session header
75+
# # @param [String] value The value to set for the Session header
7976
#
8077
# @return [self]
8178
def set_session(value)
@@ -87,8 +84,7 @@ def set_session(value)
8784
# Set ForwardedUserAgent
8885
#
8986
# The user agent string of the client that made the request
90-
#
91-
# @param [String] value The value to set for the ForwardedUserAgent header
87+
# # @param [String] value The value to set for the ForwardedUserAgent header
9288
#
9389
# @return [self]
9490
def set_forwarded_user_agent(value)
@@ -119,7 +115,6 @@ def set_self_signed(self_signed = true)
119115
self
120116
end
121117

122-
123118
# Add Header
124119
#
125120
# @param [String] key The key for the header to add
@@ -162,20 +157,9 @@ def chunked_upload(
162157
on_progress: nil,
163158
response_type: nil
164159
)
165-
input_file = params[param_name.to_sym]
160+
payload = params[param_name.to_sym]
166161

167-
case input_file.source_type
168-
when 'path'
169-
size = ::File.size(input_file.path)
170-
when 'string'
171-
size = input_file.data.bytesize
172-
end
173-
174-
if size < @chunk_size
175-
if input_file.source_type == 'path'
176-
input_file.data = IO.read(input_file.path)
177-
end
178-
params[param_name.to_sym] = input_file
162+
if payload.size < @chunk_size
179163
return call(
180164
method: 'POST',
181165
path: path,
@@ -199,21 +183,13 @@ def chunked_upload(
199183
offset = chunks_uploaded * @chunk_size
200184
end
201185

202-
while offset < size
203-
case input_file.source_type
204-
when 'path'
205-
string = IO.read(input_file.path, @chunk_size, offset)
206-
when 'string'
207-
string = input_file.data.byteslice(offset, [@chunk_size, size - offset].min)
208-
end
209-
210-
params[param_name.to_sym] = InputFile::from_string(
211-
string,
212-
filename: input_file.filename,
213-
mime_type: input_file.mime_type
186+
while offset < payload.size
187+
params[param_name.to_sym] = Payload.from_binary(
188+
payload.to_binary(offset, [@chunk_size, payload.size - offset].min),
189+
filename: payload.filename
214190
)
215191

216-
headers['content-range'] = "bytes #{offset}-#{[offset + @chunk_size - 1, size - 1].min}/#{size}"
192+
headers['content-range'] = "bytes #{offset}-#{[offset + @chunk_size - 1, payload.size - 1].min}/#{payload.size}"
217193

218194
result = call(
219195
method: 'POST',
@@ -230,8 +206,8 @@ def chunked_upload(
230206

231207
on_progress.call({
232208
id: result['$id'],
233-
progress: ([offset, size].min).to_f/size.to_f * 100.0,
234-
size_uploaded: [offset, size].min,
209+
progress: ([offset, payload.size].min).to_f/payload.size.to_f * 100.0,
210+
size_uploaded: [offset, payload.size].min,
235211
chunks_total: result['chunksTotal'],
236212
chunks_uploaded: result['chunksUploaded']
237213
}) unless on_progress.nil?
@@ -258,18 +234,27 @@ def fetch(
258234
@http.use_ssl = !@self_signed
259235
payload = ''
260236

261-
headers = @headers.merge(headers)
237+
headers = @headers.merge(headers.transform_keys(&:to_s))
262238

263239
params.compact!
264240

265-
@boundary = "----A30#3ad1"
266241
if method != "GET"
267-
case headers[:'content-type']
242+
case headers['content-type']
268243
when 'application/json'
269244
payload = params.to_json
270245
when 'multipart/form-data'
271-
payload = encode_form_data(params) + "--#{@boundary}--\r\n"
272-
headers[:'content-type'] = "multipart/form-data; boundary=#{@boundary}"
246+
multipart = MultipartBuilder.new()
247+
248+
params.each do |name, value|
249+
if value.is_a?(Payload)
250+
multipart.add(name, value.to_s, filename: value.filename)
251+
else
252+
multipart.add(name, value)
253+
end
254+
end
255+
256+
headers['content-type'] = multipart.content_type
257+
payload = multipart.body
273258
else
274259
payload = encode(params)
275260
end
@@ -299,7 +284,7 @@ def fetch(
299284
return fetch(method, uri, headers, {}, response_type, limit - 1)
300285
end
301286

302-
if response.content_type == 'application/json'
287+
if response.content_type.start_with?('application/json')
303288
begin
304289
result = JSON.parse(response.body)
305290
rescue JSON::ParserError => e
@@ -310,48 +295,33 @@ def fetch(
310295
raise Appwrite::Exception.new(result['message'], result['status'], result['type'], result)
311296
end
312297

313-
unless response_type.respond_to?("from")
314-
return result
298+
if response_type.respond_to?("from")
299+
return response_type.from(map: result)
315300
end
316301

317-
return response_type.from(map: result)
302+
return result
318303
end
319304

320305
if response.code.to_i >= 400
321306
raise Appwrite::Exception.new(response.body, response.code, response)
322307
end
323308

324-
if response.respond_to?("body_permitted?")
325-
return response.body if response.body_permitted?
326-
end
309+
if response.content_type.start_with?('multipart/form-data')
310+
multipart = MultipartParser.new(response.body, response['content-type'])
311+
result = multipart.to_hash
327312

328-
return response
329-
end
330-
331-
def encode_form_data(value, key=nil)
332-
case value
333-
when Hash
334-
value.map { |k,v| encode_form_data(v,k) }.join
335-
when Array
336-
value.map { |v| encode_form_data(v, "#{key}[]") }.join
337-
when nil
338-
''
339-
else
340-
post_body = []
341-
if value.instance_of? InputFile
342-
post_body << "--#{@boundary}"
343-
post_body << "Content-Disposition: form-data; name=\"#{key}\"; filename=\"#{value.filename}\""
344-
post_body << "Content-Type: #{value.mime_type}"
345-
post_body << ""
346-
post_body << value.data
347-
else
348-
post_body << "--#{@boundary}"
349-
post_body << "Content-Disposition: form-data; name=\"#{key}\""
350-
post_body << ""
351-
post_body << value.to_s
313+
if response_type.respond_to?("from")
314+
return response_type.from(map: result)
352315
end
353-
post_body.join("\r\n") + "\r\n"
316+
317+
return result
318+
end
319+
320+
if response.class.body_permitted?
321+
return response.body
354322
end
323+
324+
return response
355325
end
356326

357327
def encode(value, key = nil)

lib/appwrite/enums/image_format.rb

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ module ImageFormat
66
GIF = 'gif'
77
PNG = 'png'
88
WEBP = 'webp'
9+
AVIF = 'avif'
910
end
1011
end
1112
end

lib/appwrite/input_file.rb

-33
This file was deleted.

lib/appwrite/models/attribute_boolean.rb

+10
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ class AttributeBoolean
99
attr_reader :error
1010
attr_reader :required
1111
attr_reader :array
12+
attr_reader :created_at
13+
attr_reader :updated_at
1214
attr_reader :default
1315

1416
def initialize(
@@ -18,6 +20,8 @@ def initialize(
1820
error:,
1921
required:,
2022
array: ,
23+
created_at:,
24+
updated_at:,
2125
default:
2226
)
2327
@key = key
@@ -26,6 +30,8 @@ def initialize(
2630
@error = error
2731
@required = required
2832
@array = array
33+
@created_at = created_at
34+
@updated_at = updated_at
2935
@default = default
3036
end
3137

@@ -37,6 +43,8 @@ def self.from(map:)
3743
error: map["error"],
3844
required: map["required"],
3945
array: map["array"],
46+
created_at: map["$createdAt"],
47+
updated_at: map["$updatedAt"],
4048
default: map["default"]
4149
)
4250
end
@@ -49,6 +57,8 @@ def to_map
4957
"error": @error,
5058
"required": @required,
5159
"array": @array,
60+
"$createdAt": @created_at,
61+
"$updatedAt": @updated_at,
5262
"default": @default
5363
}
5464
end

0 commit comments

Comments
 (0)