Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 77 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Originally based off of: https://github.com/temando/serverless-openapi-documenta

## Install

This plugin works for Serverless 2.x and up and only supports node.js 14 and up.
This plugin works for Serverless (2.x, 3.x and 4.x) and only supports node.js 14 and up.

To add this plugin to your package.json:

Expand Down Expand Up @@ -79,26 +79,30 @@ Options:

| OpenAPI field | Serverless field |
| --------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| info.title | custom.documentation.title OR service |
| info.description | custom.documentation.description OR blank string |
| info.version | custom.documentation.version OR random v4 uuid if not provided |
| info.termsOfService | custom.documentation.termsOfService |
| info.title | `custom.documentation.title` OR service |
| info.description | `custom.documentation.description` OR blank string |
| info.version | `custom.documentation.version` OR random v4 uuid if not provided |
| info.termsOfService | `custom.documentation.termsOfService` |
| info.contact | custom.documentation.contact |
| info.contact.name | custom.documentation.contact.name OR blank string |
| info.contact.url | custom.documentation.contact.url if provided |
| info.contact.name | `custom.documentation.contact.name` OR blank string |
| info.contact.url | `custom.documentation.contact.url` if provided |
| info.contact.x- | `custom.documentation.contact.x-` extended specifications provided |
| info.license | custom.documentation.license |
| info.license.name | custom.documentation.license.name OR blank string |
| info.license.url | custom.documentation.license.url if provided |
| externalDocs.description | custom.documentation.externalDocumentation.description |
| externalDocs.url | custom.documentation.externalDocumentation.url |
| security | custom.documentation.security |
| info.license.name | `custom.documentation.license.name` OR blank string |
| info.license.url | `custom.documentation.license.url` if provided |
| info.license.x- | `custom.documentation.license.x-` if extended specifications provided provided |
| externalDocs.description | `custom.documentation.externalDocumentation.description ` |
| externalDocs.url | `custom.documentation.externalDocumentation.url` |
| x-tagGroups | `custom.documentation.x-tagGroups` if provided |
| security | `custom.documentation.security` |
| servers[].description | custom.documentation.servers.description |
| servers[].url | custom.documentation.servers.url |
| servers[].variables | custom.documentation.servers.variables |
| tags[].name | custom.documentation.tags.name |
| tags[].description | custom.documentation.tags.description |
| tags[].externalDocs.url | custom.documentation.tags.externalDocumentation.url |
| tags[].externalDocs.description | custom.documentation.tags.externalDocumentation.description |
| tags[].name | `custom.documentation.tags.name` |
| tags[].description | `custom.documentation.tags.description` |
| tags[].externalDocs.url | `custom.documentation.tags.externalDocumentation.url` |
| tags[].externalDocs.description | `custom.documentation.tags.externalDocumentation.description` |
| tags[].externalDocs.x- | `custom.documentation.tags.externalDocumentation.x-` if extended specifications provided |
| path[path] | functions.functions.events.[http OR httpApi].path |
| path[path].servers[].description | functions.functions.servers.description |
| path[path].servers[].url | functions.functions.servers.url |
Expand Down Expand Up @@ -189,7 +193,9 @@ custom:
email: John@example.com
```

These fields are optional, though `url` and `email` need to be in the format of an email address (ed: what that might be, i'm not 100% sure... go read the email RFC(s)) and a url.
These fields are optional, though `url` needs to in the form of a URL and `email` needs to be in the format of an email address (ed: what that might be, I'm not 100% sure... go read the email RFC(s)).

This can be extended using the `^x-` specification extension.

#### License

Expand All @@ -205,6 +211,8 @@ custom:

Name is required but `url` is optional and must be in the format of a url.

This can be extended using the `^x-` specification extension.

#### Extended Fields

You can also add extended fields to the documentation object:
Expand All @@ -225,6 +233,58 @@ custom:

`other-field` here will not make it to the generated OpenAPI schema.

Currently extended specification fields defined under the `documentation` tag will sit under the OpenAPI `info` object e.g.

```yml
custom:
documentation:
title: myService
x-other-field: This is an extended field
```

converts to:

```json
{
"info": {
"title": "myService",
"x-other-field": "This is an extended field"
}
}
```

An exception to this is Redocly `x-tagGroups`. If defined, they will sit at the root level of the OpenAPI specification, e.g.

```yml
custom:
documentation:
title: myService
x-other-field: This is an extended field
x-tagGroups:
- name: Customers
tags:
- Customers
```

converts to:

```json
{
"info": {
"title": "myService",
"x-other-field": "This is an extended field"
},
"x-tagGroups": [
{
"name": "Customers",
"tags": ["Customers"]
}
]
}
```

#### Moving documentation to a separate file

These configurations can be quite verbose; you can separate it out into it's own file, such as `serverless.doc.yml` as below:

```yml
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "serverless-openapi-documenter",
"version": "0.0.103",
"version": "0.0.104",
"description": "Generate OpenAPI v3 documentation and Postman Collections from your Serverless Config",
"main": "index.js",
"keywords": [
Expand All @@ -9,12 +9,16 @@
"Serverless 2",
"serverless3",
"Serverless 3",
"Serverless4",
"Serverless 4",
"serverless framework",
"serverless framework plugin",
"serverless plugin",
"swagger",
"openAPI",
"openAPIv3",
"openAPI3",
"Postman",
"PostmanCollections",
"Postman-Collections",
"Postman Collections",
Expand Down
71 changes: 64 additions & 7 deletions src/definitionGenerator.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,13 @@ class DefinitionGenerator {
if (documentation.contact.url) contactObj.url = documentation.contact.url;

contactObj.email = documentation.contact.email || "";

const extendedSpec = this.extendSpecification(documentation.contact);

if (Object.keys(extendedSpec).length) {
Object.assign(contactObj, extendedSpec);
}

Object.assign(info, { contact: contactObj });
}

Expand All @@ -164,14 +171,22 @@ class DefinitionGenerator {
if (documentation.license.url)
licenseObj.url = documentation.license.url || "";

const extendedSpec = this.extendSpecification(documentation.license);

if (Object.keys(extendedSpec).length) {
Object.assign(licenseObj, extendedSpec);
}

Object.assign(info, { license: licenseObj });
}

// for (const key of Object.keys(documentation)) {
// if (/^[x\-]/i.test(key)) {
// Object.assign(info, { [key]: documentation[key] });
// }
// }
if (documentation["x-tagGroups"]) {
Object.assign(this.openAPI, {
"x-tagGroups": documentation["x-tagGroups"],
});

delete documentation["x-tagGroups"];
}

const extendedSpec = this.extendSpecification(documentation);

Expand Down Expand Up @@ -237,6 +252,29 @@ class DefinitionGenerator {
const serverDoc = servers;
const newServers = [];

const variableManipulation = (variables) => {
for (const key of Object.keys(variables)) {
if (variables[key].enum) {
const strEnum = variables[key].enum.map((enumVar) =>
enumVar.toString()
);

variables[key].enum = strEnum;
}

if (variables[key].default)
variables[key].default = variables[key].default.toString();

const extendedSpec = this.extendSpecification(variables[key]);

if (Object.keys(extendedSpec).length) {
Object.assign(variables[key], extendedSpec);
}
}

return variables;
};

if (Array.isArray(serverDoc)) {
for (const server of serverDoc) {
const obj = {
Expand All @@ -248,7 +286,13 @@ class DefinitionGenerator {
}

if (server.variables) {
obj.variables = server.variables;
obj.variables = variableManipulation(server.variables);
}

const extendedSpec = this.extendSpecification(server);

if (Object.keys(extendedSpec).length) {
Object.assign(obj, extendedSpec);
}

newServers.push(obj);
Expand All @@ -263,7 +307,13 @@ class DefinitionGenerator {
}

if (servers.variables) {
obj.variables = servers.variables;
obj.variables = variableManipulation(servers.variables);
}

const extendedSpec = this.extendSpecification(servers);

if (Object.keys(extendedSpec).length) {
Object.assign(obj, extendedSpec);
}

newServers.push(obj);
Expand Down Expand Up @@ -297,6 +347,13 @@ class DefinitionGenerator {
tag.externalDocumentation
);
}

const extendedSpec = this.extendSpecification(tag);

if (Object.keys(extendedSpec).length) {
Object.assign(obj, extendedSpec);
}

tags.push(obj);
}
Object.assign(this.openAPI, { tags: tags });
Expand Down