Merge branch 'master' of github.com:Budibase/budibase into fix/sql-many-relationships
This commit is contained in:
commit
413628ca3f
|
@ -1 +1 @@
|
||||||
Subproject commit 14c89a5b20ee4de07723063458a2437b0dd4f2c9
|
Subproject commit 51a83b791a7a11b1d51c1fdb91f2ac246298279e
|
|
@ -38,7 +38,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 tableId = utils.getTableId(ctx)
|
const { tableId } = utils.getSourceId(ctx)
|
||||||
|
|
||||||
const { _id, ...rowData } = ctx.request.body
|
const { _id, ...rowData } = ctx.request.body
|
||||||
const table = await sdk.tables.getTable(tableId)
|
const table = await sdk.tables.getTable(tableId)
|
||||||
|
@ -93,7 +93,7 @@ export async function patch(ctx: UserCtx<PatchRowRequest, PatchRowResponse>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function destroy(ctx: UserCtx) {
|
export async function destroy(ctx: UserCtx) {
|
||||||
const tableId = utils.getTableId(ctx)
|
const { tableId } = utils.getSourceId(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, tableId, {
|
||||||
id: breakRowIdField(_id),
|
id: breakRowIdField(_id),
|
||||||
|
@ -104,7 +104,7 @@ 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.getTableId(ctx)
|
const { tableId } = utils.getSourceId(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(
|
||||||
|
@ -123,7 +123,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 tableId = utils.getTableId(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)
|
||||||
if (!datasource || !datasource.entities) {
|
if (!datasource || !datasource.entities) {
|
||||||
|
|
|
@ -47,7 +47,7 @@ export async function patch(
|
||||||
ctx: UserCtx<PatchRowRequest, PatchRowResponse>
|
ctx: UserCtx<PatchRowRequest, PatchRowResponse>
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
const appId = ctx.appId
|
const appId = ctx.appId
|
||||||
const tableId = utils.getTableId(ctx)
|
const { tableId } = utils.getSourceId(ctx)
|
||||||
const body = ctx.request.body
|
const body = ctx.request.body
|
||||||
|
|
||||||
// if it doesn't have an _id then its save
|
// if it doesn't have an _id then its save
|
||||||
|
@ -72,7 +72,7 @@ export async function patch(
|
||||||
|
|
||||||
export const save = async (ctx: UserCtx<Row, Row>) => {
|
export const save = async (ctx: UserCtx<Row, Row>) => {
|
||||||
const appId = ctx.appId
|
const appId = ctx.appId
|
||||||
const tableId = utils.getTableId(ctx)
|
const { tableId } = utils.getSourceId(ctx)
|
||||||
const body = ctx.request.body
|
const body = ctx.request.body
|
||||||
|
|
||||||
// user metadata doesn't exist yet - don't allow creation
|
// user metadata doesn't exist yet - don't allow creation
|
||||||
|
@ -97,13 +97,12 @@ export const save = async (ctx: UserCtx<Row, Row>) => {
|
||||||
gridSocket?.emitRowUpdate(ctx, row || squashed)
|
gridSocket?.emitRowUpdate(ctx, row || squashed)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchView(ctx: any) {
|
export async function fetchLegacyView(ctx: any) {
|
||||||
const tableId = utils.getTableId(ctx)
|
|
||||||
const viewName = decodeURIComponent(ctx.params.viewName)
|
const viewName = decodeURIComponent(ctx.params.viewName)
|
||||||
|
|
||||||
const { calculation, group, field } = ctx.query
|
const { calculation, group, field } = ctx.query
|
||||||
|
|
||||||
ctx.body = await sdk.rows.fetchView(tableId, viewName, {
|
ctx.body = await sdk.rows.fetchLegacyView(viewName, {
|
||||||
calculation,
|
calculation,
|
||||||
group: calculation ? group : null,
|
group: calculation ? group : null,
|
||||||
field,
|
field,
|
||||||
|
@ -111,12 +110,12 @@ export async function fetchView(ctx: any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetch(ctx: any) {
|
export async function fetch(ctx: any) {
|
||||||
const tableId = utils.getTableId(ctx)
|
const { tableId } = utils.getSourceId(ctx)
|
||||||
ctx.body = await sdk.rows.fetch(tableId)
|
ctx.body = await sdk.rows.fetch(tableId)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function find(ctx: UserCtx<void, GetRowResponse>) {
|
export async function find(ctx: UserCtx<void, GetRowResponse>) {
|
||||||
const tableId = utils.getTableId(ctx)
|
const { tableId } = utils.getSourceId(ctx)
|
||||||
const rowId = ctx.params.rowId
|
const rowId = ctx.params.rowId
|
||||||
|
|
||||||
ctx.body = await sdk.rows.find(tableId, rowId)
|
ctx.body = await sdk.rows.find(tableId, rowId)
|
||||||
|
@ -132,7 +131,7 @@ function isDeleteRow(input: any): input is DeleteRow {
|
||||||
|
|
||||||
async function processDeleteRowsRequest(ctx: UserCtx<DeleteRowRequest>) {
|
async function processDeleteRowsRequest(ctx: UserCtx<DeleteRowRequest>) {
|
||||||
let request = ctx.request.body as DeleteRows
|
let request = ctx.request.body as DeleteRows
|
||||||
const tableId = utils.getTableId(ctx)
|
const { tableId } = utils.getSourceId(ctx)
|
||||||
|
|
||||||
const processedRows = request.rows.map(row => {
|
const processedRows = request.rows.map(row => {
|
||||||
let processedRow: Row = typeof row == "string" ? { _id: row } : row
|
let processedRow: Row = typeof row == "string" ? { _id: row } : row
|
||||||
|
@ -148,7 +147,7 @@ async function processDeleteRowsRequest(ctx: UserCtx<DeleteRowRequest>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteRows(ctx: UserCtx<DeleteRowRequest>) {
|
async function deleteRows(ctx: UserCtx<DeleteRowRequest>) {
|
||||||
const tableId = utils.getTableId(ctx)
|
const { tableId } = utils.getSourceId(ctx)
|
||||||
const appId = ctx.appId
|
const appId = ctx.appId
|
||||||
|
|
||||||
let deleteRequest = ctx.request.body as DeleteRows
|
let deleteRequest = ctx.request.body as DeleteRows
|
||||||
|
@ -170,7 +169,7 @@ async function deleteRows(ctx: UserCtx<DeleteRowRequest>) {
|
||||||
|
|
||||||
async function deleteRow(ctx: UserCtx<DeleteRowRequest>) {
|
async function deleteRow(ctx: UserCtx<DeleteRowRequest>) {
|
||||||
const appId = ctx.appId
|
const appId = ctx.appId
|
||||||
const tableId = utils.getTableId(ctx)
|
const { tableId } = utils.getSourceId(ctx)
|
||||||
|
|
||||||
const resp = await pickApi(tableId).destroy(ctx)
|
const resp = await pickApi(tableId).destroy(ctx)
|
||||||
if (!tableId.includes("datasource_plus")) {
|
if (!tableId.includes("datasource_plus")) {
|
||||||
|
@ -204,7 +203,7 @@ export async function destroy(ctx: UserCtx<DeleteRowRequest>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function search(ctx: Ctx<SearchRowRequest, SearchRowResponse>) {
|
export async function search(ctx: Ctx<SearchRowRequest, SearchRowResponse>) {
|
||||||
const tableId = utils.getTableId(ctx)
|
const { tableId } = utils.getSourceId(ctx)
|
||||||
|
|
||||||
await context.ensureSnippetContext(true)
|
await context.ensureSnippetContext(true)
|
||||||
|
|
||||||
|
@ -226,7 +225,7 @@ 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.getTableId(ctx)
|
const { tableId } = utils.getSourceId(ctx)
|
||||||
// external tables are hard to validate currently
|
// external tables are hard to validate currently
|
||||||
if (isExternalTableID(tableId)) {
|
if (isExternalTableID(tableId)) {
|
||||||
ctx.body = { valid: true, errors: {} }
|
ctx.body = { valid: true, errors: {} }
|
||||||
|
@ -239,14 +238,14 @@ export async function validate(ctx: Ctx<Row, ValidateResponse>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchEnrichedRow(ctx: UserCtx<void, Row>) {
|
export async function fetchEnrichedRow(ctx: UserCtx<void, Row>) {
|
||||||
const tableId = utils.getTableId(ctx)
|
const { tableId } = utils.getSourceId(ctx)
|
||||||
ctx.body = await pickApi(tableId).fetchEnrichedRow(ctx)
|
ctx.body = await pickApi(tableId).fetchEnrichedRow(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const exportRows = async (
|
export const exportRows = async (
|
||||||
ctx: Ctx<ExportRowsRequest, ExportRowsResponse>
|
ctx: Ctx<ExportRowsRequest, ExportRowsResponse>
|
||||||
) => {
|
) => {
|
||||||
const tableId = utils.getTableId(ctx)
|
const { tableId } = utils.getSourceId(ctx)
|
||||||
|
|
||||||
const format = ctx.query.format
|
const format = ctx.query.format
|
||||||
|
|
||||||
|
@ -279,7 +278,7 @@ export const exportRows = async (
|
||||||
export async function downloadAttachment(ctx: UserCtx) {
|
export async function downloadAttachment(ctx: UserCtx) {
|
||||||
const { columnName } = ctx.params
|
const { columnName } = ctx.params
|
||||||
|
|
||||||
const tableId = utils.getTableId(ctx)
|
const { tableId } = utils.getSourceId(ctx)
|
||||||
const rowId = ctx.params.rowId
|
const rowId = ctx.params.rowId
|
||||||
const row = await sdk.rows.find(tableId, rowId)
|
const row = await sdk.rows.find(tableId, rowId)
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,6 @@ import {
|
||||||
PatchRowRequest,
|
PatchRowRequest,
|
||||||
PatchRowResponse,
|
PatchRowResponse,
|
||||||
Row,
|
Row,
|
||||||
Table,
|
|
||||||
UserCtx,
|
UserCtx,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import sdk from "../../../sdk"
|
import sdk from "../../../sdk"
|
||||||
|
@ -24,7 +23,7 @@ import { getLinkedTableIDs } from "../../../db/linkedRows/linkUtils"
|
||||||
import { flatten } from "lodash"
|
import { flatten } from "lodash"
|
||||||
|
|
||||||
export async function patch(ctx: UserCtx<PatchRowRequest, PatchRowResponse>) {
|
export async function patch(ctx: UserCtx<PatchRowRequest, PatchRowResponse>) {
|
||||||
const tableId = utils.getTableId(ctx)
|
const { tableId } = utils.getSourceId(ctx)
|
||||||
const inputs = ctx.request.body
|
const inputs = ctx.request.body
|
||||||
const isUserTable = tableId === InternalTables.USER_METADATA
|
const isUserTable = tableId === InternalTables.USER_METADATA
|
||||||
let oldRow
|
let oldRow
|
||||||
|
@ -98,7 +97,7 @@ export async function patch(ctx: UserCtx<PatchRowRequest, PatchRowResponse>) {
|
||||||
|
|
||||||
export async function destroy(ctx: UserCtx) {
|
export async function destroy(ctx: UserCtx) {
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
const tableId = utils.getTableId(ctx)
|
const { tableId } = utils.getSourceId(ctx)
|
||||||
const { _id } = ctx.request.body
|
const { _id } = ctx.request.body
|
||||||
let row = await db.get<Row>(_id)
|
let row = await db.get<Row>(_id)
|
||||||
let _rev = ctx.request.body._rev || row._rev
|
let _rev = ctx.request.body._rev || row._rev
|
||||||
|
@ -137,7 +136,7 @@ export async function destroy(ctx: UserCtx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function bulkDestroy(ctx: UserCtx) {
|
export async function bulkDestroy(ctx: UserCtx) {
|
||||||
const tableId = utils.getTableId(ctx)
|
const { tableId } = utils.getSourceId(ctx)
|
||||||
const table = await sdk.tables.getTable(tableId)
|
const table = await sdk.tables.getTable(tableId)
|
||||||
let { rows } = ctx.request.body
|
let { rows } = ctx.request.body
|
||||||
|
|
||||||
|
@ -179,7 +178,7 @@ export async function bulkDestroy(ctx: UserCtx) {
|
||||||
export async function fetchEnrichedRow(ctx: UserCtx) {
|
export async function fetchEnrichedRow(ctx: UserCtx) {
|
||||||
const fieldName = ctx.request.query.field as string | undefined
|
const fieldName = ctx.request.query.field as string | undefined
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
const tableId = utils.getTableId(ctx)
|
const { tableId } = utils.getSourceId(ctx)
|
||||||
const rowId = ctx.params.rowId as string
|
const rowId = ctx.params.rowId as string
|
||||||
// need table to work out where links go in row, as well as the link docs
|
// need table to work out where links go in row, as well as the link docs
|
||||||
const [table, links] = await Promise.all([
|
const [table, links] = await Promise.all([
|
||||||
|
@ -197,7 +196,7 @@ export async function fetchEnrichedRow(ctx: UserCtx) {
|
||||||
)
|
)
|
||||||
|
|
||||||
// get the linked tables
|
// get the linked tables
|
||||||
const linkTableIds = getLinkedTableIDs(table as Table)
|
const linkTableIds = getLinkedTableIDs(table.schema)
|
||||||
const linkTables = await sdk.tables.getTables(linkTableIds)
|
const linkTables = await sdk.tables.getTables(linkTableIds)
|
||||||
|
|
||||||
// perform output processing
|
// perform output processing
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { InternalTables } from "../../../../db/utils"
|
import * as utils from "../../../../db/utils"
|
||||||
|
|
||||||
import { context } from "@budibase/backend-core"
|
import { context } from "@budibase/backend-core"
|
||||||
import {
|
import {
|
||||||
|
@ -67,7 +67,7 @@ export async function findRow(tableId: string, rowId: string) {
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
let row: Row
|
let row: Row
|
||||||
// TODO remove special user case in future
|
// TODO remove special user case in future
|
||||||
if (tableId === InternalTables.USER_METADATA) {
|
if (tableId === utils.InternalTables.USER_METADATA) {
|
||||||
row = await getFullUser(rowId)
|
row = await getFullUser(rowId)
|
||||||
} else {
|
} else {
|
||||||
row = await db.get(rowId)
|
row = await db.get(rowId)
|
||||||
|
@ -78,22 +78,25 @@ export async function findRow(tableId: string, rowId: string) {
|
||||||
return row
|
return row
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTableId(ctx: Ctx): string {
|
export function getSourceId(ctx: Ctx): { tableId: string; viewId?: string } {
|
||||||
// top priority, use the URL first
|
// top priority, use the URL first
|
||||||
if (ctx.params?.sourceId) {
|
if (ctx.params?.sourceId) {
|
||||||
return ctx.params.sourceId
|
const { sourceId } = ctx.params
|
||||||
|
if (utils.isViewID(sourceId)) {
|
||||||
|
return {
|
||||||
|
tableId: utils.extractViewInfoFromID(sourceId).tableId,
|
||||||
|
viewId: sourceId,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { tableId: ctx.params.sourceId }
|
||||||
}
|
}
|
||||||
// now check for old way of specifying table ID
|
// now check for old way of specifying table ID
|
||||||
if (ctx.params?.tableId) {
|
if (ctx.params?.tableId) {
|
||||||
return ctx.params.tableId
|
return { tableId: ctx.params.tableId }
|
||||||
}
|
}
|
||||||
// check body for a table ID
|
// check body for a table ID
|
||||||
if (ctx.request.body?.tableId) {
|
if (ctx.request.body?.tableId) {
|
||||||
return ctx.request.body.tableId
|
return { tableId: ctx.request.body.tableId }
|
||||||
}
|
|
||||||
// now check if a specific view name
|
|
||||||
if (ctx.params?.viewName) {
|
|
||||||
return ctx.params.viewName
|
|
||||||
}
|
}
|
||||||
throw new Error("Unable to find table ID in request")
|
throw new Error("Unable to find table ID in request")
|
||||||
}
|
}
|
||||||
|
@ -200,7 +203,7 @@ export async function sqlOutputProcessing(
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isUserMetadataTable(tableId: string) {
|
export function isUserMetadataTable(tableId: string) {
|
||||||
return tableId === InternalTables.USER_METADATA
|
return tableId === utils.InternalTables.USER_METADATA
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function enrichArrayContext(
|
export async function enrichArrayContext(
|
||||||
|
|
|
@ -2,7 +2,7 @@ import viewTemplate from "./viewBuilder"
|
||||||
import { apiFileReturn } from "../../../utilities/fileSystem"
|
import { apiFileReturn } from "../../../utilities/fileSystem"
|
||||||
import { csv, json, jsonWithSchema, Format, isFormat } from "./exporters"
|
import { csv, json, jsonWithSchema, Format, isFormat } from "./exporters"
|
||||||
import { deleteView, getView, getViews, saveView } from "./utils"
|
import { deleteView, getView, getViews, saveView } from "./utils"
|
||||||
import { fetchView } from "../row"
|
import { fetchLegacyView } from "../row"
|
||||||
import { context, events } from "@budibase/backend-core"
|
import { context, events } from "@budibase/backend-core"
|
||||||
import sdk from "../../../sdk"
|
import sdk from "../../../sdk"
|
||||||
import {
|
import {
|
||||||
|
@ -170,7 +170,7 @@ export async function exportView(ctx: Ctx) {
|
||||||
ctx.params.viewName = viewName
|
ctx.params.viewName = viewName
|
||||||
}
|
}
|
||||||
|
|
||||||
await fetchView(ctx)
|
await fetchLegacyView(ctx)
|
||||||
let rows = ctx.body as Row[]
|
let rows = ctx.body as Row[]
|
||||||
|
|
||||||
let schema: TableSchema = view && view.meta && view.meta.schema
|
let schema: TableSchema = view && view.meta && view.meta.schema
|
||||||
|
|
|
@ -9,7 +9,13 @@ import {
|
||||||
import tk from "timekeeper"
|
import tk from "timekeeper"
|
||||||
import emitter from "../../../../src/events"
|
import emitter from "../../../../src/events"
|
||||||
import { outputProcessing } from "../../../utilities/rowProcessor"
|
import { outputProcessing } from "../../../utilities/rowProcessor"
|
||||||
import { context, InternalTable, tenancy } from "@budibase/backend-core"
|
import {
|
||||||
|
context,
|
||||||
|
InternalTable,
|
||||||
|
tenancy,
|
||||||
|
withEnv as withCoreEnv,
|
||||||
|
setEnv as setCoreEnv,
|
||||||
|
} from "@budibase/backend-core"
|
||||||
import { quotas } from "@budibase/pro"
|
import { quotas } from "@budibase/pro"
|
||||||
import {
|
import {
|
||||||
AttachmentFieldMetadata,
|
AttachmentFieldMetadata,
|
||||||
|
@ -69,6 +75,7 @@ async function waitForEvent(
|
||||||
|
|
||||||
describe.each([
|
describe.each([
|
||||||
["internal", undefined],
|
["internal", undefined],
|
||||||
|
["sqs", undefined],
|
||||||
[DatabaseName.POSTGRES, getDatasource(DatabaseName.POSTGRES)],
|
[DatabaseName.POSTGRES, getDatasource(DatabaseName.POSTGRES)],
|
||||||
[DatabaseName.MYSQL, getDatasource(DatabaseName.MYSQL)],
|
[DatabaseName.MYSQL, getDatasource(DatabaseName.MYSQL)],
|
||||||
[DatabaseName.SQL_SERVER, getDatasource(DatabaseName.SQL_SERVER)],
|
[DatabaseName.SQL_SERVER, getDatasource(DatabaseName.SQL_SERVER)],
|
||||||
|
@ -76,6 +83,8 @@ describe.each([
|
||||||
[DatabaseName.ORACLE, getDatasource(DatabaseName.ORACLE)],
|
[DatabaseName.ORACLE, getDatasource(DatabaseName.ORACLE)],
|
||||||
])("/rows (%s)", (providerType, dsProvider) => {
|
])("/rows (%s)", (providerType, dsProvider) => {
|
||||||
const isInternal = dsProvider === undefined
|
const isInternal = dsProvider === undefined
|
||||||
|
const isLucene = providerType === "lucene"
|
||||||
|
const isSqs = providerType === "sqs"
|
||||||
const isMSSQL = providerType === DatabaseName.SQL_SERVER
|
const isMSSQL = providerType === DatabaseName.SQL_SERVER
|
||||||
const isOracle = providerType === DatabaseName.ORACLE
|
const isOracle = providerType === DatabaseName.ORACLE
|
||||||
const config = setup.getConfig()
|
const config = setup.getConfig()
|
||||||
|
@ -83,9 +92,17 @@ describe.each([
|
||||||
let table: Table
|
let table: Table
|
||||||
let datasource: Datasource | undefined
|
let datasource: Datasource | undefined
|
||||||
let client: Knex | undefined
|
let client: Knex | undefined
|
||||||
|
let envCleanup: (() => void) | undefined
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await config.init()
|
await withCoreEnv({ SQS_SEARCH_ENABLE: "true" }, () => config.init())
|
||||||
|
if (isSqs) {
|
||||||
|
envCleanup = setCoreEnv({
|
||||||
|
SQS_SEARCH_ENABLE: "true",
|
||||||
|
SQS_SEARCH_ENABLE_TENANTS: [config.getTenantId()],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if (dsProvider) {
|
if (dsProvider) {
|
||||||
const rawDatasource = await dsProvider
|
const rawDatasource = await dsProvider
|
||||||
datasource = await config.createDatasource({
|
datasource = await config.createDatasource({
|
||||||
|
@ -97,6 +114,9 @@ describe.each([
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
setup.afterAll()
|
setup.afterAll()
|
||||||
|
if (envCleanup) {
|
||||||
|
envCleanup()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
function saveTableRequest(
|
function saveTableRequest(
|
||||||
|
@ -346,7 +366,7 @@ describe.each([
|
||||||
expect(ids).toEqual(expect.arrayContaining(sequence))
|
expect(ids).toEqual(expect.arrayContaining(sequence))
|
||||||
})
|
})
|
||||||
|
|
||||||
isInternal &&
|
isLucene &&
|
||||||
it("row values are coerced", async () => {
|
it("row values are coerced", async () => {
|
||||||
const str: FieldSchema = {
|
const str: FieldSchema = {
|
||||||
type: FieldType.STRING,
|
type: FieldType.STRING,
|
||||||
|
|
|
@ -46,7 +46,7 @@ router
|
||||||
permissions.PermissionType.TABLE,
|
permissions.PermissionType.TABLE,
|
||||||
permissions.PermissionLevel.READ
|
permissions.PermissionLevel.READ
|
||||||
),
|
),
|
||||||
rowController.fetchView
|
rowController.fetchLegacyView
|
||||||
)
|
)
|
||||||
.get("/api/views", authorized(permissions.BUILDER), viewController.v1.fetch)
|
.get("/api/views", authorized(permissions.BUILDER), viewController.v1.fetch)
|
||||||
.delete(
|
.delete(
|
||||||
|
|
|
@ -18,6 +18,7 @@ import {
|
||||||
LinkDocumentValue,
|
LinkDocumentValue,
|
||||||
Row,
|
Row,
|
||||||
Table,
|
Table,
|
||||||
|
TableSchema,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import sdk from "../../sdk"
|
import sdk from "../../sdk"
|
||||||
|
|
||||||
|
@ -46,8 +47,8 @@ export const EventType = {
|
||||||
TABLE_DELETE: "table:delete",
|
TABLE_DELETE: "table:delete",
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearRelationshipFields(table: Table, rows: Row[]) {
|
function clearRelationshipFields(schema: TableSchema, rows: Row[]) {
|
||||||
for (let [key, field] of Object.entries(table.schema)) {
|
for (let [key, field] of Object.entries(schema)) {
|
||||||
if (field.type === FieldType.LINK) {
|
if (field.type === FieldType.LINK) {
|
||||||
rows = rows.map(row => {
|
rows = rows.map(row => {
|
||||||
delete row[key]
|
delete row[key]
|
||||||
|
@ -158,11 +159,11 @@ export async function updateLinks(args: {
|
||||||
* @return returns the rows with all of the enriched relationships on it.
|
* @return returns the rows with all of the enriched relationships on it.
|
||||||
*/
|
*/
|
||||||
export async function attachFullLinkedDocs(
|
export async function attachFullLinkedDocs(
|
||||||
table: Table,
|
schema: TableSchema,
|
||||||
rows: Row[],
|
rows: Row[],
|
||||||
opts?: { fromRow?: Row }
|
opts?: { fromRow?: Row }
|
||||||
) {
|
) {
|
||||||
const linkedTableIds = getLinkedTableIDs(table)
|
const linkedTableIds = getLinkedTableIDs(schema)
|
||||||
if (linkedTableIds.length === 0) {
|
if (linkedTableIds.length === 0) {
|
||||||
return rows
|
return rows
|
||||||
}
|
}
|
||||||
|
@ -182,7 +183,7 @@ export async function attachFullLinkedDocs(
|
||||||
}
|
}
|
||||||
const linkedTables = response[1] as Table[]
|
const linkedTables = response[1] as Table[]
|
||||||
// clear any existing links that could be dupe'd
|
// clear any existing links that could be dupe'd
|
||||||
rows = clearRelationshipFields(table, rows)
|
rows = clearRelationshipFields(schema, rows)
|
||||||
// now get the docs and combine into the rows
|
// now get the docs and combine into the rows
|
||||||
let linked: Row[] = []
|
let linked: Row[] = []
|
||||||
if (linksWithoutFromRow.length > 0) {
|
if (linksWithoutFromRow.length > 0) {
|
||||||
|
@ -201,7 +202,7 @@ export async function attachFullLinkedDocs(
|
||||||
}
|
}
|
||||||
if (linkedRow) {
|
if (linkedRow) {
|
||||||
const linkedTableId =
|
const linkedTableId =
|
||||||
linkedRow.tableId || getRelatedTableForField(table, link.fieldName)
|
linkedRow.tableId || getRelatedTableForField(schema, link.fieldName)
|
||||||
const linkedTable = linkedTables.find(
|
const linkedTable = linkedTables.find(
|
||||||
table => table._id === linkedTableId
|
table => table._id === linkedTableId
|
||||||
)
|
)
|
||||||
|
@ -263,7 +264,8 @@ export async function squashLinksToPrimaryDisplay(
|
||||||
}
|
}
|
||||||
const newLinks = []
|
const newLinks = []
|
||||||
for (let link of row[column]) {
|
for (let link of row[column]) {
|
||||||
const linkTblId = link.tableId || getRelatedTableForField(table, column)
|
const linkTblId =
|
||||||
|
link.tableId || getRelatedTableForField(table.schema, column)
|
||||||
const linkedTable = await getLinkedTable(linkTblId!, linkedTables)
|
const linkedTable = await getLinkedTable(linkTblId!, linkedTables)
|
||||||
const obj: any = { _id: link._id }
|
const obj: any = { _id: link._id }
|
||||||
obj.primaryDisplay = getPrimaryDisplayValue(link, linkedTable)
|
obj.primaryDisplay = getPrimaryDisplayValue(link, linkedTable)
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
LinkDocument,
|
LinkDocument,
|
||||||
LinkDocumentValue,
|
LinkDocumentValue,
|
||||||
Table,
|
Table,
|
||||||
|
TableSchema,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import sdk from "../../sdk"
|
import sdk from "../../sdk"
|
||||||
|
|
||||||
|
@ -121,8 +122,8 @@ export function getUniqueByProp(array: any[], prop: string) {
|
||||||
return filteredArray
|
return filteredArray
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLinkedTableIDs(table: Table): string[] {
|
export function getLinkedTableIDs(schema: TableSchema): string[] {
|
||||||
return Object.values(table.schema)
|
return Object.values(schema)
|
||||||
.filter(isRelationshipColumn)
|
.filter(isRelationshipColumn)
|
||||||
.map(column => column.tableId)
|
.map(column => column.tableId)
|
||||||
}
|
}
|
||||||
|
@ -139,13 +140,16 @@ export async function getLinkedTable(id: string, tables: Table[]) {
|
||||||
return linkedTable
|
return linkedTable
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getRelatedTableForField(table: Table, fieldName: string) {
|
export function getRelatedTableForField(
|
||||||
|
schema: TableSchema,
|
||||||
|
fieldName: string
|
||||||
|
) {
|
||||||
// look to see if its on the table, straight in the schema
|
// look to see if its on the table, straight in the schema
|
||||||
const field = table.schema[fieldName]
|
const field = schema[fieldName]
|
||||||
if (field?.type === FieldType.LINK) {
|
if (field?.type === FieldType.LINK) {
|
||||||
return field.tableId
|
return field.tableId
|
||||||
}
|
}
|
||||||
for (let column of Object.values(table.schema)) {
|
for (let column of Object.values(schema)) {
|
||||||
if (column.type === FieldType.LINK && column.fieldName === fieldName) {
|
if (column.type === FieldType.LINK && column.fieldName === fieldName) {
|
||||||
return column.tableId
|
return column.tableId
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ describe("test link functionality", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("getRelatedTableForField", () => {
|
describe("getRelatedTableForField", () => {
|
||||||
let link = basicTable()
|
const link = basicTable()
|
||||||
link.schema.link = {
|
link.schema.link = {
|
||||||
name: "link",
|
name: "link",
|
||||||
relationshipType: RelationshipType.ONE_TO_MANY,
|
relationshipType: RelationshipType.ONE_TO_MANY,
|
||||||
|
@ -44,11 +44,13 @@ describe("test link functionality", () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
it("should get the field from the table directly", () => {
|
it("should get the field from the table directly", () => {
|
||||||
expect(linkUtils.getRelatedTableForField(link, "link")).toBe("tableID")
|
expect(linkUtils.getRelatedTableForField(link.schema, "link")).toBe(
|
||||||
|
"tableID"
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should get the field from the link", () => {
|
it("should get the field from the link", () => {
|
||||||
expect(linkUtils.getRelatedTableForField(link, "otherLink")).toBe(
|
expect(linkUtils.getRelatedTableForField(link.schema, "otherLink")).toBe(
|
||||||
"tableID"
|
"tableID"
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,33 +1,23 @@
|
||||||
import { Ctx, Row } from "@budibase/types"
|
import { Ctx, Row } from "@budibase/types"
|
||||||
import * as utils from "../db/utils"
|
|
||||||
import sdk from "../sdk"
|
import sdk from "../sdk"
|
||||||
import { Next } from "koa"
|
import { Next } from "koa"
|
||||||
import { getTableId } from "../api/controllers/row/utils"
|
import { getSourceId } from "../api/controllers/row/utils"
|
||||||
|
|
||||||
export default async (ctx: Ctx<Row>, next: Next) => {
|
export default async (ctx: Ctx<Row>, next: Next) => {
|
||||||
const { body } = ctx.request
|
const { body } = ctx.request
|
||||||
let { _viewId: viewId } = body
|
const viewId = getSourceId(ctx).viewId ?? body._viewId
|
||||||
|
|
||||||
const possibleViewId = getTableId(ctx)
|
|
||||||
if (utils.isViewID(possibleViewId)) {
|
|
||||||
viewId = possibleViewId
|
|
||||||
}
|
|
||||||
|
|
||||||
// nothing to do, it is not a view (just a table ID)
|
// nothing to do, it is not a view (just a table ID)
|
||||||
if (!viewId) {
|
if (!viewId) {
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
|
|
||||||
const { tableId } = utils.extractViewInfoFromID(viewId)
|
|
||||||
|
|
||||||
// don't need to trim delete requests
|
// don't need to trim delete requests
|
||||||
if (ctx?.method?.toLowerCase() !== "delete") {
|
if (ctx?.method?.toLowerCase() !== "delete") {
|
||||||
await trimViewFields(ctx.request.body, viewId)
|
await trimViewFields(ctx.request.body, viewId)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.params.sourceId = tableId
|
|
||||||
ctx.params.viewId = viewId
|
|
||||||
|
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -121,10 +121,9 @@ export async function fetchRaw(tableId: string): Promise<Row[]> {
|
||||||
return pickApi(tableId).fetchRaw(tableId)
|
return pickApi(tableId).fetchRaw(tableId)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchView(
|
export async function fetchLegacyView(
|
||||||
tableId: string,
|
|
||||||
viewName: string,
|
viewName: string,
|
||||||
params: ViewParams
|
params: ViewParams
|
||||||
): Promise<Row[]> {
|
): Promise<Row[]> {
|
||||||
return pickApi(tableId).fetchView(viewName, params)
|
return internal.fetchLegacyView(viewName, params)
|
||||||
}
|
}
|
||||||
|
|
|
@ -272,11 +272,3 @@ export async function fetchRaw(tableId: string): Promise<Row[]> {
|
||||||
})
|
})
|
||||||
return response.rows
|
return response.rows
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchView(viewName: string) {
|
|
||||||
// there are no views in external datasources, shouldn't ever be called
|
|
||||||
// for now just fetch
|
|
||||||
const split = viewName.split("all_")
|
|
||||||
const tableId = split[1] ? split[1] : split[0]
|
|
||||||
return fetch(tableId)
|
|
||||||
}
|
|
||||||
|
|
|
@ -145,7 +145,7 @@ export async function fetchRaw(tableId: string): Promise<Row[]> {
|
||||||
return rows as Row[]
|
return rows as Row[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchView(
|
export async function fetchLegacyView(
|
||||||
viewName: string,
|
viewName: string,
|
||||||
options: { calculation: string; group: string; field: string }
|
options: { calculation: string; group: string; field: string }
|
||||||
): Promise<Row[]> {
|
): Promise<Row[]> {
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { fixAutoColumnSubType, processFormulas } from "./utils"
|
||||||
import {
|
import {
|
||||||
cache,
|
cache,
|
||||||
context,
|
context,
|
||||||
|
db,
|
||||||
HTTPError,
|
HTTPError,
|
||||||
objectStore,
|
objectStore,
|
||||||
utils,
|
utils,
|
||||||
|
@ -262,7 +263,7 @@ export async function outputProcessing<T extends Row[] | Row>(
|
||||||
}
|
}
|
||||||
// attach any linked row information
|
// attach any linked row information
|
||||||
let enriched = !opts.preserveLinks
|
let enriched = !opts.preserveLinks
|
||||||
? await linkRows.attachFullLinkedDocs(table, safeRows, {
|
? await linkRows.attachFullLinkedDocs(table.schema, safeRows, {
|
||||||
fromRow: opts?.fromRow,
|
fromRow: opts?.fromRow,
|
||||||
})
|
})
|
||||||
: safeRows
|
: safeRows
|
||||||
|
@ -349,11 +350,19 @@ export async function outputProcessing<T extends Row[] | Row>(
|
||||||
}
|
}
|
||||||
// remove null properties to match internal API
|
// remove null properties to match internal API
|
||||||
const isExternal = isExternalTableID(table._id!)
|
const isExternal = isExternalTableID(table._id!)
|
||||||
if (isExternal) {
|
if (isExternal || db.isSqsEnabledForTenant()) {
|
||||||
for (const row of enriched) {
|
for (const row of enriched) {
|
||||||
for (const key of Object.keys(row)) {
|
for (const key of Object.keys(row)) {
|
||||||
if (row[key] === null) {
|
if (row[key] === null) {
|
||||||
delete row[key]
|
delete row[key]
|
||||||
|
} else if (row[key] && table.schema[key]?.type === FieldType.LINK) {
|
||||||
|
for (const link of row[key] || []) {
|
||||||
|
for (const linkKey of Object.keys(link)) {
|
||||||
|
if (link[linkKey] === null) {
|
||||||
|
delete link[linkKey]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,14 @@ import { outputProcessing } from ".."
|
||||||
import { generator, structures } from "@budibase/backend-core/tests"
|
import { generator, structures } from "@budibase/backend-core/tests"
|
||||||
import * as bbReferenceProcessor from "../bbReferenceProcessor"
|
import * as bbReferenceProcessor from "../bbReferenceProcessor"
|
||||||
|
|
||||||
|
jest.mock("@budibase/backend-core", () => ({
|
||||||
|
...jest.requireActual("@budibase/backend-core"),
|
||||||
|
db: {
|
||||||
|
...jest.requireActual("@budibase/backend-core").db,
|
||||||
|
isSqsEnabledForTenant: () => true,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
jest.mock("../bbReferenceProcessor", (): typeof bbReferenceProcessor => ({
|
jest.mock("../bbReferenceProcessor", (): typeof bbReferenceProcessor => ({
|
||||||
processInputBBReference: jest.fn(),
|
processInputBBReference: jest.fn(),
|
||||||
processInputBBReferences: jest.fn(),
|
processInputBBReferences: jest.fn(),
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { BaseSocket } from "./websocket"
|
||||||
import { auth, permissions } from "@budibase/backend-core"
|
import { auth, permissions } from "@budibase/backend-core"
|
||||||
import http from "http"
|
import http from "http"
|
||||||
import Koa from "koa"
|
import Koa from "koa"
|
||||||
import { getTableId } from "../api/controllers/row/utils"
|
import { getSourceId } from "../api/controllers/row/utils"
|
||||||
import { Row, Table, View, ViewV2 } from "@budibase/types"
|
import { Row, Table, View, ViewV2 } from "@budibase/types"
|
||||||
import { Socket } from "socket.io"
|
import { Socket } from "socket.io"
|
||||||
import { GridSocketEvent } from "@budibase/shared-core"
|
import { GridSocketEvent } from "@budibase/shared-core"
|
||||||
|
@ -80,7 +80,7 @@ export default class GridSocket extends BaseSocket {
|
||||||
}
|
}
|
||||||
|
|
||||||
emitRowUpdate(ctx: any, row: Row) {
|
emitRowUpdate(ctx: any, row: Row) {
|
||||||
const resourceId = ctx.params?.viewId || getTableId(ctx)
|
const resourceId = ctx.params?.viewId || getSourceId(ctx)
|
||||||
const room = `${ctx.appId}-${resourceId}`
|
const room = `${ctx.appId}-${resourceId}`
|
||||||
this.emitToRoom(ctx, room, GridSocketEvent.RowChange, {
|
this.emitToRoom(ctx, room, GridSocketEvent.RowChange, {
|
||||||
id: row._id,
|
id: row._id,
|
||||||
|
@ -89,7 +89,7 @@ export default class GridSocket extends BaseSocket {
|
||||||
}
|
}
|
||||||
|
|
||||||
emitRowDeletion(ctx: any, row: Row) {
|
emitRowDeletion(ctx: any, row: Row) {
|
||||||
const resourceId = ctx.params?.viewId || getTableId(ctx)
|
const resourceId = ctx.params?.viewId || getSourceId(ctx)
|
||||||
const room = `${ctx.appId}-${resourceId}`
|
const room = `${ctx.appId}-${resourceId}`
|
||||||
this.emitToRoom(ctx, room, GridSocketEvent.RowChange, {
|
this.emitToRoom(ctx, room, GridSocketEvent.RowChange, {
|
||||||
id: row._id,
|
id: row._id,
|
||||||
|
|
Loading…
Reference in New Issue