Add new UnsavedUser type and update controllers

This commit is contained in:
Andrew Kingston 2025-01-08 15:54:09 +00:00
parent 52916f11a8
commit 6bd4cb47c2
No known key found for this signature in database
4 changed files with 38 additions and 31 deletions

View File

@ -9,9 +9,18 @@ import {
SearchUsersResponse, SearchUsersResponse,
UpdateInviteRequest, UpdateInviteRequest,
User, User,
UserIdentifier,
UnsavedUser,
} from "@budibase/types" } from "@budibase/types"
import { BudiStore } from "../BudiStore" import { BudiStore } from "../BudiStore"
interface UserInfo {
email: string
password: string
forceResetPassword?: boolean
role: keyof typeof Constants.BudibaseRoles
}
type UserState = SearchUsersResponse & SearchUsersRequest type UserState = SearchUsersResponse & SearchUsersRequest
class UserStore extends BudiStore<UserState> { class UserStore extends BudiStore<UserState> {
@ -116,9 +125,9 @@ class UserStore extends BudiStore<UserState> {
return await API.getUserCountByApp(appId) return await API.getUserCountByApp(appId)
} }
async create(data: any) { async create(data: { users: UserInfo[]; groups: any[] }) {
let mappedUsers: Omit<User, "tenantId">[] = data.users.map((user: any) => { let mappedUsers: UnsavedUser[] = data.users.map((user: any) => {
const body: Omit<User, "tenantId"> = { const body: UnsavedUser = {
email: user.email, email: user.email,
password: user.password, password: user.password,
roles: {}, roles: {},
@ -128,17 +137,17 @@ class UserStore extends BudiStore<UserState> {
} }
switch (user.role) { switch (user.role) {
case "appUser": case Constants.BudibaseRoles.AppUser:
body.builder = { global: false } body.builder = { global: false }
body.admin = { global: false } body.admin = { global: false }
break break
case "developer": case Constants.BudibaseRoles.Developer:
body.builder = { global: true } body.builder = { global: true }
break break
case "creator": case Constants.BudibaseRoles.Creator:
body.builder = { creator: true, global: false } body.builder = { creator: true, global: false }
break break
case "admin": case Constants.BudibaseRoles.Admin:
body.admin = { global: true } body.admin = { global: true }
body.builder = { global: true } body.builder = { global: true }
break break
@ -157,12 +166,7 @@ class UserStore extends BudiStore<UserState> {
await API.deleteUser(id) await API.deleteUser(id)
} }
async bulkDelete( async bulkDelete(users: UserIdentifier[]) {
users: Array<{
userId: string
email: string
}>
) {
return API.deleteUsers(users) return API.deleteUsers(users)
} }
@ -199,9 +203,8 @@ class UserStore extends BudiStore<UserState> {
} }
} }
foo = this.refreshUsage(this.create) // Wrapper function to refresh quota usage after an operation,
bar = this.refreshUsage(this.save) // persisting argument and return types
refreshUsage<T extends any[], U>(fn: (...args: T) => Promise<U>) { refreshUsage<T extends any[], U>(fn: (...args: T) => Promise<U>) {
return async function (...args: T) { return async function (...args: T) {
const response = await fn(...args) const response = await fn(...args)

View File

@ -21,11 +21,12 @@ import {
SaveUserResponse, SaveUserResponse,
SearchUsersRequest, SearchUsersRequest,
SearchUsersResponse, SearchUsersResponse,
UnsavedUser,
UpdateInviteRequest, UpdateInviteRequest,
UpdateInviteResponse, UpdateInviteResponse,
UpdateSelfMetadataRequest, UpdateSelfMetadataRequest,
UpdateSelfMetadataResponse, UpdateSelfMetadataResponse,
User, UserIdentifier,
} from "@budibase/types" } from "@budibase/types"
import { BaseAPIClient } from "./types" import { BaseAPIClient } from "./types"
@ -38,14 +39,9 @@ export interface UserEndpoints {
createAdminUser: ( createAdminUser: (
user: CreateAdminUserRequest user: CreateAdminUserRequest
) => Promise<CreateAdminUserResponse> ) => Promise<CreateAdminUserResponse>
saveUser: (user: User) => Promise<SaveUserResponse> saveUser: (user: UnsavedUser) => Promise<SaveUserResponse>
deleteUser: (userId: string) => Promise<DeleteUserResponse> deleteUser: (userId: string) => Promise<DeleteUserResponse>
deleteUsers: ( deleteUsers: (users: UserIdentifier[]) => Promise<BulkUserDeleted | undefined>
users: Array<{
userId: string
email: string
}>
) => Promise<BulkUserDeleted | undefined>
onboardUsers: (data: InviteUsersRequest) => Promise<InviteUsersResponse> onboardUsers: (data: InviteUsersRequest) => Promise<InviteUsersResponse>
getUserInvite: (code: string) => Promise<CheckInviteResponse> getUserInvite: (code: string) => Promise<CheckInviteResponse>
getUserInvites: () => Promise<GetUserInvitesResponse> getUserInvites: () => Promise<GetUserInvitesResponse>
@ -60,7 +56,7 @@ export interface UserEndpoints {
getAccountHolder: () => Promise<LookupAccountHolderResponse> getAccountHolder: () => Promise<LookupAccountHolderResponse>
searchUsers: (data: SearchUsersRequest) => Promise<SearchUsersResponse> searchUsers: (data: SearchUsersRequest) => Promise<SearchUsersResponse>
createUsers: ( createUsers: (
users: Omit<User, "tenantId">[], users: UnsavedUser[],
groups: any[] groups: any[]
) => Promise<BulkUserCreated | undefined> ) => Promise<BulkUserCreated | undefined>
updateUserInvite: ( updateUserInvite: (

View File

@ -22,6 +22,8 @@ export interface UserDetails {
password?: string password?: string
} }
export type UnsavedUser = Omit<User, "tenantId">
export interface BulkUserRequest { export interface BulkUserRequest {
delete?: { delete?: {
users: Array<{ users: Array<{
@ -31,7 +33,7 @@ export interface BulkUserRequest {
} }
create?: { create?: {
roles?: any[] roles?: any[]
users: User[] users: UnsavedUser[]
groups: any[] groups: any[]
} }
} }

View File

@ -33,6 +33,7 @@ import {
SaveUserResponse, SaveUserResponse,
SearchUsersRequest, SearchUsersRequest,
SearchUsersResponse, SearchUsersResponse,
UnsavedUser,
UpdateInviteRequest, UpdateInviteRequest,
UpdateInviteResponse, UpdateInviteResponse,
User, User,
@ -49,6 +50,7 @@ import {
tenancy, tenancy,
db, db,
locks, locks,
context,
} from "@budibase/backend-core" } from "@budibase/backend-core"
import { checkAnyUserExists } from "../../../utilities/users" import { checkAnyUserExists } from "../../../utilities/users"
import { isEmailConfigured } from "../../../utilities/email" import { isEmailConfigured } from "../../../utilities/email"
@ -66,10 +68,11 @@ const generatePassword = (length: number) => {
.slice(0, length) .slice(0, length)
} }
export const save = async (ctx: UserCtx<User, SaveUserResponse>) => { export const save = async (ctx: UserCtx<UnsavedUser, SaveUserResponse>) => {
try { try {
const currentUserId = ctx.user?._id const currentUserId = ctx.user?._id
const requestUser = ctx.request.body const tenantId = context.getTenantId()
const requestUser: User = { ...ctx.request.body, tenantId }
// Do not allow the account holder role to be changed // Do not allow the account holder role to be changed
const accountMetadata = await users.getExistingAccounts([requestUser.email]) const accountMetadata = await users.getExistingAccounts([requestUser.email])
@ -149,7 +152,12 @@ export const bulkUpdate = async (
let created, deleted let created, deleted
try { try {
if (input.create) { if (input.create) {
created = await bulkCreate(input.create.users, input.create.groups) const tenantId = context.getTenantId()
const users: User[] = input.create.users.map(user => ({
...user,
tenantId,
}))
created = await bulkCreate(users, input.create.groups)
} }
if (input.delete) { if (input.delete) {
deleted = await bulkDelete(input.delete.users, currentUserId) deleted = await bulkDelete(input.delete.users, currentUserId)
@ -441,7 +449,6 @@ export const checkInvite = async (ctx: UserCtx<void, CheckInviteResponse>) => {
} catch (e) { } catch (e) {
console.warn("Error getting invite from code", e) console.warn("Error getting invite from code", e)
ctx.throw(400, "There was a problem with the invite") ctx.throw(400, "There was a problem with the invite")
return
} }
ctx.body = { ctx.body = {
email: invite.email, email: invite.email,
@ -472,7 +479,6 @@ export const updateInvite = async (
invite = await cache.invite.getCode(code) invite = await cache.invite.getCode(code)
} catch (e) { } catch (e) {
ctx.throw(400, "There was a problem with the invite") ctx.throw(400, "There was a problem with the invite")
return
} }
let updated = { let updated = {