Skip to content

Commit 600c70c

Browse files
committed
feat: multipart
1 parent 3d9fc65 commit 600c70c

35 files changed

+1019
-763
lines changed

Gemfile

Lines changed: 4 additions & 1 deletion
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 1 addition & 1 deletion
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: , # 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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 2 additions & 1 deletion
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

Lines changed: 46 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,14 @@ 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'
20-
}
18+
'x-sdk-version'=> '13.0.0', 'X-Appwrite-Response-Format' => '1.6.0' }
2119
@endpoint = 'https://cloud.appwrite.io/v1'
2220
end
2321

2422
# Set Project
2523
#
2624
# Your project ID
27-
#
28-
# @param [String] value The value to set for the Project header
25+
# # @param [String] value The value to set for the Project header
2926
#
3027
# @return [self]
3128
def set_project(value)
@@ -37,8 +34,7 @@ def set_project(value)
3734
# Set Key
3835
#
3936
# Your secret API key
40-
#
41-
# @param [String] value The value to set for the Key header
37+
# # @param [String] value The value to set for the Key header
4238
#
4339
# @return [self]
4440
def set_key(value)
@@ -50,8 +46,7 @@ def set_key(value)
5046
# Set JWT
5147
#
5248
# Your secret JSON Web Token
53-
#
54-
# @param [String] value The value to set for the JWT header
49+
# # @param [String] value The value to set for the JWT header
5550
#
5651
# @return [self]
5752
def set_jwt(value)
@@ -74,8 +69,7 @@ def set_locale(value)
7469
# Set Session
7570
#
7671
# The user session to authenticate with
77-
#
78-
# @param [String] value The value to set for the Session header
72+
# # @param [String] value The value to set for the Session header
7973
#
8074
# @return [self]
8175
def set_session(value)
@@ -87,8 +81,7 @@ def set_session(value)
8781
# Set ForwardedUserAgent
8882
#
8983
# The user agent string of the client that made the request
90-
#
91-
# @param [String] value The value to set for the ForwardedUserAgent header
84+
# # @param [String] value The value to set for the ForwardedUserAgent header
9285
#
9386
# @return [self]
9487
def set_forwarded_user_agent(value)
@@ -119,7 +112,6 @@ def set_self_signed(self_signed = true)
119112
self
120113
end
121114

122-
123115
# Add Header
124116
#
125117
# @param [String] key The key for the header to add
@@ -162,20 +154,9 @@ def chunked_upload(
162154
on_progress: nil,
163155
response_type: nil
164156
)
165-
input_file = params[param_name.to_sym]
166-
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
157+
payload = params[param_name.to_sym]
173158

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
159+
if payload.size < @chunk_size
179160
return call(
180161
method: 'POST',
181162
path: path,
@@ -199,21 +180,13 @@ def chunked_upload(
199180
offset = chunks_uploaded * @chunk_size
200181
end
201182

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
183+
while offset < payload.size
184+
params[param_name.to_sym] = Payload.from_binary(
185+
payload.to_binary(offset, [@chunk_size, payload.size - offset].min),
186+
filename: payload.filename
214187
)
215188

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

218191
result = call(
219192
method: 'POST',
@@ -230,8 +203,8 @@ def chunked_upload(
230203

231204
on_progress.call({
232205
id: result['$id'],
233-
progress: ([offset, size].min).to_f/size.to_f * 100.0,
234-
size_uploaded: [offset, size].min,
206+
progress: ([offset, payload.size].min).to_f/payload.size.to_f * 100.0,
207+
size_uploaded: [offset, payload.size].min,
235208
chunks_total: result['chunksTotal'],
236209
chunks_uploaded: result['chunksUploaded']
237210
}) unless on_progress.nil?
@@ -258,18 +231,27 @@ def fetch(
258231
@http.use_ssl = !@self_signed
259232
payload = ''
260233

261-
headers = @headers.merge(headers)
234+
headers = @headers.merge(headers.transform_keys(&:to_s))
262235

263236
params.compact!
264237

265-
@boundary = "----A30#3ad1"
266238
if method != "GET"
267-
case headers[:'content-type']
239+
case headers['content-type']
268240
when 'application/json'
269241
payload = params.to_json
270242
when 'multipart/form-data'
271-
payload = encode_form_data(params) + "--#{@boundary}--\r\n"
272-
headers[:'content-type'] = "multipart/form-data; boundary=#{@boundary}"
243+
multipart = MultipartBuilder.new()
244+
245+
params.each do |name, value|
246+
if value.is_a?(Payload)
247+
multipart.add(name, value.to_s, filename: value.filename)
248+
else
249+
multipart.add(name, value)
250+
end
251+
end
252+
253+
headers['content-type'] = multipart.content_type
254+
payload = multipart.body
273255
else
274256
payload = encode(params)
275257
end
@@ -299,7 +281,7 @@ def fetch(
299281
return fetch(method, uri, headers, {}, response_type, limit - 1)
300282
end
301283

302-
if response.content_type == 'application/json'
284+
if response.content_type.start_with?('application/json')
303285
begin
304286
result = JSON.parse(response.body)
305287
rescue JSON::ParserError => e
@@ -310,48 +292,33 @@ def fetch(
310292
raise Appwrite::Exception.new(result['message'], result['status'], result['type'], result)
311293
end
312294

313-
unless response_type.respond_to?("from")
314-
return result
295+
if response_type.respond_to?("from")
296+
return response_type.from(map: result)
315297
end
316298

317-
return response_type.from(map: result)
299+
return result
318300
end
319301

320302
if response.code.to_i >= 400
321303
raise Appwrite::Exception.new(response.body, response.code, response)
322304
end
323305

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

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
310+
if response_type.respond_to?("from")
311+
return response_type.from(map: result)
352312
end
353-
post_body.join("\r\n") + "\r\n"
313+
314+
return result
354315
end
316+
317+
if response.class.body_permitted?
318+
return response.body
319+
end
320+
321+
return response
355322
end
356323

357324
def encode(value, key = nil)

lib/appwrite/enums/image_format.rb

Lines changed: 1 addition & 0 deletions
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

Lines changed: 0 additions & 33 deletions
This file was deleted.

lib/appwrite/models/attribute_boolean.rb

Lines changed: 10 additions & 0 deletions
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)