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: multipart testing #30

Merged
merged 4 commits into from
Oct 10, 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: 4 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
source 'https://rubygems.org'

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

gemspec

2 changes: 1 addition & 1 deletion appwrite.gemspec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Gem::Specification.new do |spec|

spec.name = 'appwrite'
spec.version = '12.1.1'
spec.version = '13.0.0'
spec.license = 'BSD-3-Clause'
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'
spec.author = 'Appwrite Team'
Expand Down
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: InputFile.from_path('dir/file.png'),
code: Payload.from_file('/path/to/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: '<BODY>', # optional
body: Payload.from_json({ "x": "y" }), # 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: InputFile.from_path('dir/file.png'),
file: Payload.from_file('/path/to/file.png'),
permissions: ["read("any")"] # optional
)
3 changes: 2 additions & 1 deletion lib/appwrite.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
require_relative 'appwrite/client'
require_relative 'appwrite/service'
require_relative 'appwrite/exception'
require_relative 'appwrite/input_file'
require_relative 'appwrite/payload'
require_relative 'appwrite/multipart'
require_relative 'appwrite/query'
require_relative 'appwrite/permission'
require_relative 'appwrite/role'
Expand Down
126 changes: 48 additions & 78 deletions lib/appwrite/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@ def initialize
'x-sdk-name'=> 'Ruby',
'x-sdk-platform'=> 'server',
'x-sdk-language'=> 'ruby',
'x-sdk-version'=> '12.1.1',
'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 @@ -37,8 +37,7 @@ 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 @@ -50,8 +49,7 @@ 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 @@ -74,8 +72,7 @@ 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 @@ -87,8 +84,7 @@ 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 @@ -119,7 +115,6 @@ 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 @@ -162,20 +157,9 @@ def chunked_upload(
on_progress: nil,
response_type: nil
)
input_file = params[param_name.to_sym]
payload = 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 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
if payload.size < @chunk_size
return call(
method: 'POST',
path: path,
Expand All @@ -199,21 +183,13 @@ def chunked_upload(
offset = chunks_uploaded * @chunk_size
end

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
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
)

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

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

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

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

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

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

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

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

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

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

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
if response_type.respond_to?("from")
return response_type.from(map: result)
end
post_body.join("\r\n") + "\r\n"

return result
end

if response.class.body_permitted?
return response.body
end

return response
end

def encode(value, key = nil)
Expand Down
1 change: 1 addition & 0 deletions lib/appwrite/enums/image_format.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module ImageFormat
GIF = 'gif'
PNG = 'png'
WEBP = 'webp'
AVIF = 'avif'
end
end
end
33 changes: 0 additions & 33 deletions lib/appwrite/input_file.rb

This file was deleted.

10 changes: 10 additions & 0 deletions lib/appwrite/models/attribute_boolean.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ class AttributeBoolean
attr_reader :error
attr_reader :required
attr_reader :array
attr_reader :created_at
attr_reader :updated_at
attr_reader :default

def initialize(
Expand All @@ -18,6 +20,8 @@ def initialize(
error:,
required:,
array: ,
created_at:,
updated_at:,
default:
)
@key = key
Expand All @@ -26,6 +30,8 @@ def initialize(
@error = error
@required = required
@array = array
@created_at = created_at
@updated_at = updated_at
@default = default
end

Expand All @@ -37,6 +43,8 @@ def self.from(map:)
error: map["error"],
required: map["required"],
array: map["array"],
created_at: map["$createdAt"],
updated_at: map["$updatedAt"],
default: map["default"]
)
end
Expand All @@ -49,6 +57,8 @@ def to_map
"error": @error,
"required": @required,
"array": @array,
"$createdAt": @created_at,
"$updatedAt": @updated_at,
"default": @default
}
end
Expand Down
Loading