MariaDB tests passing.
This commit is contained in:
parent
1334f5dcc5
commit
aff0209176
|
@ -50,6 +50,7 @@ describe.each([
|
|||
["postgres", databaseTestProviders.postgres],
|
||||
["mysql", databaseTestProviders.mysql],
|
||||
["mssql", databaseTestProviders.mssql],
|
||||
["mariadb", databaseTestProviders.mariadb],
|
||||
])("/rows (%s)", (__, dsProvider) => {
|
||||
const isInternal = !dsProvider
|
||||
|
||||
|
|
|
@ -4,11 +4,15 @@ import { QueryOptions } from "../../definitions/datasource"
|
|||
import { isIsoDateString, SqlClient, isValidFilter } from "../utils"
|
||||
import SqlTableQueryBuilder from "./sqlTable"
|
||||
import {
|
||||
FieldSchema,
|
||||
FieldSubtype,
|
||||
FieldType,
|
||||
Operation,
|
||||
QueryJson,
|
||||
RelationshipsJson,
|
||||
SearchFilters,
|
||||
SortDirection,
|
||||
Table,
|
||||
} from "@budibase/types"
|
||||
import environment from "../../environment"
|
||||
|
||||
|
@ -691,6 +695,35 @@ class SqlQueryBuilder extends SqlTableQueryBuilder {
|
|||
return results.length ? results : [{ [operation.toLowerCase()]: true }]
|
||||
}
|
||||
|
||||
convertJsonStringColumns(
|
||||
table: Table,
|
||||
results: Record<string, any>[]
|
||||
): Record<string, any>[] {
|
||||
for (const [name, field] of Object.entries(table.schema)) {
|
||||
if (!this._isJsonColumn(field)) {
|
||||
continue
|
||||
}
|
||||
const fullName = `${table.name}.${name}`
|
||||
for (let row of results) {
|
||||
if (typeof row[fullName] === "string") {
|
||||
row[fullName] = JSON.parse(row[fullName])
|
||||
}
|
||||
if (typeof row[name] === "string") {
|
||||
row[name] = JSON.parse(row[name])
|
||||
}
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
_isJsonColumn(field: FieldSchema) {
|
||||
return (
|
||||
field.type === FieldType.JSON ||
|
||||
(field.type === FieldType.BB_REFERENCE &&
|
||||
field.subtype === FieldSubtype.USERS)
|
||||
)
|
||||
}
|
||||
|
||||
log(query: string, values?: any[]) {
|
||||
if (!environment.SQL_LOGGING_ENABLE) {
|
||||
return
|
||||
|
|
|
@ -504,33 +504,15 @@ class SqlServerIntegration extends Sql implements DatasourcePlus {
|
|||
}
|
||||
const operation = this._operation(json)
|
||||
const queryFn = (query: any, op: string) => this.internalQuery(query, op)
|
||||
const processFn = (result: any) =>
|
||||
result.recordset
|
||||
? this._postProcessJson(json, result.recordset)
|
||||
: [{ [operation]: true }]
|
||||
return this.queryWithReturning(json, queryFn, processFn)
|
||||
}
|
||||
|
||||
_postProcessJson(json: QueryJson, results: IRecordSet<any>) {
|
||||
const table = json.meta?.table
|
||||
if (!table) {
|
||||
return results
|
||||
}
|
||||
for (const [name, field] of Object.entries(table.schema)) {
|
||||
if (
|
||||
field.type === FieldType.JSON ||
|
||||
(field.type === FieldType.BB_REFERENCE &&
|
||||
field.subtype === FieldSubtype.USERS)
|
||||
) {
|
||||
const fullName = `${table.name}.${name}`
|
||||
for (let row of results) {
|
||||
if (typeof row[fullName] === "string") {
|
||||
row[fullName] = JSON.parse(row[fullName])
|
||||
}
|
||||
}
|
||||
const processFn = (result: any) => {
|
||||
if (json?.meta?.table && result.recordset) {
|
||||
return this.convertJsonStringColumns(json.meta.table, result.recordset)
|
||||
} else if (result.recordset) {
|
||||
return result.recordset
|
||||
}
|
||||
return [{ [operation]: true }]
|
||||
}
|
||||
return results
|
||||
return this.queryWithReturning(json, queryFn, processFn)
|
||||
}
|
||||
|
||||
async getExternalSchema() {
|
||||
|
|
|
@ -13,6 +13,8 @@ import {
|
|||
Schema,
|
||||
TableSourceType,
|
||||
DatasourcePlusQueryResponse,
|
||||
FieldType,
|
||||
FieldSubtype,
|
||||
} from "@budibase/types"
|
||||
import {
|
||||
getSqlQuery,
|
||||
|
@ -386,12 +388,40 @@ class MySQLIntegration extends Sql implements DatasourcePlus {
|
|||
try {
|
||||
const queryFn = (query: any) =>
|
||||
this.internalQuery(query, { connect: false, disableCoercion: true })
|
||||
return await this.queryWithReturning(json, queryFn)
|
||||
const processFn = (result: any) => {
|
||||
if (json?.meta?.table && Array.isArray(result)) {
|
||||
return this.convertJsonStringColumns(json.meta.table, result)
|
||||
}
|
||||
return result
|
||||
}
|
||||
return await this.queryWithReturning(json, queryFn, processFn)
|
||||
} finally {
|
||||
await this.disconnect()
|
||||
}
|
||||
}
|
||||
|
||||
_postProcessJson(json: QueryJson, results: any) {
|
||||
const table = json.meta?.table
|
||||
if (!table) {
|
||||
return results
|
||||
}
|
||||
for (const [name, field] of Object.entries(table.schema)) {
|
||||
if (
|
||||
field.type === FieldType.JSON ||
|
||||
(field.type === FieldType.BB_REFERENCE &&
|
||||
field.subtype === FieldSubtype.USERS)
|
||||
) {
|
||||
const fullName = `${table.name}.${name}`
|
||||
for (let row of results) {
|
||||
if (typeof row[fullName] === "string") {
|
||||
row[fullName] = JSON.parse(row[fullName])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
async getExternalSchema() {
|
||||
try {
|
||||
const [databaseResult] = await this.internalQuery({
|
||||
|
|
|
@ -5,6 +5,7 @@ import * as postgres from "./postgres"
|
|||
import * as mongodb from "./mongodb"
|
||||
import * as mysql from "./mysql"
|
||||
import * as mssql from "./mssql"
|
||||
import * as mariadb from "./mariadb"
|
||||
import { StartedTestContainer } from "testcontainers"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
@ -15,4 +16,10 @@ export interface DatabaseProvider {
|
|||
datasource(): Promise<Datasource>
|
||||
}
|
||||
|
||||
export const databaseTestProviders = { postgres, mongodb, mysql, mssql }
|
||||
export const databaseTestProviders = {
|
||||
postgres,
|
||||
mongodb,
|
||||
mysql,
|
||||
mssql,
|
||||
mariadb,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
import { Datasource, SourceName } from "@budibase/types"
|
||||
import { GenericContainer, Wait, StartedTestContainer } from "testcontainers"
|
||||
import { AbstractWaitStrategy } from "testcontainers/build/wait-strategies/wait-strategy"
|
||||
|
||||
let container: StartedTestContainer | undefined
|
||||
|
||||
class MariaDBWaitStrategy extends AbstractWaitStrategy {
|
||||
async waitUntilReady(container: any, boundPorts: any, startTime?: Date) {
|
||||
// Because MariaDB first starts itself up, runs an init script, then restarts,
|
||||
// it's possible for the mysqladmin ping to succeed early and then tests to
|
||||
// run against a MariaDB that's mid-restart and fail. To get around this, we
|
||||
// wait for logs and then do a ping check.
|
||||
|
||||
const logs = Wait.forLogMessage("mariadbd: ready for connections", 2)
|
||||
await logs.waitUntilReady(container, boundPorts, startTime)
|
||||
|
||||
const command = Wait.forSuccessfulCommand(
|
||||
`mysqladmin ping -h localhost -P 3306 -u root -ppassword`
|
||||
)
|
||||
await command.waitUntilReady(container)
|
||||
}
|
||||
}
|
||||
|
||||
export async function start(): Promise<StartedTestContainer> {
|
||||
return await new GenericContainer("mariadb:lts")
|
||||
.withExposedPorts(3306)
|
||||
.withEnvironment({ MARIADB_ROOT_PASSWORD: "password" })
|
||||
.withWaitStrategy(new MariaDBWaitStrategy())
|
||||
.start()
|
||||
}
|
||||
|
||||
export async function datasource(): Promise<Datasource> {
|
||||
if (!container) {
|
||||
container = await start()
|
||||
}
|
||||
const host = container.getHost()
|
||||
const port = container.getMappedPort(3306)
|
||||
|
||||
return {
|
||||
type: "datasource_plus",
|
||||
source: SourceName.MYSQL,
|
||||
plus: true,
|
||||
config: {
|
||||
host,
|
||||
port,
|
||||
user: "root",
|
||||
password: "password",
|
||||
database: "mysql",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export async function stop() {
|
||||
if (container) {
|
||||
await container.stop()
|
||||
container = undefined
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue