diff --git a/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte b/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte index 88e034a96b..37abd7f1eb 100644 --- a/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte +++ b/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte @@ -236,13 +236,13 @@ } if (!role) { - await groups.actions.removeApp(target._id, prodAppId) + await groups.removeApp(target._id, prodAppId) } else { - await groups.actions.addApp(target._id, prodAppId, role) + await groups.addApp(target._id, prodAppId, role) } await usersFetch.refresh() - await groups.actions.init() + await groups.init() } const onUpdateGroup = async (group, role) => { @@ -268,7 +268,7 @@ if (!group.roles) { return false } - return groups.actions.getGroupAppIds(group).includes(appId) + return groups.getGroupAppIds(group).includes(appId) }) } @@ -299,7 +299,7 @@ role: group?.builder?.apps.includes(prodAppId) ? Constants.Roles.CREATOR : group.roles?.[ - groups.actions.getGroupAppIds(group).find(x => x === prodAppId) + groups.getGroupAppIds(group).find(x => x === prodAppId) ], } } @@ -485,12 +485,12 @@ } const removeGroupAppBuilder = async groupId => { - await groups.actions.removeGroupAppBuilder(groupId, prodAppId) + await groups.removeGroupAppBuilder(groupId, prodAppId) } const initSidePanel = async sidePaneOpen => { if (sidePaneOpen === true) { - await groups.actions.init() + await groups.init() } loaded = true } diff --git a/packages/builder/src/pages/builder/apps/index.svelte b/packages/builder/src/pages/builder/apps/index.svelte index 8bf96d0240..e106d0dd68 100644 --- a/packages/builder/src/pages/builder/apps/index.svelte +++ b/packages/builder/src/pages/builder/apps/index.svelte @@ -53,7 +53,7 @@ } if (!Object.keys(user?.roles).length && user?.userGroups) { return userGroups.find(group => { - return groups.actions + return groups .getGroupAppIds(group) .map(role => appsStore.extractAppId(role)) .includes(app.appId) @@ -86,7 +86,7 @@ try { await organisation.init() await appsStore.load() - await groups.actions.init() + await groups.init() } catch (error) { notifications.error("Error loading apps") } diff --git a/packages/builder/src/pages/builder/portal/apps/_layout.svelte b/packages/builder/src/pages/builder/portal/apps/_layout.svelte index 560c1394fb..0a3b02f30f 100644 --- a/packages/builder/src/pages/builder/portal/apps/_layout.svelte +++ b/packages/builder/src/pages/builder/portal/apps/_layout.svelte @@ -24,7 +24,7 @@ promises.push(templates.load()) } - promises.push(groups.actions.init()) + promises.push(groups.init()) // Always load latest await Promise.all(promises) diff --git a/packages/builder/src/pages/builder/portal/users/groups/[groupId].svelte b/packages/builder/src/pages/builder/portal/users/groups/[groupId].svelte index 312d87f873..58fd1d93cb 100644 --- a/packages/builder/src/pages/builder/portal/users/groups/[groupId].svelte +++ b/packages/builder/src/pages/builder/portal/users/groups/[groupId].svelte @@ -53,9 +53,7 @@ $: readonly = !isAdmin || isScimGroup $: groupApps = $appsStore.apps .filter(app => - groups.actions - .getGroupAppIds(group) - .includes(appsStore.getProdAppID(app.devId)) + groups.getGroupAppIds(group).includes(appsStore.getProdAppID(app.devId)) ) .map(app => ({ ...app, @@ -72,7 +70,7 @@ async function deleteGroup() { try { - await groups.actions.delete(group) + await groups.delete(group) notifications.success("User group deleted successfully") $goto("./") } catch (error) { @@ -82,7 +80,7 @@ async function saveGroup(group) { try { - await groups.actions.save(group) + await groups.save(group) } catch (error) { if (error.message) { notifications.error(error.message) @@ -93,7 +91,7 @@ } const removeApp = async app => { - await groups.actions.removeApp(groupId, appsStore.getProdAppID(app.devId)) + await groups.removeApp(groupId, appsStore.getProdAppID(app.devId)) } setContext("roles", { updateRole: () => {}, @@ -102,7 +100,7 @@ onMount(async () => { try { - await Promise.all([groups.actions.init(), roles.fetch()]) + await Promise.all([groups.init(), roles.fetch()]) loaded = true } catch (error) { notifications.error("Error fetching user group data") diff --git a/packages/builder/src/pages/builder/portal/users/groups/_components/AppAddModal.svelte b/packages/builder/src/pages/builder/portal/users/groups/_components/AppAddModal.svelte index 75600c6fc0..88b8b4657b 100644 --- a/packages/builder/src/pages/builder/portal/users/groups/_components/AppAddModal.svelte +++ b/packages/builder/src/pages/builder/portal/users/groups/_components/AppAddModal.svelte @@ -23,7 +23,7 @@ return keepOpen } else { - await groups.actions.addApp(group._id, prodAppId, selectedRoleId) + await groups.addApp(group._id, prodAppId, selectedRoleId) } } diff --git a/packages/builder/src/pages/builder/portal/users/groups/_components/EditUserPicker.svelte b/packages/builder/src/pages/builder/portal/users/groups/_components/EditUserPicker.svelte index 1e7e15d1b4..d360de3850 100644 --- a/packages/builder/src/pages/builder/portal/users/groups/_components/EditUserPicker.svelte +++ b/packages/builder/src/pages/builder/portal/users/groups/_components/EditUserPicker.svelte @@ -50,11 +50,11 @@ selected={group.users?.map(user => user._id)} list={$users.data} on:select={async e => { - await groups.actions.addUser(groupId, e.detail) + await groups.addUser(groupId, e.detail) onUsersUpdated() }} on:deselect={async e => { - await groups.actions.removeUser(groupId, e.detail) + await groups.removeUser(groupId, e.detail) onUsersUpdated() }} /> diff --git a/packages/builder/src/pages/builder/portal/users/groups/index.svelte b/packages/builder/src/pages/builder/portal/users/groups/index.svelte index 77b0dc5734..9982f85352 100644 --- a/packages/builder/src/pages/builder/portal/users/groups/index.svelte +++ b/packages/builder/src/pages/builder/portal/users/groups/index.svelte @@ -60,7 +60,7 @@ async function saveGroup(group) { try { - group = await groups.actions.save(group) + group = await groups.save(group) $goto(`./${group._id}`) notifications.success(`User group created successfully`) } catch (error) { @@ -83,7 +83,7 @@ try { // always load latest await licensing.init() - await groups.actions.init() + await groups.init() } catch (error) { notifications.error("Error getting user groups") } diff --git a/packages/builder/src/pages/builder/portal/users/users/[userId].svelte b/packages/builder/src/pages/builder/portal/users/users/[userId].svelte index 94fe3081c3..57ecc9d39f 100644 --- a/packages/builder/src/pages/builder/portal/users/users/[userId].svelte +++ b/packages/builder/src/pages/builder/portal/users/users/[userId].svelte @@ -219,12 +219,12 @@ } const addGroup = async groupId => { - await groups.actions.addUser(groupId, userId) + await groups.addUser(groupId, userId) await fetchUser() } const removeGroup = async groupId => { - await groups.actions.removeUser(groupId, userId) + await groups.removeUser(groupId, userId) await fetchUser() } @@ -234,7 +234,7 @@ onMount(async () => { try { - await Promise.all([fetchUser(), groups.actions.init(), roles.fetch()]) + await Promise.all([fetchUser(), groups.init(), roles.fetch()]) loaded = true } catch (error) { notifications.error("Error getting user groups") diff --git a/packages/builder/src/pages/builder/portal/users/users/index.svelte b/packages/builder/src/pages/builder/portal/users/users/index.svelte index 80772ccbee..97120c55d4 100644 --- a/packages/builder/src/pages/builder/portal/users/users/index.svelte +++ b/packages/builder/src/pages/builder/portal/users/users/index.svelte @@ -247,7 +247,7 @@ try { bulkSaveResponse = await users.create(await removingDuplicities(userData)) notifications.success("Successfully created user") - await groups.actions.init() + await groups.init() passwordModal.show() await fetch.refresh() } catch (error) { @@ -317,7 +317,7 @@ onMount(async () => { try { - await groups.actions.init() + await groups.init() groupsLoaded = true } catch (error) { notifications.error("Error fetching user group data") diff --git a/packages/builder/src/stores/portal/groups.js b/packages/builder/src/stores/portal/groups.js deleted file mode 100644 index 408fb4189a..0000000000 --- a/packages/builder/src/stores/portal/groups.js +++ /dev/null @@ -1,103 +0,0 @@ -import { writable, get } from "svelte/store" -import { API } from "@/api" -import { licensing } from "@/stores/portal" - -export function createGroupsStore() { - const store = writable([]) - - const updateStore = group => { - store.update(state => { - const currentIdx = state.findIndex(gr => gr._id === group._id) - if (currentIdx >= 0) { - state.splice(currentIdx, 1, group) - } else { - state.push(group) - } - return state - }) - } - - const getGroup = async groupId => { - const group = await API.getGroup(groupId) - updateStore(group) - } - - const actions = { - init: async () => { - // only init if there is a groups license, just to be sure but the feature will be blocked - // on the backend anyway - if (get(licensing).groupsEnabled) { - const groups = await API.getGroups() - store.set(groups.data) - } - }, - - get: getGroup, - - save: async group => { - const { ...dataToSave } = group - delete dataToSave.scimInfo - delete dataToSave.userGroups - const response = await API.saveGroup(dataToSave) - group._id = response._id - group._rev = response._rev - updateStore(group) - return group - }, - - delete: async group => { - await API.deleteGroup(group._id, group._rev) - store.update(state => { - state = state.filter(state => state._id !== group._id) - return state - }) - }, - - addUser: async (groupId, userId) => { - await API.addUsersToGroup(groupId, userId) - // refresh the group enrichment - await getGroup(groupId) - }, - - removeUser: async (groupId, userId) => { - await API.removeUsersFromGroup(groupId, userId) - // refresh the group enrichment - await getGroup(groupId) - }, - - addApp: async (groupId, appId, roleId) => { - await API.addAppsToGroup(groupId, [{ appId, roleId }]) - // refresh the group roles - await getGroup(groupId) - }, - - removeApp: async (groupId, appId) => { - await API.removeAppsFromGroup(groupId, [{ appId }]) - // refresh the group roles - await getGroup(groupId) - }, - - getGroupAppIds: group => { - let groupAppIds = Object.keys(group?.roles || {}) - if (group?.builder?.apps) { - groupAppIds = groupAppIds.concat(group.builder.apps) - } - return groupAppIds - }, - - addGroupAppBuilder: async (groupId, appId) => { - return await API.addGroupAppBuilder(groupId, appId) - }, - - removeGroupAppBuilder: async (groupId, appId) => { - return await API.removeGroupAppBuilder(groupId, appId) - }, - } - - return { - subscribe: store.subscribe, - actions, - } -} - -export const groups = createGroupsStore() diff --git a/packages/builder/src/stores/portal/groups.ts b/packages/builder/src/stores/portal/groups.ts new file mode 100644 index 0000000000..028f300d2c --- /dev/null +++ b/packages/builder/src/stores/portal/groups.ts @@ -0,0 +1,96 @@ +import { get } from "svelte/store" +import { API } from "@/api" +import { licensing } from "@/stores/portal" +import { UserGroup } from "@budibase/types" +import { BudiStore } from "../BudiStore" + +class GroupStore extends BudiStore { + constructor() { + super([]) + } + + updateStore = (group: UserGroup) => { + this.update(state => { + const currentIdx = state.findIndex(gr => gr._id === group._id) + if (currentIdx >= 0) { + state.splice(currentIdx, 1, group) + } else { + state.push(group) + } + return state + }) + } + + async init() { + // Only init if there is a groups license, just to be sure but the feature will be blocked + // on the backend anyway + if (get(licensing).groupsEnabled) { + const groups = await API.getGroups() + this.set(groups) + } + } + + private async refreshGroup(groupId: string) { + const group = await API.getGroup(groupId) + this.updateStore(group) + } + + async save(group: UserGroup) { + const { ...dataToSave } = group + delete dataToSave.scimInfo + const response = await API.saveGroup(dataToSave) + group._id = response._id + group._rev = response._rev + this.updateStore(group) + return group + } + + async delete(group: UserGroup) { + await API.deleteGroup(group._id!, group._rev!) + this.update(groups => { + const index = groups.findIndex(g => g._id === group._id) + if (index !== -1) { + groups.splice(index, 1) + } + return groups + }) + } + + async addUser(groupId: string, userId: string) { + await API.addUsersToGroup(groupId, [userId]) + await this.refreshGroup(groupId) + } + + async removeUser(groupId: string, userId: string) { + await API.removeUsersFromGroup(groupId, [userId]) + await this.refreshGroup(groupId) + } + + async addApp(groupId: string, appId: string, roleId: string) { + await API.addAppsToGroup(groupId, [{ appId, roleId }]) + await this.refreshGroup(groupId) + } + + async removeApp(groupId: string, appId: string) { + await API.removeAppsFromGroup(groupId, [{ appId }]) + await this.refreshGroup(groupId) + } + + getGroupAppIds(group: UserGroup) { + let groupAppIds = Object.keys(group?.roles || {}) + if (group?.builder?.apps) { + groupAppIds = groupAppIds.concat(group.builder.apps) + } + return groupAppIds + } + + async addGroupAppBuilder(groupId: string, appId: string) { + return await API.addGroupAppBuilder(groupId, appId) + } + + async removeGroupAppBuilder(groupId: string, appId: string) { + return await API.removeGroupAppBuilder(groupId, appId) + } +} + +export const groups = new GroupStore() diff --git a/packages/frontend-core/src/api/groups.ts b/packages/frontend-core/src/api/groups.ts index c09c5284ec..e6374f257c 100644 --- a/packages/frontend-core/src/api/groups.ts +++ b/packages/frontend-core/src/api/groups.ts @@ -1,4 +1,8 @@ -import { SearchUserGroupResponse, UserGroup } from "@budibase/types" +import { + SearchGroupResponse, + SearchUserGroupResponse, + UserGroup, +} from "@budibase/types" import { BaseAPIClient } from "./types" export interface GroupEndpoints { @@ -64,9 +68,10 @@ export const buildGroupsEndpoints = (API: BaseAPIClient): GroupEndpoints => { * Gets all the user groups */ getGroups: async () => { - return await API.get({ + const res = await API.get({ url: "/api/global/groups", }) + return res.data }, /**