From 87362be154b8bf92de9dac5ef959a32e68dbea66 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 20 Sep 2023 10:51:21 +0100 Subject: [PATCH 001/154] Removing required from roles on user API, as it is now paid. --- packages/server/specs/openapi.json | 5 +---- packages/server/specs/openapi.yaml | 3 --- packages/server/specs/resources/user.ts | 2 +- packages/server/src/definitions/openapi.ts | 6 +++--- specs/openapi.json | 1 - specs/openapi.yaml | 1 - 6 files changed, 5 insertions(+), 13 deletions(-) delete mode 120000 specs/openapi.json delete mode 120000 specs/openapi.yaml diff --git a/packages/server/specs/openapi.json b/packages/server/specs/openapi.json index 1e5718c5b5..5301d340bc 100644 --- a/packages/server/specs/openapi.json +++ b/packages/server/specs/openapi.json @@ -1550,8 +1550,7 @@ } }, "required": [ - "email", - "roles" + "email" ] }, "userOutput": { @@ -1622,7 +1621,6 @@ }, "required": [ "email", - "roles", "_id" ] } @@ -1701,7 +1699,6 @@ }, "required": [ "email", - "roles", "_id" ] } diff --git a/packages/server/specs/openapi.yaml b/packages/server/specs/openapi.yaml index 07320917b8..a525ec3be1 100644 --- a/packages/server/specs/openapi.yaml +++ b/packages/server/specs/openapi.yaml @@ -1324,7 +1324,6 @@ components: role ID, e.g. ADMIN. required: - email - - roles userOutput: type: object properties: @@ -1385,7 +1384,6 @@ components: type: string required: - email - - roles - _id required: - data @@ -1451,7 +1449,6 @@ components: type: string required: - email - - roles - _id required: - data diff --git a/packages/server/specs/resources/user.ts b/packages/server/specs/resources/user.ts index d00ed02f81..caf736f1ab 100644 --- a/packages/server/specs/resources/user.ts +++ b/packages/server/specs/resources/user.ts @@ -92,7 +92,7 @@ const userSchema = object( }, }, }, - { required: ["email", "roles"] } + { required: ["email"] } ) const userOutputSchema = { diff --git a/packages/server/src/definitions/openapi.ts b/packages/server/src/definitions/openapi.ts index fe5c17b218..5c44b6259f 100644 --- a/packages/server/src/definitions/openapi.ts +++ b/packages/server/src/definitions/openapi.ts @@ -599,7 +599,7 @@ export interface components { global?: boolean; }; /** @description Contains the roles of the user per app (assuming they are not a builder user). This field can only be set on a business or enterprise license. */ - roles: { [key: string]: string }; + roles?: { [key: string]: string }; }; userOutput: { data: { @@ -629,7 +629,7 @@ export interface components { global?: boolean; }; /** @description Contains the roles of the user per app (assuming they are not a builder user). This field can only be set on a business or enterprise license. */ - roles: { [key: string]: string }; + roles?: { [key: string]: string }; /** @description The ID of the user. */ _id: string; }; @@ -662,7 +662,7 @@ export interface components { global?: boolean; }; /** @description Contains the roles of the user per app (assuming they are not a builder user). This field can only be set on a business or enterprise license. */ - roles: { [key: string]: string }; + roles?: { [key: string]: string }; /** @description The ID of the user. */ _id: string; }[]; diff --git a/specs/openapi.json b/specs/openapi.json deleted file mode 120000 index 6e1531e3f4..0000000000 --- a/specs/openapi.json +++ /dev/null @@ -1 +0,0 @@ -../packages/server/specs/openapi.json \ No newline at end of file diff --git a/specs/openapi.yaml b/specs/openapi.yaml deleted file mode 120000 index 67db6af71f..0000000000 --- a/specs/openapi.yaml +++ /dev/null @@ -1 +0,0 @@ -../packages/server/specs/openapi.yaml \ No newline at end of file From 65d96b5ec5405c836af9a3d958d0fbab978c690c Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 20 Sep 2023 12:13:10 +0100 Subject: [PATCH 002/154] Adding message to let user know why roles have been ignored, as well as adding test case for this. --- .../api/controllers/public/mapping/users.ts | 9 +++-- .../src/api/controllers/public/users.ts | 34 +++++++++++++++---- .../src/api/routes/public/tests/users.spec.ts | 6 ++++ packages/server/src/utilities/users.ts | 3 ++ 4 files changed, 44 insertions(+), 8 deletions(-) diff --git a/packages/server/src/api/controllers/public/mapping/users.ts b/packages/server/src/api/controllers/public/mapping/users.ts index db08b16552..2a158bede9 100644 --- a/packages/server/src/api/controllers/public/mapping/users.ts +++ b/packages/server/src/api/controllers/public/mapping/users.ts @@ -15,10 +15,15 @@ function user(body: any): User { } } -function mapUser(ctx: any): { data: User } { - return { +function mapUser(ctx: any) { + const body: { data: User; message?: string } = { data: user(ctx.body), } + if (ctx.extra?.message) { + body.message = ctx.extra.message + delete ctx.extra + } + return body } function mapUsers(ctx: any): { data: User[] } { diff --git a/packages/server/src/api/controllers/public/users.ts b/packages/server/src/api/controllers/public/users.ts index bb6fc3a6e7..4265c7ac22 100644 --- a/packages/server/src/api/controllers/public/users.ts +++ b/packages/server/src/api/controllers/public/users.ts @@ -10,6 +10,32 @@ import { search as stringSearch } from "./utils" import { UserCtx, User } from "@budibase/types" import { Next } from "koa" import { sdk } from "@budibase/pro" +import { isEqual, cloneDeep } from "lodash" + +function rolesRemoved(base: User, ctx: UserCtx) { + return ( + !isEqual(base.builder, ctx.request.body.builder) || + !isEqual(base.admin, ctx.request.body.admin) || + !isEqual(base.roles, ctx.request.body.roles) + ) +} + +const NO_ROLES_MSG = + "Roles/admin/builder can only be set on business/enterprise licenses - input ignored." + +async function createUpdateResponse(ctx: UserCtx, user?: User) { + const base = cloneDeep(ctx.request.body) + ctx = await sdk.publicApi.users.roleCheck(ctx, user) + // check the ctx before any updates to it + const removed = rolesRemoved(base, ctx) + ctx = publicApiUserFix(ctx) + const response = await saveGlobalUser(ctx) + ctx.body = await getUser(ctx, response._id) + if (removed) { + ctx.extra = { message: NO_ROLES_MSG } + } + return ctx +} function isLoggedInUser(ctx: UserCtx, user: User) { const loggedInId = ctx.user?._id @@ -35,9 +61,7 @@ export async function search(ctx: UserCtx, next: Next) { } export async function create(ctx: UserCtx, next: Next) { - ctx = publicApiUserFix(await sdk.publicApi.users.roleCheck(ctx)) - const response = await saveGlobalUser(ctx) - ctx.body = await getUser(ctx, response._id) + await createUpdateResponse(ctx) await next() } @@ -52,9 +76,7 @@ export async function update(ctx: UserCtx, next: Next) { ...ctx.request.body, _rev: user._rev, } - ctx = publicApiUserFix(await sdk.publicApi.users.roleCheck(ctx, user)) - const response = await saveGlobalUser(ctx) - ctx.body = await getUser(ctx, response._id) + await createUpdateResponse(ctx, user) await next() } diff --git a/packages/server/src/api/routes/public/tests/users.spec.ts b/packages/server/src/api/routes/public/tests/users.spec.ts index c81acca1df..7856409bec 100644 --- a/packages/server/src/api/routes/public/tests/users.spec.ts +++ b/packages/server/src/api/routes/public/tests/users.spec.ts @@ -68,6 +68,7 @@ describe("no user role update in free", () => { }) expect(res.status).toBe(200) expect(res.body.data.roles["app_a"]).toBeUndefined() + expect(res.body.message).toBeDefined() }) it("should not allow 'admin' to be updated", async () => { @@ -77,6 +78,7 @@ describe("no user role update in free", () => { }) expect(res.status).toBe(200) expect(res.body.data.admin).toBeUndefined() + expect(res.body.message).toBeDefined() }) it("should not allow 'builder' to be updated", async () => { @@ -86,6 +88,7 @@ describe("no user role update in free", () => { }) expect(res.status).toBe(200) expect(res.body.data.builder).toBeUndefined() + expect(res.body.message).toBeDefined() }) }) @@ -102,6 +105,7 @@ describe("no user role update in business", () => { }) expect(res.status).toBe(200) expect(res.body.data.roles["app_a"]).toBe("BASIC") + expect(res.body.message).toBeUndefined() }) it("should allow 'admin' to be updated", async () => { @@ -112,6 +116,7 @@ describe("no user role update in business", () => { }) expect(res.status).toBe(200) expect(res.body.data.admin.global).toBe(true) + expect(res.body.message).toBeUndefined() }) it("should allow 'builder' to be updated", async () => { @@ -122,5 +127,6 @@ describe("no user role update in business", () => { }) expect(res.status).toBe(200) expect(res.body.data.builder.global).toBe(true) + expect(res.body.message).toBeUndefined() }) }) diff --git a/packages/server/src/utilities/users.ts b/packages/server/src/utilities/users.ts index f841ec3646..bbc1370355 100644 --- a/packages/server/src/utilities/users.ts +++ b/packages/server/src/utilities/users.ts @@ -36,5 +36,8 @@ export function publicApiUserFix(ctx: UserCtx) { if (!ctx.request.body._id && ctx.params.userId) { ctx.request.body._id = ctx.params.userId } + if (!ctx.request.body.roles) { + ctx.request.body.roles = {} + } return ctx } From 7db7159b4cdcf76710278d7c30eaec4f11309e2d Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Wed, 20 Sep 2023 11:54:52 +0000 Subject: [PATCH 003/154] Bump version to 2.10.12 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index fbecff9293..19298d35ec 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.10.11", + "version": "2.10.12", "npmClient": "yarn", "packages": [ "packages/*" From 69c688a03ee7b551db31ca1b60e7d9aee1e3cd01 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 22 Sep 2023 08:48:34 +0100 Subject: [PATCH 004/154] Update section component grid column styles to explicit widths to prevent issues with dynamic resizing and overflow --- .../client/src/components/app/Section.svelte | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/packages/client/src/components/app/Section.svelte b/packages/client/src/components/app/Section.svelte index 11f0f2978f..b86c5cc352 100644 --- a/packages/client/src/components/app/Section.svelte +++ b/packages/client/src/components/app/Section.svelte @@ -47,28 +47,29 @@ diff --git a/packages/server/src/automations/steps/make.ts b/packages/server/src/automations/steps/make.ts index e90648d573..06e96907d9 100644 --- a/packages/server/src/automations/steps/make.ts +++ b/packages/server/src/automations/steps/make.ts @@ -78,8 +78,7 @@ export const definition: AutomationStepSchema = { } export async function run({ inputs }: AutomationStepInput) { - //TODO - Remove deprecated values 1,2,3,4,5 after November 2023 - const { url, value1, value2, value3, value4, value5, body } = inputs + const { url, body } = inputs let payload = {} try { @@ -104,11 +103,6 @@ export async function run({ inputs }: AutomationStepInput) { response = await fetch(url, { method: "post", body: JSON.stringify({ - value1, - value2, - value3, - value4, - value5, ...payload, }), headers: { diff --git a/packages/server/src/automations/steps/zapier.ts b/packages/server/src/automations/steps/zapier.ts index d69ac39ba7..eeff0c2c7d 100644 --- a/packages/server/src/automations/steps/zapier.ts +++ b/packages/server/src/automations/steps/zapier.ts @@ -71,8 +71,7 @@ export const definition: AutomationStepSchema = { } export async function run({ inputs }: AutomationStepInput) { - //TODO - Remove deprecated values 1,2,3,4,5 after November 2023 - const { url, value1, value2, value3, value4, value5, body } = inputs + const { url, body } = inputs let payload = {} try { @@ -100,11 +99,6 @@ export async function run({ inputs }: AutomationStepInput) { method: "post", body: JSON.stringify({ platform: "budibase", - value1, - value2, - value3, - value4, - value5, ...payload, }), headers: { From d88c1be588c20eeaa19f050cdb867fbf4f6ba37a Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Fri, 29 Sep 2023 09:15:54 +0000 Subject: [PATCH 103/154] Bump version to 2.10.16-alpha.12 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index d99355a157..b8fd13005b 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.10.16-alpha.11", + "version": "2.10.16-alpha.12", "npmClient": "yarn", "packages": [ "packages/*" From 46a224751fcdd6c74d3f009cdad8db4ec0364a90 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 29 Sep 2023 11:47:23 +0200 Subject: [PATCH 104/154] Types --- packages/server/src/api/controllers/row/external.ts | 4 +--- packages/server/src/api/controllers/table/index.ts | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/server/src/api/controllers/row/external.ts b/packages/server/src/api/controllers/row/external.ts index 899849e3a7..ddc63e5790 100644 --- a/packages/server/src/api/controllers/row/external.ts +++ b/packages/server/src/api/controllers/row/external.ts @@ -108,13 +108,11 @@ export async function save(ctx: UserCtx) { row, }) - const responseRow = response as { row: Row } - if (!isEqual(table, updatedTable)) { await sdk.tables.saveTable(updatedTable) } - const rowId = responseRow.row._id + const rowId = response.row._id if (rowId) { const row = await sdk.rows.external.getRow(tableId, rowId, { relationships: true, diff --git a/packages/server/src/api/controllers/table/index.ts b/packages/server/src/api/controllers/table/index.ts index 5029856cf4..e7c6ae57b0 100644 --- a/packages/server/src/api/controllers/table/index.ts +++ b/packages/server/src/api/controllers/table/index.ts @@ -14,7 +14,6 @@ import { Table, TableResponse, UserCtx, - Datasource, } from "@budibase/types" import sdk from "../../../sdk" import { jsonFromCsvString } from "../../../utilities/csv" From 89f753950340914ee5c33335cee96c1d34cf7f8a Mon Sep 17 00:00:00 2001 From: melohagan <101575380+melohagan@users.noreply.github.com> Date: Fri, 29 Sep 2023 11:12:43 +0100 Subject: [PATCH 105/154] Update automation bindings (#11930) * Deprecate zapier+make values1..5 * Allow dates in meeting invite to be bindable * Add DrawerBindableSlot component * Show calendar icon * refactor * Use cancel icon * Disable cancel for JS bindings * WIP * WIP * WIP * Fix linkedIds svelte binding * Label and icon update * Handle arrays * Support text area * Add reactivity to text area icon * Support boolean * JSON support * Remove rowControl * Allow boolean field to have three states * lint * Refactor * Exclude bindings button for formula types --------- Co-authored-by: Michael Drury --- .../src/components/common/bindings/DrawerBindableSlot.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/components/common/bindings/DrawerBindableSlot.svelte b/packages/builder/src/components/common/bindings/DrawerBindableSlot.svelte index 3471b53ce9..b38e8c3ae5 100644 --- a/packages/builder/src/components/common/bindings/DrawerBindableSlot.svelte +++ b/packages/builder/src/components/common/bindings/DrawerBindableSlot.svelte @@ -152,7 +152,7 @@ {updateOnChange} /> {/if} - {#if !disabled} + {#if !disabled && type !== "formula"}
{ From b63f349015897e5e54c13f2fcc831940ed9db4a6 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Fri, 29 Sep 2023 10:13:02 +0000 Subject: [PATCH 106/154] Bump version to 2.10.16-alpha.13 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index b8fd13005b..9a9a8bff1f 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.10.16-alpha.12", + "version": "2.10.16-alpha.13", "npmClient": "yarn", "packages": [ "packages/*" From 711f82099fa5638ae8d95167874f0cfa6696f953 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 29 Sep 2023 12:48:55 +0200 Subject: [PATCH 107/154] Extend bbreference test to check link relationships --- .../server/src/api/routes/tests/row.spec.ts | 267 ++++++++++-------- 1 file changed, 143 insertions(+), 124 deletions(-) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index b4a33efdde..ef4d205021 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -18,7 +18,6 @@ import { SortType, StaticQuotaName, Table, - User, } from "@budibase/types" import { expectAnyExternalColsAttributes, @@ -1515,9 +1514,82 @@ describe.each([ }) }) - describe("bb reference fields", () => { + let o2mTable: Table + let m2mTable: Table + beforeAll(async () => { + o2mTable = await config.createTable( + { ...generateTableConfig(), name: "o2m" }, + { + skipReassigning: true, + } + ) + m2mTable = await config.createTable( + { ...generateTableConfig(), name: "m2m" }, + { + skipReassigning: true, + } + ) + }) + + describe.each([ + [ + "relationship fields", + () => ({ + user: { + name: "user", + relationshipType: RelationshipType.ONE_TO_MANY, + type: FieldType.LINK, + tableId: o2mTable._id!, + fieldName: "fk_o2m", + }, + users: { + name: "users", + relationshipType: RelationshipType.MANY_TO_MANY, + type: FieldType.LINK, + tableId: m2mTable._id!, + fieldName: "fk_m2m", + }, + }), + (tableId: string) => + config.api.row.save(tableId, { + name: generator.word(), + description: generator.paragraph(), + tableId, + }), + (row: Row) => ({ + _id: row._id, + primaryDisplay: row.name, + }), + ], + [ + "bb reference fields", + () => ({ + user: { + name: "user", + relationshipType: RelationshipType.ONE_TO_MANY, + type: FieldType.BB_REFERENCE, + subtype: FieldTypeSubtypes.BB_REFERENCE.USER, + }, + users: { + name: "users", + type: FieldType.BB_REFERENCE, + subtype: FieldTypeSubtypes.BB_REFERENCE.USER, + relationshipType: RelationshipType.MANY_TO_MANY, + }, + }), + () => config.createUser(), + (row: Row) => ({ + _id: row._id, + email: row.email, + firstName: row.firstName, + lastName: row.lastName, + primaryDisplay: row.email, + }), + ], + ])("links - %s", (__, relSchema, dataGenerator, resultMapper) => { let tableId: string - let users: User[] + let o2mData: Row[] + let m2mData: Row[] beforeAll(async () => { const tableConfig = generateTableConfig() @@ -1532,31 +1604,27 @@ describe.each([ ...tableConfig, schema: { ...tableConfig.schema, - user: { - name: "user", - type: FieldType.BB_REFERENCE, - subtype: FieldTypeSubtypes.BB_REFERENCE.USER, - relationshipType: RelationshipType.ONE_TO_MANY, - }, - users: { - name: "users", - type: FieldType.BB_REFERENCE, - subtype: FieldTypeSubtypes.BB_REFERENCE.USER, - relationshipType: RelationshipType.MANY_TO_MANY, - }, + ...relSchema(), }, }) tableId = table._id! - users = [ - await config.createUser(), - await config.createUser(), - await config.createUser(), - await config.createUser(), + o2mData = [ + await dataGenerator(o2mTable._id!), + await dataGenerator(o2mTable._id!), + await dataGenerator(o2mTable._id!), + await dataGenerator(o2mTable._id!), + ] + + m2mData = [ + await dataGenerator(m2mTable._id!), + await dataGenerator(m2mTable._id!), + await dataGenerator(m2mTable._id!), + await dataGenerator(m2mTable._id!), ] }) - it("can save a row when BB reference fields are empty", async () => { + it("can save a row when relationship fields are empty", async () => { const rowData = { ...basicRow(tableId), name: generator.name(), @@ -1575,13 +1643,13 @@ describe.each([ }) }) - it("can save a row with a single BB reference field", async () => { - const user = _.sample(users)! + it("can save a row with a single relationship field", async () => { + const user = _.sample(o2mData)! const rowData = { ...basicRow(tableId), name: generator.name(), description: generator.name(), - user: user, + user: [user], } const row = await config.api.row.save(tableId, rowData) @@ -1589,24 +1657,17 @@ describe.each([ name: rowData.name, description: rowData.description, tableId, - user: [ - { - _id: user._id, - email: user.email, - firstName: user.firstName, - lastName: user.lastName, - primaryDisplay: user.email, - }, - ], + user: [user].map(u => resultMapper(u)), _id: expect.any(String), _rev: expect.any(String), id: isInternal ? undefined : expect.any(Number), type: isInternal ? "row" : undefined, + [`fk_${o2mTable.name}_fk_o2m`]: isInternal ? undefined : user.id, }) }) - it("can save a row with a multiple BB reference field", async () => { - const selectedUsers = _.sampleSize(users, 2) + it("can save a row with a multiple relationship field", async () => { + const selectedUsers = _.sampleSize(m2mData, 2) const rowData = { ...basicRow(tableId), name: generator.name(), @@ -1619,13 +1680,7 @@ describe.each([ name: rowData.name, description: rowData.description, tableId, - users: selectedUsers.map(u => ({ - _id: u._id, - email: u.email, - firstName: u.firstName, - lastName: u.lastName, - primaryDisplay: u.email, - })), + users: expect.arrayContaining(selectedUsers.map(u => resultMapper(u))), _id: expect.any(String), _rev: expect.any(String), id: isInternal ? undefined : expect.any(Number), @@ -1633,7 +1688,7 @@ describe.each([ }) }) - it("can retrieve rows with no populated BB references", async () => { + it("can retrieve rows with no populated relationships", async () => { const rowData = { ...basicRow(tableId), name: generator.name(), @@ -1655,14 +1710,15 @@ describe.each([ }) }) - it("can retrieve rows with populated BB references", async () => { - const [user1, user2] = _.sampleSize(users, 2) + it("can retrieve rows with populated relationships", async () => { + const user1 = _.sample(o2mData)! + const [user2, user3] = _.sampleSize(m2mData, 2) const rowData = { ...basicRow(tableId), name: generator.name(), description: generator.name(), - users: [user1, user2], + users: [user2, user3], user: [user1], } const row = await config.api.row.save(tableId, rowData) @@ -1672,72 +1728,51 @@ describe.each([ name: rowData.name, description: rowData.description, tableId, - user: [user1].map(u => ({ - _id: u._id, - email: u.email, - firstName: u.firstName, - lastName: u.lastName, - primaryDisplay: u.email, - })), - users: [user1, user2].map(u => ({ - _id: u._id, - email: u.email, - firstName: u.firstName, - lastName: u.lastName, - primaryDisplay: u.email, - })), + user: expect.arrayContaining([user1].map(u => resultMapper(u))), + users: expect.arrayContaining([user2, user3].map(u => resultMapper(u))), _id: row._id, _rev: expect.any(String), id: isInternal ? undefined : expect.any(Number), + [`fk_${o2mTable.name}_fk_o2m`]: isInternal ? undefined : user1.id, ...defaultRowFields, }) }) it("can update an existing populated row", async () => { - const [user1, user2, user3] = _.sampleSize(users, 3) + const user = _.sample(o2mData)! + const [users1, users2, users3] = _.sampleSize(m2mData, 3) const rowData = { ...basicRow(tableId), name: generator.name(), description: generator.name(), - users: [user1, user2], + users: [users1, users2], } const row = await config.api.row.save(tableId, rowData) const updatedRow = await config.api.row.save(tableId, { ...row, - user: [user3], - users: [user3, user2], + user: [user], + users: [users3, users1], }) expect(updatedRow).toEqual({ name: rowData.name, description: rowData.description, tableId, - user: [ - { - _id: user3._id, - email: user3.email, - firstName: user3.firstName, - lastName: user3.lastName, - primaryDisplay: user3.email, - }, - ], - users: [user3, user2].map(u => ({ - _id: u._id, - email: u.email, - firstName: u.firstName, - lastName: u.lastName, - primaryDisplay: u.email, - })), + user: expect.arrayContaining([user].map(u => resultMapper(u))), + users: expect.arrayContaining( + [users3, users1].map(u => resultMapper(u)) + ), _id: row._id, _rev: expect.any(String), id: isInternal ? undefined : expect.any(Number), type: isInternal ? "row" : undefined, + [`fk_${o2mTable.name}_fk_o2m`]: isInternal ? undefined : user.id, }) }) - it("can wipe an existing populated BB references in row", async () => { - const [user1, user2] = _.sampleSize(users, 2) + it("can wipe an existing populated relationships in row", async () => { + const [user1, user2] = _.sampleSize(m2mData, 2) const rowData = { ...basicRow(tableId), @@ -1765,34 +1800,35 @@ describe.each([ }) }) - it("fetch all will populate the BB references", async () => { - const [user1, user2, user3] = _.sampleSize(users, 3) + it("fetch all will populate the relationships", async () => { + const [user1] = _.sampleSize(o2mData, 1) + const [users1, users2, users3] = _.sampleSize(m2mData, 3) const rows: { name: string description: string - user?: User[] - users?: User[] + user?: Row[] + users?: Row[] tableId: string }[] = [ { ...basicRow(tableId), name: generator.name(), description: generator.name(), - users: [user1, user2], + users: [users1, users2], }, { ...basicRow(tableId), name: generator.name(), description: generator.name(), user: [user1], - users: [user1, user3], + users: [users1, users3], }, { ...basicRow(tableId), name: generator.name(), description: generator.name(), - users: [user3], + users: [users3], }, ] @@ -1808,57 +1844,48 @@ describe.each([ name: r.name, description: r.description, tableId, - user: r.user?.map(u => ({ - _id: u._id, - email: u.email, - firstName: u.firstName, - lastName: u.lastName, - primaryDisplay: u.email, - })), - users: r.users?.map(u => ({ - _id: u._id, - email: u.email, - firstName: u.firstName, - lastName: u.lastName, - primaryDisplay: u.email, - })), + user: r.user?.map(u => resultMapper(u)), + users: r.users?.map(u => resultMapper(u)), _id: expect.any(String), _rev: expect.any(String), id: isInternal ? undefined : expect.any(Number), + [`fk_${o2mTable.name}_fk_o2m`]: + isInternal || !r.user?.length ? undefined : r.user[0].id, ...defaultRowFields, })) ) ) }) - it("search all will populate the BB references", async () => { - const [user1, user2, user3] = _.sampleSize(users, 3) + it("search all will populate the relationships", async () => { + const [user1] = _.sampleSize(o2mData, 1) + const [users1, users2, users3] = _.sampleSize(m2mData, 3) const rows: { name: string description: string - user?: User[] - users?: User[] + user?: Row[] + users?: Row[] tableId: string }[] = [ { ...basicRow(tableId), name: generator.name(), description: generator.name(), - users: [user1, user2], + users: [users1, users2], }, { ...basicRow(tableId), name: generator.name(), description: generator.name(), user: [user1], - users: [user1, user3], + users: [users1, users3], }, { ...basicRow(tableId), name: generator.name(), description: generator.name(), - users: [user3], + users: [users3], }, ] @@ -1874,23 +1901,15 @@ describe.each([ name: r.name, description: r.description, tableId, - user: r.user?.map(u => ({ - _id: u._id, - email: u.email, - firstName: u.firstName, - lastName: u.lastName, - primaryDisplay: u.email, - })), - users: r.users?.map(u => ({ - _id: u._id, - email: u.email, - firstName: u.firstName, - lastName: u.lastName, - primaryDisplay: u.email, - })), + user: r.user?.map(u => resultMapper(u)), + users: r.users?.length + ? expect.arrayContaining(r.users?.map(u => resultMapper(u))) + : undefined, _id: expect.any(String), _rev: expect.any(String), id: isInternal ? undefined : expect.any(Number), + [`fk_${o2mTable.name}_fk_o2m`]: + isInternal || !r.user?.length ? undefined : r.user[0].id, ...defaultRowFields, })) ), From 17ecace6fad52e6b58c2b88aabbe7de5018e60f3 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 29 Sep 2023 12:54:16 +0200 Subject: [PATCH 108/154] Fix test --- packages/server/src/api/routes/tests/row.spec.ts | 4 +++- 1 file changed, 3 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 ef4d205021..662b5b9e7b 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -1845,7 +1845,9 @@ describe.each([ description: r.description, tableId, user: r.user?.map(u => resultMapper(u)), - users: r.users?.map(u => resultMapper(u)), + users: r.users?.length + ? expect.arrayContaining(r.users?.map(u => resultMapper(u))) + : undefined, _id: expect.any(String), _rev: expect.any(String), id: isInternal ? undefined : expect.any(Number), From ebe8a562f6f110eef49452c84c5af1b1bf82ef3c Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Fri, 29 Sep 2023 11:56:50 +0100 Subject: [PATCH 109/154] fix relationship direction issue --- .../backend/DataTable/modals/CreateEditColumn.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte index 5f1c8044d9..1c64dfe5ec 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte @@ -85,7 +85,7 @@ } let relationshipMap = { - [RelationshipType.MANY_TO_ONE]: { + [RelationshipType.ONE_TO_MANY]: { part1: PrettyRelationshipDefinitions.MANY, part2: PrettyRelationshipDefinitions.ONE, }, @@ -93,7 +93,7 @@ part1: PrettyRelationshipDefinitions.MANY, part2: PrettyRelationshipDefinitions.MANY, }, - [RelationshipType.ONE_TO_MANY]: { + [RelationshipType.MANY_TO_ONE]: { part1: PrettyRelationshipDefinitions.ONE, part2: PrettyRelationshipDefinitions.MANY, }, From 64fdb621e50b1302707258a81eb6ed3d6a057df5 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Fri, 29 Sep 2023 11:42:26 +0000 Subject: [PATCH 110/154] Bump version to 2.10.16-alpha.14 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 9a9a8bff1f..04ebf14448 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.10.16-alpha.13", + "version": "2.10.16-alpha.14", "npmClient": "yarn", "packages": [ "packages/*" From c5e52f7cbed1c7eceb03b1880cfb16323295d0d3 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 29 Sep 2023 14:02:45 +0200 Subject: [PATCH 111/154] Fix deleting one-2-many relationships --- .../src/api/controllers/row/ExternalRequest.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/server/src/api/controllers/row/ExternalRequest.ts b/packages/server/src/api/controllers/row/ExternalRequest.ts index 9ab96fba69..7a4f4c5a47 100644 --- a/packages/server/src/api/controllers/row/ExternalRequest.ts +++ b/packages/server/src/api/controllers/row/ExternalRequest.ts @@ -340,10 +340,16 @@ export class ExternalRequest { // one to many if (isOneSide(field)) { let id = row[key][0] - if (typeof row[key] === "string") { - id = decodeURIComponent(row[key]).match(/\[(.*?)\]/)?.[1] + if (id) { + if (typeof row[key] === "string") { + id = decodeURIComponent(row[key]).match(/\[(.*?)\]/)?.[1] + } + newRow[field.foreignKey || linkTablePrimary] = breakRowIdField(id)[0] + } else { + // + row[field.foreignKey || linkTablePrimary] = null + newRow[field.foreignKey || linkTablePrimary] = null } - newRow[field.foreignKey || linkTablePrimary] = breakRowIdField(id)[0] } // many to many else if (field.through) { From 1f7aa772c9c42d2dc353c601aef3341747e3fd6d Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 29 Sep 2023 14:05:52 +0200 Subject: [PATCH 112/154] Add comment --- packages/server/src/api/controllers/row/ExternalRequest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/api/controllers/row/ExternalRequest.ts b/packages/server/src/api/controllers/row/ExternalRequest.ts index 7a4f4c5a47..5f2502a1d1 100644 --- a/packages/server/src/api/controllers/row/ExternalRequest.ts +++ b/packages/server/src/api/controllers/row/ExternalRequest.ts @@ -346,7 +346,7 @@ export class ExternalRequest { } newRow[field.foreignKey || linkTablePrimary] = breakRowIdField(id)[0] } else { - // + // Removing from both new and row, as we don't know if it has already been processed row[field.foreignKey || linkTablePrimary] = null newRow[field.foreignKey || linkTablePrimary] = null } From 96327eaef11d9533d153356107d2053f1184dabb Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 29 Sep 2023 13:17:14 +0100 Subject: [PATCH 113/154] Quick fix for REST schema being updated by user, when the request is sent again/query performed again user updates are lost - this means that changes are maintained across runs. --- .../server/src/api/controllers/query/index.ts | 45 ++++++++++++++----- packages/server/src/threads/definitions.ts | 7 +-- packages/types/src/documents/app/query.ts | 2 +- 3 files changed, 35 insertions(+), 19 deletions(-) diff --git a/packages/server/src/api/controllers/query/index.ts b/packages/server/src/api/controllers/query/index.ts index b9a69f3988..cd52213be8 100644 --- a/packages/server/src/api/controllers/query/index.ts +++ b/packages/server/src/api/controllers/query/index.ts @@ -9,7 +9,7 @@ import { quotas } from "@budibase/pro" import { events, context, utils, constants } from "@budibase/backend-core" import sdk from "../../../sdk" import { QueryEvent } from "../../../threads/definitions" -import { Query } from "@budibase/types" +import { Query, UserCtx } from "@budibase/types" import { ValidQueryNameRegex } from "@budibase/shared-core" const Runner = new Thread(ThreadType.QUERY, { @@ -28,11 +28,11 @@ function enrichQueries(input: any) { return wasArray ? queries : queries[0] } -export async function fetch(ctx: any) { +export async function fetch(ctx: UserCtx) { ctx.body = await sdk.queries.fetch() } -const _import = async (ctx: any) => { +const _import = async (ctx: UserCtx) => { const body = ctx.request.body const data = body.data @@ -73,7 +73,7 @@ const _import = async (ctx: any) => { } export { _import as import } -export async function save(ctx: any) { +export async function save(ctx: UserCtx) { const db = context.getAppDB() const query = ctx.request.body @@ -100,19 +100,19 @@ export async function save(ctx: any) { ctx.message = `Query ${query.name} saved successfully.` } -export async function find(ctx: any) { +export async function find(ctx: UserCtx) { const queryId = ctx.params.queryId ctx.body = await sdk.queries.find(queryId) } //Required to discern between OIDC OAuth config entries -function getOAuthConfigCookieId(ctx: any) { +function getOAuthConfigCookieId(ctx: UserCtx) { if (ctx.user.providerType === constants.Config.OIDC) { return utils.getCookie(ctx, constants.Cookie.OIDC_CONFIG) } } -function getAuthConfig(ctx: any) { +function getAuthConfig(ctx: UserCtx) { const authCookie = utils.getCookie(ctx, constants.Cookie.Auth) let authConfigCtx: any = {} authConfigCtx["configId"] = getOAuthConfigCookieId(ctx) @@ -120,7 +120,7 @@ function getAuthConfig(ctx: any) { return authConfigCtx } -export async function preview(ctx: any) { +export async function preview(ctx: UserCtx) { const { datasource, envVars } = await sdk.datasources.getWithEnvVars( ctx.request.body.datasourceId ) @@ -129,6 +129,19 @@ export async function preview(ctx: any) { // this stops dynamic variables from calling the same query const { fields, parameters, queryVerb, transformer, queryId, schema } = query + let existingSchema = schema + if (queryId && !existingSchema) { + try { + const db = context.getAppDB() + const existing = (await db.get(queryId)) as Query + existingSchema = existing.schema + } catch (err) { + if (err.status !== 404) { + ctx.throw(500, "Unable to retrieve existing query") + } + } + } + const authConfigCtx: any = getAuthConfig(ctx) try { @@ -180,6 +193,14 @@ export async function preview(ctx: any) { schemaFields[key] = fieldType } } + // if existing schema, update to include any previous schema keys + if (existingSchema) { + for (let key of Object.keys(schemaFields)) { + if (existingSchema[key]?.type) { + schemaFields[key] = existingSchema[key].type + } + } + } // remove configuration before sending event delete datasource.config await events.query.previewed(datasource, query) @@ -195,7 +216,7 @@ export async function preview(ctx: any) { } async function execute( - ctx: any, + ctx: UserCtx, opts: any = { rowsOnly: false, isAutomation: false } ) { const db = context.getAppDB() @@ -260,12 +281,12 @@ async function execute( } } -export async function executeV1(ctx: any) { +export async function executeV1(ctx: UserCtx) { return execute(ctx, { rowsOnly: true, isAutomation: false }) } export async function executeV2( - ctx: any, + ctx: UserCtx, { isAutomation }: { isAutomation?: boolean } = {} ) { return execute(ctx, { rowsOnly: false, isAutomation }) @@ -292,7 +313,7 @@ const removeDynamicVariables = async (queryId: any) => { } } -export async function destroy(ctx: any) { +export async function destroy(ctx: UserCtx) { const db = context.getAppDB() const queryId = ctx.params.queryId await removeDynamicVariables(queryId) diff --git a/packages/server/src/threads/definitions.ts b/packages/server/src/threads/definitions.ts index dd0891d34a..8915642949 100644 --- a/packages/server/src/threads/definitions.ts +++ b/packages/server/src/threads/definitions.ts @@ -11,12 +11,7 @@ export interface QueryEvent { queryId: string environmentVariables?: Record ctx?: any - schema?: { - [key: string]: { - name: string - type: string - } - } + schema?: Record } export interface QueryVariable { diff --git a/packages/types/src/documents/app/query.ts b/packages/types/src/documents/app/query.ts index 31a3a3ba09..c288ed9980 100644 --- a/packages/types/src/documents/app/query.ts +++ b/packages/types/src/documents/app/query.ts @@ -6,7 +6,7 @@ export interface Query extends Document { parameters: QueryParameter[] fields: RestQueryFields | any transformer: string | null - schema: any + schema: Record readable: boolean queryVerb: string } From 45ac9ae06c717e20f29895665d96ba5968a3b17c Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 29 Sep 2023 13:33:49 +0100 Subject: [PATCH 114/154] Quick update to fix test case. --- packages/server/src/api/routes/tests/row.spec.ts | 14 ++++++++------ 1 file changed, 8 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 662b5b9e7b..ec502f17bb 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -1787,17 +1787,19 @@ describe.each([ user: null, users: null, }) - expect(updatedRow).toEqual({ + const toCompare: any = { name: rowData.name, description: rowData.description, tableId, - user: isInternal ? null : undefined, - users: isInternal ? null : undefined, _id: row._id, _rev: expect.any(String), - id: isInternal ? undefined : expect.any(Number), - type: isInternal ? "row" : undefined, - }) + type: "row", + } + if (!isInternal) { + toCompare.user = null + toCompare.users = null + } + expect(updatedRow).toEqual(toCompare) }) it("fetch all will populate the relationships", async () => { From 5a3ecc3095e28dc99c1412ed1a946a5a5875ec02 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 29 Sep 2023 13:40:40 +0100 Subject: [PATCH 115/154] Getting create/update response consistent with internal API for external. --- packages/server/src/api/routes/tests/row.spec.ts | 9 ++------- packages/server/src/utilities/rowProcessor/index.ts | 8 ++++++++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index ec502f17bb..e6f70fc617 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -1787,19 +1787,14 @@ describe.each([ user: null, users: null, }) - const toCompare: any = { + expect(updatedRow).toEqual({ name: rowData.name, description: rowData.description, tableId, _id: row._id, _rev: expect.any(String), type: "row", - } - if (!isInternal) { - toCompare.user = null - toCompare.users = null - } - expect(updatedRow).toEqual(toCompare) + }) }) it("fetch all will populate the relationships", async () => { diff --git a/packages/server/src/utilities/rowProcessor/index.ts b/packages/server/src/utilities/rowProcessor/index.ts index 773b54dd6a..cf7cb0932b 100644 --- a/packages/server/src/utilities/rowProcessor/index.ts +++ b/packages/server/src/utilities/rowProcessor/index.ts @@ -250,6 +250,14 @@ export async function outputProcessing( enriched )) as Row[] } + // remove null properties to match internal API + for (let row of enriched) { + for (let key of Object.keys(row)) { + if (row[key] === null) { + delete row[key] + } + } + } return (wasArray ? enriched : enriched[0]) as T } From c498f39f2ae630aa0879a9cb8eedf45c66e18f1d Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 29 Sep 2023 13:45:02 +0100 Subject: [PATCH 116/154] Fixing type build. --- packages/server/src/api/controllers/query/index.ts | 10 +++++----- packages/types/src/sdk/koa.ts | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/server/src/api/controllers/query/index.ts b/packages/server/src/api/controllers/query/index.ts index cd52213be8..472c0d6272 100644 --- a/packages/server/src/api/controllers/query/index.ts +++ b/packages/server/src/api/controllers/query/index.ts @@ -9,7 +9,7 @@ import { quotas } from "@budibase/pro" import { events, context, utils, constants } from "@budibase/backend-core" import sdk from "../../../sdk" import { QueryEvent } from "../../../threads/definitions" -import { Query, UserCtx } from "@budibase/types" +import { ConfigType, Query, UserCtx } from "@budibase/types" import { ValidQueryNameRegex } from "@budibase/shared-core" const Runner = new Thread(ThreadType.QUERY, { @@ -107,7 +107,7 @@ export async function find(ctx: UserCtx) { //Required to discern between OIDC OAuth config entries function getOAuthConfigCookieId(ctx: UserCtx) { - if (ctx.user.providerType === constants.Config.OIDC) { + if (ctx.user.providerType === ConfigType.OIDC) { return utils.getCookie(ctx, constants.Cookie.OIDC_CONFIG) } } @@ -135,7 +135,7 @@ export async function preview(ctx: UserCtx) { const db = context.getAppDB() const existing = (await db.get(queryId)) as Query existingSchema = existing.schema - } catch (err) { + } catch (err: any) { if (err.status !== 404) { ctx.throw(500, "Unable to retrieve existing query") } @@ -210,7 +210,7 @@ export async function preview(ctx: UserCtx) { info, extra, } - } catch (err) { + } catch (err: any) { ctx.throw(400, err) } } @@ -276,7 +276,7 @@ async function execute( } else { ctx.body = { data: rows, pagination, ...extra, ...info } } - } catch (err) { + } catch (err: any) { ctx.throw(400, err) } } diff --git a/packages/types/src/sdk/koa.ts b/packages/types/src/sdk/koa.ts index 861f5e9329..a7df701171 100644 --- a/packages/types/src/sdk/koa.ts +++ b/packages/types/src/sdk/koa.ts @@ -1,5 +1,5 @@ import { Context, Request } from "koa" -import { User, Role, UserRoles, Account } from "../documents" +import { User, Role, UserRoles, Account, ConfigType } from "../documents" import { FeatureFlag, License } from "../sdk" import { Files } from "formidable" @@ -13,6 +13,7 @@ export interface ContextUser extends Omit { csrfToken?: string featureFlags?: FeatureFlag[] accountPortalAccess?: boolean + providerType?: ConfigType account?: Account } From 9d186df018034966400b3664ca75b5d946d5ed67 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 29 Sep 2023 14:58:02 +0200 Subject: [PATCH 117/154] Don't modify internal API --- packages/server/src/utilities/rowProcessor/index.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/server/src/utilities/rowProcessor/index.ts b/packages/server/src/utilities/rowProcessor/index.ts index cf7cb0932b..5dc2546a52 100644 --- a/packages/server/src/utilities/rowProcessor/index.ts +++ b/packages/server/src/utilities/rowProcessor/index.ts @@ -251,10 +251,12 @@ export async function outputProcessing( )) as Row[] } // remove null properties to match internal API - for (let row of enriched) { - for (let key of Object.keys(row)) { - if (row[key] === null) { - delete row[key] + if (table.sourceId) { + for (let row of enriched) { + for (let key of Object.keys(row)) { + if (row[key] === null) { + delete row[key] + } } } } From dfea7f253e96c4e108bbd754cd70d6c2c80fe977 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 29 Sep 2023 14:58:41 +0200 Subject: [PATCH 118/154] Fix test ds data --- 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 e6f70fc617..fa329dbb4b 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -1793,7 +1793,8 @@ describe.each([ tableId, _id: row._id, _rev: expect.any(String), - type: "row", + id: isInternal ? undefined : expect.any(Number), + type: isInternal ? "row" : undefined, }) }) From f22cce95533557c6ddc6b81037d1ec0cb84b9f29 Mon Sep 17 00:00:00 2001 From: melohagan <101575380+melohagan@users.noreply.github.com> Date: Fri, 29 Sep 2023 14:47:07 +0100 Subject: [PATCH 119/154] Saving pre filled relation picker doesnt work (#11928) * Make sure field state is valid * Skip non-object relationship links * Note --------- Co-authored-by: Adria Navarro --- .../src/components/app/forms/RelationshipField.svelte | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/client/src/components/app/forms/RelationshipField.svelte b/packages/client/src/components/app/forms/RelationshipField.svelte index bfa7c6cbd2..bb003730b3 100644 --- a/packages/client/src/components/app/forms/RelationshipField.svelte +++ b/packages/client/src/components/app/forms/RelationshipField.svelte @@ -60,6 +60,12 @@ // even if they are not in the inital fetch results initialValuesProcessed = true optionsObj = (fieldState?.value || []).reduce((accumulator, value) => { + // fieldState has to be an array of strings to be valid for an update + // therefore we cannot guarantee value will be an object + // https://linear.app/budibase/issue/BUDI-7577/refactor-the-relationshipfield-component-to-have-better-support-for + if (!value._id) { + return accumulator + } accumulator[value._id] = { _id: value._id, [primaryDisplay]: value.primaryDisplay, From ade8b8eec859f1769c7014e11cf0917e64a3fc40 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Fri, 29 Sep 2023 13:47:24 +0000 Subject: [PATCH 120/154] Bump version to 2.10.16-alpha.15 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 04ebf14448..6d08b39400 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.10.16-alpha.14", + "version": "2.10.16-alpha.15", "npmClient": "yarn", "packages": [ "packages/*" From bc6c6b9e80e53eb01684917b2476d5dbeeab4b1f Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 29 Sep 2023 16:02:45 +0200 Subject: [PATCH 121/154] Handle deleting m2m --- packages/server/src/api/controllers/row/ExternalRequest.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/server/src/api/controllers/row/ExternalRequest.ts b/packages/server/src/api/controllers/row/ExternalRequest.ts index 5f2502a1d1..2ad1afe202 100644 --- a/packages/server/src/api/controllers/row/ExternalRequest.ts +++ b/packages/server/src/api/controllers/row/ExternalRequest.ts @@ -836,10 +836,7 @@ export class ExternalRequest { // can't really use response right now const response = await getDatasourceAndQuery(json) // handle many to many relationships now if we know the ID (could be auto increment) - if ( - operation !== Operation.READ && - processed.manyRelationships?.length > 0 - ) { + if (operation !== Operation.READ) { await this.handleManyRelationships( table._id || "", response[0], From 7050c5352a7ab97b998136f89ab2d66354a168e7 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 29 Sep 2023 16:09:08 +0200 Subject: [PATCH 122/154] Check external table types --- packages/server/src/utilities/rowProcessor/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/server/src/utilities/rowProcessor/index.ts b/packages/server/src/utilities/rowProcessor/index.ts index 5dc2546a52..c1b4bcf7e5 100644 --- a/packages/server/src/utilities/rowProcessor/index.ts +++ b/packages/server/src/utilities/rowProcessor/index.ts @@ -11,6 +11,7 @@ import { processInputBBReferences, processOutputBBReferences, } from "./bbReferenceProcessor" +import { isExternalTable } from "../../integrations/utils" export * from "./utils" type AutoColumnProcessingOpts = { @@ -251,7 +252,7 @@ export async function outputProcessing( )) as Row[] } // remove null properties to match internal API - if (table.sourceId) { + if (isExternalTable(table._id!)) { for (let row of enriched) { for (let key of Object.keys(row)) { if (row[key] === null) { From 1a00b60088d8af94d535e513faf2f81b3956e14e Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 29 Sep 2023 16:39:18 +0200 Subject: [PATCH 123/154] Fix tests --- .../server/src/utilities/rowProcessor/bbReferenceProcessor.ts | 2 +- packages/server/src/utilities/rowProcessor/index.ts | 3 --- .../src/utilities/rowProcessor/tests/outputProcessing.spec.ts | 4 ++-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/server/src/utilities/rowProcessor/bbReferenceProcessor.ts b/packages/server/src/utilities/rowProcessor/bbReferenceProcessor.ts index 5409ed925c..6f41d3d55f 100644 --- a/packages/server/src/utilities/rowProcessor/bbReferenceProcessor.ts +++ b/packages/server/src/utilities/rowProcessor/bbReferenceProcessor.ts @@ -48,7 +48,7 @@ export async function processOutputBBReferences( ) { if (typeof value !== "string") { // Already processed or nothing to process - return value + return value || undefined } const ids = value.split(",").filter(id => !!id) diff --git a/packages/server/src/utilities/rowProcessor/index.ts b/packages/server/src/utilities/rowProcessor/index.ts index c1b4bcf7e5..0bdaaa393e 100644 --- a/packages/server/src/utilities/rowProcessor/index.ts +++ b/packages/server/src/utilities/rowProcessor/index.ts @@ -235,9 +235,6 @@ export async function outputProcessing( } } else if (column.type == FieldTypes.BB_REFERENCE) { for (let row of enriched) { - if (row[property] == null) { - continue - } row[property] = await processOutputBBReferences( row[property], column.subtype as FieldSubtype diff --git a/packages/server/src/utilities/rowProcessor/tests/outputProcessing.spec.ts b/packages/server/src/utilities/rowProcessor/tests/outputProcessing.spec.ts index 1b780bed54..ecb8856c88 100644 --- a/packages/server/src/utilities/rowProcessor/tests/outputProcessing.spec.ts +++ b/packages/server/src/utilities/rowProcessor/tests/outputProcessing.spec.ts @@ -66,7 +66,7 @@ describe("rowProcessor - outputProcessing", () => { ) }) - it("does not fetch bb references when fields are empty", async () => { + it("process output even when the field is not empty", async () => { const table: Table = { _id: generator.guid(), name: "TestTable", @@ -100,7 +100,7 @@ describe("rowProcessor - outputProcessing", () => { expect(result).toEqual({ name: "Jack" }) - expect(bbReferenceProcessor.processOutputBBReferences).not.toBeCalled() + expect(bbReferenceProcessor.processOutputBBReferences).toBeCalledTimes(1) }) it("does not fetch bb references when not in the schema", async () => { From 70825433ee7eee865719f42b2d2a426ced3e4225 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Fri, 29 Sep 2023 14:46:23 +0000 Subject: [PATCH 124/154] Bump version to 2.10.16-alpha.16 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 6d08b39400..025c76e378 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.10.16-alpha.15", + "version": "2.10.16-alpha.16", "npmClient": "yarn", "packages": [ "packages/*" From cddb2d9affceedb3bec03f9566059d07bcb43e91 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Fri, 29 Sep 2023 14:59:04 +0000 Subject: [PATCH 125/154] Bump version to 2.10.16-alpha.17 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 025c76e378..05c227beae 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.10.16-alpha.16", + "version": "2.10.16-alpha.17", "npmClient": "yarn", "packages": [ "packages/*" From b36d728f7a89a464a6c893c015c5078973e00a44 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Sun, 1 Oct 2023 23:45:30 +0100 Subject: [PATCH 126/154] updating pro ref --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index 3038568214..7040ae5282 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit 30385682141e5ba9d98de7d71d5be1672109cd15 +Subproject commit 7040ae5282cc23d7ae56ac1be8a369d1c32aab2f From c71c74b96ca10d18a0db3cc2c58d52ac97ee2152 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Sun, 1 Oct 2023 22:45:58 +0000 Subject: [PATCH 127/154] Bump version to 2.10.16-alpha.18 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 05c227beae..e61a16968b 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.10.16-alpha.17", + "version": "2.10.16-alpha.18", "npmClient": "yarn", "packages": [ "packages/*" From 986069d7d7b9167c3155b5b2f4bd66b56afefb69 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 2 Oct 2023 08:19:59 +0100 Subject: [PATCH 128/154] Fix issue causing client apps to initialise 3 times --- .../client/src/components/devtools/DevToolsHeader.svelte | 1 - packages/client/src/stores/devTools.js | 9 ++++++++- packages/client/src/stores/initialise.js | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/client/src/components/devtools/DevToolsHeader.svelte b/packages/client/src/components/devtools/DevToolsHeader.svelte index a15e8351a5..55b705e717 100644 --- a/packages/client/src/components/devtools/DevToolsHeader.svelte +++ b/packages/client/src/components/devtools/DevToolsHeader.svelte @@ -25,7 +25,6 @@ value: roleId, }) } - devToolsStore.actions.changeRole(SELF_ROLE) return list } diff --git a/packages/client/src/stores/devTools.js b/packages/client/src/stores/devTools.js index 32f3c8e617..db9b9e10b4 100644 --- a/packages/client/src/stores/devTools.js +++ b/packages/client/src/stores/devTools.js @@ -2,6 +2,7 @@ import { createLocalStorageStore } from "@budibase/frontend-core" import { initialise } from "./initialise" import { authStore } from "./auth" import { API } from "../api" +import { get } from "svelte/store" const initialState = { visible: false, @@ -27,9 +28,15 @@ const createDevToolStore = () => { } const changeRole = async role => { + if (role === "self") { + role = null + } + if (role === get(store).role) { + return + } store.update(state => ({ ...state, - role: role === "self" ? null : role, + role, })) API.invalidateCache() await authStore.actions.fetchUser() diff --git a/packages/client/src/stores/initialise.js b/packages/client/src/stores/initialise.js index f34d775a4e..86c761367c 100644 --- a/packages/client/src/stores/initialise.js +++ b/packages/client/src/stores/initialise.js @@ -3,6 +3,7 @@ import { appStore } from "./app" import { orgStore } from "./org" export async function initialise() { + console.log("initialise!") await routeStore.actions.fetchRoutes() await appStore.actions.fetchAppDefinition() await orgStore.actions.init() From bdd70d8bdbc83e60abf2852d9b752b696fddc6dc Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 2 Oct 2023 08:26:47 +0100 Subject: [PATCH 129/154] Fix regression when considering empty app navigation roles --- packages/client/src/components/app/Layout.svelte | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/client/src/components/app/Layout.svelte b/packages/client/src/components/app/Layout.svelte index e482e6b336..bdab0dd9ab 100644 --- a/packages/client/src/components/app/Layout.svelte +++ b/packages/client/src/components/app/Layout.svelte @@ -3,6 +3,7 @@ import { writable } from "svelte/store" import { Heading, Icon, clickOutside } from "@budibase/bbui" import { FieldTypes } from "constants" + import { Constants } from "@budibase/frontend-core" import active from "svelte-spa-router/active" const sdk = getContext("sdk") @@ -103,7 +104,8 @@ let validLinks = (allLinks || []).filter(link => link.text && link.url) // Filter to only links allowed by the current role return validLinks.filter(link => { - return userRoleHierarchy?.find(roleId => roleId === link.roleId) + const role = link.roleId || Constants.Roles.BASIC + return userRoleHierarchy?.find(roleId => roleId === role) }) } From 6b904dd01ad43dad71673b4dc96a10768d3a05f8 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 2 Oct 2023 08:27:28 +0100 Subject: [PATCH 130/154] Remove log --- packages/client/src/stores/initialise.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/client/src/stores/initialise.js b/packages/client/src/stores/initialise.js index 86c761367c..f34d775a4e 100644 --- a/packages/client/src/stores/initialise.js +++ b/packages/client/src/stores/initialise.js @@ -3,7 +3,6 @@ import { appStore } from "./app" import { orgStore } from "./org" export async function initialise() { - console.log("initialise!") await routeStore.actions.fetchRoutes() await appStore.actions.fetchAppDefinition() await orgStore.actions.init() From b9ae3d0e23543ba92c6d7805ddbec4a2faea53d7 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Mon, 2 Oct 2023 07:50:37 +0000 Subject: [PATCH 131/154] Bump version to 2.10.16-alpha.19 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index e61a16968b..84d7bc0ec1 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.10.16-alpha.18", + "version": "2.10.16-alpha.19", "npmClient": "yarn", "packages": [ "packages/*" From ea12121ddf857616dc5bb63ec15d8bc5012267e6 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Mon, 2 Oct 2023 08:13:57 +0000 Subject: [PATCH 132/154] Bump version to 2.10.16 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 84d7bc0ec1..00a9fb9c0c 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.10.16-alpha.19", + "version": "2.10.16", "npmClient": "yarn", "packages": [ "packages/*" From 634a2974bd98d506b38ab9e9872807cd2185813a Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Mon, 2 Oct 2023 08:30:36 +0000 Subject: [PATCH 133/154] Bump version to 2.11.0 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 00a9fb9c0c..7a7fb9c26e 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.10.16", + "version": "2.11.0", "npmClient": "yarn", "packages": [ "packages/*" From 7003475cafb6e49939e67a99b763d1cec5421601 Mon Sep 17 00:00:00 2001 From: melohagan <101575380+melohagan@users.noreply.github.com> Date: Mon, 2 Oct 2023 11:42:04 +0100 Subject: [PATCH 134/154] Refactor FieldSelector to not use BindableSlot (#11942) --- .../automation/SetupPanel/RowSelector.svelte | 31 +++- .../SetupPanel/RowSelectorTypes.svelte | 152 ++++++++---------- 2 files changed, 92 insertions(+), 91 deletions(-) diff --git a/packages/builder/src/components/automation/SetupPanel/RowSelector.svelte b/packages/builder/src/components/automation/SetupPanel/RowSelector.svelte index 8c50689f22..c3097f3072 100644 --- a/packages/builder/src/components/automation/SetupPanel/RowSelector.svelte +++ b/packages/builder/src/components/automation/SetupPanel/RowSelector.svelte @@ -3,6 +3,8 @@ import { Select, Checkbox } from "@budibase/bbui" import { createEventDispatcher } from "svelte" import RowSelectorTypes from "./RowSelectorTypes.svelte" + import DrawerBindableSlot from "../../common/bindings/DrawerBindableSlot.svelte" + import AutomationBindingPanel from "../../common/bindings/ServerBindingPanel.svelte" const dispatch = createEventDispatcher() @@ -108,14 +110,29 @@
{#each schemaFields as [field, schema]} {#if !schema.autocolumn && schema.type !== "attachment"} - + value={value[field]} + on:change={e => onChange(e, field)} + {bindings} + allowJS={true} + updateOnChange={false} + drawerLeft="260px" + > + + {/if} {#if isUpdateRow && schema.type === "link"}
diff --git a/packages/builder/src/components/automation/SetupPanel/RowSelectorTypes.svelte b/packages/builder/src/components/automation/SetupPanel/RowSelectorTypes.svelte index 9111df0adc..8cf1ecf6de 100644 --- a/packages/builder/src/components/automation/SetupPanel/RowSelectorTypes.svelte +++ b/packages/builder/src/components/automation/SetupPanel/RowSelectorTypes.svelte @@ -8,7 +8,6 @@ } from "@budibase/bbui" import LinkedRowSelector from "components/common/LinkedRowSelector.svelte" import DrawerBindableInput from "../../common/bindings/DrawerBindableInput.svelte" - import DrawerBindableSlot from "../../common/bindings/DrawerBindableSlot.svelte" import ModalBindableInput from "../../common/bindings/ModalBindableInput.svelte" import AutomationBindingPanel from "../../common/bindings/ServerBindingPanel.svelte" import Editor from "components/integration/QueryEditor.svelte" @@ -31,88 +30,73 @@ } - onChange(e, field)} - {bindings} - allowJS={true} - updateOnChange={false} - drawerLeft="260px" -> - {#if schemaHasOptions(schema) && schema.type !== "array"} - onChange(e, field)} + label={field} + value={value[field]} + options={schema.constraints.inclusion} + /> +{:else if schema.type === "datetime"} + onChange(e, field)} + /> +{:else if schema.type === "boolean"} +