From 1504cead0c06485fcb05ff280360deb8cf7aa4fa Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 21 Aug 2024 17:07:49 +0200 Subject: [PATCH 001/144] Persist view related schemas --- .../src/api/controllers/view/viewsV2.ts | 29 ++++++++++++++++--- .../src/api/routes/tests/viewV2.spec.ts | 4 +-- packages/server/src/sdk/app/views/index.ts | 4 +-- .../types/src/documents/app/table/schema.ts | 9 ++++++ packages/types/src/documents/app/view.ts | 12 ++++++-- 5 files changed, 47 insertions(+), 11 deletions(-) diff --git a/packages/server/src/api/controllers/view/viewsV2.ts b/packages/server/src/api/controllers/view/viewsV2.ts index 4208772fa6..f341b853c6 100644 --- a/packages/server/src/api/controllers/view/viewsV2.ts +++ b/packages/server/src/api/controllers/view/viewsV2.ts @@ -3,11 +3,12 @@ import { CreateViewRequest, Ctx, RequiredKeys, - ViewUIFieldMetadata, UpdateViewRequest, ViewResponse, ViewResponseEnriched, ViewV2, + ViewFieldMetadata, + RelationSchemaField, } from "@budibase/types" import { builderSocket, gridSocket } from "../../../websockets" @@ -18,21 +19,41 @@ async function parseSchema(view: CreateViewRequest) { const finalViewSchema = view.schema && Object.entries(view.schema).reduce((p, [fieldName, schemaValue]) => { - const fieldSchema: RequiredKeys = { + let fieldRelatedSchema: + | Record> + | undefined + if (schemaValue.schema) { + fieldRelatedSchema = Object.entries(schemaValue.schema).reduce< + NonNullable + >((acc, [key, fieldSchema]) => { + acc[key] = { + visible: fieldSchema.visible, + readonly: fieldSchema.readonly, + } + return acc + }, {}) + } + + const fieldSchema: RequiredKeys< + ViewFieldMetadata & { + schema: typeof fieldRelatedSchema + } + > = { order: schemaValue.order, width: schemaValue.width, visible: schemaValue.visible, readonly: schemaValue.readonly, icon: schemaValue.icon, + schema: fieldRelatedSchema, } Object.entries(fieldSchema) .filter(([, val]) => val === undefined) .forEach(([key]) => { - delete fieldSchema[key as keyof ViewUIFieldMetadata] + delete fieldSchema[key as keyof ViewFieldMetadata] }) p[fieldName] = fieldSchema return p - }, {} as Record>) + }, {} as Record>) return finalViewSchema } diff --git a/packages/server/src/api/routes/tests/viewV2.spec.ts b/packages/server/src/api/routes/tests/viewV2.spec.ts index 4401efc480..ef4deacd83 100644 --- a/packages/server/src/api/routes/tests/viewV2.spec.ts +++ b/packages/server/src/api/routes/tests/viewV2.spec.ts @@ -15,7 +15,7 @@ import { Table, TableSourceType, UpdateViewRequest, - ViewUIFieldMetadata, + ViewFieldMetadata, ViewV2, SearchResponse, BasicOperator, @@ -953,7 +953,7 @@ describe.each([ const updatedTable = await config.api.table.get(table._id!) const viewSchema = updatedTable.views![view!.name!].schema as Record< string, - ViewUIFieldMetadata + ViewFieldMetadata > expect(viewSchema.Price?.visible).toEqual(false) expect(viewSchema.Category?.visible).toEqual(true) diff --git a/packages/server/src/sdk/app/views/index.ts b/packages/server/src/sdk/app/views/index.ts index 1c09f710d7..bed6254943 100644 --- a/packages/server/src/sdk/app/views/index.ts +++ b/packages/server/src/sdk/app/views/index.ts @@ -2,7 +2,7 @@ import { RenameColumn, TableSchema, View, - ViewUIFieldMetadata, + ViewFieldMetadata, ViewV2, ViewV2Enriched, } from "@budibase/types" @@ -58,7 +58,7 @@ async function guardViewSchema( if (viewSchema[field].readonly) { if ( !(await features.isViewReadonlyColumnsEnabled()) && - !(tableSchemaField as ViewUIFieldMetadata).readonly + !(tableSchemaField as ViewFieldMetadata).readonly ) { throw new HTTPError(`Readonly fields are not enabled`, 400) } diff --git a/packages/types/src/documents/app/table/schema.ts b/packages/types/src/documents/app/table/schema.ts index 6078f73d1d..7b65837ecd 100644 --- a/packages/types/src/documents/app/table/schema.ts +++ b/packages/types/src/documents/app/table/schema.ts @@ -25,8 +25,17 @@ interface BaseRelationshipFieldMetadata tableId: string tableRev?: string subtype?: AutoFieldSubType.CREATED_BY | AutoFieldSubType.UPDATED_BY + schema: RelationFieldSchema } +export type RelationFieldSchema = Record< + string, + { + visible?: boolean + readonly?: boolean + } +> + // External tables use junction tables, internal tables don't require them type ManyToManyJunctionTableMetadata = | { diff --git a/packages/types/src/documents/app/view.ts b/packages/types/src/documents/app/view.ts index 2572ddba5d..2d9b22e773 100644 --- a/packages/types/src/documents/app/view.ts +++ b/packages/types/src/documents/app/view.ts @@ -1,5 +1,5 @@ import { SearchFilter, SortOrder, SortType } from "../../api" -import { UIFieldMetadata } from "./table" +import { RelationFieldSchema, UIFieldMetadata } from "./table" import { Document } from "../document" import { DBView } from "../../sdk" @@ -33,10 +33,16 @@ export interface View { groupBy?: string } -export type ViewUIFieldMetadata = UIFieldMetadata & { +export type RelationSchemaField = { + visible?: boolean readonly?: boolean } +export type ViewFieldMetadata = UIFieldMetadata & { + readonly?: boolean + schema?: Record +} + export interface ViewV2 { version: 2 id: string @@ -49,7 +55,7 @@ export interface ViewV2 { order?: SortOrder type?: SortType } - schema?: Record + schema?: Record } export type ViewSchema = ViewCountOrSumSchema | ViewStatisticsSchema From 64679565bc972bf61b9c47713bcaa4cb72b6bf1c Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 21 Aug 2024 17:26:21 +0200 Subject: [PATCH 002/144] Lint --- packages/types/src/documents/app/view.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/types/src/documents/app/view.ts b/packages/types/src/documents/app/view.ts index 2d9b22e773..3358c18d6d 100644 --- a/packages/types/src/documents/app/view.ts +++ b/packages/types/src/documents/app/view.ts @@ -1,5 +1,5 @@ import { SearchFilter, SortOrder, SortType } from "../../api" -import { RelationFieldSchema, UIFieldMetadata } from "./table" +import { UIFieldMetadata } from "./table" import { Document } from "../document" import { DBView } from "../../sdk" From 34054849774c2bc5ac0708987c6fd7262223a226 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 21 Aug 2024 17:28:01 +0200 Subject: [PATCH 003/144] Fix required ts --- packages/types/src/documents/app/table/schema.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/types/src/documents/app/table/schema.ts b/packages/types/src/documents/app/table/schema.ts index 7b65837ecd..837477e1e1 100644 --- a/packages/types/src/documents/app/table/schema.ts +++ b/packages/types/src/documents/app/table/schema.ts @@ -25,7 +25,7 @@ interface BaseRelationshipFieldMetadata tableId: string tableRev?: string subtype?: AutoFieldSubType.CREATED_BY | AutoFieldSubType.UPDATED_BY - schema: RelationFieldSchema + schema?: RelationFieldSchema } export type RelationFieldSchema = Record< From e8f33e5fd8882476578a10939c7b6ddcf48f9ad6 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 21 Aug 2024 17:43:31 +0200 Subject: [PATCH 004/144] Reuse --- packages/types/src/documents/app/table/schema.ts | 13 +++++-------- packages/types/src/documents/app/view.ts | 7 +------ 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/packages/types/src/documents/app/table/schema.ts b/packages/types/src/documents/app/table/schema.ts index 837477e1e1..9907834c3d 100644 --- a/packages/types/src/documents/app/table/schema.ts +++ b/packages/types/src/documents/app/table/schema.ts @@ -25,16 +25,13 @@ interface BaseRelationshipFieldMetadata tableId: string tableRev?: string subtype?: AutoFieldSubType.CREATED_BY | AutoFieldSubType.UPDATED_BY - schema?: RelationFieldSchema + schema?: Record } -export type RelationFieldSchema = Record< - string, - { - visible?: boolean - readonly?: boolean - } -> +export type RelationSchemaField = { + visible?: boolean + readonly?: boolean +} // External tables use junction tables, internal tables don't require them type ManyToManyJunctionTableMetadata = diff --git a/packages/types/src/documents/app/view.ts b/packages/types/src/documents/app/view.ts index 3358c18d6d..e6a4d02394 100644 --- a/packages/types/src/documents/app/view.ts +++ b/packages/types/src/documents/app/view.ts @@ -1,5 +1,5 @@ import { SearchFilter, SortOrder, SortType } from "../../api" -import { UIFieldMetadata } from "./table" +import { RelationSchemaField, UIFieldMetadata } from "./table" import { Document } from "../document" import { DBView } from "../../sdk" @@ -33,11 +33,6 @@ export interface View { groupBy?: string } -export type RelationSchemaField = { - visible?: boolean - readonly?: boolean -} - export type ViewFieldMetadata = UIFieldMetadata & { readonly?: boolean schema?: Record From de61754312dd029c08e0ad58592b069fb4513d25 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 21 Aug 2024 11:10:50 +0200 Subject: [PATCH 005/144] Enrich relationship from backend --- .../server/src/api/controllers/table/index.ts | 4 +- packages/server/src/sdk/app/tables/getters.ts | 48 +++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/packages/server/src/api/controllers/table/index.ts b/packages/server/src/api/controllers/table/index.ts index ee6947cec8..5e40f53d59 100644 --- a/packages/server/src/api/controllers/table/index.ts +++ b/packages/server/src/api/controllers/table/index.ts @@ -91,7 +91,9 @@ export async function find(ctx: UserCtx) { const tableId = ctx.params.tableId const table = await sdk.tables.getTable(tableId) - ctx.body = sdk.tables.enrichViewSchemas(table) + let result = await sdk.tables.enrichRelationshipSchemas(table) + result = sdk.tables.enrichViewSchemas(result) + ctx.body = result } export async function save(ctx: UserCtx) { diff --git a/packages/server/src/sdk/app/tables/getters.ts b/packages/server/src/sdk/app/tables/getters.ts index 725c4e5cd2..fd6ea8494a 100644 --- a/packages/server/src/sdk/app/tables/getters.ts +++ b/packages/server/src/sdk/app/tables/getters.ts @@ -7,7 +7,9 @@ import { } from "../../../integrations/utils" import { Database, + FieldType, INTERNAL_TABLE_SOURCE_ID, + RelationshipFieldMetadata, Table, TableResponse, TableSourceType, @@ -142,6 +144,52 @@ export async function getTables(tableIds: string[]): Promise { return processTables(tables) } +export async function enrichRelationshipSchemas( + table: Table +): Promise { + const tableCache: Record = {} + + async function populateRelTableSchema(field: RelationshipFieldMetadata) { + if (!tableCache[field.tableId]) { + tableCache[field.tableId] = await sdk.tables.getTable(field.tableId) + } + const relTable = tableCache[field.tableId] + + for (const relTableFieldName of Object.keys(relTable.schema)) { + const relTableField = relTable.schema[relTableFieldName] + if (relTableField.type === FieldType.LINK) { + continue + } + + if (relTableField.visible === false) { + continue + } + + field.schema ??= {} + const isPrimaryDisplay = relTableFieldName === relTable.primaryDisplay + const isReadonly = + isPrimaryDisplay || !!field.schema[relTableFieldName]?.readonly + field.schema[relTableFieldName] = { + primaryDisplay: isPrimaryDisplay, + type: relTableField.type, + visible: isReadonly, + readonly: isReadonly, + } + } + } + + const result: TableResponse = { ...table, schema: {}, views: {} } + for (const fieldName of Object.keys(table.schema)) { + const field = { ...table.schema[fieldName] } + if (field.type === FieldType.LINK) { + await populateRelTableSchema(field) + } + + result.schema[fieldName] = field + } + return result +} + export function enrichViewSchemas(table: Table): TableResponse { return { ...table, From 0e468b04de043f604f0c713d799d8aa63455be58 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 21 Aug 2024 16:33:21 +0200 Subject: [PATCH 006/144] Fix build --- packages/server/src/sdk/app/tables/getters.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/server/src/sdk/app/tables/getters.ts b/packages/server/src/sdk/app/tables/getters.ts index fd6ea8494a..78633feed5 100644 --- a/packages/server/src/sdk/app/tables/getters.ts +++ b/packages/server/src/sdk/app/tables/getters.ts @@ -170,10 +170,11 @@ export async function enrichRelationshipSchemas( const isReadonly = isPrimaryDisplay || !!field.schema[relTableFieldName]?.readonly field.schema[relTableFieldName] = { - primaryDisplay: isPrimaryDisplay, - type: relTableField.type, visible: isReadonly, readonly: isReadonly, + // @ts-ignore this would require a big refactor around field typings, having document types and api dtos + primaryDisplay: isPrimaryDisplay, + type: relTableField.type, } } } From c2e643cebba651d99e78fc19e89d587efb25b2dd Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 21 Aug 2024 17:56:44 +0200 Subject: [PATCH 007/144] Fix enrichments --- packages/server/src/api/controllers/table/index.ts | 7 +++++-- packages/server/src/sdk/app/tables/getters.ts | 9 +++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/server/src/api/controllers/table/index.ts b/packages/server/src/api/controllers/table/index.ts index 5e40f53d59..22c4e98524 100644 --- a/packages/server/src/api/controllers/table/index.ts +++ b/packages/server/src/api/controllers/table/index.ts @@ -91,8 +91,11 @@ export async function find(ctx: UserCtx) { const tableId = ctx.params.tableId const table = await sdk.tables.getTable(tableId) - let result = await sdk.tables.enrichRelationshipSchemas(table) - result = sdk.tables.enrichViewSchemas(result) + const enrichedSchema = await sdk.tables.enrichRelationshipSchema(table) + const result = sdk.tables.enrichViewSchemas({ + ...table, + schema: enrichedSchema, + }) ctx.body = result } diff --git a/packages/server/src/sdk/app/tables/getters.ts b/packages/server/src/sdk/app/tables/getters.ts index 78633feed5..ded7f5ef09 100644 --- a/packages/server/src/sdk/app/tables/getters.ts +++ b/packages/server/src/sdk/app/tables/getters.ts @@ -12,6 +12,7 @@ import { RelationshipFieldMetadata, Table, TableResponse, + TableSchema, TableSourceType, TableViewsResponse, } from "@budibase/types" @@ -144,9 +145,9 @@ export async function getTables(tableIds: string[]): Promise { return processTables(tables) } -export async function enrichRelationshipSchemas( +export async function enrichRelationshipSchema( table: Table -): Promise { +): Promise { const tableCache: Record = {} async function populateRelTableSchema(field: RelationshipFieldMetadata) { @@ -179,14 +180,14 @@ export async function enrichRelationshipSchemas( } } - const result: TableResponse = { ...table, schema: {}, views: {} } + const result: TableSchema = {} for (const fieldName of Object.keys(table.schema)) { const field = { ...table.schema[fieldName] } if (field.type === FieldType.LINK) { await populateRelTableSchema(field) } - result.schema[fieldName] = field + result[fieldName] = field } return result } From 906abbb12af46f88bd42f44b6e33f04f0261c934 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 22 Aug 2024 11:20:43 +0200 Subject: [PATCH 008/144] Don't return primarydisplay and type --- packages/server/src/sdk/app/tables/getters.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/server/src/sdk/app/tables/getters.ts b/packages/server/src/sdk/app/tables/getters.ts index ded7f5ef09..39e43b38bc 100644 --- a/packages/server/src/sdk/app/tables/getters.ts +++ b/packages/server/src/sdk/app/tables/getters.ts @@ -173,9 +173,6 @@ export async function enrichRelationshipSchema( field.schema[relTableFieldName] = { visible: isReadonly, readonly: isReadonly, - // @ts-ignore this would require a big refactor around field typings, having document types and api dtos - primaryDisplay: isPrimaryDisplay, - type: relTableField.type, } } } From 55d4e2dffea7be2187e4e167fa7cc259983b00ae Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 22 Aug 2024 12:05:24 +0200 Subject: [PATCH 009/144] Enrich view schemas --- .../server/src/api/controllers/table/index.ts | 3 +- packages/server/src/sdk/app/tables/getters.ts | 6 +- packages/server/src/sdk/app/views/external.ts | 2 +- packages/server/src/sdk/app/views/index.ts | 7 +- packages/server/src/sdk/app/views/internal.ts | 2 +- .../src/sdk/app/views/tests/views.spec.ts | 90 +++++++++++++++++-- 6 files changed, 96 insertions(+), 14 deletions(-) diff --git a/packages/server/src/api/controllers/table/index.ts b/packages/server/src/api/controllers/table/index.ts index 22c4e98524..be49d66c82 100644 --- a/packages/server/src/api/controllers/table/index.ts +++ b/packages/server/src/api/controllers/table/index.ts @@ -91,10 +91,9 @@ export async function find(ctx: UserCtx) { const tableId = ctx.params.tableId const table = await sdk.tables.getTable(tableId) - const enrichedSchema = await sdk.tables.enrichRelationshipSchema(table) const result = sdk.tables.enrichViewSchemas({ ...table, - schema: enrichedSchema, + schema: await sdk.tables.enrichRelationshipSchema(table.schema), }) ctx.body = result } diff --git a/packages/server/src/sdk/app/tables/getters.ts b/packages/server/src/sdk/app/tables/getters.ts index 39e43b38bc..5a505fd5b4 100644 --- a/packages/server/src/sdk/app/tables/getters.ts +++ b/packages/server/src/sdk/app/tables/getters.ts @@ -146,7 +146,7 @@ export async function getTables(tableIds: string[]): Promise { } export async function enrichRelationshipSchema( - table: Table + schema: TableSchema ): Promise { const tableCache: Record = {} @@ -178,8 +178,8 @@ export async function enrichRelationshipSchema( } const result: TableSchema = {} - for (const fieldName of Object.keys(table.schema)) { - const field = { ...table.schema[fieldName] } + for (const fieldName of Object.keys(schema)) { + const field = { ...schema[fieldName] } if (field.type === FieldType.LINK) { await populateRelTableSchema(field) } diff --git a/packages/server/src/sdk/app/views/external.ts b/packages/server/src/sdk/app/views/external.ts index 2b3e271597..3afd7e9bf9 100644 --- a/packages/server/src/sdk/app/views/external.ts +++ b/packages/server/src/sdk/app/views/external.ts @@ -33,7 +33,7 @@ export async function getEnriched(viewId: string): Promise { if (!found) { throw new Error("No view found") } - return enrichSchema(found, table.schema) + return await enrichSchema(found, table.schema) } export async function create( diff --git a/packages/server/src/sdk/app/views/index.ts b/packages/server/src/sdk/app/views/index.ts index bed6254943..3e33186797 100644 --- a/packages/server/src/sdk/app/views/index.ts +++ b/packages/server/src/sdk/app/views/index.ts @@ -153,11 +153,12 @@ export function allowedFields(view: View | ViewV2) { ] } -export function enrichSchema( +export async function enrichSchema( view: ViewV2, tableSchema: TableSchema -): ViewV2Enriched { +): Promise { let schema = cloneDeep(tableSchema) + const anyViewOrder = Object.values(view.schema || {}).some( ui => ui.order != null ) @@ -171,6 +172,8 @@ export function enrichSchema( } } + schema = await sdk.tables.enrichRelationshipSchema(schema) + return { ...view, schema: schema, diff --git a/packages/server/src/sdk/app/views/internal.ts b/packages/server/src/sdk/app/views/internal.ts index 7b2f9f6c80..19a9f6ab14 100644 --- a/packages/server/src/sdk/app/views/internal.ts +++ b/packages/server/src/sdk/app/views/internal.ts @@ -24,7 +24,7 @@ export async function getEnriched(viewId: string): Promise { if (!found) { throw new Error("No view found") } - return enrichSchema(found, table.schema) + return await enrichSchema(found, table.schema) } export async function create( diff --git a/packages/server/src/sdk/app/views/tests/views.spec.ts b/packages/server/src/sdk/app/views/tests/views.spec.ts index 508285651a..265e6b159f 100644 --- a/packages/server/src/sdk/app/views/tests/views.spec.ts +++ b/packages/server/src/sdk/app/views/tests/views.spec.ts @@ -3,6 +3,7 @@ import { FieldSchema, FieldType, INTERNAL_TABLE_SOURCE_ID, + RelationshipType, Table, TableSchema, TableSourceType, @@ -10,6 +11,7 @@ import { } from "@budibase/types" import { generator } from "@budibase/backend-core/tests" import { enrichSchema, syncSchema } from ".." +import sdk from "../../../../sdk" describe("table sdk", () => { const basicTable: Table = { @@ -68,7 +70,7 @@ describe("table sdk", () => { tableId, } - const res = enrichSchema(view, basicTable.schema) + const res = await enrichSchema(view, basicTable.schema) expect(res).toEqual({ ...view, @@ -126,7 +128,7 @@ describe("table sdk", () => { }, } - const res = enrichSchema(view, basicTable.schema) + const res = await enrichSchema(view, basicTable.schema) expect(res).toEqual({ ...view, @@ -164,7 +166,7 @@ describe("table sdk", () => { }, } - const res = enrichSchema(view, basicTable.schema) + const res = await enrichSchema(view, basicTable.schema) expect(res).toEqual({ ...view, @@ -203,7 +205,7 @@ describe("table sdk", () => { }, } - const res = enrichSchema(view, basicTable.schema) + const res = await enrichSchema(view, basicTable.schema) expect(res).toEqual( expect.objectContaining({ @@ -258,7 +260,7 @@ describe("table sdk", () => { }, } - const res = enrichSchema(view, basicTable.schema) + const res = await enrichSchema(view, basicTable.schema) expect(res).toEqual( expect.objectContaining({ @@ -298,6 +300,84 @@ describe("table sdk", () => { }) ) }) + + it("should include related fields", async () => { + const table: Table = { + ...basicTable, + schema: { + name: { + name: "name", + type: FieldType.STRING, + }, + other: { + name: "other", + type: FieldType.LINK, + relationshipType: RelationshipType.ONE_TO_MANY, + fieldName: "table", + tableId: "otherTableId", + }, + }, + } + + const otherTable: Table = { + ...basicTable, + primaryDisplay: "title", + schema: { + title: { + name: "title", + type: FieldType.STRING, + }, + age: { + name: "age", + type: FieldType.NUMBER, + }, + }, + } + + const tableId = table._id! + + const getTableSpy = jest.spyOn(sdk.tables, "getTable") + getTableSpy.mockResolvedValueOnce(otherTable) + + const view: ViewV2 = { + version: 2, + id: generator.guid(), + name: generator.guid(), + tableId, + schema: { + name: { visible: true }, + other: { visible: true }, + }, + } + + const res = await enrichSchema(view, table.schema) + + expect(res).toEqual( + expect.objectContaining({ + ...view, + schema: { + name: { + ...table.schema.name, + visible: true, + }, + other: { + ...table.schema.other, + visible: true, + schema: { + title: { + visible: true, + readonly: true, + }, + age: { + visible: false, + readonly: false, + }, + }, + }, + }, + }) + ) + }) }) describe("syncSchema", () => { From 5715f7e9c15776d5c1bf36b61556d3591e15d78d Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 22 Aug 2024 12:10:52 +0200 Subject: [PATCH 010/144] Fix promises --- .../server/src/api/controllers/table/index.ts | 10 ++++++--- packages/server/src/sdk/app/tables/getters.ts | 21 +++++++++++-------- .../src/sdk/app/tables/tests/tables.spec.ts | 2 +- .../src/sdk/app/views/tests/views.spec.ts | 2 +- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/packages/server/src/api/controllers/table/index.ts b/packages/server/src/api/controllers/table/index.ts index be49d66c82..9744516c4a 100644 --- a/packages/server/src/api/controllers/table/index.ts +++ b/packages/server/src/api/controllers/table/index.ts @@ -84,14 +84,18 @@ export async function fetch(ctx: UserCtx) { } }) - ctx.body = [...internal, ...external].map(sdk.tables.enrichViewSchemas) + const result: FetchTablesResponse = [] + for (const table of [...internal, ...external]) { + result.push(await sdk.tables.enrichViewSchemas(table)) + } + ctx.body = result // } export async function find(ctx: UserCtx) { const tableId = ctx.params.tableId const table = await sdk.tables.getTable(tableId) - const result = sdk.tables.enrichViewSchemas({ + const result = await sdk.tables.enrichViewSchemas({ ...table, schema: await sdk.tables.enrichRelationshipSchema(table.schema), }) @@ -109,7 +113,7 @@ export async function save(ctx: UserCtx) { const api = pickApi({ table }) let savedTable = await api.save(ctx, renaming) if (!table._id) { - savedTable = sdk.tables.enrichViewSchemas(savedTable) + savedTable = await sdk.tables.enrichViewSchemas(savedTable) await events.table.created(savedTable) } else { await events.table.updated(savedTable) diff --git a/packages/server/src/sdk/app/tables/getters.ts b/packages/server/src/sdk/app/tables/getters.ts index 5a505fd5b4..aeab32b328 100644 --- a/packages/server/src/sdk/app/tables/getters.ts +++ b/packages/server/src/sdk/app/tables/getters.ts @@ -189,16 +189,19 @@ export async function enrichRelationshipSchema( return result } -export function enrichViewSchemas(table: Table): TableResponse { +export async function enrichViewSchemas(table: Table): Promise { + const views = [] + for (const view of Object.values(table.views ?? [])) { + if (sdk.views.isV2(view)) { + views.push(await sdk.views.enrichSchema(view, table.schema)) + } else views.push(view) + } + return { ...table, - views: Object.values(table.views ?? []) - .map(v => - sdk.views.isV2(v) ? sdk.views.enrichSchema(v, table.schema) : v - ) - .reduce((p, v) => { - p[v.name!] = v - return p - }, {} as TableViewsResponse), + views: views.reduce((p, v) => { + p[v.name!] = v + return p + }, {} as TableViewsResponse), } } diff --git a/packages/server/src/sdk/app/tables/tests/tables.spec.ts b/packages/server/src/sdk/app/tables/tests/tables.spec.ts index 6e2cf9efa8..41ac808f5c 100644 --- a/packages/server/src/sdk/app/tables/tests/tables.spec.ts +++ b/packages/server/src/sdk/app/tables/tests/tables.spec.ts @@ -75,7 +75,7 @@ describe("table sdk", () => { const view1 = getTable() const view2 = getTable() const view3 = getTable() - const res = sdk.tables.enrichViewSchemas({ + const res = await sdk.tables.enrichViewSchemas({ ...basicTable, views: { [view1.name]: view1, diff --git a/packages/server/src/sdk/app/views/tests/views.spec.ts b/packages/server/src/sdk/app/views/tests/views.spec.ts index 265e6b159f..bfdd3e9c2a 100644 --- a/packages/server/src/sdk/app/views/tests/views.spec.ts +++ b/packages/server/src/sdk/app/views/tests/views.spec.ts @@ -60,7 +60,7 @@ describe("table sdk", () => { }, } - describe("enrichViewSchemas", () => { + describe("enrichSchema", () => { it("should fetch the default schema if not overridden", async () => { const tableId = basicTable._id! const view: ViewV2 = { From 1391f5d049d70f23c6ade784ec81a26c79a5d8bd Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 22 Aug 2024 13:57:02 +0200 Subject: [PATCH 011/144] Trim wrong fields --- packages/server/src/sdk/app/tables/getters.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/server/src/sdk/app/tables/getters.ts b/packages/server/src/sdk/app/tables/getters.ts index aeab32b328..a6bdf62d58 100644 --- a/packages/server/src/sdk/app/tables/getters.ts +++ b/packages/server/src/sdk/app/tables/getters.ts @@ -9,6 +9,7 @@ import { Database, FieldType, INTERNAL_TABLE_SOURCE_ID, + RelationSchemaField, RelationshipFieldMetadata, Table, TableResponse, @@ -156,6 +157,8 @@ export async function enrichRelationshipSchema( } const relTable = tableCache[field.tableId] + const resultSchema: Record = {} + for (const relTableFieldName of Object.keys(relTable.schema)) { const relTableField = relTable.schema[relTableFieldName] if (relTableField.type === FieldType.LINK) { @@ -166,15 +169,16 @@ export async function enrichRelationshipSchema( continue } - field.schema ??= {} const isPrimaryDisplay = relTableFieldName === relTable.primaryDisplay const isReadonly = - isPrimaryDisplay || !!field.schema[relTableFieldName]?.readonly - field.schema[relTableFieldName] = { + isPrimaryDisplay || + !!(field.schema && field.schema[relTableFieldName]?.readonly) + resultSchema[relTableFieldName] = { visible: isReadonly, readonly: isReadonly, } } + field.schema = resultSchema } const result: TableSchema = {} From 6b48bbf6484ba54cbde269e9edb776b5aba12463 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 22 Aug 2024 16:17:02 +0200 Subject: [PATCH 012/144] Clean empty message --- packages/server/src/api/controllers/table/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/api/controllers/table/index.ts b/packages/server/src/api/controllers/table/index.ts index 9744516c4a..6c64d86ca0 100644 --- a/packages/server/src/api/controllers/table/index.ts +++ b/packages/server/src/api/controllers/table/index.ts @@ -88,7 +88,7 @@ export async function fetch(ctx: UserCtx) { for (const table of [...internal, ...external]) { result.push(await sdk.tables.enrichViewSchemas(table)) } - ctx.body = result // + ctx.body = result } export async function find(ctx: UserCtx) { From b92adecb53c69ee43141efff929acdbe069387f3 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 16 Aug 2024 13:49:08 +0200 Subject: [PATCH 013/144] Display chevron --- .../DataTable/modals/CreateEditColumn.svelte | 4 ++-- .../controls/ColumnsSettingContent.svelte | 22 ++++++++++++++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte index 1d66a0ba00..a956d09ee6 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte @@ -84,7 +84,7 @@ } let relationshipOpts1 = Object.values(PrettyRelationshipDefinitions) let relationshipOpts2 = Object.values(PrettyRelationshipDefinitions) - let relationshipMap = { + const relationshipMap = { [RelationshipType.ONE_TO_MANY]: { part1: PrettyRelationshipDefinitions.MANY, part2: PrettyRelationshipDefinitions.ONE, @@ -98,7 +98,7 @@ part2: PrettyRelationshipDefinitions.MANY, }, } - let autoColumnInfo = getAutoColumnInformation() + const autoColumnInfo = getAutoColumnInformation() let optionsValid = true $: rowGoldenSample = RowUtils.generateGoldenSample($rows) diff --git a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte index 4f0e4424d4..6b0e6fdee2 100644 --- a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte +++ b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte @@ -4,11 +4,14 @@ import { getColumnIcon } from "../lib/utils" import ToggleActionButtonGroup from "./ToggleActionButtonGroup.svelte" import { helpers } from "@budibase/shared-core" + import { FieldType } from "@budibase/types" export let allowViewReadonlyColumns = false const { columns, datasource, dispatch } = getContext("grid") + $: allowRelationshipSchemas = true // TODO + const toggleColumn = async (column, permission) => { const visible = permission !== PERMISSION_OPTIONS.HIDDEN const readonly = permission === PERMISSION_OPTIONS.READONLY @@ -94,11 +97,16 @@ {column.label} - toggleColumn(column, e.detail)} - value={columnToPermissionOptions(column)} - options={column.options} - /> +
+ toggleColumn(column, e.detail)} + value={columnToPermissionOptions(column)} + options={column.options} + /> + {#if allowRelationshipSchemas && column.schema.type === FieldType.LINK} + + {/if} +
{/each} @@ -131,4 +139,8 @@ white-space: nowrap; overflow: hidden; } + .column-options { + display: flex; + gap: var(--spacing-s); + } From 890de94fa0d0978b9251d0de9d664c0513087af1 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 16 Aug 2024 16:38:24 +0200 Subject: [PATCH 014/144] Allow passing columns --- .../controls/ColumnsSettingContent.svelte | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte index 6b0e6fdee2..d687edacca 100644 --- a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte +++ b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte @@ -1,14 +1,15 @@
@@ -111,23 +139,7 @@ {#if allowRelationshipSchemas && column.schema.type === FieldType.LINK}
{ - const relTable = $tables.list.find( - table => table._id === column.schema.tableId - ) - relationshipPanelColumns = Object.values(relTable?.schema || {}) - .filter( - schema => - ![FieldType.LINK, FieldType.FORMULA].includes(schema.type) - ) - .map(schema => ({ - name: schema.name, - label: schema.name, - schema, - })) - relationshipPanelAnchor = e.currentTarget - relationshipPanelOpen = !relationshipPanelOpen - }} + on:click={e => onRelationshipOpen(column, e.currentTarget)} size="S" icon="ChevronRight" quiet From 31c1fa8b7adf14a22abaac7c7a3ba62265ff85b0 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 20 Aug 2024 10:29:59 +0200 Subject: [PATCH 019/144] Allow setting visibilities via parameter --- .../grid/controls/ColumnsSettingButton.svelte | 21 ++++- .../controls/ColumnsSettingContent.svelte | 93 +++++++++++-------- packages/frontend-core/src/constants.js | 6 ++ 3 files changed, 79 insertions(+), 41 deletions(-) diff --git a/packages/frontend-core/src/components/grid/controls/ColumnsSettingButton.svelte b/packages/frontend-core/src/components/grid/controls/ColumnsSettingButton.svelte index 8c66d9ecfc..b4940c8903 100644 --- a/packages/frontend-core/src/components/grid/controls/ColumnsSettingButton.svelte +++ b/packages/frontend-core/src/components/grid/controls/ColumnsSettingButton.svelte @@ -2,16 +2,29 @@ import { getContext } from "svelte" import { ActionButton, Popover } from "@budibase/bbui" import ColumnsSettingContent from "./ColumnsSettingContent.svelte" + import { FieldPermissions } from "../../../constants" export let allowViewReadonlyColumns = false - const { columns } = getContext("grid") + const { columns, datasource } = getContext("grid") let open = false let anchor $: anyRestricted = $columns.filter(col => !col.visible || col.readonly).length $: text = anyRestricted ? `Columns (${anyRestricted} restricted)` : "Columns" + + $: permissions = + $datasource.type === "viewV2" + ? [ + FieldPermissions.WRITABLE, + FieldPermissions.READONLY, + FieldPermissions.HIDDEN, + ] + : [FieldPermissions.WRITABLE, FieldPermissions.HIDDEN] + $: disabledPermissions = allowViewReadonlyColumns + ? [] + : [FieldPermissions.READONLY]
@@ -28,5 +41,9 @@
- + diff --git a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte index a967939bb3..e2d85f7945 100644 --- a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte +++ b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte @@ -6,11 +6,22 @@ import { helpers } from "@budibase/shared-core" import { FieldType } from "@budibase/types" import { tables } from "stores/builder" + import { FieldPermissions } from "../../../constants" - export let allowViewReadonlyColumns + export let permissions = [FieldPermissions.WRITABLE, FieldPermissions.HIDDEN] + export let disabledPermissions = [] export let columns const { datasource, dispatch } = getContext("grid") + $: permissionsObj = permissions.reduce( + (acc, c) => ({ + ...acc, + [c]: { + disabled: disabledPermissions.includes(c), + }, + }), + {} + ) $: allowRelationshipSchemas = true // TODO let relationshipPanelOpen = false @@ -18,8 +29,8 @@ let relationshipPanelColumns = [] const toggleColumn = async (column, permission) => { - const visible = permission !== PERMISSION_OPTIONS.HIDDEN - const readonly = permission === PERMISSION_OPTIONS.READONLY + const visible = permission !== FieldPermissions.HIDDEN + const readonly = permission === FieldPermissions.READONLY await datasource.actions.addSchemaMutation(column.name, { visible, @@ -36,60 +47,63 @@ dispatch(visible ? "show-column" : "hide-column") } - const PERMISSION_OPTIONS = { - WRITABLE: "writable", - READONLY: "readonly", - HIDDEN: "hidden", - } - $: displayColumns = columns.map(c => { const isRequired = helpers.schema.isRequired(c.schema.constraints) const requiredTooltip = isRequired && "Required columns must be writable" const editEnabled = - !isRequired || - columnToPermissionOptions(c) !== PERMISSION_OPTIONS.WRITABLE - const options = [ - { - icon: "Edit", - value: PERMISSION_OPTIONS.WRITABLE, - tooltip: (!editEnabled && requiredTooltip) || "Writable", - disabled: !editEnabled, - }, - ] - if ($datasource.type === "viewV2") { + !isRequired || columnToPermissionOptions(c) !== FieldPermissions.WRITABLE + const options = [] + + if (permissionsObj[FieldPermissions.WRITABLE]) { options.push({ - icon: "Visibility", - value: PERMISSION_OPTIONS.READONLY, - tooltip: allowViewReadonlyColumns - ? requiredTooltip || "Read only" - : "Read only (premium feature)", - disabled: !allowViewReadonlyColumns || isRequired, + icon: "Edit", + value: FieldPermissions.WRITABLE, + tooltip: (!editEnabled && requiredTooltip) || "Writable", + disabled: + !editEnabled || permissionsObj[FieldPermissions.WRITABLE].disabled, }) } - options.push({ - icon: "VisibilityOff", - value: PERMISSION_OPTIONS.HIDDEN, - disabled: c.primaryDisplay || isRequired, - tooltip: - (c.primaryDisplay && "Display column cannot be hidden") || - requiredTooltip || - "Hidden", - }) + if (permissionsObj[FieldPermissions.READONLY]) { + options.push({ + icon: "Visibility", + value: FieldPermissions.READONLY, + tooltip: !permissionsObj[FieldPermissions.READONLY].disabled + ? requiredTooltip || "Read only" + : "Read only (premium feature)", + disabled: + permissionsObj[FieldPermissions.READONLY].disabled || isRequired, + }) + } + + if (permissionsObj[FieldPermissions.HIDDEN]) { + options.push({ + icon: "VisibilityOff", + value: FieldPermissions.HIDDEN, + disabled: + c.primaryDisplay || + isRequired || + permissionsObj[FieldPermissions.HIDDEN].disabled, + tooltip: + (c.primaryDisplay && "Display column cannot be hidden") || + requiredTooltip || + "Hidden", + }) + } return { ...c, options } }) function columnToPermissionOptions(column) { if (column.schema.visible === false) { - return PERMISSION_OPTIONS.HIDDEN + return FieldPermissions.HIDDEN } if (column.schema.readonly) { - return PERMISSION_OPTIONS.READONLY + return FieldPermissions.READONLY } - return PERMISSION_OPTIONS.WRITABLE + return FieldPermissions.WRITABLE } function onRelationshipOpen(column, domElement) { @@ -109,6 +123,7 @@ schema: { ...column, visible: !!isPrimaryDisplay, + readonly: isPrimaryDisplay || column.readonly, }, } }) @@ -158,8 +173,8 @@ align="right-outside" > {/if} diff --git a/packages/frontend-core/src/constants.js b/packages/frontend-core/src/constants.js index 22e5e8583a..a8397a9a6f 100644 --- a/packages/frontend-core/src/constants.js +++ b/packages/frontend-core/src/constants.js @@ -161,3 +161,9 @@ export const TypeIconMap = { export const OptionColours = [...new Array(12).keys()].map(idx => { return `hsla(${((idx + 1) * 222) % 360}, 90%, 75%, 0.3)` }) + +export const FieldPermissions = { + WRITABLE: "writable", + READONLY: "readonly", + HIDDEN: "hidden", +} From 6413d89cf1843835ebeea4d32668947dcfb532a0 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 20 Aug 2024 13:48:49 +0200 Subject: [PATCH 020/144] Tooltips & disableds --- .../controls/ColumnsSettingContent.svelte | 53 +++++++++++-------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte index e2d85f7945..d4b9805374 100644 --- a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte +++ b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte @@ -48,46 +48,53 @@ } $: displayColumns = columns.map(c => { - const isRequired = helpers.schema.isRequired(c.schema.constraints) - const requiredTooltip = isRequired && "Required columns must be writable" - const editEnabled = - !isRequired || columnToPermissionOptions(c) !== FieldPermissions.WRITABLE + const isRequired = + c.primaryDisplay || helpers.schema.isRequired(c.schema.constraints) + + const requiredTooltips = { + [FieldPermissions.WRITABLE]: + (c.primaryDisplay && "Display column must be writable") || + (isRequired && "Required columns must be writable"), + [FieldPermissions.READONLY]: + (c.primaryDisplay && "Display column cannot be read-only") || + (isRequired && "Required columns cannot be read-only"), + [FieldPermissions.HIDDEN]: + (c.primaryDisplay && "Display column cannot be hidden") || + (isRequired && "Required columns cannot be hidden"), + } + const options = [] - if (permissionsObj[FieldPermissions.WRITABLE]) { + let permission + if ((permission = permissionsObj[FieldPermissions.WRITABLE])) { + const tooltip = requiredTooltips[FieldPermissions.WRITABLE] || "Writable" options.push({ icon: "Edit", value: FieldPermissions.WRITABLE, - tooltip: (!editEnabled && requiredTooltip) || "Writable", - disabled: - !editEnabled || permissionsObj[FieldPermissions.WRITABLE].disabled, + tooltip, + disabled: isRequired || permission.disabled, }) } - if (permissionsObj[FieldPermissions.READONLY]) { + if ((permission = permissionsObj[FieldPermissions.READONLY])) { + const tooltip = + (requiredTooltips[FieldPermissions.READONLY] || "Read-only") + + (permission.disabled && " (premium feature)") options.push({ icon: "Visibility", value: FieldPermissions.READONLY, - tooltip: !permissionsObj[FieldPermissions.READONLY].disabled - ? requiredTooltip || "Read only" - : "Read only (premium feature)", - disabled: - permissionsObj[FieldPermissions.READONLY].disabled || isRequired, + tooltip, + disabled: permission.disabled || isRequired, }) } - if (permissionsObj[FieldPermissions.HIDDEN]) { + if ((permission = permissionsObj[FieldPermissions.HIDDEN])) { + const tooltip = requiredTooltips[FieldPermissions.HIDDEN] || "Hidden" options.push({ icon: "VisibilityOff", value: FieldPermissions.HIDDEN, - disabled: - c.primaryDisplay || - isRequired || - permissionsObj[FieldPermissions.HIDDEN].disabled, - tooltip: - (c.primaryDisplay && "Display column cannot be hidden") || - requiredTooltip || - "Hidden", + disabled: permission.disabled || isRequired, + tooltip, }) } From 9d139e0ff4fecaf94c8a8d7f34d9ca33a10f3912 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 20 Aug 2024 13:57:06 +0200 Subject: [PATCH 021/144] Tooltips --- .../controls/ColumnsSettingContent.svelte | 48 +++++++++++++++---- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte index d4b9805374..845845948a 100644 --- a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte +++ b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte @@ -51,16 +51,44 @@ const isRequired = c.primaryDisplay || helpers.schema.isRequired(c.schema.constraints) + const defaultPermission = permissions[0] const requiredTooltips = { - [FieldPermissions.WRITABLE]: - (c.primaryDisplay && "Display column must be writable") || - (isRequired && "Required columns must be writable"), - [FieldPermissions.READONLY]: - (c.primaryDisplay && "Display column cannot be read-only") || - (isRequired && "Required columns cannot be read-only"), - [FieldPermissions.HIDDEN]: - (c.primaryDisplay && "Display column cannot be hidden") || - (isRequired && "Required columns cannot be hidden"), + [FieldPermissions.WRITABLE]: (() => { + if (defaultPermission === FieldPermissions.WRITABLE) { + if (c.primaryDisplay) { + return "Display column must be writable" + } + if (isRequired) { + return "Required columns must be writable" + } + } + })(), + [FieldPermissions.READONLY]: (() => { + if (defaultPermission === FieldPermissions.WRITABLE) { + if (c.primaryDisplay) { + return "Display column cannot be read-only" + } + if (isRequired) { + return "Required columns cannot be read-only" + } + } + if (defaultPermission === FieldPermissions.READONLY) { + if (c.primaryDisplay) { + return "Display column must be read-only" + } + if (isRequired) { + return "Required columns must be read-only" + } + } + })(), + [FieldPermissions.HIDDEN]: (() => { + if (c.primaryDisplay) { + return "Display column cannot be hidden" + } + if (isRequired) { + return "Required columns cannot be hidden" + } + })(), } const options = [] @@ -79,7 +107,7 @@ if ((permission = permissionsObj[FieldPermissions.READONLY])) { const tooltip = (requiredTooltips[FieldPermissions.READONLY] || "Read-only") + - (permission.disabled && " (premium feature)") + (permission.disabled ? " (premium feature)" : "") options.push({ icon: "Visibility", value: FieldPermissions.READONLY, From 97a44a8162c74bb310b2d0c52761ec7ab20939dc Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 20 Aug 2024 14:15:20 +0200 Subject: [PATCH 022/144] Persist nested mutation --- .../controls/ColumnsSettingContent.svelte | 16 ++++++++++---- .../src/components/grid/stores/datasource.js | 22 ++++++++++++++----- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte index 845845948a..5132487361 100644 --- a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte +++ b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte @@ -11,6 +11,7 @@ export let permissions = [FieldPermissions.WRITABLE, FieldPermissions.HIDDEN] export let disabledPermissions = [] export let columns + export let fromRelationshipField const { datasource, dispatch } = getContext("grid") $: permissionsObj = permissions.reduce( @@ -27,15 +28,20 @@ let relationshipPanelOpen = false let relationshipPanelAnchor let relationshipPanelColumns = [] + let relationshipField const toggleColumn = async (column, permission) => { const visible = permission !== FieldPermissions.HIDDEN const readonly = permission === FieldPermissions.READONLY - await datasource.actions.addSchemaMutation(column.name, { - visible, - readonly, - }) + await datasource.actions.addSchemaMutation( + column.name, + { + visible, + readonly, + }, + fromRelationshipField?.name + ) try { await datasource.actions.saveSchemaMutations() } catch (e) { @@ -168,6 +174,7 @@ relationshipPanelAnchor = domElement relationshipPanelOpen = !relationshipPanelOpen + relationshipField = column } @@ -210,6 +217,7 @@ {/if} diff --git a/packages/frontend-core/src/components/grid/stores/datasource.js b/packages/frontend-core/src/components/grid/stores/datasource.js index 12a3bd5afe..96d5d76c13 100644 --- a/packages/frontend-core/src/components/grid/stores/datasource.js +++ b/packages/frontend-core/src/components/grid/stores/datasource.js @@ -147,17 +147,27 @@ export const createActions = context => { } // Adds a schema mutation for a single field - const addSchemaMutation = (field, mutation) => { + const addSchemaMutation = (field, mutation, fromNestedField) => { if (!field || !mutation) { return } schemaMutations.update($schemaMutations => { - return { - ...$schemaMutations, - [field]: { - ...$schemaMutations[field], + if (fromNestedField) { + const result = { ...$schemaMutations } + result[fromNestedField] ??= { schema: {} } + result[fromNestedField].schema[field] = { + ...result[fromNestedField].schema[field], ...mutation, - }, + } + return result + } else { + return { + ...$schemaMutations, + [field]: { + ...$schemaMutations[field], + ...mutation, + }, + } } }) } From 643210f3aa02807f237d8e8a7d6dc87785d73b0e Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 20 Aug 2024 16:16:20 +0200 Subject: [PATCH 023/144] Update/fetch nested settings --- .../controls/ColumnsSettingContent.svelte | 128 ++++++++++-------- 1 file changed, 71 insertions(+), 57 deletions(-) diff --git a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte index 5132487361..0f36de3ff9 100644 --- a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte +++ b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte @@ -14,6 +14,12 @@ export let fromRelationshipField const { datasource, dispatch } = getContext("grid") + + let relationshipPanelAnchor + let relationshipPanelColumns = [] + let relationshipFieldName + + $: relationshipField = columns.find(c => c.name === relationshipFieldName) $: permissionsObj = permissions.reduce( (acc, c) => ({ ...acc, @@ -25,33 +31,6 @@ ) $: allowRelationshipSchemas = true // TODO - let relationshipPanelOpen = false - let relationshipPanelAnchor - let relationshipPanelColumns = [] - let relationshipField - - const toggleColumn = async (column, permission) => { - const visible = permission !== FieldPermissions.HIDDEN - const readonly = permission === FieldPermissions.READONLY - - await datasource.actions.addSchemaMutation( - column.name, - { - visible, - readonly, - }, - fromRelationshipField?.name - ) - try { - await datasource.actions.saveSchemaMutations() - } catch (e) { - notifications.error(e.message) - } finally { - await datasource.actions.resetSchemaMutations() - await datasource.actions.refreshDefinition() - } - dispatch(visible ? "show-column" : "hide-column") - } $: displayColumns = columns.map(c => { const isRequired = @@ -135,6 +114,29 @@ return { ...c, options } }) + async function toggleColumn(column, permission) { + const visible = permission !== FieldPermissions.HIDDEN + const readonly = permission === FieldPermissions.READONLY + + await datasource.actions.addSchemaMutation( + column.name, + { + visible, + readonly, + }, + fromRelationshipField?.name + ) + try { + await datasource.actions.saveSchemaMutations() + } catch (e) { + notifications.error(e.message) + } finally { + await datasource.actions.resetSchemaMutations() + await datasource.actions.refreshDefinition() + } + dispatch(visible ? "show-column" : "hide-column") + } + function columnToPermissionOptions(column) { if (column.schema.visible === false) { return FieldPermissions.HIDDEN @@ -147,34 +149,43 @@ return FieldPermissions.WRITABLE } - function onRelationshipOpen(column, domElement) { - const relTable = $tables.list.find( - table => table._id === column.schema.tableId - ) - relationshipPanelColumns = Object.values(relTable?.schema || {}) - .filter( - schema => ![FieldType.LINK, FieldType.FORMULA].includes(schema.type) - ) - .map(column => { - const isPrimaryDisplay = relTable.primaryDisplay === column.name - return { - name: column.name, - label: column.name, - primaryDisplay: isPrimaryDisplay, - schema: { - ...column, - visible: !!isPrimaryDisplay, - readonly: isPrimaryDisplay || column.readonly, - }, - } - }) - .sort((a, b) => - a.primaryDisplay === b.primaryDisplay ? 0 : a.primaryDisplay ? -1 : 1 - ) - - relationshipPanelAnchor = domElement - relationshipPanelOpen = !relationshipPanelOpen - relationshipField = column + $: { + if (relationshipField) { + cache.actions + .getTable(relationshipField.schema.tableId) + .then(relTable => { + relationshipPanelColumns = Object.values(relTable?.schema || {}) + .filter( + schema => + ![FieldType.LINK, FieldType.FORMULA].includes(schema.type) + ) + .map(column => { + const isPrimaryDisplay = relTable.primaryDisplay === column.name + const isReadonly = !!( + isPrimaryDisplay || + column.readonly || + (relationshipField.schema?.schema || {})[column.name]?.readonly + ) + return { + name: column.name, + label: column.name, + primaryDisplay: isPrimaryDisplay, + schema: { + ...column, + visible: isReadonly, + readonly: isReadonly, + }, + } + }) + .sort((a, b) => + a.primaryDisplay === b.primaryDisplay + ? 0 + : a.primaryDisplay + ? -1 + : 1 + ) + }) + } } @@ -196,7 +207,10 @@ {#if allowRelationshipSchemas && column.schema.type === FieldType.LINK}
onRelationshipOpen(column, e.currentTarget)} + on:click={e => { + relationshipFieldName = column.name + relationshipPanelAnchor = e.currentTarget + }} size="S" icon="ChevronRight" quiet @@ -210,7 +224,7 @@ {#if allowRelationshipSchemas} From 11ffff377bfb0b2ff21a55f4f4b5f65f58545e1a Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 20 Aug 2024 16:21:17 +0200 Subject: [PATCH 024/144] Don't show hidden fields --- .../src/components/grid/controls/ColumnsSettingContent.svelte | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte index 0f36de3ff9..f28e62e22e 100644 --- a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte +++ b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte @@ -159,6 +159,7 @@ schema => ![FieldType.LINK, FieldType.FORMULA].includes(schema.type) ) + .filter(schema => schema.visible !== false) .map(column => { const isPrimaryDisplay = relTable.primaryDisplay === column.name const isReadonly = !!( From 81d89cf4ef4c09ba7385bc3cb8d457e62b03716c Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 20 Aug 2024 16:26:13 +0200 Subject: [PATCH 025/144] Handle onclose --- .../src/components/grid/controls/ColumnsSettingContent.svelte | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte index f28e62e22e..29ffed4dd6 100644 --- a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte +++ b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte @@ -225,6 +225,7 @@ {#if allowRelationshipSchemas} (relationshipFieldName = null)} open={!!relationshipField} anchor={relationshipPanelAnchor} align="right-outside" From 82c0f6f076b719386f0405e9862bef8906add5ca Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 21 Aug 2024 11:11:53 +0200 Subject: [PATCH 026/144] Use data from the backend --- .../controls/ColumnsSettingContent.svelte | 60 ++++++------------- 1 file changed, 19 insertions(+), 41 deletions(-) diff --git a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte index 29ffed4dd6..ba1ef3968e 100644 --- a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte +++ b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte @@ -16,7 +16,6 @@ const { datasource, dispatch } = getContext("grid") let relationshipPanelAnchor - let relationshipPanelColumns = [] let relationshipFieldName $: relationshipField = columns.find(c => c.name === relationshipFieldName) @@ -114,6 +113,25 @@ return { ...c, options } }) + $: relationshipPanelColumns = Object.entries( + relationshipField?.schema?.schema || {} + ) + .map(([name, column]) => { + return { + name: name, + label: name, + primaryDisplay: column.primaryDisplay, + schema: { + ...column, + visible: column.visible, + readonly: column.readonly, + }, + } + }) + .sort((a, b) => + a.primaryDisplay === b.primaryDisplay ? 0 : a.primaryDisplay ? -1 : 1 + ) + async function toggleColumn(column, permission) { const visible = permission !== FieldPermissions.HIDDEN const readonly = permission === FieldPermissions.READONLY @@ -148,46 +166,6 @@ return FieldPermissions.WRITABLE } - - $: { - if (relationshipField) { - cache.actions - .getTable(relationshipField.schema.tableId) - .then(relTable => { - relationshipPanelColumns = Object.values(relTable?.schema || {}) - .filter( - schema => - ![FieldType.LINK, FieldType.FORMULA].includes(schema.type) - ) - .filter(schema => schema.visible !== false) - .map(column => { - const isPrimaryDisplay = relTable.primaryDisplay === column.name - const isReadonly = !!( - isPrimaryDisplay || - column.readonly || - (relationshipField.schema?.schema || {})[column.name]?.readonly - ) - return { - name: column.name, - label: column.name, - primaryDisplay: isPrimaryDisplay, - schema: { - ...column, - visible: isReadonly, - readonly: isReadonly, - }, - } - }) - .sort((a, b) => - a.primaryDisplay === b.primaryDisplay - ? 0 - : a.primaryDisplay - ? -1 - : 1 - ) - }) - } - }
From 6c70d09fc2769632e2e4a6e5b639459e3dbbee55 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 21 Aug 2024 11:46:36 +0200 Subject: [PATCH 027/144] Mutate properly --- .../src/components/grid/stores/datasource.js | 69 +++++++++++++++---- 1 file changed, 54 insertions(+), 15 deletions(-) diff --git a/packages/frontend-core/src/components/grid/stores/datasource.js b/packages/frontend-core/src/components/grid/stores/datasource.js index 96d5d76c13..11b4c79a03 100644 --- a/packages/frontend-core/src/components/grid/stores/datasource.js +++ b/packages/frontend-core/src/components/grid/stores/datasource.js @@ -5,16 +5,24 @@ import { memo } from "../../../utils" export const createStores = () => { const definition = memo(null) const schemaMutations = memo({}) + const subSchemaMutations = memo({}) return { definition, schemaMutations, + subSchemaMutations, } } export const deriveStores = context => { - const { API, definition, schemaOverrides, datasource, schemaMutations } = - context + const { + API, + definition, + schemaOverrides, + datasource, + schemaMutations, + subSchemaMutations, + } = context const schema = derived(definition, $definition => { let schema = getDatasourceSchema({ @@ -40,8 +48,8 @@ export const deriveStores = context => { // Derives the total enriched schema, made up of the saved schema and any // prop and user overrides const enrichedSchema = derived( - [schema, schemaOverrides, schemaMutations], - ([$schema, $schemaOverrides, $schemaMutations]) => { + [schema, schemaOverrides, schemaMutations, subSchemaMutations], + ([$schema, $schemaOverrides, $schemaMutations, $subSchemaMutations]) => { if (!$schema) { return null } @@ -52,6 +60,18 @@ export const deriveStores = context => { ...$schemaOverrides?.[field], ...$schemaMutations[field], } + + if ($subSchemaMutations[field]) { + enrichedSchema[field].schema ??= {} + for (const [fieldName, mutation] of Object.entries( + $subSchemaMutations[field] + )) { + enrichedSchema[field].schema[fieldName] = { + ...enrichedSchema[field].schema[fieldName], + ...mutation, + } + } + } }) return enrichedSchema } @@ -83,6 +103,7 @@ export const createActions = context => { viewV2, nonPlus, schemaMutations, + subSchemaMutations, schema, notifications, } = context @@ -151,16 +172,21 @@ export const createActions = context => { if (!field || !mutation) { return } - schemaMutations.update($schemaMutations => { - if (fromNestedField) { - const result = { ...$schemaMutations } - result[fromNestedField] ??= { schema: {} } - result[fromNestedField].schema[field] = { - ...result[fromNestedField].schema[field], - ...mutation, + if (fromNestedField) { + subSchemaMutations.update($subSchemaMutations => { + return { + ...$subSchemaMutations, + [fromNestedField]: { + ...$subSchemaMutations[fromNestedField], + [field]: { + ...($subSchemaMutations[fromNestedField] || {})[field], + ...mutation, + }, + }, } - return result - } else { + }) + } else { + schemaMutations.update($schemaMutations => { return { ...$schemaMutations, [field]: { @@ -168,8 +194,8 @@ export const createActions = context => { ...mutation, }, } - } - }) + }) + } } // Adds schema mutations for multiple fields at once @@ -198,6 +224,7 @@ export const createActions = context => { } const $definition = get(definition) const $schemaMutations = get(schemaMutations) + const $subSchemaMutations = get(subSchemaMutations) const $schema = get(schema) let newSchema = {} @@ -207,6 +234,17 @@ export const createActions = context => { ...$schema[column], ...$schemaMutations[column], } + if ($subSchemaMutations[column]) { + newSchema[column].schema ??= {} + for (const [fieldName, mutation] of Object.entries( + $subSchemaMutations[column] + )) { + newSchema[column].schema[fieldName] = { + ...newSchema[column].schema[fieldName], + ...mutation, + } + } + } }) // Save the changes, then reset our local mutations @@ -219,6 +257,7 @@ export const createActions = context => { const resetSchemaMutations = () => { schemaMutations.set({}) + subSchemaMutations.set({}) } // Adds a row to the datasource From 2ee356692ec2a7933a1a6afdbe7984b33f3afab7 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 21 Aug 2024 12:05:14 +0200 Subject: [PATCH 028/144] Don't show chevron for hidden fields --- .../src/components/grid/controls/ColumnsSettingContent.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte index ba1ef3968e..22d58c0fe0 100644 --- a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte +++ b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte @@ -183,7 +183,7 @@ value={columnToPermissionOptions(column)} options={column.options} /> - {#if allowRelationshipSchemas && column.schema.type === FieldType.LINK} + {#if allowRelationshipSchemas && column.schema.type === FieldType.LINK && columnToPermissionOptions(column) !== FieldPermissions.HIDDEN}
{ From cdead184020345a9b671ff1d0be853aeaada9097 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 22 Aug 2024 11:19:17 +0200 Subject: [PATCH 029/144] Get isDisplay and field type from table --- .../controls/ColumnsSettingContent.svelte | 56 ++++++++++++------- .../src/components/grid/stores/cache.js | 37 ++++++------ 2 files changed, 56 insertions(+), 37 deletions(-) diff --git a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte index 22d58c0fe0..e7219e647c 100644 --- a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte +++ b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte @@ -5,7 +5,6 @@ import ToggleActionButtonGroup from "./ToggleActionButtonGroup.svelte" import { helpers } from "@budibase/shared-core" import { FieldType } from "@budibase/types" - import { tables } from "stores/builder" import { FieldPermissions } from "../../../constants" export let permissions = [FieldPermissions.WRITABLE, FieldPermissions.HIDDEN] @@ -13,12 +12,14 @@ export let columns export let fromRelationshipField - const { datasource, dispatch } = getContext("grid") + const { datasource, dispatch, cache } = getContext("grid") let relationshipPanelAnchor let relationshipFieldName - $: relationshipField = columns.find(c => c.name === relationshipFieldName) + $: relationshipField = columns.find( + c => c.name === relationshipFieldName + )?.schema $: permissionsObj = permissions.reduce( (acc, c) => ({ ...acc, @@ -113,24 +114,37 @@ return { ...c, options } }) - $: relationshipPanelColumns = Object.entries( - relationshipField?.schema?.schema || {} - ) - .map(([name, column]) => { - return { - name: name, - label: name, - primaryDisplay: column.primaryDisplay, - schema: { - ...column, - visible: column.visible, - readonly: column.readonly, - }, - } - }) - .sort((a, b) => - a.primaryDisplay === b.primaryDisplay ? 0 : a.primaryDisplay ? -1 : 1 - ) + let relationshipPanelColumns = [] + $: { + if (relationshipField) { + cache.actions.getTable(relationshipField.tableId).then(table => { + relationshipPanelColumns = Object.entries( + relationshipField?.schema || {} + ) + .map(([name, column]) => { + return { + name: name, + label: name, + primaryDisplay: name === table.primaryDisplay, + schema: { + type: table.schema[name].type, + visible: column.visible, + readonly: column.readonly, + }, + } + }) + .sort((a, b) => + a.primaryDisplay === b.primaryDisplay + ? 0 + : a.primaryDisplay + ? -1 + : 1 + ) + }) + } else { + relationshipPanelColumns = [] + } + } async function toggleColumn(column, permission) { const visible = permission !== FieldPermissions.HIDDEN diff --git a/packages/frontend-core/src/components/grid/stores/cache.js b/packages/frontend-core/src/components/grid/stores/cache.js index 7eab6795e4..cf4690f15b 100644 --- a/packages/frontend-core/src/components/grid/stores/cache.js +++ b/packages/frontend-core/src/components/grid/stores/cache.js @@ -4,35 +4,40 @@ export const createActions = context => { // Cache for the primary display columns of different tables. // If we ever need to cache table definitions for other purposes then we can // expand this to be a more generic cache. - let primaryDisplayCache = {} + let tableCache = {} - const resetPrimaryDisplayCache = () => { - primaryDisplayCache = {} + const resetCache = () => { + tableCache = {} } - const getPrimaryDisplayForTableId = async tableId => { + const fetchTable = async tableId => { // If we've never encountered this tableId before then store a promise that // resolves to the primary display so that subsequent invocations before the // promise completes can reuse this promise - if (!primaryDisplayCache[tableId]) { - primaryDisplayCache[tableId] = new Promise(resolve => { - API.fetchTableDefinition(tableId).then(def => { - const display = def?.primaryDisplay || def?.schema?.[0]?.name - primaryDisplayCache[tableId] = display - resolve(display) - }) - }) + if (!tableCache[tableId]) { + tableCache[tableId] = API.fetchTableDefinition(tableId) } - // We await the result so that we account for both promises and primitives - return await primaryDisplayCache[tableId] + return await tableCache[tableId] + } + + const getPrimaryDisplayForTableId = async tableId => { + const table = await fetchTable(tableId) + const display = table?.primaryDisplay || table?.schema?.[0]?.name + return display + } + + const getTable = async tableId => { + const table = await fetchTable(tableId) + return table } return { cache: { actions: { getPrimaryDisplayForTableId, - resetPrimaryDisplayCache, + getTable, + resetCache, }, }, } @@ -43,5 +48,5 @@ export const initialise = context => { // Wipe the caches whenever the datasource changes to ensure we aren't // storing any stale information - datasource.subscribe(cache.actions.resetPrimaryDisplayCache) + datasource.subscribe(cache.actions.resetCache) } From a63d193a3cc7be991cc5d51b442c7f171384cfb4 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 22 Aug 2024 12:22:21 +0200 Subject: [PATCH 030/144] Fix flashes --- .../components/grid/controls/ColumnsSettingContent.svelte | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte index e7219e647c..0d35ec049f 100644 --- a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte +++ b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte @@ -116,6 +116,7 @@ let relationshipPanelColumns = [] $: { + relationshipPanelColumns = [] if (relationshipField) { cache.actions.getTable(relationshipField.tableId).then(table => { relationshipPanelColumns = Object.entries( @@ -141,8 +142,6 @@ : 1 ) }) - } else { - relationshipPanelColumns = [] } } @@ -218,7 +217,7 @@ {#if allowRelationshipSchemas} (relationshipFieldName = null)} - open={!!relationshipField} + open={!!relationshipPanelColumns} anchor={relationshipPanelAnchor} align="right-outside" > From 6301b9de4cb30fd2eee14f2cf9b8f498a0fe34f6 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 22 Aug 2024 12:30:03 +0200 Subject: [PATCH 031/144] Tidy --- .../controls/ColumnsSettingContent.svelte | 50 +++++++++---------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte index 0d35ec049f..d88f7a3377 100644 --- a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte +++ b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte @@ -115,35 +115,31 @@ }) let relationshipPanelColumns = [] - $: { + async function fetchRelationshipPanelColumns(relationshipField) { relationshipPanelColumns = [] - if (relationshipField) { - cache.actions.getTable(relationshipField.tableId).then(table => { - relationshipPanelColumns = Object.entries( - relationshipField?.schema || {} - ) - .map(([name, column]) => { - return { - name: name, - label: name, - primaryDisplay: name === table.primaryDisplay, - schema: { - type: table.schema[name].type, - visible: column.visible, - readonly: column.readonly, - }, - } - }) - .sort((a, b) => - a.primaryDisplay === b.primaryDisplay - ? 0 - : a.primaryDisplay - ? -1 - : 1 - ) - }) + if (!relationshipField) { + return } + + const table = await cache.actions.getTable(relationshipField.tableId) + relationshipPanelColumns = Object.entries(relationshipField?.schema || {}) + .map(([name, column]) => { + return { + name: name, + label: name, + primaryDisplay: name === table.primaryDisplay, + schema: { + type: table.schema[name].type, + visible: column.visible, + readonly: column.readonly, + }, + } + }) + .sort((a, b) => + a.primaryDisplay === b.primaryDisplay ? 0 : a.primaryDisplay ? -1 : 1 + ) } + $: fetchRelationshipPanelColumns(relationshipField) async function toggleColumn(column, permission) { const visible = permission !== FieldPermissions.HIDDEN @@ -217,7 +213,7 @@ {#if allowRelationshipSchemas} (relationshipFieldName = null)} - open={!!relationshipPanelColumns} + open={!!relationshipField} anchor={relationshipPanelAnchor} align="right-outside" > From 1bf0f933f07b678a6d437383e7787efdbe2736ac Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 22 Aug 2024 16:25:43 +0200 Subject: [PATCH 032/144] Split between addSchemaMutation and addSubSchemaMutation --- .../controls/ColumnsSettingContent.svelte | 19 ++++--- .../src/components/grid/stores/datasource.js | 49 ++++++++++--------- 2 files changed, 40 insertions(+), 28 deletions(-) diff --git a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte index d88f7a3377..7cae9ae5fa 100644 --- a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte +++ b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte @@ -145,14 +145,21 @@ const visible = permission !== FieldPermissions.HIDDEN const readonly = permission === FieldPermissions.READONLY - await datasource.actions.addSchemaMutation( - column.name, - { + if (!fromRelationshipField) { + await datasource.actions.addSchemaMutation(column.name, { visible, readonly, - }, - fromRelationshipField?.name - ) + }) + } else { + await datasource.actions.addSubSchemaMutation( + column.name, + fromRelationshipField.name, + { + visible, + readonly, + } + ) + } try { await datasource.actions.saveSchemaMutations() } catch (e) { diff --git a/packages/frontend-core/src/components/grid/stores/datasource.js b/packages/frontend-core/src/components/grid/stores/datasource.js index 11b4c79a03..d9463e1e74 100644 --- a/packages/frontend-core/src/components/grid/stores/datasource.js +++ b/packages/frontend-core/src/components/grid/stores/datasource.js @@ -168,34 +168,38 @@ export const createActions = context => { } // Adds a schema mutation for a single field - const addSchemaMutation = (field, mutation, fromNestedField) => { + const addSchemaMutation = (field, mutation) => { if (!field || !mutation) { return } - if (fromNestedField) { - subSchemaMutations.update($subSchemaMutations => { - return { - ...$subSchemaMutations, - [fromNestedField]: { - ...$subSchemaMutations[fromNestedField], - [field]: { - ...($subSchemaMutations[fromNestedField] || {})[field], - ...mutation, - }, - }, - } - }) - } else { - schemaMutations.update($schemaMutations => { - return { - ...$schemaMutations, + schemaMutations.update($schemaMutations => { + return { + ...$schemaMutations, + [field]: { + ...$schemaMutations[field], + ...mutation, + }, + } + }) + } + + // Adds a nested schema mutation for a single field + const addSubSchemaMutation = (field, fromField, mutation) => { + if (!field || !fromField || !mutation) { + return + } + subSchemaMutations.update($subSchemaMutations => { + return { + ...$subSchemaMutations, + [fromField]: { + ...$subSchemaMutations[fromField], [field]: { - ...$schemaMutations[field], + ...($subSchemaMutations[fromField] || {})[field], ...mutation, }, - } - }) - } + }, + } + }) } // Adds schema mutations for multiple fields at once @@ -304,6 +308,7 @@ export const createActions = context => { canUseColumn, changePrimaryDisplay, addSchemaMutation, + addSubSchemaMutation, addSchemaMutations, saveSchemaMutations, resetSchemaMutations, From 76ae3a1331879aed47f5e6a568f79a96ec64682b Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 22 Aug 2024 15:55:40 +0200 Subject: [PATCH 033/144] Display relationship header --- .../grid/controls/ColumnsSettingContent.svelte | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte index 7cae9ae5fa..2316f813fa 100644 --- a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte +++ b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte @@ -220,10 +220,15 @@ {#if allowRelationshipSchemas} (relationshipFieldName = null)} - open={!!relationshipField} + open={relationshipFieldName} anchor={relationshipPanelAnchor} align="right-outside" > + {#if relationshipPanelColumns.length} +
+ {relationshipFieldName} columns +
+ {/if} From ca0ea3faf830f46ca121581f9bf462efa59dc98d Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 22 Aug 2024 16:57:22 +0200 Subject: [PATCH 034/144] Don't show primary display --- .../controls/ColumnsSettingContent.svelte | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte index 2316f813fa..cb2ffb1c28 100644 --- a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte +++ b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte @@ -122,22 +122,19 @@ } const table = await cache.actions.getTable(relationshipField.tableId) - relationshipPanelColumns = Object.entries(relationshipField?.schema || {}) - .map(([name, column]) => { - return { - name: name, - label: name, - primaryDisplay: name === table.primaryDisplay, - schema: { - type: table.schema[name].type, - visible: column.visible, - readonly: column.readonly, - }, - } - }) - .sort((a, b) => - a.primaryDisplay === b.primaryDisplay ? 0 : a.primaryDisplay ? -1 : 1 - ) + relationshipPanelColumns = Object.entries( + relationshipField?.schema || {} + ).map(([name, column]) => { + return { + name: name, + label: name, + schema: { + type: table.schema[name].type, + visible: column.visible, + readonly: column.readonly, + }, + } + }) } $: fetchRelationshipPanelColumns(relationshipField) From 84bab434bf86155ab2e8bf34031b0abc6d5ddcee Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 22 Aug 2024 16:59:47 +0200 Subject: [PATCH 035/144] Don't include primary display columns by default --- packages/server/src/sdk/app/tables/getters.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/server/src/sdk/app/tables/getters.ts b/packages/server/src/sdk/app/tables/getters.ts index a6bdf62d58..f3e2e97795 100644 --- a/packages/server/src/sdk/app/tables/getters.ts +++ b/packages/server/src/sdk/app/tables/getters.ts @@ -157,6 +157,8 @@ export async function enrichRelationshipSchema( } const relTable = tableCache[field.tableId] + const fieldSchema = field.schema || {} + const resultSchema: Record = {} for (const relTableFieldName of Object.keys(relTable.schema)) { @@ -169,10 +171,7 @@ export async function enrichRelationshipSchema( continue } - const isPrimaryDisplay = relTableFieldName === relTable.primaryDisplay - const isReadonly = - isPrimaryDisplay || - !!(field.schema && field.schema[relTableFieldName]?.readonly) + const isReadonly = !!fieldSchema[relTableFieldName]?.readonly resultSchema[relTableFieldName] = { visible: isReadonly, readonly: isReadonly, From a7399cc3e926a6f0a2b67296f85ea12c5e803704 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 22 Aug 2024 17:12:53 +0200 Subject: [PATCH 036/144] Fix test --- packages/server/src/sdk/app/views/tests/views.spec.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/server/src/sdk/app/views/tests/views.spec.ts b/packages/server/src/sdk/app/views/tests/views.spec.ts index bfdd3e9c2a..647a63431e 100644 --- a/packages/server/src/sdk/app/views/tests/views.spec.ts +++ b/packages/server/src/sdk/app/views/tests/views.spec.ts @@ -315,6 +315,12 @@ describe("table sdk", () => { relationshipType: RelationshipType.ONE_TO_MANY, fieldName: "table", tableId: "otherTableId", + schema: { + title: { + visible: true, + readonly: true, + }, + }, }, }, } From b3cd06944f4c941efc2da45c9cd84845fd076454 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 22 Aug 2024 17:23:19 +0200 Subject: [PATCH 037/144] Enrich based on schema --- packages/server/src/db/linkedRows/index.ts | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/packages/server/src/db/linkedRows/index.ts b/packages/server/src/db/linkedRows/index.ts index 2da7e212b9..04755cd601 100644 --- a/packages/server/src/db/linkedRows/index.ts +++ b/packages/server/src/db/linkedRows/index.ts @@ -253,20 +253,33 @@ export async function squashLinksToPrimaryDisplay( // will populate this as we find them const linkedTables = [table] const isArray = Array.isArray(enriched) - let enrichedArray = !isArray ? [enriched] : enriched - for (let row of enrichedArray) { + const enrichedArray = !isArray ? [enriched] : enriched + for (const row of enrichedArray) { // this only fetches the table if its not already in array const rowTable = await getLinkedTable(row.tableId!, linkedTables) - for (let [column, schema] of Object.entries(rowTable?.schema || {})) { + const safeSchema = + (rowTable?.schema && + (await sdk.tables.enrichRelationshipSchema(rowTable.schema))) || + {} + for (let [column, schema] of Object.entries(safeSchema)) { if (schema.type !== FieldType.LINK || !Array.isArray(row[column])) { continue } const newLinks = [] - for (let link of row[column]) { + for (const link of row[column]) { const linkTblId = link.tableId || getRelatedTableForField(table, column) const linkedTable = await getLinkedTable(linkTblId!, linkedTables) const obj: any = { _id: link._id } obj.primaryDisplay = getPrimaryDisplayValue(link, linkedTable) + + if (schema.schema) { + for (const relField of Object.entries(schema.schema) + .filter(([_, field]) => field.visible !== false) + .map(([fieldKey]) => fieldKey)) { + obj[relField] = link[relField] + } + } + newLinks.push(obj) } row[column] = newLinks From c397dadd76b174368d6648f4b17eb7df21c08727 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 22 Aug 2024 17:28:10 +0200 Subject: [PATCH 038/144] Add "flag" --- packages/server/src/db/linkedRows/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/server/src/db/linkedRows/index.ts b/packages/server/src/db/linkedRows/index.ts index 04755cd601..2394d00d0e 100644 --- a/packages/server/src/db/linkedRows/index.ts +++ b/packages/server/src/db/linkedRows/index.ts @@ -272,7 +272,8 @@ export async function squashLinksToPrimaryDisplay( const obj: any = { _id: link._id } obj.primaryDisplay = getPrimaryDisplayValue(link, linkedTable) - if (schema.schema) { + const allowRelationshipSchemas = true // TODO + if (schema.schema && allowRelationshipSchemas) { for (const relField of Object.entries(schema.schema) .filter(([_, field]) => field.visible !== false) .map(([fieldKey]) => fieldKey)) { From daa152183cf0565022a6229d65b09020715535aa Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 23 Aug 2024 10:42:43 +0200 Subject: [PATCH 039/144] Use isvisible for populating schemas instead of readonly --- packages/server/src/sdk/app/tables/getters.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/server/src/sdk/app/tables/getters.ts b/packages/server/src/sdk/app/tables/getters.ts index f3e2e97795..b85add114e 100644 --- a/packages/server/src/sdk/app/tables/getters.ts +++ b/packages/server/src/sdk/app/tables/getters.ts @@ -171,9 +171,10 @@ export async function enrichRelationshipSchema( continue } + const isVisible = !!fieldSchema[relTableFieldName]?.visible const isReadonly = !!fieldSchema[relTableFieldName]?.readonly resultSchema[relTableFieldName] = { - visible: isReadonly, + visible: isVisible, readonly: isReadonly, } } From c77ec0d17afed03eb45a691bf33c4daada10931a Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 23 Aug 2024 10:42:57 +0200 Subject: [PATCH 040/144] Exclude formulas --- packages/server/src/sdk/app/tables/getters.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/sdk/app/tables/getters.ts b/packages/server/src/sdk/app/tables/getters.ts index b85add114e..4ce54adc30 100644 --- a/packages/server/src/sdk/app/tables/getters.ts +++ b/packages/server/src/sdk/app/tables/getters.ts @@ -163,7 +163,7 @@ export async function enrichRelationshipSchema( for (const relTableFieldName of Object.keys(relTable.schema)) { const relTableField = relTable.schema[relTableFieldName] - if (relTableField.type === FieldType.LINK) { + if ([FieldType.LINK, FieldType.FORMULA].includes(relTableField.type)) { continue } From a3bbbb32be448ec0c44e7c4d8bd7a7e1a914e0da Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 23 Aug 2024 10:47:26 +0200 Subject: [PATCH 041/144] Add test --- .../server/src/api/routes/tests/row.spec.ts | 182 ++++++++++++++++++ 1 file changed, 182 insertions(+) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index 95e714fbd6..2c4626a661 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -2407,6 +2407,188 @@ describe.each([ }) }) + describe("relationships", () => { + let tableId: string + + let auxData: Row[] = [] + + beforeAll(async () => { + const aux2Table = await config.api.table.save(defaultTable()) + const aux2Data = await config.api.row.save(aux2Table._id!, {}) + + const auxTable = await config.api.table.save( + defaultTable({ + schema: { + name: { + name: "name", + type: FieldType.STRING, + constraints: { presence: true }, + }, + age: { + name: "age", + type: FieldType.NUMBER, + constraints: { presence: true }, + }, + address: { + name: "address", + type: FieldType.STRING, + constraints: { presence: true }, + visible: false, + }, + link: { + name: "link", + type: FieldType.LINK, + tableId: aux2Table._id!, + relationshipType: RelationshipType.MANY_TO_MANY, + fieldName: "fk_aux", + constraints: { presence: true }, + }, + formula: { + name: "formula", + type: FieldType.FORMULA, + formula: "{{ any }}", + constraints: { presence: true }, + }, + }, + }) + ) + const auxTableId = auxTable._id! + + for (const name of generator.unique(() => generator.name(), 10)) { + auxData.push( + await config.api.row.save(auxTableId, { + name, + age: generator.age(), + address: generator.address(), + link: [aux2Data], + }) + ) + } + + const table = await config.api.table.save( + defaultTable({ + schema: { + title: { + name: "title", + type: FieldType.STRING, + constraints: { presence: true }, + }, + relWithNoSchema: { + name: "relWithNoSchema", + relationshipType: RelationshipType.ONE_TO_MANY, + type: FieldType.LINK, + tableId: auxTableId, + fieldName: "fk_relWithNoSchema", + constraints: { presence: true }, + }, + relWithEmptySchema: { + name: "relWithEmptySchema", + relationshipType: RelationshipType.ONE_TO_MANY, + type: FieldType.LINK, + tableId: auxTableId, + fieldName: "fk_relWithEmptySchema", + constraints: { presence: true }, + schema: {}, + }, + relWithFullSchema: { + name: "relWithFullSchema", + relationshipType: RelationshipType.ONE_TO_MANY, + type: FieldType.LINK, + tableId: auxTableId, + fieldName: "fk_relWithFullSchema", + constraints: { presence: true }, + schema: Object.keys(auxTable.schema).reduce( + (acc, c) => ({ ...acc, [c]: { visible: true } }), + {} + ), + }, + relWithHalfSchema: { + name: "relWithHalfSchema", + relationshipType: RelationshipType.ONE_TO_MANY, + type: FieldType.LINK, + tableId: auxTableId, + fieldName: "fk_relWithHalfSchema", + constraints: { presence: true }, + schema: { + name: { visible: true }, + age: { visible: false, readonly: true }, + }, + }, + relWithIllegalSchema: { + name: "relWithIllegalSchema", + relationshipType: RelationshipType.ONE_TO_MANY, + type: FieldType.LINK, + tableId: auxTableId, + fieldName: "fk_relWithIllegalSchema", + constraints: { presence: true }, + schema: { + name: { visible: true }, + address: { visible: true }, + unexisting: { visible: true }, + }, + }, + }, + }) + ) + tableId = table._id! + }) + + it("can retrieve rows with populated relationships", async () => { + const otherRows = _.sampleSize(auxData, 5) + + const row = await config.api.row.save(tableId, { + title: generator.word(), + relWithNoSchema: [otherRows[0]], + relWithEmptySchema: [otherRows[1]], + relWithFullSchema: [otherRows[2]], + relWithHalfSchema: [otherRows[3]], + relWithIllegalSchema: [otherRows[4]], + }) + + const retrieved = await config.api.row.get(tableId, row._id!) + expect(retrieved).toEqual( + expect.objectContaining({ + title: row.title, + relWithNoSchema: [ + { + _id: otherRows[0]._id, + primaryDisplay: otherRows[0].name, + }, + ], + relWithEmptySchema: [ + { + _id: otherRows[1]._id, + primaryDisplay: otherRows[1].name, + }, + ], + relWithFullSchema: [ + { + _id: otherRows[2]._id, + primaryDisplay: otherRows[2].name, + name: otherRows[2].name, + age: otherRows[2].age, + id: otherRows[2].id, + }, + ], + relWithHalfSchema: [ + { + _id: otherRows[3]._id, + primaryDisplay: otherRows[3].name, + name: otherRows[3].name, + }, + ], + relWithIllegalSchema: [ + { + _id: otherRows[4]._id, + primaryDisplay: otherRows[4].name, + name: otherRows[4].name, + }, + ], + }) + ) + }) + }) + describe("Formula fields", () => { let table: Table let otherTable: Table From 329fefc7dd4ccb779af590484848a39ebf6b78dc Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 23 Aug 2024 10:55:46 +0200 Subject: [PATCH 042/144] Prepare reusing --- .../server/src/api/routes/tests/row.spec.ts | 109 +++++++++--------- 1 file changed, 57 insertions(+), 52 deletions(-) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index 2c4626a661..648b05c209 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -2533,60 +2533,65 @@ describe.each([ tableId = table._id! }) - it("can retrieve rows with populated relationships", async () => { - const otherRows = _.sampleSize(auxData, 5) + it.each([ + ["get row", (rowId: string) => config.api.row.get(tableId, rowId)], + ])( + "can retrieve rows with populated relationships (via %s)", + async (__, retrieveDelegate) => { + const otherRows = _.sampleSize(auxData, 5) - const row = await config.api.row.save(tableId, { - title: generator.word(), - relWithNoSchema: [otherRows[0]], - relWithEmptySchema: [otherRows[1]], - relWithFullSchema: [otherRows[2]], - relWithHalfSchema: [otherRows[3]], - relWithIllegalSchema: [otherRows[4]], - }) - - const retrieved = await config.api.row.get(tableId, row._id!) - expect(retrieved).toEqual( - expect.objectContaining({ - title: row.title, - relWithNoSchema: [ - { - _id: otherRows[0]._id, - primaryDisplay: otherRows[0].name, - }, - ], - relWithEmptySchema: [ - { - _id: otherRows[1]._id, - primaryDisplay: otherRows[1].name, - }, - ], - relWithFullSchema: [ - { - _id: otherRows[2]._id, - primaryDisplay: otherRows[2].name, - name: otherRows[2].name, - age: otherRows[2].age, - id: otherRows[2].id, - }, - ], - relWithHalfSchema: [ - { - _id: otherRows[3]._id, - primaryDisplay: otherRows[3].name, - name: otherRows[3].name, - }, - ], - relWithIllegalSchema: [ - { - _id: otherRows[4]._id, - primaryDisplay: otherRows[4].name, - name: otherRows[4].name, - }, - ], + const row = await config.api.row.save(tableId, { + title: generator.word(), + relWithNoSchema: [otherRows[0]], + relWithEmptySchema: [otherRows[1]], + relWithFullSchema: [otherRows[2]], + relWithHalfSchema: [otherRows[3]], + relWithIllegalSchema: [otherRows[4]], }) - ) - }) + + const retrieved = await retrieveDelegate(row._id!) + expect(retrieved).toEqual( + expect.objectContaining({ + title: row.title, + relWithNoSchema: [ + { + _id: otherRows[0]._id, + primaryDisplay: otherRows[0].name, + }, + ], + relWithEmptySchema: [ + { + _id: otherRows[1]._id, + primaryDisplay: otherRows[1].name, + }, + ], + relWithFullSchema: [ + { + _id: otherRows[2]._id, + primaryDisplay: otherRows[2].name, + name: otherRows[2].name, + age: otherRows[2].age, + id: otherRows[2].id, + }, + ], + relWithHalfSchema: [ + { + _id: otherRows[3]._id, + primaryDisplay: otherRows[3].name, + name: otherRows[3].name, + }, + ], + relWithIllegalSchema: [ + { + _id: otherRows[4]._id, + primaryDisplay: otherRows[4].name, + name: otherRows[4].name, + }, + ], + }) + ) + } + ) }) describe("Formula fields", () => { From a28114a01cf9cace5c15e42562adffac5c9e4521 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 23 Aug 2024 11:08:56 +0200 Subject: [PATCH 043/144] Extra tests --- .../server/src/api/routes/tests/row.spec.ts | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index 648b05c209..0484fcd81e 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -2535,6 +2535,36 @@ describe.each([ it.each([ ["get row", (rowId: string) => config.api.row.get(tableId, rowId)], + [ + "fetch", + async (rowId: string) => { + const rows = await config.api.row.fetch(tableId) + return rows.find(r => r._id === rowId) + }, + ], + [ + "search", + async (rowId: string) => { + const { rows } = await config.api.row.search(tableId) + return rows.find(r => r._id === rowId) + }, + ], + [ + "from view", + async (rowId: string) => { + const table = await config.api.table.get(tableId) + const view = await config.api.viewV2.create({ + name: generator.guid(), + tableId, + schema: Object.keys(table.schema).reduce( + (acc, c) => ({ ...acc, [c]: { visible: true } }), + {} + ), + }) + const { rows } = await config.api.viewV2.search(view.id) + return rows.find(r => r._id === rowId) + }, + ], ])( "can retrieve rows with populated relationships (via %s)", async (__, retrieveDelegate) => { From 02fdbae7ac84071a484f47b5a511f548c4814fed Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 23 Aug 2024 11:12:31 +0200 Subject: [PATCH 044/144] More tests --- .../server/src/api/routes/tests/row.spec.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index 0484fcd81e..2781f58823 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -2534,24 +2534,24 @@ describe.each([ }) it.each([ - ["get row", (rowId: string) => config.api.row.get(tableId, rowId)], + ["get row", (row: Row) => config.api.row.get(tableId, row._id!)], [ "fetch", - async (rowId: string) => { + async (row: Row) => { const rows = await config.api.row.fetch(tableId) - return rows.find(r => r._id === rowId) + return rows.find(r => r._id === row._id) }, ], [ "search", - async (rowId: string) => { + async (row: Row) => { const { rows } = await config.api.row.search(tableId) - return rows.find(r => r._id === rowId) + return rows.find(r => r._id === row._id) }, ], [ "from view", - async (rowId: string) => { + async (row: Row) => { const table = await config.api.table.get(tableId) const view = await config.api.viewV2.create({ name: generator.guid(), @@ -2562,9 +2562,10 @@ describe.each([ ), }) const { rows } = await config.api.viewV2.search(view.id) - return rows.find(r => r._id === rowId) + return rows.find(r => r._id === row._id!) }, ], + ["from original saved row", (row: Row) => row], ])( "can retrieve rows with populated relationships (via %s)", async (__, retrieveDelegate) => { @@ -2579,7 +2580,7 @@ describe.each([ relWithIllegalSchema: [otherRows[4]], }) - const retrieved = await retrieveDelegate(row._id!) + const retrieved = await retrieveDelegate(row) expect(retrieved).toEqual( expect.objectContaining({ title: row.title, From ccf734486fe0198668caf6139e220b15932b13f9 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 23 Aug 2024 12:28:02 +0200 Subject: [PATCH 045/144] Test sqs --- .../server/src/api/routes/tests/row.spec.ts | 30 +++++++++++++++---- .../src/utilities/rowProcessor/index.ts | 11 ++++++- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index 2781f58823..a91ce60949 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -9,7 +9,13 @@ import { import tk from "timekeeper" import emitter from "../../../../src/events" 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 { AttachmentFieldMetadata, @@ -69,6 +75,7 @@ async function waitForEvent( describe.each([ ["internal", undefined], + ["sqs", undefined], [DatabaseName.POSTGRES, getDatasource(DatabaseName.POSTGRES)], [DatabaseName.MYSQL, getDatasource(DatabaseName.MYSQL)], [DatabaseName.SQL_SERVER, getDatasource(DatabaseName.SQL_SERVER)], @@ -76,6 +83,7 @@ describe.each([ [DatabaseName.ORACLE, getDatasource(DatabaseName.ORACLE)], ])("/rows (%s)", (providerType, dsProvider) => { const isInternal = dsProvider === undefined + const isSqs = providerType === "sqs" const isMSSQL = providerType === DatabaseName.SQL_SERVER const isOracle = providerType === DatabaseName.ORACLE const config = setup.getConfig() @@ -83,9 +91,17 @@ describe.each([ let table: Table let datasource: Datasource | undefined let client: Knex | undefined + let envCleanup: (() => void) | undefined 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) { const rawDatasource = await dsProvider datasource = await config.createDatasource({ @@ -97,6 +113,9 @@ describe.each([ afterAll(async () => { setup.afterAll() + if (envCleanup) { + envCleanup() + } }) function saveTableRequest( @@ -2413,11 +2432,12 @@ describe.each([ let auxData: Row[] = [] beforeAll(async () => { - const aux2Table = await config.api.table.save(defaultTable()) + const aux2Table = await config.api.table.save(saveTableRequest()) const aux2Data = await config.api.row.save(aux2Table._id!, {}) const auxTable = await config.api.table.save( - defaultTable({ + saveTableRequest({ + primaryDisplay: "name", schema: { name: { name: "name", @@ -2466,7 +2486,7 @@ describe.each([ } const table = await config.api.table.save( - defaultTable({ + saveTableRequest({ schema: { title: { name: "title", diff --git a/packages/server/src/utilities/rowProcessor/index.ts b/packages/server/src/utilities/rowProcessor/index.ts index 4b2fd83882..acbff40602 100644 --- a/packages/server/src/utilities/rowProcessor/index.ts +++ b/packages/server/src/utilities/rowProcessor/index.ts @@ -3,6 +3,7 @@ import { fixAutoColumnSubType, processFormulas } from "./utils" import { cache, context, + db, HTTPError, objectStore, utils, @@ -349,11 +350,19 @@ export async function outputProcessing( } // remove null properties to match internal API const isExternal = isExternalTableID(table._id!) - if (isExternal) { + if (isExternal || db.isSqsEnabledForTenant()) { for (const row of enriched) { for (const key of Object.keys(row)) { if (row[key] === null) { 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] + } + } + } } } } From 85aa6361b2c4af9cd3f6912f093f94fe3836d146 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 23 Aug 2024 12:35:43 +0200 Subject: [PATCH 046/144] Fix broken sqs tests --- packages/server/src/api/routes/tests/row.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index a91ce60949..2922dc3939 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -83,6 +83,7 @@ describe.each([ [DatabaseName.ORACLE, getDatasource(DatabaseName.ORACLE)], ])("/rows (%s)", (providerType, dsProvider) => { const isInternal = dsProvider === undefined + const isLucene = providerType === "lucene" const isSqs = providerType === "sqs" const isMSSQL = providerType === DatabaseName.SQL_SERVER const isOracle = providerType === DatabaseName.ORACLE @@ -365,7 +366,7 @@ describe.each([ expect(ids).toEqual(expect.arrayContaining(sequence)) }) - isInternal && + isLucene && it("row values are coerced", async () => { const str: FieldSchema = { type: FieldType.STRING, From 3186179f0e17e3f6ae3db1eb44cafb1c49f4f2e8 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 23 Aug 2024 12:48:34 +0200 Subject: [PATCH 047/144] Fix test --- .../utilities/rowProcessor/tests/outputProcessing.spec.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/server/src/utilities/rowProcessor/tests/outputProcessing.spec.ts b/packages/server/src/utilities/rowProcessor/tests/outputProcessing.spec.ts index fd48f46016..0d0049a36e 100644 --- a/packages/server/src/utilities/rowProcessor/tests/outputProcessing.spec.ts +++ b/packages/server/src/utilities/rowProcessor/tests/outputProcessing.spec.ts @@ -10,6 +10,14 @@ import { outputProcessing } from ".." import { generator, structures } from "@budibase/backend-core/tests" 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 => ({ processInputBBReference: jest.fn(), processInputBBReferences: jest.fn(), From 266ae96a804f288e9570f10e7900383363752f19 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 23 Aug 2024 13:54:55 +0200 Subject: [PATCH 048/144] Don't run for mssql and oracle --- .../server/src/api/routes/tests/row.spec.ts | 178 +++++++++--------- 1 file changed, 90 insertions(+), 88 deletions(-) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index 2922dc3939..c179711fcf 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -2554,96 +2554,98 @@ describe.each([ tableId = table._id! }) - it.each([ - ["get row", (row: Row) => config.api.row.get(tableId, row._id!)], - [ - "fetch", - async (row: Row) => { - const rows = await config.api.row.fetch(tableId) - return rows.find(r => r._id === row._id) - }, - ], - [ - "search", - async (row: Row) => { - const { rows } = await config.api.row.search(tableId) - return rows.find(r => r._id === row._id) - }, - ], - [ - "from view", - async (row: Row) => { - const table = await config.api.table.get(tableId) - const view = await config.api.viewV2.create({ - name: generator.guid(), - tableId, - schema: Object.keys(table.schema).reduce( - (acc, c) => ({ ...acc, [c]: { visible: true } }), - {} - ), - }) - const { rows } = await config.api.viewV2.search(view.id) - return rows.find(r => r._id === row._id!) - }, - ], - ["from original saved row", (row: Row) => row], - ])( - "can retrieve rows with populated relationships (via %s)", - async (__, retrieveDelegate) => { - const otherRows = _.sampleSize(auxData, 5) + !isMSSQL && + !isOracle && + it.each([ + ["get row", (row: Row) => config.api.row.get(tableId, row._id!)], + [ + "fetch", + async (row: Row) => { + const rows = await config.api.row.fetch(tableId) + return rows.find(r => r._id === row._id) + }, + ], + [ + "search", + async (row: Row) => { + const { rows } = await config.api.row.search(tableId) + return rows.find(r => r._id === row._id) + }, + ], + [ + "from view", + async (row: Row) => { + const table = await config.api.table.get(tableId) + const view = await config.api.viewV2.create({ + name: generator.guid(), + tableId, + schema: Object.keys(table.schema).reduce( + (acc, c) => ({ ...acc, [c]: { visible: true } }), + {} + ), + }) + const { rows } = await config.api.viewV2.search(view.id) + return rows.find(r => r._id === row._id!) + }, + ], + ["from original saved row", (row: Row) => row], + ])( + "can retrieve rows with populated relationships (via %s)", + async (__, retrieveDelegate) => { + const otherRows = _.sampleSize(auxData, 5) - const row = await config.api.row.save(tableId, { - title: generator.word(), - relWithNoSchema: [otherRows[0]], - relWithEmptySchema: [otherRows[1]], - relWithFullSchema: [otherRows[2]], - relWithHalfSchema: [otherRows[3]], - relWithIllegalSchema: [otherRows[4]], - }) - - const retrieved = await retrieveDelegate(row) - expect(retrieved).toEqual( - expect.objectContaining({ - title: row.title, - relWithNoSchema: [ - { - _id: otherRows[0]._id, - primaryDisplay: otherRows[0].name, - }, - ], - relWithEmptySchema: [ - { - _id: otherRows[1]._id, - primaryDisplay: otherRows[1].name, - }, - ], - relWithFullSchema: [ - { - _id: otherRows[2]._id, - primaryDisplay: otherRows[2].name, - name: otherRows[2].name, - age: otherRows[2].age, - id: otherRows[2].id, - }, - ], - relWithHalfSchema: [ - { - _id: otherRows[3]._id, - primaryDisplay: otherRows[3].name, - name: otherRows[3].name, - }, - ], - relWithIllegalSchema: [ - { - _id: otherRows[4]._id, - primaryDisplay: otherRows[4].name, - name: otherRows[4].name, - }, - ], + const row = await config.api.row.save(tableId, { + title: generator.word(), + relWithNoSchema: [otherRows[0]], + relWithEmptySchema: [otherRows[1]], + relWithFullSchema: [otherRows[2]], + relWithHalfSchema: [otherRows[3]], + relWithIllegalSchema: [otherRows[4]], }) - ) - } - ) + + const retrieved = await retrieveDelegate(row) + expect(retrieved).toEqual( + expect.objectContaining({ + title: row.title, + relWithNoSchema: [ + { + _id: otherRows[0]._id, + primaryDisplay: otherRows[0].name, + }, + ], + relWithEmptySchema: [ + { + _id: otherRows[1]._id, + primaryDisplay: otherRows[1].name, + }, + ], + relWithFullSchema: [ + { + _id: otherRows[2]._id, + primaryDisplay: otherRows[2].name, + name: otherRows[2].name, + age: otherRows[2].age, + id: otherRows[2].id, + }, + ], + relWithHalfSchema: [ + { + _id: otherRows[3]._id, + primaryDisplay: otherRows[3].name, + name: otherRows[3].name, + }, + ], + relWithIllegalSchema: [ + { + _id: otherRows[4]._id, + primaryDisplay: otherRows[4].name, + name: otherRows[4].name, + }, + ], + }) + ) + } + ) }) describe("Formula fields", () => { From 99bd057160fe9b3e915f1e550b9401a9e69f1378 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 23 Aug 2024 13:55:47 +0200 Subject: [PATCH 049/144] Add comment --- packages/server/src/api/routes/tests/row.spec.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index c179711fcf..c7604eb111 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -2554,6 +2554,8 @@ describe.each([ tableId = table._id! }) + // Upserting isn't yet supported in MSSQL or Oracle, see: + // https://github.com/knex/knex/pull/6050 !isMSSQL && !isOracle && it.each([ From 8929b4a336ffef45fdbac61d13ac7b4f0e0c4a10 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 23 Aug 2024 14:15:23 +0200 Subject: [PATCH 050/144] Fix jest issues --- .../server/src/api/routes/tests/row.spec.ts | 244 +++++++++--------- 1 file changed, 122 insertions(+), 122 deletions(-) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index c7604eb111..ca9ef134cb 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -2427,137 +2427,137 @@ describe.each([ }) }) - describe("relationships", () => { - let tableId: string + // Upserting isn't yet supported in MSSQL or Oracle, see: + // https://github.com/knex/knex/pull/6050 + !isMSSQL && + !isOracle && + describe("relationships", () => { + let tableId: string - let auxData: Row[] = [] + let auxData: Row[] = [] - beforeAll(async () => { - const aux2Table = await config.api.table.save(saveTableRequest()) - const aux2Data = await config.api.row.save(aux2Table._id!, {}) + beforeAll(async () => { + const aux2Table = await config.api.table.save(saveTableRequest()) + const aux2Data = await config.api.row.save(aux2Table._id!, {}) - const auxTable = await config.api.table.save( - saveTableRequest({ - primaryDisplay: "name", - schema: { - name: { - name: "name", - type: FieldType.STRING, - constraints: { presence: true }, + const auxTable = await config.api.table.save( + saveTableRequest({ + primaryDisplay: "name", + schema: { + name: { + name: "name", + type: FieldType.STRING, + constraints: { presence: true }, + }, + age: { + name: "age", + type: FieldType.NUMBER, + constraints: { presence: true }, + }, + address: { + name: "address", + type: FieldType.STRING, + constraints: { presence: true }, + visible: false, + }, + link: { + name: "link", + type: FieldType.LINK, + tableId: aux2Table._id!, + relationshipType: RelationshipType.MANY_TO_MANY, + fieldName: "fk_aux", + constraints: { presence: true }, + }, + formula: { + name: "formula", + type: FieldType.FORMULA, + formula: "{{ any }}", + constraints: { presence: true }, + }, }, - age: { - name: "age", - type: FieldType.NUMBER, - constraints: { presence: true }, - }, - address: { - name: "address", - type: FieldType.STRING, - constraints: { presence: true }, - visible: false, - }, - link: { - name: "link", - type: FieldType.LINK, - tableId: aux2Table._id!, - relationshipType: RelationshipType.MANY_TO_MANY, - fieldName: "fk_aux", - constraints: { presence: true }, - }, - formula: { - name: "formula", - type: FieldType.FORMULA, - formula: "{{ any }}", - constraints: { presence: true }, - }, - }, - }) - ) - const auxTableId = auxTable._id! - - for (const name of generator.unique(() => generator.name(), 10)) { - auxData.push( - await config.api.row.save(auxTableId, { - name, - age: generator.age(), - address: generator.address(), - link: [aux2Data], }) ) - } + const auxTableId = auxTable._id! - const table = await config.api.table.save( - saveTableRequest({ - schema: { - title: { - name: "title", - type: FieldType.STRING, - constraints: { presence: true }, - }, - relWithNoSchema: { - name: "relWithNoSchema", - relationshipType: RelationshipType.ONE_TO_MANY, - type: FieldType.LINK, - tableId: auxTableId, - fieldName: "fk_relWithNoSchema", - constraints: { presence: true }, - }, - relWithEmptySchema: { - name: "relWithEmptySchema", - relationshipType: RelationshipType.ONE_TO_MANY, - type: FieldType.LINK, - tableId: auxTableId, - fieldName: "fk_relWithEmptySchema", - constraints: { presence: true }, - schema: {}, - }, - relWithFullSchema: { - name: "relWithFullSchema", - relationshipType: RelationshipType.ONE_TO_MANY, - type: FieldType.LINK, - tableId: auxTableId, - fieldName: "fk_relWithFullSchema", - constraints: { presence: true }, - schema: Object.keys(auxTable.schema).reduce( - (acc, c) => ({ ...acc, [c]: { visible: true } }), - {} - ), - }, - relWithHalfSchema: { - name: "relWithHalfSchema", - relationshipType: RelationshipType.ONE_TO_MANY, - type: FieldType.LINK, - tableId: auxTableId, - fieldName: "fk_relWithHalfSchema", - constraints: { presence: true }, - schema: { - name: { visible: true }, - age: { visible: false, readonly: true }, + for (const name of generator.unique(() => generator.name(), 10)) { + auxData.push( + await config.api.row.save(auxTableId, { + name, + age: generator.age(), + address: generator.address(), + link: [aux2Data], + }) + ) + } + + const table = await config.api.table.save( + saveTableRequest({ + schema: { + title: { + name: "title", + type: FieldType.STRING, + constraints: { presence: true }, + }, + relWithNoSchema: { + name: "relWithNoSchema", + relationshipType: RelationshipType.ONE_TO_MANY, + type: FieldType.LINK, + tableId: auxTableId, + fieldName: "fk_relWithNoSchema", + constraints: { presence: true }, + }, + relWithEmptySchema: { + name: "relWithEmptySchema", + relationshipType: RelationshipType.ONE_TO_MANY, + type: FieldType.LINK, + tableId: auxTableId, + fieldName: "fk_relWithEmptySchema", + constraints: { presence: true }, + schema: {}, + }, + relWithFullSchema: { + name: "relWithFullSchema", + relationshipType: RelationshipType.ONE_TO_MANY, + type: FieldType.LINK, + tableId: auxTableId, + fieldName: "fk_relWithFullSchema", + constraints: { presence: true }, + schema: Object.keys(auxTable.schema).reduce( + (acc, c) => ({ ...acc, [c]: { visible: true } }), + {} + ), + }, + relWithHalfSchema: { + name: "relWithHalfSchema", + relationshipType: RelationshipType.ONE_TO_MANY, + type: FieldType.LINK, + tableId: auxTableId, + fieldName: "fk_relWithHalfSchema", + constraints: { presence: true }, + schema: { + name: { visible: true }, + age: { visible: false, readonly: true }, + }, + }, + relWithIllegalSchema: { + name: "relWithIllegalSchema", + relationshipType: RelationshipType.ONE_TO_MANY, + type: FieldType.LINK, + tableId: auxTableId, + fieldName: "fk_relWithIllegalSchema", + constraints: { presence: true }, + schema: { + name: { visible: true }, + address: { visible: true }, + unexisting: { visible: true }, + }, }, }, - relWithIllegalSchema: { - name: "relWithIllegalSchema", - relationshipType: RelationshipType.ONE_TO_MANY, - type: FieldType.LINK, - tableId: auxTableId, - fieldName: "fk_relWithIllegalSchema", - constraints: { presence: true }, - schema: { - name: { visible: true }, - address: { visible: true }, - unexisting: { visible: true }, - }, - }, - }, - }) - ) - tableId = table._id! - }) + }) + ) + tableId = table._id! + }) - // Upserting isn't yet supported in MSSQL or Oracle, see: - // https://github.com/knex/knex/pull/6050 - !isMSSQL && - !isOracle && it.each([ ["get row", (row: Row) => config.api.row.get(tableId, row._id!)], [ @@ -2648,7 +2648,7 @@ describe.each([ ) } ) - }) + }) describe("Formula fields", () => { let table: Table From d4b0ef7d4757e9ed5c37d241a31d278b6897e950 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 27 Aug 2024 15:42:01 +0200 Subject: [PATCH 051/144] Feature flagging backend --- packages/backend-core/src/features/index.ts | 1 + packages/server/src/db/linkedRows/index.ts | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/backend-core/src/features/index.ts b/packages/backend-core/src/features/index.ts index e0bd29b1bc..1ae194ea0b 100644 --- a/packages/backend-core/src/features/index.ts +++ b/packages/backend-core/src/features/index.ts @@ -267,4 +267,5 @@ export class FlagSet, T extends { [key: string]: V }> { // default values set correctly and their types flow through the system. export const flags = new FlagSet({ DEFAULT_VALUES: Flag.boolean(false), + ENRICHED_RELATIONSHIPS: Flag.boolean(false), }) diff --git a/packages/server/src/db/linkedRows/index.ts b/packages/server/src/db/linkedRows/index.ts index 2394d00d0e..fa5808a28c 100644 --- a/packages/server/src/db/linkedRows/index.ts +++ b/packages/server/src/db/linkedRows/index.ts @@ -11,7 +11,7 @@ import { USER_METDATA_PREFIX } from "../utils" import partition from "lodash/partition" import { getGlobalUsersFromMetadata } from "../../utilities/global" import { processFormulas } from "../../utilities/rowProcessor" -import { context } from "@budibase/backend-core" +import { context, features } from "@budibase/backend-core" import { ContextUser, FieldType, @@ -272,7 +272,9 @@ export async function squashLinksToPrimaryDisplay( const obj: any = { _id: link._id } obj.primaryDisplay = getPrimaryDisplayValue(link, linkedTable) - const allowRelationshipSchemas = true // TODO + const allowRelationshipSchemas = await features.flags.isEnabled( + "ENRICHED_RELATIONSHIPS" + ) if (schema.schema && allowRelationshipSchemas) { for (const relField of Object.entries(schema.schema) .filter(([_, field]) => field.visible !== false) From 5f40737e9ceac0adc2865e5fa1e1d13722a5b913 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 27 Aug 2024 16:15:25 +0200 Subject: [PATCH 052/144] Use enums --- packages/backend-core/src/features/index.ts | 4 ++-- packages/server/src/db/linkedRows/index.ts | 3 ++- packages/types/src/sdk/featureFlag.ts | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/backend-core/src/features/index.ts b/packages/backend-core/src/features/index.ts index 1ae194ea0b..5fa73ebabe 100644 --- a/packages/backend-core/src/features/index.ts +++ b/packages/backend-core/src/features/index.ts @@ -1,7 +1,7 @@ import env from "../environment" import * as context from "../context" import { PostHog, PostHogOptions } from "posthog-node" -import { IdentityType, UserCtx } from "@budibase/types" +import { FeatureFlag, IdentityType, UserCtx } from "@budibase/types" import tracer from "dd-trace" let posthog: PostHog | undefined @@ -267,5 +267,5 @@ export class FlagSet, T extends { [key: string]: V }> { // default values set correctly and their types flow through the system. export const flags = new FlagSet({ DEFAULT_VALUES: Flag.boolean(false), - ENRICHED_RELATIONSHIPS: Flag.boolean(false), + [FeatureFlag.ENRICHED_RELATIONSHIPS]: Flag.boolean(false), }) diff --git a/packages/server/src/db/linkedRows/index.ts b/packages/server/src/db/linkedRows/index.ts index fa5808a28c..3881060d68 100644 --- a/packages/server/src/db/linkedRows/index.ts +++ b/packages/server/src/db/linkedRows/index.ts @@ -14,6 +14,7 @@ import { processFormulas } from "../../utilities/rowProcessor" import { context, features } from "@budibase/backend-core" import { ContextUser, + FeatureFlag, FieldType, LinkDocumentValue, Row, @@ -273,7 +274,7 @@ export async function squashLinksToPrimaryDisplay( obj.primaryDisplay = getPrimaryDisplayValue(link, linkedTable) const allowRelationshipSchemas = await features.flags.isEnabled( - "ENRICHED_RELATIONSHIPS" + FeatureFlag.ENRICHED_RELATIONSHIPS ) if (schema.schema && allowRelationshipSchemas) { for (const relField of Object.entries(schema.schema) diff --git a/packages/types/src/sdk/featureFlag.ts b/packages/types/src/sdk/featureFlag.ts index 257b4ee576..3d96b63c64 100644 --- a/packages/types/src/sdk/featureFlag.ts +++ b/packages/types/src/sdk/featureFlag.ts @@ -1,6 +1,7 @@ export enum FeatureFlag { PER_CREATOR_PER_USER_PRICE = "PER_CREATOR_PER_USER_PRICE", PER_CREATOR_PER_USER_PRICE_ALERT = "PER_CREATOR_PER_USER_PRICE_ALERT", + ENRICHED_RELATIONSHIPS = "ENRICHED_RELATIONSHIPS", } export interface TenantFeatureFlags { From 8a2bc639ea6549b571d73c6f9565fe1a4ecdeed3 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 27 Aug 2024 16:21:32 +0200 Subject: [PATCH 053/144] Flag checks on frontend --- .../components/backend/DataTable/TableDataTable.svelte | 5 ++++- .../backend/DataTable/ViewV2DataTable.svelte | 3 +++ .../grid/controls/ColumnsSettingContent.svelte | 10 +++++----- .../src/components/grid/layout/Grid.svelte | 2 ++ 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/builder/src/components/backend/DataTable/TableDataTable.svelte b/packages/builder/src/components/backend/DataTable/TableDataTable.svelte index 525421f996..ba3b37f812 100644 --- a/packages/builder/src/components/backend/DataTable/TableDataTable.svelte +++ b/packages/builder/src/components/backend/DataTable/TableDataTable.svelte @@ -1,6 +1,6 @@ -{#if $routeStore.routerLoaded} - {#key screenDefinition?._id} +{#if $routeStore.routerLoaded && screen?.props} + {#key screen.props._id} - + {/key} {/if} diff --git a/packages/client/src/components/app/container/GridContainer.svelte b/packages/client/src/components/app/container/GridContainer.svelte index bbcdbbf689..4dc75f3513 100644 --- a/packages/client/src/components/app/container/GridContainer.svelte +++ b/packages/client/src/components/app/container/GridContainer.svelte @@ -4,8 +4,6 @@ import { GridRowHeight, GridColumns } from "constants" import { memo } from "@budibase/frontend-core" - export let addEmptyRows = false - const component = getContext("component") const { styleable, builderStore } = getContext("sdk") const context = getContext("context") @@ -18,11 +16,8 @@ let styles = memo({}) $: inBuilder = $builderStore.inBuilder - $: requiredRows = calculateRequiredRows( - $children, - mobile, - addEmptyRows && inBuilder - ) + $: addEmptyRows = $component.isRoot && inBuilder + $: requiredRows = calculateRequiredRows($children, mobile, addEmptyRows) $: requiredHeight = requiredRows * GridRowHeight $: availableRows = Math.floor(height / GridRowHeight) $: rows = Math.max(requiredRows, availableRows) From 528a21643cae6434b28e1ee999b75b1641372267 Mon Sep 17 00:00:00 2001 From: melohagan <101575380+melohagan@users.noreply.github.com> Date: Fri, 6 Sep 2024 09:01:05 +0100 Subject: [PATCH 135/144] Fix: unable to upgrade plan (#14527) * Don't allow admin users to select themselves * Disable upgrade button for non account holders --- .../portal/_components/LockedFeature.svelte | 25 ++++++++++++++++--- .../builder/portal/users/users/index.svelte | 7 ++++-- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/packages/builder/src/pages/builder/portal/_components/LockedFeature.svelte b/packages/builder/src/pages/builder/portal/_components/LockedFeature.svelte index e6f4075e2e..1df724099b 100644 --- a/packages/builder/src/pages/builder/portal/_components/LockedFeature.svelte +++ b/packages/builder/src/pages/builder/portal/_components/LockedFeature.svelte @@ -1,10 +1,12 @@ @@ -36,8 +40,9 @@ {:else}
+ {#if upgradeDisabled} + +
+ +
+
+ {/if}
{/if}
@@ -67,7 +82,11 @@ justify-content: flex-start; gap: var(--spacing-m); } - + .icon { + position: relative; + display: flex; + justify-content: center; + } .buttons { display: flex; flex-direction: row; diff --git a/packages/builder/src/pages/builder/portal/users/users/index.svelte b/packages/builder/src/pages/builder/portal/users/users/index.svelte index b91b2129e6..a1d5496ff6 100644 --- a/packages/builder/src/pages/builder/portal/users/users/index.svelte +++ b/packages/builder/src/pages/builder/portal/users/users/index.svelte @@ -127,7 +127,10 @@ name: user.firstName ? user.firstName + " " + user.lastName : "", userGroups, __selectable: - role.value === Constants.BudibaseRoles.Owner ? false : undefined, + role.value === Constants.BudibaseRoles.Owner || + $auth.user?.email === user.email + ? false + : true, apps: [...new Set(Object.keys(user.roles))], access: role.sortOrder, } @@ -392,7 +395,7 @@ allowSelectRows={!readonly} {customRenderers} loading={!$fetch.loaded || !groupsLoaded} - defaultSortColumn={"access"} + defaultSortColumn={"__selectable"} />