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

View File

@ -221,7 +221,7 @@ export async function search(ctx: Ctx<SearchRowRequest, SearchRowResponse>) {
const searchParams: RowSearchParams = { const searchParams: RowSearchParams = {
...ctx.request.body, ...ctx.request.body,
query: enrichedQuery, query: enrichedQuery,
tableId, sourceId: tableId,
} }
ctx.status = 200 ctx.status = 200
@ -229,14 +229,15 @@ export async function search(ctx: Ctx<SearchRowRequest, SearchRowResponse>) {
} }
export async function validate(ctx: Ctx<Row, ValidateResponse>) { 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 // external tables are hard to validate currently
if (isExternalTableID(tableId)) { if (isExternalTableID(table._id!)) {
ctx.body = { valid: true, errors: {} } ctx.body = { valid: true, errors: {} }
} else { } else {
ctx.body = await sdk.rows.utils.validate({ ctx.body = await sdk.rows.utils.validate({
row: ctx.request.body, 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) return sdk.tables.getTable(tableId)
} }
export async function validate( export async function getTableFromSource(source: Table | ViewV2) {
opts: { row: Row } & ({ tableId: string } | { table: Table }) if (sdk.views.isView(source)) {
) { return await sdk.views.getTable(source.id)
let fetchedTable: Table
if ("tableId" in opts) {
fetchedTable = await sdk.tables.getTable(opts.tableId)
} else {
fetchedTable = opts.table
} }
return sdk.rows.utils.validate({ return source
...opts,
table: fetchedTable,
})
} }
function fixBooleanFields({ row, table }: { row: Row; table: Table }) { function fixBooleanFields({ row, table }: { row: Row; table: Table }) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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