feat(orchestrator): getByName for Assets + Processes, encoded folder header, meta-tag fallback [PLT-92768]#381
feat(orchestrator): getByName for Assets + Processes, encoded folder header, meta-tag fallback [PLT-92768]#381deepeshrai-tech wants to merge 5 commits into
Conversation
…[PLT-92768]
Adds assets.getByName(name, { folderPath?, folderKey? }) — resolves a single
asset by name, scoped via X-UIPATH-FolderPath / X-UIPATH-FolderKey headers
(server-side folder resolution, no client-side ID lookup needed). Either
or both may be provided; Orchestrator middleware prefers folderPath when
both are sent.
Prerequisite for the coded-apps bindings/overwrites work — SDK now has
the name-based lookup that uipath.json runtime overwrites need.
Also adds samples/asset-by-name-app — React + Vite demo of OAuth login,
folder-scoped asset listing, and getByName invocation with the JSON response
rendered in-page.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| /** | ||
| * Folder path to scope the asset lookup (e.g. 'Shared/Finance'). | ||
| * Mutually exclusive with folderKey. | ||
| */ | ||
| folderPath?: string; | ||
| /** | ||
| * Folder key (GUID) to scope the asset lookup. | ||
| * Mutually exclusive with folderPath. | ||
| */ | ||
| folderKey?: string; |
There was a problem hiding this comment.
The "Mutually exclusive" wording conflicts with the implementation and the PR description.
The PR description says: "Both folderPath and folderKey are accepted; Orchestrator middleware prefers folderPath when both are sent, so the SDK passes them straight through." The getByName implementation confirms this — it calls createHeaders({ [FOLDER_PATH]: folderPath, [FOLDER_KEY]: folderKey }) and sends whatever is non-undefined.
Calling them mutually exclusive tells users one of them will be ignored or that passing both is invalid, when it's actually valid (and the server's preference behaviour is the documented tie-breaker).
Suggested replacement:
| /** | |
| * Folder path to scope the asset lookup (e.g. 'Shared/Finance'). | |
| * Mutually exclusive with folderKey. | |
| */ | |
| folderPath?: string; | |
| /** | |
| * Folder key (GUID) to scope the asset lookup. | |
| * Mutually exclusive with folderPath. | |
| */ | |
| folderKey?: string; | |
| export interface AssetGetByNameOptions extends BaseOptions { | |
| /** | |
| * Folder path to scope the asset lookup (e.g. 'Shared/Finance'). | |
| * Sent as the `X-UIPATH-FolderPath` header. When provided alongside | |
| * `folderKey`, the server prefers `folderPath`. | |
| */ | |
| folderPath?: string; | |
| /** | |
| * Folder key (GUID) to scope the asset lookup. | |
| * Sent as the `X-UIPATH-FolderKey` header. When provided alongside | |
| * `folderPath`, the server prefers `folderPath`. | |
| */ | |
| folderKey?: string; | |
| } |
| * const asset = await assets.getByName('ApiKey'); | ||
| * ``` | ||
| */ | ||
| @track('Assets.GetByName') |
There was a problem hiding this comment.
Per the post-implementation checklist in agent_docs/rules.md, every public method requires unit tests (success + error paths) and an integration test in tests/integration/shared/orchestrator/ before merging.
The "Not-done list" in the PR description calls these out explicitly — flagging here so this doesn't slip through on merge. Minimum needed:
Unit tests (tests/unit/services/orchestrator/assets.test.ts):
getByNamereturns a transformed asset when the OData response has one itemgetByNamethrowsNotFoundErrorwhen thevaluearray is emptygetByNamesendsX-UIPATH-FolderPath/X-UIPATH-FolderKeyheaders (only those provided)getByNameOData-escapes single quotes in the name (Name eq 'O''Brien')
Integration test (tests/integration/shared/orchestrator/assets.test.ts):
getByNameresolves an existing asset by name + folderPath- transform validation: camelCase fields present, PascalCase originals absent
| name: asset.name, | ||
| folderPath: folder.fullyQualifiedName || folder.displayName, | ||
| folderKey: folder.key, | ||
| valueType: asset.valueType as unknown as string, |
There was a problem hiding this comment.
as unknown as string is explicitly banned by conventions.md ("NEVER use as unknown as type casts — refactor to make types flow naturally"). Sample apps are part of the repo and set the example for SDK consumers.
asset.valueType is AssetValueType (a string enum), so it already is a string at runtime. The fix is to widen the AssetRow.valueType field type to AssetValueType (preferred, preserves autocomplete) or cast with the direct widening asset.valueType as string which TypeScript allows between a string enum and string without the double cast:
| valueType: asset.valueType as unknown as string, | |
| valueType: asset.valueType, |
and change AssetRow.valueType from string to AssetValueType.
Review summary — 3 new findings1. Doc inaccuracy in AssetGetByNameOptions (assets.types.ts L76–85) 2. Missing unit and integration tests (assets.ts L147) 3. Banned double cast in sample code (AssetList.tsx L76) |
… meta-tag fallback [PLT-92768] Builds on the assets.getByName work with parity for Processes plus three refinements the OR team + APPS team aligned on: - Switched folder-path header to X-UIPATH-FolderPath-Encoded (URL-encoded value) per OR's recommendation for safe transport of special characters. FOLDER_PATH constant dropped from headers.ts — encoded form only. - Added init-time folderKey fallback. The SDK now reads <meta name="uipath:folder-key"> (injected automatically by jamjam on coded-app deployments) and uses it as the default folder context for getByName calls that don't pass folderPath/folderKey at the call site. Chain: call-site args > SDK init config > meta tag > no folder. Config flows through BaseConfig -> UiPathConfig -> BaseService.config so services can read it. - Added input validation at the SDK boundary. New utils/validation/name-validator.ts rejects non-string names, empty names, and non-string folder fields with ValidationError carrying descriptive messages. Not-found errors now include the folder context (path or key) to help debugging. JS callers get the runtime guard that TS types alone can't enforce. Sample app (asset-by-name-app) reorganized with per-resource folders: Assets | Processes tab nav, shared GetByNameForm component, and a Processes list + form exercising the new method. Sample's scope in .env.example bumped to include OR.Execution.Read. A demo folder-key meta tag added to index.html so the tier-3 fallback can be verified in the browser without redeploying. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…eader [PLT-92768]
Adds parity Buckets.getByName(name, { folderPath?, folderKey? }) following
the same shape as Assets/Processes (validator + encoded folder header +
init-time folderKey fallback).
Critical fix: the X-UIPATH-FolderPath-Encoded header was using
encodeURIComponent (URL-encoded UTF-8) — but Orchestrator decodes this
header as base64 of UTF-16 LE bytes (Encoding.Unicode in
HttpHeadersProviderExtensions.GetDecoded). The mismatch caused malformed
folder paths to be silently rejected; Assets and Processes appeared to
work because their controllers fall back to other folder-resolution
sources (cookies/query/session). Buckets controller has stricter
[RequireOrganizationUnit] enforcement which surfaced the bug.
Added utils/encoding/folder-path.ts with the correct UTF-16-LE + Base64
encoder, applied across all three services. Encoder mirrors the reference
implementation in the Orchestrator perf-test repo
(performance/framework-modules/test-utils.ts).
Sample app gains a Buckets tab with the same list + getByName pattern.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…art B2 options shape [PLT-92768] Mirrors the refactor that landed on feat/resources-get-by-name (PR #386): - Adds shared FolderScopedOptions to common/types.ts - Extracts FolderScopedService.getByNameLookup<TRaw, T>() — handles validation, encoding, header construction, OData filter, NotFoundError — so each service's getByName collapses to an 8-line wrapper - AssetGetByNameOptions / ProcessGetByNameOptions / BucketGetByNameOptions now extend FolderScopedOptions - ProcessService now extends FolderScopedService (was BaseService) processes.start refactored to the B2 options shape: - New ProcessStartOptions extends RequestOptions with folderId/folderPath/folderKey - start(request, options?: ProcessStartOptions) is the new preferred shape - Legacy positional start(request, folderId: number, options?) still works — runtime-branched on typeof second arg, so existing callers and the 1152 unit tests keep passing - Folder context resolved per request: folderId → X-UIPATH-OrganizationUnitId, folderPath → X-UIPATH-FolderPath-Encoded, folderKey → X-UIPATH-FolderKey - Falls back to init-time config.folderKey (uipath:folder-key meta tag) only when no folder context is supplied Sample app: Processes tab now exposes two start buttons per row — "Start (path)" and "Start (key)" — exercising each folder header independently. Lets reviewers verify both code paths in the Network tab. .env.example bumped to OR.Execution + OR.Jobs scopes (write access required for processes.start). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds maestroProcesses.getByName(name, options?) and cases.getByName(name, options?) — client-side filter over getAll() because Maestro's /processes/summary endpoint has no name-based lookup or filter parameters (verified via uipcli/packages/maestro-sdk and maestro-tool).
Both methods accept { folderPath?, folderKey? } for API parity with Orchestrator services. folderPath is matched client-side against the folderName field returned by Maestro; folderKey matches folderKey. When neither is supplied, falls back to init-time folderKey (e.g. uipath:folder-key meta tag) — same chain as processes.getByName and buckets.getByName. Throws ValidationError on bad input and NotFoundError when no record matches.
Also adds Maestro processes + Cases tabs to samples/asset-by-name-app for live verification: list view, click-to-prefill row, getByName invocation with the JSON response rendered in-page.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|




Summary
Prerequisite SDK work for the coded-apps bindings/overwrites initiative (PLT-92768 / PLT-93599). Adds name-based resource lookup for two Orchestrator services and wires the folder-context plumbing that the runtime overwrite flow depends on.
assets.getByName(name, { folderPath?, folderKey? })— OData$filter=Name eq '…'+$top=1on the folder-scoped endpoint.processes.getByName(name, { folderPath?, folderKey? })— same shape against/odata/Releases.X-UIPATH-FolderPath-Encodedreplaces the rawX-UIPATH-FolderPathheader (OR team's recommendation; URL-encoded transport is safer for special characters in paths). BothfolderPathandfolderKeyare accepted simultaneously — OR's middleware prefersfolderPath.<meta name="uipath:folder-key">(auto-injected by jamjam on coded-app deployments) and uses it as the default folder context when the caller doesn't pass one. Resolution chain: call-site args >new UiPath({ folderKey })> meta tag > no folder header.utils/validation/name-validator.tsthrowsValidationErrorwith descriptive messages for non-string names, empty names, and non-string folder fields — the guard TS types can't enforce for JS callers. Not-found errors now include the folder context (path or key) to make debugging easierUsage
Not-done / follow-ups
getByName(this PR is a draft for visual verification first)docs/oauth-scopes.mdupdategetByNameon Buckets / Queues — tracked separatelyScreen.Recording.2026-04-21.at.5.57.59.PM.mov