Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert portal user store to TS #15327

Merged
merged 13 commits into from
Jan 13, 2025
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -442,21 +442,19 @@

const onUpdateUserInvite = async (invite, role) => {
let updateBody = {
code: invite.code,
apps: {
...invite.apps,
[prodAppId]: role,
},
}

if (role === Constants.Roles.CREATOR) {
updateBody.builder = updateBody.builder || {}
updateBody.builder.apps = [...(updateBody.builder.apps ?? []), prodAppId]
delete updateBody?.apps?.[prodAppId]
} else if (role !== Constants.Roles.CREATOR && invite?.builder?.apps) {
invite.builder.apps = []
}
await users.updateInvite(updateBody)
await users.updateInvite(invite.code, updateBody)
await filterInvites(query)
}

Expand All @@ -470,8 +468,7 @@
let updated = { ...invite }
delete updated.info.apps[prodAppId]

return await users.updateInvite({
code: updated.code,
return await users.updateInvite(updated.code, {
apps: updated.apps,
})
}
Expand Down
10 changes: 8 additions & 2 deletions packages/builder/src/pages/builder/portal/apps/index.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,14 @@
? "View errors"
: "View error"}
on:dismiss={async () => {
await automationStore.actions.clearLogErrors({ appId })
await appsStore.load()
const automationId = Object.keys(automationErrors[appId] || {})[0]
if (automationId) {
await automationStore.actions.clearLogErrors({
appId,
automationId,
})
await appsStore.load()
}
}}
message={automationErrorMessage(appId)}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
]

