Skip to content

fix(maestro-processes): update getExecutionHistory to use element-executions and Traces APIs [PLT-97257]#428

Open
Sarath1018 wants to merge 8 commits into
mainfrom
sarath/getExecutionHistory
Open

fix(maestro-processes): update getExecutionHistory to use element-executions and Traces APIs [PLT-97257]#428
Sarath1018 wants to merge 8 commits into
mainfrom
sarath/getExecutionHistory

Conversation

@Sarath1018
Copy link
Copy Markdown
Contributor

@Sarath1018 Sarath1018 commented May 12, 2026

Method Added

Layer Method Signature
Service processInstances.getExecutionHistory() getExecutionHistory(instanceId: string, options: ProcessInstanceGetExecutionHistoryOptions): Promise<ProcessInstanceExecutionHistoryResponse[]>

Endpoint Called

Method HTTP Endpoint OAuth Scope
getExecutionHistory() GET pims_/api/v1/instances/{instanceId}/element-executions PIMS
getExecutionHistory() GET llmopstenant_/api/Traces/spans?traceId={traceId} Traces.Api
Sample SDK response (live API)
[
  {
    "id": "00000000-0000-0000-0b6a-051ca7aec9ce",
    "traceId": "910e3f56-1fbd-4947-9cee-148355c38d08",
    "parentId": "00000000-0000-0000-9b54-950bbe5a2e63",
    "name": "Start event",
    "startedTime": "2026-05-12T11:48:06.077714Z",
    "endTime": "2026-05-12T11:48:06.8733245Z",
    "attributes": "{\"spanType\":\"ElementRun\",\"status\":\"Completed\",\"elementId\":\"Event_start\",\"elementRunId\":\"88952b06-da40-4ab5-8cf3-2fecfbc53164\",\"elementType\":\"StartEvent\",\"runIndex\":1,...}",
    "updatedTime": "2026-05-12T11:48:07.1584589Z",
    "expiredTime": null
  },
  {
    "id": "00000000-0000-0000-42ef-2088c0ac72ac",
    "traceId": "910e3f56-1fbd-4947-9cee-148355c38d08",
    "parentId": "00000000-0000-0000-9b54-950bbe5a2e63",
    "name": "Activity_tblfOr",
    "startedTime": "2026-05-12T11:48:07.4381836Z",
    "endTime": "2026-05-12T11:48:07.7687194Z",
    "attributes": "{\"spanType\":\"ElementRun\",\"status\":\"Completed\",\"elementId\":\"Activity_tblfOr\",\"elementRunId\":\"c0322c6a-16fb-4232-bee2-8811445c16ca\",\"elementType\":\"Task\",\"runIndex\":1,...}",
    "updatedTime": "2026-05-12T11:48:08.2378818Z",
    "expiredTime": null
  },
  {
    "id": "00000000-0000-0000-38ec-7544ef9d965d",
    "traceId": "910e3f56-1fbd-4947-9cee-148355c38d08",
    "parentId": "00000000-0000-0000-9b54-950bbe5a2e63",
    "name": "Event_lLnUK8",
    "startedTime": "2026-05-12T11:48:08.5335271Z",
    "endTime": "2026-05-12T11:48:09.1996998Z",
    "attributes": "{\"spanType\":\"ElementRun\",\"status\":\"Completed\",\"elementId\":\"Event_lLnUK8\",\"elementRunId\":\"23f305b7-ffbf-4d0b-bba7-5fe2b4127aab\",\"elementType\":\"EndEvent\",\"runIndex\":1,...}",
    "updatedTime": "2026-05-12T11:48:09.4081965Z",
    "expiredTime": null
  }
]

Example Usage

import { UiPath } from '@uipath/uipath-typescript/core';
import { ProcessInstances } from '@uipath/uipath-typescript/maestro-processes';

const sdk = new UiPath(config);
await sdk.initialize();
const processInstances = new ProcessInstances(sdk);

// By folder key
const history = await processInstances.getExecutionHistory(
  '<instanceId>',
  { folderKey: '<folderKey>' }
);

