Skip to content

Commit 11bc700

Browse files
Merge pull request #33 from appwrite/dev
fix: revert multipart
2 parents 0d86e4f + 5b3756a commit 11bc700

25 files changed

+778
-912
lines changed

Gemfile

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

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

docs/examples/databases/update-string-attribute.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@ result = databases.update_string_attribute(
1515
key: '',
1616
required: false,
1717
default: '<DEFAULT>',
18-
size: null, # optional
18+
size: 1, # optional
1919
new_key: '' # optional
2020
)

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: Payload.from_file('/path/to/file.png'),
14+
code: InputFile.from_path('dir/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: Payload.from_json({ "x": "y" }), # optional
14+
body: '<BODY>', # 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: Payload.from_file('/path/to/file.png'),
15+
file: InputFile.from_path('dir/file.png'),
1616
permissions: ["read("any")"] # optional
1717
)

lib/appwrite.rb

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

lib/appwrite/client.rb

+78-47
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,17 @@ def initialize
1515
'x-sdk-name'=> 'Ruby',
1616
'x-sdk-platform'=> 'server',
1717
'x-sdk-language'=> 'ruby',
18-
'x-sdk-version'=> '13.0.0',
19-
'x-appwrite-response-format' => '1.6.0'
18+
'x-sdk-version'=> '13.0.0',
19+
'X-Appwrite-Response-Format' => '1.6.0'
2020
}
2121
@endpoint = 'https://cloud.appwrite.io/v1'
2222
end
2323

2424
# Set Project
2525
#
2626
# Your project ID
27-
# # @param [String] value The value to set for the Project header
27+
#
28+
# @param [String] value The value to set for the Project header
2829
#
2930
# @return [self]
3031
def set_project(value)
@@ -36,7 +37,8 @@ def set_project(value)
3637
# Set Key
3738
#
3839
# Your secret API key
39-
# # @param [String] value The value to set for the Key header
40+
#
41+
# @param [String] value The value to set for the Key header
4042
#
4143
# @return [self]
4244
def set_key(value)
@@ -48,7 +50,8 @@ def set_key(value)
4850
# Set JWT
4951
#
5052
# Your secret JSON Web Token
51-
# # @param [String] value The value to set for the JWT header
53+
#
54+
# @param [String] value The value to set for the JWT header
5255
#
5356
# @return [self]
5457
def set_jwt(value)
@@ -71,7 +74,8 @@ def set_locale(value)
7174
# Set Session
7275
#
7376
# The user session to authenticate with
74-
# # @param [String] value The value to set for the Session header
77+
#
78+
# @param [String] value The value to set for the Session header
7579
#
7680
# @return [self]
7781
def set_session(value)
@@ -83,7 +87,8 @@ def set_session(value)
8387
# Set ForwardedUserAgent
8488
#
8589
# The user agent string of the client that made the request
86-
# # @param [String] value The value to set for the ForwardedUserAgent header
90+
#
91+
# @param [String] value The value to set for the ForwardedUserAgent header
8792
#
8893
# @return [self]
8994
def set_forwarded_user_agent(value)
@@ -114,6 +119,7 @@ def set_self_signed(self_signed = true)
114119
self
115120
end
116121

122+
117123
# Add Header
118124
#
119125
# @param [String] key The key for the header to add
@@ -156,9 +162,20 @@ def chunked_upload(
156162
on_progress: nil,
157163
response_type: nil
158164
)
159-
payload = params[param_name.to_sym]
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
160173

