1
0
Fork 0
mirror of synced 2024-10-03 10:36:59 +13:00

Fixing response types of DS+ query function.

This commit is contained in:
mike12345567 2024-02-26 17:56:28 +00:00
parent 45d2e67905
commit cb19e1f24c
10 changed files with 67 additions and 25 deletions

View file

@ -332,7 +332,7 @@ export class ExternalRequest<T extends Operation> {
endpoint: getEndpoint(table._id!, Operation.READ), endpoint: getEndpoint(table._id!, Operation.READ),
filters: buildFilters(rowId, {}, table), filters: buildFilters(rowId, {}, table),
}) })
if (response.length > 0) { if (Array.isArray(response)) {
return response[0] return response[0]
} else { } else {
throw new Error(`Cannot fetch row by ID "${rowId}"`) throw new Error(`Cannot fetch row by ID "${rowId}"`)
@ -646,7 +646,7 @@ export class ExternalRequest<T extends Operation> {
}, },
}) })
// this is the response from knex if no rows found // this is the response from knex if no rows found
const rows: Row[] = !response[0].read ? response : [] const rows: Row[] = response?.[0].read ? [] : (response as Row[])
const storeTo = isMany ? field.throughFrom || linkPrimaryKey : fieldName const storeTo = isMany ? field.throughFrom || linkPrimaryKey : fieldName
related[storeTo] = { rows, isMany, tableId } related[storeTo] = { rows, isMany, tableId }
} }
@ -899,15 +899,16 @@ export class ExternalRequest<T extends Operation> {
response = await getDatasourceAndQuery(json) response = await getDatasourceAndQuery(json)
} }
const responseRows = Array.isArray(response) ? response : []
// handle many-to-many relationships now if we know the ID (could be auto increment) // handle many-to-many relationships now if we know the ID (could be auto increment)
if (operation !== Operation.READ) { if (operation !== Operation.READ) {
await this.handleManyRelationships( await this.handleManyRelationships(
table._id || "", table._id || "",
response[0], responseRows[0],
processed.manyRelationships processed.manyRelationships
) )
} }
const output = this.outputProcessing(response, table, relationships) const output = this.outputProcessing(responseRows, table, relationships)
// if reading it'll just be an array of rows, return whole thing // if reading it'll just be an array of rows, return whole thing
if (operation === Operation.READ) { if (operation === Operation.READ) {
return ( return (

View file

@ -1,4 +1,10 @@
import { QueryJson, SearchFilters, Table, Row } from "@budibase/types" import {
QueryJson,
SearchFilters,
Table,
Row,
DatasourcePlusQueryResponse,
} from "@budibase/types"
import { getDatasourceAndQuery } from "../../../sdk/app/rows/utils" import { getDatasourceAndQuery } from "../../../sdk/app/rows/utils"
import { cloneDeep } from "lodash" import { cloneDeep } from "lodash"
@ -68,9 +74,8 @@ export default class AliasTables {
return map return map
} }
async queryWithAliasing(json: QueryJson) { async queryWithAliasing(json: QueryJson): DatasourcePlusQueryResponse {
json = cloneDeep(json) json = cloneDeep(json)
const aliasField = (field: string) => this.aliasField(field)
const aliasTable = (table: Table) => ({ const aliasTable = (table: Table) => ({
...table, ...table,
name: this.getAlias(table.name), name: this.getAlias(table.name),
@ -78,7 +83,7 @@ export default class AliasTables {
// run through the query json to update anywhere a table may be used // run through the query json to update anywhere a table may be used
if (json.resource?.fields) { if (json.resource?.fields) {
json.resource.fields = json.resource.fields.map(field => json.resource.fields = json.resource.fields.map(field =>
aliasField(field) this.aliasField(field)
) )
} }
if (json.filters) { if (json.filters) {
@ -88,7 +93,7 @@ export default class AliasTables {
} }
const aliasedFilters: typeof filter = {} const aliasedFilters: typeof filter = {}
for (let key of Object.keys(filter)) { for (let key of Object.keys(filter)) {
aliasedFilters[aliasField(key)] = filter[key] aliasedFilters[this.aliasField(key)] = filter[key]
} }
json.filters[filterKey as keyof SearchFilters] = aliasedFilters json.filters[filterKey as keyof SearchFilters] = aliasedFilters
} }
@ -120,6 +125,10 @@ export default class AliasTables {
} }
json.tableAliases = invertedTableAliases json.tableAliases = invertedTableAliases
const response = await getDatasourceAndQuery(json) const response = await getDatasourceAndQuery(json)
return this.reverse(response) if (Array.isArray(response)) {
return this.reverse(response)
} else {
return response
}
} }
} }

View file

@ -1,11 +1,15 @@
import { QueryJson, Datasource } from "@budibase/types" import {
QueryJson,
Datasource,
DatasourcePlusQueryResponse,
} from "@budibase/types"
import { getIntegration } from "../index" import { getIntegration } from "../index"
import sdk from "../../sdk" import sdk from "../../sdk"
export async function makeExternalQuery( export async function makeExternalQuery(
datasource: Datasource, datasource: Datasource,
json: QueryJson json: QueryJson
) { ): DatasourcePlusQueryResponse {
datasource = await sdk.datasources.enrich(datasource) datasource = await sdk.datasources.enrich(datasource)
const Integration = await getIntegration(datasource.source) const Integration = await getIntegration(datasource.source)
// query is the opinionated function // query is the opinionated function

View file

@ -16,6 +16,7 @@ import {
Table, Table,
TableRequest, TableRequest,
TableSourceType, TableSourceType,
DatasourcePlusQueryResponse,
} from "@budibase/types" } from "@budibase/types"
import { OAuth2Client } from "google-auth-library" import { OAuth2Client } from "google-auth-library"
import { import {
@ -334,7 +335,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
return { tables: externalTables, errors } return { tables: externalTables, errors }
} }
async query(json: QueryJson) { async query(json: QueryJson): DatasourcePlusQueryResponse {
const sheet = json.endpoint.entityId const sheet = json.endpoint.entityId
switch (json.endpoint.operation) { switch (json.endpoint.operation) {
case Operation.CREATE: case Operation.CREATE:
@ -384,7 +385,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
} }
try { try {
await this.connect() await this.connect()
return await this.client.addSheet({ title: name, headerValues: [name] }) await this.client.addSheet({ title: name, headerValues: [name] })
} catch (err) { } catch (err) {
console.error("Error creating new table in google sheets", err) console.error("Error creating new table in google sheets", err)
throw err throw err
@ -450,7 +451,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
try { try {
await this.connect() await this.connect()
const sheetToDelete = this.client.sheetsByTitle[sheet] const sheetToDelete = this.client.sheetsByTitle[sheet]
return await sheetToDelete.delete() await sheetToDelete.delete()
} catch (err) { } catch (err) {
console.error("Error deleting table in google sheets", err) console.error("Error deleting table in google sheets", err)
throw err throw err

View file

@ -13,6 +13,7 @@ import {
SourceName, SourceName,
Schema, Schema,
TableSourceType, TableSourceType,
DatasourcePlusQueryResponse,
} from "@budibase/types" } from "@budibase/types"
import { import {
getSqlQuery, getSqlQuery,
@ -493,7 +494,7 @@ class SqlServerIntegration extends Sql implements DatasourcePlus {
return response.recordset || [{ deleted: true }] return response.recordset || [{ deleted: true }]
} }
async query(json: QueryJson) { async query(json: QueryJson): DatasourcePlusQueryResponse {
const schema = this.config.schema const schema = this.config.schema
await this.connect() await this.connect()
if (schema && schema !== DEFAULT_SCHEMA && json?.endpoint) { if (schema && schema !== DEFAULT_SCHEMA && json?.endpoint) {

View file

@ -12,7 +12,7 @@ import {
SourceName, SourceName,
Schema, Schema,
TableSourceType, TableSourceType,
FieldType, DatasourcePlusQueryResponse,
} from "@budibase/types" } from "@budibase/types"
import { import {
getSqlQuery, getSqlQuery,
@ -381,7 +381,7 @@ class MySQLIntegration extends Sql implements DatasourcePlus {
return results.length ? results : [{ deleted: true }] return results.length ? results : [{ deleted: true }]
} }
async query(json: QueryJson) { async query(json: QueryJson): DatasourcePlusQueryResponse {
await this.connect() await this.connect()
try { try {
const queryFn = (query: any) => const queryFn = (query: any) =>

View file

@ -12,6 +12,8 @@ import {
ConnectionInfo, ConnectionInfo,
Schema, Schema,
TableSourceType, TableSourceType,
Row,
DatasourcePlusQueryResponse,
} from "@budibase/types" } from "@budibase/types"
import { import {
buildExternalTableId, buildExternalTableId,
@ -420,7 +422,7 @@ class OracleIntegration extends Sql implements DatasourcePlus {
: [{ deleted: true }] : [{ deleted: true }]
} }
async query(json: QueryJson) { async query(json: QueryJson): DatasourcePlusQueryResponse {
const operation = this._operation(json) const operation = this._operation(json)
const input = this._query(json, { disableReturning: true }) const input = this._query(json, { disableReturning: true })
if (Array.isArray(input)) { if (Array.isArray(input)) {
@ -444,7 +446,7 @@ class OracleIntegration extends Sql implements DatasourcePlus {
if (deletedRows?.rows?.length) { if (deletedRows?.rows?.length) {
return deletedRows.rows return deletedRows.rows
} else if (response.rows?.length) { } else if (response.rows?.length) {
return response.rows return response.rows as Row[]
} else { } else {
// get the last row that was updated // get the last row that was updated
if ( if (
@ -455,7 +457,7 @@ class OracleIntegration extends Sql implements DatasourcePlus {
const lastRow = await this.internalQuery({ const lastRow = await this.internalQuery({
sql: `SELECT * FROM \"${json.endpoint.entityId}\" WHERE ROWID = '${response.lastRowid}'`, sql: `SELECT * FROM \"${json.endpoint.entityId}\" WHERE ROWID = '${response.lastRowid}'`,
}) })
return lastRow.rows return lastRow.rows as Row[]
} else { } else {
return [{ [operation.toLowerCase()]: true }] return [{ [operation.toLowerCase()]: true }]
} }

View file

@ -12,6 +12,7 @@ import {
SourceName, SourceName,
Schema, Schema,
TableSourceType, TableSourceType,
DatasourcePlusQueryResponse,
} from "@budibase/types" } from "@budibase/types"
import { import {
getSqlQuery, getSqlQuery,
@ -419,7 +420,7 @@ class PostgresIntegration extends Sql implements DatasourcePlus {
return response.rows.length ? response.rows : [{ deleted: true }] return response.rows.length ? response.rows : [{ deleted: true }]
} }
async query(json: QueryJson) { async query(json: QueryJson): DatasourcePlusQueryResponse {
const operation = this._operation(json).toLowerCase() const operation = this._operation(json).toLowerCase()
const input = this._query(json) const input = this._query(json)
if (Array.isArray(input)) { if (Array.isArray(input)) {

View file

@ -1,12 +1,21 @@
import cloneDeep from "lodash/cloneDeep" import cloneDeep from "lodash/cloneDeep"
import validateJs from "validate.js" import validateJs from "validate.js"
import { FieldType, QueryJson, Row, Table, TableSchema } from "@budibase/types" import {
FieldType,
QueryJson,
Row,
Table,
TableSchema,
DatasourcePlusQueryResponse,
} from "@budibase/types"
import { makeExternalQuery } from "../../../integrations/base/query" import { makeExternalQuery } from "../../../integrations/base/query"
import { Format } from "../../../api/controllers/view/exporters" import { Format } from "../../../api/controllers/view/exporters"
import sdk from "../.." import sdk from "../.."
import { isRelationshipColumn } from "../../../db/utils" import { isRelationshipColumn } from "../../../db/utils"
export async function getDatasourceAndQuery(json: QueryJson) { export async function getDatasourceAndQuery(
json: QueryJson
): DatasourcePlusQueryResponse {
const datasourceId = json.endpoint.datasourceId const datasourceId = json.endpoint.datasourceId
const datasource = await sdk.datasources.get(datasourceId) const datasource = await sdk.datasources.get(datasourceId)
return makeExternalQuery(datasource, json) return makeExternalQuery(datasource, json)

View file

@ -1,4 +1,5 @@
import { Table } from "../documents" import { Table, Row } from "../documents"
import { QueryJson } from "./search"
export const PASSWORD_REPLACEMENT = "--secret-value--" export const PASSWORD_REPLACEMENT = "--secret-value--"
@ -180,11 +181,24 @@ export interface Schema {
errors: Record<string, string> errors: Record<string, string>
} }
// return these when an operation occurred but we got no response
enum DSPlusOperation {
CREATE = "create",
READ = "read",
UPDATE = "update",
DELETE = "delete",
}
export type DatasourcePlusQueryResponse = Promise<
Row[] | Record<DSPlusOperation, boolean>[] | void
>
export interface DatasourcePlus extends IntegrationBase { export interface DatasourcePlus extends IntegrationBase {
// if the datasource supports the use of bindings directly (to protect against SQL injection) // if the datasource supports the use of bindings directly (to protect against SQL injection)
// this returns the format of the identifier // this returns the format of the identifier
getBindingIdentifier(): string getBindingIdentifier(): string
getStringConcat(parts: string[]): string getStringConcat(parts: string[]): string
query(json: QueryJson): DatasourcePlusQueryResponse
buildSchema( buildSchema(
datasourceId: string, datasourceId: string,
entities: Record<string, Table> entities: Record<string, Table>