Skip to content

Commit d46f47f

Browse files
authored
Create batch users (#553) RELEASE
* feat: users create batch * commit file * proofread
1 parent 51a49d5 commit d46f47f

File tree

8 files changed

+119
-17
lines changed

8 files changed

+119
-17
lines changed

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,7 @@ await descopeClient.management.user.invite('[email protected]', {
717717
// Make sure to configure the invite URL in the Descope console prior to using this function,
718718
// and that an email address / phone number is provided in the information. You can also set
719719
// a cleartext password or import a prehashed one from another service.
720+
// Note: This function will send an invitation to each user in the `users` array. If you want to create users without sending invitations, use `createBatch` instead.
720721
await descopeClient.management.user.inviteBatch(
721722
[
722723
{
@@ -737,6 +738,25 @@ await descopeClient.management.user.inviteBatch(
737738
false,
738739
);
739740

741+
// Create a batch of users.
742+
// This is useful when you want to create users programmatically without triggering the invitation flow.
743+
// You can set a cleartext password or import a prehashed one from another service.
744+
// Note: This function will NOT send an invitation to the created users. If invitations are required use `inviteBatch` instead.
745+
await descopeClient.management.user.createBatch([
746+
{
747+
loginId: '[email protected]',
748+
749+
phone: '+123456789123',
750+
displayName: 'Desmond Copeland',
751+
userTenants: [{ tenantId: 'tenant-ID1', roleNames: ['role-name1'] }],
752+
hashedPassword: {
753+
bcrypt: {
754+
hash: '$2a$...',
755+
},
756+
},
757+
},
758+
]);
759+
740760
// Update will override all fields as is. Use carefully.
741761
await descopeClient.management.user.update('[email protected]', {
742762

lib/constants.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// eslint-disable-next-line import/prefer-default-export
21
/** Refresh JWT cookie name */
32
export const refreshTokenCookieName = 'DSR';
43
/** Session JWT cookie name */

lib/errors.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// eslint-disable-next-line import/prefer-default-export
21
/** Common Error Codes */
32
export default {
43
badRequest: 'E011001',

lib/helpers.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ export const getCookieValue = (cookie: string | null | undefined, name: string)
2525
return match ? match[1] : null;
2626
};
2727

28-
// eslint-disable-next-line import/prefer-default-export
2928
/**
3029
* Add cookie generation to core-js functions.
3130
* @param fn the function we are wrapping

lib/management/helpers.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/* eslint-disable import/prefer-default-export */
2+
import { User } from './types';
3+
4+
/**
5+
* Transforms user objects by converting roles to roleNames
6+
*/
7+
export function transformUsersForBatch(users: User[]): any[] {
8+
return users.map(({ roles, ...user }) => ({
9+
...user,
10+
roleNames: roles,
11+
}));
12+
}

lib/management/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,7 @@ export type UserFailedResponse = {
549549
user: UserResponse;
550550
};
551551

552-
export type InviteBatchResponse = {
552+
export type CreateOrInviteBatchResponse = {
553553
createdUsers: UserResponse[];
554554
failedUsers: UserFailedResponse[];
555555
additionalErrors: Record<string, string>;

lib/management/user.test.ts

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
GenerateEnchantedLinkForTestResponse,
99
ProviderTokenResponse,
1010
GenerateEmbeddedLinkResponse,
11-
InviteBatchResponse,
11+
CreateOrInviteBatchResponse,
1212
UserPasswordHashed,
1313
} from './types';
1414

@@ -385,7 +385,7 @@ describe('Management User', () => {
385385
},
386386
};
387387

388-
const resp: SdkResponse<InviteBatchResponse> = await management.user.inviteBatch(
388+
const resp: SdkResponse<CreateOrInviteBatchResponse> = await management.user.inviteBatch(
389389
[
390390
{ loginId: 'one', roles: ['r1'], email: 'one@one', password: 'clear', seed: 'aaa' },
391391
{ loginId: 'two', roles: ['r1'], email: 'two@two', hashedPassword: hashed },
@@ -437,6 +437,74 @@ describe('Management User', () => {
437437
});
438438
});
439439

440+
describe('create batch', () => {
441+
it('should send the correct request and receive correct response', async () => {
442+
const httpResponse = {
443+
ok: true,
444+
json: () => mockMgmtInviteBatchResponse,
445+
clone: () => ({
446+
json: () => Promise.resolve(mockMgmtInviteBatchResponse),
447+
}),
448+
status: 200,
449+
};
450+
mockHttpClient.post.mockResolvedValue(httpResponse);
451+
452+
const hashed: UserPasswordHashed = {
453+
firebase: {
454+
hash: 'h',
455+
salt: 's',
456+
saltSeparator: 'ss',
457+
signerKey: 'sk',
458+
memory: 14,
459+
rounds: 8,
460+
},
461+
};
462+
463+
const resp: SdkResponse<CreateOrInviteBatchResponse> = await management.user.createBatch([
464+
{ loginId: 'one', roles: ['r1'], email: 'one@one', password: 'clear', seed: 'aaa' },
465+
{ loginId: 'two', roles: ['r1'], email: 'two@two', hashedPassword: hashed },
466+
]);
467+
468+
expect(mockHttpClient.post).toHaveBeenCalledWith(
469+
apiPaths.user.createBatch,
470+
{
471+
users: [
472+
{
473+
loginId: 'one',
474+
roleNames: ['r1'],
475+
email: 'one@one',
476+
password: 'clear',
477+
seed: 'aaa',
478+
},
479+
{
480+
loginId: 'two',
481+
roleNames: ['r1'],
482+
email: 'two@two',
483+
hashedPassword: {
484+
firebase: {
485+
hash: 'h',
486+
salt: 's',
487+
saltSeparator: 'ss',
488+
signerKey: 'sk',
489+
memory: 14,
490+
rounds: 8,
491+
},
492+
},
493+
},
494+
],
495+
},
496+
{ token: 'key' },
497+
);
498+
499+
expect(resp).toEqual({
500+
code: 200,
501+
data: mockMgmtInviteBatchResponse,
502+
ok: true,
503+
response: httpResponse,
504+
});
505+
});
506+
});
507+
440508
describe('update', () => {
441509
it('should send the correct request and receive correct response with multiple arguments', async () => {
442510
const httpResponse = {

lib/management/user.ts

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@ import {
1515
AttributesTypes,
1616
UserStatus,
1717
User,
18-
InviteBatchResponse,
18+
CreateOrInviteBatchResponse,
1919
TemplateOptions,
2020
ProviderTokenOptions,
2121
UserOptions,
2222
} from './types';
2323
import { CoreSdk, DeliveryMethodForTestUser } from '../types';
2424
import apiPaths from './paths';
25+
import { transformUsersForBatch } from './helpers';
2526

2627
type SearchSort = {
2728
field: string;
@@ -443,19 +444,12 @@ const withUser = (sdk: CoreSdk, managementKey?: string) => {
443444
sendSMS?: boolean, // send invite via text message, default is according to project settings
444445
templateOptions?: TemplateOptions,
445446
templateId?: string,
446-
): Promise<SdkResponse<InviteBatchResponse>> =>
447-
transformResponse<InviteBatchResponse, InviteBatchResponse>(
447+
): Promise<SdkResponse<CreateOrInviteBatchResponse>> =>
448+
transformResponse<CreateOrInviteBatchResponse, CreateOrInviteBatchResponse>(
448449
sdk.httpClient.post(
449450
apiPaths.user.createBatch,
450451
{
451-
users: users.map((u) => {
452-
const res = {
453-
...u,
454-
roleNames: u.roles,
455-
};
456-
delete res.roles;
457-
return res;
458-
}),
452+
users: transformUsersForBatch(users),
459453
invite: true,
460454
inviteUrl,
461455
sendMail,
@@ -467,6 +461,17 @@ const withUser = (sdk: CoreSdk, managementKey?: string) => {
467461
),
468462
(data) => data,
469463
),
464+
createBatch: (users: User[]): Promise<SdkResponse<CreateOrInviteBatchResponse>> =>
465+
transformResponse<CreateOrInviteBatchResponse, CreateOrInviteBatchResponse>(
466+
sdk.httpClient.post(
467+
apiPaths.user.createBatch,
468+
{
469+
users: transformUsersForBatch(users),
470+
},
471+
{ token: managementKey },
472+
),
473+
(data) => data,
474+
),
470475
update,
471476
patch,
472477
/**

0 commit comments

Comments
 (0)