From 03f7fb37ed5381ca59b51e45d1570c906c69694e Mon Sep 17 00:00:00 2001 From: jvcalderon Date: Thu, 18 Jan 2024 11:14:25 +0100 Subject: [PATCH 01/26] Calculate creators count when group role changes --- packages/account-portal | 2 +- packages/backend-core/src/index.ts | 1 + packages/backend-core/src/users/db.ts | 15 +++-- .../backend-core/src/users/test/utils.spec.ts | 67 +++++++++++++++++++ packages/backend-core/src/users/users.ts | 3 +- packages/backend-core/src/users/utils.ts | 35 +++++++++- packages/pro | 2 +- packages/server/src/middleware/authorized.ts | 2 +- .../shared-core/src/sdk/documents/users.ts | 2 +- 9 files changed, 117 insertions(+), 12 deletions(-) create mode 100644 packages/backend-core/src/users/test/utils.spec.ts diff --git a/packages/account-portal b/packages/account-portal index 1bc0128714..b23fb3b179 160000 --- a/packages/account-portal +++ b/packages/account-portal @@ -1 +1 @@ -Subproject commit 1bc012871496ff55e376931b620075b565e34d09 +Subproject commit b23fb3b17961fb04badd9487913a683fcf26dbe6 diff --git a/packages/backend-core/src/index.ts b/packages/backend-core/src/index.ts index 7bf26f3688..8946e37486 100644 --- a/packages/backend-core/src/index.ts +++ b/packages/backend-core/src/index.ts @@ -2,6 +2,7 @@ export * as configs from "./configs" export * as events from "./events" export * as migrations from "./migrations" export * as users from "./users" +export * as usersUtils from "./users/utils" export * as roles from "./security/roles" export * as permissions from "./security/permissions" export * as accounts from "./accounts" diff --git a/packages/backend-core/src/users/db.ts b/packages/backend-core/src/users/db.ts index 4d0d216603..136cb4b8ad 100644 --- a/packages/backend-core/src/users/db.ts +++ b/packages/backend-core/src/users/db.ts @@ -251,7 +251,8 @@ export class UserDB { } const change = dbUser ? 0 : 1 // no change if there is existing user - const creatorsChange = isCreator(dbUser) !== isCreator(user) ? 1 : 0 + const creatorsChange = + (await isCreator(dbUser)) !== (await isCreator(user)) ? 1 : 0 return UserDB.quotas.addUsers(change, creatorsChange, async () => { await validateUniqueUser(email, tenantId) @@ -335,7 +336,7 @@ export class UserDB { } newUser.userGroups = groups || [] newUsers.push(newUser) - if (isCreator(newUser)) { + if (await isCreator(newUser)) { newCreators.push(newUser) } } @@ -432,12 +433,16 @@ export class UserDB { _deleted: true, })) const dbResponse = await usersCore.bulkUpdateGlobalUsers(toDelete) - const creatorsToDelete = usersToDelete.filter(isCreator) + + const creatorsEval = await Promise.all(usersToDelete.map(isCreator)) + const creatorsToDeleteCount = creatorsEval.filter( + creator => !!creator + ).length for (let user of usersToDelete) { await bulkDeleteProcessing(user) } - await UserDB.quotas.removeUsers(toDelete.length, creatorsToDelete.length) + await UserDB.quotas.removeUsers(toDelete.length, creatorsToDeleteCount) // Build Response // index users by id @@ -486,7 +491,7 @@ export class UserDB { await db.remove(userId, dbUser._rev) - const creatorsToDelete = isCreator(dbUser) ? 1 : 0 + const creatorsToDelete = (await isCreator(dbUser)) ? 1 : 0 await UserDB.quotas.removeUsers(1, creatorsToDelete) await eventHelpers.handleDeleteEvents(dbUser) await cache.user.invalidateUser(userId) diff --git a/packages/backend-core/src/users/test/utils.spec.ts b/packages/backend-core/src/users/test/utils.spec.ts new file mode 100644 index 0000000000..0fe27f57a6 --- /dev/null +++ b/packages/backend-core/src/users/test/utils.spec.ts @@ -0,0 +1,67 @@ +import { User, UserGroup } from "@budibase/types" +import { generator, structures } from "../../../tests" +import { DBTestConfiguration } from "../../../tests/extra" +import { getGlobalDB } from "../../context" +import { isCreator } from "../utils" + +const config = new DBTestConfiguration() + +describe("Users", () => { + it("User is a creator if it is configured as a global builder", async () => { + const user: User = structures.users.user({ builder: { global: true } }) + expect(await isCreator(user)).toBe(true) + }) + + it("User is a creator if it is configured as a global admin", async () => { + const user: User = structures.users.user({ admin: { global: true } }) + expect(await isCreator(user)).toBe(true) + }) + + it("User is a creator if it is configured with creator permission", async () => { + const user: User = structures.users.user({ builder: { creator: true } }) + expect(await isCreator(user)).toBe(true) + }) + + it("User is a creator if it is a builder in some application", async () => { + const user: User = structures.users.user({ builder: { apps: ["app1"] } }) + expect(await isCreator(user)).toBe(true) + }) + + it("User is a creator if it has CREATOR permission in some application", async () => { + const user: User = structures.users.user({ roles: { app1: "CREATOR" } }) + expect(await isCreator(user)).toBe(true) + }) + + it("User is a creator if it has ADMIN permission in some application", async () => { + const user: User = structures.users.user({ roles: { app1: "ADMIN" } }) + expect(await isCreator(user)).toBe(true) + }) + + it("User is a creator if it remains to a group with ADMIN permissions", async () => { + const usersInGroup = 10 + const groupId = "gr_17abffe89e0b40268e755b952f101a59" + const group: UserGroup = { + ...structures.userGroups.userGroup(), + ...{ _id: groupId, roles: { app1: "ADMIN" } }, + } + const users: User[] = [] + for (const _ of Array.from({ length: usersInGroup })) { + const userId = `us_${generator.guid()}` + const user: User = structures.users.user({ + _id: userId, + userGroups: [groupId], + }) + users.push(user) + } + + await config.doInTenant(async () => { + const db = getGlobalDB() + await db.put(group) + for (let user of users) { + await db.put(user) + const creator = await isCreator(user) + expect(creator).toBe(true) + } + }) + }) +}) diff --git a/packages/backend-core/src/users/users.ts b/packages/backend-core/src/users/users.ts index cc2b4fc27f..638da4a5b1 100644 --- a/packages/backend-core/src/users/users.ts +++ b/packages/backend-core/src/users/users.ts @@ -309,7 +309,8 @@ export async function getCreatorCount() { let creators = 0 async function iterate(startPage?: string) { const page = await paginatedUsers({ bookmark: startPage }) - creators += page.data.filter(isCreator).length + const creatorsEval = await Promise.all(page.data.map(isCreator)) + creators += creatorsEval.filter(creator => !!creator).length if (page.hasNextPage) { await iterate(page.nextPage) } diff --git a/packages/backend-core/src/users/utils.ts b/packages/backend-core/src/users/utils.ts index 0ef4b77998..348ad1532f 100644 --- a/packages/backend-core/src/users/utils.ts +++ b/packages/backend-core/src/users/utils.ts @@ -1,4 +1,4 @@ -import { CloudAccount } from "@budibase/types" +import { CloudAccount, ContextUser, User, UserGroup } from "@budibase/types" import * as accountSdk from "../accounts" import env from "../environment" import { getPlatformUser } from "./lookup" @@ -6,17 +6,48 @@ import { EmailUnavailableError } from "../errors" import { getTenantId } from "../context" import { sdk } from "@budibase/shared-core" import { getAccountByTenantId } from "../accounts" +import { BUILTIN_ROLE_IDS } from "../security/roles" +import * as context from "../context" // extract from shared-core to make easily accessible from backend-core export const isBuilder = sdk.users.isBuilder export const isAdmin = sdk.users.isAdmin -export const isCreator = sdk.users.isCreator export const isGlobalBuilder = sdk.users.isGlobalBuilder export const isAdminOrBuilder = sdk.users.isAdminOrBuilder export const hasAdminPermissions = sdk.users.hasAdminPermissions export const hasBuilderPermissions = sdk.users.hasBuilderPermissions export const hasAppBuilderPermissions = sdk.users.hasAppBuilderPermissions +export async function isCreator(user?: User | ContextUser) { + const isCreatorByUserDefinition = sdk.users.isCreator(user) + if (!isCreatorByUserDefinition && user) { + return await isCreatorByGroupMembership(user) + } + return isCreatorByUserDefinition +} + +async function isCreatorByGroupMembership(user?: User | ContextUser) { + const userGroups = user?.userGroups || [] + if (userGroups.length > 0) { + const db = context.getGlobalDB() + const groups: UserGroup[] = [] + for (let groupId of userGroups) { + try { + const group = await db.get(groupId) + groups.push(group) + } catch (e: any) { + if (e.error !== "not_found") { + throw e + } + } + } + return groups.some(group => + Object.values(group.roles || {}).includes(BUILTIN_ROLE_IDS.ADMIN) + ) + } + return false +} + export async function validateUniqueUser(email: string, tenantId: string) { // check budibase users in other tenants if (env.MULTI_TENANCY) { diff --git a/packages/pro b/packages/pro index 9d80daaa5b..8c466d6ef2 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit 9d80daaa5b79da68730d6c5f497f629c47a78ef8 +Subproject commit 8c466d6ef2a0c09b843ef63276793ab5af2e96f7 diff --git a/packages/server/src/middleware/authorized.ts b/packages/server/src/middleware/authorized.ts index cba765a887..fe7f6bb6fe 100644 --- a/packages/server/src/middleware/authorized.ts +++ b/packages/server/src/middleware/authorized.ts @@ -34,7 +34,7 @@ const checkAuthorized = async ( const isCreatorApi = permType === PermissionType.CREATOR const isBuilderApi = permType === PermissionType.BUILDER const isGlobalBuilder = users.isGlobalBuilder(ctx.user) - const isCreator = users.isCreator(ctx.user) + const isCreator = await users.isCreator(ctx.user) const isBuilder = appId ? users.isBuilder(ctx.user, appId) : users.hasBuilderPermissions(ctx.user) diff --git a/packages/shared-core/src/sdk/documents/users.ts b/packages/shared-core/src/sdk/documents/users.ts index 1aaf44ff7c..11e80dcf29 100644 --- a/packages/shared-core/src/sdk/documents/users.ts +++ b/packages/shared-core/src/sdk/documents/users.ts @@ -70,7 +70,7 @@ export function hasAppCreatorPermissions(user?: User | ContextUser): boolean { return _.flow( _.get("roles"), _.values, - _.find(x => x === "CREATOR"), + _.find(x => ["CREATOR", "ADMIN"].includes(x)), x => !!x )(user) } From ce0b1f8df3d9d11d3cf8c25daa1944aa257c5ded Mon Sep 17 00:00:00 2001 From: jvcalderon Date: Thu, 18 Jan 2024 11:22:16 +0100 Subject: [PATCH 02/26] Account portal submodule hash to master --- packages/account-portal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/account-portal b/packages/account-portal index b23fb3b179..1bc0128714 160000 --- a/packages/account-portal +++ b/packages/account-portal @@ -1 +1 @@ -Subproject commit b23fb3b17961fb04badd9487913a683fcf26dbe6 +Subproject commit 1bc012871496ff55e376931b620075b565e34d09 From 64d3114c9f62f2b8f4c7f1382cce49ece836efd9 Mon Sep 17 00:00:00 2001 From: jvcalderon Date: Thu, 18 Jan 2024 12:03:26 +0100 Subject: [PATCH 03/26] Refactor: usersUtils -> userUtils --- packages/backend-core/src/index.ts | 2 +- packages/pro | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend-core/src/index.ts b/packages/backend-core/src/index.ts index 8946e37486..8001017092 100644 --- a/packages/backend-core/src/index.ts +++ b/packages/backend-core/src/index.ts @@ -2,7 +2,7 @@ export * as configs from "./configs" export * as events from "./events" export * as migrations from "./migrations" export * as users from "./users" -export * as usersUtils from "./users/utils" +export * as userUtils from "./users/utils" export * as roles from "./security/roles" export * as permissions from "./security/permissions" export * as accounts from "./accounts" diff --git a/packages/pro b/packages/pro index 8c466d6ef2..1857cbbd07 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit 8c466d6ef2a0c09b843ef63276793ab5af2e96f7 +Subproject commit 1857cbbd071a855a56ed70877e55d5566c7f29e1 From 9befe6aa9d19098ccbb5314a1fa70d0f92ce8cc3 Mon Sep 17 00:00:00 2001 From: jvcalderon Date: Thu, 18 Jan 2024 12:32:19 +0100 Subject: [PATCH 04/26] Update pro submodule --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index 1857cbbd07..3d815d2629 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit 1857cbbd071a855a56ed70877e55d5566c7f29e1 +Subproject commit 3d815d262939c53092e5aeb0b033d6b481bb5583 From e47b19ef366b2df72ef5e0c72c82cbbe89dfd862 Mon Sep 17 00:00:00 2001 From: jvcalderon Date: Thu, 18 Jan 2024 12:36:06 +0100 Subject: [PATCH 05/26] Update pro submodule --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index 3d815d2629..e8511333e0 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit 3d815d262939c53092e5aeb0b033d6b481bb5583 +Subproject commit e8511333e0ef84f89a4d5eb041534438744dc2fd From ede73efd81e03f49ee74c3df02541f6924769843 Mon Sep 17 00:00:00 2001 From: jvcalderon Date: Thu, 18 Jan 2024 12:59:36 +0100 Subject: [PATCH 06/26] Upgrade pro submodule --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index e8511333e0..35032a6aea 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit e8511333e0ef84f89a4d5eb041534438744dc2fd +Subproject commit 35032a6aeab96437c64b7892ca0ca585e11a6605 From a825aa191f44ac437de07cb4ff688b0a9d0e583c Mon Sep 17 00:00:00 2001 From: jvcalderon Date: Thu, 18 Jan 2024 16:37:25 +0100 Subject: [PATCH 07/26] Upgrade pro submodule --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index 35032a6aea..b91d3933e4 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit 35032a6aeab96437c64b7892ca0ca585e11a6605 +Subproject commit b91d3933e453fb86b1ea66c8d92d44420fb6d2e7 From afcd9dc114806dc3aea006979c5fb3dd6582353f Mon Sep 17 00:00:00 2001 From: jvcalderon Date: Thu, 18 Jan 2024 18:10:14 +0100 Subject: [PATCH 08/26] Upgrade pro submodule --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index b91d3933e4..e131cce3d3 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit b91d3933e453fb86b1ea66c8d92d44420fb6d2e7 +Subproject commit e131cce3d36e33632d5af1e8d5a83b4fd9e8fe00 From 6fca47f3030bbfbe338c14c83eaf79ed0f436dc3 Mon Sep 17 00:00:00 2001 From: jvcalderon Date: Fri, 19 Jan 2024 10:26:54 +0100 Subject: [PATCH 09/26] Update pro submodule --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index e131cce3d3..26d25b6fe4 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit e131cce3d36e33632d5af1e8d5a83b4fd9e8fe00 +Subproject commit 26d25b6fe4e426abc924c03184138a2d836bb91e From c83a5219f6d7b21d0f0e12fa86f508115654d950 Mon Sep 17 00:00:00 2001 From: jvcalderon Date: Fri, 19 Jan 2024 13:12:17 +0100 Subject: [PATCH 10/26] Update pro submodule --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index 26d25b6fe4..1d187da286 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit 26d25b6fe4e426abc924c03184138a2d836bb91e +Subproject commit 1d187da286d2006d26d47ed6212c6026b500cf66 From af9824c2ae95851d0efd891d5fa9942048f4a2e0 Mon Sep 17 00:00:00 2001 From: Mel O'Hagan Date: Mon, 22 Jan 2024 12:35:23 +0000 Subject: [PATCH 11/26] Revert old picker styling from 2.10 --- packages/bbui/src/Actions/position_dropdown.js | 3 +-- packages/bbui/src/Form/Core/Multiselect.svelte | 4 ---- packages/bbui/src/Form/Core/Picker.svelte | 4 ---- packages/bbui/src/Form/Core/Select.svelte | 4 ---- packages/bbui/src/Popover/Popover.svelte | 2 -- 5 files changed, 1 insertion(+), 16 deletions(-) diff --git a/packages/bbui/src/Actions/position_dropdown.js b/packages/bbui/src/Actions/position_dropdown.js index f2018272f6..cc169eac09 100644 --- a/packages/bbui/src/Actions/position_dropdown.js +++ b/packages/bbui/src/Actions/position_dropdown.js @@ -18,7 +18,6 @@ export default function positionDropdown(element, opts) { useAnchorWidth, offset = 5, customUpdate, - offsetBelow, } = opts if (!anchor) { return @@ -48,7 +47,7 @@ export default function positionDropdown(element, opts) { styles.top = anchorBounds.top - elementBounds.height - offset styles.maxHeight = maxHeight || 240 } else { - styles.top = anchorBounds.bottom + (offsetBelow || offset) + styles.top = anchorBounds.bottom + offset styles.maxHeight = maxHeight || window.innerHeight - anchorBounds.bottom - 20 } diff --git a/packages/bbui/src/Form/Core/Multiselect.svelte b/packages/bbui/src/Form/Core/Multiselect.svelte index d5d6515d2d..2243570cd5 100644 --- a/packages/bbui/src/Form/Core/Multiselect.svelte +++ b/packages/bbui/src/Form/Core/Multiselect.svelte @@ -15,8 +15,6 @@ export let autoWidth = false export let searchTerm = null export let customPopoverHeight - export let customPopoverOffsetBelow - export let customPopoverMaxHeight export let open = false export let loading @@ -98,7 +96,5 @@ {sort} {autoWidth} {customPopoverHeight} - {customPopoverOffsetBelow} - {customPopoverMaxHeight} {loading} /> diff --git a/packages/bbui/src/Form/Core/Picker.svelte b/packages/bbui/src/Form/Core/Picker.svelte index 94fbe73cf2..cfb1654403 100644 --- a/packages/bbui/src/Form/Core/Picker.svelte +++ b/packages/bbui/src/Form/Core/Picker.svelte @@ -37,8 +37,6 @@ export let sort = false export let searchTerm = null export let customPopoverHeight - export let customPopoverOffsetBelow - export let customPopoverMaxHeight export let align = "left" export let footer = null export let customAnchor = null @@ -156,9 +154,7 @@ on:close={() => (open = false)} useAnchorWidth={!autoWidth} maxWidth={autoWidth ? 400 : null} - maxHeight={customPopoverMaxHeight} customHeight={customPopoverHeight} - offsetBelow={customPopoverOffsetBelow} >
option === value} diff --git a/packages/bbui/src/Popover/Popover.svelte b/packages/bbui/src/Popover/Popover.svelte index a68430e973..5066e3aa05 100644 --- a/packages/bbui/src/Popover/Popover.svelte +++ b/packages/bbui/src/Popover/Popover.svelte @@ -18,7 +18,6 @@ export let useAnchorWidth = false export let dismissible = true export let offset = 5 - export let offsetBelow export let customHeight export let animate = true export let customZindex @@ -89,7 +88,6 @@ maxWidth, useAnchorWidth, offset, - offsetBelow, customUpdate: handlePostionUpdate, }} use:clickOutside={{ From a11a99cd09ca2e27dd2e557f1cf922289e1443b8 Mon Sep 17 00:00:00 2001 From: Mel O'Hagan Date: Mon, 22 Jan 2024 12:45:31 +0000 Subject: [PATCH 12/26] Remove unused customPopoverMaxHeight prop --- .../client/src/components/app/forms/RelationshipField.svelte | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/client/src/components/app/forms/RelationshipField.svelte b/packages/client/src/components/app/forms/RelationshipField.svelte index af48183c20..6782bcfca6 100644 --- a/packages/client/src/components/app/forms/RelationshipField.svelte +++ b/packages/client/src/components/app/forms/RelationshipField.svelte @@ -236,7 +236,6 @@ bind:searchTerm loading={$fetch.loading} bind:open - customPopoverMaxHeight={400} /> {/if} From 09b75c3924d653f3add28906ebaef1d8b827f30e Mon Sep 17 00:00:00 2001 From: jvcalderon Date: Tue, 23 Jan 2024 13:21:54 +0100 Subject: [PATCH 13/26] Recalculate creators count on plan downgrade --- packages/pro | 2 +- packages/worker/src/api/controllers/global/self.ts | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index 1d187da286..38e7b34a70 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit 1d187da286d2006d26d47ed6212c6026b500cf66 +Subproject commit 38e7b34a708bcaf6abc677be54b9d74bd8d18ea5 diff --git a/packages/worker/src/api/controllers/global/self.ts b/packages/worker/src/api/controllers/global/self.ts index d14aecd1a7..2d7e83a2c3 100644 --- a/packages/worker/src/api/controllers/global/self.ts +++ b/packages/worker/src/api/controllers/global/self.ts @@ -91,6 +91,9 @@ export async function getSelf(ctx: any) { id: userId, } + // Adjust creators quotas (prevents wrong creators count if user has changed the plan) + await groups.adjustGroupCreatorsQuotas() + // get the main body of the user const user = await userSdk.db.getUser(userId) ctx.body = await groups.enrichUserRolesFromGroups(user) From 54903920df657067bdfa159ed166a150455d7b39 Mon Sep 17 00:00:00 2001 From: jvcalderon Date: Wed, 24 Jan 2024 08:59:31 +0100 Subject: [PATCH 14/26] Update pro submodule --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index 38e7b34a70..8b30020974 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit 38e7b34a708bcaf6abc677be54b9d74bd8d18ea5 +Subproject commit 8b30020974609cdfeb4ebd68e77fc5f8af38c0fb From 37cf41e19199b1792bc36bcd9da40e420500b1be Mon Sep 17 00:00:00 2001 From: jvcalderon Date: Wed, 24 Jan 2024 09:06:27 +0100 Subject: [PATCH 15/26] Update pro submodule --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index 8b30020974..d6227b9214 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit 8b30020974609cdfeb4ebd68e77fc5f8af38c0fb +Subproject commit d6227b9214dcc92604c09560ac90f498ddb9726a From 118e1bf5a7f712c20f6e379e9090e4d23626bc7e Mon Sep 17 00:00:00 2001 From: jvcalderon Date: Wed, 24 Jan 2024 09:09:06 +0100 Subject: [PATCH 16/26] Update account-portal submodule --- packages/account-portal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/account-portal b/packages/account-portal index 1bc0128714..05c90ce551 160000 --- a/packages/account-portal +++ b/packages/account-portal @@ -1 +1 @@ -Subproject commit 1bc012871496ff55e376931b620075b565e34d09 +Subproject commit 05c90ce55144e260da6688335c16783eab79bf96 From d4387471d42a797238f1e25c2016f50c150359da Mon Sep 17 00:00:00 2001 From: jvcalderon Date: Wed, 24 Jan 2024 09:35:58 +0100 Subject: [PATCH 17/26] Update pro submodule --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index d6227b9214..1ed918cc5a 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit d6227b9214dcc92604c09560ac90f498ddb9726a +Subproject commit 1ed918cc5a4c90de37d7521102c710066e462514 From d57db8c32d93b9906b920b298e7be9c383362173 Mon Sep 17 00:00:00 2001 From: jvcalderon Date: Wed, 24 Jan 2024 10:13:01 +0100 Subject: [PATCH 18/26] Update pro submodule --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index 1ed918cc5a..0e4c5f95bd 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit 1ed918cc5a4c90de37d7521102c710066e462514 +Subproject commit 0e4c5f95bda6af126a5cddeef01b8cf551f236be From 6f9075d44cfd99977f95ada70512daf3e3dae27c Mon Sep 17 00:00:00 2001 From: jvcalderon Date: Wed, 24 Jan 2024 12:27:02 +0100 Subject: [PATCH 19/26] Solve a failing test --- packages/server/src/sdk/users/tests/utils.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/src/sdk/users/tests/utils.spec.ts b/packages/server/src/sdk/users/tests/utils.spec.ts index f7c9413ebd..86dc411caf 100644 --- a/packages/server/src/sdk/users/tests/utils.spec.ts +++ b/packages/server/src/sdk/users/tests/utils.spec.ts @@ -84,7 +84,7 @@ describe("syncGlobalUsers", () => { await syncGlobalUsers() const metadata = await rawUserMetadata() - expect(metadata).toHaveLength(3) + expect(metadata).toHaveLength(2) expect(metadata).toContainEqual( expect.objectContaining({ _id: db.generateUserMetadataID(user1._id!), @@ -121,7 +121,7 @@ describe("syncGlobalUsers", () => { await syncGlobalUsers() const metadata = await rawUserMetadata() - expect(metadata).toHaveLength(0) + expect(metadata).toHaveLength(1) //ADMIN user created in test bootstrap still in the application }) }) }) From 361fea2a6b4e05958724d20dd574db12e618bd38 Mon Sep 17 00:00:00 2001 From: Dean Date: Wed, 24 Jan 2024 12:06:38 +0000 Subject: [PATCH 20/26] Broke out the hover tracking into its own store. Value check on the button group to mitigate excessive rendering. Fix for relationship field --- packages/builder/src/builderStore/index.js | 2 ++ .../src/builderStore/store/frontend.js | 15 ----------- .../builder/src/builderStore/store/hover.js | 27 +++++++++++++++++++ .../ButtonConfiguration.svelte | 8 +++++- .../[screenId]/_components/AppPreview.svelte | 4 +-- .../ComponentList/ComponentTree.svelte | 5 ++-- .../_components/ComponentList/index.svelte | 13 ++++++--- .../app/forms/RelationshipField.svelte | 5 +++- 8 files changed, 54 insertions(+), 25 deletions(-) create mode 100644 packages/builder/src/builderStore/store/hover.js diff --git a/packages/builder/src/builderStore/index.js b/packages/builder/src/builderStore/index.js index dd54dcf13e..b58d196024 100644 --- a/packages/builder/src/builderStore/index.js +++ b/packages/builder/src/builderStore/index.js @@ -9,6 +9,7 @@ import { findComponent, findComponentPath } from "./componentUtils" import { RoleUtils } from "@budibase/frontend-core" import { createHistoryStore } from "builderStore/store/history" import { cloneDeep } from "lodash/fp" +import { getHoverStore } from "./store/hover" export const store = getFrontendStore() export const automationStore = getAutomationStore() @@ -16,6 +17,7 @@ export const themeStore = getThemeStore() export const temporalStore = getTemporalStore() export const userStore = getUserStore() export const deploymentStore = getDeploymentStore() +export const hoverStore = getHoverStore() // Setup history for screens export const screenHistoryStore = createHistoryStore({ diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js index b05b127b1c..ff7c0d74b8 100644 --- a/packages/builder/src/builderStore/store/frontend.js +++ b/packages/builder/src/builderStore/store/frontend.js @@ -92,9 +92,6 @@ const INITIAL_FRONTEND_STATE = { // Onboarding onboarding: false, tourNodes: null, - - // UI state - hoveredComponentId: null, } export const getFrontendStore = () => { @@ -1415,18 +1412,6 @@ export const getFrontendStore = () => { return state }) }, - hover: (componentId, notifyClient = true) => { - if (componentId === get(store).hoveredComponentId) { - return - } - store.update(state => { - state.hoveredComponentId = componentId - return state - }) - if (notifyClient) { - store.actions.preview.sendEvent("hover-component", componentId) - } - }, }, links: { save: async (url, title) => { diff --git a/packages/builder/src/builderStore/store/hover.js b/packages/builder/src/builderStore/store/hover.js new file mode 100644 index 0000000000..5db9272975 --- /dev/null +++ b/packages/builder/src/builderStore/store/hover.js @@ -0,0 +1,27 @@ +import { get, writable } from "svelte/store" +import { store as builder } from "builderStore" + +export const getHoverStore = () => { + const initialValue = { + componentId: null, + } + + const store = writable(initialValue) + + const update = (componentId, notifyClient = true) => { + if (componentId === get(store).componentId) { + return + } + store.update(state => { + state.componentId = componentId + return state + }) + if (notifyClient) { + builder.actions.preview.sendEvent("hover-component", componentId) + } + } + return { + subscribe: store.subscribe, + actions: { update }, + } +} diff --git a/packages/builder/src/components/design/settings/controls/ButtonConfiguration/ButtonConfiguration.svelte b/packages/builder/src/components/design/settings/controls/ButtonConfiguration/ButtonConfiguration.svelte index 63bfecf386..fade2db761 100644 --- a/packages/builder/src/components/design/settings/controls/ButtonConfiguration/ButtonConfiguration.svelte +++ b/packages/builder/src/components/design/settings/controls/ButtonConfiguration/ButtonConfiguration.svelte @@ -5,6 +5,7 @@ import { store } from "builderStore" import { Helpers } from "@budibase/bbui" import { getEventContextBindings } from "builderStore/dataBinding" + import { cloneDeep, isEqual } from "lodash/fp" export let componentInstance export let componentBindings @@ -17,8 +18,13 @@ const dispatch = createEventDispatcher() let focusItem + let cachedValue - $: buttonList = sanitizeValue(value) || [] + $: if (!isEqual(value, cachedValue)) { + cachedValue = cloneDeep(value) + } + + $: buttonList = sanitizeValue(cachedValue) || [] $: buttonCount = buttonList.length $: eventContextBindings = getEventContextBindings({ componentInstance, diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPreview.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPreview.svelte index 2127392bb9..011980bbe2 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPreview.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/AppPreview.svelte @@ -1,7 +1,7 @@
    @@ -111,7 +112,7 @@ on:dragover={dragover(component, index)} on:iconClick={() => toggleNodeOpen(component._id)} on:drop={onDrop} - hovering={$store.hoveredComponentId === component._id} + hovering={$hoverStore.componentId === component._id} on:mouseenter={() => hover(component._id)} on:mouseleave={() => hover(null)} text={getComponentText(component)} diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ComponentList/index.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ComponentList/index.svelte index d2ffc5de74..13f2e73853 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ComponentList/index.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/_components/ComponentList/index.svelte @@ -1,7 +1,12 @@
    @@ -60,7 +65,7 @@ icon="WebPage" on:drop={onDrop} on:click={() => ($store.selectedComponentId = screenComponentId)} - hovering={$store.hoveredComponentId === screenComponentId} + hovering={$hoverStore.componentId === screenComponentId} on:mouseenter={() => hover(screenComponentId)} on:mouseleave={() => hover(null)} id="component-screen" @@ -79,7 +84,7 @@ : "VisibilityOff"} on:drop={onDrop} on:click={() => ($store.selectedComponentId = navComponentId)} - hovering={$store.hoveredComponentId === navComponentId} + hovering={$hoverStore.componentId === navComponentId} on:mouseenter={() => hover(navComponentId)} on:mouseleave={() => hover(null)} id="component-nav" diff --git a/packages/client/src/components/app/forms/RelationshipField.svelte b/packages/client/src/components/app/forms/RelationshipField.svelte index af48183c20..b2380bb845 100644 --- a/packages/client/src/components/app/forms/RelationshipField.svelte +++ b/packages/client/src/components/app/forms/RelationshipField.svelte @@ -108,10 +108,13 @@ } } - $: forceFetchRows(filter) + $: forceFetchRows(filter, fieldApi) $: debouncedFetchRows(searchTerm, primaryDisplay, defaultValue) const forceFetchRows = async () => { + if (!fieldApi) { + return + } // if the filter has changed, then we need to reset the options, clear the selection, and re-fetch optionsObj = {} fieldApi.setValue([]) From 381e98a2583cf8fe20c5348d00717abd319fd7c7 Mon Sep 17 00:00:00 2001 From: Mel O'Hagan Date: Wed, 24 Jan 2024 12:50:58 +0000 Subject: [PATCH 21/26] update pro submodule --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index ce7722ed44..0e4c5f95bd 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit ce7722ed4474718596b465dcfd49bef36cab2e42 +Subproject commit 0e4c5f95bda6af126a5cddeef01b8cf551f236be From 5c23443a08b8dab0eb237ed27920b70e44dc1309 Mon Sep 17 00:00:00 2001 From: Dean Date: Wed, 24 Jan 2024 16:15:45 +0000 Subject: [PATCH 22/26] Update pro --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index ce7722ed44..0e4c5f95bd 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit ce7722ed4474718596b465dcfd49bef36cab2e42 +Subproject commit 0e4c5f95bda6af126a5cddeef01b8cf551f236be From b4be6daea6a5fac4e16a7ce2436638ae903ba942 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 25 Jan 2024 08:46:59 +0000 Subject: [PATCH 23/26] Quick fix for all usages of compare function in select. --- packages/bbui/src/Form/Core/Select.svelte | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/bbui/src/Form/Core/Select.svelte b/packages/bbui/src/Form/Core/Select.svelte index aaca8b640d..3ee37be915 100644 --- a/packages/bbui/src/Form/Core/Select.svelte +++ b/packages/bbui/src/Form/Core/Select.svelte @@ -35,15 +35,20 @@ $: fieldIcon = getFieldAttribute(getOptionIcon, value, options) $: fieldColour = getFieldAttribute(getOptionColour, value, options) + function compareOptionAndValue(option, value) { + return typeof compare === "function" + ? compare(option, value) + : option === value + } + const getFieldAttribute = (getAttribute, value, options) => { // Wait for options to load if there is a value but no options if (!options?.length) { return "" } - const index = options.findIndex((option, idx) => { - const opt = getOptionValue(option, idx) - return typeof compare === "function" ? compare(opt, value) : opt === value - }) + const index = options.findIndex((option, idx) => + compareOptionAndValue(getOptionValue(option, idx), value) + ) return index !== -1 ? getAttribute(options[index], index) : null } @@ -96,7 +101,7 @@ {customPopoverMaxHeight} isPlaceholder={value == null || value === ""} placeholderOption={placeholder === false ? null : placeholder} - isOptionSelected={option => compare(option, value)} + isOptionSelected={option => compareOptionAndValue(option, value)} onSelectOption={selectOption} {loading} /> From 830563feadc3cd1063eccaba6a8aed863957d2e9 Mon Sep 17 00:00:00 2001 From: melohagan <101575380+melohagan@users.noreply.github.com> Date: Thu, 25 Jan 2024 12:08:00 +0000 Subject: [PATCH 24/26] Feature/auto confirm barcode scanner (#12859) * Add autoConfirm scan * Don't auto confirm if validation fails * Set status light to red if invalid --- packages/client/manifest.json | 6 ++++++ .../components/app/forms/CodeScanner.svelte | 18 ++++++++++++++++-- .../app/forms/CodeScannerField.svelte | 3 +++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/packages/client/manifest.json b/packages/client/manifest.json index 2a9bc6f258..83a5b858db 100644 --- a/packages/client/manifest.json +++ b/packages/client/manifest.json @@ -3969,6 +3969,12 @@ "key": "allowManualEntry", "defaultValue": false }, + { + "type": "boolean", + "label": "Auto confirm", + "key": "autoConfirm", + "defaultValue": false + }, { "type": "boolean", "label": "Play sound on scan", diff --git a/packages/client/src/components/app/forms/CodeScanner.svelte b/packages/client/src/components/app/forms/CodeScanner.svelte index ff860d216f..2a546eb64c 100644 --- a/packages/client/src/components/app/forms/CodeScanner.svelte +++ b/packages/client/src/components/app/forms/CodeScanner.svelte @@ -14,11 +14,13 @@ export let value export let disabled = false export let allowManualEntry = false + export let autoConfirm = false export let scanButtonText = "Scan code" export let beepOnScan = false export let beepFrequency = 2637 export let customFrequency = 1046 export let preferredCamera = "environment" + export let validator const dispatch = createEventDispatcher() @@ -41,6 +43,9 @@ beep() } dispatch("change", decodedText) + if (autoConfirm && !validator?.(decodedText)) { + camModal?.hide() + } } } @@ -127,7 +132,11 @@
    {#if value && !manualMode}
    - + {#if validator?.(value)} + + {:else} + + {/if} {value}
    {/if} @@ -183,11 +192,16 @@
    {#if cameraEnabled === true}
    - {#if value} + {#if value && !validator?.(value)}
    {value}
    + {:else if value && validator?.(value)} +
    + + {value} +
    {:else}
    diff --git a/packages/client/src/components/app/forms/CodeScannerField.svelte b/packages/client/src/components/app/forms/CodeScannerField.svelte index be590106c2..7c9948554a 100644 --- a/packages/client/src/components/app/forms/CodeScannerField.svelte +++ b/packages/client/src/components/app/forms/CodeScannerField.svelte @@ -11,6 +11,7 @@ export let defaultValue = "" export let onChange export let allowManualEntry + export let autoConfirm export let scanButtonText export let beepOnScan export let beepFrequency @@ -49,11 +50,13 @@ on:change={handleUpdate} disabled={fieldState.disabled || fieldState.readonly} {allowManualEntry} + {autoConfirm} scanButtonText={scanText} {beepOnScan} {beepFrequency} {customFrequency} {preferredCamera} + validator={fieldState.validator} /> {/if} From 827508816e12ea48ab923375905aee8da4e89ae9 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Thu, 25 Jan 2024 12:14:55 +0000 Subject: [PATCH 25/26] Fix for relationship save button being enabled when table was missing --- .../backend/Datasources/CreateEditRelationship.svelte | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/builder/src/components/backend/Datasources/CreateEditRelationship.svelte b/packages/builder/src/components/backend/Datasources/CreateEditRelationship.svelte index c837247986..f9b688210a 100644 --- a/packages/builder/src/components/backend/Datasources/CreateEditRelationship.svelte +++ b/packages/builder/src/components/backend/Datasources/CreateEditRelationship.svelte @@ -88,8 +88,12 @@ hasValidated = false }) } + $: valid = - getErrorCount(errors) === 0 && allRequiredAttributesSet(relationshipType) + getErrorCount(errors) === 0 && + allRequiredAttributesSet(relationshipType) && + fromId && + toId $: isManyToMany = relationshipType === RelationshipType.MANY_TO_MANY $: isManyToOne = relationshipType === RelationshipType.MANY_TO_ONE || From 94defe86483dbb018cdf39c47ab97725df446cf9 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Thu, 25 Jan 2024 13:54:25 +0000 Subject: [PATCH 26/26] Bump version to 2.15.6 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 587a678788..4049d5d734 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.15.5", + "version": "2.15.6", "npmClient": "yarn", "packages": [ "packages/*",