Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: revert multipart #33

Merged
merged 1 commit into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
source 'https://rubygems.org'

gem 'mime-types', '~> 3.4.1'

gemspec

gemspec
2 changes: 1 addition & 1 deletion docs/examples/databases/update-string-attribute.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ result = databases.update_string_attribute(
key: '',
required: false,
default: '<DEFAULT>',
size: null, # optional
size: 1, # optional
new_key: '' # optional
)
2 changes: 1 addition & 1 deletion docs/examples/functions/create-deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ functions = Functions.new(client)

result = functions.create_deployment(
function_id: '<FUNCTION_ID>',
code: Payload.from_file('/path/to/file.png'),
code: InputFile.from_path('dir/file.png'),
activate: false,
entrypoint: '<ENTRYPOINT>', # optional
commands: '<COMMANDS>' # optional
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/functions/create-execution.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ functions = Functions.new(client)

result = functions.create_execution(
function_id: '<FUNCTION_ID>',
body: Payload.from_json({ "x": "y" }), # optional
body: '<BODY>', # optional
async: false, # optional
path: '<PATH>', # optional
method: ExecutionMethod::GET, # optional
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/storage/create-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ storage = Storage.new(client)
result = storage.create_file(
bucket_id: '<BUCKET_ID>',
file_id: '<FILE_ID>',
file: Payload.from_file('/path/to/file.png'),
file: InputFile.from_path('dir/file.png'),
permissions: ["read("any")"] # optional
)
3 changes: 1 addition & 2 deletions lib/appwrite.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
require_relative 'appwrite/client'
require_relative 'appwrite/service'
require_relative 'appwrite/exception'
require_relative 'appwrite/payload'
require_relative 'appwrite/multipart'
require_relative 'appwrite/input_file'
require_relative 'appwrite/query'
require_relative 'appwrite/permission'
require_relative 'appwrite/role'
Expand Down
125 changes: 78 additions & 47 deletions lib/appwrite/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,17 @@ def initialize
'x-sdk-name'=> 'Ruby',
'x-sdk-platform'=> 'server',
'x-sdk-language'=> 'ruby',
'x-sdk-version'=> '13.0.0',
'x-appwrite-response-format' => '1.6.0'
'x-sdk-version'=> '13.0.0',
'X-Appwrite-Response-Format' => '1.6.0'
}
@endpoint = 'https://cloud.appwrite.io/v1'
end

# Set Project
#
# Your project ID
# # @param [String] value The value to set for the Project header
#
# @param [String] value The value to set for the Project header
#
# @return [self]
def set_project(value)
Expand All @@ -36,7 +37,8 @@ def set_project(value)
# Set Key
#
# Your secret API key
# # @param [String] value The value to set for the Key header
#
# @param [String] value The value to set for the Key header
#
# @return [self]
def set_key(value)
Expand All @@ -48,7 +50,8 @@ def set_key(value)
# Set JWT
#
# Your secret JSON Web Token
# # @param [String] value The value to set for the JWT header
#
# @param [String] value The value to set for the JWT header
#
# @return [self]
def set_jwt(value)
Expand All @@ -71,7 +74,8 @@ def set_locale(value)
# Set Session
#
# The user session to authenticate with
# # @param [String] value The value to set for the Session header
#
# @param [String] value The value to set for the Session header
#
# @return [self]
def set_session(value)
Expand All @@ -83,7 +87,8 @@ def set_session(value)
# Set ForwardedUserAgent
#
# The user agent string of the client that made the request
# # @param [String] value The value to set for the ForwardedUserAgent header
#
# @param [String] value The value to set for the ForwardedUserAgent header
#
# @return [self]
def set_forwarded_user_agent(value)
Expand Down Expand Up @@ -114,6 +119,7 @@ def set_self_signed(self_signed = true)
self
end


# Add Header
#
# @param [String] key The key for the header to add
Expand Down Expand Up @@ -156,9 +162,20 @@ def chunked_upload(
on_progress: nil,
response_type: nil
)
payload = params[param_name.to_sym]
input_file = params[param_name.to_sym]

case input_file.source_type
when 'path'
size = ::File.size(input_file.path)
when 'string'
size = input_file.data.bytesize
end

if payload.size < @chunk_size
if size < @chunk_size
if input_file.source_type == 'path'
input_file.data = IO.read(input_file.path)
end
params[param_name.to_sym] = input_file
return call(
method: 'POST',
path: path,
Expand All @@ -182,13 +199,21 @@ def chunked_upload(
offset = chunks_uploaded * @chunk_size
end

while offset < payload.size
params[param_name.to_sym] = Payload.from_binary(
payload.to_binary(offset, [@chunk_size, payload.size - offset].min),
filename: payload.filename
while offset < size
case input_file.source_type
when 'path'
string = IO.read(input_file.path, @chunk_size, offset)
when 'string'
string = input_file.data.byteslice(offset, [@chunk_size, size - offset].min)
end

params[param_name.to_sym] = InputFile::from_string(
string,
filename: input_file.filename,
mime_type: input_file.mime_type
)

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

result = call(
method: 'POST',
Expand All @@ -205,8 +230,8 @@ def chunked_upload(

on_progress.call({
id: result['$id'],
progress: ([offset, payload.size].min).to_f/payload.size.to_f * 100.0,
size_uploaded: [offset, payload.size].min,
progress: ([offset, size].min).to_f/size.to_f * 100.0,
size_uploaded: [offset, size].min,
chunks_total: result['chunksTotal'],
chunks_uploaded: result['chunksUploaded']
}) unless on_progress.nil?
Expand All @@ -233,27 +258,18 @@ def fetch(
@http.use_ssl = !@self_signed
payload = ''

headers = @headers.merge(headers.transform_keys(&:to_s))
headers = @headers.merge(headers)

params.compact!

@boundary = "----A30#3ad1"
if method != "GET"
case headers['content-type']
case headers[:'content-type']
when 'application/json'
payload = params.to_json
when 'multipart/form-data'
multipart = MultipartBuilder.new()

params.each do |name, value|
if value.is_a?(Payload)
multipart.add(name, value.to_s, filename: value.filename)
else
multipart.add(name, value)
end
end

headers['content-type'] = multipart.content_type
payload = multipart.body
payload = encode_form_data(params) + "--#{@boundary}--\r\n"
headers[:'content-type'] = "multipart/form-data; boundary=#{@boundary}"
else
payload = encode(params)
end
Expand Down Expand Up @@ -283,7 +299,7 @@ def fetch(
return fetch(method, uri, headers, {}, response_type, limit - 1)
end

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

if response_type.respond_to?("from")
return response_type.from(map: result)
unless response_type.respond_to?("from")
return result
end

return result
return response_type.from(map: result)
end

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

if response.content_type.start_with?('multipart/form-data')
multipart = MultipartParser.new(response.body, response['content-type'])
result = multipart.to_hash

if response_type.respond_to?("from")
return response_type.from(map: result)
end

return result
end

if response.class.body_permitted?
return response.body
if response.respond_to?("body_permitted?")
return response.body if response.body_permitted?
end

return response
end

def encode_form_data(value, key=nil)
case value
when Hash
value.map { |k,v| encode_form_data(v,k) }.join
when Array
value.map { |v| encode_form_data(v, "#{key}[]") }.join
when nil
''
else
post_body = []
if value.instance_of? InputFile
post_body << "--#{@boundary}"
post_body << "Content-Disposition: form-data; name=\"#{key}\"; filename=\"#{value.filename}\""
post_body << "Content-Type: #{value.mime_type}"
post_body << ""
post_body << value.data
else
post_body << "--#{@boundary}"
post_body << "Content-Disposition: form-data; name=\"#{key}\""
post_body << ""
post_body << value.to_s
end
post_body.join("\r\n") + "\r\n"
end
end

def encode(value, key = nil)
case value
Expand Down
11 changes: 10 additions & 1 deletion lib/appwrite/enums/runtime.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module Runtime
NODE_19_0 = 'node-19.0'
NODE_20_0 = 'node-20.0'
NODE_21_0 = 'node-21.0'
NODE_22 = 'node-22'
PHP_8_0 = 'php-8.0'
PHP_8_1 = 'php-8.1'
PHP_8_2 = 'php-8.2'
Expand All @@ -25,31 +26,39 @@ module Runtime
DENO_1_24 = 'deno-1.24'
DENO_1_35 = 'deno-1.35'
DENO_1_40 = 'deno-1.40'
DENO_1_46 = 'deno-1.46'
DENO_2_0 = 'deno-2.0'
DART_2_15 = 'dart-2.15'
DART_2_16 = 'dart-2.16'
DART_2_17 = 'dart-2.17'
DART_2_18 = 'dart-2.18'
DART_3_0 = 'dart-3.0'
DART_3_1 = 'dart-3.1'
DART_3_3 = 'dart-3.3'
DOTNET_3_1 = 'dotnet-3.1'
DART_3_5 = 'dart-3.5'
DOTNET_6_0 = 'dotnet-6.0'
DOTNET_7_0 = 'dotnet-7.0'
DOTNET_8_0 = 'dotnet-8.0'
JAVA_8_0 = 'java-8.0'
JAVA_11_0 = 'java-11.0'
JAVA_17_0 = 'java-17.0'
JAVA_18_0 = 'java-18.0'
JAVA_21_0 = 'java-21.0'
JAVA_22 = 'java-22'
SWIFT_5_5 = 'swift-5.5'
SWIFT_5_8 = 'swift-5.8'
SWIFT_5_9 = 'swift-5.9'
SWIFT_5_10 = 'swift-5.10'
KOTLIN_1_6 = 'kotlin-1.6'
KOTLIN_1_8 = 'kotlin-1.8'
KOTLIN_1_9 = 'kotlin-1.9'
KOTLIN_2_0 = 'kotlin-2.0'
CPP_17 = 'cpp-17'
CPP_20 = 'cpp-20'
BUN_1_0 = 'bun-1.0'
BUN_1_1 = 'bun-1.1'
GO_1_23 = 'go-1.23'
STATIC_1 = 'static-1'
end
end
end
33 changes: 33 additions & 0 deletions lib/appwrite/input_file.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
require 'mime/types'

module Appwrite
class InputFile
attr_accessor :path
attr_accessor :filename
attr_accessor :mime_type
attr_accessor :source_type
attr_accessor :data

def self.from_path(path)
instance = InputFile.new
instance.path = path
instance.filename = ::File.basename(path)
instance.mime_type = MIME::Types.type_for(path).first.content_type
instance.source_type = 'path'
instance
end

def self.from_string(string, filename: nil, mime_type: nil)
instance = InputFile.new
instance.data = string
instance.filename = filename
instance.mime_type = mime_type
instance.source_type = 'string'
instance
end

def self.from_bytes(bytes, filename: nil, mime_type: nil)
self.from_string(bytes.pack('C*'), filename: filename, mime_type: mime_type)
end
end
end
Loading