From 05630776a0a12bfefd39bc9106817ca0fd57f604 Mon Sep 17 00:00:00 2001 From: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com> Date: Sun, 12 Feb 2023 06:54:07 +0100 Subject: [PATCH] fix(server): more asset upload validation and docs (#1720) * fix(server): more asset upload validation and docs * remove unused DTO * changed Object.keys() to Object.values() * apply patch to openapi generator for web * revert CreateAssetDto assetType enum * resolve merge conflict * Revert "resolve merge conflict" This reverts commit 0e0080518759a05d029f2f7a761537c58cb586d0. --- mobile/openapi/doc/AssetApi.md | Bin 32498 -> 33632 bytes mobile/openapi/lib/api/asset_api.dart | Bin 39046 -> 41770 bytes mobile/openapi/test/asset_api_test.dart | Bin 4603 -> 4809 bytes .../src/api-v1/asset/asset.controller.ts | 10 +- .../api-v1/asset/dto/asset-file-upload.dto.ts | 8 - .../src/api-v1/asset/dto/create-asset.dto.ts | 11 +- .../validation/file-not-empty-validator.ts | 25 ++ server/bin/generate-open-api.sh | 12 +- server/immich-openapi-specs.json | 44 ++- .../patch/api_client.dart.patch | 2 +- .../native/native_class.mustache | 0 .../native/native_class.mustache.patch | 0 .../templates/web/apiInner.mustache | 372 ++++++++++++++++++ .../templates/web/apiInner.mustache.patch | 14 + web/src/api/open-api/api.ts | 108 ++++- 15 files changed, 578 insertions(+), 28 deletions(-) delete mode 100644 server/apps/immich/src/api-v1/asset/dto/asset-file-upload.dto.ts create mode 100644 server/apps/immich/src/api-v1/validation/file-not-empty-validator.ts rename server/openapi-generator/templates/{ => mobile}/serialization/native/native_class.mustache (100%) rename server/openapi-generator/templates/{ => mobile}/serialization/native/native_class.mustache.patch (100%) create mode 100644 server/openapi-generator/templates/web/apiInner.mustache create mode 100644 server/openapi-generator/templates/web/apiInner.mustache.patch diff --git a/mobile/openapi/doc/AssetApi.md b/mobile/openapi/doc/AssetApi.md index 4695c961a99256bf3f47bd83276459a26ad2c5e6..aa5335d17487e8cc8dd9f03f8be6a8702bf5335a 100644 GIT binary patch delta 736 zcmezLm+?Ux(}q8l^&yo7sX7XY#l@*5E{P?HItnSNWtqvTjvxWg6c`u8N-j!GEJ;mq zEYVTO%}>cp%Y?8qi`^2-@{2M{fX1X{=A^n-l%(brXXfYWDCA_8r3Pf=m*hiC&MXeg zEY3{I0V+%>ElMl_s?ps1Q2rUC49F1*whGn?`uYlBn?VAud8N4uH42k&S6j-aW#%R3 zpgIkx9_rM|`GLxl^AfbAA*$f+1ge0uCpRXDLrpq582< SWU_=Y&*b^WqMI{nyBGn(T@Jed delta 57 zcmaFR#`NhgOn7pe}qTx9zD8~&CGTT>%o)el=tg1@6EjZzC25veo1{e>MxXo zfXl6CEnaE1-5sFX#Mirvc+}70+ubxC_GYFT?}$1t1QwI@oWD|#%eq6ngG5VEcD=k^hNVAjC)oy)fln8=NzVelM=U2V9)a$5J8o8 zJYPs|a;!NBbYZjYNYSFcyi*NFEP*vr#3_ZK@m!iy0zDjo1KeNU9Nv9XpSqx)1W$35 zs6W{qSc8yET`sx5!9Onib9 zuYdyOV>5viCukIY&oARgT&l#&lTaHc=vs5nW?xhn_l%VY8gJJG$J$oK+ORwcJS%5{ zsTWt_vf4->>G1IC@vW)J<+fNFDSh$0IO=^V8hISH9^Fjgr&kw|oILymDE@UT delta 93 zcmZ2=jHzuS(}w { const file = mapToUploadFile(files.assetData[0]); diff --git a/server/apps/immich/src/api-v1/asset/dto/asset-file-upload.dto.ts b/server/apps/immich/src/api-v1/asset/dto/asset-file-upload.dto.ts deleted file mode 100644 index 25ca5a835f..0000000000 --- a/server/apps/immich/src/api-v1/asset/dto/asset-file-upload.dto.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty } from 'class-validator'; - -export class AssetFileUploadDto { - @IsNotEmpty() - @ApiProperty({ type: 'string', format: 'binary' }) - assetData!: any; -} diff --git a/server/apps/immich/src/api-v1/asset/dto/create-asset.dto.ts b/server/apps/immich/src/api-v1/asset/dto/create-asset.dto.ts index 0d6af77bca..d27d2ff0fc 100644 --- a/server/apps/immich/src/api-v1/asset/dto/create-asset.dto.ts +++ b/server/apps/immich/src/api-v1/asset/dto/create-asset.dto.ts @@ -1,6 +1,6 @@ import { AssetType } from '@app/infra'; import { ApiProperty } from '@nestjs/swagger'; -import { IsBoolean, IsNotEmpty, IsOptional } from 'class-validator'; +import { IsBoolean, IsEnum, IsNotEmpty, IsOptional } from 'class-validator'; import { ImmichFile } from '../../../config/asset-upload.config'; export class CreateAssetDto { @@ -11,6 +11,7 @@ export class CreateAssetDto { deviceId!: string; @IsNotEmpty() + @IsEnum(AssetType) @ApiProperty({ enumName: 'AssetTypeEnum', enum: AssetType }) assetType!: AssetType; @@ -32,6 +33,14 @@ export class CreateAssetDto { @IsOptional() duration?: string; + + // The properties below are added to correctly generate the API docs + // and client SDKs. Validation should be handled in the controller. + @ApiProperty({ type: 'string', format: 'binary' }) + assetData!: any; + + @ApiProperty({ type: 'string', format: 'binary' }) + livePhotoData?: any; } export interface UploadFile { diff --git a/server/apps/immich/src/api-v1/validation/file-not-empty-validator.ts b/server/apps/immich/src/api-v1/validation/file-not-empty-validator.ts new file mode 100644 index 0000000000..f75899eecb --- /dev/null +++ b/server/apps/immich/src/api-v1/validation/file-not-empty-validator.ts @@ -0,0 +1,25 @@ +import { FileValidator, Injectable } from '@nestjs/common'; + +@Injectable() +export default class FileNotEmptyValidator extends FileValidator { + requiredFields: string[]; + + constructor(requiredFields: string[]) { + super({}); + this.requiredFields = requiredFields; + } + + isValid(files?: any): boolean { + if (!files) { + return false; + } + + return this.requiredFields.every((field) => { + return files[field]; + }); + } + + buildErrorMessage(): string { + return `Field(s) ${this.requiredFields.join(', ')} should not be empty`; + } +} diff --git a/server/bin/generate-open-api.sh b/server/bin/generate-open-api.sh index fb37bf22d3..9633d4539a 100755 --- a/server/bin/generate-open-api.sh +++ b/server/bin/generate-open-api.sh @@ -2,11 +2,11 @@ function mobile { rm -rf ../mobile/openapi - cd ./openapi-generator/templates/serialization/native + cd ./openapi-generator/templates/mobile/serialization/native wget -O native_class.mustache https://raw.githubusercontent.com/OpenAPITools/openapi-generator/master/modules/openapi-generator/src/main/resources/dart2/serialization/native/native_class.mustache patch -u native_class.mustache jsonDecode(j), json), targetType, growable: growable); - } \ No newline at end of file + } diff --git a/server/openapi-generator/templates/serialization/native/native_class.mustache b/server/openapi-generator/templates/mobile/serialization/native/native_class.mustache similarity index 100% rename from server/openapi-generator/templates/serialization/native/native_class.mustache rename to server/openapi-generator/templates/mobile/serialization/native/native_class.mustache diff --git a/server/openapi-generator/templates/serialization/native/native_class.mustache.patch b/server/openapi-generator/templates/mobile/serialization/native/native_class.mustache.patch similarity index 100% rename from server/openapi-generator/templates/serialization/native/native_class.mustache.patch rename to server/openapi-generator/templates/mobile/serialization/native/native_class.mustache.patch diff --git a/server/openapi-generator/templates/web/apiInner.mustache b/server/openapi-generator/templates/web/apiInner.mustache new file mode 100644 index 0000000000..848b41b971 --- /dev/null +++ b/server/openapi-generator/templates/web/apiInner.mustache @@ -0,0 +1,372 @@ +{{#withSeparateModelsAndApi}} +/* tslint:disable */ +/* eslint-disable */ +{{>licenseInfo}} + +import globalAxios, { AxiosPromise, AxiosInstance, AxiosRequestConfig } from 'axios'; +import { Configuration } from '{{apiRelativeToRoot}}configuration'; +{{#withNodeImports}} +// URLSearchParams not necessarily used +// @ts-ignore +import { URL, URLSearchParams } from 'url'; +{{#multipartFormData}} +import FormData from 'form-data' +{{/multipartFormData}} +{{/withNodeImports}} +// Some imports not used depending on template conditions +// @ts-ignore +import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction } from '{{apiRelativeToRoot}}common'; +// @ts-ignore +import { BASE_PATH, COLLECTION_FORMATS, RequestArgs, BaseAPI, RequiredError } from '{{apiRelativeToRoot}}base'; +{{#imports}} +// @ts-ignore +import { {{classname}} } from '{{apiRelativeToRoot}}{{tsModelPackage}}'; +{{/imports}} +{{/withSeparateModelsAndApi}} +{{^withSeparateModelsAndApi}} +{{/withSeparateModelsAndApi}} +{{#operations}} +/** + * {{classname}} - axios parameter creator{{#description}} + * {{&description}}{{/description}} + * @export + */ +export const {{classname}}AxiosParamCreator = function (configuration?: Configuration) { + return { + {{#operation}} + /** + * {{¬es}} + {{#summary}} + * @summary {{&summary}} + {{/summary}} + {{#allParams}} + * @param {{=<% %>=}}{<%&dataType%>}<%={{ }}=%> {{^required}}[{{/required}}{{paramName}}{{^required}}]{{/required}} {{description}} + {{/allParams}} + * @param {*} [options] Override http request option.{{#isDeprecated}} + * @deprecated{{/isDeprecated}} + * @throws {RequiredError} + */ + {{nickname}}: async ({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, {{/allParams}}options: AxiosRequestConfig = {}): Promise => { + {{#allParams}} + {{#required}} + // verify required parameter '{{paramName}}' is not null or undefined + assertParamExists('{{nickname}}', '{{paramName}}', {{paramName}}) + {{/required}} + {{/allParams}} + const localVarPath = `{{{path}}}`{{#pathParams}} + .replace(`{${"{{baseName}}"}}`, encodeURIComponent(String({{paramName}}))){{/pathParams}}; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: '{{httpMethod}}', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any;{{#vendorExtensions}}{{#hasFormParams}} + const localVarFormParams = new {{^multipartFormData}}URLSearchParams(){{/multipartFormData}}{{#multipartFormData}}((configuration && configuration.formDataCtor) || FormData)(){{/multipartFormData}};{{/hasFormParams}}{{/vendorExtensions}} + + {{#authMethods}} + // authentication {{name}} required + {{#isApiKey}} + {{#isKeyInHeader}} + await setApiKeyToObject(localVarHeaderParameter, "{{keyParamName}}", configuration) + {{/isKeyInHeader}} + {{#isKeyInQuery}} + await setApiKeyToObject(localVarQueryParameter, "{{keyParamName}}", configuration) + {{/isKeyInQuery}} + {{/isApiKey}} + {{#isBasicBasic}} + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration) + {{/isBasicBasic}} + {{#isBasicBearer}} + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + {{/isBasicBearer}} + {{#isOAuth}} + // oauth required + await setOAuthToObject(localVarHeaderParameter, "{{name}}", [{{#scopes}}"{{{scope}}}"{{^-last}}, {{/-last}}{{/scopes}}], configuration) + {{/isOAuth}} + + {{/authMethods}} + {{#queryParams}} + {{#isArray}} + if ({{paramName}}) { + {{#isCollectionFormatMulti}} + {{#uniqueItems}} + localVarQueryParameter['{{baseName}}'] = Array.from({{paramName}}); + {{/uniqueItems}} + {{^uniqueItems}} + localVarQueryParameter['{{baseName}}'] = {{paramName}}; + {{/uniqueItems}} + {{/isCollectionFormatMulti}} + {{^isCollectionFormatMulti}} + {{#uniqueItems}} + localVarQueryParameter['{{baseName}}'] = Array.from({{paramName}}).join(COLLECTION_FORMATS.{{collectionFormat}}); + {{/uniqueItems}} + {{^uniqueItems}} + localVarQueryParameter['{{baseName}}'] = {{paramName}}.join(COLLECTION_FORMATS.{{collectionFormat}}); + {{/uniqueItems}} + {{/isCollectionFormatMulti}} + } + {{/isArray}} + {{^isArray}} + if ({{paramName}} !== undefined) { + {{#isDateTime}} + localVarQueryParameter['{{baseName}}'] = ({{paramName}} as any instanceof Date) ? + ({{paramName}} as any).toISOString() : + {{paramName}}; + {{/isDateTime}} + {{^isDateTime}} + {{#isDate}} + localVarQueryParameter['{{baseName}}'] = ({{paramName}} as any instanceof Date) ? + ({{paramName}} as any).toISOString().substr(0,10) : + {{paramName}}; + {{/isDate}} + {{^isDate}} + localVarQueryParameter['{{baseName}}'] = {{paramName}}; + {{/isDate}} + {{/isDateTime}} + } + {{/isArray}} + + {{/queryParams}} + {{#headerParams}} + {{#isArray}} + if ({{paramName}}) { + {{#uniqueItems}} + let mapped = Array.from({{paramName}}).map(value => ("{{{dataType}}}" !== "Set") ? JSON.stringify(value) : (value || "")); + {{/uniqueItems}} + {{^uniqueItems}} + let mapped = {{paramName}}.map(value => ("{{{dataType}}}" !== "Array") ? JSON.stringify(value) : (value || "")); + {{/uniqueItems}} + localVarHeaderParameter['{{baseName}}'] = mapped.join(COLLECTION_FORMATS["{{collectionFormat}}"]); + } + {{/isArray}} + {{^isArray}} + if ({{paramName}} !== undefined && {{paramName}} !== null) { + {{#isString}} + localVarHeaderParameter['{{baseName}}'] = String({{paramName}}); + {{/isString}} + {{^isString}} + localVarHeaderParameter['{{baseName}}'] = String(JSON.stringify({{paramName}})); + {{/isString}} + } + {{/isArray}} + + {{/headerParams}} + {{#vendorExtensions}} + {{#formParams}} + {{#isArray}} + if ({{paramName}}) { + {{#isCollectionFormatMulti}} + {{paramName}}.forEach((element) => { + localVarFormParams.{{#multipartFormData}}append{{/multipartFormData}}{{^multipartFormData}}set{{/multipartFormData}}('{{baseName}}', element as any); + }) + {{/isCollectionFormatMulti}} + {{^isCollectionFormatMulti}} + localVarFormParams.{{#multipartFormData}}append{{/multipartFormData}}{{^multipartFormData}}set{{/multipartFormData}}('{{baseName}}', {{paramName}}.join(COLLECTION_FORMATS.{{collectionFormat}})); + {{/isCollectionFormatMulti}} + }{{/isArray}} + {{^isArray}} + if ({{paramName}} !== undefined) { {{^multipartFormData}} + localVarFormParams.set('{{baseName}}', {{paramName}} as any);{{/multipartFormData}}{{#multipartFormData}}{{#isPrimitiveType}} + localVarFormParams.append('{{baseName}}', {{paramName}} as any);{{/isPrimitiveType}}{{^isPrimitiveType}}{{#isEnum}} + localVarFormParams.append('{{baseName}}', {{paramName}} as any);{{/isEnum}}{{^isEnum}} + localVarFormParams.append('{{baseName}}', new Blob([JSON.stringify({{paramName}})], { type: "application/json", }));{{/isEnum}}{{/isPrimitiveType}}{{/multipartFormData}} + }{{/isArray}} + {{/formParams}}{{/vendorExtensions}} + {{#vendorExtensions}}{{#hasFormParams}}{{^multipartFormData}} + localVarHeaderParameter['Content-Type'] = 'application/x-www-form-urlencoded';{{/multipartFormData}}{{#multipartFormData}} + localVarHeaderParameter['Content-Type'] = 'multipart/form-data';{{/multipartFormData}} + {{/hasFormParams}}{{/vendorExtensions}} + {{#bodyParam}} + {{^consumes}} + localVarHeaderParameter['Content-Type'] = 'application/json'; + {{/consumes}} + {{#consumes.0}} + localVarHeaderParameter['Content-Type'] = '{{{mediaType}}}'; + {{/consumes.0}} + + {{/bodyParam}} + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions,{{#hasFormParams}}{{#multipartFormData}} ...(localVarFormParams as any).getHeaders?.(),{{/multipartFormData}}{{/hasFormParams}} ...options.headers}; + {{#hasFormParams}} + localVarRequestOptions.data = localVarFormParams{{#vendorExtensions}}{{^multipartFormData}}.toString(){{/multipartFormData}}{{/vendorExtensions}}; + {{/hasFormParams}} + {{#bodyParam}} + localVarRequestOptions.data = serializeDataIfNeeded({{paramName}}, localVarRequestOptions, configuration) + {{/bodyParam}} + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + {{/operation}} + } +}; + +/** + * {{classname}} - functional programming interface{{#description}} + * {{{.}}}{{/description}} + * @export + */ +export const {{classname}}Fp = function(configuration?: Configuration) { + const localVarAxiosParamCreator = {{classname}}AxiosParamCreator(configuration) + return { + {{#operation}} + /** + * {{¬es}} + {{#summary}} + * @summary {{&summary}} + {{/summary}} + {{#allParams}} + * @param {{=<% %>=}}{<%&dataType%>}<%={{ }}=%> {{^required}}[{{/required}}{{paramName}}{{^required}}]{{/required}} {{description}} + {{/allParams}} + * @param {*} [options] Override http request option.{{#isDeprecated}} + * @deprecated{{/isDeprecated}} + * @throws {RequiredError} + */ + async {{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, {{/allParams}}options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<{{{returnType}}}{{^returnType}}void{{/returnType}}>> { + const localVarAxiosArgs = await localVarAxiosParamCreator.{{nickname}}({{#allParams}}{{paramName}}, {{/allParams}}options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + {{/operation}} + } +}; + +/** + * {{classname}} - factory interface{{#description}} + * {{&description}}{{/description}} + * @export + */ +export const {{classname}}Factory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { + const localVarFp = {{classname}}Fp(configuration) + return { + {{#operation}} + /** + * {{¬es}} + {{#summary}} + * @summary {{&summary}} + {{/summary}} + {{#allParams}} + * @param {{=<% %>=}}{<%&dataType%>}<%={{ }}=%> {{^required}}[{{/required}}{{paramName}}{{^required}}]{{/required}} {{description}} + {{/allParams}} + * @param {*} [options] Override http request option.{{#isDeprecated}} + * @deprecated{{/isDeprecated}} + * @throws {RequiredError} + */ + {{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, {{/allParams}}options?: any): AxiosPromise<{{{returnType}}}{{^returnType}}void{{/returnType}}> { + return localVarFp.{{nickname}}({{#allParams}}{{paramName}}, {{/allParams}}options).then((request) => request(axios, basePath)); + }, + {{/operation}} + }; +}; + +{{#withInterfaces}} +/** + * {{classname}} - interface{{#description}} + * {{&description}}{{/description}} + * @export + * @interface {{classname}} + */ +export interface {{classname}}Interface { +{{#operation}} + /** + * {{¬es}} + {{#summary}} + * @summary {{&summary}} + {{/summary}} + {{#allParams}} + * @param {{=<% %>=}}{<%&dataType%>}<%={{ }}=%> {{^required}}[{{/required}}{{paramName}}{{^required}}]{{/required}} {{description}} + {{/allParams}} + * @param {*} [options] Override http request option.{{#isDeprecated}} + * @deprecated{{/isDeprecated}} + * @throws {RequiredError} + * @memberof {{classname}}Interface + */ + {{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, {{/allParams}}options?: AxiosRequestConfig): AxiosPromise<{{{returnType}}}{{^returnType}}void{{/returnType}}>; + +{{/operation}} +} + +{{/withInterfaces}} +{{#useSingleRequestParameter}} +{{#operation}} +{{#allParams.0}} +/** + * Request parameters for {{nickname}} operation in {{classname}}. + * @export + * @interface {{classname}}{{operationIdCamelCase}}Request + */ +export interface {{classname}}{{operationIdCamelCase}}Request { + {{#allParams}} + /** + * {{description}} + * @type {{=<% %>=}}{<%&dataType%>}<%={{ }}=%> + * @memberof {{classname}}{{operationIdCamelCase}} + */ + readonly {{paramName}}{{^required}}?{{/required}}: {{{dataType}}} + {{^-last}} + + {{/-last}} + {{/allParams}} +} + +{{/allParams.0}} +{{/operation}} +{{/useSingleRequestParameter}} +/** + * {{classname}} - object-oriented interface{{#description}} + * {{{.}}}{{/description}} + * @export + * @class {{classname}} + * @extends {BaseAPI} + */ +{{#withInterfaces}} +export class {{classname}} extends BaseAPI implements {{classname}}Interface { +{{/withInterfaces}} +{{^withInterfaces}} +export class {{classname}} extends BaseAPI { +{{/withInterfaces}} + {{#operation}} + /** + * {{¬es}} + {{#summary}} + * @summary {{&summary}} + {{/summary}} + {{#useSingleRequestParameter}} + {{#allParams.0}} + * @param {{=<% %>=}}{<%& classname %><%& operationIdCamelCase %>Request}<%={{ }}=%> requestParameters Request parameters. + {{/allParams.0}} + {{/useSingleRequestParameter}} + {{^useSingleRequestParameter}} + {{#allParams}} + * @param {{=<% %>=}}{<%&dataType%>}<%={{ }}=%> {{^required}}[{{/required}}{{paramName}}{{^required}}]{{/required}} {{description}} + {{/allParams}} + {{/useSingleRequestParameter}} + * @param {*} [options] Override http request option.{{#isDeprecated}} + * @deprecated{{/isDeprecated}} + * @throws {RequiredError} + * @memberof {{classname}} + */ + {{#useSingleRequestParameter}} + public {{nickname}}({{#allParams.0}}requestParameters: {{classname}}{{operationIdCamelCase}}Request{{^hasRequiredParams}} = {}{{/hasRequiredParams}}, {{/allParams.0}}options?: AxiosRequestConfig) { + return {{classname}}Fp(this.configuration).{{nickname}}({{#allParams.0}}{{#allParams}}requestParameters.{{paramName}}, {{/allParams}}{{/allParams.0}}options).then((request) => request(this.axios, this.basePath)); + } + {{/useSingleRequestParameter}} + {{^useSingleRequestParameter}} + public {{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, {{/allParams}}options?: AxiosRequestConfig) { + return {{classname}}Fp(this.configuration).{{nickname}}({{#allParams}}{{paramName}}, {{/allParams}}options).then((request) => request(this.axios, this.basePath)); + } + {{/useSingleRequestParameter}} + {{^-last}} + + {{/-last}} + {{/operation}} +} +{{/operations}} diff --git a/server/openapi-generator/templates/web/apiInner.mustache.patch b/server/openapi-generator/templates/web/apiInner.mustache.patch new file mode 100644 index 0000000000..99963da620 --- /dev/null +++ b/server/openapi-generator/templates/web/apiInner.mustache.patch @@ -0,0 +1,14 @@ +--- apiInner.mustache 2023-02-10 17:44:20.945845049 +0000 ++++ apiInner.mustache.patch 2023-02-10 17:46:28.669054112 +0000 +@@ -173,8 +173,9 @@ + {{^isArray}} + if ({{paramName}} !== undefined) { {{^multipartFormData}} + localVarFormParams.set('{{baseName}}', {{paramName}} as any);{{/multipartFormData}}{{#multipartFormData}}{{#isPrimitiveType}} +- localVarFormParams.append('{{baseName}}', {{paramName}} as any);{{/isPrimitiveType}}{{^isPrimitiveType}} +- localVarFormParams.append('{{baseName}}', new Blob([JSON.stringify({{paramName}})], { type: "application/json", }));{{/isPrimitiveType}}{{/multipartFormData}} ++ localVarFormParams.append('{{baseName}}', {{paramName}} as any);{{/isPrimitiveType}}{{^isPrimitiveType}}{{#isEnum}} ++ localVarFormParams.append('{{baseName}}', {{paramName}} as any);{{/isEnum}}{{^isEnum}} ++ localVarFormParams.append('{{baseName}}', new Blob([JSON.stringify({{paramName}})], { type: "application/json", }));{{/isEnum}}{{/isPrimitiveType}}{{/multipartFormData}} + }{{/isArray}} + {{/formParams}}{{/vendorExtensions}} + {{#vendorExtensions}}{{#hasFormParams}}{{^multipartFormData}} diff --git a/web/src/api/open-api/api.ts b/web/src/api/open-api/api.ts index 0c0e98ea70..5ceade1fc5 100644 --- a/web/src/api/open-api/api.ts +++ b/web/src/api/open-api/api.ts @@ -4402,13 +4402,37 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration }, /** * + * @param {AssetTypeEnum} assetType * @param {any} assetData + * @param {string} deviceAssetId + * @param {string} deviceId + * @param {string} createdAt + * @param {string} modifiedAt + * @param {boolean} isFavorite + * @param {string} fileExtension + * @param {any} [livePhotoData] + * @param {boolean} [isVisible] + * @param {string} [duration] * @param {*} [options] Override http request option. * @throws {RequiredError} */ - uploadFile: async (assetData: any, options: AxiosRequestConfig = {}): Promise => { + uploadFile: async (assetType: AssetTypeEnum, assetData: any, deviceAssetId: string, deviceId: string, createdAt: string, modifiedAt: string, isFavorite: boolean, fileExtension: string, livePhotoData?: any, isVisible?: boolean, duration?: string, options: AxiosRequestConfig = {}): Promise => { + // verify required parameter 'assetType' is not null or undefined + assertParamExists('uploadFile', 'assetType', assetType) // verify required parameter 'assetData' is not null or undefined assertParamExists('uploadFile', 'assetData', assetData) + // verify required parameter 'deviceAssetId' is not null or undefined + assertParamExists('uploadFile', 'deviceAssetId', deviceAssetId) + // verify required parameter 'deviceId' is not null or undefined + assertParamExists('uploadFile', 'deviceId', deviceId) + // verify required parameter 'createdAt' is not null or undefined + assertParamExists('uploadFile', 'createdAt', createdAt) + // verify required parameter 'modifiedAt' is not null or undefined + assertParamExists('uploadFile', 'modifiedAt', modifiedAt) + // verify required parameter 'isFavorite' is not null or undefined + assertParamExists('uploadFile', 'isFavorite', isFavorite) + // verify required parameter 'fileExtension' is not null or undefined + assertParamExists('uploadFile', 'fileExtension', fileExtension) const localVarPath = `/asset/upload`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); @@ -4427,10 +4451,50 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration await setBearerAuthToObject(localVarHeaderParameter, configuration) + if (assetType !== undefined) { + localVarFormParams.append('assetType', new Blob([JSON.stringify(assetType)], { type: "application/json", })); + } + if (assetData !== undefined) { localVarFormParams.append('assetData', assetData as any); } + if (livePhotoData !== undefined) { + localVarFormParams.append('livePhotoData', livePhotoData as any); + } + + if (deviceAssetId !== undefined) { + localVarFormParams.append('deviceAssetId', deviceAssetId as any); + } + + if (deviceId !== undefined) { + localVarFormParams.append('deviceId', deviceId as any); + } + + if (createdAt !== undefined) { + localVarFormParams.append('createdAt', createdAt as any); + } + + if (modifiedAt !== undefined) { + localVarFormParams.append('modifiedAt', modifiedAt as any); + } + + if (isFavorite !== undefined) { + localVarFormParams.append('isFavorite', isFavorite as any); + } + + if (isVisible !== undefined) { + localVarFormParams.append('isVisible', isVisible as any); + } + + if (fileExtension !== undefined) { + localVarFormParams.append('fileExtension', fileExtension as any); + } + + if (duration !== undefined) { + localVarFormParams.append('duration', duration as any); + } + localVarHeaderParameter['Content-Type'] = 'multipart/form-data'; @@ -4668,12 +4732,22 @@ export const AssetApiFp = function(configuration?: Configuration) { }, /** * + * @param {AssetTypeEnum} assetType * @param {any} assetData + * @param {string} deviceAssetId + * @param {string} deviceId + * @param {string} createdAt + * @param {string} modifiedAt + * @param {boolean} isFavorite + * @param {string} fileExtension + * @param {any} [livePhotoData] + * @param {boolean} [isVisible] + * @param {string} [duration] * @param {*} [options] Override http request option. * @throws {RequiredError} */ - async uploadFile(assetData: any, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.uploadFile(assetData, options); + async uploadFile(assetType: AssetTypeEnum, assetData: any, deviceAssetId: string, deviceId: string, createdAt: string, modifiedAt: string, isFavorite: boolean, fileExtension: string, livePhotoData?: any, isVisible?: boolean, duration?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.uploadFile(assetType, assetData, deviceAssetId, deviceId, createdAt, modifiedAt, isFavorite, fileExtension, livePhotoData, isVisible, duration, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } @@ -4879,12 +4953,22 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath }, /** * + * @param {AssetTypeEnum} assetType * @param {any} assetData + * @param {string} deviceAssetId + * @param {string} deviceId + * @param {string} createdAt + * @param {string} modifiedAt + * @param {boolean} isFavorite + * @param {string} fileExtension + * @param {any} [livePhotoData] + * @param {boolean} [isVisible] + * @param {string} [duration] * @param {*} [options] Override http request option. * @throws {RequiredError} */ - uploadFile(assetData: any, options?: any): AxiosPromise { - return localVarFp.uploadFile(assetData, options).then((request) => request(axios, basePath)); + uploadFile(assetType: AssetTypeEnum, assetData: any, deviceAssetId: string, deviceId: string, createdAt: string, modifiedAt: string, isFavorite: boolean, fileExtension: string, livePhotoData?: any, isVisible?: boolean, duration?: string, options?: any): AxiosPromise { + return localVarFp.uploadFile(assetType, assetData, deviceAssetId, deviceId, createdAt, modifiedAt, isFavorite, fileExtension, livePhotoData, isVisible, duration, options).then((request) => request(axios, basePath)); }, }; }; @@ -5131,13 +5215,23 @@ export class AssetApi extends BaseAPI { /** * + * @param {AssetTypeEnum} assetType * @param {any} assetData + * @param {string} deviceAssetId + * @param {string} deviceId + * @param {string} createdAt + * @param {string} modifiedAt + * @param {boolean} isFavorite + * @param {string} fileExtension + * @param {any} [livePhotoData] + * @param {boolean} [isVisible] + * @param {string} [duration] * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof AssetApi */ - public uploadFile(assetData: any, options?: AxiosRequestConfig) { - return AssetApiFp(this.configuration).uploadFile(assetData, options).then((request) => request(this.axios, this.basePath)); + public uploadFile(assetType: AssetTypeEnum, assetData: any, deviceAssetId: string, deviceId: string, createdAt: string, modifiedAt: string, isFavorite: boolean, fileExtension: string, livePhotoData?: any, isVisible?: boolean, duration?: string, options?: AxiosRequestConfig) { + return AssetApiFp(this.configuration).uploadFile(assetType, assetData, deviceAssetId, deviceId, createdAt, modifiedAt, isFavorite, fileExtension, livePhotoData, isVisible, duration, options).then((request) => request(this.axios, this.basePath)); } }