161-
if payload.size < @chunk_size
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
162179
return call(
163180
method: 'POST',
164181
path: path,
@@ -182,13 +199,21 @@ def chunked_upload(
182199
offset = chunks_uploaded * @chunk_size
183200
end
184201

185-
while offset < payload.size
186-
params[param_name.to_sym] = Payload.from_binary(
187-
payload.to_binary(offset, [@chunk_size, payload.size - offset].min),
188-
filename: payload.filename
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
189214
)
190215

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

193218
result = call(
194219
method: 'POST',
@@ -205,8 +230,8 @@ def chunked_upload(
205230

206231
on_progress.call({
207232
id: result['$id'],
208-
progress: ([offset, payload.size].min).to_f/payload.size.to_f * 100.0,
209-
size_uploaded: [offset, payload.size].min,
233+
progress: ([offset, size].min).to_f/size.to_f * 100.0,
234+
size_uploaded: [offset, size].min,
210235
chunks_total: result['chunksTotal'],
211236
chunks_uploaded: result['chunksUploaded']
212237
}) unless on_progress.nil?
@@ -233,27 +258,18 @@ def fetch(
233258
@http.use_ssl = !@self_signed
234259
payload = ''
235260

236-
headers = @headers.merge(headers.transform_keys(&:to_s))
261+
headers = @headers.merge(headers)
237262

238263
params.compact!
239264

265+
@boundary = "----A30#3ad1"
240266
if method != "GET"
241-
case headers['content-type']
267+
case headers[:'content-type']
242268
when 'application/json'
243269
payload = params.to_json
244270
when 'multipart/form-data'
245-
multipart = MultipartBuilder.new()
246-
247-
params.each do |name, value|
248-
if value.is_a?(Payload)
249-
multipart.add(name, value.to_s, filename: value.filename)
250-
else
251-
multipart.add(name, value)
252-
end
253-
end
254-
255-
headers['content-type'] = multipart.content_type
256-
payload = multipart.body
271+
payload = encode_form_data(params) + "--#{@boundary}--\r\n"
272+
headers[:'content-type'] = "multipart/form-data; boundary=#{@boundary}"
257273
else
258274
payload = encode(params)
259275
end
@@ -283,7 +299,7 @@ def fetch(
283299
return fetch(method, uri, headers, {}, response_type, limit - 1)
284300
end
285301

286-
if response.content_type.start_with?('application/json')
302+
if response.content_type == 'application/json'
287303
begin
288304
result = JSON.parse(response.body)
289305
rescue JSON::ParserError => e
@@ -294,34 +310,49 @@ def fetch(
294310
raise Appwrite::Exception.new(result['message'], result['status'], result['type'], result)
295311
end
296312

297-
if response_type.respond_to?("from")
298-
return response_type.from(map: result)
313+
unless response_type.respond_to?("from")
314+
return result
299315
end
300316

301-
return result
317+
return response_type.from(map: result)
302318
end
303319

304320
if response.code.to_i >= 400
305321
raise Appwrite::Exception.new(response.body, response.code, response)
306322
end
307323

308-
if response.content_type.start_with?('multipart/form-data')
309-
multipart = MultipartParser.new(response.body, response['content-type'])
310-
result = multipart.to_hash
311-
312-
if response_type.respond_to?("from")
313-
return response_type.from(map: result)
314-
end
315-
316-
return result
317-
end
318-
319-
if response.class.body_permitted?
320-
return response.body
324+
if response.respond_to?("body_permitted?")
325+
return response.body if response.body_permitted?
321326
end
322327

323328
return response
324329
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
352+
end
353+
post_body.join("\r\n") + "\r\n"
354+
end
355+
end
325356

326357
def encode(value, key = nil)
327358
case value

lib/appwrite/enums/runtime.rb

+10-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ module Runtime
77
NODE_19_0 = 'node-19.0'
88
NODE_20_0 = 'node-20.0'
99
NODE_21_0 = 'node-21.0'
10+
NODE_22 = 'node-22'
1011
PHP_8_0 = 'php-8.0'
1112
PHP_8_1 = 'php-8.1'
1213
PHP_8_2 = 'php-8.2'
@@ -25,31 +26,39 @@ module Runtime
2526
DENO_1_24 = 'deno-1.24'
2627
DENO_1_35 = 'deno-1.35'
2728
DENO_1_40 = 'deno-1.40'
29+
DENO_1_46 = 'deno-1.46'
30+
DENO_2_0 = 'deno-2.0'
2831
DART_2_15 = 'dart-2.15'
2932
DART_2_16 = 'dart-2.16'
3033
DART_2_17 = 'dart-2.17'
3134
DART_2_18 = 'dart-2.18'
3235
DART_3_0 = 'dart-3.0'
3336
DART_3_1 = 'dart-3.1'
3437
DART_3_3 = 'dart-3.3'
35-
DOTNET_3_1 = 'dotnet-3.1'
38+
DART_3_5 = 'dart-3.5'
3639
DOTNET_6_0 = 'dotnet-6.0'
3740
DOTNET_7_0 = 'dotnet-7.0'
41+
DOTNET_8_0 = 'dotnet-8.0'
3842
JAVA_8_0 = 'java-8.0'
3943
JAVA_11_0 = 'java-11.0'
4044
JAVA_17_0 = 'java-17.0'
4145
JAVA_18_0 = 'java-18.0'
4246
JAVA_21_0 = 'java-21.0'
47+
JAVA_22 = 'java-22'
4348
SWIFT_5_5 = 'swift-5.5'
4449
SWIFT_5_8 = 'swift-5.8'
4550
SWIFT_5_9 = 'swift-5.9'
51+
SWIFT_5_10 = 'swift-5.10'
4652
KOTLIN_1_6 = 'kotlin-1.6'
4753
KOTLIN_1_8 = 'kotlin-1.8'
4854
KOTLIN_1_9 = 'kotlin-1.9'
55+
KOTLIN_2_0 = 'kotlin-2.0'
4956
CPP_17 = 'cpp-17'
5057
CPP_20 = 'cpp-20'
5158
BUN_1_0 = 'bun-1.0'
59+
BUN_1_1 = 'bun-1.1'
5260
GO_1_23 = 'go-1.23'
61+
STATIC_1 = 'static-1'
5362
end
5463
end
5564
end

lib/appwrite/input_file.rb

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
require 'mime/types'
2+
3+
module Appwrite
4+
class InputFile
5+
attr_accessor :path
6+
attr_accessor :filename
7+
attr_accessor :mime_type
8+
attr_accessor :source_type
9+
attr_accessor :data
10+
11+
def self.from_path(path)
12+
instance = InputFile.new
13+
instance.path = path
14+
instance.filename = ::File.basename(path)
15+
instance.mime_type = MIME::Types.type_for(path).first.content_type
16+
instance.source_type = 'path'
17+
instance
18+
end
19+
20+
def self.from_string(string, filename: nil, mime_type: nil)
21+
instance = InputFile.new
22+
instance.data = string
23+
instance.filename = filename
24+
instance.mime_type = mime_type
25+
instance.source_type = 'string'
26+
instance
27+
end
28+
29+
def self.from_bytes(bytes, filename: nil, mime_type: nil)
30+
self.from_string(bytes.pack('C*'), filename: filename, mime_type: mime_type)
31+
end
32+
end
33+
end

0 commit comments

Comments
 (0)