diff --git a/src/SDK/Language/CLI.php b/src/SDK/Language/CLI.php index fed1e0388..3bd0d77a4 100644 --- a/src/SDK/Language/CLI.php +++ b/src/SDK/Language/CLI.php @@ -180,6 +180,33 @@ public function getFiles(): array ]; } + /** + * @param array $parameter + * @param array $nestedTypes + * @return string + */ + public function getTypeName(array $parameter, array $spec = []): string + { + if (isset($parameter['enumName'])) { + return \ucfirst($parameter['enumName']); + } + if (!empty($parameter['enumValues'])) { + return \ucfirst($parameter['name']); + } + return match ($parameter['type']) { + self::TYPE_INTEGER, + self::TYPE_NUMBER => 'number', + self::TYPE_STRING => 'string', + self::TYPE_FILE => 'string', + self::TYPE_BOOLEAN => 'boolean', + self::TYPE_OBJECT => 'object', + self::TYPE_ARRAY => (!empty(($parameter['array'] ?? [])['type']) && !\is_array($parameter['array']['type'])) + ? $this->getTypeName($parameter['array']) . '[]' + : 'string[]', + default => $parameter['type'], + }; + } + /** * @param array $param * @return string diff --git a/templates/cli/base/params.twig b/templates/cli/base/params.twig new file mode 100644 index 000000000..2dcdf0d56 --- /dev/null +++ b/templates/cli/base/params.twig @@ -0,0 +1,79 @@ + let payload = {}; +{% for parameter in method.parameters.query %} + if (typeof {{ parameter.name | caseCamel | escapeKeyword }} !== 'undefined') { + payload['{{ parameter.name }}'] = {{ parameter.name | caseCamel | escapeKeyword }}; + } +{% endfor %} +{% for parameter in method.parameters.body %} +{% if parameter.type == 'file' %} +{% if method.packaging %} + const folderPath = fs.realpathSync({{ parameter.name | caseCamel | escapeKeyword }}); + if (!fs.lstatSync(folderPath).isDirectory()) { + throw new Error('The path is not a directory.'); + } + + const ignorer = ignore(); + + const func = localConfig.getFunction(functionId); + + if (func.ignore) { + ignorer.add(func.ignore); + log('Ignoring files using configuration from appwrite.json'); + } else if (fs.existsSync(pathLib.join({{ parameter.name | caseCamel | escapeKeyword }}, '.gitignore'))) { + ignorer.add(fs.readFileSync(pathLib.join({{ parameter.name | caseCamel | escapeKeyword }}, '.gitignore')).toString()); + log('Ignoring files in .gitignore'); + } + + const files = getAllFiles({{ parameter.name | caseCamel | escapeKeyword }}).map((file) => pathLib.relative({{ parameter.name | caseCamel | escapeKeyword }}, file)).filter((file) => !ignorer.ignores(file)); + + await tar + .create({ + gzip: true, + sync: true, + cwd: folderPath, + file: 'code.tar.gz' + }, files); + +{% endif %} + const filePath = fs.realpathSync({{ parameter.name | caseCamel | escapeKeyword }}); + const nodeStream = fs.createReadStream(filePath); + const stream = convertReadStreamToReadableStream(nodeStream); + + if (typeof filePath !== 'undefined') { + {{ parameter.name | caseCamel | escapeKeyword }} = { type: 'file', stream, filename: pathLib.basename(filePath), size: fs.statSync(filePath).size }; + payload['{{ parameter.name }}'] = {{ parameter.name | caseCamel | escapeKeyword }} + } +{% elseif parameter.type == 'boolean' %} + if (typeof {{ parameter.name | caseCamel | escapeKeyword }} !== 'undefined') { + payload['{{ parameter.name }}'] = {{ parameter.name | caseCamel | escapeKeyword }}; + } +{% elseif parameter.type == 'number' %} + if (typeof {{ parameter.name | caseCamel | escapeKeyword }} !== 'undefined') { + payload['{{ parameter.name }}'] = {{ parameter.name | caseCamel | escapeKeyword }}; + } +{% elseif parameter.type == 'string' %} + if (typeof {{ parameter.name | caseCamel | escapeKeyword }} !== 'undefined') { + payload['{{ parameter.name }}'] = {{ parameter.name | caseCamel | escapeKeyword }}; + } +{% elseif parameter.type == 'object' %} + if (typeof {{ parameter.name | caseCamel | escapeKeyword }} !== 'undefined') { + payload['{{ parameter.name }}'] = JSON.parse({{ parameter.name | caseCamel | escapeKeyword}}); + } +{% elseif parameter.type == 'array' %} + {{ parameter.name | caseCamel | escapeKeyword}} = {{ parameter.name | caseCamel | escapeKeyword}} === true ? [] : {{ parameter.name | caseCamel | escapeKeyword}}; + if (typeof {{ parameter.name | caseCamel | escapeKeyword }} !== 'undefined') { + payload['{{ parameter.name }}'] = {{ parameter.name | caseCamel | escapeKeyword}}; + } +{% else %} + if (typeof {{ parameter.name | caseCamel | escapeKeyword }} !== 'undefined') { + payload['{{ parameter.name }}'] = {{ parameter.name | caseCamel | escapeKeyword}}{% if method.consumes[0] == "multipart/form-data" %}.toString(){% endif %}; + } +{% endif %} +{% endfor %} + +{% if method.type == 'location' %} + payload['project'] = localConfig.getProject().projectId + payload['key'] = globalConfig.getKey(); + const queryParams = new URLSearchParams(payload); + apiPath = `${globalConfig.getEndpoint()}${apiPath}?${queryParams.toString()}`; +{% endif %} \ No newline at end of file diff --git a/templates/cli/base/requests/api.twig b/templates/cli/base/requests/api.twig new file mode 100644 index 000000000..2b67f7f4c --- /dev/null +++ b/templates/cli/base/requests/api.twig @@ -0,0 +1,21 @@ + let response = undefined; + + response = await client.call('{{ method.method | caseLower }}', apiPath, { +{% for parameter in method.parameters.header %} + '{{ parameter.name }}': ${{ parameter.name | caseCamel | escapeKeyword }}, +{% endfor %} +{% for key, header in method.headers %} + '{{ key }}': '{{ header }}', +{% endfor %} + }, payload{% if method.type == 'location' %}, 'arraybuffer'{% endif %}); + +{% if method.type == 'location' %} + fs.writeFileSync(destination, response); +{% endif %} + + if (parseOutput) { + parse(response) + success() + } + + return response; \ No newline at end of file diff --git a/templates/cli/base/requests/file.twig b/templates/cli/base/requests/file.twig new file mode 100644 index 000000000..61b7f29b3 --- /dev/null +++ b/templates/cli/base/requests/file.twig @@ -0,0 +1,116 @@ +{% for parameter in method.parameters.all %} +{% if parameter.type == 'file' %} + + const size = {{ parameter.name | caseCamel | escapeKeyword }}.size; + + const apiHeaders = { +{% for parameter in method.parameters.header %} + '{{ parameter.name }}': ${{ parameter.name | caseCamel | escapeKeyword }}, +{% endfor %} +{% for key, header in method.headers %} + '{{ key }}': '{{ header }}', +{% endfor %} + }; + + let id = undefined; + let response = undefined; + + let chunksUploaded = 0; +{% for parameter in method.parameters.all %} +{% if parameter.isUploadID %} + + if({{ parameter.name | caseCamel | escapeKeyword }} != 'unique()') { + try { + response = await client.call('get', apiPath + '/' + {{ parameter.name }}, apiHeaders); + chunksUploaded = response.chunksUploaded; + } catch(e) { + } + } +{% endif %} +{% endfor %} + + let currentChunk = 1; + let currentPosition = 0; + let uploadableChunk = new Uint8Array(client.CHUNK_SIZE); + + const uploadChunk = async (lastUpload = false) => { + if(currentChunk <= chunksUploaded) { + return; + } + + const start = ((currentChunk - 1) * client.CHUNK_SIZE); + let end = start + currentPosition - 1; + + if(!lastUpload || currentChunk !== 1) { + apiHeaders['content-range'] = 'bytes ' + start + '-' + end + '/' + size; + } + + let uploadableChunkTrimmed; + + if(currentPosition + 1 >= client.CHUNK_SIZE) { + uploadableChunkTrimmed = uploadableChunk; + } else { + uploadableChunkTrimmed = new Uint8Array(currentPosition); + for(let i = 0; i <= currentPosition; i++) { + uploadableChunkTrimmed[i] = uploadableChunk[i]; + } + } + + if (id) { + apiHeaders['x-{{spec.title | caseLower }}-id'] = id; + } + + payload['{{ parameter.name }}'] = { type: 'file', file: new File([uploadableChunkTrimmed], {{ parameter.name | caseCamel | escapeKeyword }}.filename), filename: {{ parameter.name | caseCamel | escapeKeyword }}.filename }; + + response = await client.call('{{ method.method | caseLower }}', apiPath, apiHeaders, payload{% if method.type == 'location' %}, 'arraybuffer'{% endif %}); + + if (!id) { + id = response['$id']; + } + + if (onProgress !== null) { + onProgress({ + $id: response['$id'], + progress: Math.min((currentChunk) * client.CHUNK_SIZE, size) / size * 100, + sizeUploaded: end+1, + chunksTotal: response['chunksTotal'], + chunksUploaded: response['chunksUploaded'] + }); + } + + uploadableChunk = new Uint8Array(client.CHUNK_SIZE); + currentChunk++; + currentPosition = 0; + } + + for await (const chunk of {{ parameter.name | caseCamel | escapeKeyword }}.stream) { + for(const b of chunk) { + uploadableChunk[currentPosition] = b; + + currentPosition++; + if(currentPosition >= client.CHUNK_SIZE) { + await uploadChunk(); + currentPosition = 0; + } + } + } + + if (currentPosition > 0) { // Check if there's any remaining data for the last chunk + await uploadChunk(true); + } + +{% if method.packaging %} + fs.unlinkSync(filePath); +{% endif %} +{% if method.type == 'location' %} + fs.writeFileSync(destination, response); +{% endif %} + + if (parseOutput) { + parse(response) + success() + } + + return response; +{% endif %} +{% endfor %} diff --git a/templates/cli/lib/client.js.twig b/templates/cli/lib/client.js.twig index 32142337f..061ccf707 100644 --- a/templates/cli/lib/client.js.twig +++ b/templates/cli/lib/client.js.twig @@ -1,8 +1,7 @@ const os = require('os'); const https = require("https"); -const axios = require("axios"); +const { fetch, FormData, Agent } = require("undici"); const JSONbig = require("json-bigint")({ storeAsString: false }); -const FormData = require("form-data"); const {{spec.title | caseUcfirst}}Exception = require("./exception.js"); const { globalConfig } = require("./config.js"); @@ -94,93 +93,81 @@ class Client { return this; } - async call( - method, - path = "", - headers = {}, - params = {}, - responseType = "json" - ) { - headers = Object.assign({}, this.headers, headers); + async call(method, path = "", headers = {}, params = {}, responseType = "json") { + headers = {...this.headers, ...headers}; + const url = new URL(this.endpoint + path); - let contentType = headers["content-type"].toLowerCase(); + let body = undefined; - let formData = null; + if (method.toUpperCase() === "GET") { + url.search = new URLSearchParams(Client.flatten(params)).toString(); + } else if (headers["content-type"]?.toLowerCase().startsWith("multipart/form-data")) { + delete headers["content-type"]; + const formData = new FormData(); - if (contentType.startsWith("multipart/form-data")) { - const form = new FormData(); + const flatParams = Client.flatten(params); - let flatParams = Client.flatten(params); - - for (const key in flatParams) { - form.append(key, flatParams[key]); + for (const [key, value] of Object.entries(flatParams)) { + if (value && value.type && value.type === "file") { + formData.append(key, value.file, value.filename); + } else { + formData.append(key, value); + } } - headers = { - ...headers, - ...form.getHeaders(), - }; + body = formData; + } else { + body = JSON.stringify(params); + } - formData = form; + let response = undefined; + try { + response = await fetch(url.toString(), { + method: method.toUpperCase(), + headers, + body, + dispatcher: new Agent({ + connect: { + rejectUnauthorized: !this.selfSigned, + }, + }), + }); + } catch (error) { + throw new {{spec.title | caseUcfirst}}Exception(error.message); } - let options = { - method: method.toUpperCase(), - url: this.endpoint + path, - params: method.toUpperCase() === "GET" ? params : {}, - headers: headers, - data: - method.toUpperCase() === "GET" || contentType.startsWith("multipart/form-data") ? formData : params, - json: contentType.startsWith("application/json"), - transformRequest: method.toUpperCase() === "GET" || contentType.startsWith("multipart/form-data") ? undefined : (data) => JSONbig.stringify(data), - transformResponse: [ (data) => data ? JSONbig.parse(data) : data ], - responseType: responseType, - }; - if (this.selfSigned == true) { - // Allow self signed requests - options.httpsAgent = new https.Agent({ rejectUnauthorized: false }); + if (response.status >= 400) { + const text = await response.text(); + let json = undefined; + try { + json = JSON.parse(text); + } catch (error) { + throw new {{spec.title | caseUcfirst}}Exception(text, response.status, "", text); + } + throw new {{spec.title | caseUcfirst}}Exception(json.message, json.code, json.type, json); } + + if (responseType === "arraybuffer") { + const data = await response.arrayBuffer(); + return data; + } + + const text = await response.text(); + let json = undefined; try { - let response = await axios(options); - if (response.headers["set-cookie"]) { - globalConfig.setCookie(response.headers["set-cookie"][0]); - } - return response.data; + json = JSON.parse(text); } catch (error) { - if ("response" in error && error.response !== undefined) { - if (error.response && "data" in error.response) { - if (typeof error.response.data === "string") { - throw new {{spec.title | caseUcfirst}}Exception( - error.response.data, - error.response.status, - error.response.data - ); - } else { - throw new {{spec.title | caseUcfirst}}Exception( - error.response.data.message, - error.response.status, - error.response.data - ); - } - } else { - throw new {{spec.title | caseUcfirst}}Exception( - error.response.statusText, - error.response.status, - error.response.data - ); - } - } else { - throw new {{spec.title | caseUcfirst}}Exception(error.message); - } + return text; } + return json; } - static flatten(data, prefix = "") { + static flatten(data, prefix = '') { let output = {}; for (const key in data) { let value = data[key]; - let finalKey = prefix ? prefix + "[" + key + "]" : key; + let finalKey = prefix ? prefix + '[' + key +']' : key; if (Array.isArray(value)) { output = Object.assign(output, Client.flatten(value, finalKey)); // @todo: handle name collision here if needed diff --git a/templates/cli/lib/commands/command.js.twig b/templates/cli/lib/commands/command.js.twig index 52bc99044..b4683231d 100644 --- a/templates/cli/lib/commands/command.js.twig +++ b/templates/cli/lib/commands/command.js.twig @@ -9,205 +9,40 @@ const { Command } = require('commander'); const { sdkForProject, sdkForConsole } = require('../sdks') const { parse, actionRunner, parseInteger, parseBool, commandDescriptions, success, log } = require('../parser') const { localConfig, globalConfig } = require("../config"); +const { File } = require('undici'); const {{ service.name | caseLower }} = new Command("{{ service.name | caseLower }}").description(commandDescriptions['{{ service.name | caseLower }}']).configureHelp({ helpWidth: process.stdout.columns || 80 }) {% for method in service.methods %} -const {{ service.name | caseLower }}{{ method.name | caseUcfirst }} = async ({ {% for parameter in method.parameters.all %}{{ parameter.name | caseCamel | escapeKeyword }}, {% endfor %}parseOutput = true, sdk = undefined{% if 'multipart/form-data' in method.consumes %}, onProgress = () => {}{% endif %}{% if method.type == 'location' %}, destination{% endif %}}) => { +/** + * @typedef {Object} {{ service.name | caseUcfirst }}{{ method.name | caseUcfirst }}RequestParams {% for parameter in method.parameters.all %} - /* @param {{ '{' }}{{ parameter | typeName }}{{ '}' }} {{ parameter.name | caseCamel | escapeKeyword }} */ + * @property {{ "{" }}{{ parameter | typeName }}{{ "}" }} {{ parameter.name | caseCamel | escapeKeyword }} {{ parameter.description | replace({'`':'\''}) | replace({'\n':' '}) | replace({'\n \n':' '}) }} {% endfor %} - - let client = !sdk ? await {% if service.name == "projects" %}sdkForConsole(){% else %}sdkForProject(){% endif %} : sdk; - let apiPath = '{{ method.path }}'{% for parameter in method.parameters.path %}.replace('{{ '{' }}{{ parameter.name | caseCamel }}{{ '}' }}', {{ parameter.name | caseCamel | escapeKeyword }}){% endfor %}; - let payload = {}; -{% if method.parameters.query|length > 0 %} - - /** Query Params */ + * @property {boolean} parseOutput + * @property {libClient | undefined} sdk +{% if 'multipart/form-data' in method.consumes %} + * @property {CallableFunction} onProgress {% endif %} -{% for parameter in method.parameters.query %} - if (typeof {{ parameter.name | caseCamel | escapeKeyword }} !== 'undefined') { - payload['{{ parameter.name }}'] = {{ parameter.name | caseCamel | escapeKeyword }}; - } -{% endfor %} -{% if method.parameters.body|length > 0 %} - - /** Body Params */ +{% if method.type == 'location' %} +* @property {string} destination {% endif %} -{% for parameter in method.parameters.body %} -{% if parameter.type == 'file' and method.packaging %} - let folderPath = fs.realpathSync({{ parameter.name | caseCamel | escapeKeyword }}); - if (!fs.lstatSync(folderPath).isDirectory()) - throw new Error('The path is not a directory.'); - - const ignorer = ignore(); + */ - const func = localConfig.getFunction(functionId); - - if(func.ignore) { - ignorer.add(func.ignore); - log('Ignoring files using configuration from appwrite.json'); - } else if(fs.existsSync(pathLib.join({{ parameter.name | caseCamel | escapeKeyword }}, '.gitignore'))) { - ignorer.add(fs.readFileSync(pathLib.join({{ parameter.name | caseCamel | escapeKeyword }}, '.gitignore')).toString()); - log('Ignoring files in .gitignore'); - } +/** + * @param {{ "{" }}{{ service.name | caseUcfirst }}{{ method.name | caseUcfirst }}RequestParams{{ "}" }} params + */ +const {{ service.name | caseLower }}{{ method.name | caseUcfirst }} = async ({ {% for parameter in method.parameters.all %}{{ parameter.name | caseCamel | escapeKeyword }}, {% endfor %}parseOutput = true, sdk = undefined{% if 'multipart/form-data' in method.consumes %}, onProgress = () => {}{% endif %}{% if method.type == 'location' %}, destination{% endif %}}) => { + let client = !sdk ? await {% if service.name == "projects" %}sdkForConsole(){% else %}sdkForProject(){% endif %} : sdk; - const files = getAllFiles({{ parameter.name | caseCamel | escapeKeyword }}).map((file) => pathLib.relative({{ parameter.name | caseCamel | escapeKeyword }}, file)).filter((file) => !ignorer.ignores(file)); - - await tar - .create({ - gzip: true, - sync: true, - cwd: folderPath, - file: 'code.tar.gz' - }, files); - let archivePath = fs.realpathSync('code.tar.gz') - if (typeof archivePath !== 'undefined') { - payload['{{ parameter.name }}'] = archivePath; - {{ parameter.name }} = archivePath; - } - -{% elseif parameter.type == 'file' %} - let filePath = fs.realpathSync({{ parameter.name | caseCamel | escapeKeyword }}); - if (typeof filePath !== 'undefined') { - payload['{{ parameter.name }}'] = filePath; - } - -{% elseif parameter.type == 'object' %} - if (typeof {{ parameter.name | caseCamel | escapeKeyword }} !== 'undefined') { - payload['{{ parameter.name }}'] = JSON.parse({{ parameter.name | caseCamel | escapeKeyword}}); - } - -{% else %} -{% if parameter.type == 'array' %} - {{ parameter.name | caseCamel | escapeKeyword}} = {{ parameter.name | caseCamel | escapeKeyword}} === true ? [] : {{ parameter.name | caseCamel | escapeKeyword}}; -{% endif %} - - if (typeof {{ parameter.name | caseCamel | escapeKeyword }} !== 'undefined') { - payload['{{ parameter.name }}'] = {{ parameter.name | caseCamel | escapeKeyword}}{% if method.consumes[0] == "multipart/form-data" and ( parameter.type != "string" and parameter.type != "array" ) %}.toString(){% endif %}; - } - -{% endif %} -{% endfor %} -{% if method.type == 'location' %} - payload['project'] = localConfig.getProject().projectId - payload['key'] = globalConfig.getKey(); - const queryParams = new URLSearchParams(payload); - apiPath = `${globalConfig.getEndpoint()}${apiPath}?${queryParams.toString()}`; - - const response = await client.call('{{ method.method | caseLower }}', apiPath, { -{% for parameter in method.parameters.header %} - '{{ parameter.name }}': ${{ parameter.name | caseCamel | escapeKeyword }}, -{% endfor %} -{% for key, header in method.headers %} - '{{ key }}': '{{ header }}', -{% endfor %} - }, payload{% if method.type == 'location' %}, 'arraybuffer'{% endif %}); - - fs.writeFileSync(destination, response); - - if (parseOutput) { - log(`File stored in ${destination}`) - success() - } -{% else %} - let response = undefined; + let apiPath = '{{ method.path }}'{% for parameter in method.parameters.path %}.replace('{{ '{' }}{{ parameter.name | caseCamel }}{{ '}' }}', {{ parameter.name | caseCamel | escapeKeyword }}){% endfor %}; +{{ include ('cli/base/params.twig') }} {% if 'multipart/form-data' in method.consumes %} -{% for parameter in method.parameters.all %} -{% if parameter.type == 'file' %} - const { size: size } = await promisify(fs.stat)({{ parameter.name | caseCamel | escapeKeyword }}); - - if (size <= libClient.CHUNK_SIZE) { - payload['{{ parameter.name }}'] = fs.createReadStream(payload['{{ parameter.name }}']); - - response = await client.call('{{ method.method | caseLower }}', apiPath, { -{% for parameter in method.parameters.header %} - '{{ parameter.name }}': ${{ parameter.name | caseCamel | escapeKeyword }}, -{% endfor %} -{% for key, header in method.headers %} - '{{ key }}': '{{ header }}', -{% endfor %} - }, payload{% if method.type == 'location' %}, 'arraybuffer'{% endif %}){% if method.packaging %}.catch(err => { - fs.unlinkSync(archivePath); - throw err - });{% endif %} - - } else { - const streamFilePath = payload['{{ parameter.name }}']; - - const apiHeaders = { -{% for parameter in method.parameters.header %} - '{{ parameter.name }}': ${{ parameter.name | caseCamel | escapeKeyword }}, -{% endfor %} -{% for key, header in method.headers %} - '{{ key }}': '{{ header }}', -{% endfor %} - }; - - let offset = 0; -{% for parameter in method.parameters.all %} -{% if parameter.isUploadID %} - if({{ parameter.name | caseCamel | escapeKeyword }} != 'unique()') { - try { - response = await client.call('get', apiPath + '/' + {{ parameter.name }}, apiHeaders); - offset = response.chunksUploaded * libClient.CHUNK_SIZE; - } catch(e) { - } - } -{% endif %} -{% endfor %} - - while (offset < size) { - let end = Math.min(offset + libClient.CHUNK_SIZE - 1, size - 1); - - apiHeaders['content-range'] = 'bytes ' + offset + '-' + end + '/' + size; - if (response && response.$id) { - apiHeaders['x-{{spec.title | caseLower }}-id'] = response.$id; - } - - const stream = fs.createReadStream(streamFilePath, { - start: offset, - end - }); - payload['{{ parameter.name }}'] = stream; - response = await client.call('{{ method.method | caseLower }}', apiPath, apiHeaders, payload{% if method.type == 'location' %}, 'arraybuffer'{% endif %}); - - if (onProgress) { - onProgress({ - $id: response.$id, - progress: ( offset / size ) * 100, - sizeUploaded: offset, - chunksTotal: response.chunksTotal, - chunksUploaded: response.chunksUploaded - }); - } - offset += libClient.CHUNK_SIZE; - } - } -{% endif %} -{% endfor %} +{{ include ('cli/base/requests/file.twig') }} {% else %} - response = await client.call('{{ method.method | caseLower }}', apiPath, { -{% for parameter in method.parameters.header %} - '{{ parameter.name }}': ${{ parameter.name | caseCamel | escapeKeyword }}, -{% endfor %} -{% for key, header in method.headers %} - '{{ key }}': '{{ header }}', -{% endfor %} - }, payload{% if method.type == 'location' %}, 'arraybuffer'{% endif %}); -{% endif %} -{% if method.packaging %} - - fs.unlinkSync(archivePath); -{% endif %} - - if (parseOutput) { - parse(response) - success() - } - return response; +{{ include('cli/base/requests/api.twig') }} {% endif %} } @@ -233,6 +68,6 @@ module.exports = { {{ service.name | caseLower }}, {% for method in service.methods %} {{ service.name | caseLower }}{{ method.name | caseUcfirst }}{% if not loop.last %},{% endif %} - + {% endfor %} }; diff --git a/templates/cli/package.json.twig b/templates/cli/package.json.twig index b4c51eb16..87c11d373 100644 --- a/templates/cli/package.json.twig +++ b/templates/cli/package.json.twig @@ -22,11 +22,10 @@ "windows-arm64": "pkg -t node16-win-arm64 -o build/appwrite-cli-win-arm64.exe package.json" }, "dependencies": { - "axios": "1.5.0", + "undici": "^5.28.2", "chalk": "4.1.2", "cli-table3": "^0.6.2", "commander": "^9.2.0", - "form-data": "^4.0.0", "json-bigint": "^1.0.0", "inquirer": "^8.2.4", "tar": "^6.1.11",