Type checks pass, now to find out how much stuff I've broken.
This commit is contained in:
parent
0fee7dfd4f
commit
0eb90cfbea
|
@ -79,8 +79,8 @@ export class QueryBuilder<T> {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
setTable(tableId: string) {
|
setSource(sourceId: string) {
|
||||||
this.#query.equal!.tableId = tableId
|
this.#query.equal!.tableId = sourceId
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -638,8 +638,8 @@ async function recursiveSearch<T>(
|
||||||
.setSortOrder(params.sortOrder)
|
.setSortOrder(params.sortOrder)
|
||||||
.setSortType(params.sortType)
|
.setSortType(params.sortType)
|
||||||
|
|
||||||
if (params.tableId) {
|
if (params.sourceId) {
|
||||||
queryBuilder.setTable(params.tableId)
|
queryBuilder.setSource(params.sourceId)
|
||||||
}
|
}
|
||||||
|
|
||||||
const page = await queryBuilder.run()
|
const page = await queryBuilder.run()
|
||||||
|
@ -672,8 +672,8 @@ export async function paginatedSearch<T>(
|
||||||
if (params.version) {
|
if (params.version) {
|
||||||
search.setVersion(params.version)
|
search.setVersion(params.version)
|
||||||
}
|
}
|
||||||
if (params.tableId) {
|
if (params.sourceId) {
|
||||||
search.setTable(params.tableId)
|
search.setSource(params.sourceId)
|
||||||
}
|
}
|
||||||
if (params.sort) {
|
if (params.sort) {
|
||||||
search
|
search
|
||||||
|
@ -695,8 +695,8 @@ export async function paginatedSearch<T>(
|
||||||
// Try fetching 1 row in the next page to see if another page of results
|
// Try fetching 1 row in the next page to see if another page of results
|
||||||
// exists or not
|
// exists or not
|
||||||
search.setBookmark(searchResults.bookmark).setLimit(1)
|
search.setBookmark(searchResults.bookmark).setLimit(1)
|
||||||
if (params.tableId) {
|
if (params.sourceId) {
|
||||||
search.setTable(params.tableId)
|
search.setSource(params.sourceId)
|
||||||
}
|
}
|
||||||
const nextResults = await search.run()
|
const nextResults = await search.run()
|
||||||
|
|
||||||
|
|
|
@ -366,7 +366,7 @@ describe("lucene", () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tableId: TABLE_ID,
|
sourceId: TABLE_ID,
|
||||||
limit: 1,
|
limit: 1,
|
||||||
sort: "property",
|
sort: "property",
|
||||||
sortType: SortType.STRING,
|
sortType: SortType.STRING,
|
||||||
|
@ -390,7 +390,7 @@ describe("lucene", () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tableId: TABLE_ID,
|
sourceId: TABLE_ID,
|
||||||
query: {},
|
query: {},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -45,7 +45,7 @@ export async function patch(ctx: UserCtx<PatchRowRequest, PatchRowResponse>) {
|
||||||
const table = await utils.getTableFromSource(source)
|
const table = await utils.getTableFromSource(source)
|
||||||
const { _id, ...rowData } = ctx.request.body
|
const { _id, ...rowData } = ctx.request.body
|
||||||
|
|
||||||
const { row: dataToUpdate } = await inputProcessing(
|
const dataToUpdate = await inputProcessing(
|
||||||
ctx.user?._id,
|
ctx.user?._id,
|
||||||
cloneDeep(source),
|
cloneDeep(source),
|
||||||
rowData
|
rowData
|
||||||
|
|
|
@ -21,18 +21,19 @@ import {
|
||||||
import sdk from "../../../sdk"
|
import sdk from "../../../sdk"
|
||||||
import { getLinkedTableIDs } from "../../../db/linkedRows/linkUtils"
|
import { getLinkedTableIDs } from "../../../db/linkedRows/linkUtils"
|
||||||
import { flatten } from "lodash"
|
import { flatten } from "lodash"
|
||||||
|
import { findRow } from "../../../sdk/app/rows/internal"
|
||||||
|
|
||||||
export async function patch(ctx: UserCtx<PatchRowRequest, PatchRowResponse>) {
|
export async function patch(ctx: UserCtx<PatchRowRequest, PatchRowResponse>) {
|
||||||
const { tableId, viewId } = utils.getSourceId(ctx)
|
const { tableId } = utils.getSourceId(ctx)
|
||||||
|
const source = await utils.getSource(ctx)
|
||||||
|
const table = sdk.views.isView(source)
|
||||||
|
? await sdk.views.getTable(source.id)
|
||||||
|
: source
|
||||||
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
|
||||||
const dbTable = await sdk.tables.getTable(tableId)
|
|
||||||
try {
|
try {
|
||||||
oldRow = await outputProcessing(
|
oldRow = await outputProcessing(source, await findRow(tableId, inputs._id!))
|
||||||
dbTable,
|
|
||||||
await utils.findRow(tableId, inputs._id!)
|
|
||||||
)
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (isUserTable) {
|
if (isUserTable) {
|
||||||
// don't include the rev, it'll be the global rev
|
// don't include the rev, it'll be the global rev
|
||||||
|
@ -48,22 +49,18 @@ export async function patch(ctx: UserCtx<PatchRowRequest, PatchRowResponse>) {
|
||||||
// need to build up full patch fields before coerce
|
// need to build up full patch fields before coerce
|
||||||
let combinedRow: any = cloneDeep(oldRow)
|
let combinedRow: any = cloneDeep(oldRow)
|
||||||
for (let key of Object.keys(inputs)) {
|
for (let key of Object.keys(inputs)) {
|
||||||
if (!dbTable.schema[key]) continue
|
if (!table.schema[key]) continue
|
||||||
combinedRow[key] = inputs[key]
|
combinedRow[key] = inputs[key]
|
||||||
}
|
}
|
||||||
|
|
||||||
// need to copy the table so it can be differenced on way out
|
// need to copy the table so it can be differenced on way out
|
||||||
const tableClone = cloneDeep(dbTable)
|
const tableClone = cloneDeep(table)
|
||||||
|
|
||||||
// this returns the table and row incase they have been updated
|
// this returns the table and row incase they have been updated
|
||||||
let { table, row } = await inputProcessing(
|
let row = await inputProcessing(ctx.user?._id, tableClone, combinedRow)
|
||||||
ctx.user?._id,
|
|
||||||
tableClone,
|
|
||||||
combinedRow
|
|
||||||
)
|
|
||||||
const validateResult = await sdk.rows.utils.validate({
|
const validateResult = await sdk.rows.utils.validate({
|
||||||
row,
|
row,
|
||||||
table,
|
source,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!validateResult.valid) {
|
if (!validateResult.valid) {
|
||||||
|
@ -87,10 +84,8 @@ export async function patch(ctx: UserCtx<PatchRowRequest, PatchRowResponse>) {
|
||||||
return { row: ctx.body as Row, table, oldRow }
|
return { row: ctx.body as Row, table, oldRow }
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await finaliseRow(table, row, {
|
const result = await finaliseRow(source, row, {
|
||||||
oldTable: dbTable,
|
|
||||||
updateFormula: true,
|
updateFormula: true,
|
||||||
fromViewId: viewId,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return { ...result, oldRow }
|
return { ...result, oldRow }
|
||||||
|
@ -186,7 +181,7 @@ export async function fetchEnrichedRow(ctx: UserCtx) {
|
||||||
sdk.tables.getTable(tableId),
|
sdk.tables.getTable(tableId),
|
||||||
linkRows.getLinkDocuments({ tableId, rowId, fieldName }),
|
linkRows.getLinkDocuments({ tableId, rowId, fieldName }),
|
||||||
])
|
])
|
||||||
let row = await utils.findRow(tableId, rowId)
|
let row = await findRow(tableId, rowId)
|
||||||
row = await outputProcessing(table, row)
|
row = await outputProcessing(table, row)
|
||||||
const linkVals = links as LinkDocumentValue[]
|
const linkVals = links as LinkDocumentValue[]
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,11 @@ import {
|
||||||
processFormulas,
|
processFormulas,
|
||||||
} from "../../../utilities/rowProcessor"
|
} from "../../../utilities/rowProcessor"
|
||||||
import { context } from "@budibase/backend-core"
|
import { context } from "@budibase/backend-core"
|
||||||
import { Table, Row, FormulaType, FieldType } from "@budibase/types"
|
import { Table, Row, FormulaType, FieldType, ViewV2 } from "@budibase/types"
|
||||||
import * as linkRows from "../../../db/linkedRows"
|
import * as linkRows from "../../../db/linkedRows"
|
||||||
import isEqual from "lodash/isEqual"
|
import isEqual from "lodash/isEqual"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
|
import sdk from "../../../sdk"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function runs through a list of enriched rows, looks at the rows which
|
* This function runs through a list of enriched rows, looks at the rows which
|
||||||
|
@ -121,33 +122,26 @@ export async function updateAllFormulasInTable(table: Table) {
|
||||||
* expects the row to be totally enriched/contain all relationships.
|
* expects the row to be totally enriched/contain all relationships.
|
||||||
*/
|
*/
|
||||||
export async function finaliseRow(
|
export async function finaliseRow(
|
||||||
table: Table,
|
source: Table | ViewV2,
|
||||||
row: Row,
|
row: Row,
|
||||||
{
|
opts?: { updateFormula: boolean }
|
||||||
oldTable,
|
|
||||||
updateFormula,
|
|
||||||
fromViewId,
|
|
||||||
}: { oldTable?: Table; updateFormula: boolean; fromViewId?: string } = {
|
|
||||||
updateFormula: true,
|
|
||||||
}
|
|
||||||
) {
|
) {
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
|
const { updateFormula = true } = opts || {}
|
||||||
|
const table = sdk.views.isView(source)
|
||||||
|
? await sdk.views.getTable(source.id)
|
||||||
|
: source
|
||||||
|
|
||||||
row.type = "row"
|
row.type = "row"
|
||||||
// process the row before return, to include relationships
|
// process the row before return, to include relationships
|
||||||
let enrichedRow = (await outputProcessing(table, cloneDeep(row), {
|
let enrichedRow = await outputProcessing(source, cloneDeep(row), {
|
||||||
squash: false,
|
squash: false,
|
||||||
})) as Row
|
})
|
||||||
// use enriched row to generate formulas for saving, specifically only use as context
|
// use enriched row to generate formulas for saving, specifically only use as context
|
||||||
row = await processFormulas(table, row, {
|
row = await processFormulas(table, row, {
|
||||||
dynamic: false,
|
dynamic: false,
|
||||||
contextRows: [enrichedRow],
|
contextRows: [enrichedRow],
|
||||||
})
|
})
|
||||||
// don't worry about rev, tables handle rev/lastID updates
|
|
||||||
// if another row has been written since processing this will
|
|
||||||
// handle the auto ID clash
|
|
||||||
if (oldTable && !isEqual(oldTable, table)) {
|
|
||||||
await db.put(table)
|
|
||||||
}
|
|
||||||
const response = await db.put(row)
|
const response = await db.put(row)
|
||||||
// for response, calculate the formulas for the enriched row
|
// for response, calculate the formulas for the enriched row
|
||||||
enrichedRow._rev = response.rev
|
enrichedRow._rev = response.rev
|
||||||
|
@ -158,8 +152,6 @@ export async function finaliseRow(
|
||||||
if (updateFormula) {
|
if (updateFormula) {
|
||||||
await updateRelatedFormula(table, enrichedRow)
|
await updateRelatedFormula(table, enrichedRow)
|
||||||
}
|
}
|
||||||
const squashed = await linkRows.squashLinks(table, enrichedRow, {
|
const squashed = await linkRows.squashLinks(source, enrichedRow)
|
||||||
fromViewId,
|
|
||||||
})
|
|
||||||
return { row: enrichedRow, squashed, table }
|
return { row: enrichedRow, squashed, table }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import * as utils from "../../../../db/utils"
|
import * as utils from "../../../../db/utils"
|
||||||
|
|
||||||
import { context, docIds } from "@budibase/backend-core"
|
import { docIds } from "@budibase/backend-core"
|
||||||
import {
|
import {
|
||||||
Aggregation,
|
Aggregation,
|
||||||
Ctx,
|
Ctx,
|
||||||
|
@ -20,7 +20,6 @@ import { basicProcessing, generateIdForRow, getInternalRowId } from "./basic"
|
||||||
import sdk from "../../../../sdk"
|
import sdk from "../../../../sdk"
|
||||||
import { processStringSync } from "@budibase/string-templates"
|
import { processStringSync } from "@budibase/string-templates"
|
||||||
import validateJs from "validate.js"
|
import validateJs from "validate.js"
|
||||||
import { getFullUser } from "../../../../utilities/users"
|
|
||||||
|
|
||||||
validateJs.extend(validateJs.validators.datetime, {
|
validateJs.extend(validateJs.validators.datetime, {
|
||||||
parse: function (value: string) {
|
parse: function (value: string) {
|
||||||
|
@ -60,21 +59,6 @@ export async function processRelationshipFields(
|
||||||
return row
|
return row
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function findRow(tableId: string, rowId: string) {
|
|
||||||
const db = context.getAppDB()
|
|
||||||
let row: Row
|
|
||||||
// TODO remove special user case in future
|
|
||||||
if (tableId === utils.InternalTables.USER_METADATA) {
|
|
||||||
row = await getFullUser(rowId)
|
|
||||||
} else {
|
|
||||||
row = await db.get(rowId)
|
|
||||||
}
|
|
||||||
if (row.tableId !== tableId) {
|
|
||||||
throw "Supplied tableId does not match the rows tableId"
|
|
||||||
}
|
|
||||||
return row
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getSourceId(ctx: Ctx): { tableId: string; viewId?: 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) {
|
||||||
|
|
|
@ -113,11 +113,10 @@ export async function bulkImport(
|
||||||
const processed = await inputProcessing(ctx.user?._id, table, row, {
|
const processed = await inputProcessing(ctx.user?._id, table, row, {
|
||||||
noAutoRelationships: true,
|
noAutoRelationships: true,
|
||||||
})
|
})
|
||||||
parsedRows.push(processed.row)
|
parsedRows.push(processed)
|
||||||
table = processed.table
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await handleRequest(Operation.BULK_UPSERT, table._id!, {
|
await handleRequest(Operation.BULK_UPSERT, table, {
|
||||||
rows: parsedRows,
|
rows: parsedRows,
|
||||||
})
|
})
|
||||||
await events.rows.imported(table, parsedRows.length)
|
await events.rows.imported(table, parsedRows.length)
|
||||||
|
|
|
@ -139,8 +139,7 @@ export async function importToRows(
|
||||||
const processed = await inputProcessing(user?._id, table, row, {
|
const processed = await inputProcessing(user?._id, table, row, {
|
||||||
noAutoRelationships: true,
|
noAutoRelationships: true,
|
||||||
})
|
})
|
||||||
row = processed.row
|
row = processed
|
||||||
table = processed.table
|
|
||||||
|
|
||||||
// However here we must reference the original table, as we want to mutate
|
// However here we must reference the original table, as we want to mutate
|
||||||
// the real schema of the table passed in, not the clone used for
|
// the real schema of the table passed in, not the clone used for
|
||||||
|
|
|
@ -157,7 +157,7 @@ describe.each([
|
||||||
if (isInMemory) {
|
if (isInMemory) {
|
||||||
return dataFilters.search(_.cloneDeep(rows), this.query)
|
return dataFilters.search(_.cloneDeep(rows), this.query)
|
||||||
} else {
|
} else {
|
||||||
return config.api.row.search(this.query.tableId, this.query)
|
return config.api.row.search(this.query.sourceId, this.query)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,8 +327,8 @@ describe.each([
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function expectSearch(query: Omit<RowSearchParams, "tableId">) {
|
function expectSearch(query: Omit<RowSearchParams, "sourceId">) {
|
||||||
return new SearchAssertion({ ...query, tableId: table._id! })
|
return new SearchAssertion({ ...query, sourceId: table._id! })
|
||||||
}
|
}
|
||||||
|
|
||||||
function expectQuery(query: SearchFilters) {
|
function expectQuery(query: SearchFilters) {
|
||||||
|
@ -1898,7 +1898,7 @@ describe.each([
|
||||||
let { rows: fullRowList } = await config.api.row.search(
|
let { rows: fullRowList } = await config.api.row.search(
|
||||||
table._id!,
|
table._id!,
|
||||||
{
|
{
|
||||||
tableId: table._id!,
|
sourceId: table._id!,
|
||||||
query: {},
|
query: {},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -1909,7 +1909,7 @@ describe.each([
|
||||||
rowCount: number = 0
|
rowCount: number = 0
|
||||||
do {
|
do {
|
||||||
const response = await config.api.row.search(table._id!, {
|
const response = await config.api.row.search(table._id!, {
|
||||||
tableId: table._id!,
|
sourceId: table._id!,
|
||||||
limit: 1,
|
limit: 1,
|
||||||
paginate: true,
|
paginate: true,
|
||||||
query: {},
|
query: {},
|
||||||
|
@ -1933,7 +1933,7 @@ describe.each([
|
||||||
// eslint-disable-next-line no-constant-condition
|
// eslint-disable-next-line no-constant-condition
|
||||||
while (true) {
|
while (true) {
|
||||||
const response = await config.api.row.search(table._id!, {
|
const response = await config.api.row.search(table._id!, {
|
||||||
tableId: table._id!,
|
sourceId: table._id!,
|
||||||
limit: 3,
|
limit: 3,
|
||||||
query: {},
|
query: {},
|
||||||
bookmark,
|
bookmark,
|
||||||
|
|
|
@ -113,7 +113,7 @@ describe("/templates", () => {
|
||||||
expect(users.name).toBe("Users")
|
expect(users.name).toBe("Users")
|
||||||
|
|
||||||
const { rows } = await config.api.row.search(agencyProjects._id!, {
|
const { rows } = await config.api.row.search(agencyProjects._id!, {
|
||||||
tableId: agencyProjects._id!,
|
sourceId: agencyProjects._id!,
|
||||||
query: {},
|
query: {},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -255,31 +255,31 @@ export type SquashTableFields = Record<string, { visibleFieldNames: string[] }>
|
||||||
* @returns The rows after having their links squashed to only contain the ID and primary display.
|
* @returns The rows after having their links squashed to only contain the ID and primary display.
|
||||||
*/
|
*/
|
||||||
export async function squashLinks<T = Row[] | Row>(
|
export async function squashLinks<T = Row[] | Row>(
|
||||||
table: Table,
|
source: Table | ViewV2,
|
||||||
enriched: T,
|
enriched: T
|
||||||
options?: {
|
|
||||||
fromViewId?: string
|
|
||||||
}
|
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
const allowRelationshipSchemas = await features.flags.isEnabled(
|
const allowRelationshipSchemas = await features.flags.isEnabled(
|
||||||
FeatureFlag.ENRICHED_RELATIONSHIPS
|
FeatureFlag.ENRICHED_RELATIONSHIPS
|
||||||
)
|
)
|
||||||
|
|
||||||
let viewSchema: Record<string, ViewUIFieldMetadata> = {}
|
let viewSchema: Record<string, ViewUIFieldMetadata> = {}
|
||||||
if (options?.fromViewId) {
|
if (sdk.views.isView(source)) {
|
||||||
const view = Object.values(table.views || {}).find(
|
if (helpers.views.isCalculationView(source)) {
|
||||||
(v): v is ViewV2 => sdk.views.isV2(v) && v.id === options?.fromViewId
|
|
||||||
)
|
|
||||||
|
|
||||||
if (view && helpers.views.isCalculationView(view)) {
|
|
||||||
return enriched
|
return enriched
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allowRelationshipSchemas && view) {
|
if (allowRelationshipSchemas) {
|
||||||
viewSchema = view.schema || {}
|
viewSchema = source.schema || {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let table: Table
|
||||||
|
if (sdk.views.isView(source)) {
|
||||||
|
table = await sdk.views.getTable(source.id)
|
||||||
|
} else {
|
||||||
|
table = source
|
||||||
|
}
|
||||||
|
|
||||||
// will populate this as we find them
|
// will populate this as we find them
|
||||||
const linkedTables = [table]
|
const linkedTables = [table]
|
||||||
const isArray = Array.isArray(enriched)
|
const isArray = Array.isArray(enriched)
|
||||||
|
|
|
@ -219,7 +219,7 @@ describe("Google Sheets Integration", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
let resp = await config.api.row.search(table._id!, {
|
let resp = await config.api.row.search(table._id!, {
|
||||||
tableId: table._id!,
|
sourceId: table._id!,
|
||||||
query: {},
|
query: {},
|
||||||
paginate: true,
|
paginate: true,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
|
@ -228,7 +228,7 @@ describe("Google Sheets Integration", () => {
|
||||||
|
|
||||||
while (resp.hasNextPage) {
|
while (resp.hasNextPage) {
|
||||||
resp = await config.api.row.search(table._id!, {
|
resp = await config.api.row.search(table._id!, {
|
||||||
tableId: table._id!,
|
sourceId: table._id!,
|
||||||
query: {},
|
query: {},
|
||||||
paginate: true,
|
paginate: true,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
|
@ -637,7 +637,7 @@ describe("Google Sheets Integration", () => {
|
||||||
|
|
||||||
it("should be able to find rows with equals filter", async () => {
|
it("should be able to find rows with equals filter", async () => {
|
||||||
const response = await config.api.row.search(table._id!, {
|
const response = await config.api.row.search(table._id!, {
|
||||||
tableId: table._id!,
|
sourceId: table._id!,
|
||||||
query: {
|
query: {
|
||||||
equal: {
|
equal: {
|
||||||
name: "Foo",
|
name: "Foo",
|
||||||
|
@ -651,7 +651,7 @@ describe("Google Sheets Integration", () => {
|
||||||
|
|
||||||
it("should be able to find rows with not equals filter", async () => {
|
it("should be able to find rows with not equals filter", async () => {
|
||||||
const response = await config.api.row.search(table._id!, {
|
const response = await config.api.row.search(table._id!, {
|
||||||
tableId: table._id!,
|
sourceId: table._id!,
|
||||||
query: {
|
query: {
|
||||||
notEqual: {
|
notEqual: {
|
||||||
name: "Foo",
|
name: "Foo",
|
||||||
|
@ -666,7 +666,7 @@ describe("Google Sheets Integration", () => {
|
||||||
|
|
||||||
it("should be able to find rows with empty filter", async () => {
|
it("should be able to find rows with empty filter", async () => {
|
||||||
const response = await config.api.row.search(table._id!, {
|
const response = await config.api.row.search(table._id!, {
|
||||||
tableId: table._id!,
|
sourceId: table._id!,
|
||||||
query: {
|
query: {
|
||||||
empty: {
|
empty: {
|
||||||
name: null,
|
name: null,
|
||||||
|
@ -679,7 +679,7 @@ describe("Google Sheets Integration", () => {
|
||||||
|
|
||||||
it("should be able to find rows with not empty filter", async () => {
|
it("should be able to find rows with not empty filter", async () => {
|
||||||
const response = await config.api.row.search(table._id!, {
|
const response = await config.api.row.search(table._id!, {
|
||||||
tableId: table._id!,
|
sourceId: table._id!,
|
||||||
query: {
|
query: {
|
||||||
notEmpty: {
|
notEmpty: {
|
||||||
name: null,
|
name: null,
|
||||||
|
@ -692,7 +692,7 @@ describe("Google Sheets Integration", () => {
|
||||||
|
|
||||||
it("should be able to find rows with one of filter", async () => {
|
it("should be able to find rows with one of filter", async () => {
|
||||||
const response = await config.api.row.search(table._id!, {
|
const response = await config.api.row.search(table._id!, {
|
||||||
tableId: table._id!,
|
sourceId: table._id!,
|
||||||
query: {
|
query: {
|
||||||
oneOf: {
|
oneOf: {
|
||||||
name: ["Foo", "Bar"],
|
name: ["Foo", "Bar"],
|
||||||
|
@ -707,7 +707,7 @@ describe("Google Sheets Integration", () => {
|
||||||
|
|
||||||
it("should be able to find rows with fuzzy filter", async () => {
|
it("should be able to find rows with fuzzy filter", async () => {
|
||||||
const response = await config.api.row.search(table._id!, {
|
const response = await config.api.row.search(table._id!, {
|
||||||
tableId: table._id!,
|
sourceId: table._id!,
|
||||||
query: {
|
query: {
|
||||||
fuzzy: {
|
fuzzy: {
|
||||||
name: "oo",
|
name: "oo",
|
||||||
|
@ -721,7 +721,7 @@ describe("Google Sheets Integration", () => {
|
||||||
|
|
||||||
it("should be able to find rows with range filter", async () => {
|
it("should be able to find rows with range filter", async () => {
|
||||||
const response = await config.api.row.search(table._id!, {
|
const response = await config.api.row.search(table._id!, {
|
||||||
tableId: table._id!,
|
sourceId: table._id!,
|
||||||
query: {
|
query: {
|
||||||
range: {
|
range: {
|
||||||
name: {
|
name: {
|
||||||
|
@ -750,7 +750,7 @@ describe("Google Sheets Integration", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
let response = await config.api.row.search(table._id!, {
|
let response = await config.api.row.search(table._id!, {
|
||||||
tableId: table._id!,
|
sourceId: table._id!,
|
||||||
query: { equal: { name: "Unique value!" } },
|
query: { equal: { name: "Unique value!" } },
|
||||||
paginate: true,
|
paginate: true,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
|
@ -759,7 +759,7 @@ describe("Google Sheets Integration", () => {
|
||||||
|
|
||||||
while (response.hasNextPage) {
|
while (response.hasNextPage) {
|
||||||
response = await config.api.row.search(table._id!, {
|
response = await config.api.row.search(table._id!, {
|
||||||
tableId: table._id!,
|
sourceId: table._id!,
|
||||||
query: { equal: { name: "Unique value!" } },
|
query: { equal: { name: "Unique value!" } },
|
||||||
paginate: true,
|
paginate: true,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
|
|
|
@ -14,7 +14,6 @@ import {
|
||||||
outputProcessing,
|
outputProcessing,
|
||||||
} from "../../../utilities/rowProcessor"
|
} from "../../../utilities/rowProcessor"
|
||||||
import cloneDeep from "lodash/fp/cloneDeep"
|
import cloneDeep from "lodash/fp/cloneDeep"
|
||||||
import isEqual from "lodash/fp/isEqual"
|
|
||||||
import { tryExtractingTableAndViewId } from "./utils"
|
import { tryExtractingTableAndViewId } from "./utils"
|
||||||
|
|
||||||
export async function getRow(
|
export async function getRow(
|
||||||
|
@ -55,11 +54,7 @@ export async function save(
|
||||||
source = await sdk.tables.getTable(tableId)
|
source = await sdk.tables.getTable(tableId)
|
||||||
}
|
}
|
||||||
|
|
||||||
const { table: updatedTable, row } = await inputProcessing(
|
const row = await inputProcessing(userId, cloneDeep(source), inputs)
|
||||||
userId,
|
|
||||||
cloneDeep(source),
|
|
||||||
inputs
|
|
||||||
)
|
|
||||||
|
|
||||||
const validateResult = await sdk.rows.utils.validate({
|
const validateResult = await sdk.rows.utils.validate({
|
||||||
row,
|
row,
|
||||||
|
@ -73,10 +68,6 @@ export async function save(
|
||||||
row,
|
row,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (sdk.tables.isTable(source) && !isEqual(source, updatedTable)) {
|
|
||||||
await sdk.tables.saveTable(updatedTable)
|
|
||||||
}
|
|
||||||
|
|
||||||
const rowId = response.row._id
|
const rowId = response.row._id
|
||||||
if (rowId) {
|
if (rowId) {
|
||||||
const row = await getRow(source, rowId, {
|
const row = await getRow(source, rowId, {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { context, db } from "@budibase/backend-core"
|
import { context, db } from "@budibase/backend-core"
|
||||||
import { Row } from "@budibase/types"
|
import { Row, Table, ViewV2 } from "@budibase/types"
|
||||||
import sdk from "../../../sdk"
|
import sdk from "../../../sdk"
|
||||||
import cloneDeep from "lodash/fp/cloneDeep"
|
import cloneDeep from "lodash/fp/cloneDeep"
|
||||||
import { finaliseRow } from "../../../api/controllers/row/staticFormula"
|
import { finaliseRow } from "../../../api/controllers/row/staticFormula"
|
||||||
|
@ -10,7 +10,7 @@ import {
|
||||||
import * as linkRows from "../../../db/linkedRows"
|
import * as linkRows from "../../../db/linkedRows"
|
||||||
import { InternalTables } from "../../../db/utils"
|
import { InternalTables } from "../../../db/utils"
|
||||||
import { getFullUser } from "../../../utilities/users"
|
import { getFullUser } from "../../../utilities/users"
|
||||||
import { tryExtractingTableAndViewId } from "./utils"
|
import { getSource, tryExtractingTableAndViewId } from "./utils"
|
||||||
|
|
||||||
export async function save(
|
export async function save(
|
||||||
tableOrViewId: string,
|
tableOrViewId: string,
|
||||||
|
@ -20,21 +20,28 @@ export async function save(
|
||||||
const { tableId, viewId } = tryExtractingTableAndViewId(tableOrViewId)
|
const { tableId, viewId } = tryExtractingTableAndViewId(tableOrViewId)
|
||||||
inputs.tableId = tableId
|
inputs.tableId = tableId
|
||||||
|
|
||||||
|
let source: Table | ViewV2
|
||||||
|
let table: Table
|
||||||
|
if (viewId) {
|
||||||
|
source = await sdk.views.get(viewId)
|
||||||
|
table = await sdk.views.getTable(viewId)
|
||||||
|
} else {
|
||||||
|
source = await sdk.tables.getTable(tableId)
|
||||||
|
table = source
|
||||||
|
}
|
||||||
|
|
||||||
if (!inputs._rev && !inputs._id) {
|
if (!inputs._rev && !inputs._id) {
|
||||||
inputs._id = db.generateRowID(inputs.tableId)
|
inputs._id = db.generateRowID(inputs.tableId)
|
||||||
}
|
}
|
||||||
|
|
||||||
// this returns the table and row incase they have been updated
|
|
||||||
const dbTable = await sdk.tables.getTable(inputs.tableId)
|
|
||||||
|
|
||||||
// need to copy the table so it can be differenced on way out
|
// need to copy the table so it can be differenced on way out
|
||||||
const tableClone = cloneDeep(dbTable)
|
const sourceClone = cloneDeep(source)
|
||||||
|
|
||||||
let { table, row } = await inputProcessing(userId, tableClone, inputs)
|
let row = await inputProcessing(userId, sourceClone, inputs)
|
||||||
|
|
||||||
const validateResult = await sdk.rows.utils.validate({
|
const validateResult = await sdk.rows.utils.validate({
|
||||||
row,
|
row,
|
||||||
table,
|
source,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!validateResult.valid) {
|
if (!validateResult.valid) {
|
||||||
|
@ -49,24 +56,18 @@ export async function save(
|
||||||
table,
|
table,
|
||||||
})) as Row
|
})) as Row
|
||||||
|
|
||||||
return finaliseRow(table, row, {
|
return finaliseRow(table, row, { updateFormula: true })
|
||||||
oldTable: dbTable,
|
}
|
||||||
updateFormula: true,
|
|
||||||
fromViewId: viewId,
|
export async function find(sourceId: string, rowId: string): Promise<Row> {
|
||||||
|
const source = await getSource(sourceId)
|
||||||
|
return await outputProcessing(source, await findRow(sourceId, rowId), {
|
||||||
|
squash: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function find(tableOrViewId: string, rowId: string): Promise<Row> {
|
export async function findRow(sourceId: string, rowId: string) {
|
||||||
const { tableId, viewId } = tryExtractingTableAndViewId(tableOrViewId)
|
const { tableId } = tryExtractingTableAndViewId(sourceId)
|
||||||
|
|
||||||
const table = await sdk.tables.getTable(tableId)
|
|
||||||
let row = await findRow(tableId, rowId)
|
|
||||||
|
|
||||||
row = await outputProcessing(table, row, { squash: true, fromViewId: viewId })
|
|
||||||
return row
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
||||||
|
|
|
@ -64,7 +64,7 @@ export async function exportRows(
|
||||||
result = await outputProcessing(table, response)
|
result = await outputProcessing(table, response)
|
||||||
} else if (query) {
|
} else if (query) {
|
||||||
let searchResponse = await sdk.rows.search({
|
let searchResponse = await sdk.rows.search({
|
||||||
tableId,
|
sourceId: tableId,
|
||||||
query,
|
query,
|
||||||
sort,
|
sort,
|
||||||
sortOrder,
|
sortOrder,
|
||||||
|
|
|
@ -8,21 +8,30 @@ import {
|
||||||
SortType,
|
SortType,
|
||||||
Table,
|
Table,
|
||||||
User,
|
User,
|
||||||
|
ViewV2,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import { getGlobalUsersFromMetadata } from "../../../../../utilities/global"
|
import { getGlobalUsersFromMetadata } from "../../../../../utilities/global"
|
||||||
import { outputProcessing } from "../../../../../utilities/rowProcessor"
|
import { outputProcessing } from "../../../../../utilities/rowProcessor"
|
||||||
import pick from "lodash/pick"
|
import pick from "lodash/pick"
|
||||||
|
import sdk from "../../../../"
|
||||||
|
|
||||||
export async function search(
|
export async function search(
|
||||||
options: RowSearchParams,
|
options: RowSearchParams,
|
||||||
table: Table
|
source: Table | ViewV2
|
||||||
): Promise<SearchResponse<Row>> {
|
): Promise<SearchResponse<Row>> {
|
||||||
const { tableId } = options
|
const { sourceId } = options
|
||||||
|
|
||||||
|
let table: Table
|
||||||
|
if (sdk.views.isView(source)) {
|
||||||
|
table = await sdk.views.getTable(source.id)
|
||||||
|
} else {
|
||||||
|
table = source
|
||||||
|
}
|
||||||
|
|
||||||
const { paginate, query } = options
|
const { paginate, query } = options
|
||||||
|
|
||||||
const params: RowSearchParams = {
|
const params: RowSearchParams = {
|
||||||
tableId: options.tableId,
|
sourceId: options.sourceId,
|
||||||
sort: options.sort,
|
sort: options.sort,
|
||||||
sortOrder: options.sortOrder,
|
sortOrder: options.sortOrder,
|
||||||
sortType: options.sortType,
|
sortType: options.sortType,
|
||||||
|
@ -50,7 +59,7 @@ export async function search(
|
||||||
// Enrich search results with relationships
|
// Enrich search results with relationships
|
||||||
if (response.rows && response.rows.length) {
|
if (response.rows && response.rows.length) {
|
||||||
// enrich with global users if from users table
|
// enrich with global users if from users table
|
||||||
if (tableId === InternalTables.USER_METADATA) {
|
if (sourceId === InternalTables.USER_METADATA) {
|
||||||
response.rows = await getGlobalUsersFromMetadata(response.rows as User[])
|
response.rows = await getGlobalUsersFromMetadata(response.rows as User[])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,9 +68,8 @@ export async function search(
|
||||||
response.rows = response.rows.map((r: any) => pick(r, fields))
|
response.rows = response.rows.map((r: any) => pick(r, fields))
|
||||||
}
|
}
|
||||||
|
|
||||||
response.rows = await outputProcessing(table, response.rows, {
|
response.rows = await outputProcessing(source, response.rows, {
|
||||||
squash: true,
|
squash: true,
|
||||||
fromViewId: options.viewId,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ import {
|
||||||
SortType,
|
SortType,
|
||||||
SqlClient,
|
SqlClient,
|
||||||
Table,
|
Table,
|
||||||
|
ViewV2,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import {
|
import {
|
||||||
buildInternalRelationships,
|
buildInternalRelationships,
|
||||||
|
@ -292,11 +293,18 @@ function resyncDefinitionsRequired(status: number, message: string) {
|
||||||
|
|
||||||
export async function search(
|
export async function search(
|
||||||
options: RowSearchParams,
|
options: RowSearchParams,
|
||||||
table: Table,
|
source: Table | ViewV2,
|
||||||
opts?: { retrying?: boolean }
|
opts?: { retrying?: boolean }
|
||||||
): Promise<SearchResponse<Row>> {
|
): Promise<SearchResponse<Row>> {
|
||||||
let { paginate, query, ...params } = cloneDeep(options)
|
let { paginate, query, ...params } = cloneDeep(options)
|
||||||
|
|
||||||
|
let table: Table
|
||||||
|
if (sdk.views.isView(source)) {
|
||||||
|
table = await sdk.views.getTable(source.id)
|
||||||
|
} else {
|
||||||
|
table = source
|
||||||
|
}
|
||||||
|
|
||||||
const allTables = await sdk.tables.getAllInternalTables()
|
const allTables = await sdk.tables.getAllInternalTables()
|
||||||
const allTablesMap = buildTableMap(allTables)
|
const allTablesMap = buildTableMap(allTables)
|
||||||
// make sure we have the mapped/latest table
|
// make sure we have the mapped/latest table
|
||||||
|
@ -406,7 +414,6 @@ export async function search(
|
||||||
let finalRows = await outputProcessing(table, processed, {
|
let finalRows = await outputProcessing(table, processed, {
|
||||||
preserveLinks: true,
|
preserveLinks: true,
|
||||||
squash: true,
|
squash: true,
|
||||||
fromViewId: options.viewId,
|
|
||||||
aggregations: options.aggregations,
|
aggregations: options.aggregations,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -122,7 +122,7 @@ describe.each([
|
||||||
it("querying by fields will always return data attribute columns", async () => {
|
it("querying by fields will always return data attribute columns", async () => {
|
||||||
await config.doInContext(config.appId, async () => {
|
await config.doInContext(config.appId, async () => {
|
||||||
const { rows } = await search({
|
const { rows } = await search({
|
||||||
tableId: table._id!,
|
sourceId: table._id!,
|
||||||
query: {},
|
query: {},
|
||||||
fields: ["name", "age"],
|
fields: ["name", "age"],
|
||||||
})
|
})
|
||||||
|
@ -142,7 +142,7 @@ describe.each([
|
||||||
it("will decode _id in oneOf query", async () => {
|
it("will decode _id in oneOf query", async () => {
|
||||||
await config.doInContext(config.appId, async () => {
|
await config.doInContext(config.appId, async () => {
|
||||||
const result = await search({
|
const result = await search({
|
||||||
tableId: table._id!,
|
sourceId: table._id!,
|
||||||
query: {
|
query: {
|
||||||
oneOf: {
|
oneOf: {
|
||||||
_id: ["%5B1%5D", "%5B4%5D", "%5B8%5D"],
|
_id: ["%5B1%5D", "%5B4%5D", "%5B8%5D"],
|
||||||
|
@ -174,7 +174,7 @@ describe.each([
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
const result = await search({
|
const result = await search({
|
||||||
tableId: table._id!,
|
sourceId: table._id!,
|
||||||
query: {},
|
query: {},
|
||||||
})
|
})
|
||||||
expect(result.rows).toHaveLength(10)
|
expect(result.rows).toHaveLength(10)
|
||||||
|
@ -205,7 +205,7 @@ describe.each([
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
const result = await search({
|
const result = await search({
|
||||||
tableId: table._id!,
|
sourceId: table._id!,
|
||||||
query: {},
|
query: {},
|
||||||
fields: ["name", "age"],
|
fields: ["name", "age"],
|
||||||
})
|
})
|
||||||
|
@ -229,7 +229,7 @@ describe.each([
|
||||||
async (queryFields, expectedRows) => {
|
async (queryFields, expectedRows) => {
|
||||||
await config.doInContext(config.appId, async () => {
|
await config.doInContext(config.appId, async () => {
|
||||||
const { rows } = await search({
|
const { rows } = await search({
|
||||||
tableId: table._id!,
|
sourceId: table._id!,
|
||||||
query: {
|
query: {
|
||||||
$or: {
|
$or: {
|
||||||
conditions: [
|
conditions: [
|
||||||
|
|
|
@ -48,7 +48,7 @@ describe.each([tableWithUserCol, tableWithUsersCol])(
|
||||||
|
|
||||||
it("should be able to map ro_ to global user IDs", () => {
|
it("should be able to map ro_ to global user IDs", () => {
|
||||||
const params: RowSearchParams = {
|
const params: RowSearchParams = {
|
||||||
tableId,
|
sourceId: tableId,
|
||||||
query: {
|
query: {
|
||||||
equal: {
|
equal: {
|
||||||
"1:user": userMedataId,
|
"1:user": userMedataId,
|
||||||
|
@ -61,7 +61,7 @@ describe.each([tableWithUserCol, tableWithUsersCol])(
|
||||||
|
|
||||||
it("should handle array of user IDs", () => {
|
it("should handle array of user IDs", () => {
|
||||||
const params: RowSearchParams = {
|
const params: RowSearchParams = {
|
||||||
tableId,
|
sourceId: tableId,
|
||||||
query: {
|
query: {
|
||||||
oneOf: {
|
oneOf: {
|
||||||
"1:user": [userMedataId, globalUserId],
|
"1:user": [userMedataId, globalUserId],
|
||||||
|
@ -78,7 +78,7 @@ describe.each([tableWithUserCol, tableWithUsersCol])(
|
||||||
it("shouldn't change any other input", () => {
|
it("shouldn't change any other input", () => {
|
||||||
const email = "test@example.com"
|
const email = "test@example.com"
|
||||||
const params: RowSearchParams = {
|
const params: RowSearchParams = {
|
||||||
tableId,
|
sourceId: tableId,
|
||||||
query: {
|
query: {
|
||||||
equal: {
|
equal: {
|
||||||
"1:user": email,
|
"1:user": email,
|
||||||
|
@ -90,10 +90,8 @@ describe.each([tableWithUserCol, tableWithUsersCol])(
|
||||||
})
|
})
|
||||||
|
|
||||||
it("shouldn't error if no query supplied", () => {
|
it("shouldn't error if no query supplied", () => {
|
||||||
const params: any = {
|
// @ts-expect-error - intentionally passing in a bad type
|
||||||
tableId,
|
const output = searchInputMapping(col, { sourceId: tableId })
|
||||||
}
|
|
||||||
const output = searchInputMapping(col, params)
|
|
||||||
expect(output.query).toBeUndefined()
|
expect(output.query).toBeUndefined()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ describe("validate", () => {
|
||||||
it("should accept empty values", async () => {
|
it("should accept empty values", async () => {
|
||||||
const row = {}
|
const row = {}
|
||||||
const table = getTable()
|
const table = getTable()
|
||||||
const output = await validate({ table, tableId: table._id!, row })
|
const output = await validate({ source: table, row })
|
||||||
expect(output.valid).toBe(true)
|
expect(output.valid).toBe(true)
|
||||||
expect(output.errors).toEqual({})
|
expect(output.errors).toEqual({})
|
||||||
})
|
})
|
||||||
|
@ -43,7 +43,7 @@ describe("validate", () => {
|
||||||
time: `${hour()}:${minute()}`,
|
time: `${hour()}:${minute()}`,
|
||||||
}
|
}
|
||||||
const table = getTable()
|
const table = getTable()
|
||||||
const output = await validate({ table, tableId: table._id!, row })
|
const output = await validate({ source: table, row })
|
||||||
expect(output.valid).toBe(true)
|
expect(output.valid).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ describe("validate", () => {
|
||||||
time: `${hour()}:${minute()}:${second()}`,
|
time: `${hour()}:${minute()}:${second()}`,
|
||||||
}
|
}
|
||||||
const table = getTable()
|
const table = getTable()
|
||||||
const output = await validate({ table, tableId: table._id!, row })
|
const output = await validate({ source: table, row })
|
||||||
expect(output.valid).toBe(true)
|
expect(output.valid).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ describe("validate", () => {
|
||||||
table.schema.time.constraints = {
|
table.schema.time.constraints = {
|
||||||
presence: true,
|
presence: true,
|
||||||
}
|
}
|
||||||
const output = await validate({ table, tableId: table._id!, row })
|
const output = await validate({ source: table, row })
|
||||||
expect(output.valid).toBe(false)
|
expect(output.valid).toBe(false)
|
||||||
expect(output.errors).toEqual({ time: ['"time" is not a valid time'] })
|
expect(output.errors).toEqual({ time: ['"time" is not a valid time'] })
|
||||||
})
|
})
|
||||||
|
@ -91,7 +91,7 @@ describe("validate", () => {
|
||||||
`${generator.integer({ min: 11, max: 23 })}:${minute()}`,
|
`${generator.integer({ min: 11, max: 23 })}:${minute()}`,
|
||||||
])("should accept values after config value (%s)", async time => {
|
])("should accept values after config value (%s)", async time => {
|
||||||
const row = { time }
|
const row = { time }
|
||||||
const output = await validate({ table, tableId: table._id!, row })
|
const output = await validate({ source: table, row })
|
||||||
expect(output.valid).toBe(true)
|
expect(output.valid).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -100,7 +100,7 @@ describe("validate", () => {
|
||||||
`${generator.integer({ min: 0, max: 9 })}:${minute()}`,
|
`${generator.integer({ min: 0, max: 9 })}:${minute()}`,
|
||||||
])("should reject values before config value (%s)", async time => {
|
])("should reject values before config value (%s)", async time => {
|
||||||
const row = { time }
|
const row = { time }
|
||||||
const output = await validate({ table, tableId: table._id!, row })
|
const output = await validate({ source: table, row })
|
||||||
expect(output.valid).toBe(false)
|
expect(output.valid).toBe(false)
|
||||||
expect(output.errors).toEqual({
|
expect(output.errors).toEqual({
|
||||||
time: ["must be no earlier than 10:00"],
|
time: ["must be no earlier than 10:00"],
|
||||||
|
@ -125,7 +125,7 @@ describe("validate", () => {
|
||||||
`${generator.integer({ min: 0, max: 12 })}:${minute()}`,
|
`${generator.integer({ min: 0, max: 12 })}:${minute()}`,
|
||||||
])("should accept values before config value (%s)", async time => {
|
])("should accept values before config value (%s)", async time => {
|
||||||
const row = { time }
|
const row = { time }
|
||||||
const output = await validate({ table, tableId: table._id!, row })
|
const output = await validate({ source: table, row })
|
||||||
expect(output.valid).toBe(true)
|
expect(output.valid).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ describe("validate", () => {
|
||||||
`${generator.integer({ min: 16, max: 23 })}:${minute()}`,
|
`${generator.integer({ min: 16, max: 23 })}:${minute()}`,
|
||||||
])("should reject values after config value (%s)", async time => {
|
])("should reject values after config value (%s)", async time => {
|
||||||
const row = { time }
|
const row = { time }
|
||||||
const output = await validate({ table, tableId: table._id!, row })
|
const output = await validate({ source: table, row })
|
||||||
expect(output.valid).toBe(false)
|
expect(output.valid).toBe(false)
|
||||||
expect(output.errors).toEqual({
|
expect(output.errors).toEqual({
|
||||||
time: ["must be no later than 15:16:17"],
|
time: ["must be no later than 15:16:17"],
|
||||||
|
@ -156,7 +156,7 @@ describe("validate", () => {
|
||||||
"should accept values in range (%s)",
|
"should accept values in range (%s)",
|
||||||
async time => {
|
async time => {
|
||||||
const row = { time }
|
const row = { time }
|
||||||
const output = await validate({ table, tableId: table._id!, row })
|
const output = await validate({ source: table, row })
|
||||||
expect(output.valid).toBe(true)
|
expect(output.valid).toBe(true)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -166,7 +166,7 @@ describe("validate", () => {
|
||||||
`${generator.integer({ min: 0, max: 9 })}:${minute()}`,
|
`${generator.integer({ min: 0, max: 9 })}:${minute()}`,
|
||||||
])("should reject values before range (%s)", async time => {
|
])("should reject values before range (%s)", async time => {
|
||||||
const row = { time }
|
const row = { time }
|
||||||
const output = await validate({ table, tableId: table._id!, row })
|
const output = await validate({ source: table, row })
|
||||||
expect(output.valid).toBe(false)
|
expect(output.valid).toBe(false)
|
||||||
expect(output.errors).toEqual({
|
expect(output.errors).toEqual({
|
||||||
time: ["must be no earlier than 10:00"],
|
time: ["must be no earlier than 10:00"],
|
||||||
|
@ -178,7 +178,7 @@ describe("validate", () => {
|
||||||
`${generator.integer({ min: 16, max: 23 })}:${minute()}`,
|
`${generator.integer({ min: 16, max: 23 })}:${minute()}`,
|
||||||
])("should reject values after range (%s)", async time => {
|
])("should reject values after range (%s)", async time => {
|
||||||
const row = { time }
|
const row = { time }
|
||||||
const output = await validate({ table, tableId: table._id!, row })
|
const output = await validate({ source: table, row })
|
||||||
expect(output.valid).toBe(false)
|
expect(output.valid).toBe(false)
|
||||||
expect(output.errors).toEqual({
|
expect(output.errors).toEqual({
|
||||||
time: ["must be no later than 15:00"],
|
time: ["must be no later than 15:00"],
|
||||||
|
@ -199,7 +199,7 @@ describe("validate", () => {
|
||||||
"should accept values in range (%s)",
|
"should accept values in range (%s)",
|
||||||
async time => {
|
async time => {
|
||||||
const row = { time }
|
const row = { time }
|
||||||
const output = await validate({ table, tableId: table._id!, row })
|
const output = await validate({ source: table, row })
|
||||||
expect(output.valid).toBe(true)
|
expect(output.valid).toBe(true)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -208,7 +208,7 @@ describe("validate", () => {
|
||||||
"should reject values out range (%s)",
|
"should reject values out range (%s)",
|
||||||
async time => {
|
async time => {
|
||||||
const row = { time }
|
const row = { time }
|
||||||
const output = await validate({ table, tableId: table._id!, row })
|
const output = await validate({ source: table, row })
|
||||||
expect(output.valid).toBe(false)
|
expect(output.valid).toBe(false)
|
||||||
expect(output.errors).toEqual({
|
expect(output.errors).toEqual({
|
||||||
time: ["must be no later than 10:00"],
|
time: ["must be no later than 10:00"],
|
||||||
|
@ -226,7 +226,7 @@ describe("validate", () => {
|
||||||
table.schema.time.constraints = {
|
table.schema.time.constraints = {
|
||||||
presence: true,
|
presence: true,
|
||||||
}
|
}
|
||||||
const output = await validate({ table, tableId: table._id!, row })
|
const output = await validate({ source: table, row })
|
||||||
expect(output.valid).toBe(false)
|
expect(output.valid).toBe(false)
|
||||||
expect(output.errors).toEqual({ time: ["can't be blank"] })
|
expect(output.errors).toEqual({ time: ["can't be blank"] })
|
||||||
})
|
})
|
||||||
|
@ -237,7 +237,7 @@ describe("validate", () => {
|
||||||
table.schema.time.constraints = {
|
table.schema.time.constraints = {
|
||||||
presence: true,
|
presence: true,
|
||||||
}
|
}
|
||||||
const output = await validate({ table, tableId: table._id!, row })
|
const output = await validate({ source: table, row })
|
||||||
expect(output.valid).toBe(false)
|
expect(output.valid).toBe(false)
|
||||||
expect(output.errors).toEqual({ time: ["can't be blank"] })
|
expect(output.errors).toEqual({ time: ["can't be blank"] })
|
||||||
})
|
})
|
||||||
|
@ -257,7 +257,7 @@ describe("validate", () => {
|
||||||
"should accept values in range (%s)",
|
"should accept values in range (%s)",
|
||||||
async time => {
|
async time => {
|
||||||
const row = { time }
|
const row = { time }
|
||||||
const output = await validate({ table, tableId: table._id!, row })
|
const output = await validate({ source: table, row })
|
||||||
expect(output.valid).toBe(true)
|
expect(output.valid).toBe(true)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -267,7 +267,7 @@ describe("validate", () => {
|
||||||
`${generator.integer({ min: 0, max: 9 })}:${minute()}`,
|
`${generator.integer({ min: 0, max: 9 })}:${minute()}`,
|
||||||
])("should reject values before range (%s)", async time => {
|
])("should reject values before range (%s)", async time => {
|
||||||
const row = { time }
|
const row = { time }
|
||||||
const output = await validate({ table, tableId: table._id!, row })
|
const output = await validate({ source: table, row })
|
||||||
expect(output.valid).toBe(false)
|
expect(output.valid).toBe(false)
|
||||||
expect(output.errors).toEqual({
|
expect(output.errors).toEqual({
|
||||||
time: ["must be no earlier than 10:00"],
|
time: ["must be no earlier than 10:00"],
|
||||||
|
@ -279,7 +279,7 @@ describe("validate", () => {
|
||||||
`${generator.integer({ min: 16, max: 23 })}:${minute()}`,
|
`${generator.integer({ min: 16, max: 23 })}:${minute()}`,
|
||||||
])("should reject values after range (%s)", async time => {
|
])("should reject values after range (%s)", async time => {
|
||||||
const row = { time }
|
const row = { time }
|
||||||
const output = await validate({ table, tableId: table._id!, row })
|
const output = await validate({ source: table, row })
|
||||||
expect(output.valid).toBe(false)
|
expect(output.valid).toBe(false)
|
||||||
expect(output.errors).toEqual({
|
expect(output.errors).toEqual({
|
||||||
time: ["must be no later than 15:00"],
|
time: ["must be no later than 15:00"],
|
||||||
|
@ -301,7 +301,7 @@ describe("validate", () => {
|
||||||
"should accept values in range (%s)",
|
"should accept values in range (%s)",
|
||||||
async time => {
|
async time => {
|
||||||
const row = { time }
|
const row = { time }
|
||||||
const output = await validate({ table, tableId: table._id!, row })
|
const output = await validate({ source: table, row })
|
||||||
expect(output.valid).toBe(true)
|
expect(output.valid).toBe(true)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -311,7 +311,7 @@ describe("validate", () => {
|
||||||
`${generator.integer({ min: 0, max: 9 })}:${minute()}`,
|
`${generator.integer({ min: 0, max: 9 })}:${minute()}`,
|
||||||
])("should reject values before range (%s)", async time => {
|
])("should reject values before range (%s)", async time => {
|
||||||
const row = { time }
|
const row = { time }
|
||||||
const output = await validate({ table, tableId: table._id!, row })
|
const output = await validate({ source: table, row })
|
||||||
expect(output.valid).toBe(false)
|
expect(output.valid).toBe(false)
|
||||||
expect(output.errors).toEqual({
|
expect(output.errors).toEqual({
|
||||||
time: ["must be no earlier than 10:00"],
|
time: ["must be no earlier than 10:00"],
|
||||||
|
@ -323,7 +323,7 @@ describe("validate", () => {
|
||||||
`${generator.integer({ min: 16, max: 23 })}:${minute()}`,
|
`${generator.integer({ min: 16, max: 23 })}:${minute()}`,
|
||||||
])("should reject values after range (%s)", async time => {
|
])("should reject values after range (%s)", async time => {
|
||||||
const row = { time }
|
const row = { time }
|
||||||
const output = await validate({ table, tableId: table._id!, row })
|
const output = await validate({ source: table, row })
|
||||||
expect(output.valid).toBe(false)
|
expect(output.valid).toBe(false)
|
||||||
expect(output.errors).toEqual({
|
expect(output.errors).toEqual({
|
||||||
time: ["must be no later than 15:00"],
|
time: ["must be no later than 15:00"],
|
||||||
|
|
|
@ -321,3 +321,10 @@ export function tryExtractingTableAndViewId(tableOrViewId: string) {
|
||||||
|
|
||||||
return { tableId: tableOrViewId }
|
return { tableId: tableOrViewId }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getSource(tableOrViewId: string) {
|
||||||
|
if (docIds.isViewId(tableOrViewId)) {
|
||||||
|
return sdk.views.get(tableOrViewId)
|
||||||
|
}
|
||||||
|
return sdk.tables.getTable(tableOrViewId)
|
||||||
|
}
|
||||||
|
|
|
@ -40,7 +40,8 @@ export async function getEnriched(viewId: string): Promise<ViewV2Enriched> {
|
||||||
return pickApi(tableId).getEnriched(viewId)
|
return pickApi(tableId).getEnriched(viewId)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getTable(viewId: string): Promise<Table> {
|
export async function getTable(view: string | ViewV2): Promise<Table> {
|
||||||
|
const viewId = typeof view === "string" ? view : view.id
|
||||||
const cached = context.getTableForView(viewId)
|
const cached = context.getTableForView(viewId)
|
||||||
if (cached) {
|
if (cached) {
|
||||||
return cached
|
return cached
|
||||||
|
|
|
@ -73,6 +73,7 @@ export async function processAutoColumn(
|
||||||
// check its not user table, or whether any of the processing options have been disabled
|
// check its not user table, or whether any of the processing options have been disabled
|
||||||
const shouldUpdateUserFields =
|
const shouldUpdateUserFields =
|
||||||
!isUserTable && !opts?.reprocessing && !opts?.noAutoRelationships && !noUser
|
!isUserTable && !opts?.reprocessing && !opts?.noAutoRelationships && !noUser
|
||||||
|
let tableMutated = false
|
||||||
for (let [key, schema] of Object.entries(table.schema)) {
|
for (let [key, schema] of Object.entries(table.schema)) {
|
||||||
if (!schema.autocolumn) {
|
if (!schema.autocolumn) {
|
||||||
continue
|
continue
|
||||||
|
@ -105,10 +106,17 @@ export async function processAutoColumn(
|
||||||
row[key] = schema.lastID + 1
|
row[key] = schema.lastID + 1
|
||||||
schema.lastID++
|
schema.lastID++
|
||||||
table.schema[key] = schema
|
table.schema[key] = schema
|
||||||
|
tableMutated = true
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tableMutated) {
|
||||||
|
const db = context.getAppDB()
|
||||||
|
const resp = await db.put(table)
|
||||||
|
table._rev = resp.rev
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function processDefaultValues(table: Table, row: Row) {
|
async function processDefaultValues(table: Table, row: Row) {
|
||||||
|
@ -235,8 +243,7 @@ export async function inputProcessing(
|
||||||
|
|
||||||
await processAutoColumn(userId, table, clonedRow, opts)
|
await processAutoColumn(userId, table, clonedRow, opts)
|
||||||
await processDefaultValues(table, clonedRow)
|
await processDefaultValues(table, clonedRow)
|
||||||
|
return clonedRow
|
||||||
return { table, row: clonedRow }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -271,6 +278,14 @@ export async function outputProcessing<T extends Row[] | Row>(
|
||||||
} else {
|
} else {
|
||||||
safeRows = rows
|
safeRows = rows
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let table: Table
|
||||||
|
if (sdk.views.isView(source)) {
|
||||||
|
table = await sdk.views.getTable(source.id)
|
||||||
|
} else {
|
||||||
|
table = source
|
||||||
|
}
|
||||||
|
|
||||||
// attach any linked row information
|
// attach any linked row information
|
||||||
let enriched = !opts.preserveLinks
|
let enriched = !opts.preserveLinks
|
||||||
? await linkRows.attachFullLinkedDocs(table.schema, safeRows, {
|
? await linkRows.attachFullLinkedDocs(table.schema, safeRows, {
|
||||||
|
@ -360,9 +375,7 @@ export async function outputProcessing<T extends Row[] | Row>(
|
||||||
enriched = await processFormulas(table, enriched, { dynamic: true })
|
enriched = await processFormulas(table, enriched, { dynamic: true })
|
||||||
|
|
||||||
if (opts.squash) {
|
if (opts.squash) {
|
||||||
enriched = await linkRows.squashLinks(table, enriched, {
|
enriched = await linkRows.squashLinks(source, enriched)
|
||||||
fromViewId: opts?.fromViewId,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove null properties to match internal API
|
// remove null properties to match internal API
|
||||||
|
|
|
@ -65,7 +65,7 @@ describe("rowProcessor - inputProcessing", () => {
|
||||||
|
|
||||||
processInputBBReferenceMock.mockResolvedValue(user)
|
processInputBBReferenceMock.mockResolvedValue(user)
|
||||||
|
|
||||||
const { row } = await inputProcessing(userId, table, newRow)
|
const row = await inputProcessing(userId, table, newRow)
|
||||||
|
|
||||||
expect(bbReferenceProcessor.processInputBBReference).toHaveBeenCalledTimes(
|
expect(bbReferenceProcessor.processInputBBReference).toHaveBeenCalledTimes(
|
||||||
1
|
1
|
||||||
|
@ -117,7 +117,7 @@ describe("rowProcessor - inputProcessing", () => {
|
||||||
|
|
||||||
processInputBBReferencesMock.mockResolvedValue(user)
|
processInputBBReferencesMock.mockResolvedValue(user)
|
||||||
|
|
||||||
const { row } = await inputProcessing(userId, table, newRow)
|
const row = await inputProcessing(userId, table, newRow)
|
||||||
|
|
||||||
expect(bbReferenceProcessor.processInputBBReferences).toHaveBeenCalledTimes(
|
expect(bbReferenceProcessor.processInputBBReferences).toHaveBeenCalledTimes(
|
||||||
1
|
1
|
||||||
|
@ -164,7 +164,7 @@ describe("rowProcessor - inputProcessing", () => {
|
||||||
name: "Jack",
|
name: "Jack",
|
||||||
}
|
}
|
||||||
|
|
||||||
const { row } = await inputProcessing(userId, table, newRow)
|
const row = await inputProcessing(userId, table, newRow)
|
||||||
|
|
||||||
expect(bbReferenceProcessor.processInputBBReferences).not.toHaveBeenCalled()
|
expect(bbReferenceProcessor.processInputBBReferences).not.toHaveBeenCalled()
|
||||||
expect(row).toEqual({ ...newRow, user: undefined })
|
expect(row).toEqual({ ...newRow, user: undefined })
|
||||||
|
@ -207,7 +207,7 @@ describe("rowProcessor - inputProcessing", () => {
|
||||||
user: userValue,
|
user: userValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
const { row } = await inputProcessing(userId, table, newRow)
|
const row = await inputProcessing(userId, table, newRow)
|
||||||
|
|
||||||
if (userValue === undefined) {
|
if (userValue === undefined) {
|
||||||
// The 'user' field is omitted
|
// The 'user' field is omitted
|
||||||
|
@ -262,7 +262,7 @@ describe("rowProcessor - inputProcessing", () => {
|
||||||
user: "123",
|
user: "123",
|
||||||
}
|
}
|
||||||
|
|
||||||
const { row } = await inputProcessing(userId, table, newRow)
|
const row = await inputProcessing(userId, table, newRow)
|
||||||
|
|
||||||
expect(bbReferenceProcessor.processInputBBReferences).not.toHaveBeenCalled()
|
expect(bbReferenceProcessor.processInputBBReferences).not.toHaveBeenCalled()
|
||||||
expect(row).toEqual({
|
expect(row).toEqual({
|
||||||
|
|
Loading…
Reference in New Issue