diff --git a/packages/server/src/integrations/index.ts b/packages/server/src/integrations/index.ts index f3285e441f..82432dfa2c 100644 --- a/packages/server/src/integrations/index.ts +++ b/packages/server/src/integrations/index.ts @@ -20,7 +20,7 @@ import env from "../environment" import { cloneDeep } from "lodash" import sdk from "../sdk" -const DEFINITIONS: { [key: string]: Integration } = { +const DEFINITIONS: Record = { [SourceName.POSTGRES]: postgres.schema, [SourceName.DYNAMODB]: dynamodb.schema, [SourceName.MONGODB]: mongodb.schema, @@ -38,7 +38,7 @@ const DEFINITIONS: { [key: string]: Integration } = { [SourceName.SNOWFLAKE]: snowflake.schema, } -const INTEGRATIONS: { [key: string]: any } = { +const INTEGRATIONS: Record = { [SourceName.POSTGRES]: postgres.integration, [SourceName.DYNAMODB]: dynamodb.integration, [SourceName.MONGODB]: mongodb.integration, @@ -55,6 +55,7 @@ const INTEGRATIONS: { [key: string]: any } = { [SourceName.REDIS]: redis.integration, [SourceName.FIRESTORE]: firebase.integration, [SourceName.SNOWFLAKE]: snowflake.integration, + [SourceName.ORACLE]: undefined, } // optionally add oracle integration if the oracle binary can be installed @@ -69,8 +70,9 @@ if ( export async function getDefinition(source: SourceName): Promise { // check if its integrated, faster - if (DEFINITIONS[source]) { - return DEFINITIONS[source] + const definition = DEFINITIONS[source] + if (definition) { + return definition } const allDefinitions = await getDefinitions() return allDefinitions[source] @@ -98,7 +100,7 @@ export async function getDefinitions() { } } -export async function getIntegration(integration: string) { +export async function getIntegration(integration: SourceName) { if (INTEGRATIONS[integration]) { return INTEGRATIONS[integration] } @@ -119,7 +121,18 @@ export async function getIntegration(integration: string) { throw new Error("No datasource implementation found.") } +const VALIDATORS: Partial< + Record Promise> +> = { + [SourceName.POSTGRES]: postgres.validateConnection, +} + +function getValidators(integration: SourceName) { + return VALIDATORS[integration] +} + export default { getDefinitions, getIntegration, + getValidators, } diff --git a/packages/server/src/integrations/postgres.ts b/packages/server/src/integrations/postgres.ts index 4fb786a409..d84ea273c4 100644 --- a/packages/server/src/integrations/postgres.ts +++ b/packages/server/src/integrations/postgres.ts @@ -330,7 +330,20 @@ class PostgresIntegration extends Sql implements DatasourcePlus { } } +async function validateConnection(config: PostgresConfig) { + const integration = new PostgresIntegration(config) + try { + await integration.openConnection() + return true + } catch { + return false + } finally { + await integration.closeConnection() + } +} + export default { schema: SCHEMA, integration: PostgresIntegration, + validateConnection, } diff --git a/packages/server/src/sdk/tests/datasources/validators.spec.ts b/packages/server/src/sdk/tests/datasources/validators.spec.ts new file mode 100644 index 0000000000..700953feea --- /dev/null +++ b/packages/server/src/sdk/tests/datasources/validators.spec.ts @@ -0,0 +1,58 @@ +import { SourceName } from "@budibase/types" +import integrations from "../../../integrations" +import { GenericContainer } from "testcontainers" + +jest.unmock("pg") + +describe("datasource validators", () => { + describe("postgres", () => { + const validator = integrations.getValidators(SourceName.POSTGRES)! + + let host: string + let port: number + + beforeAll(async () => { + const container = await new GenericContainer("postgres") + .withExposedPorts(5432) + .withEnv("POSTGRES_PASSWORD", "password") + .start() + + host = container.getContainerIpAddress() + port = container.getMappedPort(5432) + }) + + it("test valid connection string", async () => { + const result = await validator({ + host, + port, + database: "postgres", + user: "postgres", + password: "password", + schema: "public", + ssl: false, + rejectUnauthorized: false, + ca: false, + }) + expect(result).toBeTruthy() + }) + + it("test invalid connection string", async () => { + try { + const result = await validator({ + host, + port, + database: "postgres", + user: "wrong", + password: "password", + schema: "public", + ssl: false, + rejectUnauthorized: false, + ca: false, + }) + expect(result).toBeFalsy() + } catch (e) { + console.error(e) + } + }) + }) +})