-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add method to generate openApi documentation
- Loading branch information
Quentin Nativel
committed
Nov 17, 2023
1 parent
a0ccbb4
commit 358715e
Showing
5 changed files
with
283 additions
and
0 deletions.
There are no files selected for viewing
1 change: 1 addition & 0 deletions
1
packages/aws-zod-interface-contracts/src/apiGateway/features/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
export * from './lambdaHandler'; | ||
export * from './fetchRequest'; | ||
export * from './axiosRequest'; | ||
export * from './openApiDocumentation'; |
1 change: 1 addition & 0 deletions
1
packages/aws-zod-interface-contracts/src/apiGateway/features/openApiDocumentation/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { getContractDocumentation } from './openApiDocumentation'; |
167 changes: 167 additions & 0 deletions
167
...rface-contracts/src/apiGateway/features/openApiDocumentation/openApiDocumentation.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
import { z } from 'zod'; | ||
|
||
import { ApiGatewayContract } from 'apiGateway/ApiGatewayContract'; | ||
import { HttpStatusCodes } from 'types/http'; | ||
|
||
import { getContractDocumentation } from './openApiDocumentation'; | ||
|
||
describe('apiGateway openApi contract documentation', () => { | ||
const pathParametersSchema = z.object({ | ||
userId: z.string(), | ||
pageNumber: z.string(), | ||
}); | ||
|
||
const queryStringParametersSchema = z.object({ | ||
testId: z.string(), | ||
}); | ||
|
||
const headersSchema = z.object({ | ||
myHeader: z.string(), | ||
}); | ||
|
||
const bodySchema = z.object({ | ||
foo: z.string(), | ||
}); | ||
|
||
const outputSchema = z.object({ | ||
id: z.string(), | ||
name: z.string(), | ||
}); | ||
|
||
const unauthorizedSchema = z.object({ | ||
message: z.string(), | ||
}); | ||
|
||
const outputSchemas = { | ||
[HttpStatusCodes.OK]: outputSchema, | ||
[HttpStatusCodes.UNAUTHORIZED]: unauthorizedSchema, | ||
}; | ||
|
||
describe('httpApi, when all parameters are set', () => { | ||
const httpApiContract = new ApiGatewayContract({ | ||
id: 'testContract', | ||
path: '/users/{userId}', | ||
method: 'GET', | ||
integrationType: 'httpApi', | ||
pathParametersSchema, | ||
queryStringParametersSchema, | ||
headersSchema, | ||
bodySchema, | ||
outputSchemas, | ||
}); | ||
|
||
it('should generate open api documentation', () => { | ||
expect(getContractDocumentation(httpApiContract)).toEqual({ | ||
path: '/users/{userId}', | ||
method: 'get', | ||
documentation: { | ||
parameters: [ | ||
{ | ||
in: 'header', | ||
name: 'myHeader', | ||
required: true, | ||
schema: { | ||
type: 'string', | ||
}, | ||
}, | ||
{ | ||
in: 'query', | ||
name: 'testId', | ||
required: true, | ||
schema: { | ||
type: 'string', | ||
}, | ||
}, | ||
{ | ||
in: 'path', | ||
name: 'userId', | ||
required: true, | ||
schema: { | ||
type: 'string', | ||
}, | ||
}, | ||
{ | ||
in: 'path', | ||
name: 'pageNumber', | ||
required: true, | ||
schema: { | ||
type: 'string', | ||
}, | ||
}, | ||
], | ||
requestBody: { | ||
content: { | ||
'application/json': { | ||
schema: { | ||
type: 'object', | ||
properties: { | ||
foo: { | ||
type: 'string', | ||
}, | ||
}, | ||
required: ['foo'], | ||
}, | ||
}, | ||
}, | ||
}, | ||
responses: { | ||
'200': { | ||
description: 'Response: 200', | ||
content: { | ||
'application/json': { | ||
schema: { | ||
type: 'object', | ||
properties: { | ||
id: { | ||
type: 'string', | ||
}, | ||
name: { | ||
type: 'string', | ||
}, | ||
}, | ||
required: ['id', 'name'], | ||
}, | ||
}, | ||
}, | ||
}, | ||
'401': { | ||
description: 'Response: 401', | ||
content: { | ||
'application/json': { | ||
schema: { | ||
type: 'object', | ||
properties: { | ||
message: { | ||
type: 'string', | ||
}, | ||
}, | ||
required: ['message'], | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}); | ||
}); | ||
}); | ||
|
||
describe('restApi, when it is instanciated with a subset of schemas', () => { | ||
const restApiContract = new ApiGatewayContract({ | ||
id: 'testContract', | ||
path: 'coucou', | ||
method: 'POST', | ||
integrationType: 'restApi', | ||
}); | ||
|
||
it('should generate open api documentation', () => { | ||
expect(getContractDocumentation(restApiContract)).toEqual({ | ||
path: 'coucou', | ||
method: 'post', | ||
documentation: { | ||
responses: {}, // no response is configured | ||
}, | ||
}); | ||
}); | ||
}); | ||
}); |
107 changes: 107 additions & 0 deletions
107
...-interface-contracts/src/apiGateway/features/openApiDocumentation/openApiDocumentation.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
import { generateSchema } from '@anatine/zod-openapi'; | ||
import isUndefined from 'lodash/isUndefined'; | ||
import omitBy from 'lodash/omitBy'; | ||
import { OpenAPIV3 } from 'openapi-types'; | ||
|
||
import { GenericApiGatewayContract } from 'apiGateway/ApiGatewayContract'; | ||
import { ContractOpenApiDocumentation } from 'types/contractOpenApiDocumentation'; | ||
|
||
export const getContractDocumentation = < | ||
Contract extends GenericApiGatewayContract, | ||
>( | ||
contract: Contract, | ||
): ContractOpenApiDocumentation => { | ||
const initialDocumentation: OpenAPIV3.OperationObject = { | ||
responses: {}, | ||
}; | ||
|
||
const definedOutputSchema = omitBy(contract.outputSchemas, isUndefined); | ||
console.log(definedOutputSchema); | ||
|
||
// add responses to the object | ||
const contractDocumentation = Object.keys(definedOutputSchema).reduce( | ||
(config: OpenAPIV3.OperationObject, responseCode) => { | ||
const schema = definedOutputSchema[responseCode]; | ||
|
||
if (schema === undefined) { | ||
return config; | ||
} | ||
|
||
const openApiSchema = generateSchema(schema); | ||
|
||
return { | ||
...config, | ||
responses: { | ||
...config.responses, | ||
[responseCode]: { | ||
description: `Response: ${responseCode}`, | ||
content: { | ||
'application/json': { | ||
schema: openApiSchema as OpenAPIV3.SchemaObject, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}; | ||
}, | ||
initialDocumentation, | ||
); | ||
|
||
if (contract.pathParametersSchema !== undefined) { | ||
contractDocumentation.parameters = [ | ||
...Object.entries(contract.pathParametersSchema.shape).map( | ||
([variableName, variableDefinition]) => ({ | ||
name: variableName, | ||
in: 'path', | ||
schema: generateSchema(variableDefinition) as OpenAPIV3.SchemaObject, | ||
required: !variableDefinition.isOptional(), | ||
}), | ||
), | ||
...(contractDocumentation.parameters ?? []), | ||
]; | ||
} | ||
|
||
if (contract.queryStringParametersSchema !== undefined) { | ||
contractDocumentation.parameters = [ | ||
...Object.entries(contract.queryStringParametersSchema.shape).map( | ||
([variableName, variableDefinition]) => ({ | ||
name: variableName, | ||
in: 'query', | ||
schema: generateSchema(variableDefinition) as OpenAPIV3.SchemaObject, | ||
required: !variableDefinition.isOptional(), | ||
}), | ||
), | ||
...(contractDocumentation.parameters ?? []), | ||
]; | ||
} | ||
|
||
if (contract.headersSchema !== undefined) { | ||
contractDocumentation.parameters = [ | ||
...Object.entries(contract.headersSchema.shape).map( | ||
([variableName, variableDefinition]) => ({ | ||
name: variableName, | ||
in: 'header', | ||
schema: generateSchema(variableDefinition) as OpenAPIV3.SchemaObject, | ||
required: !variableDefinition.isOptional(), | ||
}), | ||
), | ||
...(contractDocumentation.parameters ?? []), | ||
]; | ||
} | ||
|
||
if (contract.bodySchema !== undefined) { | ||
contractDocumentation.requestBody = { | ||
content: { | ||
'application/json': { | ||
schema: generateSchema(contract.bodySchema) as OpenAPIV3.SchemaObject, | ||
}, | ||
}, | ||
}; | ||
} | ||
|
||
return { | ||
path: contract.path, | ||
method: contract.method.toLowerCase(), | ||
documentation: contractDocumentation, | ||
}; | ||
}; |
7 changes: 7 additions & 0 deletions
7
packages/aws-zod-interface-contracts/src/types/contractOpenApiDocumentation.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { OpenAPIV3 } from 'openapi-types'; | ||
|
||
export interface ContractOpenApiDocumentation { | ||
path: string; | ||
method: string; | ||
documentation: OpenAPIV3.OperationObject; | ||
} |