diff --git a/package-lock.json b/package-lock.json index bbbcb7a7..78994ed0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4684,6 +4684,11 @@ "@types/node": "*" } }, + "node_modules/@types/swagger-ui": { + "version": "3.52.4", + "resolved": "https://registry.npmjs.org/@types/swagger-ui/-/swagger-ui-3.52.4.tgz", + "integrity": "sha512-7NV7q8BfupqdQxr26OkM0g0YEPB9uXnKGzXadgcearvI9MoCHt3F72lPTX3fZZIlrr21DC0IK26wcDMZ37oFDA==" + }, "node_modules/@types/through": { "version": "0.0.30", "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz", @@ -18110,9 +18115,9 @@ } }, "node_modules/type-fest": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.7.1.tgz", - "integrity": "sha512-iWr8RUmzAJRfhZugX9O7nZE6pCxDU8CZ3QxsLuTnGcBLJpCaP2ll3s4eMTBoFnU/CeXY/5rfQSuAEsTGJO4y8A==", + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.8.2.tgz", + "integrity": "sha512-mcvrCjixA5166hSrUoJgGb9gBQN4loMYyj9zxuMs/66ibHNEFd5JXMw37YVDx58L4/QID9jIzdTBB4mDwDJ6KQ==", "engines": { "node": ">=16" }, @@ -18805,7 +18810,7 @@ "pkg-dir": "^8.0.0", "portfinder": "^1.0.32", "schema2dts": "^5.3.0", - "type-fest": "^4.7.1", + "type-fest": "^4.8.2", "yargs-parser": "^21.1.1", "yerror": "^8.0.0" }, @@ -18903,7 +18908,7 @@ "openapi-types": "^12.1.3", "qs": "^6.11.2", "strict-qs": "^8.0.1", - "type-fest": "^4.7.1", + "type-fest": "^4.8.2", "uuid": "^9.0.1", "yerror": "^8.0.0", "yhttperror": "^8.0.0" @@ -19267,7 +19272,7 @@ "openapi-types": "^12.1.3", "pkg-dir": "^8.0.0", "strict-qs": "^8.0.1", - "type-fest": "^4.7.1", + "type-fest": "^4.8.2", "yerror": "^8.0.0", "yhttperror": "^8.0.0" }, @@ -19403,7 +19408,7 @@ "common-services": "^15.0.0", "knifecycle": "^17.0.1", "openapi-types": "^12.1.3", - "type-fest": "^4.7.1", + "type-fest": "^4.8.2", "yhttperror": "^8.0.0" }, "devDependencies": { @@ -19457,7 +19462,7 @@ "qs": "^6.11.2", "siso": "^6.0.1", "strict-qs": "^8.0.1", - "type-fest": "^4.7.1", + "type-fest": "^4.8.2", "yerror": "^8.0.0", "yhttperror": "^8.0.0" }, @@ -19538,7 +19543,7 @@ "ms": "^2.1.3", "openapi-types": "^12.1.3", "statuses": "^2.0.1", - "type-fest": "^4.7.1", + "type-fest": "^4.8.2", "yerror": "^8.0.0", "yhttperror": "^8.0.0" }, @@ -19618,7 +19623,7 @@ "cookie": "^0.6.0", "knifecycle": "^17.0.1", "openapi-types": "^12.1.3", - "type-fest": "^4.7.1", + "type-fest": "^4.8.2", "yerror": "^8.0.0", "yhttperror": "^8.0.0" }, @@ -19658,13 +19663,15 @@ "version": "14.0.0", "license": "MIT", "dependencies": { + "@types/swagger-ui": "3.52.4", "@whook/http-router": "^14.0.0", "@whook/whook": "^14.0.0", "axios": "^1.6.1", "ecstatic": "^4.1.4", "knifecycle": "^17.0.1", "openapi-types": "^12.1.3", - "swagger-ui-dist": "^5.9.3" + "swagger-ui-dist": "^5.9.3", + "type-fest": "^4.8.2" }, "devDependencies": { "@swc/cli": "^0.1.62", diff --git a/packages/whook-aws-lambda/package.json b/packages/whook-aws-lambda/package.json index 8bcdde6e..56d49267 100644 --- a/packages/whook-aws-lambda/package.json +++ b/packages/whook-aws-lambda/package.json @@ -61,7 +61,7 @@ "openapi-types": "^12.1.3", "qs": "^6.11.2", "strict-qs": "^8.0.1", - "type-fest": "^4.7.1", + "type-fest": "^4.8.2", "uuid": "^9.0.1", "yerror": "^8.0.0", "yhttperror": "^8.0.0" diff --git a/packages/whook-create/src/services/__snapshots__/createWhook.test.ts.snap b/packages/whook-create/src/services/__snapshots__/createWhook.test.ts.snap index f14658d5..d402a92e 100644 --- a/packages/whook-create/src/services/__snapshots__/createWhook.test.ts.snap +++ b/packages/whook-create/src/services/__snapshots__/createWhook.test.ts.snap @@ -151,7 +151,7 @@ DEV_MODE=1 "openapi-types": "^12.1.3", "pkg-dir": "^8.0.0", "strict-qs": "^8.0.1", - "type-fest": "^4.7.1", + "type-fest": "^4.8.2", "yerror": "^8.0.0", "yhttperror": "^8.0.0" }, @@ -491,7 +491,7 @@ DEV_MODE=1 "openapi-types": "^12.1.3", "pkg-dir": "^8.0.0", "strict-qs": "^8.0.1", - "type-fest": "^4.7.1", + "type-fest": "^4.8.2", "yerror": "^8.0.0", "yhttperror": "^8.0.0" }, @@ -863,7 +863,7 @@ DEV_MODE=1 "openapi-types": "^12.1.3", "pkg-dir": "^8.0.0", "strict-qs": "^8.0.1", - "type-fest": "^4.7.1", + "type-fest": "^4.8.2", "yerror": "^8.0.0", "yhttperror": "^8.0.0" }, @@ -1218,7 +1218,7 @@ DEV_MODE=1 "openapi-types": "^12.1.3", "pkg-dir": "^8.0.0", "strict-qs": "^8.0.1", - "type-fest": "^4.7.1", + "type-fest": "^4.8.2", "yerror": "^8.0.0", "yhttperror": "^8.0.0" }, diff --git a/packages/whook-create/src/services/createWhook.test.ts b/packages/whook-create/src/services/createWhook.test.ts index 871a1e78..e8456fec 100644 --- a/packages/whook-create/src/services/createWhook.test.ts +++ b/packages/whook-create/src/services/createWhook.test.ts @@ -174,7 +174,7 @@ describe('initCreateWhook', () => { "openapi-types": "^12.1.3", "pkg-dir": "^8.0.0", "strict-qs": "^8.0.1", - "type-fest": "^4.7.1", + "type-fest": "^4.8.2", "yerror": "^8.0.0", "yhttperror": "^8.0.0", }, @@ -396,7 +396,7 @@ describe('initCreateWhook', () => { "openapi-types": "^12.1.3", "pkg-dir": "^8.0.0", "strict-qs": "^8.0.1", - "type-fest": "^4.7.1", + "type-fest": "^4.8.2", "yerror": "^8.0.0", "yhttperror": "^8.0.0", }, @@ -603,7 +603,7 @@ describe('initCreateWhook', () => { "openapi-types": "^12.1.3", "pkg-dir": "^8.0.0", "strict-qs": "^8.0.1", - "type-fest": "^4.7.1", + "type-fest": "^4.8.2", "yerror": "^8.0.0", "yhttperror": "^8.0.0", }, diff --git a/packages/whook-example/package.json b/packages/whook-example/package.json index d67eb67e..fbf5cb27 100644 --- a/packages/whook-example/package.json +++ b/packages/whook-example/package.json @@ -95,7 +95,7 @@ "openapi-types": "^12.1.3", "pkg-dir": "^8.0.0", "strict-qs": "^8.0.1", - "type-fest": "^4.7.1", + "type-fest": "^4.8.2", "yerror": "^8.0.0", "yhttperror": "^8.0.0" }, diff --git a/packages/whook-example/src/config/common/config.ts b/packages/whook-example/src/config/common/config.ts index 758195a6..a6a64535 100644 --- a/packages/whook-example/src/config/common/config.ts +++ b/packages/whook-example/src/config/common/config.ts @@ -1,6 +1,7 @@ import { DEFAULT_ERRORS_DESCRIPTORS } from '@whook/http-router'; import { readFileSync } from 'fs'; import { NodeEnv } from 'application-services'; +import { DEFAULT_SWAGGER_UI_CONFIG } from '@whook/swagger-ui'; import type { AppConfig } from 'application-services'; /* Architecture Note #2: Configuration @@ -67,6 +68,10 @@ const CONFIG: Omit = { 'User-Agent', ].join(','), }, + SWAGGER_UI_CONFIG: { + ...DEFAULT_SWAGGER_UI_CONFIG, + defaultModelRendering: 'model', + }, }; export default CONFIG; diff --git a/packages/whook-graphql/package.json b/packages/whook-graphql/package.json index 22d7108d..e793615c 100644 --- a/packages/whook-graphql/package.json +++ b/packages/whook-graphql/package.json @@ -72,7 +72,7 @@ "common-services": "^15.0.0", "knifecycle": "^17.0.1", "openapi-types": "^12.1.3", - "type-fest": "^4.7.1", + "type-fest": "^4.8.2", "yhttperror": "^8.0.0" }, "devDependencies": { diff --git a/packages/whook-http-router/package.json b/packages/whook-http-router/package.json index c56f586c..decef2c5 100644 --- a/packages/whook-http-router/package.json +++ b/packages/whook-http-router/package.json @@ -73,7 +73,7 @@ "qs": "^6.11.2", "siso": "^6.0.1", "strict-qs": "^8.0.1", - "type-fest": "^4.7.1", + "type-fest": "^4.8.2", "yerror": "^8.0.0", "yhttperror": "^8.0.0" }, diff --git a/packages/whook-http-transaction/package.json b/packages/whook-http-transaction/package.json index f63b7564..76c23cb5 100644 --- a/packages/whook-http-transaction/package.json +++ b/packages/whook-http-transaction/package.json @@ -59,7 +59,7 @@ "ms": "^2.1.3", "openapi-types": "^12.1.3", "statuses": "^2.0.1", - "type-fest": "^4.7.1", + "type-fest": "^4.8.2", "yerror": "^8.0.0", "yhttperror": "^8.0.0" }, diff --git a/packages/whook-oauth2/package.json b/packages/whook-oauth2/package.json index 22633f23..1caeba80 100644 --- a/packages/whook-oauth2/package.json +++ b/packages/whook-oauth2/package.json @@ -51,7 +51,7 @@ "cookie": "^0.6.0", "knifecycle": "^17.0.1", "openapi-types": "^12.1.3", - "type-fest": "^4.7.1", + "type-fest": "^4.8.2", "yerror": "^8.0.0", "yhttperror": "^8.0.0" }, diff --git a/packages/whook-swagger-ui/package.json b/packages/whook-swagger-ui/package.json index 581df032..82f5a47d 100644 --- a/packages/whook-swagger-ui/package.json +++ b/packages/whook-swagger-ui/package.json @@ -66,13 +66,15 @@ }, "homepage": "https://github.com/nfroidure/whook", "dependencies": { + "@types/swagger-ui": "3.52.4", "@whook/http-router": "^14.0.0", "@whook/whook": "^14.0.0", "axios": "^1.6.1", "ecstatic": "^4.1.4", "knifecycle": "^17.0.1", "openapi-types": "^12.1.3", - "swagger-ui-dist": "^5.9.3" + "swagger-ui-dist": "^5.9.3", + "type-fest": "^4.8.2" }, "devDependencies": { "@swc/cli": "^0.1.62", diff --git a/packages/whook-swagger-ui/src/index.test.ts b/packages/whook-swagger-ui/src/index.test.ts index ce69b6d3..c6b9b186 100644 --- a/packages/whook-swagger-ui/src/index.test.ts +++ b/packages/whook-swagger-ui/src/index.test.ts @@ -184,6 +184,9 @@ describe('wrapHTTPRouterWithSwaggerUI', () => { [ "DEFAULT_ERROR_CODE", ], + [ + "SWAGGER_UI_CONFIG", + ], [ "PROCESS_NAME", ], @@ -378,6 +381,9 @@ describe('wrapHTTPRouterWithSwaggerUI', () => { [ "DEFAULT_ERROR_CODE", ], + [ + "SWAGGER_UI_CONFIG", + ], [ "PROCESS_NAME", ], @@ -457,6 +463,224 @@ describe('wrapHTTPRouterWithSwaggerUI', () => { `); }); + it('should serve Swagger Initializer', async () => { + $.register(constant('PORT', PORT + 2)); + $.register(wrapHTTPRouterWithSwaggerUI(initHTTPRouter)); + $.register( + constant('CONFIG', { + localURL: `http://${HOST}:${PORT + 2}`, + }), + ); + $.register( + constant('ENV', { + NODE_ENV: 'test', + DEV_MODE: '1', + }), + ); + $.register( + constant('SWAGGER_UI_CONFIG', { + layout: 'StandaloneLayout', + }), + ); + $.register(constant('DEBUG_NODE_ENVS', ['test'])); + + time.mockReturnValue(new Date('2010-03-06T00:00:00Z').getTime()); + + const { $instance } = await prepareServer( + ['$instance', 'httpServer', 'process'], + $, + ); + const { status, headers, data } = await axios({ + method: 'get', + url: `http://${HOST}:${PORT + 2}/docs/swagger-initializer.js`, + headers: { 'user-agent': '__avoid_axios_version__' }, + validateStatus: () => true, + }); + + await $instance.destroy(); + + expect(data).toMatchInlineSnapshot(` +" +window.onload = function() { + // + + // the following lines will be replaced by docker/configurator, when it runs in a docker-container + window.ui = SwaggerUIBundle( + Object.assign( + { + urls: [{"name":"Public API","url":"http://localhost:22224/v1/openAPI"}, {"name":"Private API","url":"http://localhost:22224/v1/openAPI?access_token=oudelali"}], + dom_id: '#swagger-ui', + presets: [ + SwaggerUIBundle.presets.apis, + SwaggerUIStandalonePreset + ], + plugins: [ + SwaggerUIBundle.plugins.DownloadUrl, + SwaggerUIBundle.plugins.Topbar + ], + }, + {"layout":"StandaloneLayout"} + ) + ); + + // +}; +" +`); + expect(logger.output.mock.calls.length).toEqual(0); + expect({ + status, + headers: { + ...headers, + // Erasing the Date header that may be added by Axios :/ + date: undefined, + etag: undefined, + 'last-modified': undefined, + server: undefined, + connection: undefined, + 'keep-alive': undefined, + }, + debugCalls: logger.debug.mock.calls.sort(sortLogs), + logErrorCalls: logger.error.mock.calls, + autoloaderCalls: $autoload.mock.calls, + }).toMatchInlineSnapshot(` +{ + "autoloaderCalls": [ + [ + "KEEP_ALIVE_TIMEOUT", + ], + [ + "SOCKET_TIMEOUT", + ], + [ + "MAX_CONNECTIONS", + ], + [ + "BUFFER_LIMIT", + ], + [ + "PARSERS", + ], + [ + "STRINGIFYERS", + ], + [ + "DECODERS", + ], + [ + "ENCODERS", + ], + [ + "QUERY_PARSER", + ], + [ + "TIMEOUT", + ], + [ + "TRANSACTIONS", + ], + [ + "SHIELD_CHAR", + ], + [ + "MAX_CLEAR_CHARS", + ], + [ + "MAX_CLEAR_RATIO", + ], + [ + "SENSIBLE_PROPS", + ], + [ + "SENSIBLE_HEADERS", + ], + [ + "uniqueId", + ], + [ + "ERRORS_DESCRIPTORS", + ], + [ + "DEFAULT_ERROR_CODE", + ], + [ + "PROCESS_NAME", + ], + [ + "SIGNALS", + ], + [ + "MAX_HEADERS_COUNT", + ], + ], + "debugCalls": [ + [ + "⌛ - Delay service initialized.", + ], + [ + "⏳ - Cancelling pending timeouts:", + 0, + ], + [ + "✅ - Closing HTTP server.", + ], + [ + "✔️ - HTTP server closed!", + ], + [ + "❤️ - Initializing the APM service.", + ], + [ + "👣 - Logging service initialized.", + ], + [ + "💱 - HTTP Transaction initialized.", + ], + [ + "📇 - Process service initialized.", + ], + [ + "🕶️ - Initializing the obfuscator service.", + ], + [ + "🚦 - HTTP Router initialized.", + ], + [ + "🛂 - Dynamic import of "ecstatic".", + ], + [ + "🛂 - Dynamic import of "swagger-ui-dist".", + ], + [ + "🛂 - Initializing the importer!", + ], + ], + "headers": { + "connection": undefined, + "content-type": "text/javascript", + "date": undefined, + "etag": undefined, + "keep-alive": undefined, + "last-modified": undefined, + "server": undefined, + "transfer-encoding": "chunked", + }, + "logErrorCalls": [ + [ + "💁 - Serving the API docs: http://localhost:22224/docs", + ], + [ + "🎙️ - HTTP Server listening at "http://localhost:22224".", + ], + [ + "On air 🚀🌕", + ], + ], + "status": 200, +} +`); + }); + it('should be bypassed with no debug env', async () => { $.register(constant('PORT', PORT + 1)); $.register(wrapHTTPRouterWithSwaggerUI(initHTTPRouter)); @@ -541,6 +765,9 @@ describe('wrapHTTPRouterWithSwaggerUI', () => { [ "DEFAULT_ERROR_CODE", ], + [ + "SWAGGER_UI_CONFIG", + ], [ "PROCESS_NAME", ], diff --git a/packages/whook-swagger-ui/src/index.ts b/packages/whook-swagger-ui/src/index.ts index 02c0f333..4f04bc0f 100644 --- a/packages/whook-swagger-ui/src/index.ts +++ b/packages/whook-swagger-ui/src/index.ts @@ -10,6 +10,8 @@ import type { import type { ImporterService, LogService } from 'common-services'; import type ECStatic from 'ecstatic'; import type { IncomingMessage, ServerResponse } from 'http'; +import type { SwaggerUIOptions } from 'swagger-ui'; +import type { Jsonify } from 'type-fest'; export { initGetOpenAPI, getOpenAPIDefinition }; @@ -21,6 +23,7 @@ export type WhookSwaggerUIConfig = { BASE_PATH?: string; HOST?: string; PORT?: number; + SWAGGER_UI_CONFIG?: Omit, 'dom_id' | 'urls'>; }; export type WhookSwaggerUIDependencies = WhookSwaggerUIConfig & { ENV: WhookSwaggerUIEnv; @@ -34,6 +37,13 @@ export type WhookAPIOperationSwaggerConfig = { private?: boolean; }; +export const DEFAULT_SWAGGER_UI_CONFIG: WhookSwaggerUIConfig['SWAGGER_UI_CONFIG'] = + { + deepLinking: true, + layout: 'StandaloneLayout', + displayOperationId: true, + }; + /** * Wraps the `httpRouter` initializer to also serve the * Swagger/OpenAPI UI for development purpose. @@ -54,6 +64,7 @@ export default function wrapHTTPRouterWithSwaggerUI( '?BASE_PATH', 'HOST', 'PORT', + '?SWAGGER_UI_CONFIG', 'importer', '?log', ], @@ -68,8 +79,9 @@ export default function wrapHTTPRouterWithSwaggerUI( BASE_PATH = '', HOST, PORT, - log = noop, + SWAGGER_UI_CONFIG = DEFAULT_SWAGGER_UI_CONFIG, importer, + log = noop, }: WhookSwaggerUIDependencies, httpRouter: WhookHTTPRouterProvider, ) => { @@ -96,28 +108,31 @@ window.onload = function() { // // the following lines will be replaced by docker/configurator, when it runs in a docker-container - window.ui = SwaggerUIBundle({ - urls: [{"name":"Public API","url":"${publicSwaggerURL}"}${ - DEV_ACCESS_TOKEN - ? `, {"name":"Private API","url":"${ - publicSwaggerURL + - '?access_token=' + - encodeURIComponent(DEV_ACCESS_TOKEN) - }"}` - : '' - }], - dom_id: '#swagger-ui', - deepLinking: true, - presets: [ - SwaggerUIBundle.presets.apis, - SwaggerUIStandalonePreset - ], - plugins: [ - SwaggerUIBundle.plugins.DownloadUrl, - SwaggerUIBundle.plugins.Topbar - ], - layout: "StandaloneLayout" - }); + window.ui = SwaggerUIBundle( + Object.assign( + { + urls: [{"name":"Public API","url":"${publicSwaggerURL}"}${ + DEV_ACCESS_TOKEN + ? `, {"name":"Private API","url":"${ + publicSwaggerURL + + '?access_token=' + + encodeURIComponent(DEV_ACCESS_TOKEN) + }"}` + : '' + }], + dom_id: '#swagger-ui', + presets: [ + SwaggerUIBundle.presets.apis, + SwaggerUIStandalonePreset + ], + plugins: [ + SwaggerUIBundle.plugins.DownloadUrl, + SwaggerUIBundle.plugins.Topbar + ], + }, + ${JSON.stringify(SWAGGER_UI_CONFIG)} + ) + ); // }; diff --git a/packages/whook/package.json b/packages/whook/package.json index ff82a167..adf4d8a2 100644 --- a/packages/whook/package.json +++ b/packages/whook/package.json @@ -125,7 +125,7 @@ "pkg-dir": "^8.0.0", "portfinder": "^1.0.32", "schema2dts": "^5.3.0", - "type-fest": "^4.7.1", + "type-fest": "^4.8.2", "yargs-parser": "^21.1.1", "yerror": "^8.0.0" },