From 6acdba5f7b21c207ac974ac39889bc2666d30882 Mon Sep 17 00:00:00 2001 From: Alex Varchuk Date: Wed, 26 May 2021 18:40:35 +0300 Subject: [PATCH] feat: add test and improve code --- demo/openapi.yaml | 53 +++++++- e2e/integration/menu.e2e.ts | 2 +- src/components/Fields/FieldDetails.tsx | 2 +- src/services/MenuBuilder.ts | 10 +- .../__tests__/fixtures/3.1/pathItems.json | 66 ++++++++++ src/services/__tests__/models/ApiInfo.test.ts | 16 +++ .../__tests__/models/MenuBuilder.test.ts | 25 ++++ src/services/models/Webhook.ts | 10 +- .../loadAndBundleSpec.test.ts.snap | 115 ++++++++++++++---- 9 files changed, 256 insertions(+), 43 deletions(-) create mode 100644 src/services/__tests__/fixtures/3.1/pathItems.json create mode 100644 src/services/__tests__/models/MenuBuilder.test.ts diff --git a/demo/openapi.yaml b/demo/openapi.yaml index 74c16b70f2..e486189008 100644 --- a/demo/openapi.yaml +++ b/demo/openapi.yaml @@ -1,4 +1,4 @@ -openapi: 3.0.0 +openapi: 3.1.0 servers: - url: //petstore.swagger.io/v2 description: Default server @@ -42,6 +42,7 @@ info: version: 1.0.0 title: Swagger Petstore + summary: My lovely API termsOfService: 'http://swagger.io/terms/' contact: name: API Support @@ -53,6 +54,7 @@ info: license: name: Apache 2.0 url: 'http://www.apache.org/licenses/LICENSE-2.0.html' + identifier: Apache 2.0 externalDocs: description: Find out how to create Github repo for your OpenAPI spec. url: 'https://github.com/Rebilly/generator-openapi-repo' @@ -893,6 +895,38 @@ paths: default: description: successful operation components: + pathItems: + catsWebhook: + put: + summary: Get a cat details after update + description: Get a cat details after update + operationId: updatedCat + tags: + - pet + requestBody: + description: Information about cat in the system + content: + multipart/form-data: + schema: + $ref: "#/components/schemas/Cat" + responses: + '200': + description: update Cat details + post: + summary: Create new cat + description: Info about new cat + operationId: createdCat + tags: + - pet + requestBody: + description: Information about cat in the system + content: + multipart/form-data: + schema: + $ref: "#/components/schemas/Cat" + responses: + '200': + description: create Cat details schemas: ApiResponse: type: object @@ -1040,7 +1074,8 @@ components: example: Guru photoUrls: description: The list of URL to a cute photos featuring pet - type: array + type: [string, integer, 'null', array] + minItems: 1 maxItems: 20 xml: name: photoUrl @@ -1054,7 +1089,8 @@ components: tags: description: Tags attached to the pet type: array - minItems: 1 + exclusiveMaximum: 100 + exclusiveMinimum: 0 xml: name: tag wrapped: true @@ -1067,6 +1103,7 @@ components: - available - pending - sold + default: pending petType: description: Type of a pet type: string @@ -1187,13 +1224,13 @@ components: shipDate: '2018-10-19T16:46:45Z' status: placed complete: false -x-webhooks: +webhooks: newPet: post: summary: New pet description: Information about a new pet in the systems operationId: newPet - tags: + tags: - pet requestBody: content: @@ -1202,4 +1239,8 @@ x-webhooks: $ref: "#/components/schemas/Pet" responses: "200": - description: Return a 200 status to indicate that the data was received successfully \ No newline at end of file + description: Return a 200 status to indicate that the data was received successfully + myWebhook: + $ref: '#/components/pathItems/catsWebhook' + description: Overriding description + summary: Overriding summary \ No newline at end of file diff --git a/e2e/integration/menu.e2e.ts b/e2e/integration/menu.e2e.ts index e1b053d125..41ecd79cea 100644 --- a/e2e/integration/menu.e2e.ts +++ b/e2e/integration/menu.e2e.ts @@ -6,7 +6,7 @@ describe('Menu', () => { it('should have valid items count', () => { cy.get('.menu-content') .find('li') - .should('have.length', 34); + .should('have.length', 36); }); it('should sync active menu items while scroll', () => { diff --git a/src/components/Fields/FieldDetails.tsx b/src/components/Fields/FieldDetails.tsx index be102622ca..9ac6cea235 100644 --- a/src/components/Fields/FieldDetails.tsx +++ b/src/components/Fields/FieldDetails.tsx @@ -110,7 +110,7 @@ export class FieldDetails extends React.PureComponent )} {(renderDiscriminatorSwitch && renderDiscriminatorSwitch(this.props)) || null} - {field.const && () || null} + {field.const && () || null} ); } diff --git a/src/services/MenuBuilder.ts b/src/services/MenuBuilder.ts index 7a750d65d8..7322dab00d 100644 --- a/src/services/MenuBuilder.ts +++ b/src/services/MenuBuilder.ts @@ -6,7 +6,6 @@ import { Referenced, OpenAPIServer, OpenAPIPaths, - OpenAPIPath, } from '../types'; import { isOperationName, @@ -235,12 +234,11 @@ export class MenuBuilder { for (const pathName of Object.keys(paths)) { const path = paths[pathName]; const operations = Object.keys(path).filter(isOperationName); - for (let operationName of operations) { - let operationInfo = path[operationName]; + for (const operationName of operations) { + const operationInfo = path[operationName]; if (path.$ref) { - const resolvedPath = parser.deref(path || {}) - operationName = Object.keys(resolvedPath)[0] - operationInfo = resolvedPath[operationName] + const resolvedPaths = parser.deref(path as OpenAPIPaths); + getTags(parser, { [operationName]: resolvedPaths }, isWebhook); } let operationTags = operationInfo?.tags; diff --git a/src/services/__tests__/fixtures/3.1/pathItems.json b/src/services/__tests__/fixtures/3.1/pathItems.json new file mode 100644 index 0000000000..87888a1b91 --- /dev/null +++ b/src/services/__tests__/fixtures/3.1/pathItems.json @@ -0,0 +1,66 @@ +{ + "openapi": "3.1.0", + "info": { + "version": "1.0.0", + "title": "Swagger Petstore" + }, + "webhooks": { + "myWebhook": { + "$ref": "#/components/pathItems/catsWebhook", + "description": "Overriding description", + "summary": "Overriding summary" + } + }, + "components": { + "pathItems": { + "catsWebhook": { + "put": { + "summary": "Get a cat details after update", + "description": "Get a cat details after update", + "operationId": "updatedCat", + "tags": [ + "pet" + ], + "requestBody": { + "description": "Information about cat in the system", + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "responses": { + "200": { + "description": "update Cat details" + } + } + }, + "post": { + "summary": "Create new cat", + "description": "Info about new cat", + "operationId": "createdCat", + "tags": [ + "pet" + ], + "requestBody": { + "description": "Information about cat in the system", + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "responses": { + "200": { + "description": "create Cat details" + } + } + } + } + } + } +} \ No newline at end of file diff --git a/src/services/__tests__/models/ApiInfo.test.ts b/src/services/__tests__/models/ApiInfo.test.ts index 457937ecfd..5db4252751 100644 --- a/src/services/__tests__/models/ApiInfo.test.ts +++ b/src/services/__tests__/models/ApiInfo.test.ts @@ -46,5 +46,21 @@ describe('Models', () => { const info = new ApiInfoModel(parser); expect(info.summary).toEqual('Test summary\nsome text\n## Heading\n test'); }); + + test('should correctly populate license identifier', () => { + parser.spec = { + openapi: '3.1.0', + info: { + license: { + name: 'MIT', + identifier: 'MIT', + url: 'https://opensource.org/licenses/MIT' + } + }, + } as any; + + const { license = { identifier: null } } = new ApiInfoModel(parser); + expect(license.identifier).toEqual('MIT'); + }); }); }); diff --git a/src/services/__tests__/models/MenuBuilder.test.ts b/src/services/__tests__/models/MenuBuilder.test.ts new file mode 100644 index 0000000000..a6ab4776c7 --- /dev/null +++ b/src/services/__tests__/models/MenuBuilder.test.ts @@ -0,0 +1,25 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +import { MenuBuilder } from '../../MenuBuilder'; +import { OpenAPIParser } from '../../OpenAPIParser'; + +import { RedocNormalizedOptions } from '../../RedocNormalizedOptions'; + +const opts = new RedocNormalizedOptions({}); + +describe('Models', () => { + describe('MenuBuilder', () => { + let parser; + + test('discriminator with one field', () => { + const spec = require('../fixtures/3.1/pathItems.json'); + parser = new OpenAPIParser(spec, undefined, opts); + const contentItems = MenuBuilder.buildStructure(parser, opts); + expect(contentItems).toHaveLength(2); + expect(contentItems[0].items).toHaveLength(2); + expect(contentItems[0].id).toEqual('tag/pet'); + expect(contentItems[0].name).toEqual('pet'); + expect(contentItems[0].type).toEqual('tag'); + + }); + }); +}); diff --git a/src/services/models/Webhook.ts b/src/services/models/Webhook.ts index a1be7d3911..5512293518 100644 --- a/src/services/models/Webhook.ts +++ b/src/services/models/Webhook.ts @@ -14,16 +14,18 @@ export class WebhookModel { ) { const webhooks = parser.deref(infoOrRef || {}); parser.exitRef(infoOrRef); + this.initWebhooks(parser, webhooks, options); + } + initWebhooks(parser: OpenAPIParser, webhooks: OpenAPIPath, options: RedocNormalizedOptions) { for (const webhookName of Object.keys(webhooks)) { const webhook = webhooks[webhookName]; const operations = Object.keys(webhook).filter(isOperationName); - for (let operationName of operations) { - let operationInfo = webhook[operationName]; + for (const operationName of operations) { + const operationInfo = webhook[operationName]; if (webhook.$ref) { const resolvedWebhook = parser.deref(webhook || {}); - operationName = Object.keys(resolvedWebhook)[0]; - operationInfo = resolvedWebhook[operationName]; + this.initWebhooks(parser, { [operationName]: resolvedWebhook }, options); } if (!operationInfo) continue; diff --git a/src/utils/__tests__/__snapshots__/loadAndBundleSpec.test.ts.snap b/src/utils/__tests__/__snapshots__/loadAndBundleSpec.test.ts.snap index e7c967e6e8..ebf6792ded 100644 --- a/src/utils/__tests__/__snapshots__/loadAndBundleSpec.test.ts.snap +++ b/src/utils/__tests__/__snapshots__/loadAndBundleSpec.test.ts.snap @@ -13,6 +13,56 @@ Object { }, }, }, + "pathItems": Object { + "catsWebhook": Object { + "post": Object { + "description": "Info about new cat", + "operationId": "createdCat", + "requestBody": Object { + "content": Object { + "multipart/form-data": Object { + "schema": Object { + "$ref": "#/components/schemas/Cat", + }, + }, + }, + "description": "Information about cat in the system", + }, + "responses": Object { + "200": Object { + "description": "create Cat details", + }, + }, + "summary": "Create new cat", + "tags": Array [ + "pet", + ], + }, + "put": Object { + "description": "Get a cat details after update", + "operationId": "updatedCat", + "requestBody": Object { + "content": Object { + "multipart/form-data": Object { + "schema": Object { + "$ref": "#/components/schemas/Cat", + }, + }, + }, + "description": "Information about cat in the system", + }, + "responses": Object { + "200": Object { + "description": "update Cat details", + }, + }, + "summary": "Get a cat details after update", + "tags": Array [ + "pet", + ], + }, + }, + }, "requestBodies": Object { "Pet": Object { "content": Object { @@ -292,13 +342,20 @@ Object { "type": "string", }, "maxItems": 20, - "type": "array", + "minItems": 1, + "type": Array [ + "string", + "integer", + "null", + "array", + ], "xml": Object { "name": "photoUrl", "wrapped": true, }, }, "status": Object { + "default": "pending", "description": "Pet status in the store", "enum": Array [ "available", @@ -309,10 +366,11 @@ Object { }, "tags": Object { "description": "Tags attached to the pet", + "exclusiveMaximum": 100, + "exclusiveMinimum": 0, "items": Object { "$ref": "#/components/schemas/Tag", }, - "minItems": 1, "type": "array", "xml": Object { "name": "tag", @@ -485,9 +543,11 @@ and standard method from web, mobile and desktop applications. ", "license": Object { + "identifier": "Apache 2.0", "name": "Apache 2.0", "url": "http://www.apache.org/licenses/LICENSE-2.0.html", }, + "summary": "My lovely API", "termsOfService": "http://swagger.io/terms/", "title": "Swagger Petstore", "version": "1.0.0", @@ -496,7 +556,7 @@ and standard method from web, mobile and desktop applications. "url": "https://redocly.github.io/redoc/petstore-logo.png", }, }, - "openapi": "3.0.0", + "openapi": "3.1.0", "paths": Object { "/pet": Object { "parameters": Array [ @@ -1745,29 +1805,12 @@ culpa qui officia deserunt mollit anim id est laborum. "x-displayName": "The Order Model", }, ], - "x-tagGroups": Array [ - Object { - "name": "General", - "tags": Array [ - "pet", - "store", - ], - }, - Object { - "name": "User Management", - "tags": Array [ - "user", - ], + "webhooks": Object { + "myWebhook": Object { + "$ref": "#/components/pathItems/catsWebhook", + "description": "Overriding description", + "summary": "Overriding summary", }, - Object { - "name": "Models", - "tags": Array [ - "pet_model", - "store_model", - ], - }, - ], - "x-webhooks": Object { "newPet": Object { "post": Object { "description": "Information about a new pet in the systems", @@ -1793,6 +1836,28 @@ culpa qui officia deserunt mollit anim id est laborum. }, }, }, + "x-tagGroups": Array [ + Object { + "name": "General", + "tags": Array [ + "pet", + "store", + ], + }, + Object { + "name": "User Management", + "tags": Array [ + "user", + ], + }, + Object { + "name": "Models", + "tags": Array [ + "pet_model", + "store_model", + ], + }, + ], } `;