Skip to content

feat(platform): add Directory service for principal lookups - #665

Open
Sarath1018 wants to merge 1 commit into
feat/platform-groupsfrom
feat/platform-directory
Open

feat(platform): add Directory service for principal lookups#665
Sarath1018 wants to merge 1 commit into
feat/platform-groupsfrom
feat/platform-directory

Conversation

@Sarath1018

Copy link
Copy Markdown
Collaborator

Summary

PR 3/4 in the platform RBAC stack (Users → Groups → Directory → Roles). Stacked on #664 — merge order: #663, #664, then this; the diff here is Directory-only.

Adds PlatformDirectoryService (exported as Directory) to the /platform subpath — the read-only lookup layer over an organization's principals.

Method Endpoint Notes
search(organizationId, options?) GET /identity_/api/Directory/Search/{org} Filters: startsWith, entityType (user/group/application), sources (local/directory users/groups, robot accounts, applications)
getGroupMembership(userId, groupIds, organizationId) POST /identity_/api/Directory/GroupMembership/{org} Returns the subset of groupIds the user belongs to — the RBAC membership check ("is this user an admin?")

Design decisions (validated against the live API)

  • Renames: identifierid, identityNamename (SDK-wide naming); redundant objectType discriminator dropped
  • Numeric enum codes: entity type arrives as 0/1/2 despite the spec's string enum — mapped to PlatformDirectoryEntityType
  • Two raw wire types: the two endpoints return differently-shaped principal objects (identityName vs name) — typed independently per the wire-format-first convention
  • Read-only service: no bound methods / Methods interface / model test, per the read-only services convention
  • No pagination: both endpoints return plain arrays (verified, not assumed)

Testing

  • Unit: 2403 passing (18 new: transforms, enum codes, wire param names incl. sourcessourceFilter, empty results, all validation branches)
  • Integration (live): deterministic probe group with a known member; member/non-member/subset assertions; transform validation against live responses. All four platform suites green in a parallel run (31/31)
  • Also hardens the Users suite against parallel-file races on the shared mutable test user (membership-survival assertion instead of exact snapshot equality)

Docs

docs/oauth-scopes.md (PM.Directory.Read — directory has no parent/write scope), mkdocs.yml nav. No pagination.md entry (plain arrays).

Stack

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Aug 13, 2026

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-665/

Built to branch gh-pages at 2026-09-10 19:15 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

* '<organizationId>'
* );
* const isAdmin = memberships.length > 0;
* ```

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.

The getGroupMembership example uses directory without showing where it comes from — a user copying this block can't run it. search() correctly opens with full import + setup boilerplate; this one should too.

Suggested change
* ```
* ```typescript
* import { UiPath } from '@uipath/uipath-typescript/core';
* import { Directory } from '@uipath/uipath-typescript/platform';
*
* const sdk = new UiPath(config);
* await sdk.initialize();
*
* const directory = new Directory(sdk);
* const memberships = await directory.getGroupMembership(
* '<userId>',
* ['<adminGroupId>'],
* '<organizationId>'
* );
* const isAdmin = memberships.length > 0;
* ```


it('should return only the subset of groups the user belongs to', async () => {
const allGroups = await groups.getAll(organizationId);
const everyone = allGroups.find((g) => g.name === 'Everyone')!;

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.

Non-null assertion here produces a cryptic TypeError: Cannot read properties of undefined (reading 'id') if the Everyone group doesn't exist. Per integration test convention, preconditions must use an explicit throw new Error(...).

Suggested change
const everyone = allGroups.find((g) => g.name === 'Everyone')!;
const everyone = allGroups.find((g) => g.name === 'Everyone');
if (!everyone) throw new Error('Expected an "Everyone" group in the organization — check test environment setup');

@claude

claude Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Review findings

Two issues found, both actionable:

1. getGroupMembership JSDoc example not self-contained (line 84, src/models/platform/directory.models.ts): the single example uses directory without any import or setup block. search() correctly shows the full boilerplate in its first example; getGroupMembership should too — per convention, first examples must be copy-pasteable standalone.

2. Unguarded non-null assertion in integration test (line 114, tests/integration/shared/platform/directory.integration.test.ts): allGroups.find((g) => g.name === 'Everyone')! produces a cryptic TypeError if the group is absent. Convention requires an explicit throw new Error(...) for missing test preconditions.

Adds PlatformDirectoryService (exported as Directory) to the /platform
subpath — the read-only lookup layer over an organization's principals:

- search(organizationId, options?) — find users, groups, and
  applications by name prefix, entity type, and source
- getGroupMembership(userId, groupIds, organizationId) — returns the
  subset of the given groups the user belongs to; the membership check
  behind RBAC gating ("is this user in the Administrators group?")

Transforms: identifier→id and identityName→name renames, redundant
objectType dropped, numeric entity-type codes mapped to enums (live
API sends codes despite the spec declaring strings). Read-only service
— no bound methods, per convention.

Also hardens the Users integration suite against parallel-file races:
sibling suites mutate the shared test user's group memberships, so the
replace-semantics assertion now checks membership survival across the
update call instead of exact snapshot equality.

Verified against the live API: 2403 unit tests passing, all four
platform integration suites green in a parallel run (31/31).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Sarath1018
Sarath1018 force-pushed the feat/platform-directory branch from e954823 to cf87e11 Compare September 10, 2026 19:14
@claude

claude Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

✅ No issues found. Checked for bugs and CLAUDE.md compliance.

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