diff --git a/packages/account-portal b/packages/account-portal index 8cd052ce82..607e3db866 160000 --- a/packages/account-portal +++ b/packages/account-portal @@ -1 +1 @@ -Subproject commit 8cd052ce8288f343812a514d06c5a9459b3ba1a8 +Subproject commit 607e3db866c366cb609b0015a1293edbeb703237 diff --git a/packages/backend-core/src/events/identification.ts b/packages/backend-core/src/events/identification.ts index c7bc1c817b..69bf7009b2 100644 --- a/packages/backend-core/src/events/identification.ts +++ b/packages/backend-core/src/events/identification.ts @@ -171,9 +171,9 @@ const identifyUser = async ( if (isSSOUser(user)) { providerType = user.providerType } - const accountHolder = account?.budibaseUserId === user._id || false - const verified = - account && account?.budibaseUserId === user._id ? account.verified : false + const accountHolder = await users.getExistingAccounts([user.email]) + const isAccountHolder = accountHolder.length > 0 + const verified = !!account && isAccountHolder && account.verified const installationId = await getInstallationId() const hosting = account ? account.hosting : getHostingFromEnv() const environment = getDeploymentEnvironment() @@ -185,7 +185,7 @@ const identifyUser = async ( installationId, tenantId, verified, - accountHolder, + accountHolder: isAccountHolder, providerType, builder, admin, @@ -207,9 +207,10 @@ const identifyAccount = async (account: Account) => { const environment = getDeploymentEnvironment() if (isCloudAccount(account)) { - if (account.budibaseUserId) { + const user = await users.getGlobalUserByEmail(account.email) + if (user?._id) { // use the budibase user as the id if set - id = account.budibaseUserId + id = user._id } } diff --git a/packages/backend-core/src/tenancy/db.ts b/packages/backend-core/src/tenancy/db.ts index 332ecbca48..10477a8579 100644 --- a/packages/backend-core/src/tenancy/db.ts +++ b/packages/backend-core/src/tenancy/db.ts @@ -1,29 +1,6 @@ import { getDB } from "../db/db" import { getGlobalDBName } from "../context" -import { TenantInfo } from "@budibase/types" export function getTenantDB(tenantId: string) { return getDB(getGlobalDBName(tenantId)) } - -export async function saveTenantInfo(tenantInfo: TenantInfo) { - const db = getTenantDB(tenantInfo.tenantId) - // save the tenant info to db - return db.put({ - _id: "tenant_info", - ...tenantInfo, - }) -} - -export async function getTenantInfo( - tenantId: string -): Promise { - try { - const db = getTenantDB(tenantId) - const tenantInfo = (await db.get("tenant_info")) as TenantInfo - delete tenantInfo.owner.password - return tenantInfo - } catch { - return undefined - } -} diff --git a/packages/backend-core/src/users/db.ts b/packages/backend-core/src/users/db.ts index c96c615f4b..cbc0019303 100644 --- a/packages/backend-core/src/users/db.ts +++ b/packages/backend-core/src/users/db.ts @@ -16,14 +16,15 @@ import { isSSOUser, SaveUserOpts, User, - UserStatus, UserGroup, + UserIdentifier, + UserStatus, PlatformUserBySsoId, PlatformUserById, AnyDocument, } from "@budibase/types" import { - getAccountHolderFromUserIds, + getAccountHolderFromUsers, isAdmin, isCreator, validateUniqueUser, @@ -412,7 +413,9 @@ export class UserDB { ) } - static async bulkDelete(userIds: string[]): Promise { + static async bulkDelete( + users: Array + ): Promise { const db = getGlobalDB() const response: BulkUserDeleted = { @@ -421,13 +424,13 @@ export class UserDB { } // remove the account holder from the delete request if present - const account = await getAccountHolderFromUserIds(userIds) - if (account) { - userIds = userIds.filter(u => u !== account.budibaseUserId) + const accountHolder = await getAccountHolderFromUsers(users) + if (accountHolder) { + users = users.filter(u => u.userId !== accountHolder.userId) // mark user as unsuccessful response.unsuccessful.push({ - _id: account.budibaseUserId, - email: account.email, + _id: accountHolder.userId, + email: accountHolder.email, reason: "Account holder cannot be deleted", }) } @@ -435,7 +438,7 @@ export class UserDB { // Get users and delete const allDocsResponse = await db.allDocs({ include_docs: true, - keys: userIds, + keys: users.map(u => u.userId), }) const usersToDelete = allDocsResponse.rows.map(user => { return user.doc! diff --git a/packages/backend-core/src/users/users.ts b/packages/backend-core/src/users/users.ts index f4838597b6..0bff428fa9 100644 --- a/packages/backend-core/src/users/users.ts +++ b/packages/backend-core/src/users/users.ts @@ -70,6 +70,17 @@ export async function getAllUserIds() { return response.rows.map(row => row.id) } +export async function getAllUsers(): Promise { + const db = getGlobalDB() + const startKey = `${DocumentType.USER}${SEPARATOR}` + const response = await db.allDocs({ + startkey: startKey, + endkey: `${startKey}${UNICODE_MAX}`, + include_docs: true, + }) + return response.rows.map(row => row.doc) as User[] +} + export async function bulkUpdateGlobalUsers(users: User[]) { const db = getGlobalDB() return (await db.bulkDocs(users)) as BulkDocsResponse diff --git a/packages/backend-core/src/users/utils.ts b/packages/backend-core/src/users/utils.ts index e1e3da181d..91b667ce17 100644 --- a/packages/backend-core/src/users/utils.ts +++ b/packages/backend-core/src/users/utils.ts @@ -1,11 +1,9 @@ -import { CloudAccount, ContextUser, User, UserGroup } from "@budibase/types" +import { ContextUser, User, UserGroup, UserIdentifier } from "@budibase/types" import * as accountSdk from "../accounts" import env from "../environment" -import { getFirstPlatformUser } from "./lookup" +import { getExistingAccounts, getFirstPlatformUser } from "./lookup" 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" @@ -67,21 +65,17 @@ export async function validateUniqueUser(email: string, tenantId: string) { } /** - * For the given user id's, return the account holder if it is in the ids. + * For a list of users, return the account holder if there is an email match. */ -export async function getAccountHolderFromUserIds( - userIds: string[] -): Promise { +export async function getAccountHolderFromUsers( + users: Array +): Promise { if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) { - const tenantId = getTenantId() - const account = await getAccountByTenantId(tenantId) - if (!account) { - throw new Error(`Account not found for tenantId=${tenantId}`) - } - - const budibaseUserId = account.budibaseUserId - if (userIds.includes(budibaseUserId)) { - return account - } + const accountMetadata = await getExistingAccounts( + users.map(user => user.email) + ) + return users.find(user => + accountMetadata.map(metadata => metadata.email).includes(user.email) + ) } } 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 458c9a3f79..3547a4876e 100644 --- a/packages/builder/src/pages/builder/portal/users/users/[userId].svelte +++ b/packages/builder/src/pages/builder/portal/users/users/[userId].svelte @@ -206,7 +206,7 @@ if (!user?._id) { $goto("./") } - tenantOwner = await users.tenantOwner($auth.tenantId) + tenantOwner = await users.getAccountHolder() } async function toggleFlags(detail) { 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 a1d5496ff6..5a7e334c9c 100644 --- a/packages/builder/src/pages/builder/portal/users/users/index.svelte +++ b/packages/builder/src/pages/builder/portal/users/users/index.svelte @@ -280,7 +280,12 @@ } if (ids.length > 0) { - await users.bulkDelete(ids) + await users.bulkDelete( + selectedRows.map(user => ({ + userId: user._id, + email: user.email, + })) + ) } if (selectedInvites.length > 0) { @@ -319,7 +324,7 @@ groupsLoaded = true pendingInvites = await users.getInvites() invitesLoaded = true - tenantOwner = await users.tenantOwner($auth.tenantId) + tenantOwner = await users.getAccountHolder() tenantOwnerLoaded = true } catch (error) { notifications.error("Error fetching user group data") diff --git a/packages/builder/src/stores/portal/users.js b/packages/builder/src/stores/portal/users.js index e9e65c3803..747dc5144d 100644 --- a/packages/builder/src/stores/portal/users.js +++ b/packages/builder/src/stores/portal/users.js @@ -112,8 +112,8 @@ export function createUsersStore() { return await API.getUserCountByApp({ appId }) } - async function bulkDelete(userIds) { - return API.deleteUsers(userIds) + async function bulkDelete(users) { + return API.deleteUsers(users) } async function save(user) { @@ -128,9 +128,8 @@ export function createUsersStore() { return await API.removeAppBuilder({ userId, appId }) } - async function getTenantOwner(tenantId) { - const tenantInfo = await API.getTenantInfo({ tenantId }) - return tenantInfo?.owner + async function getAccountHolder() { + return await API.getAccountHolder() } const getUserRole = user => { @@ -176,7 +175,7 @@ export function createUsersStore() { save: refreshUsage(save), bulkDelete: refreshUsage(bulkDelete), delete: refreshUsage(del), - tenantOwner: getTenantOwner, + getAccountHolder, } } diff --git a/packages/frontend-core/src/api/user.js b/packages/frontend-core/src/api/user.js index a2846c1e7b..45d481183a 100644 --- a/packages/frontend-core/src/api/user.js +++ b/packages/frontend-core/src/api/user.js @@ -122,14 +122,14 @@ export const buildUserEndpoints = API => ({ /** * Deletes multiple users - * @param userIds the ID of the user to delete + * @param users the ID/email pair of the user to delete */ - deleteUsers: async userIds => { + deleteUsers: async users => { const res = await API.post({ url: `/api/global/users/bulk`, body: { delete: { - userIds, + users, }, }, }) @@ -296,9 +296,9 @@ export const buildUserEndpoints = API => ({ }) }, - getTenantInfo: async ({ tenantId }) => { + getAccountHolder: async () => { return await API.get({ - url: `/api/global/tenant/${tenantId}`, + url: `/api/global/users/accountholder`, }) }, }) diff --git a/packages/types/src/api/web/user.ts b/packages/types/src/api/web/user.ts index 471ca86616..e102f136c3 100644 --- a/packages/types/src/api/web/user.ts +++ b/packages/types/src/api/web/user.ts @@ -15,7 +15,10 @@ export interface UserDetails { export interface BulkUserRequest { delete?: { - userIds: string[] + users: Array<{ + userId: string + email: string + }> } create?: { roles?: any[] diff --git a/packages/types/src/documents/global/index.ts b/packages/types/src/documents/global/index.ts index 6784f2638c..b728439dd6 100644 --- a/packages/types/src/documents/global/index.ts +++ b/packages/types/src/documents/global/index.ts @@ -7,4 +7,3 @@ export * from "./schedule" export * from "./templates" export * from "./environmentVariables" export * from "./auditLogs" -export * from "./tenantInfo" diff --git a/packages/types/src/documents/global/tenantInfo.ts b/packages/types/src/documents/global/tenantInfo.ts deleted file mode 100644 index 4c8837cf2a..0000000000 --- a/packages/types/src/documents/global/tenantInfo.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Hosting } from "../../sdk" -import { Document } from "../document" - -export interface TenantInfo extends Document { - owner: { - email: string - password?: string - ssoId?: string - givenName?: string - familyName?: string - budibaseUserId?: string - } - tenantId: string - hosting: Hosting -} diff --git a/packages/types/src/documents/global/user.ts b/packages/types/src/documents/global/user.ts index 829a171843..1b242886b5 100644 --- a/packages/types/src/documents/global/user.ts +++ b/packages/types/src/documents/global/user.ts @@ -38,6 +38,11 @@ export function isSSOUser(user: User): user is SSOUser { // USER +export interface UserIdentifier { + userId: string + email: string +} + export interface User extends Document { tenantId: string email: string diff --git a/packages/worker/src/api/controllers/global/tenant.ts b/packages/worker/src/api/controllers/global/tenant.ts deleted file mode 100644 index 8b5ae6d528..0000000000 --- a/packages/worker/src/api/controllers/global/tenant.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { tenancy } from "@budibase/backend-core" -import { TenantInfo, Ctx } from "@budibase/types" - -export const save = async (ctx: Ctx) => { - const response = await tenancy.saveTenantInfo(ctx.request.body) - ctx.body = { - _id: response.id, - _rev: response.rev, - } -} - -export const get = async (ctx: Ctx) => { - ctx.body = await tenancy.getTenantInfo(ctx.params.id) -} diff --git a/packages/worker/src/api/controllers/global/users.ts b/packages/worker/src/api/controllers/global/users.ts index 921e0324d1..52f8821fab 100644 --- a/packages/worker/src/api/controllers/global/users.ts +++ b/packages/worker/src/api/controllers/global/users.ts @@ -23,9 +23,11 @@ import { SearchUsersRequest, User, UserCtx, + UserIdentifier, } from "@budibase/types" import { accounts, + users, cache, ErrorCode, events, @@ -55,8 +57,8 @@ export const save = async (ctx: UserCtx) => { const requestUser = ctx.request.body // Do not allow the account holder role to be changed - const tenantInfo = await tenancy.getTenantInfo(requestUser.tenantId) - if (tenantInfo?.owner.email === requestUser.email) { + const accountMetadata = await users.getExistingAccounts([requestUser.email]) + if (accountMetadata?.length > 0) { if ( requestUser.admin?.global !== true || requestUser.builder?.global !== true @@ -103,11 +105,14 @@ export const addSsoSupport = async (ctx: Ctx) => { } } -const bulkDelete = async (userIds: string[], currentUserId: string) => { - if (userIds?.indexOf(currentUserId) !== -1) { +const bulkDelete = async ( + users: Array, + currentUserId: string +) => { + if (users.find(u => u.userId === currentUserId)) { throw new Error("Unable to delete self.") } - return await userSdk.db.bulkDelete(userIds) + return await userSdk.db.bulkDelete(users) } const bulkCreate = async (users: User[], groupIds: string[]) => { @@ -130,7 +135,7 @@ export const bulkUpdate = async ( created = await bulkCreate(input.create.users, input.create.groups) } if (input.delete) { - deleted = await bulkDelete(input.delete.userIds, currentUserId) + deleted = await bulkDelete(input.delete.users, currentUserId) } } catch (err: any) { ctx.throw(err.status || 400, err?.message || err) @@ -302,6 +307,23 @@ export const tenantUserLookup = async (ctx: any) => { } } +/** + * This will be paginated to a default of the first 50 users, + * So the account holder may not be found until further pagination has occurred + */ +export const accountHolderLookup = async (ctx: Ctx) => { + const users = await userSdk.core.getAllUsers() + const response = await userSdk.core.getExistingAccounts( + users.map(u => u.email) + ) + const holder = response[0] + if (!holder) { + return + } + holder._id = users.find(u => u.email === holder.email)?._id + ctx.body = holder +} + /* Encapsulate the app user onboarding flows here. */ diff --git a/packages/worker/src/api/index.ts b/packages/worker/src/api/index.ts index db0a80acfd..a7594b713e 100644 --- a/packages/worker/src/api/index.ts +++ b/packages/worker/src/api/index.ts @@ -71,10 +71,6 @@ const PUBLIC_ENDPOINTS = [ route: "/api/global/users/invite", method: "GET", }, - { - route: "/api/global/tenant", - method: "POST", - }, ] const NO_TENANCY_ENDPOINTS = [ @@ -121,11 +117,7 @@ const NO_TENANCY_ENDPOINTS = [ method: "GET", }, { - route: "/api/global/tenant", - method: "POST", - }, - { - route: "/api/global/tenant/:id", + route: "/api/global/users/accountholder", method: "GET", }, ] diff --git a/packages/worker/src/api/routes/global/tenant.ts b/packages/worker/src/api/routes/global/tenant.ts deleted file mode 100644 index f15e374774..0000000000 --- a/packages/worker/src/api/routes/global/tenant.ts +++ /dev/null @@ -1,11 +0,0 @@ -import Router from "@koa/router" -import * as controller from "../../controllers/global/tenant" -import cloudRestricted from "../../../middleware/cloudRestricted" - -const router: Router = new Router() - -router - .post("/api/global/tenant", cloudRestricted, controller.save) - .get("/api/global/tenant/:id", controller.get) - -export default router diff --git a/packages/worker/src/api/routes/global/tests/tenant.spec.ts b/packages/worker/src/api/routes/global/tests/tenant.spec.ts deleted file mode 100644 index 390cfa2af5..0000000000 --- a/packages/worker/src/api/routes/global/tests/tenant.spec.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { Hosting, TenantInfo } from "@budibase/types" -import { TestConfiguration } from "../../../../tests" -import { tenancy as _tenancy } from "@budibase/backend-core" - -const tenancy = jest.mocked(_tenancy) - -describe("/api/global/tenant", () => { - const config = new TestConfiguration() - - beforeAll(async () => { - await config.beforeAll() - }) - - afterAll(async () => { - await config.afterAll() - }) - - beforeEach(() => { - jest.clearAllMocks() - }) - - describe("POST /api/global/tenant", () => { - it("should save the tenantInfo", async () => { - tenancy.saveTenantInfo = jest.fn().mockImplementation(async () => ({ - id: "DOC_ID", - ok: true, - rev: "DOC_REV", - })) - const tenantInfo: TenantInfo = { - owner: { - email: "test@example.com", - password: "PASSWORD123!", - ssoId: "SSO_ID", - givenName: "Jane", - familyName: "Doe", - budibaseUserId: "USER_ID", - }, - tenantId: "tenant123", - hosting: Hosting.CLOUD, - } - const response = await config.api.tenants.saveTenantInfo(tenantInfo) - - expect(_tenancy.saveTenantInfo).toHaveBeenCalledTimes(1) - expect(_tenancy.saveTenantInfo).toHaveBeenCalledWith(tenantInfo) - expect(response.text).toEqual('{"_id":"DOC_ID","_rev":"DOC_REV"}') - }) - }) -}) diff --git a/packages/worker/src/api/routes/global/tests/users.spec.ts b/packages/worker/src/api/routes/global/tests/users.spec.ts index c8e71f7eb4..b6237c7b4b 100644 --- a/packages/worker/src/api/routes/global/tests/users.spec.ts +++ b/packages/worker/src/api/routes/global/tests/users.spec.ts @@ -412,28 +412,6 @@ describe("/api/global/users", () => { expect(events.user.permissionBuilderRemoved).toHaveBeenCalledTimes(1) }) - it("should not be able to update an account holder user to a basic user", async () => { - const accountHolderUser = await config.createUser( - structures.users.adminUser() - ) - jest.clearAllMocks() - tenancy.getTenantInfo = jest.fn().mockImplementation(() => ({ - owner: { - email: accountHolderUser.email, - }, - })) - - accountHolderUser.admin!.global = false - accountHolderUser.builder!.global = false - - await config.api.users.saveUser(accountHolderUser, 400) - - expect(events.user.created).not.toHaveBeenCalled() - expect(events.user.updated).not.toHaveBeenCalled() - expect(events.user.permissionAdminRemoved).not.toHaveBeenCalled() - expect(events.user.permissionBuilderRemoved).not.toHaveBeenCalled() - }) - it("should be able to update an builder user to a basic user", async () => { const user = await config.createUser(structures.users.builderUser()) jest.clearAllMocks() @@ -592,55 +570,21 @@ describe("/api/global/users", () => { describe("POST /api/global/users/bulk (delete)", () => { it("should not be able to bulk delete current user", async () => { - const user = await config.user! + const user = config.user! - const response = await config.api.users.bulkDeleteUsers([user._id!], 400) + const response = await config.api.users.bulkDeleteUsers( + [ + { + userId: user._id!, + email: "test@example.com", + }, + ], + 400 + ) expect(response.message).toBe("Unable to delete self.") expect(events.user.deleted).not.toHaveBeenCalled() }) - - it("should not be able to bulk delete account owner", async () => { - const user = await config.createUser() - const account = structures.accounts.cloudAccount() - account.budibaseUserId = user._id! - accounts.getAccountByTenantId.mockReturnValue(Promise.resolve(account)) - - const response = await config.api.users.bulkDeleteUsers([user._id!]) - - expect(response.deleted?.successful.length).toBe(0) - expect(response.deleted?.unsuccessful.length).toBe(1) - expect(response.deleted?.unsuccessful[0].reason).toBe( - "Account holder cannot be deleted" - ) - expect(response.deleted?.unsuccessful[0]._id).toBe(user._id) - expect(events.user.deleted).not.toHaveBeenCalled() - }) - - it("should be able to bulk delete users", async () => { - const account = structures.accounts.cloudAccount() - accounts.getAccountByTenantId.mockReturnValue(Promise.resolve(account)) - - const builder = structures.users.builderUser() - const admin = structures.users.adminUser() - const user = structures.users.user() - const createdUsers = await config.api.users.bulkCreateUsers([ - builder, - admin, - user, - ]) - - const toDelete = createdUsers.created?.successful.map( - u => u._id! - ) as string[] - const response = await config.api.users.bulkDeleteUsers(toDelete) - - expect(response.deleted?.successful.length).toBe(3) - expect(response.deleted?.unsuccessful.length).toBe(0) - expect(events.user.deleted).toHaveBeenCalledTimes(3) - expect(events.user.permissionAdminRemoved).toHaveBeenCalledTimes(1) - expect(events.user.permissionBuilderRemoved).toHaveBeenCalledTimes(2) - }) }) describe("POST /api/global/users/search", () => { diff --git a/packages/worker/src/api/routes/global/users.ts b/packages/worker/src/api/routes/global/users.ts index d5dfa47923..4078f348ad 100644 --- a/packages/worker/src/api/routes/global/users.ts +++ b/packages/worker/src/api/routes/global/users.ts @@ -136,6 +136,7 @@ router buildAdminInitValidation(), controller.adminUser ) + .get("/api/global/users/accountholder", controller.accountHolderLookup) .get("/api/global/users/tenant/:id", controller.tenantUserLookup) // global endpoint but needs to come at end (blocks other endpoints otherwise) .get("/api/global/users/:id", auth.builderOrAdmin, controller.find) diff --git a/packages/worker/src/api/routes/index.ts b/packages/worker/src/api/routes/index.ts index 2eb4b5cd5d..e6cacf110f 100644 --- a/packages/worker/src/api/routes/index.ts +++ b/packages/worker/src/api/routes/index.ts @@ -1,7 +1,6 @@ import Router from "@koa/router" import { api as pro } from "@budibase/pro" import userRoutes from "./global/users" -import tenantRoutes from "./global/tenant" import configRoutes from "./global/configs" import workspaceRoutes from "./global/workspaces" import templateRoutes from "./global/templates" @@ -41,7 +40,6 @@ export const routes: Router[] = [ accountRoutes, restoreRoutes, eventRoutes, - tenantRoutes, pro.scim, ] diff --git a/packages/worker/src/api/routes/validation/users.ts b/packages/worker/src/api/routes/validation/users.ts index 333435ed29..46c66285fd 100644 --- a/packages/worker/src/api/routes/validation/users.ts +++ b/packages/worker/src/api/routes/validation/users.ts @@ -66,7 +66,14 @@ export const buildUserBulkUserValidation = (isSelf = false) => { users: Joi.array().items(Joi.object(schema).required().unknown(true)), }), delete: Joi.object({ - userIds: Joi.array().items(Joi.string()), + users: Joi.array().items( + Joi.object({ + email: Joi.string(), + userId: Joi.string(), + }) + .required() + .unknown(true) + ), }), } diff --git a/packages/worker/src/tests/api/tenants.ts b/packages/worker/src/tests/api/tenants.ts index c404b8ad58..16f970915a 100644 --- a/packages/worker/src/tests/api/tenants.ts +++ b/packages/worker/src/tests/api/tenants.ts @@ -1,4 +1,3 @@ -import { TenantInfo } from "@budibase/types" import TestConfiguration from "../TestConfiguration" import { TestAPI, TestAPIOpts } from "./base" @@ -15,12 +14,4 @@ export class TenantAPI extends TestAPI { .set(opts?.headers) .expect(opts?.status ? opts.status : 204) } - - saveTenantInfo = (tenantInfo: TenantInfo) => { - return this.request - .post("/api/global/tenant") - .set(this.config.internalAPIHeaders()) - .send(tenantInfo) - .expect(200) - } } diff --git a/packages/worker/src/tests/api/users.ts b/packages/worker/src/tests/api/users.ts index 8ff31f04fc..b54fb45d2c 100644 --- a/packages/worker/src/tests/api/users.ts +++ b/packages/worker/src/tests/api/users.ts @@ -81,8 +81,14 @@ export class UserAPI extends TestAPI { return res.body as BulkUserResponse } - bulkDeleteUsers = async (userIds: string[], status?: number) => { - const body: BulkUserRequest = { delete: { userIds } } + bulkDeleteUsers = async ( + users: Array<{ + userId: string + email: string + }>, + status?: number + ) => { + const body: BulkUserRequest = { delete: { users } } const res = await this.request .post(`/api/global/users/bulk`) .send(body)