Mostly solving type errors around passing the view all the way down, got a fair few left.

This commit is contained in:
Sam Rose 2024-09-24 13:01:33 +01:00
parent 51774b3434
commit fc9b54cb85
No known key found for this signature in database
9 changed files with 89 additions and 70 deletions

View File

@ -42,6 +42,7 @@ export async function handleRequest<T extends Operation>(
export async function patch(ctx: UserCtx<PatchRowRequest, PatchRowResponse>) {
const source = await utils.getSource(ctx)
const table = await utils.getTableFromSource(source)
const { _id, ...rowData } = ctx.request.body
const { row: dataToUpdate } = await inputProcessing(
@ -58,11 +59,11 @@ export async function patch(ctx: UserCtx<PatchRowRequest, PatchRowResponse>) {
throw { validation: validateResult.errors }
}
const beforeRow = await sdk.rows.external.getRow(tableId, _id, {
const beforeRow = await sdk.rows.external.getRow(table._id!, _id, {
relationships: true,
})
const response = await handleRequest(Operation.UPDATE, tableId, {
const response = await handleRequest(Operation.UPDATE, source, {
id: breakRowIdField(_id),
row: dataToUpdate,
})
@ -70,7 +71,7 @@ export async function patch(ctx: UserCtx<PatchRowRequest, PatchRowResponse>) {
// The id might have been changed, so the refetching would fail. Recalculating the id just in case
const updatedId =
generateIdForRow({ ...beforeRow, ...dataToUpdate }, table) || _id
const row = await sdk.rows.external.getRow(tableId, updatedId, {
const row = await sdk.rows.external.getRow(table._id!, updatedId, {
relationships: true,
})
@ -78,7 +79,6 @@ export async function patch(ctx: UserCtx<PatchRowRequest, PatchRowResponse>) {
outputProcessing(table, row, {
squash: true,
preserveLinks: true,
fromViewId: viewId,
}),
outputProcessing(table, beforeRow, {
squash: true,
@ -95,9 +95,9 @@ export async function patch(ctx: UserCtx<PatchRowRequest, PatchRowResponse>) {
}
export async function destroy(ctx: UserCtx) {
const { tableId } = utils.getSourceId(ctx)
const source = await utils.getSource(ctx)
const _id = ctx.request.body._id
const { row } = await handleRequest(Operation.DELETE, tableId, {
const { row } = await handleRequest(Operation.DELETE, source, {
id: breakRowIdField(_id),
includeSqlRelationships: IncludeRelationship.EXCLUDE,
})
@ -106,11 +106,11 @@ export async function destroy(ctx: UserCtx) {
export async function bulkDestroy(ctx: UserCtx) {
const { rows } = ctx.request.body
const { tableId } = utils.getSourceId(ctx)
const source = await utils.getSource(ctx)
let promises: Promise<{ row: Row; table: Table }>[] = []
for (let row of rows) {
promises.push(
handleRequest(Operation.DELETE, tableId, {
handleRequest(Operation.DELETE, source, {
id: breakRowIdField(row._id),
includeSqlRelationships: IncludeRelationship.EXCLUDE,
})
@ -125,6 +125,7 @@ export async function bulkDestroy(ctx: UserCtx) {
export async function fetchEnrichedRow(ctx: UserCtx) {
const id = ctx.params.rowId
const source = await utils.getSource(ctx)
const { tableId } = utils.getSourceId(ctx)
const { datasourceId, tableName } = breakExternalTableId(tableId)
const datasource: Datasource = await sdk.datasources.get(datasourceId)
@ -132,7 +133,7 @@ export async function fetchEnrichedRow(ctx: UserCtx) {
ctx.throw(400, "Datasource has not been configured for plus API.")
}
const tables = datasource.entities
const response = await handleRequest(Operation.READ, tableId, {
const response = await handleRequest(Operation.READ, source, {
id,
datasource,
includeSqlRelationships: IncludeRelationship.INCLUDE,
@ -156,7 +157,7 @@ export async function fetchEnrichedRow(ctx: UserCtx) {
// don't support composite keys right now
const linkedIds = links.map((link: Row) => breakRowIdField(link._id!)[0])
const primaryLink = linkedTable.primary?.[0] as string
const relatedRows = await handleRequest(Operation.READ, linkedTableId!, {
const relatedRows = await handleRequest(Operation.READ, linkedTable, {
tables,
filters: {
oneOf: {

View File

@ -221,7 +221,7 @@ export async function search(ctx: Ctx<SearchRowRequest, SearchRowResponse>) {
const searchParams: RowSearchParams = {
...ctx.request.body,
query: enrichedQuery,
tableId,
sourceId: tableId,
}
ctx.status = 200
@ -229,14 +229,15 @@ export async function search(ctx: Ctx<SearchRowRequest, SearchRowResponse>) {
}
export async function validate(ctx: Ctx<Row, ValidateResponse>) {
const { tableId } = utils.getSourceId(ctx)
const source = await utils.getSource(ctx)
const table = await utils.getTableFromSource(source)
// external tables are hard to validate currently
if (isExternalTableID(tableId)) {
if (isExternalTableID(table._id!)) {
ctx.body = { valid: true, errors: {} }
} else {
ctx.body = await sdk.rows.utils.validate({
row: ctx.request.body,
tableId,
source,
})
}
}

View File

@ -106,19 +106,11 @@ export async function getSource(ctx: Ctx): Promise<Table | ViewV2> {
return sdk.tables.getTable(tableId)
}
export async function validate(
opts: { row: Row } & ({ tableId: string } | { table: Table })
) {
let fetchedTable: Table
if ("tableId" in opts) {
fetchedTable = await sdk.tables.getTable(opts.tableId)
} else {
fetchedTable = opts.table
export async function getTableFromSource(source: Table | ViewV2) {
if (sdk.views.isView(source)) {
return await sdk.views.getTable(source.id)
}
return sdk.rows.utils.validate({
...opts,
table: fetchedTable,
})
return source
}
function fixBooleanFields({ row, table }: { row: Row; table: Table }) {

View File

@ -1,5 +1,11 @@
import { IncludeRelationship, Operation, Row } from "@budibase/types"
import { HTTPError } from "@budibase/backend-core"
import {
IncludeRelationship,
Operation,
Row,
Table,
ViewV2,
} from "@budibase/types"
import { docIds, HTTPError } from "@budibase/backend-core"
import { handleRequest } from "../../../api/controllers/row/external"
import { breakRowIdField } from "../../../integrations/utils"
import sdk from "../../../sdk"
@ -12,11 +18,21 @@ import isEqual from "lodash/fp/isEqual"
import { tryExtractingTableAndViewId } from "./utils"
export async function getRow(
tableId: string,
sourceId: string | Table | ViewV2,
rowId: string,
opts?: { relationships?: boolean }
) {
const response = await handleRequest(Operation.READ, tableId, {
let source: Table | ViewV2
if (typeof sourceId === "string") {
if (docIds.isViewId(sourceId)) {
source = await sdk.views.get(sourceId)
} else {
source = await sdk.tables.getTable(sourceId)
}
} else {
source = sourceId
}
const response = await handleRequest(Operation.READ, source, {
id: breakRowIdField(rowId),
includeSqlRelationships: opts?.relationships
? IncludeRelationship.INCLUDE
@ -27,45 +43,50 @@ export async function getRow(
}
export async function save(
tableOrViewId: string,
sourceId: string,
inputs: Row,
userId: string | undefined
) {
const { tableId, viewId } = tryExtractingTableAndViewId(tableOrViewId)
const table = await sdk.tables.getTable(tableId)
const { tableId, viewId } = tryExtractingTableAndViewId(sourceId)
let source: Table | ViewV2
if (viewId) {
source = await sdk.views.get(viewId)
} else {
source = await sdk.tables.getTable(tableId)
}
const { table: updatedTable, row } = await inputProcessing(
userId,
cloneDeep(table),
cloneDeep(source),
inputs
)
const validateResult = await sdk.rows.utils.validate({
row,
tableId,
source,
})
if (!validateResult.valid) {
throw { validation: validateResult.errors }
}
const response = await handleRequest(Operation.CREATE, tableId, {
const response = await handleRequest(Operation.CREATE, source, {
row,
})
if (!isEqual(table, updatedTable)) {
if (sdk.tables.isTable(source) && !isEqual(source, updatedTable)) {
await sdk.tables.saveTable(updatedTable)
}
const rowId = response.row._id
if (rowId) {
const row = await getRow(tableId, rowId, {
const row = await getRow(source, rowId, {
relationships: true,
})
return {
...response,
row: await outputProcessing(table, row, {
row: await outputProcessing(source, row, {
preserveLinks: true,
squash: true,
fromViewId: viewId,
}),
}
} else {
@ -76,7 +97,14 @@ export async function save(
export async function find(tableOrViewId: string, rowId: string): Promise<Row> {
const { tableId, viewId } = tryExtractingTableAndViewId(tableOrViewId)
const row = await getRow(tableId, rowId, {
let source: Table | ViewV2
if (viewId) {
source = await sdk.views.get(viewId)
} else {
source = await sdk.tables.getTable(tableId)
}
const row = await getRow(source, rowId, {
relationships: true,
})
@ -84,11 +112,10 @@ export async function find(tableOrViewId: string, rowId: string): Promise<Row> {
throw new HTTPError("Row not found", 404)
}
const table = await sdk.tables.getTable(tableId)
// Preserving links, as the outputProcessing does not support external rows yet and we don't need it in this use case
return await outputProcessing(table, row, {
// Preserving links, as the outputProcessing does not support external rows
// yet and we don't need it in this use case
return await outputProcessing(source, row, {
squash: true,
preserveLinks: true,
fromViewId: viewId,
})
}

View File

@ -1,9 +1,6 @@
import { db as dbCore, context, docIds } from "@budibase/backend-core"
import { Database, Row } from "@budibase/types"
import {
extractViewInfoFromID,
getRowParams,
} from "../../../db/utils"
import { extractViewInfoFromID, getRowParams } from "../../../db/utils"
import { isExternalTableID } from "../../../integrations/utils"
import * as internal from "./internal"
import * as external from "./external"
@ -36,13 +33,13 @@ function pickApi(tableOrViewId: string) {
}
export async function save(
tableOrViewId: string,
sourceId: string,
row: Row,
userId: string | undefined
) {
return pickApi(tableOrViewId).save(tableOrViewId, row, userId)
return pickApi(sourceId).save(sourceId, row, userId)
}
export async function find(tableOrViewId: string, rowId: string) {
return pickApi(tableOrViewId).find(tableOrViewId, rowId)
export async function find(sourceId: string, rowId: string) {
return pickApi(sourceId).find(sourceId, rowId)
}

View File

@ -106,9 +106,9 @@ export async function search(
includeSqlRelationships: IncludeRelationship.INCLUDE,
}
const [{ rows, rawResponseSize }, totalRows] = await Promise.all([
handleRequest(Operation.READ, tableId, parameters),
handleRequest(Operation.READ, source, parameters),
countRows
? handleRequest(Operation.COUNT, tableId, parameters)
? handleRequest(Operation.COUNT, source, parameters)
: Promise.resolve(undefined),
])
@ -200,7 +200,7 @@ export async function exportRows(
}
let result = await search(
{ tableId, query: requestQuery, sort, sortOrder },
{ sourceId: table._id!, query: requestQuery, sort, sortOrder },
table
)
let rows: Row[] = []
@ -256,10 +256,10 @@ export async function exportRows(
}
export async function fetch(tableId: string): Promise<Row[]> {
const response = await handleRequest(Operation.READ, tableId, {
const table = await sdk.tables.getTable(tableId)
const response = await handleRequest(Operation.READ, table, {
includeSqlRelationships: IncludeRelationship.INCLUDE,
})
const table = await sdk.tables.getTable(tableId)
return await outputProcessing(table, response.rows, {
preserveLinks: true,
squash: true,
@ -267,7 +267,8 @@ export async function fetch(tableId: string): Promise<Row[]> {
}
export async function fetchRaw(tableId: string): Promise<Row[]> {
const response = await handleRequest(Operation.READ, tableId, {
const table = await sdk.tables.getTable(tableId)
const response = await handleRequest(Operation.READ, table, {
includeSqlRelationships: IncludeRelationship.INCLUDE,
})
return response.rows

View File

@ -9,7 +9,6 @@ import {
SearchResponse,
Row,
RowSearchParams,
ViewV2,
} from "@budibase/types"
import { db as dbCore, context } from "@budibase/backend-core"
import { utils } from "@budibase/shared-core"

View File

@ -21,6 +21,7 @@ import sdk from "../.."
import { extractViewInfoFromID, isRelationshipColumn } from "../../../db/utils"
import { isSQL } from "../../../integrations/utils"
import { docIds } from "@budibase/backend-core"
import { getTableFromSource } from "../../../api/controllers/row/utils"
const SQL_CLIENT_SOURCE_MAP: Record<SourceName, SqlClient | undefined> = {
[SourceName.POSTGRES]: SqlClient.POSTGRES,
@ -149,12 +150,7 @@ export async function validate({
valid: boolean
errors: Record<string, any>
}> {
let table: Table
if (sdk.views.isView(source)) {
table = await sdk.views.getTable(source.id)
} else {
table = source
}
const table = await getTableFromSource(source)
const errors: Record<string, any> = {}
const disallowArrayTypes = [
FieldType.ATTACHMENT_SINGLE,

View File

@ -19,6 +19,7 @@ import {
RowAttachment,
Table,
User,
ViewV2,
} from "@budibase/types"
import { cloneDeep } from "lodash/fp"
import {
@ -34,7 +35,11 @@ import {
PROTECTED_INTERNAL_COLUMNS,
} from "@budibase/shared-core"
import { processString } from "@budibase/string-templates"
import { isUserMetadataTable } from "../../api/controllers/row/utils"
import {
getTableFromSource,
isUserMetadataTable,
} from "../../api/controllers/row/utils"
import sdk from "../../sdk"
export * from "./utils"
export * from "./attachments"
@ -170,11 +175,12 @@ export function coerce(row: any, type: string) {
*/
export async function inputProcessing(
userId: string | null | undefined,
table: Table,
source: Table | ViewV2,
row: Row,
opts?: AutoColumnProcessingOpts
) {
const clonedRow = cloneDeep(row)
const table = await getTableFromSource(source)
const dontCleanseKeys = ["type", "_id", "_rev", "tableId"]
for (const [key, value] of Object.entries(clonedRow)) {
@ -243,14 +249,13 @@ export async function inputProcessing(
* @returns the enriched rows will be returned.
*/
export async function outputProcessing<T extends Row[] | Row>(
table: Table,
source: Table | ViewV2,
rows: T,
opts: {
squash?: boolean
preserveLinks?: boolean
fromRow?: Row
skipBBReferences?: boolean
fromViewId?: string
aggregations?: Aggregation[]
} = {
squash: true,