diff --git a/packages/cli/src/PublicApi/index.ts b/packages/cli/src/PublicApi/index.ts index 212b06984c..029cade4c5 100644 --- a/packages/cli/src/PublicApi/index.ts +++ b/packages/cli/src/PublicApi/index.ts @@ -3,6 +3,9 @@ import express, { Router } from 'express'; import fs from 'fs/promises'; import path from 'path'; +import validator from 'validator'; +import { middleware as openapiValidatorMiddleware } from 'express-openapi-validator'; +import YAML from 'yamljs'; import type { HttpError } from 'express-openapi-validator/dist/framework/types'; import type { OpenAPIV3 } from 'openapi-types'; import type { JsonObject } from 'swagger-ui-express'; @@ -16,11 +19,9 @@ async function createApiRouter( version: string, openApiSpecPath: string, handlersDirectory: string, - swaggerThemeCss: string, publicApiEndpoint: string, ): Promise { const n8nPath = config.getEnv('path'); - const YAML = await import('yamljs'); const swaggerDocument = YAML.load(openApiSpecPath) as JsonObject; // add the server depending on the config so the user can interact with the API // from the Swagger UI @@ -33,6 +34,8 @@ async function createApiRouter( if (!config.getEnv('publicApi.swaggerUi.disabled')) { const { serveFiles, setup } = await import('swagger-ui-express'); + const swaggerThemePath = path.join(__dirname, 'swaggerTheme.css'); + const swaggerThemeCss = await fs.readFile(swaggerThemePath, { encoding: 'utf-8' }); apiController.use( `/${publicApiEndpoint}/${version}/docs`, @@ -45,12 +48,10 @@ async function createApiRouter( ); } - const { default: validator } = await import('validator'); - const { middleware } = await import('express-openapi-validator'); apiController.use( `/${publicApiEndpoint}/${version}`, express.json(), - middleware({ + openapiValidatorMiddleware({ apiSpec: openApiSpecPath, operationHandlers: handlersDirectory, validateRequests: true, @@ -129,15 +130,13 @@ async function createApiRouter( export const loadPublicApiVersions = async ( publicApiEndpoint: string, ): Promise<{ apiRouters: express.Router[]; apiLatestVersion: number }> => { - const swaggerThemePath = path.join(__dirname, 'swaggerTheme.css'); const folders = await fs.readdir(__dirname); - const css = (await fs.readFile(swaggerThemePath)).toString(); const versions = folders.filter((folderName) => folderName.startsWith('v')); const apiRouters = await Promise.all( versions.map(async (version) => { const openApiPath = path.join(__dirname, version, 'openapi.yml'); - return createApiRouter(version, openApiPath, __dirname, css, publicApiEndpoint); + return createApiRouter(version, openApiPath, __dirname, publicApiEndpoint); }), ); diff --git a/packages/cli/src/config/index.ts b/packages/cli/src/config/index.ts index 68a11628ad..56a3f01288 100644 --- a/packages/cli/src/config/index.ts +++ b/packages/cli/src/config/index.ts @@ -22,6 +22,7 @@ if (inE2ETests) { } if (inTest) { process.env.N8N_PUBLIC_API_DISABLED = 'true'; + process.env.N8N_PUBLIC_API_SWAGGERUI_DISABLED = 'true'; } else { dotenv.config(); } diff --git a/packages/cli/test/integration/publicApi/credentials.test.ts b/packages/cli/test/integration/publicApi/credentials.test.ts index 776eebe4d9..7786b8c309 100644 --- a/packages/cli/test/integration/publicApi/credentials.test.ts +++ b/packages/cli/test/integration/publicApi/credentials.test.ts @@ -18,7 +18,11 @@ let credentialOwnerRole: Role; let saveCredential: SaveCredentialFunction; beforeAll(async () => { - app = await utils.initTestServer({ endpointGroups: ['publicApi'], applyAuth: false }); + app = await utils.initTestServer({ + endpointGroups: ['publicApi'], + applyAuth: false, + enablePublicAPI: true, + }); await testDb.init(); utils.initConfigFile(); diff --git a/packages/cli/test/integration/publicApi/executions.test.ts b/packages/cli/test/integration/publicApi/executions.test.ts index 24ff6f7bad..580f8bfa22 100644 --- a/packages/cli/test/integration/publicApi/executions.test.ts +++ b/packages/cli/test/integration/publicApi/executions.test.ts @@ -13,7 +13,11 @@ let globalOwnerRole: Role; let workflowRunner: ActiveWorkflowRunner; beforeAll(async () => { - app = await utils.initTestServer({ endpointGroups: ['publicApi'], applyAuth: false }); + app = await utils.initTestServer({ + endpointGroups: ['publicApi'], + applyAuth: false, + enablePublicAPI: true, + }); await testDb.init(); globalOwnerRole = await testDb.getGlobalOwnerRole(); @@ -43,7 +47,7 @@ beforeEach(async () => { }); afterEach(async () => { - await workflowRunner.removeAll(); + await workflowRunner?.removeAll(); }); afterAll(async () => { diff --git a/packages/cli/test/integration/publicApi/workflows.test.ts b/packages/cli/test/integration/publicApi/workflows.test.ts index 125f215fa4..c9978a9931 100644 --- a/packages/cli/test/integration/publicApi/workflows.test.ts +++ b/packages/cli/test/integration/publicApi/workflows.test.ts @@ -17,7 +17,11 @@ let workflowOwnerRole: Role; let workflowRunner: ActiveWorkflowRunner; beforeAll(async () => { - app = await utils.initTestServer({ endpointGroups: ['publicApi'], applyAuth: false }); + app = await utils.initTestServer({ + endpointGroups: ['publicApi'], + applyAuth: false, + enablePublicAPI: true, + }); await testDb.init(); const [fetchedGlobalOwnerRole, fetchedGlobalMemberRole, fetchedWorkflowOwnerRole] = @@ -49,7 +53,7 @@ beforeEach(async () => { }); afterEach(async () => { - await workflowRunner.removeAll(); + await workflowRunner?.removeAll(); }); afterAll(async () => { diff --git a/packages/cli/test/integration/shared/testDb.ts b/packages/cli/test/integration/shared/testDb.ts index 47fc7eff73..a0e9dc52f4 100644 --- a/packages/cli/test/integration/shared/testDb.ts +++ b/packages/cli/test/integration/shared/testDb.ts @@ -95,7 +95,8 @@ export async function init() { * Drop test DB, closing bootstrap connection if existing. */ export async function terminate() { - await Db.getConnection().destroy(); + const connection = Db.getConnection(); + if (connection.isInitialized) await connection.destroy(); } /** diff --git a/packages/cli/test/integration/shared/utils.ts b/packages/cli/test/integration/shared/utils.ts index 3a664b494e..58a2a8e6e9 100644 --- a/packages/cli/test/integration/shared/utils.ts +++ b/packages/cli/test/integration/shared/utils.ts @@ -82,16 +82,15 @@ CredentialTypes(loadNodesAndCredentials); /** * Initialize a test server. - * - * @param applyAuth Whether to apply auth middleware to test server. - * @param endpointGroups Groups of endpoints to apply to test server. */ export async function initTestServer({ applyAuth, endpointGroups, + enablePublicAPI = false, }: { applyAuth: boolean; endpointGroups?: EndpointGroup[]; + enablePublicAPI?: boolean; }) { const testServer = { app: express(), @@ -124,17 +123,20 @@ export async function initTestServer({ const [routerEndpoints, functionEndpoints] = classifyEndpointGroups(endpointGroups); if (routerEndpoints.length) { - const { apiRouters } = await loadPublicApiVersions(testServer.publicApiEndpoint); const map: Record = { credentials: { controller: credentialsController, path: 'credentials' }, workflows: { controller: workflowsController, path: 'workflows' }, nodes: { controller: nodesController, path: 'nodes' }, license: { controller: licenseController, path: 'license' }, eventBus: { controller: eventBusRouter, path: 'eventbus' }, - publicApi: apiRouters, ldap: { controller: ldapController, path: 'ldap' }, }; + if (enablePublicAPI) { + const { apiRouters } = await loadPublicApiVersions(testServer.publicApiEndpoint); + map.publicApi = apiRouters; + } + for (const group of routerEndpoints) { if (group === 'publicApi') { testServer.app.use(...(map[group] as express.Router[]));