const removeUser = async id => {
await groups.actions.removeUser(groupId, id)
await groups.removeUser(groupId, id)
fetchGroupUsers.refresh()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@
passwordModal.show()
await fetch.refresh()
} catch (error) {
console.error(error)
notifications.error("Error creating user")
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,41 +1,71 @@
import { writable } from "svelte/store"
import { API } from "@/api"
import { update } from "lodash"
import { licensing } from "."
import { sdk } from "@budibase/shared-core"
import { Constants } from "@budibase/frontend-core"
import {
DeleteInviteUsersRequest,
InviteUsersRequest,
SearchUsersRequest,
SearchUsersResponse,
UpdateInviteRequest,
User,
UserIdentifier,
UnsavedUser,
} from "@budibase/types"
import { BudiStore } from "../BudiStore"

interface UserInfo {
email: string
password: string
forceResetPassword?: boolean
role: keyof typeof Constants.BudibaseRoles
}

export function createUsersStore() {
const { subscribe, set } = writable({})
type UserState = SearchUsersResponse & SearchUsersRequest

// opts can contain page and search params
async function search(opts = {}) {
class UserStore extends BudiStore<UserState> {
constructor() {
super({
data: [],
})
}

async search(opts: SearchUsersRequest = {}) {
const paged = await API.searchUsers(opts)
set({
this.set({
...paged,
...opts,
})
return paged
}

async function get(userId) {
async get(userId: string) {
try {
return await API.getUser(userId)
} catch (err) {
return null
}
}
const fetch = async () => {

async fetch() {
return await API.getUsers()
}

// One or more users.
async function onboard(payload) {
async onboard(payload: InviteUsersRequest) {
return await API.onboardUsers(payload)
}

async function invite(payload) {
const users = payload.map(user => {
async invite(
payload: {
admin?: boolean
builder?: boolean
creator?: boolean
email: string
apps?: any[]
groups?: any[]
}[]
) {
const users: InviteUsersRequest = payload.map(user => {
let builder = undefined
if (user.admin || user.builder) {
builder = { global: true }
Expand All @@ -55,11 +85,16 @@ export function createUsersStore() {
return API.inviteUsers(users)
}

async function removeInvites(payload) {
async removeInvites(payload: DeleteInviteUsersRequest) {
return API.removeUserInvites(payload)
}

async function acceptInvite(inviteCode, password, firstName, lastName) {
async acceptInvite(
inviteCode: string,
password: string,
firstName: string,
lastName?: string
) {
return API.acceptInvite({
inviteCode,
password,
Expand All @@ -68,21 +103,25 @@ export function createUsersStore() {
})
}

async function fetchInvite(inviteCode) {
async fetchInvite(inviteCode: string) {
return API.getUserInvite(inviteCode)
}

async function getInvites() {
async getInvites() {
return API.getUserInvites()
}

async function updateInvite(invite) {
return API.updateUserInvite(invite.code, invite)
async updateInvite(code: string, invite: UpdateInviteRequest) {
return API.updateUserInvite(code, invite)
}

async function create(data) {
let mappedUsers = data.users.map(user => {
const body = {
async getUserCountByApp(appId: string) {
return await API.getUserCountByApp(appId)
}

async create(data: { users: UserInfo[]; groups: any[] }) {
let mappedUsers: UnsavedUser[] = data.users.map((user: any) => {
const body: UnsavedUser = {
email: user.email,
password: user.password,
roles: {},
Expand All @@ -92,17 +131,17 @@ export function createUsersStore() {
}

switch (user.role) {
case "appUser":
case Constants.BudibaseRoles.AppUser:
body.builder = { global: false }
body.admin = { global: false }
break
case "developer":
case Constants.BudibaseRoles.Developer:
body.builder = { global: true }
break
case "creator":
case Constants.BudibaseRoles.Creator:
body.builder = { creator: true, global: false }
break
case "admin":
case Constants.BudibaseRoles.Admin:
body.admin = { global: true }
body.builder = { global: true }
break
Expand All @@ -111,43 +150,47 @@ export function createUsersStore() {
return body
})
const response = await API.createUsers(mappedUsers, data.groups)
licensing.setQuotaUsage()

// re-search from first page
await search()
await this.search()
return response
}

async function del(id) {
async delete(id: string) {
await API.deleteUser(id)
update(users => users.filter(user => user._id !== id))
licensing.setQuotaUsage()
}

async function getUserCountByApp(appId) {
return await API.getUserCountByApp(appId)
async bulkDelete(users: UserIdentifier[]) {
const res = API.deleteUsers(users)
licensing.setQuotaUsage()
return res
}

async function bulkDelete(users) {
return API.deleteUsers(users)
async save(user: User) {
const res = await API.saveUser(user)
licensing.setQuotaUsage()
return res
}

async function save(user) {
return await API.saveUser(user)
}

async function addAppBuilder(userId, appId) {
async addAppBuilder(userId: string, appId: string) {
return await API.addAppBuilder(userId, appId)
}

async function removeAppBuilder(userId, appId) {
async removeAppBuilder(userId: string, appId: string) {
return await API.removeAppBuilder(userId, appId)
}

async function getAccountHolder() {
async getAccountHolder() {
return await API.getAccountHolder()
}

const getUserRole = user => {
if (user && user.email === user.tenantOwnerEmail) {
getUserRole(user?: User & { tenantOwnerEmail?: string }) {
if (!user) {
return Constants.BudibaseRoles.AppUser
}
if (user.email === user.tenantOwnerEmail) {
return Constants.BudibaseRoles.Owner
} else if (sdk.users.isAdmin(user)) {
return Constants.BudibaseRoles.Admin
Expand All @@ -159,38 +202,6 @@ export function createUsersStore() {
return Constants.BudibaseRoles.AppUser
}
}

const refreshUsage =
fn =>
async (...args) => {
const response = await fn(...args)
await licensing.setQuotaUsage()
return response
}

return {
subscribe,
search,
get,
getUserRole,
fetch,
invite,
onboard,
fetchInvite,
getInvites,
removeInvites,
updateInvite,
getUserCountByApp,
addAppBuilder,
removeAppBuilder,
// any operation that adds or deletes users
acceptInvite,
create: refreshUsage(create),
save: refreshUsage(save),
bulkDelete: refreshUsage(bulkDelete),
delete: refreshUsage(del),
getAccountHolder,
}
}

export const users = createUsersStore()
export const users = new UserStore()
14 changes: 5 additions & 9 deletions packages/frontend-core/src/api/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@ import {
SaveUserResponse,
SearchUsersRequest,
SearchUsersResponse,
UnsavedUser,
UpdateInviteRequest,
UpdateInviteResponse,
UpdateSelfMetadataRequest,
UpdateSelfMetadataResponse,
User,
UserIdentifier,
} from "@budibase/types"
import { BaseAPIClient } from "./types"

Expand All @@ -38,14 +39,9 @@ export interface UserEndpoints {
createAdminUser: (
user: CreateAdminUserRequest
) => Promise<CreateAdminUserResponse>
saveUser: (user: User) => Promise<SaveUserResponse>
saveUser: (user: UnsavedUser) => Promise<SaveUserResponse>
deleteUser: (userId: string) => Promise<DeleteUserResponse>
deleteUsers: (
users: Array<{
userId: string
email: string
}>
) => Promise<BulkUserDeleted | undefined>
deleteUsers: (users: UserIdentifier[]) => Promise<BulkUserDeleted | undefined>
onboardUsers: (data: InviteUsersRequest) => Promise<InviteUsersResponse>
getUserInvite: (code: string) => Promise<CheckInviteResponse>
getUserInvites: () => Promise<GetUserInvitesResponse>
Expand All @@ -60,7 +56,7 @@ export interface UserEndpoints {
getAccountHolder: () => Promise<LookupAccountHolderResponse>
searchUsers: (data: SearchUsersRequest) => Promise<SearchUsersResponse>
createUsers: (
users: User[],
users: UnsavedUser[],
groups: any[]
) => Promise<BulkUserCreated | undefined>
updateUserInvite: (
Expand Down
6 changes: 4 additions & 2 deletions packages/types/src/api/web/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export interface UserDetails {
password?: string
}

export type UnsavedUser = Omit<User, "tenantId">

export interface BulkUserRequest {
delete?: {
users: Array<{
Expand All @@ -31,7 +33,7 @@ export interface BulkUserRequest {
}
create?: {
roles?: any[]
users: User[]
users: UnsavedUser[]
groups: any[]
}
}
Expand Down Expand Up @@ -124,7 +126,7 @@ export interface AcceptUserInviteRequest {
inviteCode: string
password: string
firstName: string
lastName: string
lastName?: string
}

export interface AcceptUserInviteResponse {
Expand Down
Loading
Loading