Fixing an issue with relationship modal breaking when multiple data sources available to relate to, also fixing an pile of issues with creating and reading rows from SQL server plus.
This commit is contained in:
parent
3474f3ae8e
commit
b34cef26c3
|
@ -59,9 +59,6 @@
|
||||||
let deletion
|
let deletion
|
||||||
|
|
||||||
$: checkConstraints(field)
|
$: checkConstraints(field)
|
||||||
$: tableOptions = $tables.list.filter(
|
|
||||||
opt => opt._id !== $tables.draft._id && opt.type === table.type
|
|
||||||
)
|
|
||||||
$: required = !!field?.constraints?.presence || primaryDisplay
|
$: required = !!field?.constraints?.presence || primaryDisplay
|
||||||
$: uneditable =
|
$: uneditable =
|
||||||
$tables.selected?._id === TableNames.USERS &&
|
$tables.selected?._id === TableNames.USERS &&
|
||||||
|
@ -88,6 +85,14 @@
|
||||||
field.type !== LINK_TYPE && !uneditable && field.type !== AUTO_TYPE
|
field.type !== LINK_TYPE && !uneditable && field.type !== AUTO_TYPE
|
||||||
$: relationshipOptions = getRelationshipOptions(field)
|
$: relationshipOptions = getRelationshipOptions(field)
|
||||||
$: external = table.type === "external"
|
$: external = table.type === "external"
|
||||||
|
// in the case of internal tables the sourceId will just be undefined
|
||||||
|
$: tableOptions = $tables.list.filter(
|
||||||
|
opt =>
|
||||||
|
opt._id !== $tables.draft._id &&
|
||||||
|
opt.type === table.type &&
|
||||||
|
table.sourceId === opt.sourceId
|
||||||
|
)
|
||||||
|
$: console.log(tableOptions)
|
||||||
|
|
||||||
async function saveColumn() {
|
async function saveColumn() {
|
||||||
if (field.type === AUTO_TYPE) {
|
if (field.type === AUTO_TYPE) {
|
||||||
|
@ -174,7 +179,7 @@
|
||||||
if (!field || !field.tableId) {
|
if (!field || !field.tableId) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
const linkTable = tableOptions.find(table => table._id === field.tableId)
|
const linkTable = tableOptions?.find(table => table._id === field.tableId)
|
||||||
if (!linkTable) {
|
if (!linkTable) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,7 +119,7 @@ export interface SortJson {
|
||||||
|
|
||||||
export interface PaginationJson {
|
export interface PaginationJson {
|
||||||
limit: number
|
limit: number
|
||||||
page: string | number
|
page?: string | number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RelationshipsJson {
|
export interface RelationshipsJson {
|
||||||
|
|
|
@ -216,6 +216,10 @@ class InternalBuilder {
|
||||||
query = query.orderBy(key, direction)
|
query = query.orderBy(key, direction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (this.client === "mssql" && !sort && paginate?.limit) {
|
||||||
|
// @ts-ignore
|
||||||
|
query = query.orderBy(json.meta?.table?.primary[0])
|
||||||
|
}
|
||||||
query = this.addFilters(tableName, query, filters)
|
query = this.addFilters(tableName, query, filters)
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
let preQuery: KnexQuery = knex({
|
let preQuery: KnexQuery = knex({
|
||||||
|
@ -301,6 +305,85 @@ class SqlQueryBuilder extends SqlTableQueryBuilder {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return query.toSQL().toNative()
|
return query.toSQL().toNative()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getReturningRow(queryFn: Function, json: QueryJson) {
|
||||||
|
if (!json.extra || !json.extra.idFilter) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
const input = this._query({
|
||||||
|
endpoint: {
|
||||||
|
...json.endpoint,
|
||||||
|
operation: Operation.READ,
|
||||||
|
},
|
||||||
|
resource: {
|
||||||
|
fields: [],
|
||||||
|
},
|
||||||
|
filters: json.extra.idFilter,
|
||||||
|
paginate: {
|
||||||
|
limit: 1,
|
||||||
|
},
|
||||||
|
meta: json.meta,
|
||||||
|
})
|
||||||
|
return queryFn(input, Operation.READ)
|
||||||
|
}
|
||||||
|
|
||||||
|
// when creating if an ID has been inserted need to make sure
|
||||||
|
// the id filter is enriched with it before trying to retrieve the row
|
||||||
|
checkLookupKeys(id: any, json: QueryJson) {
|
||||||
|
if (!id || !json.meta?.table || !json.meta.table.primary) {
|
||||||
|
return json
|
||||||
|
}
|
||||||
|
const primaryKey = json.meta.table.primary?.[0]
|
||||||
|
json.extra = {
|
||||||
|
idFilter: {
|
||||||
|
equal: {
|
||||||
|
[primaryKey]: id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return json
|
||||||
|
}
|
||||||
|
|
||||||
|
// this function recreates the returning functionality of postgres
|
||||||
|
async queryWithReturning(
|
||||||
|
json: QueryJson,
|
||||||
|
queryFn: Function,
|
||||||
|
processFn: Function = (result: any) => result
|
||||||
|
) {
|
||||||
|
const sqlClient = this.getSqlClient()
|
||||||
|
const operation = this._operation(json)
|
||||||
|
const input = this._query(json, { disableReturning: true })
|
||||||
|
if (Array.isArray(input)) {
|
||||||
|
const responses = []
|
||||||
|
for (let query of input) {
|
||||||
|
responses.push(await queryFn(query, operation))
|
||||||
|
}
|
||||||
|
return responses
|
||||||
|
}
|
||||||
|
let row
|
||||||
|
// need to manage returning, a feature mySQL can't do
|
||||||
|
if (operation === Operation.DELETE) {
|
||||||
|
row = processFn(await this.getReturningRow(queryFn, json))
|
||||||
|
}
|
||||||
|
const response = await queryFn(input, operation)
|
||||||
|
const results = processFn(response)
|
||||||
|
// same as delete, manage returning
|
||||||
|
if (operation === Operation.CREATE || operation === Operation.UPDATE) {
|
||||||
|
let id
|
||||||
|
if (sqlClient === "mssql") {
|
||||||
|
id = results?.[0].id
|
||||||
|
} else if (sqlClient === "mysql") {
|
||||||
|
id = results?.insertId
|
||||||
|
}
|
||||||
|
row = processFn(
|
||||||
|
await this.getReturningRow(queryFn, this.checkLookupKeys(id, json))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (operation !== Operation.READ) {
|
||||||
|
return row
|
||||||
|
}
|
||||||
|
return results.length ? results : [{ [operation.toLowerCase()]: true }]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = SqlQueryBuilder
|
module.exports = SqlQueryBuilder
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import {
|
import {
|
||||||
Integration,
|
|
||||||
DatasourceFieldTypes,
|
DatasourceFieldTypes,
|
||||||
QueryTypes,
|
Integration,
|
||||||
|
Operation,
|
||||||
QueryJson,
|
QueryJson,
|
||||||
|
QueryTypes,
|
||||||
SqlQuery,
|
SqlQuery,
|
||||||
} from "../definitions/datasource"
|
} from "../definitions/datasource"
|
||||||
import { getSqlQuery } from "./utils"
|
import { getSqlQuery } from "./utils"
|
||||||
|
@ -103,15 +104,26 @@ module MSSQLModule {
|
||||||
json: DatasourceFieldTypes.JSON,
|
json: DatasourceFieldTypes.JSON,
|
||||||
}
|
}
|
||||||
|
|
||||||
async function internalQuery(client: any, query: SqlQuery) {
|
async function internalQuery(
|
||||||
|
client: any,
|
||||||
|
query: SqlQuery,
|
||||||
|
operation: string | undefined = undefined
|
||||||
|
) {
|
||||||
|
const request = client.request()
|
||||||
try {
|
try {
|
||||||
if (Array.isArray(query.bindings)) {
|
if (Array.isArray(query.bindings)) {
|
||||||
let count = 0
|
let count = 0
|
||||||
for (let binding of query.bindings) {
|
for (let binding of query.bindings) {
|
||||||
client.input(`p${count++}`, binding)
|
request.input(`p${count++}`, binding)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return await client.query(query.sql)
|
// this is a hack to get the inserted ID back,
|
||||||
|
// no way to do this with Knex nicely
|
||||||
|
const sql =
|
||||||
|
operation === Operation.CREATE
|
||||||
|
? `${query.sql}; SELECT SCOPE_IDENTITY() AS id;`
|
||||||
|
: query.sql
|
||||||
|
return await request.query(sql)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
throw new Error(err)
|
throw new Error(err)
|
||||||
|
@ -180,8 +192,7 @@ module MSSQLModule {
|
||||||
|
|
||||||
async connect() {
|
async connect() {
|
||||||
try {
|
try {
|
||||||
const client = await this.pool.connect()
|
this.client = await this.pool.connect()
|
||||||
this.client = client.request()
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
throw new Error(err)
|
throw new Error(err)
|
||||||
|
@ -276,10 +287,12 @@ module MSSQLModule {
|
||||||
|
|
||||||
async query(json: QueryJson) {
|
async query(json: QueryJson) {
|
||||||
await this.connect()
|
await this.connect()
|
||||||
const operation = this._operation(json).toLowerCase()
|
const operation = this._operation(json)
|
||||||
const input = this._query(json)
|
const queryFn = (query: any, op: string) =>
|
||||||
const response = await internalQuery(this.client, input)
|
internalQuery(this.client, query, op)
|
||||||
return response.recordset ? response.recordset : [{ [operation]: true }]
|
const processFn = (result: any) =>
|
||||||
|
result.recordset ? result.recordset : [{ [operation]: true }]
|
||||||
|
return this.queryWithReturning(json, queryFn, processFn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -223,67 +223,12 @@ module MySQLModule {
|
||||||
return results.length ? results : [{ deleted: true }]
|
return results.length ? results : [{ deleted: true }]
|
||||||
}
|
}
|
||||||
|
|
||||||
async getReturningRow(json: QueryJson) {
|
|
||||||
if (!json.extra || !json.extra.idFilter) {
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
const input = this._query({
|
|
||||||
endpoint: {
|
|
||||||
...json.endpoint,
|
|
||||||
operation: Operation.READ,
|
|
||||||
},
|
|
||||||
fields: [],
|
|
||||||
filters: json.extra.idFilter,
|
|
||||||
paginate: {
|
|
||||||
limit: 1,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
return internalQuery(this.client, input, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// when creating if an ID has been inserted need to make sure
|
|
||||||
// the id filter is enriched with it before trying to retrieve the row
|
|
||||||
checkLookupKeys(results: any, json: QueryJson) {
|
|
||||||
if (!results?.insertId || !json.meta?.table || !json.meta.table.primary) {
|
|
||||||
return json
|
|
||||||
}
|
|
||||||
const primaryKey = json.meta.table.primary?.[0]
|
|
||||||
json.extra = {
|
|
||||||
idFilter: {
|
|
||||||
equal: {
|
|
||||||
[primaryKey]: results.insertId,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return json
|
|
||||||
}
|
|
||||||
|
|
||||||
async query(json: QueryJson) {
|
async query(json: QueryJson) {
|
||||||
const operation = this._operation(json)
|
|
||||||
this.client.connect()
|
this.client.connect()
|
||||||
const input = this._query(json, { disableReturning: true })
|
const queryFn = (query: any) => internalQuery(this.client, query, false)
|
||||||
if (Array.isArray(input)) {
|
const output = await this.queryWithReturning(json, queryFn)
|
||||||
const responses = []
|
|
||||||
for (let query of input) {
|
|
||||||
responses.push(await internalQuery(this.client, query, false))
|
|
||||||
}
|
|
||||||
return responses
|
|
||||||
}
|
|
||||||
let row
|
|
||||||
// need to manage returning, a feature mySQL can't do
|
|
||||||
if (operation === operation.DELETE) {
|
|
||||||
row = this.getReturningRow(json)
|
|
||||||
}
|
|
||||||
const results = await internalQuery(this.client, input, false)
|
|
||||||
// same as delete, manage returning
|
|
||||||
if (operation === Operation.CREATE || operation === Operation.UPDATE) {
|
|
||||||
row = this.getReturningRow(this.checkLookupKeys(results, json))
|
|
||||||
}
|
|
||||||
this.client.end()
|
this.client.end()
|
||||||
if (operation !== Operation.READ) {
|
return output
|
||||||
return row
|
|
||||||
}
|
|
||||||
return results.length ? results : [{ [operation.toLowerCase()]: true }]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { SqlQuery } from "../definitions/datasource"
|
import { Operation, SqlQuery } from "../definitions/datasource"
|
||||||
import { Datasource, Table } from "../definitions/common"
|
import { Datasource, Table } from "../definitions/common"
|
||||||
import { SourceNames } from "../definitions/datasource"
|
import { SourceNames } from "../definitions/datasource"
|
||||||
const { DocumentTypes, SEPARATOR } = require("../db/utils")
|
const { DocumentTypes, SEPARATOR } = require("../db/utils")
|
||||||
|
|
Loading…
Reference in New Issue