Skip to content

Commit

Permalink
feat(oas-typescript-koa): implement multer for file uploads and stuff (
Browse files Browse the repository at this point in the history
  • Loading branch information
imballinst authored Jan 7, 2024
1 parent 19c6f72 commit 4a31928
Show file tree
Hide file tree
Showing 27 changed files with 388 additions and 194 deletions.
7 changes: 7 additions & 0 deletions .changeset/stupid-hornets-smoke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@oas-typescript/shared-cli-http-server': minor
'@oas-typescript/koa': minor
'@oas-typescript/shared': minor
---

feat: implement multer for file uploads and stuff
Binary file not shown.
Binary file not shown.
Binary file modified .yarn/install-state.gz
Binary file not shown.
43 changes: 23 additions & 20 deletions packages/oas-typescript-koa/generated-vanilla/static/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ export const Pet = z
})
.passthrough();
export interface Pet extends z.infer<typeof Pet> {}
export const uploadFileMultipart_Body = z
.object({ name: z.string(), profileImage: z.any() })
.partial()
.passthrough();
export interface uploadFileMultipart_Body
extends z.infer<typeof uploadFileMultipart_Body> {}
export const Order = z
.object({
id: z.number().int(),
Expand Down Expand Up @@ -314,12 +320,12 @@ export const DeletePetErrors = {
} as const;
export type DeletePetErrors = typeof DeletePetErrors;

export const UploadFileParameters = [
export const UploadFileMultipartParameters = [
{
name: 'body',
type: 'Body',
schema: z.object({ profileImage: z.string() }),
formDataMode: 'single'
schema: uploadFileMultipart_Body,
isFormData: true
},
{
name: 'petId',
Expand All @@ -332,7 +338,7 @@ export const UploadFileParameters = [
schema: z.string().optional()
}
] as const;
export const UploadFileSecurity = {
export const UploadFileMultipartSecurity = {
petstore_auth: {
meta: {
type: 'oauth2',
Expand All @@ -350,24 +356,24 @@ export const UploadFileSecurity = {
}
} as const;

export const UploadFileResponse = {
export const UploadFileMultipartResponse = {
schema: z
.object({ code: z.number().int(), type: z.string(), message: z.string() })
.partial()
.passthrough(),
status: 200
} as const;
export type UploadFileResponse = typeof UploadFileResponse;
export type UploadFileMultipartResponse = typeof UploadFileMultipartResponse;

export const UploadFileErrors = {} as const;
export type UploadFileErrors = typeof UploadFileErrors;
export const UploadFileMultipartErrors = {} as const;
export type UploadFileMultipartErrors = typeof UploadFileMultipartErrors;

export const UploadFileMultipartParameters = [
export const UploadFileParameters = [
{
name: 'body',
type: 'Body',
schema: z.object({ profileImage: z.any() }).partial().passthrough(),
formDataMode: 'multiple'
schema: z.object({ profileImage: z.string() }),
isFormData: true
},
{
name: 'petId',
Expand All @@ -380,7 +386,7 @@ export const UploadFileMultipartParameters = [
schema: z.string().optional()
}
] as const;
export const UploadFileMultipartSecurity = {
export const UploadFileSecurity = {
petstore_auth: {
meta: {
type: 'oauth2',
Expand All @@ -398,17 +404,14 @@ export const UploadFileMultipartSecurity = {
}
} as const;

export const UploadFileMultipartResponse = {
schema: z
.object({ code: z.number().int(), type: z.string(), message: z.string() })
.partial()
.passthrough(),
export const UploadFileResponse = {
schema: z.void(),
status: 200
} as const;
export type UploadFileMultipartResponse = typeof UploadFileMultipartResponse;
export type UploadFileResponse = typeof UploadFileResponse;

export const UploadFileMultipartErrors = {} as const;
export type UploadFileMultipartErrors = typeof UploadFileMultipartErrors;
export const UploadFileErrors = {} as const;
export type UploadFileErrors = typeof UploadFileErrors;

export const FindPetsByStatusParameters = [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,12 @@ router.post(
);

router.post(
'/pet/:petId/uploadImageMultipart',
'/pet/:petId/updatePetMultipart',
KoaGeneratedUtils.createSecurityMiddleware(UploadFileMultipartSecurity),
upload.fields([
{
name: 'name'
},
{
name: 'profileImage'
}
Expand Down
22 changes: 13 additions & 9 deletions packages/oas-typescript-koa/generated-vanilla/static/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,20 @@ export type ResponseHeaders<
THeadersSchemaType = string | number | z.ZodSchema
> = Record<string, { schema: THeadersSchemaType; nullable?: boolean }>;

type MaybePromise<T> = T | Promise<T>;

type BuildResponseObject<TObject extends { headers?: ResponseHeaders }> =
TObject['headers'] extends object
? Omit<TObject, 'headers'> & {
headers: {
[K in keyof TObject['headers']]: TObject['headers'][K]['schema'] extends z.ZodSchema
? z.infer<TObject['headers'][K]['schema']>
: TObject['headers'][K]['schema'];
};
}
: Omit<TObject, 'headers'>;
MaybePromise<
TObject['headers'] extends object
? Omit<TObject, 'headers'> & {
headers: {
[K in keyof TObject['headers']]: TObject['headers'][K]['schema'] extends z.ZodSchema
? z.infer<TObject['headers'][K]['schema']>
: TObject['headers'][K]['schema'];
};
}
: Omit<TObject, 'headers'>
>;

export interface ErrorResponse<
TSchemaType = string,
Expand Down
38 changes: 33 additions & 5 deletions packages/oas-typescript-koa/generated-vanilla/static/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export interface OasParameter {
name: string;
description?: string;
type: 'Path' | 'Query' | 'Body' | 'Header';
formDataMode?: 'single' | 'multiple';
isFormData?: boolean;
schema: z.ZodTypeAny;
}

Expand Down Expand Up @@ -53,6 +53,8 @@ export class KoaGeneratedUtils {
ctx: Koa.Context;
oasParameters: OasParametersType;
}): ParsedRequestInfo<OasParametersType> | undefined {
try {
} catch (err) {}
const pathParams: Record<string, any> = {};
const queryParams: Record<string, any> = {};
const headerParams: Record<string, any> = {};
Expand Down Expand Up @@ -84,10 +86,36 @@ export class KoaGeneratedUtils {
if (oasParameter.type === 'Body') {
let body: any;

if (oasParameter.formDataMode === 'single') {
body = ctx.request.file;
} else if (oasParameter.formDataMode === 'multiple') {
body = ctx.request.files;
// TODO: properly handle multer.single, multer.array, and multer.fields.
if (oasParameter.isFormData) {
if (ctx.request.file) {
// Single file --> multer: single.
body = {
[ctx.request.file.fieldname]:
ctx.request.file.buffer.toString('base64')
};
} else {
// Multiple files --> multer: array or fields.
body = ctx.request.body;

const files = ctx.request.files;

if (!Array.isArray(files)) {
// Multer: fields.
for (const key in files) {
body[key] = files[key].map((item) =>
item.buffer.toString('base64')
);
}
} else if (files.length > 0) {
// Multer: array.
body = {
[files[0].fieldname]: files.map((item) =>
item.buffer.toString('base64')
)
};
}
}
} else {
body = ctx.request.body;
}
Expand Down
24 changes: 12 additions & 12 deletions packages/oas-typescript-koa/generated/controllers/PetController.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { mkdir, writeFile } from 'fs/promises';
import { Pet } from '../static/client.js';
import {
AddPetControllerFunction,
Expand All @@ -10,6 +11,7 @@ import {
UploadFileControllerFunction,
UploadFileMultipartControllerFunction
} from '../static/controller-types/PetControllerTypes.js';
import path from 'path';

const db: Pet[] = [];

Expand Down Expand Up @@ -73,26 +75,24 @@ export class PetController {
status: 204
};
};
static uploadFile: UploadFileControllerFunction = (params) => {
return {
body: {},
status: 200
};
};
static uploadFileMultipart: UploadFileMultipartControllerFunction = (
params
) => {
static uploadFile: UploadFileControllerFunction = async (params) => {
await mkdir(path.join(process.cwd(), 'tests/output'), { recursive: true });
await writeFile(
path.join(process.cwd(), 'tests/output/image.jpg'),
Buffer.from(params.body.profileImage, 'base64'),
'binary'
);
return {
body: undefined,
status: undefined
status: 200
};
};
static uploadFileMultipart: UploadFileMultipartControllerFunction = (
params
) => {
return {
body: undefined,
status: undefined
body: {},
status: 200
};
};
}
43 changes: 23 additions & 20 deletions packages/oas-typescript-koa/generated/static/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ export const Pet = z
})
.passthrough();
export interface Pet extends z.infer<typeof Pet> {}
export const uploadFileMultipart_Body = z
.object({ name: z.string(), profileImage: z.any() })
.partial()
.passthrough();
export interface uploadFileMultipart_Body
extends z.infer<typeof uploadFileMultipart_Body> {}
export const Order = z
.object({
id: z.number().int(),
Expand Down Expand Up @@ -314,12 +320,12 @@ export const DeletePetErrors = {
} as const;
export type DeletePetErrors = typeof DeletePetErrors;

export const UploadFileParameters = [
export const UploadFileMultipartParameters = [
{
name: 'body',
type: 'Body',
schema: z.object({ profileImage: z.string() }),
formDataMode: 'single'
schema: uploadFileMultipart_Body,
isFormData: true
},
{
name: 'petId',
Expand All @@ -332,7 +338,7 @@ export const UploadFileParameters = [
schema: z.string().optional()
}
] as const;
export const UploadFileSecurity = {
export const UploadFileMultipartSecurity = {
petstore_auth: {
meta: {
type: 'oauth2',
Expand All @@ -350,24 +356,24 @@ export const UploadFileSecurity = {
}
} as const;

export const UploadFileResponse = {
export const UploadFileMultipartResponse = {
schema: z
.object({ code: z.number().int(), type: z.string(), message: z.string() })
.partial()
.passthrough(),
status: 200
} as const;
export type UploadFileResponse = typeof UploadFileResponse;
export type UploadFileMultipartResponse = typeof UploadFileMultipartResponse;

export const UploadFileErrors = {} as const;
export type UploadFileErrors = typeof UploadFileErrors;
export const UploadFileMultipartErrors = {} as const;
export type UploadFileMultipartErrors = typeof UploadFileMultipartErrors;

export const UploadFileMultipartParameters = [
export const UploadFileParameters = [
{
name: 'body',
type: 'Body',
schema: z.object({ profileImage: z.any() }).partial().passthrough(),
formDataMode: 'multiple'
schema: z.object({ profileImage: z.string() }),
isFormData: true
},
{
name: 'petId',
Expand All @@ -380,7 +386,7 @@ export const UploadFileMultipartParameters = [
schema: z.string().optional()
}
] as const;
export const UploadFileMultipartSecurity = {
export const UploadFileSecurity = {
petstore_auth: {
meta: {
type: 'oauth2',
Expand All @@ -398,17 +404,14 @@ export const UploadFileMultipartSecurity = {
}
} as const;

export const UploadFileMultipartResponse = {
schema: z
.object({ code: z.number().int(), type: z.string(), message: z.string() })
.partial()
.passthrough(),
export const UploadFileResponse = {
schema: z.void(),
status: 200
} as const;
export type UploadFileMultipartResponse = typeof UploadFileMultipartResponse;
export type UploadFileResponse = typeof UploadFileResponse;

export const UploadFileMultipartErrors = {} as const;
export type UploadFileMultipartErrors = typeof UploadFileMultipartErrors;
export const UploadFileErrors = {} as const;
export type UploadFileErrors = typeof UploadFileErrors;

export const FindPetsByStatusParameters = [
{
Expand Down
Loading

0 comments on commit 4a31928

Please sign in to comment.