Skip to content

feat(platform): add Groups service for organization group management - #664

Open
Sarath1018 wants to merge 1 commit into
feat/platform-usersfrom
feat/platform-groups
Open

feat(platform): add Groups service for organization group management#664
Sarath1018 wants to merge 1 commit into
feat/platform-usersfrom
feat/platform-groups

Conversation

@Sarath1018

Copy link
Copy Markdown
Collaborator

Summary

PR 2/4 in the platform RBAC stack (Users → Groups → Directory → Roles). Stacked on #663 — merge that first; this PR's diff is Groups-only.

Adds PlatformGroupService (exported as Groups) to the /platform subpath.

Method Endpoint Notes
getAll(organizationId) GET /identity_/api/Group/{org} All local + built-in groups
getById(groupId, organizationId) GET /identity_/api/Group/{org}/{groupId}
create(name, organizationId, options?) POST /identity_/api/Group SDK generates the required client-side group GUID; initial members via memberUserIds
updateById(groupId, organizationId, name, options?) PUT /identity_/api/Group/{groupId} name is required by the API on every update (409 without it — live-verified); bound group.update() auto-fills the current name
deleteById(groupId, organizationId) DELETE /identity_/api/Group/{org}/{groupId}
getMembers(groupId, organizationId, options?) GET .../Members Paged (top/skip); fetch-all default

Design decisions (validated against the live API)

  • Enrichment: groups carry organizationId (not on the wire) so bound update()/delete()/getMembers() capture full context
  • Numeric enum codes: GroupType arrives as 0/1 despite the spec's string enum — mapped to PlatformGroupType.BuiltIn/.Custom
  • Dropped fields: members (always empty on the wire — membership served by getMembers()), mappedRole, scope (undocumented nulls)
  • Wire-name abstraction: the API uses directoryUserMemberIDs (create) vs directoryUserIDsToAdd/Remove (update); the SDK exposes uniform memberUserIds* names
  • Bound methods: update / delete / getMembers (state-changing + contextual read); entry points not bound

Testing

  • Unit: 2389 passing (40 new incl. transform completeness, GUID-generation assertion, wire-name mapping, fetch-all loop, validation branches for every required param)
  • Model tests: delegation incl. the name-autofill branch
  • Integration (live, 8/8): full CRUD round-trip with cleanup tracking, membership round-trips from both the group side and the user side (users.updateById + groupIdsToAdd — completes the RBAC grant/revoke flow), members pagination against a built-in group
  • E2E: runtime validation through packed dist bundles (create with members → bound update/delete → user-side membership)

Docs

docs/oauth-scopes.md (PM.Group), docs/pagination.md (getMembers), mkdocs.yml nav.

Stack

🤖 Generated with Claude Code

Adds PlatformGroupService (exported as Groups) to the /platform subpath:

- getAll(organizationId) — all local and built-in groups
- getById(groupId, organizationId)
- create(name, organizationId, options?) — the API requires a
  client-generated group GUID, so the SDK generates it; initial members
  via memberUserIds
- updateById(groupId, organizationId, name, options?) — the API
  requires the name on every update (409 without it, live-verified), so
  it is a required positional; membership edits via memberUserIdsToAdd/
  memberUserIdsToRemove; the bound group.update() auto-fills the
  current name
- deleteById(groupId, organizationId)
- getMembers(groupId, organizationId, options?) — paged member
  references with fetch-all default

Groups are enriched with organizationId (not on the wire) so bound
update/delete/getMembers capture full context. Numeric GroupType codes
mapped to enums; members/mappedRole/scope dropped from the public
shape (members is always empty on the wire — membership is served by
getMembers).

Verified against the live API: 2389 unit tests passing, integration
8/8 (full CRUD, membership round-trips from both group and user side,
pagination), runtime E2E through packed dist bundles.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://UiPath.github.io/uipath-typescript/pr-preview/pr-664/

Built to branch gh-pages at 2026-08-13 03:26 UTC.
Preview will be ready when the GitHub Pages deployment is complete.


await expect(group.delete()).rejects.toThrow('Group ID is undefined');
expect(mockService.deleteById).not.toHaveBeenCalled();
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing organizationId validation test for the delete bound method. The implementation guards both groupData.id and groupData.organizationId, but only the first guard is exercised here. Convention: "Test both success and error scenarios for every public method."

Suggested change
});
});
it('should throw when the organization ID is missing', async () => {
const group = createPlatformGroupWithMethods(createTransformedGroup({ organizationId: '' }), mockService);
await expect(group.delete()).rejects.toThrow('Group organization ID is undefined');
expect(mockService.deleteById).not.toHaveBeenCalled();
});
});


await expect(group.getMembers()).rejects.toThrow('Group ID is undefined');
expect(mockService.getMembers).not.toHaveBeenCalled();
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same gap as delete: getMembers guards organizationId too, but the test only covers the groupId check.

Suggested change
});
});
it('should throw when the organization ID is missing', async () => {
const group = createPlatformGroupWithMethods(createTransformedGroup({ organizationId: '' }), mockService);
await expect(group.getMembers()).rejects.toThrow('Group organization ID is undefined');
expect(mockService.getMembers).not.toHaveBeenCalled();
});
});

@claude

claude Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Two missing error-scenario tests in the model test file; everything else looks good.

New findings this run:

  • tests/unit/models/platform/groups.test.ts line 114 — delete bound method: the organizationId guard is implemented but untested
  • tests/unit/models/platform/groups.test.ts line 136 — getMembers bound method: same gap

Both inline comments include a drop-in suggestion. Service-level and integration tests are complete; the transform pipeline, wire-name mapping, fetch-all loop, GUID generation, pagination, JSDoc, and docs pages are all convention-compliant.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant