Fix table.spec.ts.

This commit is contained in:
Sam Rose 2024-11-27 17:28:20 +00:00
parent 78bbf90f17
commit dbe9eedcba
No known key found for this signature in database
15 changed files with 58 additions and 49 deletions

View File

@ -3,7 +3,6 @@ import * as dbCore from "../db"
import { import {
getNativeSql, getNativeSql,
isExternalTable, isExternalTable,
isInternalTableID,
isInvalidISODateString, isInvalidISODateString,
isValidFilter, isValidFilter,
isValidISODateString, isValidISODateString,
@ -497,9 +496,8 @@ class InternalBuilder {
filterKey: string, filterKey: string,
whereCb: (filterKey: string, query: Knex.QueryBuilder) => Knex.QueryBuilder whereCb: (filterKey: string, query: Knex.QueryBuilder) => Knex.QueryBuilder
): Knex.QueryBuilder { ): Knex.QueryBuilder {
const { relationships, endpoint, tableAliases: aliases } = this.query const { relationships, endpoint, tableAliases: aliases, table } = this.query
const tableName = endpoint.entityId const fromAlias = aliases?.[table.name] || table.name
const fromAlias = aliases?.[tableName] || tableName
const matches = (value: string) => const matches = (value: string) =>
filterKey.match(new RegExp(`^${value}\\.`)) filterKey.match(new RegExp(`^${value}\\.`))
if (!relationships) { if (!relationships) {
@ -1455,14 +1453,14 @@ class InternalBuilder {
} }
qualifiedKnex(opts?: { alias?: string | boolean }): Knex.QueryBuilder { qualifiedKnex(opts?: { alias?: string | boolean }): Knex.QueryBuilder {
let alias = this.query.tableAliases?.[this.query.endpoint.entityId] let alias = this.query.tableAliases?.[this.query.table.name]
if (opts?.alias === false) { if (opts?.alias === false) {
alias = undefined alias = undefined
} else if (typeof opts?.alias === "string") { } else if (typeof opts?.alias === "string") {
alias = opts.alias alias = opts.alias
} }
return this.knex( return this.knex(
this.tableNameWithSchema(this.query.endpoint.entityId, { this.tableNameWithSchema(this.query.table.name, {
alias, alias,
schema: this.query.endpoint.schema, schema: this.query.endpoint.schema,
}) })
@ -1558,11 +1556,10 @@ class InternalBuilder {
limits?: { base: number; query: number } limits?: { base: number; query: number }
} = {} } = {}
): Knex.QueryBuilder { ): Knex.QueryBuilder {
let { endpoint, filters, paginate, relationships } = this.query let { endpoint, filters, paginate, relationships, table } = this.query
const { limits } = opts const { limits } = opts
const counting = endpoint.operation === Operation.COUNT const counting = endpoint.operation === Operation.COUNT
const tableName = endpoint.entityId
// start building the query // start building the query
let query = this.qualifiedKnex() let query = this.qualifiedKnex()
// handle pagination // handle pagination
@ -1610,9 +1607,7 @@ class InternalBuilder {
// handle relationships with a CTE for all others // handle relationships with a CTE for all others
if (relationships?.length && aggregations.length === 0) { if (relationships?.length && aggregations.length === 0) {
const mainTable = const mainTable = this.query.tableAliases?.[table.name] || table.name
this.query.tableAliases?.[this.query.endpoint.entityId] ||
this.query.endpoint.entityId
const cte = this.addSorting( const cte = this.addSorting(
this.knex this.knex
.with("paginated", query) .with("paginated", query)
@ -1622,7 +1617,7 @@ class InternalBuilder {
}) })
) )
// add JSON aggregations attached to the CTE // add JSON aggregations attached to the CTE
return this.addJsonRelationships(cte, tableName, relationships) return this.addJsonRelationships(cte, table.name, relationships)
} }
return query return query

View File

@ -25,7 +25,7 @@ function generateSchema(
schema: CreateTableBuilder, schema: CreateTableBuilder,
table: Table, table: Table,
tables: Record<string, Table>, tables: Record<string, Table>,
oldTable: null | Table = null, oldTable?: Table,
renamed?: RenameColumn renamed?: RenameColumn
) { ) {
let primaryKeys = table && table.primary ? table.primary : [] let primaryKeys = table && table.primary ? table.primary : []
@ -55,7 +55,7 @@ function generateSchema(
) )
for (let [key, column] of Object.entries(table.schema)) { for (let [key, column] of Object.entries(table.schema)) {
// skip things that are already correct // skip things that are already correct
const oldColumn = oldTable ? oldTable.schema[key] : null const oldColumn = oldTable?.schema[key]
if ( if (
(oldColumn && oldColumn.type) || (oldColumn && oldColumn.type) ||
columnTypeSet.includes(key) || columnTypeSet.includes(key) ||
@ -199,7 +199,7 @@ function buildUpdateTable(
knex: SchemaBuilder, knex: SchemaBuilder,
table: Table, table: Table,
tables: Record<string, Table>, tables: Record<string, Table>,
oldTable: Table, oldTable?: Table,
renamed?: RenameColumn renamed?: RenameColumn
): SchemaBuilder { ): SchemaBuilder {
return knex.alterTable(table.name, schema => { return knex.alterTable(table.name, schema => {
@ -281,7 +281,7 @@ class SqlTableQueryBuilder {
client, client,
json.table, json.table,
json.tables, json.tables,
json.table, json.meta?.oldTable,
json.meta?.renamed json.meta?.renamed
) )

@ -1 +1 @@
Subproject commit 06e4506cef37110e509a0ef0b9811ac07f8b2844 Subproject commit 8c5125b4f6e53d0a43d4de90aecbf53d9f7da8ff

View File

@ -137,7 +137,7 @@ function cleanupConfig(config: RunConfig, table: Table): RunConfig {
function getEndpoint(tableId: string, operation: Operation) { function getEndpoint(tableId: string, operation: Operation) {
const { datasourceId, tableName } = breakExternalTableId(tableId) const { datasourceId, tableName } = breakExternalTableId(tableId)
return { datasource: datasourceId, entityId: tableName, operation } return { datasourceId, entityId: tableName, operation }
} }
function isOneSide( function isOneSide(
@ -706,8 +706,8 @@ export class ExternalRequest<T extends Operation> {
let json: QueryJson = { let json: QueryJson = {
endpoint: { endpoint: {
datasource: this.datasource, datasourceId: this.datasource,
entityId: table.name, entityId: table,
operation, operation,
}, },
resource: { resource: {

View File

@ -11,19 +11,23 @@ export async function makeTableRequest(
datasource: Datasource, datasource: Datasource,
operation: Operation, operation: Operation,
table: Table, table: Table,
oldTable?: Table,
renamed?: RenameColumn renamed?: RenameColumn
) { ) {
const json: QueryJson = { const json: QueryJson = {
endpoint: { endpoint: {
datasource, datasourceId: datasource,
entityId: table._id!, entityId: table,
operation, operation,
}, },
} }
if (renamed) {
if (!json.meta) { if (!json.meta) {
json.meta = {} json.meta = {}
} }
if (oldTable) {
json.meta.oldTable = oldTable
}
if (renamed) {
json.meta.renamed = renamed json.meta.renamed = renamed
} }
return makeExternalQuery(json) return makeExternalQuery(json)

View File

@ -54,7 +54,7 @@ export async function updateTable(
return table return table
} catch (err: any) { } catch (err: any) {
if (err instanceof Error) { if (err instanceof Error) {
ctx.throw(400, err.message) throw err
} else { } else {
ctx.throw(err.status || 500, err?.message || err) ctx.throw(err.status || 500, err?.message || err)
} }

View File

@ -837,7 +837,7 @@ if (descriptions.length) {
const res = await config.api.datasource.query({ const res = await config.api.datasource.query({
endpoint: { endpoint: {
datasource: datasource._id!, datasourceId: datasource._id!,
operation: Operation.READ, operation: Operation.READ,
entityId, entityId,
}, },

View File

@ -382,7 +382,7 @@ export class GoogleSheetsIntegration implements DatasourcePlus {
} }
async query(json: EnrichedQueryJson): Promise<DatasourcePlusQueryResponse> { async query(json: EnrichedQueryJson): Promise<DatasourcePlusQueryResponse> {
const sheet = json.endpoint.entityId const sheet = json.table.name
switch (json.endpoint.operation) { switch (json.endpoint.operation) {
case Operation.CREATE: case Operation.CREATE:
return this.create({ sheet, row: json.body as Row }) return this.create({ sheet, row: json.body as Row })

View File

@ -580,7 +580,7 @@ class OracleIntegration extends Sql implements DatasourcePlus {
operation !== Operation.DELETE operation !== Operation.DELETE
) { ) {
const lastRow = await this.internalQuery({ const lastRow = await this.internalQuery({
sql: `SELECT * FROM "${json.endpoint.entityId}" WHERE ROWID = '${response.lastRowid}'`, sql: `SELECT * FROM "${json.table.name}" WHERE ROWID = '${response.lastRowid}'`,
}) })
return lastRow.rows as Row[] return lastRow.rows as Row[]
} else { } else {

View File

@ -231,8 +231,7 @@ describe("Captures of real examples", () => {
}, queryJson) }, queryJson)
expect(returningQuery).toEqual({ expect(returningQuery).toEqual({
sql: multiline( sql: multiline(
`select top (@p0) * from [people] as [a] where CASE WHEN [a].[name] = @p1 THEN 1 ELSE 0 END = 1 and `select top (@p0) * from [people] where CASE WHEN [people].[name] = @p1 THEN 1 ELSE 0 END = 1 and CASE WHEN [people].[age] = @p2 THEN 1 ELSE 0 END = 1 order by [people].[name] asc`
CASE WHEN [a].[age] = @p2 THEN 1 ELSE 0 END = 1 order by [a].[name] asc`
), ),
bindings: [1, "Test", 22], bindings: [1, "Test", 22],
}) })
@ -270,7 +269,7 @@ describe("Captures of real examples", () => {
fields: string[] = ["a"] fields: string[] = ["a"]
): EnrichedQueryJson { ): EnrichedQueryJson {
return { return {
endpoint: { datasource: "", entityId: "", operation: op }, endpoint: { datasourceId: "", entityId: "", operation: op },
resource: { resource: {
fields, fields,
}, },

View File

@ -358,7 +358,7 @@ export async function search(
const request: QueryJson = { const request: QueryJson = {
endpoint: { endpoint: {
// not important, we query ourselves // not important, we query ourselves
datasource: SQS_DATASOURCE_INTERNAL, datasourceId: SQS_DATASOURCE_INTERNAL,
entityId: table._id!, entityId: table._id!,
operation: Operation.READ, operation: Operation.READ,
}, },

View File

@ -222,7 +222,7 @@ export default class AliasTables {
aliases: this.aliasMap([ aliases: this.aliasMap([
relationship.through, relationship.through,
relationship.tableName, relationship.tableName,
json.endpoint.entityId, json.table.name,
]), ]),
})) }))
} }

View File

@ -23,8 +23,6 @@ import { isSQL } from "../../../integrations/utils"
import { docIds, sql, SQS_DATASOURCE_INTERNAL } from "@budibase/backend-core" import { docIds, sql, SQS_DATASOURCE_INTERNAL } from "@budibase/backend-core"
import { getTableFromSource } from "../../../api/controllers/row/utils" import { getTableFromSource } from "../../../api/controllers/row/utils"
import env from "../../../environment" import env from "../../../environment"
import { breakExternalTableId } from "@budibase/backend-core/src/sql/utils"
import { isDatasourceId } from "@budibase/backend-core/src/docIds"
const SQL_CLIENT_SOURCE_MAP: Record<SourceName, SqlClient | undefined> = { const SQL_CLIENT_SOURCE_MAP: Record<SourceName, SqlClient | undefined> = {
[SourceName.POSTGRES]: SqlClient.POSTGRES, [SourceName.POSTGRES]: SqlClient.POSTGRES,
@ -90,18 +88,15 @@ export async function enrichQueryJson(
json: QueryJson json: QueryJson
): Promise<EnrichedQueryJson> { ): Promise<EnrichedQueryJson> {
let datasource: Datasource | undefined = undefined let datasource: Datasource | undefined = undefined
let entityId = json.endpoint.entityId
if (typeof json.endpoint.datasource === "string") { if (typeof json.endpoint.datasourceId === "string") {
if (json.endpoint.datasource !== SQS_DATASOURCE_INTERNAL) { if (json.endpoint.datasourceId !== SQS_DATASOURCE_INTERNAL) {
datasource = await sdk.datasources.get(json.endpoint.datasource, { datasource = await sdk.datasources.get(json.endpoint.datasourceId, {
enriched: true, enriched: true,
}) })
} }
} else { } else {
datasource = json.endpoint.datasource datasource = json.endpoint.datasourceId
if (isDatasourceId(entityId)) {
entityId = breakExternalTableId(entityId).tableName
}
} }
let tables: Record<string, Table> let tables: Record<string, Table>
@ -111,7 +106,16 @@ export async function enrichQueryJson(
tables = processInternalTables(await sdk.tables.getAllInternalTables()) tables = processInternalTables(await sdk.tables.getAllInternalTables())
} }
const table = tables[entityId] let table: Table
if (typeof json.endpoint.entityId === "string") {
let entityId = json.endpoint.entityId
if (docIds.isDatasourceId(entityId)) {
entityId = sql.utils.breakExternalTableId(entityId).tableName
}
table = tables[entityId]
} else {
table = json.endpoint.entityId
}
return { return {
table, table,

View File

@ -241,7 +241,13 @@ export async function save(
} }
const operation = tableId ? Operation.UPDATE_TABLE : Operation.CREATE_TABLE const operation = tableId ? Operation.UPDATE_TABLE : Operation.CREATE_TABLE
await makeTableRequest(datasource, operation, tableToSave, opts?.renaming) await makeTableRequest(
datasource,
operation,
tableToSave,
oldTable,
opts?.renaming
)
// update any extra tables (like foreign keys in other tables) // update any extra tables (like foreign keys in other tables)
for (let extraTable of extraTablesToUpdate) { for (let extraTable of extraTablesToUpdate) {
const oldExtraTable = oldTables[extraTable.name] const oldExtraTable = oldTables[extraTable.name]

View File

@ -158,8 +158,8 @@ export interface ManyToManyRelationshipJson {
export interface QueryJson { export interface QueryJson {
endpoint: { endpoint: {
datasource: string | Datasource datasourceId: string | Datasource
entityId: string entityId: string | Table
operation: Operation operation: Operation
schema?: string schema?: string
} }
@ -173,6 +173,7 @@ export interface QueryJson {
body?: Row | Row[] body?: Row | Row[]
meta?: { meta?: {
renamed?: RenameColumn renamed?: RenameColumn
oldTable?: Table
// can specify something that columns could be prefixed with // can specify something that columns could be prefixed with
columnPrefix?: string columnPrefix?: string
} }
@ -190,7 +191,7 @@ export interface EnrichedQueryJson extends QueryJson {
} }
export interface QueryJsonRequest extends Omit<QueryJson, "endpoint"> { export interface QueryJsonRequest extends Omit<QueryJson, "endpoint"> {
endpoint: QueryJson["endpoint"] & { datasource: string } endpoint: QueryJson["endpoint"] & { datasourceId: string; entityId: string }
} }
export interface QueryOptions { export interface QueryOptions {