diff --git a/packages/create-whook/src/handlers/getOpenAPI.js b/packages/create-whook/src/handlers/getOpenAPI.js new file mode 100644 index 00000000..dc357c51 --- /dev/null +++ b/packages/create-whook/src/handlers/getOpenAPI.js @@ -0,0 +1,8 @@ +import getOpenAPI, { + definition, +} from 'whook-swagger-ui/dist/handlers/getOpenAPI'; + +// TODO: Use WHOOK_PLUGINS to get handlers from plugins +// instead of proxying here +export { definition }; +export default getOpenAPI; diff --git a/packages/create-whook/src/services/API.js b/packages/create-whook/src/services/API.js index f37cd482..7716d9c3 100644 --- a/packages/create-whook/src/services/API.js +++ b/packages/create-whook/src/services/API.js @@ -1,5 +1,6 @@ import { name, autoService } from 'knifecycle'; +import { definition as getOpenAPIDefinition } from '../handlers/getOpenAPI'; import { definition as getPingDefinition } from '../handlers/getPing'; import { definition as getTimeDefinition } from '../handlers/getTime'; import { definition as putEchoDefinition } from '../handlers/putEcho'; @@ -28,6 +29,9 @@ async function initAPI({ CONFIG, log }) { description: CONFIG.description, }, paths: { + [getOpenAPIDefinition.path]: { + [getOpenAPIDefinition.method]: getOpenAPIDefinition.operation, + }, [getPingDefinition.path]: { [getPingDefinition.method]: getPingDefinition.operation, }, diff --git a/packages/create-whook/src/services/__snapshots__/API.test.js.snap b/packages/create-whook/src/services/__snapshots__/API.test.js.snap index 35504972..0826fb85 100644 --- a/packages/create-whook/src/services/__snapshots__/API.test.js.snap +++ b/packages/create-whook/src/services/__snapshots__/API.test.js.snap @@ -23,7 +23,11 @@ Object { }, }, "summary": "Enable OPTIONS for CORS", + "tags": Array [ + "CORS", + ], "x-whook": Object { + "private": true, "sourceOperationId": "putEcho", "suffix": "CORS", "type": "http", @@ -82,6 +86,51 @@ Object { ], }, }, + "/openAPI": Object { + "get": Object { + "consumes": Array [], + "operationId": "getOpenAPI", + "produces": Array [ + "application/json", + ], + "responses": Object { + "200": Object { + "description": "Provides the private Open API documentation", + "schema": Object { + "type": "object", + }, + }, + }, + "summary": "Get API documentation.", + "tags": Array [ + "system", + ], + "x-whook": Object { + "private": false, + }, + }, + "options": Object { + "consumes": Array [], + "operationId": "optionsWithCORS", + "parameters": Array [], + "produces": Array [], + "responses": Object { + "200": Object { + "description": "CORS sent.", + }, + }, + "summary": "Enable OPTIONS for CORS", + "tags": Array [ + "CORS", + ], + "x-whook": Object { + "private": true, + "sourceOperationId": "getOpenAPI", + "suffix": "CORS", + "type": "http", + }, + }, + }, "/ping": Object { "get": Object { "consumes": Array [], @@ -122,7 +171,11 @@ Object { }, }, "summary": "Enable OPTIONS for CORS", + "tags": Array [ + "CORS", + ], "x-whook": Object { + "private": true, "sourceOperationId": "getPing", "suffix": "CORS", "type": "http", @@ -167,7 +220,11 @@ Object { }, }, "summary": "Enable OPTIONS for CORS", + "tags": Array [ + "CORS", + ], "x-whook": Object { + "private": true, "sourceOperationId": "getTime", "suffix": "CORS", "type": "http", diff --git a/packages/whook-cors/src/__snapshots__/index.test.js.snap b/packages/whook-cors/src/__snapshots__/index.test.js.snap index 3530d7b4..e274871d 100644 --- a/packages/whook-cors/src/__snapshots__/index.test.js.snap +++ b/packages/whook-cors/src/__snapshots__/index.test.js.snap @@ -88,7 +88,11 @@ Object { }, }, "summary": "Enable OPTIONS for CORS", + "tags": Array [ + "CORS", + ], "x-whook": Object { + "private": true, "sourceOperationId": "ping", "suffix": "CORS", "type": "http", diff --git a/packages/whook-cors/src/index.js b/packages/whook-cors/src/index.js index 02b3a8d6..a62af0be 100644 --- a/packages/whook-cors/src/index.js +++ b/packages/whook-cors/src/index.js @@ -54,6 +54,7 @@ export async function augmentAPIWithCORS(API) { ...(operation['x-whook'] || {}), suffix: 'CORS', sourceOperationId: operation.operationId, + private: true, }; if (whookConfig.type !== 'http') { @@ -69,6 +70,7 @@ export async function augmentAPIWithCORS(API) { newAPI.paths[operation.path].options = { operationId: 'optionsWithCORS', summary: 'Enable OPTIONS for CORS', + tags: ['CORS'], 'x-whook': { ...whookConfig, }, diff --git a/packages/whook-swagger-ui/src/handlers/__snapshots__/getOpenAPI.test.js.snap b/packages/whook-swagger-ui/src/handlers/__snapshots__/getOpenAPI.test.js.snap new file mode 100644 index 00000000..8781b1d1 --- /dev/null +++ b/packages/whook-swagger-ui/src/handlers/__snapshots__/getOpenAPI.test.js.snap @@ -0,0 +1,58 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`getOpenAPI should show every endpoints when authenticated 1`] = ` +Object { + "response": Object { + "body": Object { + "info": Object { + "version": "", + }, + "paths": Object { + "/time": Object { + "get": Object { + "tags": Array [ + "public", + ], + "x-whook": Object { + "memx": 2, + "tx": 18, + }, + }, + "put": Object { + "tags": Array [], + "x-whook": Object { + "private": true, + }, + }, + }, + }, + "swagger": "2.0", + }, + "status": 200, + }, +} +`; + +exports[`getOpenAPI should work 1`] = ` +Object { + "response": Object { + "body": Object { + "info": Object { + "version": "", + }, + "paths": Object { + "/time": Object { + "get": Object { + "tags": Array [ + "public", + ], + "x-whook": undefined, + }, + }, + }, + "swagger": "2.0", + }, + "status": 200, + }, +} +`; diff --git a/packages/whook-swagger-ui/src/handlers/getOpenAPI.js b/packages/whook-swagger-ui/src/handlers/getOpenAPI.js new file mode 100644 index 00000000..490ba45b --- /dev/null +++ b/packages/whook-swagger-ui/src/handlers/getOpenAPI.js @@ -0,0 +1,60 @@ +import { autoHandler } from 'knifecycle'; +import { getSwaggerOperations } from 'swagger-http-router/dist/utils'; + +export default autoHandler(getOpenAPI); + +export const definition = { + path: '/openAPI', + method: 'get', + operation: { + operationId: 'getOpenAPI', + summary: 'Get API documentation.', + tags: ['system'], + 'x-whook': { private: false }, + consumes: [], + produces: ['application/json'], + responses: { + '200': { + description: 'Provides the private Open API documentation', + schema: { + type: 'object', + }, + }, + }, + }, +}; + +async function getOpenAPI({ API }, { userId }) { + const authenticatedRequest = !!userId; + if (authenticatedRequest) { + return { + status: 200, + body: API, + }; + } + + const operations = await getSwaggerOperations(API); + const CLEANED_API = { + ...API, + paths: operations.reduce((paths, operation) => { + if (operation['x-whook'] && operation['x-whook'].private) { + return paths; + } + + paths[operation.path] = { + ...paths[operation.path], + [operation.method]: { + ...API.paths[operation.path][operation.method], + 'x-whook': {}.undef, + }, + }; + + return paths; + }, {}), + }; + + return { + status: 200, + body: CLEANED_API, + }; +} diff --git a/packages/whook-swagger-ui/src/handlers/getOpenAPI.test.js b/packages/whook-swagger-ui/src/handlers/getOpenAPI.test.js new file mode 100644 index 00000000..09acadb7 --- /dev/null +++ b/packages/whook-swagger-ui/src/handlers/getOpenAPI.test.js @@ -0,0 +1,61 @@ +import initGetOpenAPI from './getOpenAPI'; + +describe('getOpenAPI', () => { + const API = { + swagger: '2.0', + paths: { + '/time': { + get: { + tags: ['public'], + 'x-whook': { memx: 2, tx: 18 }, + }, + put: { + tags: [], + 'x-whook': { private: true }, + }, + }, + }, + }; + + it('should work', async () => { + const getOpenAPI = await initGetOpenAPI({ + API, + }); + const response = await getOpenAPI({}); + + expect({ + response: { + ...response, + body: { + ...response.body, + info: { + ...response.body.info, + version: '', + }, + }, + }, + }).toMatchSnapshot(); + }); + + it('should show every endpoints when authenticated', async () => { + const getOpenAPI = await initGetOpenAPI({ + API, + }); + const response = await getOpenAPI({ + userId: 1, + }); + + expect({ + response: { + ...response, + body: { + ...response.body, + info: { + ...response.body.info, + version: '', + }, + }, + }, + }).toMatchSnapshot(); + }); +});