Skip to content

Commit

Permalink
feat(opapi): support for binary content types (#203)
Browse files Browse the repository at this point in the history
  • Loading branch information
spg authored Mar 13, 2024
1 parent b5545c7 commit bf01620
Show file tree
Hide file tree
Showing 7 changed files with 26 additions and 10 deletions.
2 changes: 1 addition & 1 deletion opapi/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@bpinternal/opapi",
"version": "0.7.10",
"version": "0.8.0",
"description": "",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
Expand Down
1 change: 1 addition & 0 deletions opapi/src/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ function mapOperationPropsToHandlerProps(
params: [],
body: isOperationWithBodyProps(operation) ? true : false,
isEmptyBody: isOperationWithBodyProps(operation) ? schemaIsEmptyObject(operation.requestBody.schema) : true,
contentType: isOperationWithBodyProps(operation) ? operation.contentType ?? 'application/json' : 'application/json',
}

if (operation.parameters) {
Expand Down
8 changes: 5 additions & 3 deletions opapi/src/generators/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export type GenerateHandlerProps = {
status: number
body: boolean
isEmptyBody: boolean
contentType: string
}

const generateRequestResponseTypes = (useExpressTypes: boolean) => {
Expand All @@ -63,8 +64,8 @@ ${generateParameterFields(props)}
}
`

const generateBodyField = (operationName: string) =>
`\t\t...req.body as NonNullable<components['requestBodies']['${operationName}Body']>['content']['application/json'],`
const generateBodyField = (operationName: string, contentType: string) =>
`\t\t...req.body as NonNullable<components['requestBodies']['${operationName}Body']>['content']['${contentType}'],`

const generateParameterFields = ({
cookies,
Expand All @@ -73,9 +74,10 @@ const generateParameterFields = ({
queries,
body,
operationName,
contentType,
}: GenerateHandlerProps): string =>
[
body ? generateBodyField(operationName) : undefined,
body ? generateBodyField(operationName, contentType) : undefined,
...cookies.map((cookie) =>
generateField(cookie.name, 'cookies', cookie.parameter, cookie.parameter.required !== false),
),
Expand Down
7 changes: 5 additions & 2 deletions opapi/src/generators/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
const typesCode = `
type RequestBody = { requestBody?: { content: { 'application/json': {} } } }
type JsonRequestBody = { requestBody?: { content: { 'application/json': {} } } }
type BinaryRequestBody = { requestBody?: { content: { '*/*': {} } } }
type RequestBody = JsonRequestBody | BinaryRequestBody
type Responses = { responses: { default: { content: { 'application/json': {} } } } }
type Parameters = { parameters?: {} }
Expand All @@ -9,7 +12,7 @@ type GetHeaderParameters<T> = T extends { header?: any } ? NonNullable<T['header
type GetCookieParameters<T> = T extends { cookie?: any } ? NonNullable<T['cookie']> : {}
type GetQueryParameters<T> = T extends { query?: any } ? NonNullable<T['query']> : {}
type GetRequestBody<T> = T extends RequestBody ? NonNullable<T['requestBody']>['content']['application/json'] : {}
type GetRequestBody<T> = T extends JsonRequestBody ? NonNullable<T['requestBody']>['content']['application/json'] : T extends BinaryRequestBody ? NonNullable<T['requestBody']>['content']['*/*'] : {}
type GetParameters<T> = T extends Parameters
? GetPathParameters<NonNullable<T['parameters']>> &
GetHeaderParameters<NonNullable<T['parameters']>> &
Expand Down
3 changes: 2 additions & 1 deletion opapi/src/openapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,12 @@ export const createOpenapi = <

if (isOperationWithBodyProps(operationObject)) {
const requestBody = operationObject.requestBody
const contentType = operationObject.contentType ?? 'application/json'

openapi.addRequestBody(bodyName, {
description: requestBody.description,
content: {
'application/json': {
[contentType]: {
schema: requestBody.schema,
},
},
Expand Down
10 changes: 8 additions & 2 deletions opapi/src/operation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ export const addOperation = <
const response = {
description: operationProps.response.description,
status: operationProps.response.status,
schema: generateSchemaFromZod(extendApi(operationProps.response.schema, { title: responseName }), state.options),
schema: generateSchemaFromZod(
extendApi(operationProps.response.schema, { title: responseName, format: operationProps.response.format }),
state.options,
),
}

let operation: Operation<DefaultParameterName, SectionName, string, 'json-schema'>
Expand All @@ -62,7 +65,10 @@ export const addOperation = <
response,
requestBody: {
description: operationProps.requestBody.description,
schema: generateSchemaFromZod(extendApi(operationProps.requestBody.schema, { title: bodyName }), state.options),
schema: generateSchemaFromZod(
extendApi(operationProps.requestBody.schema, { title: bodyName, format: operationProps.requestBody?.format }),
state.options,
),
},
}
} else {
Expand Down
5 changes: 4 additions & 1 deletion opapi/src/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,9 @@ export type OperationWithBodyProps<
requestBody: {
description: string
schema: SchemaOfType<S>
format?: 'binary'
}
contentType?: 'application/json' | '*/*'
} & BaseOperationProps<DefaultParameterName, SectionName, Path, S>

export const operationsWithoutBodyMethod = ['get', 'delete', 'options', 'head', 'trace'] as const
Expand Down Expand Up @@ -196,9 +198,10 @@ type BaseOperationProps<
response: {
// Status code of the response
// Default is 200
status?: 200 | 201 | 418
status?: 200 | 201 | 418 | 302
description: string
schema: SchemaOfType<S>
format?: 'binary'
}
}

Expand Down

0 comments on commit bf01620

Please sign in to comment.