Merge branch 'master' into v3-ui

This commit is contained in:
deanhannigan 2024-09-26 09:13:56 +01:00 committed by GitHub
commit 577dfb0d17
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 198 additions and 19 deletions

View File

@ -1,6 +1,6 @@
{
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
"version": "2.32.7",
"version": "2.32.8",
"npmClient": "yarn",
"packages": [
"packages/*",

@ -1 +1 @@
Subproject commit 905773d70854a43c6ef2461c7a49671bff56fedc
Subproject commit 558a32dfd1f55bd894804a503e7e1090937df88c

View File

@ -3,6 +3,7 @@ import * as context from "../context"
import { PostHog, PostHogOptions } from "posthog-node"
import { FeatureFlag, IdentityType, UserCtx } from "@budibase/types"
import tracer from "dd-trace"
import { Duration } from "../utils"
let posthog: PostHog | undefined
export function init(opts?: PostHogOptions) {
@ -16,6 +17,7 @@ export function init(opts?: PostHogOptions) {
posthog = new PostHog(env.POSTHOG_TOKEN, {
host: env.POSTHOG_API_HOST,
personalApiKey: env.POSTHOG_PERSONAL_TOKEN,
featureFlagsPollingInterval: Duration.fromMinutes(3).toMs(),
...opts,
})
} else {

View File

@ -396,6 +396,11 @@
padding: 6px 10px;
margin-bottom: 8px;
}
.compact .placeholder {
height: fit-content;
}
.title {
display: flex;
flex-direction: row;

View File

@ -22,6 +22,8 @@ import {
TableSchema,
ViewFieldMetadata,
RenameColumn,
FeatureFlag,
BBReferenceFieldSubType,
} from "@budibase/types"
import { generator, mocks } from "@budibase/backend-core/tests"
import { DatabaseName, getDatasource } from "../../../integrations/tests/utils"
@ -32,6 +34,7 @@ import {
roles,
withEnv as withCoreEnv,
setEnv as setCoreEnv,
env,
} from "@budibase/backend-core"
import sdk from "../../../sdk"
@ -694,22 +697,23 @@ describe.each([
)
})
it("cannot update views v1", async () => {
const viewV1 = await config.api.legacyView.save({
tableId: table._id!,
name: generator.guid(),
filters: [],
schema: {},
})
isInternal &&
it("cannot update views v1", async () => {
const viewV1 = await config.api.legacyView.save({
tableId: table._id!,
name: generator.guid(),
filters: [],
schema: {},
})
await config.api.viewV2.update(viewV1 as unknown as ViewV2, {
status: 400,
body: {
message: "Only views V2 can be updated",
await config.api.viewV2.update(viewV1 as unknown as ViewV2, {
status: 400,
},
body: {
message: "Only views V2 can be updated",
status: 400,
},
})
})
})
it("cannot update the a view with unmatching ids between url and body", async () => {
const anotherView = await config.api.viewV2.create({
@ -2213,6 +2217,171 @@ describe.each([
})
)
})
describe("foreign relationship columns", () => {
let envCleanup: () => void
beforeAll(() => {
const flags = [`*:${FeatureFlag.ENRICHED_RELATIONSHIPS}`]
if (env.TENANT_FEATURE_FLAGS) {
flags.push(...env.TENANT_FEATURE_FLAGS.split(","))
}
envCleanup = setCoreEnv({
TENANT_FEATURE_FLAGS: flags.join(","),
})
})
afterAll(() => {
envCleanup?.()
})
const createMainTable = async (
links: {
name: string
tableId: string
fk: string
}[]
) => {
const table = await config.api.table.save(
saveTableRequest({
schema: { title: { name: "title", type: FieldType.STRING } },
})
)
await config.api.table.save({
...table,
schema: {
...table.schema,
...links.reduce<TableSchema>((acc, c) => {
acc[c.name] = {
name: c.name,
relationshipType: RelationshipType.ONE_TO_MANY,
type: FieldType.LINK,
tableId: c.tableId,
fieldName: c.fk,
constraints: { type: "array" },
}
return acc
}, {}),
},
})
return table
}
const createAuxTable = (schema: TableSchema) =>
config.api.table.save(
saveTableRequest({
primaryDisplay: "name",
schema: {
...schema,
name: { name: "name", type: FieldType.STRING },
},
})
)
it("returns squashed fields respecting the view config", async () => {
const auxTable = await createAuxTable({
age: { name: "age", type: FieldType.NUMBER },
})
const auxRow = await config.api.row.save(auxTable._id!, {
name: generator.name(),
age: generator.age(),
})
const table = await createMainTable([
{ name: "aux", tableId: auxTable._id!, fk: "fk_aux" },
])
await config.api.row.save(table._id!, {
title: generator.word(),
aux: [auxRow],
})
const view = await config.api.viewV2.create({
tableId: table._id!,
name: generator.guid(),
schema: {
title: { visible: true },
aux: {
visible: true,
columns: {
name: { visible: false, readonly: false },
age: { visible: true, readonly: true },
},
},
},
})
const response = await config.api.viewV2.search(view.id)
expect(response.rows).toEqual([
expect.objectContaining({
aux: [
{
_id: auxRow._id,
primaryDisplay: auxRow.name,
age: auxRow.age,
},
],
}),
])
})
it("enriches squashed fields", async () => {
const auxTable = await createAuxTable({
user: {
name: "user",
type: FieldType.BB_REFERENCE_SINGLE,
subtype: BBReferenceFieldSubType.USER,
constraints: { presence: true },
},
})
const table = await createMainTable([
{ name: "aux", tableId: auxTable._id!, fk: "fk_aux" },
])
const user = config.getUser()
const auxRow = await config.api.row.save(auxTable._id!, {
name: generator.name(),
user: user._id,
})
await config.api.row.save(table._id!, {
title: generator.word(),
aux: [auxRow],
})
const view = await config.api.viewV2.create({
tableId: table._id!,
name: generator.guid(),
schema: {
title: { visible: true },
aux: {
visible: true,
columns: {
name: { visible: true, readonly: true },
user: { visible: true, readonly: true },
},
},
},
})
const response = await config.api.viewV2.search(view.id)
expect(response.rows).toEqual([
expect.objectContaining({
aux: [
{
_id: auxRow._id,
primaryDisplay: auxRow.name,
name: auxRow.name,
user: {
_id: user._id,
email: user.email,
firstName: user.firstName,
lastName: user.lastName,
primaryDisplay: user.email,
},
},
],
}),
])
})
})
})
describe("permissions", () => {

View File

@ -10,7 +10,7 @@ import flatten from "lodash/flatten"
import { USER_METDATA_PREFIX } from "../utils"
import partition from "lodash/partition"
import { getGlobalUsersFromMetadata } from "../../utilities/global"
import { processFormulas } from "../../utilities/rowProcessor"
import { outputProcessing, processFormulas } from "../../utilities/rowProcessor"
import { context, features } from "@budibase/backend-core"
import {
ContextUser,
@ -275,7 +275,7 @@ export async function squashLinks<T = Row[] | Row>(
// will populate this as we find them
const linkedTables = [table]
const isArray = Array.isArray(enriched)
const enrichedArray = !isArray ? [enriched] : enriched
const enrichedArray = !isArray ? [enriched as Row] : (enriched as Row[])
for (const row of enrichedArray) {
// this only fetches the table if its not already in array
const rowTable = await getLinkedTable(row.tableId!, linkedTables)
@ -292,6 +292,9 @@ export async function squashLinks<T = Row[] | Row>(
obj.primaryDisplay = getPrimaryDisplayValue(link, linkedTable)
if (viewSchema[column]?.columns) {
const enrichedLink = await outputProcessing(linkedTable, link, {
squash: false,
})
const squashFields = Object.entries(viewSchema[column].columns)
.filter(([columnName, viewColumnConfig]) => {
const tableColumn = linkedTable.schema[columnName]
@ -312,7 +315,7 @@ export async function squashLinks<T = Row[] | Row>(
.map(([columnName]) => columnName)
for (const relField of squashFields) {
obj[relField] = link[relField]
obj[relField] = enrichedLink[relField]
}
}
@ -321,5 +324,5 @@ export async function squashLinks<T = Row[] | Row>(
row[column] = newLinks
}
}
return isArray ? enrichedArray : enrichedArray[0]
return (isArray ? enrichedArray : enrichedArray[0]) as T
}