// Analyze execution timeline
history.forEach(span => {
  console.log(`Activity: ${span.name}`);
  console.log(`Start: ${span.startedTime}`);
});

API Response vs SDK Response

Composition flow

getExecutionHistory(instanceId, options)
  │
  ├─ resolveFolderHeaders(options) → folder headers
  │
  ├─ GET pims_/api/v1/instances/{instanceId}/element-executions
  │     → ElementExecutionsApiResponse { traceId, elementExecutions[] }
  │
  ├─ GET llmopstenant_/api/Traces/spans?traceId={traceId}
  │     → TraceSpan[] (PascalCase fields; elementRunId inside Attributes JSON)
  │
  └─ Merge: build map of Attributes.elementRunId → span
            for each elementExecution.elementRun, look up by elementRunId
            map matched spans → ProcessInstanceExecutionHistoryResponse[]
            (spans without elementRunId in Attributes dropped — e.g. root ProcessRun span)

Fixes #265

🤖 Auto-generated using onboarding skills

@Sarath1018 Sarath1018 requested a review from a team May 12, 2026 05:52
Comment thread src/models/maestro/process-instances.models.ts
Comment thread tests/integration/shared/maestro/process-instances.integration.test.ts Outdated
@claude

This comment was marked as resolved.

Comment thread src/models/maestro/process-instances.models.ts Outdated
@claude

This comment was marked as resolved.

Sarath1018 and others added 6 commits May 13, 2026 17:39
…cutions and Traces APIs

Replaces the single spans endpoint with a two-call flow: first fetches
element-executions to get structural data and traceId, then fetches
Traces/spans and merges results by elementRunId. Adds folderKey param
for authorization on both calls.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…xecutionHistory

Replaces positional folderKey string param with ProcessInstanceGetExecutionHistoryOptions
extending FolderScopedOptions (folderKey/folderId/folderPath). Uses resolveFolderHeaders
for consistent folder context resolution, matching the pattern from PR #386.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… error handling

- Correct @example: replace non-existent span.startTime/duration with
  span.startedTime/endTime; add missing @param for options
- Integration test: throw instead of console.log+return on missing
  precondition; remove try/catch so errors propagate naturally

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ceId lookup

- Use response.data.traceId (not instanceId) for the Traces/spans call
- Match spans by parsing Attributes JSON for elementRunId instead of span.Id
- Add traceId field to ElementExecutionsApiResponse internal type
- Fix integration test to use config.folderKey (not folderId) and add
  field-level assertions on the response shape
- Update mock traceId and Attributes to match real API structure
- Update oauth-scopes.md: getExecutionHistory requires PIMS + Traces.Api

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Allows calling without folder context when SDK is initialized with a
meta-tag folder key (coded-app deployments). resolveFolderHeaders
handles the fallback. Bound method passes folderKey if present on
instance data, undefined otherwise.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Sarath1018 Sarath1018 force-pushed the sarath/getExecutionHistory branch from 07f84f9 to aef9191 Compare May 13, 2026 12:09
Comment thread src/services/maestro/processes/process-instances.ts
@claude

This comment was marked as resolved.

… in service class

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
name: span.Name,
startedTime: span.StartTime,
endTime: span.EndTime,
attributes: span.Attributes,
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

should we parse attributes as this is a json string?


