-
Notifications
You must be signed in to change notification settings - Fork 12
feat(functions): add Functions service for invoking coded functions [PLT-106037] #614
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
amrit-agarwal-1
wants to merge
5
commits into
main
Choose a base branch
from
feat/sdk-plt-106037
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
4bc5c33
feat(functions): add Functions service for invoking coded functions […
amrit-agarwal-1 f0026ee
feat(functions): long-running invoke support and parent-job attributi…
amrit-agarwal-1 25527d1
refactor(functions): single invoke path, package filtering, better lo…
amrit-agarwal-1 b2eab8c
refactor(functions): remove dead code from the Functions PR [PLT-106037]
amrit-agarwal-1 12cad94
refactor(functions): test hygiene, mock types and doc comments [PLT-1…
amrit-agarwal-1 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| /** | ||
| * OData query rewrite map for Functions (API field → SDK field), reversed by | ||
| * `transformOptions()` so callers can use SDK field names in `filter`, | ||
| * `orderby`, `select`, and `expand`. | ||
| * | ||
| * Responses are reshaped by `toFunctionResponse()` instead, which flattens the | ||
| * nested `Release` entity and drops the job-runner fields a map cannot express. | ||
| * Semantic renames only — case conversion is handled by `pascalToCamelCaseKeys()`. | ||
| */ | ||
| export const FunctionMap = { | ||
| organizationUnitId: 'folderId', | ||
| organizationUnitFullyQualifiedName: 'folderName', | ||
| releaseKey: 'processKey', | ||
| // The package fields are flattened out of the nested `Release` entity, so the | ||
| // API names are navigation paths. Keeps `filter: "processName eq '...'"` working. | ||
| 'Release/Name': 'processName', | ||
| 'Release/Slug': 'processSlug', | ||
| } as const; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| /** | ||
| * Internal types for the Functions service — raw API wire formats before | ||
| * transformation. Not exported through the public barrel. | ||
| */ | ||
|
|
||
| /** | ||
| * Raw HTTP trigger row from `GET /odata/HttpTriggers` after | ||
| * `pascalToCamelCaseKeys()`. Only the fields consumed by the service are | ||
| * declared; the API returns many more job-runner fields that the SDK drops. | ||
| */ | ||
| export interface RawFunctionTrigger { | ||
| /** Trigger identifier (GUID). */ | ||
| id: string; | ||
| /** Trigger name — unique within a folder. */ | ||
| name: string; | ||
| /** URL path segment within the package. */ | ||
| slug: string; | ||
| /** HTTP verb, e.g. `Post`. */ | ||
| method: string; | ||
| /** Description from the function definition. */ | ||
| description?: string | null; | ||
| /** Whether the trigger is enabled. */ | ||
| enabled: boolean; | ||
| /** Default input arguments as a JSON string. */ | ||
| inputArguments?: string | null; | ||
| /** Source file path inside the package. */ | ||
| entryPointPath?: string | null; | ||
| /** Key (GUID) of the release that owns the trigger. */ | ||
| releaseKey: string; | ||
| /** Numeric ID of the folder the trigger lives in. */ | ||
| organizationUnitId: number; | ||
| /** Fully qualified folder name. */ | ||
| organizationUnitFullyQualifiedName?: string | null; | ||
| /** Release (process) that packages the function. */ | ||
| release: { | ||
| name: string; | ||
| slug: string; | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * Raw folder entity from `GET /odata/Folders({id})` — PascalCase wire format, | ||
| * limited to the field the Functions service consumes. | ||
| */ | ||
| export interface RawFolderResponse { | ||
| /** Folder key (GUID) — the `t/{key}` segment of the function invoke URL. */ | ||
| Key: string; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,188 @@ | ||
| import { FunctionGetAllOptions, FunctionInvokeOptions, RawFunctionGetResponse } from './functions.types'; | ||
| import { PaginatedResponse, NonPaginatedResponse, HasPaginationOptions } from '../../utils/pagination'; | ||
|
|
||
| /** Combined response type for function data with bound methods. */ | ||
| export type FunctionGetResponse = RawFunctionGetResponse & FunctionMethods; | ||
|
|
||
| /** | ||
| * Service for invoking UiPath Coded Functions. | ||
| * | ||
| * Coded functions are lightweight units of TypeScript/JavaScript or Python code | ||
| * deployed to UiPath and executed on demand. Each function is packaged as a | ||
| * process, exposed through an HTTP endpoint, and uniquely named within its folder. | ||
| * This service lets you discover deployed functions and invoke them with typed | ||
| * input and output — without dealing with the underlying process and trigger plumbing. | ||
| * | ||
| * ### Usage | ||
| * | ||
| * Prerequisites: Initialize the SDK first - see [Getting Started](/uipath-typescript/getting-started/#import-initialize) | ||
| * | ||
| * ```typescript | ||
| * import { Functions } from '@uipath/uipath-typescript/functions'; | ||
| * | ||
| * const functions = new Functions(sdk); | ||
| * const result = await functions.invoke('my-function', { amount: 42 }, { folderId: <folderId> }); | ||
| * ``` | ||
| */ | ||
| export interface FunctionServiceModel { | ||
| /** | ||
| * Gets all functions in a folder with optional filtering and pagination. | ||
| * | ||
| * Returns each function's identity (name, slug, HTTP method), state, and | ||
| * packaging details, with an {@link FunctionMethods.invoke | invoke} method | ||
| * bound to each item. Folder context is required — pass one of `folderId`, | ||
| * `folderKey`, or `folderPath` in the options, or initialize the SDK with a | ||
| * folder context. | ||
| * | ||
| * One package can expose several functions, so filter on `processName`, | ||
| * `processSlug`, or `processKey` to narrow the result to a single package. | ||
| * | ||
| * @param options - Query options including folder scoping (`folderId` / `folderKey` / `folderPath`), filtering, and pagination options | ||
| * @returns Promise resolving to either an array of functions {@link NonPaginatedResponse}<{@link FunctionGetResponse}> or a {@link PaginatedResponse}<{@link FunctionGetResponse}> when pagination options are used. | ||
| * @example | ||
| * ```typescript | ||
| * // Get all functions in a folder | ||
| * const deployed = await functions.getAll({ folderId: <folderId> }); | ||
| * | ||
| * // By folder path | ||
| * const shared = await functions.getAll({ folderPath: 'Shared/Finance' }); | ||
| * | ||
| * // With filtering | ||
| * const enabled = await functions.getAll({ | ||
| * folderId: <folderId>, | ||
| * filter: 'enabled eq true', | ||
| * orderby: 'name asc', | ||
| * }); | ||
| * | ||
| * // Only the functions deployed from one package | ||
| * const fromPackage = await functions.getAll({ | ||
| * folderId: <folderId>, | ||
| * filter: "processName eq 'my-functions'", | ||
| * }); | ||
| * | ||
| * // First page with pagination | ||
| * const page1 = await functions.getAll({ folderId: <folderId>, pageSize: 10 }); | ||
| * | ||
| * // Navigate using cursor | ||
| * if (page1.hasNextPage) { | ||
| * const page2 = await functions.getAll({ folderId: <folderId>, cursor: page1.nextCursor }); | ||
| * } | ||
| * ``` | ||
| */ | ||
| getAll<T extends FunctionGetAllOptions = FunctionGetAllOptions>( | ||
| options?: T | ||
| ): Promise< | ||
| T extends HasPaginationOptions<T> | ||
| ? PaginatedResponse<FunctionGetResponse> | ||
| : NonPaginatedResponse<FunctionGetResponse> | ||
| >; | ||
|
|
||
| /** | ||
| * Invokes a function by name and returns its output. | ||
| * | ||
| * The call is synchronous — it resolves with the function's output. Coded | ||
| * functions exposed over HTTP are request/response only, so a run that exceeds | ||
| * the platform's execution limit fails rather than continuing in the background. | ||
| * | ||
| * Type the input and output by supplying the generics — they should match the | ||
| * input/output schema the function declares. Folder context is required — pass | ||
| * one of `folderId`, `folderKey`, or `folderPath` in the options, or initialize | ||
| * the SDK with a folder context. | ||
| * | ||
| * @param name - Name of the function to invoke (unique within a folder) | ||
| * @param input - Input for the function, sent as the request body (or as query | ||
| * parameters for functions declared with the `Get` method). Defaults to an empty object. | ||
| * @param options - Folder scoping (`folderId` / `folderKey` / `folderPath`) and | ||
| * parent job attribution (`jobKey`) | ||
| * @returns Promise resolving to the function's output | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * // Invoke a function | ||
| * const result = await functions.invoke('hello', { name: 'Alice' }, { folderId: <folderId> }); | ||
| * ``` | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * // Typed input and output, attributed to a parent job's licensing transaction | ||
| * interface SyncInput { since: string } | ||
| * interface SyncOutput { processed: number } | ||
| * | ||
| * const result = await functions.invoke<SyncInput, SyncOutput>( | ||
| * 'sync-invoices', | ||
| * { since: '2026-01-01' }, | ||
| * { folderPath: 'Shared/Finance', jobKey: '<parentJobKey>' } | ||
| * ); | ||
| * console.log(result.processed); | ||
| * ``` | ||
| */ | ||
| invoke<TInput extends object = Record<string, unknown>, TOutput = unknown>( | ||
| name: string, | ||
| input?: TInput, | ||
| options?: FunctionInvokeOptions | ||
| ): Promise<TOutput>; | ||
| } | ||
|
|
||
| /** | ||
| * Methods available on function response objects. | ||
| * These are bound to the function data and delegate to the service. | ||
| */ | ||
| export interface FunctionMethods { | ||
| /** | ||
| * Invokes this function and returns its output. | ||
| * | ||
| * @param input - Input for the function, sent as the request body (or as query | ||
| * parameters for functions declared with the `Get` method). Defaults to an empty object. | ||
| * @returns Promise resolving to the function's output | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * const deployed = await functions.getAll({ folderId: <folderId> }); | ||
| * const hello = deployed.items.find(f => f.name === 'hello'); | ||
| * | ||
| * if (hello) { | ||
| * const result = await hello.invoke({ name: 'Alice' }); | ||
| * } | ||
| * ``` | ||
| */ | ||
| invoke<TInput extends object = Record<string, unknown>, TOutput = unknown>( | ||
| input?: TInput | ||
| ): Promise<TOutput>; | ||
| } | ||
|
|
||
| /** | ||
| * Creates methods for a function response object. | ||
| * | ||
| * @param functionData - The transformed function data | ||
| * @param service - The function service instance | ||
| * @returns Object containing function methods | ||
| */ | ||
| function createFunctionMethods( | ||
| functionData: RawFunctionGetResponse, | ||
| service: FunctionServiceModel | ||
| ): FunctionMethods { | ||
| return { | ||
| async invoke<TInput extends object = Record<string, unknown>, TOutput = unknown>( | ||
| input?: TInput | ||
| ): Promise<TOutput> { | ||
| if (!functionData.name) throw new Error('Function name is undefined'); | ||
| if (!functionData.folderId) throw new Error('Function folderId is undefined'); | ||
| return service.invoke<TInput, TOutput>(functionData.name, input, { folderId: functionData.folderId }); | ||
| }, | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * Creates a function response with bound methods. | ||
| * | ||
| * @param functionData - The transformed function data | ||
| * @param service - The function service instance | ||
| * @returns A function object with added methods | ||
| */ | ||
| export function createFunctionWithMethods( | ||
| functionData: RawFunctionGetResponse, | ||
| service: FunctionServiceModel | ||
| ): FunctionGetResponse { | ||
| const methods = createFunctionMethods(functionData, service); | ||
| return Object.assign({}, functionData, methods); | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Orphaned
{@link}tag — this line renders as stray text (or a dangling link) in TypeDoc output. It appears to be a copy-paste artifact; the@returnsdescription on the line above already includes the two relevant links. Remove this line.