Skip to content

Commit 5e31db9

Browse files
Sarath1018claude
andcommitted
feat(identity): add updateById, deleteById and create to Users service
Second slice of the Identity user management onboarding (follows the getById scaffold PR): - updateById(userId, options) PUT /api/User/{userId} - deleteById(userId) DELETE /api/User/{userId} - create(users, organizationId, opts?) POST /api/User/BulkCreate Introduces bound entity methods (user.update(), user.delete()) attached to getById() and create() results via createUserWithMethods, with model tests for the delegation. Live-API finding encoded: BulkCreate returns { result, users[] } with the full created users, not the bare IdentityResult the Swagger spec declares; userName is required (email alone is rejected). The integration suite now self-provisions its fixture user via create() and cleans up with deleteById() — the transient IDENTITY_TEST_USER_ID env var from the previous slice is replaced by UIPATH_ORGANIZATION_ID. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 53b1bf8 commit 5e31db9

15 files changed

Lines changed: 687 additions & 40 deletions

File tree

docs/oauth-scopes.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,3 +278,6 @@ The `ConversationalAgents` scope is required for real-time WebSocket sessions (`
278278
| Method | OAuth Scope |
279279
|--------|-------------|
280280
| `getById()` | `PM.Users` or `PM.Users.Read` |
281+
| `updateById()` | `PM.Users` or `PM.Users.Write` |
282+
| `deleteById()` | `PM.Users` or `PM.Users.Write` |
283+
| `create()` | `PM.Users` or `PM.Users.Write` |

src/models/identity/users.constants.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@ import { UserType, UserCategory } from './users.types';
44
* User field mappings (API field name → SDK field name).
55
*
66
* Semantic renames only — the API already returns camelCase, so no case
7-
* conversion is involved.
7+
* conversion is involved. `groupIDsToAdd`/`groupIDsToRemove` appear only in
8+
* update requests; `transformRequest()` reverses the map for outbound payloads.
89
*/
910
export const UserMap: { [key: string]: string } = {
1011
creationTime: 'createdTime',
1112
lastModificationTime: 'lastModifiedTime',
1213
groupIDs: 'groupIds',
14+
groupIDsToAdd: 'groupIdsToAdd',
15+
groupIDsToRemove: 'groupIdsToRemove',
1316
};
1417

1518
/**

src/models/identity/users.internal-types.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
* NOT exported from the public barrel (`src/models/identity/index.ts`).
55
*/
66

7+
import type { UserOperationResult } from './users.types';
8+
79
/**
810
* Raw user shape as returned by the Identity user management API.
911
*
@@ -46,3 +48,14 @@ export interface RawUserEntry {
4648
export const INTERNAL_USER_FIELDS = [
4749
'legacyId',
4850
] as const satisfies ReadonlyArray<keyof RawUserEntry>;
51+
52+
/**
53+
* Raw response of `POST /api/User/BulkCreate`.
54+
*
55+
* The Swagger spec declares a bare `IdentityResult`, but the live API also
56+
* returns the created users.
57+
*/
58+
export interface RawUserCreateResponse {
59+
result: UserOperationResult;
60+
users: RawUserEntry[];
61+
}

src/models/identity/users.models.ts

Lines changed: 166 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,37 @@
33
* interface that drives generated API documentation.
44
*/
55

6-
import type { RawUserGetResponse } from './users.types';
6+
import type {
7+
RawUserGetResponse,
8+
UserCreateData,
9+
UserCreateOptions,
10+
UserOperationResult,
11+
UserUpdateOptions,
12+
UserUpdateResponse,
13+
} from './users.types';
714

815
/**
9-
* User returned by the Users service.
16+
* User with attached methods
1017
*/
11-
export interface UserGetResponse extends RawUserGetResponse {}
18+
export type UserGetResponse = RawUserGetResponse & UserMethods;
19+
20+
/**
21+
* Response from `create()`.
22+
*
23+
* `result` reflects the request as a whole; `users` contains the created users.
24+
*/
25+
export interface UserCreateResponse {
26+
/** Overall outcome of the create request. */
27+
result: UserOperationResult;
28+
/** The created users. */
29+
users: UserGetResponse[];
30+
}
1231

1332
/**
1433
* Service model for managing users in a UiPath organization.
1534
*
16-
* Provides organization-level user administration.
35+
* Provides organization-level user administration: retrieving, updating and
36+
* deleting users, creating users in bulk, and inviting users by email.
1737
*
1838
* ### Usage
1939
*
@@ -31,17 +51,157 @@ export interface UserServiceModel {
3151
* Gets a user by ID.
3252
*
3353
* Returns the full user details including profile fields, group membership,
34-
* activity flags and invitation state.
54+
* activity flags and invitation state, with entity methods attached
55+
* (`update`, `delete`).
3556
*
3657
* @param userId - User GUID
37-
* @returns Promise resolving to the user
58+
* @returns Promise resolving to the user with methods
3859
* {@link UserGetResponse}
3960
*
4061
* @example
4162
* ```typescript
4263
* const user = await users.getById('<userId>');
4364
* console.log(`${user.displayName} (${user.email}) — active: ${user.isActive}`);
65+
*
66+
* // Operate on the user directly via bound methods
67+
* await user.update({ displayName: 'New Name' });
4468
* ```
4569
*/
4670
getById(userId: string): Promise<UserGetResponse>;
71+
72+
/**
73+
* Updates a user. Only the provided fields are changed.
74+
*
75+
* @param userId - User GUID
76+
* @param options - Fields to update
77+
* @returns Promise resolving to the operation outcome
78+
* {@link UserUpdateResponse}
79+
*
80+
* @example
81+
* ```typescript
82+
* // First, get the user with users.getById() or from users.create()
83+
* const result = await users.updateById('<userId>', { displayName: 'New Name' });
84+
* if (result.succeeded) {
85+
* console.log('User updated');
86+
* }
87+
* ```
88+
*
89+
* @example Manage group membership
90+
* ```typescript
91+
* await users.updateById('<userId>', {
92+
* groupIdsToAdd: ['<groupId-1>'],
93+
* groupIdsToRemove: ['<groupId-2>'],
94+
* });
95+
* ```
96+
*/
97+
updateById(userId: string, options: UserUpdateOptions): Promise<UserUpdateResponse>;
98+
99+
/**
100+
* Deletes a user.
101+
*
102+
* @param userId - User GUID
103+
* @returns Promise that resolves when the user is deleted
104+
*
105+
* @example
106+
* ```typescript
107+
* await users.deleteById('<userId>');
108+
* ```
109+
*/
110+
deleteById(userId: string): Promise<void>;
111+
112+
/**
113+
* Creates users in bulk.
114+
*
115+
* Returns the created users with entity methods attached (`update`, `delete`).
116+
* A single invalid user fails the whole request — check `result.errors` for
117+
* the reason.
118+
*
119+
* @param users - Users to create
120+
* @param organizationId - Organization GUID the users belong to
121+
* @param options - Optional group assignment applied to every created user
122+
* @returns Promise resolving to the overall outcome and the created users
123+
* {@link UserCreateResponse}
124+
*
125+
* @example
126+
* ```typescript
127+
* const response = await users.create(
128+
* [{ userName: 'jdoe', email: 'jdoe@acme.com', name: 'Jane', surname: 'Doe' }],
129+
* '<organizationId>'
130+
* );
131+
* if (response.result.succeeded) {
132+
* console.log(`Created ${response.users[0].id}`);
133+
* }
134+
* ```
135+
*
136+
* @example Add every created user to groups
137+
* ```typescript
138+
* const response = await users.create(
139+
* [{ userName: 'jdoe', email: 'jdoe@acme.com' }],
140+
* '<organizationId>',
141+
* { groupIds: ['<groupId>'] }
142+
* );
143+
* ```
144+
*/
145+
create(
146+
users: UserCreateData[],
147+
organizationId: string,
148+
options?: UserCreateOptions
149+
): Promise<UserCreateResponse>;
150+
151+
}
152+
153+
/**
154+
* Methods attached to user objects returned by `getById()` and `create()`.
155+
*/
156+
export interface UserMethods {
157+
/**
158+
* Updates this user. Only the provided fields are changed.
159+
*
160+
* @param options - Fields to update
161+
* @returns Promise resolving to the operation outcome
162+
*/
163+
update(options: UserUpdateOptions): Promise<UserUpdateResponse>;
164+
165+
/**
166+
* Deletes this user.
167+
*
168+
* @returns Promise that resolves when the user is deleted
169+
*/
170+
delete(): Promise<void>;
171+
}
172+
173+
/**
174+
* Creates methods for a user
175+
*
176+
* @param userData - The user data (response from API)
177+
* @param service - The user service instance
178+
* @returns Object containing user methods
179+
*/
180+
function createUserMethods(userData: RawUserGetResponse, service: UserServiceModel): UserMethods {
181+
return {
182+
async update(options: UserUpdateOptions): Promise<UserUpdateResponse> {
183+
if (!userData.id) throw new Error('User ID is undefined');
184+
return service.updateById(userData.id, options);
185+
},
186+
187+
async delete(): Promise<void> {
188+
if (!userData.id) throw new Error('User ID is undefined');
189+
return service.deleteById(userData.id);
190+
},
191+
};
192+
}
193+
194+
/**
195+
* Attaches methods to a user object
196+
*
197+
* @param userData - The user data (response from API)
198+
* @param service - The user service instance
199+
* @returns User data with methods attached
200+
*/
201+
export function createUserWithMethods(
202+
userData: RawUserGetResponse,
203+
service: UserServiceModel
204+
): UserGetResponse {
205+
const methods = createUserMethods(userData, service);
206+
return Object.assign({}, userData, methods);
47207
}

src/models/identity/users.types.ts

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Identity user management types — response shapes and enums.
2+
* Identity user management types — response shapes, request payloads and enums.
33
*/
44

55
/**
@@ -72,3 +72,83 @@ export interface RawUserGetResponse {
7272
/** Whether the user has accepted their invitation. */
7373
invitationAccepted: boolean;
7474
}
75+
76+
/**
77+
* Error entry from a failed user operation.
78+
*/
79+
export interface UserOperationError {
80+
/** Machine-readable error code (e.g. `InvalidUserName`). */
81+
code: string;
82+
/** Human-readable error description. */
83+
description: string;
84+
}
85+
86+
/**
87+
* Overall outcome of a user operation.
88+
*/
89+
export interface UserOperationResult {
90+
/** Whether the operation succeeded. */
91+
succeeded: boolean;
92+
/** Errors that caused the operation to fail. Empty on success. */
93+
errors: UserOperationError[];
94+
}
95+
96+
/**
97+
* Response from `updateById()`.
98+
*/
99+
export interface UserUpdateResponse extends UserOperationResult {}
100+
101+
/**
102+
* Payload for `updateById()`. Only the provided fields are changed.
103+
*/
104+
export interface UserUpdateOptions {
105+
/** New first name. */
106+
name?: string;
107+
/** New last name. */
108+
surname?: string;
109+
/** New display name. */
110+
displayName?: string;
111+
/** New email address. */
112+
email?: string;
113+
/** Activate (`true`) or deactivate (`false`) the user. */
114+
isActive?: boolean;
115+
/** New password. */
116+
password?: string;
117+
/** GUIDs of groups to add the user to. */
118+
groupIdsToAdd?: string[];
119+
/** GUIDs of groups to remove the user from. */
120+
groupIdsToRemove?: string[];
121+
/** Whether this user bypasses the basic authentication restriction. */
122+
bypassBasicAuthRestriction?: boolean;
123+
/** Whether the user should be marked as having accepted their invitation. */
124+
invitationAccepted?: boolean;
125+
}
126+
127+
/**
128+
* A user to create with `create()`.
129+
*/
130+
export interface UserCreateData {
131+
/** Username — can only contain letters or digits. */
132+
userName: string;
133+
/** Email address. */
134+
email?: string;
135+
/** First name. */
136+
name?: string;
137+
/** Last name. */
138+
surname?: string;
139+
/** Display name. */
140+
displayName?: string;
141+
/** Whether the user should be marked as having accepted their invitation. */
142+
invitationAccepted?: boolean;
143+
/** Whether this user bypasses the basic authentication restriction. */
144+
bypassBasicAuthRestriction?: boolean;
145+
}
146+
147+
/**
148+
* Options for `create()`.
149+
*/
150+
export interface UserCreateOptions {
151+
/** GUIDs of groups every created user is added to. */
152+
groupIds?: string[];
153+
}
154+

src/services/identity/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* Identity Users Service Module
33
*
44
* Provides organization-level user administration via the UiPath Identity API:
5-
* - `Users` — retrieve users in the organization
5+
* - `Users` — retrieve, update and delete users, and create users in bulk
66
*
77
* @example
88
* ```typescript

0 commit comments

Comments
 (0)