From e21dca55823b1b919ffe995febd5ebbf0d45db66 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 19 Jun 2023 15:32:49 +0100 Subject: [PATCH] Implement and test mysql sql dump --- packages/server/src/integrations/mysql.ts | 26 ++++- .../external-schema/mysql.integration.spec.ts | 108 ++++++++++++++++++ 2 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 qa-core/src/integrations/external-schema/mysql.integration.spec.ts diff --git a/packages/server/src/integrations/mysql.ts b/packages/server/src/integrations/mysql.ts index 46bd97836b..4bcecb0b44 100644 --- a/packages/server/src/integrations/mysql.ts +++ b/packages/server/src/integrations/mysql.ts @@ -327,11 +327,33 @@ class MySQLIntegration extends Sql implements DatasourcePlus { } async getExternalSchema() { - const [result] = await this.internalQuery({ + try { + const [databaseResult] = await this.internalQuery({ sql: `SHOW CREATE DATABASE ${this.config.database}`, }) - const schema = result["Create Database"] + let dumpContent = [databaseResult["Create Database"]] + + const tablesResult = await this.internalQuery({ + sql: `SHOW TABLES`, + }) + + for (const row of tablesResult) { + const tableName = row[`Tables_in_${this.config.database}`] + + const createTableResults = await this.internalQuery({ + sql: `SHOW CREATE TABLE \`${tableName}\``, + }) + + const createTableStatement = createTableResults[0]["Create Table"] + + dumpContent.push(createTableStatement) + } + + const schema = dumpContent.join("\n") return schema + } finally { + this.disconnect() + } } } diff --git a/qa-core/src/integrations/external-schema/mysql.integration.spec.ts b/qa-core/src/integrations/external-schema/mysql.integration.spec.ts new file mode 100644 index 0000000000..c34651ea0e --- /dev/null +++ b/qa-core/src/integrations/external-schema/mysql.integration.spec.ts @@ -0,0 +1,108 @@ +import { GenericContainer } from "testcontainers" +import mysql from "../../../../packages/server/src/integrations/mysql" + +jest.unmock("mysql2/promise") + +describe("datasource validators", () => { + describe("mysql", () => { + let config: any + + 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() + + const host = container.getContainerIpAddress() + const port = container.getMappedPort(3306) + config = { + host, + port, + user: "user", + database: "db", + password: "password", + rejectUnauthorized: true, + } + }) + + it("can export an empty database", async () => { + const integration = new mysql.integration(config) + const result = await integration.getExternalSchema() + expect(result).toMatchInlineSnapshot( + `"CREATE DATABASE \`db\` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */ /*!80016 DEFAULT ENCRYPTION='N' */"` + ) + }) + + it("can export a database with tables", async () => { + const integration = new mysql.integration(config) + + await integration.internalQuery({ + sql: ` + CREATE TABLE users ( + id INT AUTO_INCREMENT, + name VARCHAR(100) NOT NULL, + role VARCHAR(15) NOT NULL, + PRIMARY KEY (id) + ); + + + CREATE TABLE products ( + id INT AUTO_INCREMENT, + name VARCHAR(100) NOT NULL, + price DECIMAL, + PRIMARY KEY (id) + ); + `, + }) + + const result = await integration.getExternalSchema() + expect(result).toMatchInlineSnapshot(` + "CREATE DATABASE \`db\` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */ /*!80016 DEFAULT ENCRYPTION='N' */ + CREATE TABLE \`products\` ( + \`id\` int NOT NULL AUTO_INCREMENT, + \`name\` varchar(100) NOT NULL, + \`price\` decimal(10,0) DEFAULT NULL, + PRIMARY KEY (\`id\`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci + CREATE TABLE \`users\` ( + \`id\` int NOT NULL AUTO_INCREMENT, + \`name\` varchar(100) NOT NULL, + \`role\` varchar(15) NOT NULL, + PRIMARY KEY (\`id\`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci" + `) + }) + + it("does not export a data", async () => { + const integration = new mysql.integration(config) + + await integration.internalQuery({ + sql: `INSERT INTO users (name, role) VALUES ('John Doe', 'Administrator');`, + }) + + await integration.internalQuery({ + sql: `INSERT INTO products (name, price) VALUES ('Book', 7.68);`, + }) + + const result = await integration.getExternalSchema() + expect(result).toMatchInlineSnapshot(` + "CREATE DATABASE \`db\` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */ /*!80016 DEFAULT ENCRYPTION='N' */ + CREATE TABLE \`products\` ( + \`id\` int NOT NULL AUTO_INCREMENT, + \`name\` varchar(100) NOT NULL, + \`price\` decimal(10,0) DEFAULT NULL, + PRIMARY KEY (\`id\`) + ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci + CREATE TABLE \`users\` ( + \`id\` int NOT NULL AUTO_INCREMENT, + \`name\` varchar(100) NOT NULL, + \`role\` varchar(15) NOT NULL, + PRIMARY KEY (\`id\`) + ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci" + `) + }) + }) +})