Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Patch: Fix documentation preview with nested $ref model #46

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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ To download it in YAML format, simply use `yml` or `yaml` extension in the "outp
By default, the documentation will be generated in OpenAPI 2.0 (Swagger).
To generate it in OpenAPI 3.0, use `oas30` or `openapi30` type in the "exportType" argument: `serverless generateDocumentation --outputFileName=filename.ext --exportType oas30`

**NOTE:** The documentation preview generated locally might be invalid (e.g., when some mandatory fields are missing from the `serverless.yml` documentation) and might differt from the final documentation deployed to and downloaded from AWS.
**NOTE:** The documentation preview generated locally might be invalid (e.g., when some mandatory fields are missing from the `serverless.yml` documentation) and might differ from the final documentation deployed to and downloaded from AWS.
ottopecz marked this conversation as resolved.
Show resolved Hide resolved

### Download the documentation from AWS API Gateway

Expand Down
23 changes: 22 additions & 1 deletion src/generateDocumentation.js
Original file line number Diff line number Diff line change
Expand Up @@ -292,12 +292,33 @@ function generateModels(templateModels) {

templateModels.forEach((templateModel) => {
if (!templateModel.name || !templateModel.schema) return;
models[templateModel.name] = templateModel.schema;
models[templateModel.name] = generateModelSchema(templateModel.schema);
});

return models;
}

function generateModelSchema(templateSchema) {
if (!templateSchema) return templateSchema;
ottopecz marked this conversation as resolved.
Show resolved Hide resolved

let schema = {};
Object.keys(templateSchema).forEach((key) => {
let field = templateSchema[key];
if (field && key === "$ref") {
const match = /{{\s*model\s*:\s*([\-\w]+)\s*}}/.exec(field);
if (match) {
field = `#/components/schemas/${match[1]}`;
}
} else if (field && field.constructor === Object) {
// NOTE: We are only interested in looping through "items" and "properties" fields (which are both objects)!
field = generateModelSchema(field);
}
schema[key] = field;
});

return schema;
}

function generateSchema(field, excludeKeys) {
if (field.schema) return field.schema;

Expand Down
12 changes: 12 additions & 0 deletions src/generateDocumentation.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,12 @@ describe('ServerlessAWSDocumentation', function () {
${[{ name: 'AnyModel', schema: { type: 'any-type' } }]} | ${{ 'AnyModel': { type: 'any-type' } }}
${[{ name: 'AnyModel', contentType: 'application/json', schema: { type: 'any-type' } }]} | ${{ 'AnyModel': { type: 'any-type' } }}
${[{ name: 'AnyModel', schema: { type: 'any-type' } }, { name: 'AnyOtherModel', schema: {} }]} | ${{ 'AnyModel': { type: 'any-type' }, 'AnyOtherModel': {} }}
${[{ name: 'AnyModel', schema: { type: 'array', items: { '$ref': 'http://path/to/AnyOtherModel' } } }]} | ${{ 'AnyModel': { type: 'array', items: { '$ref': 'http://path/to/AnyOtherModel' } } }}
${[{ name: 'AnyModel', schema: { type: 'array', items: { '$ref': '{{model: AnyOtherModel}}' } } }]} | ${{ 'AnyModel': { type: 'array', items: { '$ref': '#/components/schemas/AnyOtherModel' } } }}
${[{ name: 'AnyModel', schema: { type: 'object', required: ['id'], properties: { id: { type: 'string', default: '' } } } }]} | ${{ 'AnyModel': { type: 'object', required: ['id'], properties: { id: { type: 'string', default: '' } } } }}
${[{ name: 'AnyModel', schema: { type: 'object', required: ['id'], properties: { id: { type: 'integer', minimum: 1, exclusiveMinimum: false } } } }]} | ${{ 'AnyModel': { type: 'object', required: ['id'], properties: { id: { type: 'integer', minimum: 1, exclusiveMinimum: false } } } }}
${[{ name: 'AnyModel', schema: { type: 'object', required: ['id'], properties: { id: { '$ref': '{{model: AnyOtherModel}}' } } } }]} | ${{ 'AnyModel': { type: 'object', required: ['id'], properties: { id: { '$ref': '#/components/schemas/AnyOtherModel' } } } }}
${[{ name: 'AnyModel', schema: { type: 'object', properties: { ids: { type: 'array', items: { '$ref': '{{model: AnyOtherModel}}' } } } } }]} | ${{ 'AnyModel': { type: 'object', properties: { ids: { type: 'array', items: { '$ref': '#/components/schemas/AnyOtherModel' } } } } }}
`(
'generates definitions field when exportType: swagger and models: $models',
async ({
Expand Down Expand Up @@ -375,6 +381,12 @@ describe('ServerlessAWSDocumentation', function () {
${[{ name: 'AnyModel', schema: { type: 'any-type' } }]} | ${{ schemas: { 'AnyModel': { type: 'any-type' } }, securitySchemes: {} }}
${[{ name: 'AnyModel', contentType: 'application/json', schema: { type: 'any-type' } }]} | ${{ schemas: { 'AnyModel': { type: 'any-type' } }, securitySchemes: {} }}
${[{ name: 'AnyModel', schema: { type: 'any-type' } }, { name: 'AnyOtherModel', schema: {} }]} | ${{ schemas: { 'AnyModel': { type: 'any-type' }, 'AnyOtherModel': {} }, securitySchemes: {} }}
${[{ name: 'AnyModel', schema: { type: 'array', items: { '$ref': 'http://path/to/AnyOtherModel' } } }]} | ${{ schemas: { 'AnyModel': { type: 'array', items: { '$ref': 'http://path/to/AnyOtherModel' } } }, securitySchemes: {} }}
${[{ name: 'AnyModel', schema: { type: 'array', items: { '$ref': '{{model: AnyOtherModel}}' } } }]} | ${{ schemas: { 'AnyModel': { type: 'array', items: { '$ref': '#/components/schemas/AnyOtherModel' } } }, securitySchemes: {} }}
${[{ name: 'AnyModel', schema: { type: 'object', required: ['id'], properties: { id: { type: 'string', default: '', nullable: false } } } }]} | ${{ schemas: { 'AnyModel': { type: 'object', required: ['id'], properties: { id: { type: 'string', default: '', nullable: false } } } }, securitySchemes: {} }}
${[{ name: 'AnyModel', schema: { type: 'object', required: ['id'], properties: { id: { type: 'integer', minimum: 1, exclusiveMinimum: false } } } }]} | ${{ schemas: { 'AnyModel': { type: 'object', required: ['id'], properties: { id: { type: 'integer', minimum: 1, exclusiveMinimum: false } } } }, securitySchemes: {} }}
${[{ name: 'AnyModel', schema: { type: 'object', required: ['id'], properties: { id: { '$ref': '{{model: AnyOtherModel}}' } } } }]} | ${{ schemas: { 'AnyModel': { type: 'object', required: ['id'], properties: { id: { '$ref': '#/components/schemas/AnyOtherModel' } } } }, securitySchemes: {} }}
${[{ name: 'AnyModel', schema: { type: 'object', properties: { ids: { type: 'array', items: { '$ref': '{{model: AnyOtherModel}}' } } } } }]} | ${{ schemas: { 'AnyModel': { type: 'object', properties: { ids: { type: 'array', items: { '$ref': '#/components/schemas/AnyOtherModel' } }} } }, securitySchemes: {} }}
`(
'generates components.schemas field when exportType: oas30 and models: $models',
async ({
Expand Down
Loading