try {
const result = await processInstances.getExecutionHistory(testInstanceId);
const result = await processInstances.getExecutionHistory(testInstanceId, { folderKey: config.folderKey || '' });
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.

Per convention: "Always throw new Error() when test preconditions are not met — whether it's missing config (e.g., no folderId) or missing test data." config.folderKey || '' silently passes an empty string when folderKey is not configured. This changes folder-resolution behaviour in resolveFolderHeaders (an empty string is not the same as undefined) and masks an unrunnable test.

Suggested change
const result = await processInstances.getExecutionHistory(testInstanceId, { folderKey: config.folderKey || '' });
if (!config.folderKey) throw new Error('folderKey not configured — set UIPATH_FOLDER_KEY in test env to run this test');
const result = await processInstances.getExecutionHistory(testInstanceId, { folderKey: config.folderKey });

Comment on lines 239 to +241
expect(result[0]).toHaveProperty('id', MAESTRO_TEST_CONSTANTS.SPAN_ID);
expect(result[0]).toHaveProperty('traceId', MAESTRO_TEST_CONSTANTS.TRACE_ID);
expect(result[0]).toHaveProperty('name', MAESTRO_TEST_CONSTANTS.ACTIVITY_NAME);
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.

Per convention: "Validate transform completeness — verify both: (a) transformed fields have correct values, AND (b) original PascalCase fields are absent. This catches transform regressions that value-only assertions miss."

mapSpanToHistory explicitly maps span.Id → id, span.TraceId → traceId, span.Name → name, span.StartTime → startedTime, etc. The test verifies the camelCase values are present but never checks that the raw PascalCase fields are absent. A regression where mapSpanToHistory accidentally spreads the whole span (exposing Id, TraceId, StartTime, …) would pass these assertions undetected.

Add absence checks after the existing ones:

expect(result[0]).toHaveProperty('id', MAESTRO_TEST_CONSTANTS.SPAN_ID);
expect(result[0]).toHaveProperty('traceId', MAESTRO_TEST_CONSTANTS.TRACE_ID);
expect(result[0]).toHaveProperty('name', MAESTRO_TEST_CONSTANTS.ACTIVITY_NAME);
// transform completeness: raw PascalCase fields must not leak through
expect((result[0] as any).Id).toBeUndefined();
expect((result[0] as any).TraceId).toBeUndefined();
expect((result[0] as any).StartTime).toBeUndefined();

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 13, 2026

Review findings

Two new issues found this run:

1. Integration test silently accepts missing folderKeyprocess-instances.integration.test.ts line 251

config.folderKey || '' passes an empty string when the config value is absent. This changes resolveFolderHeaders behaviour (empty string ≠ undefined) and masks an unrunnable test. Should guard with throw new Error() per convention.

2. Transform completeness not validated — process-instances.test.ts lines 239–241

mapSpanToHistory maps PascalCase span fields (Id, TraceId, Name, StartTime, …) to camelCase. The test checks transformed values are present but never asserts the original PascalCase fields are absent. Per convention, both directions must be verified to catch spread-regressions.

@Sarath1018 Sarath1018 changed the title fix(maestro-processes): update getExecutionHistory to use element-executions and Traces APIs fix(maestro-processes): update getExecutionHistory to use element-executions and Traces APIs [PLT-97257] May 18, 2026
…n getExecutionHistory

Aligns getExecutionHistory with the rest of the ProcessInstances service
(getById, cancel, pause, resume, getBpmn) which all take folderKey as a
required positional argument. The Maestro element-executions endpoint
requires X-UIPATH-FolderKey and does not accept folderId or
X-UIPATH-FolderPath-Encoded, so the FolderScopedOptions surface was
misleading.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Get execution history (spans) for a process instance
* @param instanceId The ID of the instance to get history for
* @param folderKey The folder key for authorization
* @returns Promise<ProcessInstanceExecutionHistoryResponse[]>
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.

Per convention: "Keep JSDoc on service class methods in sync with {Entity}ServiceModel — both the models file and the service implementation file must have identical JSDoc for each public method."

The ProcessInstancesServiceModel interface in process-instances.models.ts has:

 * @returns Promise resolving to execution history
 * {@link ProcessInstanceExecutionHistoryResponse}
 * @example
 * ```typescript
 * // Get execution history for a process instance
 * const history = await processInstances.getExecutionHistory(
 *   <instanceId>,
 *   <folderKey>
 * );
 *
 * // Analyze execution timeline
 * history.forEach(span => {
 *   console.log(`Activity: ${span.name}`);
 *   console.log(`Start: ${span.startedTime}`);
 *   console.log(`End: ${span.endTime}`);
 * });
 * ```

The service implementation has only @returns Promise<ProcessInstanceExecutionHistoryResponse[]> — missing the {@link} and the entire @example block. The service copy must be brought to match the model.

@sonarqubecloud
Copy link
Copy Markdown

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.

getExecutionHistory method for maestro process Instance is returning 404

1 participant