Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 872d31d

Browse files
authoredMay 15, 2024··
Add support for webhooks (#47)
1 parent 1b5fd94 commit 872d31d

14 files changed

+276
-33
lines changed
 

‎README.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Works well with [Stoplight.io](https://stoplight.io/)
2323
- [`cookieParams`](#cookieparams)
2424
- [`requestModels`](#requestmodels)
2525
- [`methodResponses`](#methodresponses-and-responsemodels)
26+
- [`webhooks`](#webhooks)
2627
- [Install](#install)
2728

2829
---
@@ -114,6 +115,7 @@ The `documentation` section of the event configuration can contain the following
114115
* `pathParams`: a list of path parameters (see [pathParams](#pathparams) below) - **these _can_ be autogenerated for you from TypeScript**
115116
* `cookieParams`: a list of cookie parameters (see [cookieParams](#cookieparams) below)
116117
* `methodResponses`: an array of response models and applicable status codes (see [methodResponses](#methodresponses-and-responsemodels)) - **these _will_ be autogenerated for you from TypeScript**
118+
* `webhooks`: an object with all the webhooks with descriptions (see [webhooks](#webhooks)) - **these _will_ be autogenerated for you from TypeScript**
117119

118120
```yml
119121
functions:
@@ -435,6 +437,68 @@ functions:
435437

436438
Endpoints that are not attached to a custom tag, are still attached to the title ( which is the default tag ).
437439

440+
#### `webhooks`
441+
OpenAPI have an option to add your application `webhooks`, while this feature isn't supported by `serverless`.
442+
443+
For those the plugin will look for the webhooks under `custom.documentation.webhooks`.
444+
445+
For example
446+
447+
```yaml
448+
custom:
449+
documentation:
450+
apiNamespace: MyApi
451+
webhooks:
452+
WebhookName:
453+
post:
454+
requestBody:
455+
description: |
456+
This is a request body description
457+
responses:
458+
200:
459+
description: |
460+
This is a expected response description
461+
```
462+
463+
this will generate the next OpenAPI file
464+
465+
```yaml
466+
components:
467+
schemas:
468+
MyApi.Webhooks.WebhookName:
469+
type: object
470+
webhooks:
471+
WebhookName:
472+
post:
473+
requestBody:
474+
description: |
475+
This is a request body description
476+
content:
477+
application/json:
478+
schema:
479+
$ref: '#/components/schemas/MyApi.Webhooks.WebhookName'
480+
responses:
481+
'200':
482+
description: |
483+
This is a expected response description
484+
```
485+
486+
With the next `api.d.ts`:
487+
488+
```typescript
489+
export namespace MyApi {
490+
export namespace Webhooks {
491+
export type WebhookName = {
492+
id: string,
493+
name: string,
494+
age: number
495+
// ...
496+
}
497+
}
498+
}
499+
```
500+
501+
438502
## Install
439503

440504
This plugin is **an extension**.

‎package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "serverless-openapi-typescript",
3-
"version": "2.0.0",
3+
"version": "2.1.0",
44
"description": "An extension of @conqa/serverless-openapi-documentation that also generates your OpenAPI models from TypeScript",
55
"main": "dist/index.js",
66
"scripts": {
@@ -14,6 +14,7 @@
1414
"ts-json-schema-generator": "^1.1.2"
1515
},
1616
"devDependencies": {
17+
"@types/node": "20.12.11",
1718
"@conqa/serverless-openapi-documentation": "^1.1.0",
1819
"@types/jest": "^27.0.1",
1920
"@types/serverless": "^1.78.35",

‎pnpm-lock.yaml

Lines changed: 55 additions & 27 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎src/serverless-openapi-typescript.ts

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export default class ServerlessOpenapiTypeScript {
2323
private typescriptApiModelPath: string;
2424
private tsconfigPath: string;
2525
private schemaGenerator: SchemaGenerator;
26+
private webhookEntries: Record<string, any> = {};
2627

2728
constructor(private serverless: Serverless, private options: Options) {
2829
this.assertPluginOrder();
@@ -64,6 +65,10 @@ export default class ServerlessOpenapiTypeScript {
6465
return this.serverless.service.functions || {};
6566
}
6667

68+
get webhooks() {
69+
return this.serverless.service.custom.documentation.webhooks || {};
70+
}
71+
6772
log(msg) {
6873
this.serverless.cli.log(`[serverless-openapi-typescript] ${msg}`);
6974
}
@@ -77,7 +82,7 @@ export default class ServerlessOpenapiTypeScript {
7782
if (httpEvent.documentation) {
7883
this.log(`Generating docs for ${functionName}`);
7984

80-
this.setModels(httpEvent, functionName);
85+
this.setHttpMethodModels(httpEvent, functionName);
8186

8287
const paths = get(httpEvent, 'request.parameters.paths', []);
8388
const querystrings = get(httpEvent, 'request.parameters.querystrings', {});
@@ -94,6 +99,16 @@ export default class ServerlessOpenapiTypeScript {
9499
});
95100
});
96101

102+
this.log('Scanning webhooks for documentation attribute');
103+
Object.keys(this.webhooks).forEach(webhookName => {
104+
const webhook = this.webhooks[webhookName];
105+
const methodDefinition = webhook['post'];
106+
if (methodDefinition) {
107+
this.setWebhookModels(methodDefinition, webhookName);
108+
this.log(`Generating docs for webhook ${webhookName}`);
109+
}
110+
});
111+
97112
this.assertAllFunctionsDocumented();
98113
}
99114

@@ -138,7 +153,7 @@ export default class ServerlessOpenapiTypeScript {
138153
});
139154
}
140155

141-
setModels(httpEvent, functionName) {
156+
setHttpMethodModels(httpEvent, functionName) {
142157
const definitionPrefix = `${this.serverless.service.custom.documentation.apiNamespace}.${upperFirst(camelCase(functionName))}`;
143158
const method = httpEvent.method.toLowerCase();
144159
switch (method) {
@@ -179,10 +194,21 @@ export default class ServerlessOpenapiTypeScript {
179194
}
180195
}
181196

197+
setWebhookModels(webhook, webhookName: string) {
198+
const webhookModelName = `${this.serverless.service.custom.documentation.apiNamespace}.Webhooks.${upperFirst(camelCase(webhookName))}`;
199+
this.setModel(webhookModelName);
200+
this.webhookEntries[webhookName] = {
201+
post: webhook
202+
};
203+
// Since the original plugin doesn't read `webhooks` property and handle it we need to help it
204+
set(this.webhookEntries[webhookName], 'post.requestBody.content', { 'application/json': { schema: { '$ref': `#/components/schemas/${webhookModelName}` } } });
205+
}
206+
182207
postProcessOpenApi() {
183208
// @ts-ignore
184209
const outputFile = this.serverless.processedInput.options.output;
185210
const openApi = yaml.load(fs.readFileSync(outputFile));
211+
openApi['webhooks'] = this.webhookEntries;
186212
this.patchOpenApiVersion(openApi);
187213
this.enrichMethodsInfo(openApi);
188214
const encodedOpenAPI = this.encodeOpenApiToStandard(openApi);

‎test/fixtures/expect-openapi-custom-tags.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ paths:
6161
$ref: '#/components/schemas/ProjectApi.GetFunc.Response'
6262
tags:
6363
- BazTitle
64+
webhooks: {}
6465
tags:
6566
- name: Project
6667
description: DummyDescription

‎test/fixtures/expect-openapi-full.yml

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ components:
3737
type: string
3838
generic:
3939
$ref: >-
40-
#/components/schemas/ProjectApi.GenericType_structure-1448918441-633-661-1448918441-620-662-1448918441-599-663-1448918441-547-673-1448918441-515-674-1448918441-283-680-1448918441-250-680-1448918441-104-1213-1448918441-75-1213-1448918441-0-1214_
40+
#/components/schemas/ProjectApi.GenericType_structure-1448918441-758-786-1448918441-745-787-1448918441-724-788-1448918441-672-798-1448918441-640-799-1448918441-408-805-1448918441-376-805-1448918441-104-1338-1448918441-75-1338-1448918441-0-1339_
4141
required:
4242
- id
4343
- uuid
@@ -70,7 +70,18 @@ components:
7070
required:
7171
- data
7272
additionalProperties: false
73-
ProjectApi.GenericType_structure-1448918441-633-661-1448918441-620-662-1448918441-599-663-1448918441-547-673-1448918441-515-674-1448918441-283-680-1448918441-250-680-1448918441-104-1213-1448918441-75-1213-1448918441-0-1214_:
73+
ProjectApi.Webhooks.OnCreateWebhook:
74+
type: object
75+
properties:
76+
id:
77+
type: string
78+
name:
79+
type: string
80+
required:
81+
- id
82+
- name
83+
additionalProperties: false
84+
ProjectApi.GenericType_structure-1448918441-758-786-1448918441-745-787-1448918441-724-788-1448918441-672-798-1448918441-640-799-1448918441-408-805-1448918441-376-805-1448918441-104-1338-1448918441-75-1338-1448918441-0-1339_:
7485
type: array
7586
items:
7687
type: object
@@ -83,6 +94,7 @@ components:
8394
- key
8495
- name
8596
additionalProperties: false
97+
8698
info:
8799
title: Project
88100
description: >
@@ -220,6 +232,21 @@ paths:
220232
$ref: '#/components/schemas/ProjectApi.GetFunc.Response'
221233
tags:
222234
- Project
235+
webhooks:
236+
OnCreateWebhook:
237+
post:
238+
requestBody:
239+
description: |
240+
This is a request body description
241+
content:
242+
application/json:
243+
schema:
244+
$ref: >-
245+
#/components/schemas/ProjectApi.Webhooks.OnCreateWebhook
246+
responses:
247+
'200':
248+
description: |
249+
This is a expected response description
223250
tags:
224251
- name: Project
225252
description: >

‎test/fixtures/expect-openapi-hyphenated-functions.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ paths:
6161
$ref: '#/components/schemas/ProjectApi.GetFunc.Response'
6262
tags:
6363
- BazTitle
64+
webhooks: {}
6465
tags:
6566
- name: Project
6667
description: DummyDescription

‎test/fixtures/expect-openapi-query-param-type.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ paths:
4848
$ref: '#/components/schemas/ProjectApi.Func.Response'
4949
tags:
5050
- Project
51+
webhooks: {}
5152
tags:
5253
- name: Project
5354
description: DummyDescription
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
openapi: 3.1.0
2+
components:
3+
schemas:
4+
ProjectApi.Webhooks.OnCreateWebhook:
5+
type: object
6+
properties:
7+
id:
8+
type: string
9+
name:
10+
type: string
11+
required:
12+
- id
13+
- name
14+
additionalProperties: false
15+
info:
16+
title: Project
17+
description: DummyDescription
18+
paths: { }
19+
webhooks:
20+
OnCreateWebhook:
21+
post:
22+
requestBody:
23+
description: |
24+
This is a request body description
25+
content:
26+
application/json:
27+
schema:
28+
$ref: >-
29+
#/components/schemas/ProjectApi.Webhooks.OnCreateWebhook
30+
responses:
31+
'200':
32+
description: |
33+
This is a expected response description
34+
tags:
35+
- name: Project
36+
description: DummyDescription
37+
- name: FooBarTitle
38+
description: FooBarDescription
39+
- name: BazTitle
40+
description: BazDescription

‎test/serverless-full/api.d.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@ export namespace ProjectApi {
88
export type Number = number
99
export type String = string;
1010
export type GenericType<T> = T[];
11-
11+
export namespace Webhooks {
12+
export type OnCreateWebhook = {
13+
id: string;
14+
name: string;
15+
}
16+
}
1217
export namespace CreateFunc {
1318
export namespace Request {
1419
export type Body = {

‎test/serverless-full/resources/serverless.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,16 @@ custom:
2020
2121
More on https://google.com
2222
apiNamespace: ProjectApi
23+
webhooks:
24+
OnCreateWebhook:
25+
post:
26+
requestBody:
27+
description: |
28+
This is a request body description
29+
responses:
30+
200:
31+
description: |
32+
This is a expected response description
2333
2434
functions:
2535
createFunc:

‎test/serverless-openapi-typescript.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ describe('ServerlessOpenapiTypeScript', () => {
1616
${'Custom Tags'} | ${'custom-tags'}
1717
${'Hyphenated Functions'} | ${'hyphenated-functions'}
1818
${'Full Project'} | ${'full'}
19+
${'Webhooks'} | ${'webhooks'}
1920
`('when using $testCase', ({projectName}) => {
2021

2122
beforeEach(async () => {

‎test/serverless-webhooks/api.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export namespace ProjectApi {
2+
export namespace Webhooks {
3+
export type OnCreateWebhook = {
4+
id: string;
5+
name: string;
6+
}
7+
}
8+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
service: serverless-openapi-typescript-demo
2+
provider:
3+
name: aws
4+
5+
plugins:
6+
- ../node_modules/@conqa/serverless-openapi-documentation
7+
- ../src/index
8+
9+
custom:
10+
documentation:
11+
title: 'Project'
12+
description: DummyDescription
13+
apiNamespace: ProjectApi
14+
tags:
15+
- name: FooBarTitle
16+
description: FooBarDescription
17+
- name: BazTitle
18+
description: BazDescription
19+
webhooks:
20+
OnCreateWebhook:
21+
post:
22+
requestBody:
23+
description: |
24+
This is a request body description
25+
responses:
26+
200:
27+
description: |
28+
This is a expected response description
29+
30+

0 commit comments

Comments
 (0)
Please sign in to comment.