1
0
Fork 0
mirror of synced 2024-07-03 21:40:55 +12:00

Merge pull request #10546 from Budibase/budi-6932/verify_mysql

Implement mysql connection verification
This commit is contained in:
Michael Drury 2023-05-15 17:13:32 +01:00 committed by GitHub
commit fbc3697c8c
2 changed files with 88 additions and 16 deletions

View file

@ -21,18 +21,11 @@ import { NUMBER_REGEX } from "../utilities"
import Sql from "./base/sql"
import { MySQLColumn } from "./base/types"
const mysql = require("mysql2/promise")
import mysql from "mysql2/promise"
interface MySQLConfig {
host: string
port: number
user: string
password: string
interface MySQLConfig extends mysql.ConnectionOptions {
database: string
ssl?: { [key: string]: any }
rejectUnauthorized: boolean
typeCast: Function
multipleStatements: boolean
}
const SCHEMA: Integration = {
@ -94,8 +87,6 @@ const SCHEMA: Integration = {
},
}
const TimezoneAwareDateTypes = ["timestamp"]
function bindingTypeCoerce(bindings: any[]) {
for (let i = 0; i < bindings.length; i++) {
const binding = bindings[i]
@ -122,7 +113,7 @@ function bindingTypeCoerce(bindings: any[]) {
class MySQLIntegration extends Sql implements DatasourcePlus {
private config: MySQLConfig
private client: any
private client?: mysql.Connection
public tables: Record<string, Table> = {}
public schemaErrors: Record<string, string> = {}
@ -136,7 +127,8 @@ class MySQLIntegration extends Sql implements DatasourcePlus {
if (
config.rejectUnauthorized != null &&
!config.rejectUnauthorized &&
config.ssl
config.ssl &&
typeof config.ssl !== "string"
) {
config.ssl.rejectUnauthorized = config.rejectUnauthorized
}
@ -162,6 +154,18 @@ class MySQLIntegration extends Sql implements DatasourcePlus {
}
}
async testConnection() {
try {
const [result] = await this.internalQuery(
{ sql: "SELECT 1+1 AS checkRes" },
{ connect: true }
)
return result?.checkRes == 2
} catch (e: any) {
return { error: e.message as string }
}
}
getBindingIdentifier(): string {
return "?"
}
@ -175,7 +179,7 @@ class MySQLIntegration extends Sql implements DatasourcePlus {
}
async disconnect() {
await this.client.end()
await this.client!.end()
}
async internalQuery(
@ -194,10 +198,10 @@ class MySQLIntegration extends Sql implements DatasourcePlus {
? baseBindings
: bindingTypeCoerce(baseBindings)
// Node MySQL is callback based, so we must wrap our call in a promise
const response = await this.client.query(query.sql, bindings)
const response = await this.client!.query(query.sql, bindings)
return response[0]
} finally {
if (opts?.connect) {
if (opts?.connect && this.client) {
await this.disconnect()
}
}

View file

@ -0,0 +1,68 @@
import { GenericContainer } from "testcontainers"
import mysql from "../../../../packages/server/src/integrations/mysql"
jest.unmock("mysql2/promise")
describe("datasource validators", () => {
describe("mysql", () => {
let host: string
let port: number
beforeAll(async () => {
const container = await new GenericContainer("mysql")
.withExposedPorts(3306)
.withEnv("MYSQL_ROOT_PASSWORD", "admin")
.withEnv("MYSQL_DATABASE", "db")
.withEnv("MYSQL_USER", "user")
.withEnv("MYSQL_PASSWORD", "password")
.start()
host = container.getContainerIpAddress()
port = container.getMappedPort(3306)
})
it("test valid connection string", async () => {
const integration = new mysql.integration({
host,
port,
user: "user",
database: "db",
password: "password",
rejectUnauthorized: true,
})
const result = await integration.testConnection()
expect(result).toBe(true)
})
it("test invalid database", async () => {
const integration = new mysql.integration({
host,
port,
user: "user",
database: "test",
password: "password",
rejectUnauthorized: true,
})
const result = await integration.testConnection()
expect(result).toEqual({
error: "Access denied for user 'user'@'%' to database 'test'",
})
})
it("test invalid password", async () => {
const integration = new mysql.integration({
host,
port,
user: "root",
database: "test",
password: "wrong",
rejectUnauthorized: true,
})
const result = await integration.testConnection()
expect(result).toEqual({
error:
"Access denied for user 'root'@'172.17.0.1' (using password: YES)",
})
})
})
})