UNPKG

@temporalio/client

Version:
554 lines (553 loc) 27.2 kB
import { status as grpcStatus } from '@grpc/grpc-js'; import { BaseWorkflowHandle, HistoryAndWorkflowId, QueryDefinition, UpdateDefinition, WithWorkflowArgs, Workflow, WorkflowResultType, WorkflowIdConflictPolicy } from '@temporalio/common'; import { History } from '@temporalio/common/lib/proto-utils'; import { temporal } from '@temporalio/proto'; import { WorkflowCancelInput, WorkflowClientInterceptor, WorkflowClientInterceptors, WorkflowDescribeInput, WorkflowQueryInput, WorkflowSignalInput, WorkflowSignalWithStartInput, WorkflowStartInput, WorkflowTerminateInput, WorkflowStartUpdateInput, WorkflowStartUpdateOutput, WorkflowStartUpdateWithStartInput, WorkflowStartUpdateWithStartOutput } from './interceptors'; import { CountWorkflowExecution, DescribeWorkflowExecutionResponse, QueryRejectCondition, RequestCancelWorkflowExecutionResponse, StartWorkflowExecutionRequest, TerminateWorkflowExecutionResponse, WorkflowExecution, WorkflowExecutionDescription, WorkflowExecutionInfo, WorkflowService } from './types'; import { WorkflowOptions, WorkflowSignalWithStartOptions, WorkflowStartOptions, WorkflowUpdateOptions } from './workflow-options'; import { BaseClient, BaseClientOptions, LoadedWithDefaults } from './base-client'; import { WorkflowUpdateStage } from './workflow-update-stage'; /** * A client side handle to a single Workflow instance. * It can be used to start, signal, query, wait for completion, terminate and cancel a Workflow execution. * * Given the following Workflow definition: * ```ts * export const incrementSignal = defineSignal<[number]>('increment'); * export const getValueQuery = defineQuery<number>('getValue'); * export const incrementAndGetValueUpdate = defineUpdate<number, [number]>('incrementAndGetValue'); * export async function counterWorkflow(initialValue: number): Promise<void>; * ``` * * Create a handle for running and interacting with a single Workflow: * ```ts * const client = new WorkflowClient(); * // Start the Workflow with initialValue of 2. * const handle = await client.start({ * workflowType: counterWorkflow, * args: [2], * taskQueue: 'tutorial', * }); * await handle.signal(incrementSignal, 2); * const queryResult = await handle.query(getValueQuery); // 4 * const firstUpdateResult = await handle.executeUpdate(incrementAndGetValueUpdate, { args: [2] }); // 6 * const secondUpdateHandle = await handle.startUpdate(incrementAndGetValueUpdate, { args: [2] }); * const secondUpdateResult = await secondUpdateHandle.result(); // 8 * await handle.cancel(); * await handle.result(); // throws a WorkflowFailedError with `cause` set to a CancelledFailure. * ``` */ export interface WorkflowHandle<T extends Workflow = Workflow> extends BaseWorkflowHandle<T> { /** * Start an Update and wait for the result. * * @throws {@link WorkflowUpdateFailedError} if Update validation fails or if ApplicationFailure is thrown in the Update handler. * @throws {@link WorkflowUpdateRPCTimeoutOrCancelledError} if this Update call timed out or was cancelled. This doesn't * mean the update itself was timed out or cancelled. * @param def an Update definition as returned from {@link defineUpdate} * @param options Update arguments * * @example * ```ts * const updateResult = await handle.executeUpdate(incrementAndGetValueUpdate, { args: [2] }); * ``` */ executeUpdate<Ret, Args extends [any, ...any[]], Name extends string = string>(def: UpdateDefinition<Ret, Args, Name> | string, options: WorkflowUpdateOptions & { args: Args; }): Promise<Ret>; executeUpdate<Ret, Args extends [], Name extends string = string>(def: UpdateDefinition<Ret, Args, Name> | string, options?: WorkflowUpdateOptions & { args?: Args; }): Promise<Ret>; /** * Start an Update and receive a handle to the Update. The Update validator (if present) is run * before the handle is returned. * * @throws {@link WorkflowUpdateFailedError} if Update validation fails. * @throws {@link WorkflowUpdateRPCTimeoutOrCancelledError} if this Update call timed out or was cancelled. This doesn't * mean the update itself was timed out or cancelled. * * @param def an Update definition as returned from {@link defineUpdate} * @param options update arguments, and update lifecycle stage to wait for * * Currently, startUpdate always waits until a worker is accepting tasks for the workflow and the * update is accepted or rejected, and the options object must be at least * ```ts * { * waitForStage: WorkflowUpdateStage.ACCEPTED * } * ``` * If the update takes arguments, then the options object must additionally contain an `args` * property with an array of argument values. * * @example * ```ts * const updateHandle = await handle.startUpdate(incrementAndGetValueUpdate, { * args: [2], * waitForStage: 'ACCEPTED', * }); * const updateResult = await updateHandle.result(); * ``` */ startUpdate<Ret, Args extends [any, ...any[]], Name extends string = string>(def: UpdateDefinition<Ret, Args, Name> | string, options: WorkflowUpdateOptions & { args: Args; waitForStage: 'ACCEPTED'; }): Promise<WorkflowUpdateHandle<Ret>>; startUpdate<Ret, Args extends [], Name extends string = string>(def: UpdateDefinition<Ret, Args, Name> | string, options: WorkflowUpdateOptions & { args?: Args; waitForStage: typeof WorkflowUpdateStage.ACCEPTED; }): Promise<WorkflowUpdateHandle<Ret>>; /** * Get a handle to an Update of this Workflow. */ getUpdateHandle<Ret>(updateId: string): WorkflowUpdateHandle<Ret>; /** * Query a running or completed Workflow. * * @param def a query definition as returned from {@link defineQuery} or query name (string) * * @example * ```ts * await handle.query(getValueQuery); * await handle.query<number, []>('getValue'); * ``` */ query<Ret, Args extends any[] = []>(def: QueryDefinition<Ret, Args> | string, ...args: Args): Promise<Ret>; /** * Terminate a running Workflow */ terminate(reason?: string): Promise<TerminateWorkflowExecutionResponse>; /** * Cancel a running Workflow. * * When a Workflow is cancelled, the root scope throws {@link CancelledFailure} with `message: 'Workflow canceled'`. * That means that all cancellable scopes will throw `CancelledFailure`. * * Cancellation may be propagated to Activities depending on {@link ActivityOptions#cancellationType}, after which * Activity calls may throw an {@link ActivityFailure}, and `isCancellation(error)` will be true (see {@link isCancellation}). * * Cancellation may be propagated to Child Workflows depending on {@link ChildWorkflowOptions#cancellationType}, after * which calls to {@link executeChild} and {@link ChildWorkflowHandle#result} will throw, and `isCancellation(error)` * will be true (see {@link isCancellation}). */ cancel(): Promise<RequestCancelWorkflowExecutionResponse>; /** * Describe the current workflow execution */ describe(): Promise<WorkflowExecutionDescription>; /** * Return a workflow execution's history */ fetchHistory(): Promise<History>; /** * Readonly accessor to the underlying WorkflowClient */ readonly client: WorkflowClient; } /** * This interface is exactly the same as {@link WorkflowHandle} except it * includes the `firstExecutionRunId` returned from {@link WorkflowClient.start}. */ export interface WorkflowHandleWithFirstExecutionRunId<T extends Workflow = Workflow> extends WorkflowHandle<T> { /** * Run Id of the first Execution in the Workflow Execution Chain. */ readonly firstExecutionRunId: string; } /** * This interface is exactly the same as {@link WorkflowHandle} except it * includes the `signaledRunId` returned from `signalWithStart`. */ export interface WorkflowHandleWithSignaledRunId<T extends Workflow = Workflow> extends WorkflowHandle<T> { /** * The Run Id of the bound Workflow at the time of {@link WorkflowClient.signalWithStart}. * * Since `signalWithStart` may have signaled an existing Workflow Chain, `signaledRunId` might not be the * `firstExecutionRunId`. */ readonly signaledRunId: string; } export interface WorkflowClientOptions extends BaseClientOptions { /** * Used to override and extend default Connection functionality * * Useful for injecting auth headers and tracing Workflow executions */ interceptors?: WorkflowClientInterceptors | WorkflowClientInterceptor[]; /** * Should a query be rejected by closed and failed workflows * * @default `undefined` which means that closed and failed workflows are still queryable */ queryRejectCondition?: QueryRejectCondition; } export type LoadedWorkflowClientOptions = LoadedWithDefaults<WorkflowClientOptions>; /** * Options for getting a result of a Workflow execution. */ export interface WorkflowResultOptions { /** * If set to true, instructs the client to follow the chain of execution before returning a Workflow's result. * * Workflow execution is chained if the Workflow has a cron schedule or continues-as-new or configured to retry * after failure or timeout. * * @default true */ followRuns?: boolean; } /** * Options for {@link WorkflowClient.getHandle} */ export interface GetWorkflowHandleOptions extends WorkflowResultOptions { /** * ID of the first execution in the Workflow execution chain. * * When getting a handle with no `runId`, pass this option to ensure some * {@link WorkflowHandle} methods (e.g. `terminate` and `cancel`) don't * affect executions from another chain. */ firstExecutionRunId?: string; } interface WorkflowHandleOptions extends GetWorkflowHandleOptions { workflowId: string; runId?: string; interceptors: WorkflowClientInterceptor[]; /** * A runId to use for getting the workflow's result. * * - When creating a handle using `getHandle`, uses the provided runId or firstExecutionRunId * - When creating a handle using `start`, uses the returned runId (first in the chain) * - When creating a handle using `signalWithStart`, uses the the returned runId */ runIdForResult?: string; } /** * An iterable list of WorkflowExecution, as returned by {@link WorkflowClient.list}. */ export interface AsyncWorkflowListIterable extends AsyncIterable<WorkflowExecutionInfo> { /** * Return an iterable of histories corresponding to this iterable's WorkflowExecutions. * Workflow histories will be fetched concurrently. * * Useful in batch replaying */ intoHistories: (intoHistoriesOptions?: IntoHistoriesOptions) => AsyncIterable<HistoryAndWorkflowId>; } /** * A client-side handle to an Update. */ export interface WorkflowUpdateHandle<Ret> { /** * The ID of this Update request. */ updateId: string; /** * The ID of the Workflow being targeted by this Update request. */ workflowId: string; /** * The ID of the Run of the Workflow being targeted by this Update request. */ workflowRunId?: string; /** * Return the result of the Update. * @throws {@link WorkflowUpdateFailedError} if ApplicationFailure is thrown in the Update handler. */ result(): Promise<Ret>; } /** * Options for {@link WorkflowHandle.getUpdateHandle} */ export interface GetWorkflowUpdateHandleOptions { /** * The ID of the Run of the Workflow targeted by the Update. */ workflowRunId?: string; } /** * Options for {@link WorkflowClient.list} */ export interface ListOptions { /** * Maximum number of results to fetch per page. * * @default depends on server config, typically 1000 */ pageSize?: number; /** * Query string for matching and ordering the results */ query?: string; } /** * Options for {@link WorkflowClient.list().intoHistories()} */ export interface IntoHistoriesOptions { /** * Maximum number of workflow histories to download concurrently. * * @default 5 */ concurrency?: number; /** * Maximum number of workflow histories to buffer ahead, ready for consumption. * * It is recommended to set `bufferLimit` to a rasonnably low number if it is expected that the * iterable may be stopped before reaching completion (for example, when implementing a fail fast * bach replay test). * * Ignored unless `concurrency > 1`. No limit applies if set to `undefined`. * * @default unlimited */ bufferLimit?: number; } declare const withStartWorkflowOperationResolve: unique symbol; declare const withStartWorkflowOperationReject: unique symbol; declare const withStartWorkflowOperationUsed: unique symbol; /** * Define how to start a workflow when using {@link WorkflowClient.startUpdateWithStart} and * {@link WorkflowClient.executeUpdateWithStart}. `workflowIdConflictPolicy` is required in the options. * * @experimental Update-with-Start is an experimental feature and may be subject to change. */ export declare class WithStartWorkflowOperation<T extends Workflow> { workflowTypeOrFunc: string | T; options: WorkflowStartOptions<T> & { workflowIdConflictPolicy: WorkflowIdConflictPolicy; }; private [withStartWorkflowOperationUsed]; private [withStartWorkflowOperationResolve]; private [withStartWorkflowOperationReject]; private workflowHandlePromise; constructor(workflowTypeOrFunc: string | T, options: WorkflowStartOptions<T> & { workflowIdConflictPolicy: WorkflowIdConflictPolicy; }); workflowHandle(): Promise<WorkflowHandle<T>>; } /** * Client for starting Workflow executions and creating Workflow handles. * * Typically this client should not be instantiated directly, instead create the high level {@link Client} and use * {@link Client.workflow} to interact with Workflows. */ export declare class WorkflowClient extends BaseClient { readonly options: LoadedWorkflowClientOptions; constructor(options?: WorkflowClientOptions); /** * Raw gRPC access to the Temporal service. * * **NOTE**: The namespace provided in {@link options} is **not** automatically set on requests made via this service * object. */ get workflowService(): WorkflowService; protected _start<T extends Workflow>(workflowTypeOrFunc: string | T, options: WithWorkflowArgs<T, WorkflowOptions>, interceptors: WorkflowClientInterceptor[]): Promise<string>; protected _signalWithStart<T extends Workflow, SA extends any[]>(workflowTypeOrFunc: string | T, options: WithWorkflowArgs<T, WorkflowSignalWithStartOptions<SA>>, interceptors: WorkflowClientInterceptor[]): Promise<string>; /** * Start a new Workflow execution. * * @returns a {@link WorkflowHandle} to the started Workflow */ start<T extends Workflow>(workflowTypeOrFunc: string | T, options: WorkflowStartOptions<T>): Promise<WorkflowHandleWithFirstExecutionRunId<T>>; /** * Start a new Workflow Execution and immediately send a Signal to that Workflow. * * The behavior of Signal-with-Start in the case where there is already a running Workflow with * the given Workflow ID depends on the {@link WorkflowIDConflictPolicy}. That is, if the policy * is `USE_EXISTING`, then the Signal is issued against the already existing Workflow Execution; * however, if the policy is `FAIL`, then an error is thrown. If no policy is specified, * Signal-with-Start defaults to `USE_EXISTING`. * * @returns a {@link WorkflowHandle} to the started Workflow */ signalWithStart<WorkflowFn extends Workflow, SignalArgs extends any[] = []>(workflowTypeOrFunc: string | WorkflowFn, options: WithWorkflowArgs<WorkflowFn, WorkflowSignalWithStartOptions<SignalArgs>>): Promise<WorkflowHandleWithSignaledRunId<WorkflowFn>>; /** * Start a new Workflow Execution and immediately send an Update to that Workflow, * then await and return the Update's result. * * The `updateOptions` object must contain a {@link WithStartWorkflowOperation}, which defines * the options for the Workflow execution to start (e.g. the Workflow's type, task queue, input * arguments, etc.) * * The behavior of Update-with-Start in the case where there is already a running Workflow with * the given Workflow ID depends on the specified {@link WorkflowIDConflictPolicy}. That is, if * the policy is `USE_EXISTING`, then the Update is issued against the already existing Workflow * Execution; however, if the policy is `FAIL`, then an error is thrown. Caller MUST specify * the desired WorkflowIDConflictPolicy. * * This call will block until the Update has completed. The Workflow handle can be retrieved by * awaiting on {@link WithStartWorkflowOperation.workflowHandle}, whether or not the Update * succeeds. * * @returns the Update result * * @experimental Update-with-Start is an experimental feature and may be subject to change. */ executeUpdateWithStart<T extends Workflow, Ret, Args extends any[]>(updateDef: UpdateDefinition<Ret, Args> | string, updateOptions: WorkflowUpdateOptions & { args?: Args; startWorkflowOperation: WithStartWorkflowOperation<T>; }): Promise<Ret>; /** * Start a new Workflow Execution and immediately send an Update to that Workflow, * then return a {@link WorkflowUpdateHandle} for that Update. * * The `updateOptions` object must contain a {@link WithStartWorkflowOperation}, which defines * the options for the Workflow execution to start (e.g. the Workflow's type, task queue, input * arguments, etc.) * * The behavior of Update-with-Start in the case where there is already a running Workflow with * the given Workflow ID depends on the specified {@link WorkflowIDConflictPolicy}. That is, if * the policy is `USE_EXISTING`, then the Update is issued against the already existing Workflow * Execution; however, if the policy is `FAIL`, then an error is thrown. Caller MUST specify * the desired WorkflowIDConflictPolicy. * * This call will block until the Update has reached the specified {@link WorkflowUpdateStage}. * Note that this means that the call will not return successfully until the Update has * been delivered to a Worker. The Workflow handle can be retrieved by awaiting on * {@link WithStartWorkflowOperation.workflowHandle}, whether or not the Update succeeds. * * @returns a {@link WorkflowUpdateHandle} to the started Update * * @experimental Update-with-Start is an experimental feature and may be subject to change. */ startUpdateWithStart<T extends Workflow, Ret, Args extends any[]>(updateDef: UpdateDefinition<Ret, Args> | string, updateOptions: WorkflowUpdateOptions & { args?: Args; waitForStage: 'ACCEPTED'; startWorkflowOperation: WithStartWorkflowOperation<T>; }): Promise<WorkflowUpdateHandle<Ret>>; protected _startUpdateWithStart<T extends Workflow, Ret, Args extends any[]>(updateDef: UpdateDefinition<Ret, Args> | string, updateWithStartOptions: WorkflowUpdateOptions & { args?: Args; waitForStage: WorkflowUpdateStage; startWorkflowOperation: WithStartWorkflowOperation<T>; }): Promise<WorkflowUpdateHandle<Ret>>; /** * Start a new Workflow execution, then await for its completion and return that Workflow's result. * * @returns the result of the Workflow execution */ execute<T extends Workflow>(workflowTypeOrFunc: string | T, options: WorkflowStartOptions<T>): Promise<WorkflowResultType<T>>; /** * Get the result of a Workflow execution. * * Follow the chain of execution in case Workflow continues as new, or has a cron schedule or retry policy. */ result<T extends Workflow>(workflowId: string, runId?: string, opts?: WorkflowResultOptions): Promise<WorkflowResultType<T>>; protected rethrowUpdateGrpcError(err: unknown, fallbackMessage: string, workflowExecution?: WorkflowExecution): never; protected rethrowGrpcError(err: unknown, fallbackMessage: string, workflowExecution?: WorkflowExecution): never; /** * Use given input to make a queryWorkflow call to the service * * Used as the final function of the query interceptor chain */ protected _queryWorkflowHandler(input: WorkflowQueryInput): Promise<unknown>; protected _createUpdateWorkflowRequest(lifecycleStage: temporal.api.enums.v1.UpdateWorkflowExecutionLifecycleStage, input: WorkflowStartUpdateInput): Promise<temporal.api.workflowservice.v1.IUpdateWorkflowExecutionRequest>; /** * Start the Update. * * Used as the final function of the interceptor chain during startUpdate and executeUpdate. */ protected _startUpdateHandler(waitForStage: WorkflowUpdateStage, input: WorkflowStartUpdateInput): Promise<WorkflowStartUpdateOutput>; /** * Send the Update-With-Start MultiOperation request. * * Used as the final function of the interceptor chain during * startUpdateWithStart and executeUpdateWithStart. */ protected _updateWithStartHandler(waitForStage: WorkflowUpdateStage, onStart: (startResponse: temporal.api.workflowservice.v1.IStartWorkflowExecutionResponse) => void, onStartError: (err: any) => void, input: WorkflowStartUpdateWithStartInput): Promise<WorkflowStartUpdateWithStartOutput>; protected createWorkflowUpdateHandle<Ret>(updateId: string, workflowId: string, workflowRunId?: string, outcome?: temporal.api.update.v1.IOutcome): WorkflowUpdateHandle<Ret>; /** * Poll Update until a response with an outcome is received; return that outcome. * This is used directly; no interceptor is available. */ protected _pollForUpdateOutcome(updateId: string, workflowExecution: temporal.api.common.v1.IWorkflowExecution): Promise<temporal.api.update.v1.IOutcome>; /** * Use given input to make a signalWorkflowExecution call to the service * * Used as the final function of the signal interceptor chain */ protected _signalWorkflowHandler(input: WorkflowSignalInput): Promise<void>; /** * Use given input to make a signalWithStartWorkflowExecution call to the service * * Used as the final function of the signalWithStart interceptor chain */ protected _signalWithStartWorkflowHandler(input: WorkflowSignalWithStartInput): Promise<string>; /** * Use given input to make startWorkflowExecution call to the service * * Used as the final function of the start interceptor chain */ protected _startWorkflowHandler(input: WorkflowStartInput): Promise<string>; protected createStartWorkflowRequest(input: WorkflowStartInput): Promise<StartWorkflowExecutionRequest>; /** * Use given input to make terminateWorkflowExecution call to the service * * Used as the final function of the terminate interceptor chain */ protected _terminateWorkflowHandler(input: WorkflowTerminateInput): Promise<TerminateWorkflowExecutionResponse>; /** * Uses given input to make requestCancelWorkflowExecution call to the service * * Used as the final function of the cancel interceptor chain */ protected _cancelWorkflowHandler(input: WorkflowCancelInput): Promise<RequestCancelWorkflowExecutionResponse>; /** * Uses given input to make describeWorkflowExecution call to the service * * Used as the final function of the describe interceptor chain */ protected _describeWorkflowHandler(input: WorkflowDescribeInput): Promise<DescribeWorkflowExecutionResponse>; /** * Create a new workflow handle for new or existing Workflow execution */ protected _createWorkflowHandle<T extends Workflow>({ workflowId, runId, firstExecutionRunId, interceptors, runIdForResult, ...resultOptions }: WorkflowHandleOptions): WorkflowHandle<T>; /** * Create a handle to an existing Workflow. * * - If only `workflowId` is passed, and there are multiple Workflow Executions with that ID, the handle will refer to * the most recent one. * - If `workflowId` and `runId` are passed, the handle will refer to the specific Workflow Execution with that Run * ID. * - If `workflowId` and {@link GetWorkflowHandleOptions.firstExecutionRunId} are passed, the handle will refer to the * most recent Workflow Execution in the *Chain* that started with `firstExecutionRunId`. * * A *Chain* is a series of Workflow Executions that share the same Workflow ID and are connected by: * - Being part of the same {@link https://docs.temporal.io/typescript/clients#scheduling-cron-workflows | Cron} * - {@link https://docs.temporal.io/typescript/workflows#continueasnew | Continue As New} * - {@link https://typescript.temporal.io/api/interfaces/client.workflowoptions/#retry | Retries} * * This method does not validate `workflowId`. If there is no Workflow Execution with the given `workflowId`, handle * methods like `handle.describe()` will throw a {@link WorkflowNotFoundError} error. */ getHandle<T extends Workflow>(workflowId: string, runId?: string, options?: GetWorkflowHandleOptions): WorkflowHandle<T>; protected _list(options?: ListOptions): AsyncIterable<WorkflowExecutionInfo>; /** * Return a list of Workflow Executions matching the given `query`. * * Note that the list of Workflow Executions returned is approximate and eventually consistent. * * More info on the concept of "visibility" and the query syntax on the Temporal documentation site: * https://docs.temporal.io/visibility */ list(options?: ListOptions): AsyncWorkflowListIterable; /** * Return the number of Workflow Executions matching the given `query`. If no `query` is provided, then return the * total number of Workflow Executions for this namespace. * * Note that the number of Workflow Executions returned is approximate and eventually consistent. * * More info on the concept of "visibility" and the query syntax on the Temporal documentation site: * https://docs.temporal.io/visibility */ count(query?: string): Promise<CountWorkflowExecution>; protected getOrMakeInterceptors(workflowId: string, runId?: string): WorkflowClientInterceptor[]; } export declare class QueryRejectedError extends Error { readonly status: temporal.api.enums.v1.WorkflowExecutionStatus; constructor(status: temporal.api.enums.v1.WorkflowExecutionStatus); } export declare class QueryNotRegisteredError extends Error { readonly code: grpcStatus; constructor(message: string, code: grpcStatus); } export {};