-
Notifications
You must be signed in to change notification settings - Fork 41
[Test PR] SDK Multipart support #93
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
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I dont see type change.. like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have added There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import { realpathSync, readFileSync } from "fs"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we import |
||
export class NewPayload { | ||
vermakhushboo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
private data: any; | ||
|
||
constructor(data: any) { | ||
vermakhushboo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
this.data = data; | ||
} | ||
|
||
// converts JSON to binary data (Buffer) | ||
static async fromJson(json: object): Promise<NewPayload> { | ||
vermakhushboo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const jsonString = JSON.stringify(json); | ||
const encoder = new TextEncoder(); | ||
const buffer = encoder.encode(jsonString); | ||
return new NewPayload(buffer); | ||
} | ||
|
||
// converts file to binary data (Buffer) | ||
static fromPath(path: string): NewPayload { | ||
const realPath = realpathSync(path); | ||
const contents = readFileSync(realPath); | ||
return new NewPayload(new Uint8Array(contents)); | ||
} | ||
|
||
// converts text to binary data (Buffer) | ||
static async fromPlainText(text: string): Promise<NewPayload> { | ||
vermakhushboo marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. return type is not a promise and remove async from function |
||
const arrayBytes = new TextEncoder().encode(text); | ||
return new NewPayload(arrayBytes); | ||
} | ||
|
||
async fromBinary(): Promise<NewPayload> { | ||
return new NewPayload(this.data); | ||
} | ||
vermakhushboo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// converts binary data (Buffer) to JSON | ||
async toJson(): Promise<any> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. again no async and return type shouldn't be a promise.. Also instead of any, is there any other type we can return that more closely resembles a JSON object? any means it can be string, number etc. |
||
const decoder = new TextDecoder(); | ||
const jsonString = decoder.decode(this.data); | ||
return JSON.parse(jsonString); | ||
} | ||
|
||
// convert binary data (Buffer) to file | ||
async toFile(fileName: string): Promise<File> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. toPath might also make sense. It would use fs.writeFileSync to write it to disk, similarly how fromPath reads it using fs |
||
const blob = new Blob([this.data]); | ||
return new File([blob], fileName); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't File class used in HTML File input? Or it is Node class? I am not familiar with it |
||
} | ||
|
||
// converts binary data (Buffer) to text | ||
async toPlainText(): Promise<string> { | ||
const decoder = new TextDecoder(); | ||
return decoder.decode(this.data); | ||
} | ||
|
||
toBinary(): Uint8Array { | ||
vermakhushboo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return Uint8Array.from(this.data); | ||
vermakhushboo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
public getData(): any { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lets avoid any There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you make the Payload class parameterised like Payload |
||
return this.data; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,9 @@ | ||
import { fetch, FormData, File } from 'node-fetch-native-with-agent'; | ||
import { createAgent } from 'node-fetch-native-with-agent/agent'; | ||
import { Models } from './models'; | ||
import { NewPayload } from './NewPayload'; | ||
import * as multipart from 'parse-multipart-data'; | ||
const { buffer } = require('node:stream/consumers'); | ||
|
||
type Payload = { | ||
[key: string]: any; | ||
|
@@ -32,6 +35,16 @@ class AppwriteException extends Error { | |
} | ||
} | ||
|
||
function getBoundary(str: string): string { | ||
vermakhushboo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const lines = str.replaceAll("\r\n", "\n").split("\n").reverse(); | ||
for (const line of lines) { | ||
if (line !== "") { | ||
return line.slice(0, -2).slice(2); | ||
} | ||
} | ||
return ""; | ||
} | ||
|
||
function getUserAgent() { | ||
let ua = 'AppwriteNodeJSSDK/13.0.0'; | ||
|
||
|
@@ -331,7 +344,22 @@ class Client { | |
data = await response.json(); | ||
} else if (responseType === 'arrayBuffer') { | ||
data = await response.arrayBuffer(); | ||
} else { | ||
} else if (response.headers.get('content-type')?.includes('multipart/form-data')) { | ||
const body = await buffer(response.body); | ||
const boundary = getBoundary(body.toString()); | ||
const parts = multipart.parse(body, boundary); | ||
const partsObject = parts.reduce<{ [key: string]: Buffer }>((acc, part) => { | ||
if (part.name) { | ||
acc[part.name] = part.data; | ||
} | ||
return acc; | ||
}, {}); | ||
vermakhushboo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
data = { | ||
...partsObject, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think everything in multipart response is string. Try createExecution, and print it. See what is type of |
||
responseBody: new NewPayload(partsObject.responseBody) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ResponseBody can be empty. in which case it would probably be.. empty string? in which case, it might not fit the buffer type? Just thinking out lit. its something to test - to make async execution (that doesn't have body) and see if all works fine |
||
} | ||
} | ||
else { | ||
data = { | ||
message: await response.text() | ||
}; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,3 +33,5 @@ export { ImageGravity } from './enums/image-gravity'; | |
export { ImageFormat } from './enums/image-format'; | ||
export { PasswordHash } from './enums/password-hash'; | ||
export { MessagingProviderType } from './enums/messaging-provider-type'; | ||
export { NewPayload } from './NewPayload'; | ||
export { InputFile } from './inputFile'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should remove all mention of InputFile. It should no longer exist. With that, we also need to update existing endpoints that used to need it. they all should now use payload class |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ import { AppwriteException, Client, type Payload, UploadProgress } from '../clie | |
import type { Models } from '../models'; | ||
import { Runtime } from '../enums/runtime'; | ||
import { ExecutionMethod } from '../enums/execution-method'; | ||
import { NewPayload } from '../NewPayload'; | ||
|
||
export class Functions { | ||
client: Client; | ||
|
@@ -624,22 +625,22 @@ Use the "command" param to set the entrypoint used to execute your cod | |
* Trigger a function execution. The returned object will return you the current execution status. You can ping the `Get Execution` endpoint to get updates on the current execution status. Once this endpoint is called, your function execution process will start asynchronously. | ||
* | ||
* @param {string} functionId | ||
* @param {string} body | ||
* @param {NewPayload} body | ||
* @param {boolean} async | ||
* @param {string} xpath | ||
* @param {ExecutionMethod} method | ||
* @param {object} headers | ||
* @throws {AppwriteException} | ||
* @returns {Promise<Models.Execution>} | ||
*/ | ||
async createExecution(functionId: string, body?: string, async?: boolean, xpath?: string, method?: ExecutionMethod, headers?: object): Promise<Models.Execution> { | ||
async createExecution(functionId: string, body?: NewPayload, async?: boolean, xpath?: string, method?: ExecutionMethod, headers?: object): Promise<Models.Execution> { | ||
if (typeof functionId === 'undefined') { | ||
throw new AppwriteException('Missing required parameter: "functionId"'); | ||
} | ||
const apiPath = '/functions/{functionId}/executions'.replace('{functionId}', functionId); | ||
const payload: Payload = {}; | ||
if (typeof body !== 'undefined') { | ||
payload['body'] = body; | ||
payload['body'] = body ? body.getData : body; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, I dont understand the check. It doesn't make sense, right? |
||
} | ||
if (typeof async !== 'undefined') { | ||
payload['async'] = async; | ||
|
@@ -656,7 +657,8 @@ Use the "command" param to set the entrypoint used to execute your cod | |
const uri = new URL(this.client.config.endpoint + apiPath); | ||
|
||
const apiHeaders: { [header: string]: string } = { | ||
'content-type': 'application/json', | ||
'content-type': 'multipart/form-data', | ||
'accept': 'multipart/form-data', | ||
} | ||
|
||
return await this.client.call( | ||
|
Uh oh!
There was an error while loading. Please reload this page.