From 8250627e66494c8d053aace921163ad0a480b672 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Thu, 2 Oct 2025 11:28:21 +0300 Subject: [PATCH 01/60] init --- .env.example | 5 +- README.md | 2167 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 2142 insertions(+), 30 deletions(-) diff --git a/.env.example b/.env.example index 1450e51f..05712548 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,6 @@ CONDUCTOR_SERVER_URL="" + CONDUCTOR_AUTH_KEY="" -CONDUCTOR_AUTH_SECRET="" \ No newline at end of file +CONDUCTOR_AUTH_SECRET="" + +CONDUCTOR_MAX_HTTP2_CONNECTIONS= \ No newline at end of file diff --git a/README.md b/README.md index 8f7e31f0..b5e3a8ac 100644 --- a/README.md +++ b/README.md @@ -1,55 +1,2164 @@ -# Conductor OSS Javascript/Typescript SDK +# Conductor JavaScript/TypeScript SDK -The `conductor-javascript` repository provides the client SDKs to build task workers in javascript/typescript. +The `@io-orkes/conductor-javascript` SDK provides a comprehensive TypeScript/JavaScript client for building workflows and task workers with Netflix Conductor and Orkes Conductor. -Building the task workers in javascript mainly consists of the following steps: +## Table of Contents -1. Setup conductor-javascript package -2. [Create and run task workers](workers_sdk.md) -3. [Create workflows using code](workflow_sdk.md) - -### Setup Conductor Javascript Package +- [Installation](#installation) +- [Quick Start](#quick-start) +- [Authentication & Configuration](#authentication--configuration) +- [Core Concepts](#core-concepts) +- [Client Management](#client-management) +- [Task Types](#task-types) +- [SDK Factory Functions](#sdk-factory-functions) +- [Workflow Management](#workflow-management) +- [Task Management](#task-management) +- [Scheduler Management](#scheduler-management) +- [Service Registry](#service-registry) +- [Human Tasks](#human-tasks) +- [Metadata Management](#metadata-management) +- [Error Handling](#error-handling) +- [Logging](#logging) +- [Best Practices](#best-practices) +- [API Reference](#api-reference) +- [Examples](#examples) +- [Troubleshooting](#troubleshooting) -* Get the package from npm +## Installation -```shell -npm i @io-orkes/conductor-javascript +Install the SDK using npm or yarn: + +```bash +npm install @io-orkes/conductor-javascript ``` + or -```shell +```bash yarn add @io-orkes/conductor-javascript ``` -## Configurations +## Quick Start + +Here's a simple example to get you started: + +```typescript +import { + orkesConductorClient, + WorkflowExecutor, + TaskManager, + simpleTask, + workflow +} from "@io-orkes/conductor-javascript"; + +// 1. Create client +const client = await orkesConductorClient({ + serverUrl: "https://play.orkes.io/api", + keyId: "your-key-id", + keySecret: "your-key-secret" +}); + +// 2. Create workflow executor +const executor = new WorkflowExecutor(client); -### Authentication Settings (Optional) -Configure the authentication settings if your Conductor server requires authentication. -* keyId: Key for authentication. -* keySecret: Secret for the key. +// 3. Define a simple workflow +const myWorkflow = workflow("hello_world", [ + simpleTask("greet_task", "greeting_task", { message: "Hello World!" }) +]); + +// 4. Register workflow +await executor.registerWorkflow(true, myWorkflow); + +// 5. Start workflow execution +const executionId = await executor.startWorkflow({ + name: "hello_world", + version: 1, + input: { name: "Developer" } +}); + +console.log(`Workflow started with ID: ${executionId}`); +``` + +## Authentication & Configuration ### Access Control Setup -See [Access Control](https://orkes.io/content/docs/getting-started/concepts/access-control) for more details on role-based access control with Conductor and generating API keys for your environment. -### Configure API Client +The SDK supports authentication using API keys. See [Access Control](https://orkes.io/content/docs/getting-started/concepts/access-control) for more details on role-based access control with Conductor and generating API keys. + +### Configuration Options ```typescript -/** - * Application keys generated from the Application menu > Create Application - * then edit and create Access Keys - * - */ import { OrkesApiConfig, orkesConductorClient } from "@io-orkes/conductor-javascript"; const config: Partial = { - keyId: "XXX", // optional - keySecret: "XXXX", // optional - refreshTokenInterval: 0, // optional (in milliseconds) | defaults to 30 minutes | 0 = no refresh + serverUrl: "https://play.orkes.io/api", // server api url + keyId: "your-key-id", // authentication key + keySecret: "your-key-secret", // authentication secret + refreshTokenInterval: 0, // optional: token refresh interval, 0 = no refresh (default: 30min) + maxHttp2Connections: 1, // optional: max HTTP2 connections (default: 1) + useEnvVars: false // DEPRECATED: has no effect +}; + +const client = await orkesConductorClient(config); +``` + +### Environment Variables + +You can configure authentication using environment variables: + +```bash +CONDUCTOR_SERVER_URL=https://play.orkes.io/api +CONDUCTOR_AUTH_KEY=your-key-id +CONDUCTOR_AUTH_SECRET=your-key-secret +CONDUCTOR_MAX_HTTP2_CONNECTIONS=1 +``` +Environment variables are prioritized over config variables. + +### Custom Fetch Function + +You can provide a custom fetch function for HTTP requests: + +```typescript +const client = await orkesConductorClient(config, fetch); +``` + +## Core Concepts + +### Tasks +Tasks are individual units of work that can be executed by workers or handled by the Conductor server. + +### Workflows +Workflows are the main orchestration units in Conductor. They define a sequence of tasks and their dependencies. + +### Workers +Workers are applications that execute specific types of tasks. They poll for work and execute tasks assigned to them. + +### Task Definitions +Task definitions describe the metadata and configuration for tasks, including retry policies, timeouts, and input/output schemas. + +## Client Management + +### OrkesConductorClient + +The `orkesConductorClient` function creates a configured client instance: + +```typescript +import { orkesConductorClient } from "@io-orkes/conductor-javascript"; + +const client = await orkesConductorClient({ + serverUrl: "https://play.orkes.io/api", + keyId: "your-key-id", + keySecret: "your-key-secret" +}); +``` + +## Task Types + +The SDK provides generators for various task types: + +### Simple Task + +```typescript +import { simpleTask } from "@io-orkes/conductor-javascript"; + +const task = simpleTask("task_ref", "task_name", { + inputParam: "value" +}, false); // optional parameter +``` + +### HTTP Task + +```typescript +import { httpTask } from "@io-orkes/conductor-javascript"; + +const task = httpTask("http_ref", "http://api.example.com/data", { + method: "GET", + headers: { "Authorization": "Bearer token" }, + connectionTimeOut: 5000, + readTimeOut: 10000 +}); +``` + +### Switch Task + +```typescript +import { switchTask } from "@io-orkes/conductor-javascript"; + +const task = switchTask("switch_ref", "input.status", { + "active": [simpleTask("active_task", "process_active", {})], + "inactive": [simpleTask("inactive_task", "process_inactive", {})], + "default": [simpleTask("default_task", "process_default", {})] +}); +``` + +### Fork-Join Task + +```typescript +import { forkJoinTask } from "@io-orkes/conductor-javascript"; + +const task = forkJoinTask("fork_ref", [ + [simpleTask("task1", "process_1", {})], + [simpleTask("task2", "process_2", {})], + [simpleTask("task3", "process_3", {})] +]); +``` + +### Do-While Task + +```typescript +import { doWhileTask } from "@io-orkes/conductor-javascript"; + +const task = doWhileTask("while_ref", "workflow.variables.counter < 10", [ + simpleTask("loop_task", "process_item", { + index: "${workflow.variables.counter}" + }), + setVariableTask("increment", { + variableName: "counter", + value: "${workflow.variables.counter + 1}" + }) +]); +``` + +### Sub-Workflow Task + +```typescript +import { subWorkflowTask } from "@io-orkes/conductor-javascript"; + +const task = subWorkflowTask("sub_ref", "child_workflow", 1, { + inputParam: "value" +}, "COMPLETED"); // wait for completion status +``` + +### Event Task + +```typescript +import { eventTask } from "@io-orkes/conductor-javascript"; + +const task = eventTask("event_ref", "event_name", { + sink: "event_sink", + asyncComplete: true +}); +``` + +### Wait Task + +```typescript +import { waitTask } from "@io-orkes/conductor-javascript"; + +const task = waitTask("wait_ref", 30); // wait 30 seconds +``` + +### Terminate Task + +```typescript +import { terminateTask } from "@io-orkes/conductor-javascript"; + +const task = terminateTask("terminate_ref", "FAILED", "Error message"); +``` + +### Set Variable Task + +```typescript +import { setVariableTask } from "@io-orkes/conductor-javascript"; + +const task = setVariableTask("var_ref", { + variableName: "result", + value: "computed_value" +}); +``` + +### JSON JQ Transform Task + +```typescript +import { jsonJqTask } from "@io-orkes/conductor-javascript"; + +const task = jsonJqTask("transform_ref", ".data.items[] | {id: .id, name: .name}"); +``` + +### Kafka Publish Task + +```typescript +import { kafkaPublishTask } from "@io-orkes/conductor-javascript"; + +const task = kafkaPublishTask("kafka_ref", "topic_name", { + message: "Hello Kafka!" +}, { + key: "message_key", + partition: 0 +}); +``` + +### Inline Task + +```typescript +import { inlineTask } from "@io-orkes/conductor-javascript"; + +const task = inlineTask("inline_ref", ` + function execute(input) { + return { result: input.value * 2 }; + } +`); +``` + +### Dynamic Fork Task + +```typescript +import { dynamicForkTask } from "@io-orkes/conductor-javascript"; + +const task = dynamicForkTask("dynamic_ref", "input.tasks", "task_name"); +``` + +### Join Task + +```typescript +import { joinTask } from "@io-orkes/conductor-javascript"; + +const task = joinTask("join_ref"); +``` + +### Human Task + +```typescript +import { humanTask } from "@io-orkes/conductor-javascript"; + +const task = humanTask("human_ref", "approval_task", { + assignee: "user@example.com", + form: { + fields: [ + { name: "approved", type: "boolean", required: true }, + { name: "comments", type: "text", required: false } + ] + } +}); +``` + +## SDK Factory Functions + +### Workflow Factory + +```typescript +import { workflow } from "@io-orkes/conductor-javascript"; + +const myWorkflow = workflow("workflow_name", [ + simpleTask("task1", "process_1", {}), + simpleTask("task2", "process_2", {}) +]); +``` + +### Task Definition Factory + +```typescript +import { taskDefinition } from "@io-orkes/conductor-javascript"; + +const taskDef = taskDefinition("task_name", { + timeoutSeconds: 300, + retryCount: 3, + retryDelaySeconds: 60, + responseTimeoutSeconds: 300, + pollTimeoutSeconds: 300, + pollIntervalSeconds: 30, + concurrentExecLimit: 10, + rateLimitPerFrequency: 100, + rateLimitFrequencyInSeconds: 60, + ownerEmail: "owner@example.com", + description: "Task description", + inputTemplate: { + param1: "default_value" + }, + outputTemplate: { + result: "computed_value" + }, + inputKeys: ["param1", "param2"], + outputKeys: ["result"], + tags: ["tag1", "tag2"], + executionNameSpace: "namespace", + isolationGroupId: "isolation_group", + maxConcurrentExecutions: 5, + concurrentExecLimit: 10, + rateLimitPerFrequency: 100, + rateLimitFrequencyInSeconds: 60, + ownerEmail: "owner@example.com", + description: "Task description", + inputTemplate: { + param1: "default_value" + }, + outputTemplate: { + result: "computed_value" + }, + inputKeys: ["param1", "param2"], + outputKeys: ["result"], + tags: ["tag1", "tag2"], + executionNameSpace: "namespace", + isolationGroupId: "isolation_group", + maxConcurrentExecutions: 5 +}); +``` + +## Workflow Management + +### WorkflowExecutor + +The `WorkflowExecutor` class provides methods for managing workflows: + +```typescript +import { WorkflowExecutor } from "@io-orkes/conductor-javascript"; + +const executor = new WorkflowExecutor(client); +``` + +#### Register Workflow + +```typescript +const workflowDef = { + name: "my_workflow", + version: 1, + ownerEmail: "developer@example.com", + tasks: [/* task definitions */], + inputParameters: [], + outputParameters: {}, + timeoutSeconds: 0 +}; + +await executor.registerWorkflow(true, workflowDef); +``` + +#### Start Workflow + +```typescript +const executionId = await executor.startWorkflow({ + name: "my_workflow", + version: 1, + input: { /* workflow input */ } +}); +``` + +#### Get Workflow Status + +```typescript +const workflowStatus = await executor.getWorkflow(executionId, true); +console.log(`Status: ${workflowStatus.status}`); +``` + +#### Pause Workflow + +```typescript +await executor.pauseWorkflow(executionId, "Pausing for maintenance"); +``` + +#### Resume Workflow + +```typescript +await executor.resumeWorkflow(executionId); +``` + +#### Terminate Workflow + +```typescript +await executor.terminateWorkflow(executionId, "Terminating due to error"); +``` + +#### Workflow Search + +```typescript +const searchResults = await executor.searchWorkflows({ + query: "status:RUNNING", + start: 0, + size: 10 +}); +``` + +## Task Management + +### TaskManager + +The `TaskManager` class manages multiple workers and handles task polling: + +```typescript +import { TaskManager, ConductorWorker } from "@io-orkes/conductor-javascript"; + +const workers: ConductorWorker[] = [ + { + taskDefName: "greeting_task", + execute: async (task) => { + return { + outputData: { greeting: "Hello!" }, + status: "COMPLETED" + }; + } + } +]; + +const manager = new TaskManager(client, workers, { + options: { + pollInterval: 1000, + concurrency: 2, + workerID: "worker-group-1", + domain: "production", + batchPollingTimeout: 100 + }, + logger: customLogger, + onError: errorHandler, + maxRetries: 3 +}); + +// Start polling +manager.startPolling(); + +// Update polling options +manager.updatePollingOptions({ pollInterval: 500 }); + +// Stop polling +await manager.stopPolling(); +``` + +#### TaskManager Configuration + +The `TaskManager` supports comprehensive configuration options: + +```typescript +interface TaskManagerConfig { + logger?: ConductorLogger; + options?: Partial; + onError?: TaskErrorHandler; + maxRetries?: number; +} + +interface TaskManagerOptions { + workerID?: string; // Unique worker identifier + pollInterval?: number; // Polling interval in milliseconds + domain?: string; // Task domain for isolation + concurrency?: number; // Number of concurrent executions + batchPollingTimeout?: number; // Batch polling timeout +} +``` + +#### Starting Workers + +There are two ways to start workers in the SDK: + +##### Using TaskManager (Recommended) + +`TaskManager` is the high-level interface that manages multiple workers and their corresponding `TaskRunner` instances. It's the recommended approach for most use cases. + +```typescript +import { + OrkesApiConfig, + orkesConductorClient, + TaskManager, +} from "@io-orkes/conductor-javascript"; + +const client = await orkesConductorClient({ + keyId: "XXX", + keySecret: "XXXX", + serverUrl: "https://play.orkes.io/api", +}); + +const taskDefName = "HelloWorldWorker"; + +const customWorker: ConductorWorker = { + taskDefName, + execute: async ({ inputData, taskId }) => { + return { + outputData: { + greeting: "Hello World", + }, + status: "COMPLETED", + }; + }, +}; + +const taskManager = new TaskManager(client, [customWorker], { + logger: new DefaultLogger(), + options: { + pollInterval: 1000, + concurrency: 1, + workerID: "my-worker", + }, +}); + +// Start all workers +await taskManager.startPolling(); + +// Stop all workers +await taskManager.shutdown(); +``` + +##### Using TaskRunner (Low-level) + +`TaskRunner` is the low-level interface used internally by `TaskManager`. It handles individual worker execution, polling the server for work, and updating results back to the server. Use this when you need fine-grained control over a single worker. + +```typescript +import { + orkesConductorClient, + TaskRunner, + DefaultLogger, +} from "@io-orkes/conductor-javascript"; + +const client = await orkesConductorClient({ + keyId: "XXX", + keySecret: "XXXX", serverUrl: "https://play.orkes.io/api", +}); + +const customWorker: ConductorWorker = { + taskDefName: "HelloWorldWorker", + execute: async ({ inputData, taskId }) => { + return { + outputData: { + greeting: "Hello World", + }, + status: "COMPLETED", + }; + }, +}; + +const taskRunner = new TaskRunner({ + worker: customWorker, + taskResource: client.taskResource, + options: { + pollInterval: 1000, + concurrency: 1, + workerID: "my-worker", + }, + logger: new DefaultLogger(), +}); + +// Start the worker +await taskRunner.startPolling(); + +// Stop the worker +await taskRunner.shutdown(); +``` + +##### When to Use Each Approach + +- **Use TaskManager** when you have multiple workers or want the convenience of managing all workers together +- **Use TaskRunner** when you need fine-grained control over a single worker or want to implement custom worker management logic + +// Worker Options will take precedence over options defined in the manager +const manager = new TaskManager(client, [customWorker], { + options: { pollInterval: 100, concurrency: 1 }, +}); + +manager.startPolling(); + +// You can update all worker settings at once using +manager.updatePollingOptions({ pollInterval: 100, concurrency: 1 }); + +// You can update a single worker setting using: +manager.updatePollingOptionForWorker(taskDefName, { + pollInterval: 100, + concurrency: 1, +}); + +manager.isPolling; // Will resolve to true + +await manager.stopPolling(); + +manager.isPolling; // Will resolve to false +``` + +#### Polling Mechanism + +The TaskManager uses a sophisticated polling mechanism: + +1. **Batch Polling**: Workers poll for multiple tasks at once for better performance +2. **Concurrent Execution**: Multiple tasks can be executed simultaneously +3. **Domain Isolation**: Tasks can be isolated by domain +4. **Worker Identification**: Each worker has a unique identifier +5. **Retry Logic**: Built-in retry mechanism for failed operations + +#### Polling Configuration Examples + +```typescript +// High-frequency polling for critical tasks +const criticalManager = new TaskManager(client, criticalWorkers, { + options: { + pollInterval: 100, // Poll every 100ms + concurrency: 5, // High concurrency + batchPollingTimeout: 50 // Quick batch timeout + } +}); + +// Low-frequency polling for background tasks +const backgroundManager = new TaskManager(client, backgroundWorkers, { + options: { + pollInterval: 5000, // Poll every 5 seconds + concurrency: 1, // Single execution + batchPollingTimeout: 1000 // Longer batch timeout + } +}); + +// Domain-specific polling +const productionManager = new TaskManager(client, productionWorkers, { + options: { + domain: "production", + pollInterval: 1000, + concurrency: 2 + } +}); +``` + +### ConductorWorker + +Define workers using the `ConductorWorker` interface: + +```typescript +const worker: ConductorWorker = { + taskDefName: "my_task", + execute: async (task) => { + // Process the task + const result = await processTask(task.inputData); + + return { + outputData: result, + status: "COMPLETED" + }; + }, + pollInterval: 1000, // optional + concurrency: 1, // optional + domain: "production" // optional +}; +``` + +#### Worker Design Principles + +Each worker embodies design patterns and follows certain basic principles: + +1. **Workers are stateless** and do not implement workflow-specific logic +2. **Each worker executes a very specific task** and produces well-defined output given specific inputs +3. **Workers are meant to be idempotent** (or should handle cases where the task that is partially executed gets rescheduled due to timeouts etc.) +4. **Workers do not implement the logic to handle retries** etc, that is taken care by the Conductor server + +#### Task Worker Implementation + +Task worker is implemented using a function that conforms to the following interface: + +```typescript +import { ConductorWorker, Task } from "@io-orkes/conductor-javascript"; + +const worker: ConductorWorker = { + taskDefName: "task-def-name", + execute: async ( + task: Task + ): Promise> => { + // Your task execution logic here + }, }; +``` + +Worker returns an object as the output of the task execution. The object is just a JSON that follows the TaskResult interface. +If an `error` is returned, the task is marked as `FAILED` + +#### Task Worker that Returns an Object -orkesConductorClient(config).then(client => ..... ); +```typescript +import { ConductorWorker, Task } from "@io-orkes/conductor-javascript"; +const worker: ConductorWorker = { + taskDefName: "task-def-name", + execute: async (task: Task) => { + // Sample output + return { + outputData: { + hello: "From your worker", + }, + status: "COMPLETED", + }; + }, +}; ``` -### Next: [Create and run task workers](workers_sdk.md) +#### Controlling Execution for Long-running Tasks + +For long-running tasks you might want to spawn another process/routine and update the status of the task at a later point and complete the execution function without actually marking the task as `COMPLETED`. Use `TaskResult` Interface that allows you to specify more fine-grained control. + +Here is an example of a task execution function that returns with `IN_PROGRESS` status asking server to push the task again in 60 seconds. + +```typescript +const worker: ConductorWorker = { + taskDefName: "task-def-name", + execute: async (task: Task) => { + // Sample output + return { + outputData: {}, + status: "IN_PROGRESS", + callbackAfterSeconds: 60, + }; + }, + pollInterval: 100, // optional + concurrency: 2, // optional +}; +``` + +#### Worker Configuration Options + +Workers support several configuration options: + +```typescript +const worker: ConductorWorker = { + taskDefName: "my_task", + execute: async (task) => { + return { outputData: {}, status: "COMPLETED" }; + }, + pollInterval: 1000, // Polling interval in milliseconds + concurrency: 2, // Number of concurrent executions + domain: "production" // Task domain for isolation +}; +``` + +#### Worker Error Handling + +```typescript +const worker: ConductorWorker = { + taskDefName: "error_prone_task", + execute: async (task) => { + try { + const result = await riskyOperation(task.inputData); + return { + outputData: result, + status: "COMPLETED" + }; + } catch (error) { + return { + outputData: {}, + status: "FAILED", + reasonForIncompletion: error.message + }; + } + } +}; +``` + +#### Long-running Tasks + +For long-running tasks, you can return `IN_PROGRESS` status: + +```typescript +const worker: ConductorWorker = { + taskDefName: "long_task", + execute: async (task) => { + // Start long-running process + startLongProcess(); + + return { + outputData: {}, + status: "IN_PROGRESS", + callbackAfterSeconds: 60 // Check again in 60 seconds + }; + } +}; +``` + +### TaskRunner + +For more control, use `TaskRunner` directly: + +```typescript +import { TaskRunner } from "@io-orkes/conductor-javascript"; + +const runner = new TaskRunner({ + worker, + taskResource: client.taskResource, + options: { + pollInterval: 1000, + concurrency: 1, + workerID: "worker-1", + domain: "production", + batchPollingTimeout: 100 + }, + logger: customLogger, + onError: errorHandler, + maxRetries: 3 +}); + +runner.startPolling(); +``` + +#### TaskRunner Configuration + +The `TaskRunner` provides fine-grained control over individual worker execution: + +```typescript +interface RunnerArgs { + worker: ConductorWorker; + taskResource: TaskResourceService; + options: TaskRunnerOptions; + logger?: ConductorLogger; + onError?: TaskErrorHandler; + maxRetries?: number; +} + +interface TaskRunnerOptions { + workerID?: string; // Unique worker identifier + pollInterval?: number; // Polling interval in milliseconds + domain?: string; // Task domain for isolation + concurrency?: number; // Number of concurrent executions + batchPollingTimeout?: number; // Batch polling timeout +} +``` + +#### TaskRunner Lifecycle + +```typescript +const runner = new TaskRunner({ + worker: myWorker, + taskResource: client.taskResource, + options: { + pollInterval: 1000, + concurrency: 2, + workerID: "my-worker-1" + } +}); + +// Start polling +runner.startPolling(); + +// Check if polling +console.log(runner.isPolling); // true + +// Update options dynamically +runner.updateOptions({ + pollInterval: 500, + concurrency: 3 +}); + +// Get current options +const currentOptions = runner.getOptions; + +// Stop polling +await runner.stopPolling(); +``` + +#### TaskRunner vs TaskManager + +- **TaskRunner**: Manages a single worker with fine-grained control +- **TaskManager**: Manages multiple workers with centralized configuration + +Use `TaskRunner` when you need: +- Individual worker control +- Different polling strategies per worker +- Custom error handling per worker +- Dynamic configuration updates + +Use `TaskManager` when you need: +- Centralized worker management +- Consistent configuration across workers +- Simplified worker lifecycle management +- Bulk operations on multiple workers + +### TaskClient + +The `TaskClient` provides additional task management capabilities: + +```typescript +import { TaskClient } from "@io-orkes/conductor-javascript"; + +const taskClient = new TaskClient(client); + +// Search tasks +const searchResults = await taskClient.search(0, 10, "status:COMPLETED", "", ""); + +// Get task by ID +const task = await taskClient.getTask(taskId); + +// Update task result +await taskClient.updateTaskResult( + workflowId, + taskReferenceName, + "COMPLETED", + { result: "success" } +); +``` + +## Scheduler Management + +### SchedulerClient + +The `SchedulerClient` manages workflow scheduling: + +```typescript +import { SchedulerClient } from "@io-orkes/conductor-javascript"; + +const scheduler = new SchedulerClient(client); + +// Create a schedule +await scheduler.saveSchedule({ + name: "daily_report", + cronExpression: "0 0 9 * * ?", // Every day at 9 AM + startWorkflowRequest: { + name: "report_workflow", + version: 1, + input: { reportType: "daily" } + } +}); + +// Get schedule +const schedule = await scheduler.getSchedule("daily_report"); + +// Pause schedule +await scheduler.pauseSchedule("daily_report"); + +// Resume schedule +await scheduler.resumeSchedule("daily_report"); + +// Delete schedule +await scheduler.deleteSchedule("daily_report"); + +// Get all schedules +const allSchedules = await scheduler.getAllSchedules(); + +// Get next few execution times +const nextExecutions = await scheduler.getNextFewSchedules( + "0 0 9 * * ?", + Date.now(), + Date.now() + 7 * 24 * 60 * 60 * 1000, // Next 7 days + 5 +); + +// Search schedule executions +const executions = await scheduler.search(0, 10, "", "", "status:RUNNING"); + +// Pause all schedules (debugging only) +await scheduler.pauseAllSchedules(); + +// Resume all schedules +await scheduler.resumeAllSchedules(); + +// Requeue all execution records +await scheduler.requeueAllExecutionRecords(); +``` + +## Service Registry + +### ServiceRegistryClient + +The `ServiceRegistryClient` manages service registrations and circuit breakers: + +```typescript +import { ServiceRegistryClient } from "@io-orkes/conductor-javascript"; + +const serviceRegistry = new ServiceRegistryClient(client); + +// Register a service +await serviceRegistry.addOrUpdateService({ + name: "user-service", + type: "HTTP", + serviceURI: "https://api.example.com/users", + circuitBreakerConfig: { + failureRateThreshold: 50.0, + slidingWindowSize: 10, + minimumNumberOfCalls: 5, + waitDurationInOpenState: 60000 + } +}); + +// Get all registered services +const services = await serviceRegistry.getRegisteredServices(); + +// Get specific service +const service = await serviceRegistry.getService("user-service"); + +// Add service method +await serviceRegistry.addOrUpdateServiceMethod("user-service", { + operationName: "getUser", + methodName: "getUser", + methodType: "GET", + inputType: "string", + outputType: "User", + requestParams: [ + { + name: "id", + type: "Path", + required: true, + schema: { type: "string" } + } + ] +}); + +// Circuit breaker management +await serviceRegistry.openCircuitBreaker("user-service"); +await serviceRegistry.closeCircuitBreaker("user-service"); +const status = await serviceRegistry.getCircuitBreakerStatus("user-service"); + +// Proto file management (for gRPC services) +await serviceRegistry.setProtoData("grpc-service", "user.proto", protoBlob); +const protoData = await serviceRegistry.getProtoData("grpc-service", "user.proto"); +const allProtos = await serviceRegistry.getAllProtos("grpc-service"); +await serviceRegistry.deleteProto("grpc-service", "user.proto"); + +// Service discovery +const methods = await serviceRegistry.discover("user-service", true); + +// Remove service +await serviceRegistry.removeService("user-service"); +``` + +## Metadata Management + +### MetadataClient + +The `MetadataClient` class provides methods for managing task and workflow definitions: + +```typescript +import { MetadataClient } from "@io-orkes/conductor-javascript"; + +const metadataClient = new MetadataClient(client); +``` + +#### Register Task Definition + +```typescript +const taskDef = { + name: "process_order", + description: "Process customer order", + timeoutSeconds: 300, + retryCount: 3, + retryDelaySeconds: 60, + responseTimeoutSeconds: 300, + pollTimeoutSeconds: 300, + pollIntervalSeconds: 30, + concurrentExecLimit: 10, + rateLimitPerFrequency: 100, + rateLimitFrequencyInSeconds: 60, + ownerEmail: "owner@example.com", + inputTemplate: { + orderId: "string", + customerId: "string" + }, + outputTemplate: { + processedOrderId: "string", + status: "string" + }, + inputKeys: ["orderId", "customerId"], + outputKeys: ["processedOrderId", "status"], + tags: ["order", "processing"], + executionNameSpace: "orders", + isolationGroupId: "order_processing", + maxConcurrentExecutions: 5 +}; + +await metadataClient.registerTask(taskDef); +``` + +#### Update Task Definition + +```typescript +const updatedTaskDef = { + ...taskDef, + timeoutSeconds: 600, // Increased timeout + retryCount: 5 // Increased retry count +}; + +await metadataClient.updateTask(taskDef.name, updatedTaskDef); +``` + +#### Unregister Task Definition + +```typescript +await metadataClient.unregisterTask("process_order"); +``` + +#### Register Workflow Definition + +```typescript +const workflowDef = { + name: "order_processing_workflow", + version: 1, + description: "Complete order processing workflow", + tasks: [ + { + name: "validate_order", + taskReferenceName: "validate_order_ref", + type: "SIMPLE", + inputParameters: { + orderId: "${workflow.input.orderId}" + } + }, + { + name: "process_payment", + taskReferenceName: "process_payment_ref", + type: "SIMPLE", + inputParameters: { + orderId: "${workflow.input.orderId}", + amount: "${workflow.input.amount}" + } + }, + { + name: "send_confirmation", + taskReferenceName: "send_confirmation_ref", + type: "SIMPLE", + inputParameters: { + orderId: "${workflow.input.orderId}", + customerEmail: "${workflow.input.customerEmail}" + } + } + ], + inputParameters: ["orderId", "amount", "customerEmail"], + outputParameters: { + processedOrderId: "${validate_order_ref.output.processedOrderId}", + paymentStatus: "${process_payment_ref.output.status}", + confirmationSent: "${send_confirmation_ref.output.sent}" + }, + failureWorkflow: "order_failure_workflow", + restartable: true, + workflowStatusListenerEnabled: true, + schemaVersion: 2, + ownerEmail: "workflow-owner@example.com", + timeoutPolicy: "ALERT_ONLY", + timeoutSeconds: 3600, + variables: { + maxRetries: 3, + retryDelay: 60 + } +}; + +await metadataClient.registerWorkflowDef(workflowDef); +``` + +#### Unregister Workflow Definition + +```typescript +await metadataClient.unregisterWorkflow("order_processing_workflow", 1); +``` + +### TemplateClient + +The `TemplateClient` class provides methods for managing human task templates: + +```typescript +import { TemplateClient } from "@io-orkes/conductor-javascript"; + +const templateClient = new TemplateClient(client); +``` + +#### Register Form Template + +```typescript +const formTemplate = { + name: "approval_form", + version: 1, + description: "Order approval form template", + formTemplate: { + name: "Order Approval Form", + fields: [ + { + name: "approved", + type: "boolean", + required: true, + label: "Approve Order", + description: "Check to approve the order" + }, + { + name: "comments", + type: "text", + required: false, + label: "Comments", + description: "Additional comments about the approval decision", + maxLength: 500 + }, + { + name: "approver_name", + type: "text", + required: true, + label: "Approver Name", + description: "Name of the person approving the order" + }, + { + name: "approval_date", + type: "date", + required: true, + label: "Approval Date", + description: "Date of approval" + } + ], + validationRules: [ + { + field: "approved", + rule: "required", + message: "Approval decision is required" + }, + { + field: "approver_name", + rule: "minLength:2", + message: "Approver name must be at least 2 characters" + } + ] + }, + uiTemplate: { + name: "Order Approval UI", + template: ` +
+

Order Approval

+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ `, + styles: ` + .approval-form { + max-width: 600px; + margin: 0 auto; + padding: 20px; + font-family: Arial, sans-serif; + } + .form-group { + margin-bottom: 15px; + } + .form-group label { + display: block; + margin-bottom: 5px; + font-weight: bold; + } + .form-group input, + .form-group textarea { + width: 100%; + padding: 8px; + border: 1px solid #ddd; + border-radius: 4px; + } + .form-group input[type="checkbox"] { + width: auto; + margin-right: 8px; + } + ` + }, + tags: ["approval", "order", "form"], + ownerEmail: "template-owner@example.com" +}; + +await templateClient.registerTemplate(formTemplate); +``` + +#### Register UI Template + +```typescript +const uiTemplate = { + name: "custom_ui_template", + version: 1, + description: "Custom UI template for human tasks", + uiTemplate: { + name: "Custom Task UI", + template: ` +
+

Custom Task Interface

+
+

Task: {{taskName}}

+

Description: {{taskDescription}}

+
+ + {{#each taskInputs}} +
+ + +
+ {{/each}} +
+
+ + +
+
+
+ `, + styles: ` + .custom-task-ui { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + max-width: 800px; + margin: 0 auto; + padding: 20px; + background-color: #f5f5f5; + border-radius: 8px; + box-shadow: 0 2px 10px rgba(0,0,0,0.1); + } + .custom-task-ui h1 { + color: #333; + text-align: center; + margin-bottom: 30px; + } + .task-content { + background-color: white; + padding: 20px; + border-radius: 6px; + margin-bottom: 20px; + } + .input-field { + margin-bottom: 15px; + } + .input-field label { + display: block; + margin-bottom: 5px; + font-weight: 600; + color: #555; + } + .input-field input { + width: 100%; + padding: 10px; + border: 2px solid #e1e1e1; + border-radius: 4px; + font-size: 14px; + transition: border-color 0.3s ease; + } + .input-field input:focus { + outline: none; + border-color: #007bff; + } + .task-actions { + text-align: center; + margin-top: 20px; + } + .task-actions button { + background-color: #007bff; + color: white; + border: none; + padding: 12px 24px; + margin: 0 10px; + border-radius: 4px; + cursor: pointer; + font-size: 14px; + transition: background-color 0.3s ease; + } + .task-actions button:hover { + background-color: #0056b3; + } + .task-actions button:last-child { + background-color: #dc3545; + } + .task-actions button:last-child:hover { + background-color: #c82333; + } + `, + scripts: ` + function completeTask() { + const formData = new FormData(document.querySelector('.custom-task-ui')); + const outputData = {}; + for (let [key, value] of formData.entries()) { + outputData[key] = value; + } + + // Send completion data to Conductor + fetch('/api/tasks/complete', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + taskId: '{{taskId}}', + outputData: outputData, + status: 'COMPLETED' + }) + }) + .then(response => response.json()) + .then(data => { + alert('Task completed successfully!'); + window.close(); + }) + .catch(error => { + console.error('Error completing task:', error); + alert('Error completing task. Please try again.'); + }); + } + + function failTask() { + const reason = prompt('Please provide a reason for failing the task:'); + if (reason) { + fetch('/api/tasks/fail', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + taskId: '{{taskId}}', + reasonForIncompletion: reason, + status: 'FAILED' + }) + }) + .then(response => response.json()) + .then(data => { + alert('Task marked as failed.'); + window.close(); + }) + .catch(error => { + console.error('Error failing task:', error); + alert('Error failing task. Please try again.'); + }); + } + } + ` + }, + tags: ["ui", "custom", "template"], + ownerEmail: "ui-developer@example.com" +}; + +await templateClient.registerTemplate(uiTemplate); +``` + +## Human Tasks + +### HumanExecutor + +The `HumanExecutor` class provides comprehensive human task management: + +```typescript +import { HumanExecutor } from "@io-orkes/conductor-javascript"; + +const humanExecutor = new HumanExecutor(client); + +// Search human tasks +const tasks = await humanExecutor.search({ + states: ["PENDING", "ASSIGNED"], + assignees: [{ userType: "EXTERNAL_USER", user: "john@example.com" }], + taskRefNames: ["approval_task"], + taskInputQuery: "priority:high", + size: 10, + start: 0 +}); + +// Poll for tasks until found +const polledTasks = await humanExecutor.pollSearch({ + states: ["PENDING"], + assignees: [{ userType: "EXTERNAL_USER", user: "john@example.com" }] +}, { + pollInterval: 1000, + maxPollTimes: 30 +}); + +// Get task by ID +const task = await humanExecutor.getTaskById(taskId); + +// Claim task as external user +const claimedTask = await humanExecutor.claimTaskAsExternalUser( + taskId, + "john@example.com", + { overrideAssignment: false, withTemplate: true } +); + +// Claim task as conductor user +const conductorClaimedTask = await humanExecutor.claimTaskAsConductorUser( + taskId, + { overrideAssignment: false, withTemplate: true } +); + +// Release task +await humanExecutor.releaseTask(taskId); + +// Update task output +await humanExecutor.updateTaskOutput(taskId, { + output: { + approved: true, + comments: "Approved with conditions" + } +}); + +// Complete task +await humanExecutor.completeTask(taskId, { + output: { + approved: true, + comments: "Task completed" + } +}); + +// Get template by name and version +const template = await humanExecutor.getTemplateByNameVersion("approval_template", 1); + +// Get template by ID (deprecated, use getTemplateByNameVersion) +const templateById = await humanExecutor.getTemplateById("approval_template"); +``` + +## SDK Factory Functions + +The `MetadataClient` manages task and workflow definitions: + +```typescript +import { MetadataClient } from "@io-orkes/conductor-javascript"; + +const metadataClient = new MetadataClient(client); + +// Register a task definition +await metadataClient.registerTask({ + name: "email_sender", + description: "Sends email notifications", + timeoutSeconds: 300, + retryCount: 3, + retryDelaySeconds: 60, + retryLogic: "FIXED", + timeoutPolicy: "RETRY", + inputKeys: ["to", "subject", "body"], + outputKeys: ["messageId", "status"], + rateLimitPerFrequency: 100, + rateLimitFrequencyInSeconds: 60 +}); + +// Update an existing task definition +await metadataClient.updateTask({ + name: "email_sender", + description: "Updated email sender task", + timeoutSeconds: 600, // Increased timeout + retryCount: 5, // Increased retries + // ... other properties +}); + +// Unregister a task definition +await metadataClient.unregisterTask("email_sender"); + +// Register a workflow definition +await metadataClient.registerWorkflowDef({ + name: "notification_workflow", + version: 1, + description: "Sends notifications to users", + tasks: [ + simpleTask("send_email", "email_sender", {}), + simpleTask("send_sms", "sms_sender", {}) + ], + inputParameters: ["userId", "message"], + outputParameters: { + emailSent: "${send_email.output.success}", + smsSent: "${send_sms.output.success}" + }, + timeoutSeconds: 3600, + timeoutPolicy: "ALERT_ONLY", + retryPolicy: { + retryCount: 2, + retryDelaySeconds: 120, + retryLogic: "EXPONENTIAL_BACKOFF" + } +}, true); // overwrite = true + +// Unregister a workflow definition +await metadataClient.unregisterWorkflow("notification_workflow", 1); +``` + +### TemplateClient + +The `TemplateClient` manages human task templates: + +```typescript +import { TemplateClient } from "@io-orkes/conductor-javascript"; + +const templateClient = new TemplateClient(client); + +// Register a human task template +await templateClient.registerTemplate({ + name: "approval_template", + version: 1, + description: "Template for approval tasks", + formTemplate: { + fields: [ + { + name: "approved", + type: "boolean", + required: true, + description: "Whether the request is approved" + }, + { + name: "comments", + type: "text", + required: false, + description: "Additional comments" + }, + { + name: "priority", + type: "select", + required: true, + options: ["low", "medium", "high"], + defaultValue: "medium" + } + ] + }, + uiTemplate: { + template: ` +
+

Approval Request

+
+ + +
+
+ + +
+
+ + +
+
+ ` + } +}, false); // asNewVersion = false + +// Register a new version of existing template +await templateClient.registerTemplate({ + name: "approval_template", + version: 2, + description: "Updated approval template with new fields", + formTemplate: { + fields: [ + { + name: "approved", + type: "boolean", + required: true + }, + { + name: "comments", + type: "text", + required: false + }, + { + name: "approver", + type: "text", + required: true, + description: "Name of the approver" + }, + { + name: "approvalDate", + type: "date", + required: true, + description: "Date of approval" + } + ] + } +}, true); // asNewVersion = true +``` + +## SDK Factory Functions + +### Workflow Factory + +```typescript +import { workflow } from "@io-orkes/conductor-javascript"; + +const myWorkflow = workflow("workflow_name", [ + simpleTask("task1", "process_1", {}), + simpleTask("task2", "process_2", {}) +]); +``` + +### Task Definition Factory + +```typescript +import { taskDefinition } from "@io-orkes/conductor-javascript"; + +const taskDef = taskDefinition("task_name", { + timeoutSeconds: 300, + retryCount: 3, + retryDelaySeconds: 60, + retryLogic: "FIXED", + timeoutPolicy: "RETRY", + inputKeys: ["param1", "param2"], + outputKeys: ["result"], + rateLimitPerFrequency: 100, + rateLimitFrequencyInSeconds: 60 +}); +``` + +## Error Handling + +### Worker Error Handling + +```typescript +const worker: ConductorWorker = { + taskDefName: "error_prone_task", + execute: async (task) => { + try { + const result = await riskyOperation(task.inputData); + return { + outputData: result, + status: "COMPLETED" + }; + } catch (error) { + return { + outputData: {}, + status: "FAILED", + reasonForIncompletion: error.message + }; + } + } +}; +``` + +### Task Manager Error Handling + +```typescript +const manager = new TaskManager(client, workers, { + onError: (error, task) => { + console.error(`Error processing task ${task.taskId}:`, error); + // Custom error handling logic + }, + maxRetries: 3 +}); +``` + +### Workflow Error Handling + +```typescript +try { + const executionId = await executor.startWorkflow({ + name: "workflow_name", + version: 1, + input: {} + }); +} catch (error) { + console.error("Failed to start workflow:", error); +} +``` + +## Logging + +### Default Logger + +```typescript +import { DefaultLogger } from "@io-orkes/conductor-javascript"; + +const logger = new DefaultLogger(); +``` + +### Custom Logger + +```typescript +import { ConductorLogger } from "@io-orkes/conductor-javascript"; + +class CustomLogger implements ConductorLogger { + info(message: string, ...args: any[]): void { + console.log(`[INFO] ${message}`, ...args); + } + + error(message: string, ...args: any[]): void { + console.error(`[ERROR] ${message}`, ...args); + } + + debug(message: string, ...args: any[]): void { + console.debug(`[DEBUG] ${message}`, ...args); + } +} + +const manager = new TaskManager(client, workers, { + logger: new CustomLogger() +}); +``` + +## Best Practices + +### Worker Design + +1. **Stateless**: Workers should be stateless and not maintain workflow-specific state +2. **Idempotent**: Workers should handle cases where tasks are rescheduled due to timeouts +3. **Specific**: Each worker should execute a very specific task with well-defined inputs/outputs +4. **No Retry Logic**: Let Conductor handle retries; workers should focus on task execution + +### Workflow Design + +1. **Clear Naming**: Use descriptive names for workflows and tasks +2. **Versioning**: Always version your workflows +3. **Input Validation**: Validate workflow inputs +4. **Error Handling**: Include proper error handling in workflows + +### Performance + +1. **Polling Intervals**: Adjust polling intervals based on your workload +2. **Concurrency**: Set appropriate concurrency levels for workers +3. **Batch Polling**: Use batch polling for better performance +4. **Connection Pooling**: Configure HTTP connection pooling with `maxHttp2Connections` + +### Security + +1. **Environment Variables**: Use environment variables for sensitive configuration +2. **Access Control**: Implement proper access control +3. **Input Validation**: Validate all inputs to prevent injection attacks +4. **Secure Communication**: Use HTTPS for all communications + +## API Reference + +### Core Classes + +- `WorkflowExecutor`: Main class for workflow management +- `TaskManager`: Manages multiple workers +- `TaskRunner`: Handles individual worker execution +- `TaskClient`: Additional task management capabilities +- `SchedulerClient`: Manages workflow scheduling +- `ServiceRegistryClient`: Manages service registrations and circuit breakers +- `HumanExecutor`: Comprehensive human task management +- `TemplateClient`: Manages human task templates +- `MetadataClient`: Manages task and workflow definitions +- `ConductorWorker`: Interface for defining workers + +### Task Generators + +- `simpleTask()`: Creates simple tasks +- `httpTask()`: Creates HTTP tasks +- `switchTask()`: Creates conditional tasks +- `forkJoinTask()`: Creates parallel execution tasks +- `doWhileTask()`: Creates loop tasks +- `subWorkflowTask()`: Creates sub-workflow tasks +- `eventTask()`: Creates event-driven tasks +- `waitTask()`: Creates wait tasks +- `terminateTask()`: Creates termination tasks +- `setVariableTask()`: Creates variable setting tasks +- `jsonJqTask()`: Creates JSON transformation tasks +- `kafkaPublishTask()`: Creates Kafka publishing tasks +- `inlineTask()`: Creates inline script tasks +- `dynamicForkTask()`: Creates dynamic fork tasks +- `joinTask()`: Creates join tasks +- `humanTask()`: Creates human tasks + +### Factory Functions + +- `workflow()`: Creates workflow definitions +- `taskDefinition()`: Creates task definitions + +### Configuration Options + +#### OrkesApiConfig +- `serverUrl`: Conductor server URL +- `keyId`: Authentication key ID +- `keySecret`: Authentication key secret +- `refreshTokenInterval`: Token refresh interval (0 = no refresh) +- `maxHttp2Connections`: Maximum HTTP2 connections (default: 1) +- `useEnvVars`: DEPRECATED, has no effect + +#### TaskManagerOptions +- `pollInterval`: Polling interval in milliseconds +- `concurrency`: Number of concurrent task executions +- `workerID`: Unique worker identifier +- `domain`: Task domain +- `batchPollingTimeout`: Batch polling timeout + +#### Environment Variables +- `CONDUCTOR_SERVER_URL`: Server URL +- `CONDUCTOR_AUTH_KEY`: Authentication key +- `CONDUCTOR_AUTH_SECRET`: Authentication secret +- `CONDUCTOR_MAX_HTTP2_CONNECTIONS`: Maximum HTTP2 connections + +## Examples + +### Complete Example: Order Processing Workflow + +```typescript +import { + orkesConductorClient, + WorkflowExecutor, + TaskManager, + SchedulerClient, + workflow, + simpleTask, + switchTask, + forkJoinTask, + httpTask +} from "@io-orkes/conductor-javascript"; + +async function setupOrderProcessing() { + // Create client + const client = await orkesConductorClient({ + serverUrl: "https://play.orkes.io/api", + keyId: "your-key-id", + keySecret: "your-key-secret" +}); + + // Create workflow executor +const executor = new WorkflowExecutor(client); + const scheduler = new SchedulerClient(client); + + // Define order processing workflow + const orderWorkflow = workflow("order_processing", [ + // Validate order + simpleTask("validate_order", "order_validation", {}), + + // Check inventory + httpTask("check_inventory", "https://inventory-api.com/check", { + method: "POST" + }), + + // Process payment based on payment method + switchTask("process_payment", "input.paymentMethod", { + "credit_card": [ + httpTask("charge_card", "https://payment-api.com/charge", { + method: "POST" + }) + ], + "paypal": [ + httpTask("paypal_payment", "https://paypal-api.com/pay", { + method: "POST" + }) + ], + "default": [ + simpleTask("manual_payment", "manual_payment_processing", {}) + ] + }), + + // Parallel fulfillment tasks + forkJoinTask("fulfillment", [ + [httpTask("update_inventory", "https://inventory-api.com/update", { + method: "POST" + })], + [simpleTask("send_notification", "email_notification", {})], + [simpleTask("create_shipment", "shipping_task", {})] + ]), + + // Final confirmation + simpleTask("confirm_order", "order_confirmation", {}) + ]); + + // Register workflow + await executor.registerWorkflow(true, orderWorkflow); + + // Schedule daily order processing + await scheduler.saveSchedule({ + name: "daily_order_processing", + cronExpression: "0 0 2 * * ?", // Every day at 2 AM + startWorkflowRequest: { + name: "order_processing", + version: 1, + input: { batchProcessing: true } + } + }); + + // Define workers + const workers = [ + { + taskDefName: "order_validation", + execute: async (task) => { + const order = task.inputData; + // Validate order logic + return { + outputData: { valid: true, orderId: order.id }, + status: "COMPLETED" + }; + } + }, + { + taskDefName: "email_notification", + execute: async (task) => { + // Send email logic + return { + outputData: { sent: true }, + status: "COMPLETED" + }; + } + }, + { + taskDefName: "shipping_task", + execute: async (task) => { + // Create shipping label logic + return { + outputData: { trackingNumber: "TRK123456" }, + status: "COMPLETED" + }; + } + } + ]; + + // Create task manager + const manager = new TaskManager(client, workers, { + options: { + pollInterval: 1000, + concurrency: 2, + workerID: "order-processor", + domain: "production" + } + }); + + // Start workers + manager.startPolling(); + + // Start workflow execution +const executionId = await executor.startWorkflow({ + name: "order_processing", + version: 1, + input: { + orderId: "ORD-123", + customerId: "CUST-456", + paymentMethod: "credit_card", + items: [{ id: "ITEM-1", quantity: 2 }] + } + }); + + console.log(`Order processing started: ${executionId}`); + + return { executor, manager, scheduler, executionId }; +} +``` + +## Troubleshooting + +### Common Issues + +#### Authentication Errors + +```typescript +// Check your credentials +const client = await orkesConductorClient({ + serverUrl: "https://play.orkes.io/api", + keyId: "your-key-id", // Make sure this is correct + keySecret: "your-key-secret" // Make sure this is correct +}); +``` + +#### Worker Not Receiving Tasks + +1. Check if the worker is registered with the correct task definition name +2. Verify the task manager is polling +3. Check the Conductor server logs for errors +4. Verify the domain configuration matches + +#### Workflow Execution Issues + +1. Verify the workflow is registered +2. Check workflow input parameters +3. Review task dependencies +4. Check scheduler status if using scheduled workflows + +#### Service Registry Issues + +1. Verify service is properly registered +2. Check circuit breaker status +3. Ensure service URI is accessible +4. Verify proto files for gRPC services + +### Debug Mode + +Enable debug logging to troubleshoot issues: + +```typescript +import { DefaultLogger } from "@io-orkes/conductor-javascript"; + +const logger = new DefaultLogger(); +logger.debug("Debug message", { data: "value" }); +``` + +### Health Checks + +```typescript +// Check if the Conductor server is healthy +const health = await client.healthCheckResource.healthCheck(); +console.log("Server health:", health); + +// Check scheduler status +const schedules = await scheduler.getAllSchedules(); +console.log("Active schedules:", schedules.length); + +// Check service registry +const services = await serviceRegistry.getRegisteredServices(); +console.log("Registered services:", services.length); +``` + +--- + +For more examples and detailed API documentation, visit the [GitHub repository](https://github.com/conductor-sdk/conductor-javascript) or the [Orkes Conductor documentation](https://orkes.io/content/docs/). \ No newline at end of file From a0faa9ef7f5318a232cf3a1f6392548a4e8cb54f Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Thu, 2 Oct 2025 12:42:48 +0300 Subject: [PATCH 02/60] Update README.md --- README.md | 445 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 249 insertions(+), 196 deletions(-) diff --git a/README.md b/README.md index b5e3a8ac..f960e2cc 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,9 @@ The `@io-orkes/conductor-javascript` SDK provides a comprehensive TypeScript/Jav - [Quick Start](#quick-start) - [Authentication & Configuration](#authentication--configuration) - [Core Concepts](#core-concepts) -- [Client Management](#client-management) - [Task Types](#task-types) -- [SDK Factory Functions](#sdk-factory-functions) - [Workflow Management](#workflow-management) -- [Task Management](#task-management) +- [Task Management](#worker-management) - [Scheduler Management](#scheduler-management) - [Service Registry](#service-registry) - [Human Tasks](#human-tasks) @@ -133,28 +131,17 @@ Workflows are the main orchestration units in Conductor. They define a sequence ### Workers Workers are applications that execute specific types of tasks. They poll for work and execute tasks assigned to them. -### Task Definitions -Task definitions describe the metadata and configuration for tasks, including retry policies, timeouts, and input/output schemas. +### Scheduler +The scheduler allows you to schedule workflows to run at specific times or intervals, enabling automated workflow execution based on time-based triggers. -## Client Management - -### OrkesConductorClient +## Task Types -The `orkesConductorClient` function creates a configured client instance: +The SDK provides generators for various task types. These generators can be used in two ways: -```typescript -import { orkesConductorClient } from "@io-orkes/conductor-javascript"; +1. **Creating Workflows** - Use task generators to build workflow definitions +2. **Registering Metadata** - Use task generators to create task definitions for registration -const client = await orkesConductorClient({ - serverUrl: "https://play.orkes.io/api", - keyId: "your-key-id", - keySecret: "your-key-secret" -}); -``` - -## Task Types - -The SDK provides generators for various task types: +### Available Task Generators ### Simple Task @@ -332,66 +319,34 @@ const task = humanTask("human_ref", "approval_task", { }); ``` -## SDK Factory Functions - -### Workflow Factory +### Usage Examples +#### In Workflow Creation ```typescript -import { workflow } from "@io-orkes/conductor-javascript"; +import { workflow, simpleTask, httpTask } from "@io-orkes/conductor-javascript"; -const myWorkflow = workflow("workflow_name", [ - simpleTask("task1", "process_1", {}), - simpleTask("task2", "process_2", {}) +const myWorkflow = workflow("order_processing", [ + simpleTask("validate_order", "validate_order_task", {}), + httpTask("call_payment", "https://api.payment.com/charge", { + method: "POST", + headers: { "Authorization": "Bearer token" } + }), + simpleTask("send_confirmation", "send_email_task", {}) ]); ``` -### Task Definition Factory - +#### In Metadata Registration ```typescript -import { taskDefinition } from "@io-orkes/conductor-javascript"; +import { MetadataClient, simpleTask, httpTask } from "@io-orkes/conductor-javascript"; -const taskDef = taskDefinition("task_name", { - timeoutSeconds: 300, - retryCount: 3, - retryDelaySeconds: 60, - responseTimeoutSeconds: 300, - pollTimeoutSeconds: 300, - pollIntervalSeconds: 30, - concurrentExecLimit: 10, - rateLimitPerFrequency: 100, - rateLimitFrequencyInSeconds: 60, - ownerEmail: "owner@example.com", - description: "Task description", - inputTemplate: { - param1: "default_value" - }, - outputTemplate: { - result: "computed_value" - }, - inputKeys: ["param1", "param2"], - outputKeys: ["result"], - tags: ["tag1", "tag2"], - executionNameSpace: "namespace", - isolationGroupId: "isolation_group", - maxConcurrentExecutions: 5, - concurrentExecLimit: 10, - rateLimitPerFrequency: 100, - rateLimitFrequencyInSeconds: 60, - ownerEmail: "owner@example.com", - description: "Task description", - inputTemplate: { - param1: "default_value" - }, - outputTemplate: { - result: "computed_value" - }, - inputKeys: ["param1", "param2"], - outputKeys: ["result"], - tags: ["tag1", "tag2"], - executionNameSpace: "namespace", - isolationGroupId: "isolation_group", - maxConcurrentExecutions: 5 -}); +const metadataClient = new MetadataClient(client); + +// Register individual task definitions +await metadataClient.registerTask(simpleTask("validate_order", "validate_order_task", {})); +await metadataClient.registerTask(httpTask("call_payment", "https://api.payment.com/charge", { + method: "POST", + headers: { "Authorization": "Bearer token" } +})); ``` ## Workflow Management @@ -406,6 +361,19 @@ import { WorkflowExecutor } from "@io-orkes/conductor-javascript"; const executor = new WorkflowExecutor(client); ``` +### Workflow Factory + +The `workflow` function provides a convenient way to create workflow definitions: + +```typescript +import { workflow, simpleTask } from "@io-orkes/conductor-javascript"; + +const myWorkflow = workflow("workflow_name", [ + simpleTask("task1", "process_1", {}), + simpleTask("task2", "process_2", {}) +]); +``` + #### Register Workflow ```typescript @@ -439,6 +407,54 @@ const workflowStatus = await executor.getWorkflow(executionId, true); console.log(`Status: ${workflowStatus.status}`); ``` +The `getWorkflow()` method returns a `Workflow` object with the following properties: + +```typescript +interface Workflow { + // Basic identification + workflowId?: string; + workflowName?: string; + workflowVersion?: number; + + // Status and timing + status?: 'RUNNING' | 'COMPLETED' | 'FAILED' | 'TIMED_OUT' | 'TERMINATED' | 'PAUSED'; + createTime?: number; + updateTime?: number; + startTime?: number; + endTime?: number; + lastRetriedTime?: number; + + // Data + input?: Record; + output?: Record; + variables?: Record; + + // Relationships + parentWorkflowId?: string; + parentWorkflowTaskId?: string; + reRunFromWorkflowId?: string; + correlationId?: string; + + // Tasks and execution + tasks?: Array; + failedReferenceTaskNames?: Array; + taskToDomain?: Record; + + // Configuration + priority?: number; + externalInputPayloadStoragePath?: string; + externalOutputPayloadStoragePath?: string; + + // Metadata + ownerApp?: string; + createdBy?: string; + updatedBy?: string; + reasonForIncompletion?: string; + event?: string; + workflowDefinition?: WorkflowDef; +} +``` + #### Pause Workflow ```typescript @@ -467,20 +483,95 @@ const searchResults = await executor.searchWorkflows({ }); ``` -## Task Management +## Worker Management + +### Overview + +Workers are applications that execute specific types of tasks. The SDK provides two main approaches for managing workers: + +- **TaskManager** - High-level interface for managing multiple workers (recommended) +- **TaskRunner** - Low-level interface for individual worker control + +### Worker Design Principles + +When creating workers, follow these principles: + +#### 1. Stateless Workers +Workers should be stateless and not rely on external state: + +```typescript +// ✅ Good - Stateless +const worker: ConductorWorker = { + taskDefName: "process_data", + execute: async (task) => { + const result = await processData(task.inputData); + return { outputData: result, status: "COMPLETED" }; + } +}; + +// ❌ Bad - Stateful +let processedCount = 0; +const worker: ConductorWorker = { + taskDefName: "process_data", + execute: async (task) => { + processedCount++; // This creates state dependency + return { outputData: { count: processedCount }, status: "COMPLETED" }; + } +}; +``` + +#### 2. Idempotent Operations +Workers should produce the same result when executed multiple times: + +```typescript +// ✅ Good - Idempotent +const worker: ConductorWorker = { + taskDefName: "update_user", + execute: async (task) => { + const { userId, data } = task.inputData; + await updateUser(userId, data); // Safe to retry + return { outputData: { updated: true }, status: "COMPLETED" }; + } +}; +``` + +#### 3. Specific Task Types +Each worker should handle one specific task type: + +```typescript +// ✅ Good - Specific +const emailWorker: ConductorWorker = { + taskDefName: "send_email", + execute: async (task) => { + await sendEmail(task.inputData); + return { outputData: { sent: true }, status: "COMPLETED" }; + } +}; + +// ❌ Bad - Generic +const genericWorker: ConductorWorker = { + taskDefName: "do_anything", + execute: async (task) => { + // Handles multiple different operations - hard to maintain + if (task.inputData.type === "email") { /* ... */ } + else if (task.inputData.type === "sms") { /* ... */ } + // ... + } +}; +``` -### TaskManager +### TaskManager (Recommended) -The `TaskManager` class manages multiple workers and handles task polling: +`TaskManager` is the high-level interface that manages multiple workers and their corresponding `TaskRunner` instances. It's the recommended approach for most use cases. ```typescript -import { TaskManager, ConductorWorker } from "@io-orkes/conductor-javascript"; +import { TaskManager, ConductorWorker, DefaultLogger } from "@io-orkes/conductor-javascript"; const workers: ConductorWorker[] = [ { taskDefName: "greeting_task", execute: async (task) => { - return { + return { outputData: { greeting: "Hello!" }, status: "COMPLETED" }; @@ -489,6 +580,7 @@ const workers: ConductorWorker[] = [ ]; const manager = new TaskManager(client, workers, { + logger: new DefaultLogger(), options: { pollInterval: 1000, concurrency: 2, @@ -496,131 +588,46 @@ const manager = new TaskManager(client, workers, { domain: "production", batchPollingTimeout: 100 }, - logger: customLogger, - onError: errorHandler, + onError: (error) => console.error("Worker error:", error), maxRetries: 3 }); -// Start polling -manager.startPolling(); +// Start all workers +await manager.startPolling(); // Update polling options manager.updatePollingOptions({ pollInterval: 500 }); -// Stop polling -await manager.stopPolling(); -``` - -#### TaskManager Configuration - -The `TaskManager` supports comprehensive configuration options: - -```typescript -interface TaskManagerConfig { - logger?: ConductorLogger; - options?: Partial; - onError?: TaskErrorHandler; - maxRetries?: number; -} - -interface TaskManagerOptions { - workerID?: string; // Unique worker identifier - pollInterval?: number; // Polling interval in milliseconds - domain?: string; // Task domain for isolation - concurrency?: number; // Number of concurrent executions - batchPollingTimeout?: number; // Batch polling timeout -} -``` - -#### Starting Workers - -There are two ways to start workers in the SDK: - -##### Using TaskManager (Recommended) - -`TaskManager` is the high-level interface that manages multiple workers and their corresponding `TaskRunner` instances. It's the recommended approach for most use cases. - -```typescript -import { - OrkesApiConfig, - orkesConductorClient, - TaskManager, -} from "@io-orkes/conductor-javascript"; - -const client = await orkesConductorClient({ - keyId: "XXX", - keySecret: "XXXX", - serverUrl: "https://play.orkes.io/api", -}); - -const taskDefName = "HelloWorldWorker"; - -const customWorker: ConductorWorker = { - taskDefName, - execute: async ({ inputData, taskId }) => { - return { - outputData: { - greeting: "Hello World", - }, - status: "COMPLETED", - }; - }, -}; - -const taskManager = new TaskManager(client, [customWorker], { - logger: new DefaultLogger(), - options: { - pollInterval: 1000, - concurrency: 1, - workerID: "my-worker", - }, -}); - -// Start all workers -await taskManager.startPolling(); - // Stop all workers -await taskManager.shutdown(); +await manager.shutdown(); ``` -##### Using TaskRunner (Low-level) +### TaskRunner (Low-level) `TaskRunner` is the low-level interface used internally by `TaskManager`. It handles individual worker execution, polling the server for work, and updating results back to the server. Use this when you need fine-grained control over a single worker. ```typescript -import { - orkesConductorClient, - TaskRunner, - DefaultLogger, -} from "@io-orkes/conductor-javascript"; +import { TaskRunner, ConductorWorker, DefaultLogger } from "@io-orkes/conductor-javascript"; -const client = await orkesConductorClient({ - keyId: "XXX", - keySecret: "XXXX", - serverUrl: "https://play.orkes.io/api", -}); - -const customWorker: ConductorWorker = { +const worker: ConductorWorker = { taskDefName: "HelloWorldWorker", execute: async ({ inputData, taskId }) => { return { - outputData: { - greeting: "Hello World", - }, - status: "COMPLETED", + outputData: { greeting: "Hello World" }, + status: "COMPLETED" }; - }, + } }; const taskRunner = new TaskRunner({ - worker: customWorker, + worker: worker, taskResource: client.taskResource, options: { pollInterval: 1000, concurrency: 1, - workerID: "my-worker", + workerID: "my-worker" }, - logger: new DefaultLogger(), + logger: new DefaultLogger() }); // Start the worker @@ -630,33 +637,45 @@ await taskRunner.startPolling(); await taskRunner.shutdown(); ``` -##### When to Use Each Approach +### Configuration Options -- **Use TaskManager** when you have multiple workers or want the convenience of managing all workers together -- **Use TaskRunner** when you need fine-grained control over a single worker or want to implement custom worker management logic +#### TaskManager Configuration -// Worker Options will take precedence over options defined in the manager -const manager = new TaskManager(client, [customWorker], { - options: { pollInterval: 100, concurrency: 1 }, -}); +```typescript +interface TaskManagerConfig { + logger?: ConductorLogger; + options?: Partial; + onError?: TaskErrorHandler; + maxRetries?: number; +} -manager.startPolling(); +interface TaskManagerOptions { + workerID?: string; // Unique worker identifier + pollInterval?: number; // Polling interval in milliseconds + domain?: string; // Task domain for isolation + concurrency?: number; // Number of concurrent executions + batchPollingTimeout?: number; // Batch polling timeout +} +``` -// You can update all worker settings at once using -manager.updatePollingOptions({ pollInterval: 100, concurrency: 1 }); +#### TaskRunner Configuration -// You can update a single worker setting using: -manager.updatePollingOptionForWorker(taskDefName, { - pollInterval: 100, - concurrency: 1, -}); +```typescript +interface TaskRunnerOptions { + workerID?: string; + pollInterval?: number; + domain?: string; + concurrency?: number; + batchPollingTimeout?: number; +} +``` -manager.isPolling; // Will resolve to true +### When to Use Each Approach -await manager.stopPolling(); +- **Use TaskManager** when you have multiple workers or want the convenience of managing all workers together +- **Use TaskRunner** when you need fine-grained control over a single worker or want to implement custom worker management logic -manager.isPolling; // Will resolve to false -``` +## Scheduler Management #### Polling Mechanism @@ -862,7 +881,7 @@ const runner = new TaskRunner({ taskResource: client.taskResource, options: { pollInterval: 1000, - concurrency: 1, + concurrency: 1, workerID: "worker-1", domain: "production", batchPollingTimeout: 100 @@ -1106,6 +1125,40 @@ import { MetadataClient } from "@io-orkes/conductor-javascript"; const metadataClient = new MetadataClient(client); ``` +### Task Definition Factory + +The `taskDefinition` function provides a convenient way to create task definitions: + +```typescript +import { taskDefinition } from "@io-orkes/conductor-javascript"; + +const taskDef = taskDefinition("task_name", { + timeoutSeconds: 300, + retryCount: 3, + retryDelaySeconds: 60, + responseTimeoutSeconds: 300, + pollTimeoutSeconds: 300, + pollIntervalSeconds: 30, + concurrentExecLimit: 10, + rateLimitPerFrequency: 100, + rateLimitFrequencyInSeconds: 60, + ownerEmail: "owner@example.com", + description: "Task description", + inputTemplate: { + param1: "default_value" + }, + outputTemplate: { + result: "computed_value" + }, + inputKeys: ["param1", "param2"], + outputKeys: ["result"], + tags: ["tag1", "tag2"], + executionNameSpace: "namespace", + isolationGroupId: "isolation_group", + maxConcurrentExecutions: 5 +}); +``` + #### Register Task Definition ```typescript From 89d72c16f581d5e4ba850f2747a4c0bdbe631ecd Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Thu, 2 Oct 2025 14:22:58 +0300 Subject: [PATCH 03/60] Update README.md --- README.md | 355 ++++++++++++------------------------------------------ 1 file changed, 78 insertions(+), 277 deletions(-) diff --git a/README.md b/README.md index f960e2cc..549b86bc 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,8 @@ The `@io-orkes/conductor-javascript` SDK provides a comprehensive TypeScript/Jav - [Core Concepts](#core-concepts) - [Task Types](#task-types) - [Workflow Management](#workflow-management) -- [Task Management](#worker-management) +- [Worker Management](#worker-management) +- [Task Management](#task-management) - [Scheduler Management](#scheduler-management) - [Service Registry](#service-registry) - [Human Tasks](#human-tasks) @@ -675,319 +676,119 @@ interface TaskRunnerOptions { - **Use TaskManager** when you have multiple workers or want the convenience of managing all workers together - **Use TaskRunner** when you need fine-grained control over a single worker or want to implement custom worker management logic -## Scheduler Management - -#### Polling Mechanism - -The TaskManager uses a sophisticated polling mechanism: +## Task Management -1. **Batch Polling**: Workers poll for multiple tasks at once for better performance -2. **Concurrent Execution**: Multiple tasks can be executed simultaneously -3. **Domain Isolation**: Tasks can be isolated by domain -4. **Worker Identification**: Each worker has a unique identifier -5. **Retry Logic**: Built-in retry mechanism for failed operations +### TaskClient -#### Polling Configuration Examples +The `TaskClient` provides additional task management capabilities for querying and updating existing tasks: ```typescript -// High-frequency polling for critical tasks -const criticalManager = new TaskManager(client, criticalWorkers, { - options: { - pollInterval: 100, // Poll every 100ms - concurrency: 5, // High concurrency - batchPollingTimeout: 50 // Quick batch timeout - } -}); - -// Low-frequency polling for background tasks -const backgroundManager = new TaskManager(client, backgroundWorkers, { - options: { - pollInterval: 5000, // Poll every 5 seconds - concurrency: 1, // Single execution - batchPollingTimeout: 1000 // Longer batch timeout - } -}); +import { TaskClient } from "@io-orkes/conductor-javascript"; -// Domain-specific polling -const productionManager = new TaskManager(client, productionWorkers, { - options: { - domain: "production", - pollInterval: 1000, - concurrency: 2 - } -}); -``` +const taskClient = new TaskClient(client); -### ConductorWorker +// Search tasks +const searchResults = await taskClient.search(0, 10, "", "*", "status:COMPLETED"); -Define workers using the `ConductorWorker` interface: +// Get task by ID +const task = await taskClient.getTask(taskId); -```typescript -const worker: ConductorWorker = { - taskDefName: "my_task", - execute: async (task) => { - // Process the task - const result = await processTask(task.inputData); - - return { - outputData: result, - status: "COMPLETED" - }; - }, - pollInterval: 1000, // optional - concurrency: 1, // optional - domain: "production" // optional -}; +// Update task result +await taskClient.updateTaskResult( + workflowId, + taskReferenceName, + "COMPLETED", + { result: "success" } +); ``` -#### Worker Design Principles +### Task Status and Monitoring -Each worker embodies design patterns and follows certain basic principles: +Tasks in Conductor have various statuses that indicate their current state: -1. **Workers are stateless** and do not implement workflow-specific logic -2. **Each worker executes a very specific task** and produces well-defined output given specific inputs -3. **Workers are meant to be idempotent** (or should handle cases where the task that is partially executed gets rescheduled due to timeouts etc.) -4. **Workers do not implement the logic to handle retries** etc, that is taken care by the Conductor server +- **SCHEDULED**: Task is scheduled for execution +- **IN_PROGRESS**: Task is currently being executed +- **COMPLETED**: Task completed successfully +- **COMPLETED_WITH_ERRORS**: Task completed but with errors +- **FAILED**: Task execution failed +- **FAILED_WITH_TERMINAL_ERROR**: Task failed with a terminal error (no retries) +- **TIMED_OUT**: Task execution timed out +- **CANCELED**: Task was canceled +- **SKIPPED**: Task was skipped -#### Task Worker Implementation +### Task Search and Filtering -Task worker is implemented using a function that conforms to the following interface: +You can search for tasks using various criteria: ```typescript -import { ConductorWorker, Task } from "@io-orkes/conductor-javascript"; +// Search by status +const completedTasks = await taskClient.search(0, 10, "", "*", "status:COMPLETED"); -const worker: ConductorWorker = { - taskDefName: "task-def-name", - execute: async ( - task: Task - ): Promise> => { - // Your task execution logic here - }, -}; -``` +// Search by workflow +const workflowTasks = await taskClient.search(0, 10, "", "*", "workflowId:workflow-123"); -Worker returns an object as the output of the task execution. The object is just a JSON that follows the TaskResult interface. -If an `error` is returned, the task is marked as `FAILED` +// Search by task type +const simpleTasks = await taskClient.search(0, 10, "", "*", "taskType:SIMPLE"); -#### Task Worker that Returns an Object +// Search by free text +const textSearch = await taskClient.search(0, 10, "", "error", ""); -```typescript -import { ConductorWorker, Task } from "@io-orkes/conductor-javascript"; - -const worker: ConductorWorker = { - taskDefName: "task-def-name", - execute: async (task: Task) => { - // Sample output - return { - outputData: { - hello: "From your worker", - }, - status: "COMPLETED", - }; - }, -}; +// Search with sorting +const sortedTasks = await taskClient.search(0, 10, "startTime:DESC", "*", "status:FAILED"); ``` -#### Controlling Execution for Long-running Tasks - -For long-running tasks you might want to spawn another process/routine and update the status of the task at a later point and complete the execution function without actually marking the task as `COMPLETED`. Use `TaskResult` Interface that allows you to specify more fine-grained control. +### Task Debugging -Here is an example of a task execution function that returns with `IN_PROGRESS` status asking server to push the task again in 60 seconds. +When debugging task execution issues: ```typescript -const worker: ConductorWorker = { - taskDefName: "task-def-name", - execute: async (task: Task) => { - // Sample output - return { - outputData: {}, - status: "IN_PROGRESS", - callbackAfterSeconds: 60, - }; - }, - pollInterval: 100, // optional - concurrency: 2, // optional -}; -``` - -#### Worker Configuration Options - -Workers support several configuration options: - -```typescript -const worker: ConductorWorker = { - taskDefName: "my_task", - execute: async (task) => { - return { outputData: {}, status: "COMPLETED" }; - }, - pollInterval: 1000, // Polling interval in milliseconds - concurrency: 2, // Number of concurrent executions - domain: "production" // Task domain for isolation -}; -``` - -#### Worker Error Handling - -```typescript -const worker: ConductorWorker = { - taskDefName: "error_prone_task", - execute: async (task) => { - try { - const result = await riskyOperation(task.inputData); - return { - outputData: result, - status: "COMPLETED" - }; - } catch (error) { - return { - outputData: {}, - status: "FAILED", - reasonForIncompletion: error.message - }; - } - } -}; -``` - -#### Long-running Tasks - -For long-running tasks, you can return `IN_PROGRESS` status: - -```typescript -const worker: ConductorWorker = { - taskDefName: "long_task", - execute: async (task) => { - // Start long-running process - startLongProcess(); - - return { - outputData: {}, - status: "IN_PROGRESS", - callbackAfterSeconds: 60 // Check again in 60 seconds - }; - } -}; -``` - -### TaskRunner - -For more control, use `TaskRunner` directly: - -```typescript -import { TaskRunner } from "@io-orkes/conductor-javascript"; - -const runner = new TaskRunner({ - worker, - taskResource: client.taskResource, - options: { - pollInterval: 1000, - concurrency: 1, - workerID: "worker-1", - domain: "production", - batchPollingTimeout: 100 - }, - logger: customLogger, - onError: errorHandler, - maxRetries: 3 -}); - -runner.startPolling(); -``` - -#### TaskRunner Configuration - -The `TaskRunner` provides fine-grained control over individual worker execution: - -```typescript -interface RunnerArgs { - worker: ConductorWorker; - taskResource: TaskResourceService; - options: TaskRunnerOptions; - logger?: ConductorLogger; - onError?: TaskErrorHandler; - maxRetries?: number; -} - -interface TaskRunnerOptions { - workerID?: string; // Unique worker identifier - pollInterval?: number; // Polling interval in milliseconds - domain?: string; // Task domain for isolation - concurrency?: number; // Number of concurrent executions - batchPollingTimeout?: number; // Batch polling timeout +try { + // Get detailed task information + const task = await taskClient.getTask(taskId); + + console.log("Task Status:", task.status); + console.log("Task Input:", task.inputData); + console.log("Task Output:", task.outputData); + console.log("Retry Count:", task.retryCount); + console.log("Execution Time:", task.endTime - task.startTime); + + // Check for failed tasks + const failedTasks = await taskClient.search(0, 50, "", "*", "status:FAILED"); + failedTasks.results.forEach(task => { + console.log(`Task ${task.taskId} failed: ${task.reasonForIncompletion}`); + }); +} catch (error) { + console.error("Error debugging tasks:", error); } ``` -#### TaskRunner Lifecycle +### Task Search Parameters -```typescript -const runner = new TaskRunner({ - worker: myWorker, - taskResource: client.taskResource, - options: { - pollInterval: 1000, - concurrency: 2, - workerID: "my-worker-1" - } -}); - -// Start polling -runner.startPolling(); +The `search` method accepts the following parameters: -// Check if polling -console.log(runner.isPolling); // true +- `start`: Starting index for pagination (default: 0) +- `size`: Number of results to return (default: 100) +- `sort`: Sort field and direction (e.g., "startTime:DESC", "status:ASC") +- `freeText`: Free text search term (use "*" for all) +- `query`: Structured query string (e.g., "status:FAILED", "workflowId:workflow-123") -// Update options dynamically -runner.updateOptions({ - pollInterval: 500, - concurrency: 3 -}); - -// Get current options -const currentOptions = runner.getOptions; - -// Stop polling -await runner.stopPolling(); -``` - -#### TaskRunner vs TaskManager - -- **TaskRunner**: Manages a single worker with fine-grained control -- **TaskManager**: Manages multiple workers with centralized configuration - -Use `TaskRunner` when you need: -- Individual worker control -- Different polling strategies per worker -- Custom error handling per worker -- Dynamic configuration updates - -Use `TaskManager` when you need: -- Centralized worker management -- Consistent configuration across workers -- Simplified worker lifecycle management -- Bulk operations on multiple workers - -### TaskClient - -The `TaskClient` provides additional task management capabilities: +### Common Search Queries ```typescript -import { TaskClient } from "@io-orkes/conductor-javascript"; +// Find all failed tasks +const failedTasks = await taskClient.search(0, 100, "startTime:DESC", "*", "status:FAILED"); -const taskClient = new TaskClient(client); +// Find tasks for a specific workflow +const workflowTasks = await taskClient.search(0, 100, "", "*", "workflowId:my-workflow-123"); -// Search tasks -const searchResults = await taskClient.search(0, 10, "status:COMPLETED", "", ""); +// Find tasks by worker ID +const workerTasks = await taskClient.search(0, 100, "", "*", "workerId:worker-123"); -// Get task by ID -const task = await taskClient.getTask(taskId); +// Find tasks with specific input data +const inputTasks = await taskClient.search(0, 100, "", "*", "inputData.orderId:order-123"); -// Update task result -await taskClient.updateTaskResult( - workflowId, - taskReferenceName, - "COMPLETED", - { result: "success" } -); +// Find tasks that timed out +const timeoutTasks = await taskClient.search(0, 100, "endTime:DESC", "*", "status:TIMED_OUT"); ``` ## Scheduler Management From f128a5665b830f90ae24dd069280814961e1b27f Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Thu, 2 Oct 2025 16:30:44 +0300 Subject: [PATCH 04/60] Update README.md --- README.md | 238 ++++++++---------------------------------------------- 1 file changed, 33 insertions(+), 205 deletions(-) diff --git a/README.md b/README.md index 549b86bc..efb45687 100644 --- a/README.md +++ b/README.md @@ -91,11 +91,10 @@ import { OrkesApiConfig, orkesConductorClient } from "@io-orkes/conductor-javasc const config: Partial = { serverUrl: "https://play.orkes.io/api", // server api url - keyId: "your-key-id", // authentication key - keySecret: "your-key-secret", // authentication secret - refreshTokenInterval: 0, // optional: token refresh interval, 0 = no refresh (default: 30min) - maxHttp2Connections: 1, // optional: max HTTP2 connections (default: 1) - useEnvVars: false // DEPRECATED: has no effect + keyId: "your-key-id", // authentication key + keySecret: "your-key-secret", // authentication secret + refreshTokenInterval: 0, // optional: token refresh interval, 0 = no refresh (default: 30min) + maxHttp2Connections: 1 // optional: max HTTP2 connections (default: 1) }; const client = await orkesConductorClient(config); @@ -137,10 +136,9 @@ The scheduler allows you to schedule workflows to run at specific times or inter ## Task Types -The SDK provides generators for various task types. These generators can be used in two ways: +The SDK provides generators for various task types to build workflow definitions. These generators create workflow task references that are used within workflow definitions. -1. **Creating Workflows** - Use task generators to build workflow definitions -2. **Registering Metadata** - Use task generators to create task definitions for registration +**Note:** These task generators create workflow task references, not task metadata definitions. To register task metadata (like task definitions with retry policies, timeouts, etc.), use the `taskDefinition()` factory function or plain objects with the `MetadataClient` (see [Metadata Management](#metadata-management) section). ### Available Task Generators @@ -151,7 +149,7 @@ import { simpleTask } from "@io-orkes/conductor-javascript"; const task = simpleTask("task_ref", "task_name", { inputParam: "value" -}, false); // optional parameter +}, false); // optional: if true, workflow continues even if task fails ``` ### HTTP Task @@ -320,9 +318,8 @@ const task = humanTask("human_ref", "approval_task", { }); ``` -### Usage Examples +### Usage Example: Creating Workflows -#### In Workflow Creation ```typescript import { workflow, simpleTask, httpTask } from "@io-orkes/conductor-javascript"; @@ -336,20 +333,6 @@ const myWorkflow = workflow("order_processing", [ ]); ``` -#### In Metadata Registration -```typescript -import { MetadataClient, simpleTask, httpTask } from "@io-orkes/conductor-javascript"; - -const metadataClient = new MetadataClient(client); - -// Register individual task definitions -await metadataClient.registerTask(simpleTask("validate_order", "validate_order_task", {})); -await metadataClient.registerTask(httpTask("call_payment", "https://api.payment.com/charge", { - method: "POST", - headers: { "Authorization": "Bearer token" } -})); -``` - ## Workflow Management ### WorkflowExecutor @@ -388,6 +371,7 @@ const workflowDef = { timeoutSeconds: 0 }; +// Register workflow (overwrite=true means it will replace existing definition) await executor.registerWorkflow(true, workflowDef); ``` @@ -459,29 +443,31 @@ interface Workflow { #### Pause Workflow ```typescript -await executor.pauseWorkflow(executionId, "Pausing for maintenance"); +await executor.pause(executionId); ``` #### Resume Workflow ```typescript -await executor.resumeWorkflow(executionId); +await executor.resume(executionId); ``` #### Terminate Workflow ```typescript -await executor.terminateWorkflow(executionId, "Terminating due to error"); +await executor.terminate(executionId, "Terminating due to error"); ``` #### Workflow Search ```typescript -const searchResults = await executor.searchWorkflows({ - query: "status:RUNNING", - start: 0, - size: 10 -}); +const searchResults = await executor.search( + 0, // start + 10, // size + "status:RUNNING", // query + "*", // freeText + "startTime:DESC" // sort (optional) +); ``` ## Worker Management @@ -600,7 +586,7 @@ await manager.startPolling(); manager.updatePollingOptions({ pollInterval: 500 }); // Stop all workers -await manager.shutdown(); +await manager.stopPolling(); ``` ### TaskRunner (Low-level) @@ -635,7 +621,7 @@ const taskRunner = new TaskRunner({ await taskRunner.startPolling(); // Stop the worker -await taskRunner.shutdown(); +await taskRunner.stopPolling(); ``` ### Configuration Options @@ -933,7 +919,8 @@ The `taskDefinition` function provides a convenient way to create task definitio ```typescript import { taskDefinition } from "@io-orkes/conductor-javascript"; -const taskDef = taskDefinition("task_name", { +const taskDef = taskDefinition({ + name: "task_name", timeoutSeconds: 300, retryCount: 3, retryDelaySeconds: 60, @@ -1004,7 +991,7 @@ const updatedTaskDef = { retryCount: 5 // Increased retry count }; -await metadataClient.updateTask(taskDef.name, updatedTaskDef); +await metadataClient.updateTask(updatedTaskDef); ``` #### Unregister Task Definition @@ -1436,165 +1423,6 @@ const templateById = await humanExecutor.getTemplateById("approval_template"); ## SDK Factory Functions -The `MetadataClient` manages task and workflow definitions: - -```typescript -import { MetadataClient } from "@io-orkes/conductor-javascript"; - -const metadataClient = new MetadataClient(client); - -// Register a task definition -await metadataClient.registerTask({ - name: "email_sender", - description: "Sends email notifications", - timeoutSeconds: 300, - retryCount: 3, - retryDelaySeconds: 60, - retryLogic: "FIXED", - timeoutPolicy: "RETRY", - inputKeys: ["to", "subject", "body"], - outputKeys: ["messageId", "status"], - rateLimitPerFrequency: 100, - rateLimitFrequencyInSeconds: 60 -}); - -// Update an existing task definition -await metadataClient.updateTask({ - name: "email_sender", - description: "Updated email sender task", - timeoutSeconds: 600, // Increased timeout - retryCount: 5, // Increased retries - // ... other properties -}); - -// Unregister a task definition -await metadataClient.unregisterTask("email_sender"); - -// Register a workflow definition -await metadataClient.registerWorkflowDef({ - name: "notification_workflow", - version: 1, - description: "Sends notifications to users", - tasks: [ - simpleTask("send_email", "email_sender", {}), - simpleTask("send_sms", "sms_sender", {}) - ], - inputParameters: ["userId", "message"], - outputParameters: { - emailSent: "${send_email.output.success}", - smsSent: "${send_sms.output.success}" - }, - timeoutSeconds: 3600, - timeoutPolicy: "ALERT_ONLY", - retryPolicy: { - retryCount: 2, - retryDelaySeconds: 120, - retryLogic: "EXPONENTIAL_BACKOFF" - } -}, true); // overwrite = true - -// Unregister a workflow definition -await metadataClient.unregisterWorkflow("notification_workflow", 1); -``` - -### TemplateClient - -The `TemplateClient` manages human task templates: - -```typescript -import { TemplateClient } from "@io-orkes/conductor-javascript"; - -const templateClient = new TemplateClient(client); - -// Register a human task template -await templateClient.registerTemplate({ - name: "approval_template", - version: 1, - description: "Template for approval tasks", - formTemplate: { - fields: [ - { - name: "approved", - type: "boolean", - required: true, - description: "Whether the request is approved" - }, - { - name: "comments", - type: "text", - required: false, - description: "Additional comments" - }, - { - name: "priority", - type: "select", - required: true, - options: ["low", "medium", "high"], - defaultValue: "medium" - } - ] - }, - uiTemplate: { - template: ` -
-

Approval Request

-
- - -
-
- - -
-
- - -
-
- ` - } -}, false); // asNewVersion = false - -// Register a new version of existing template -await templateClient.registerTemplate({ - name: "approval_template", - version: 2, - description: "Updated approval template with new fields", - formTemplate: { - fields: [ - { - name: "approved", - type: "boolean", - required: true - }, - { - name: "comments", - type: "text", - required: false - }, - { - name: "approver", - type: "text", - required: true, - description: "Name of the approver" - }, - { - name: "approvalDate", - type: "date", - required: true, - description: "Date of approval" - } - ] - } -}, true); // asNewVersion = true -``` - -## SDK Factory Functions - ### Workflow Factory ```typescript @@ -1611,7 +1439,8 @@ const myWorkflow = workflow("workflow_name", [ ```typescript import { taskDefinition } from "@io-orkes/conductor-javascript"; -const taskDef = taskDefinition("task_name", { +const taskDef = taskDefinition({ + name: "task_name", timeoutSeconds: 300, retryCount: 3, retryDelaySeconds: 60, @@ -1784,9 +1613,8 @@ const manager = new TaskManager(client, workers, { - `serverUrl`: Conductor server URL - `keyId`: Authentication key ID - `keySecret`: Authentication key secret -- `refreshTokenInterval`: Token refresh interval (0 = no refresh) +- `refreshTokenInterval`: Token refresh interval in milliseconds (0 = no refresh, default: 30 minutes) - `maxHttp2Connections`: Maximum HTTP2 connections (default: 1) -- `useEnvVars`: DEPRECATED, has no effect #### TaskManagerOptions - `pollInterval`: Polling interval in milliseconds @@ -1821,13 +1649,13 @@ import { async function setupOrderProcessing() { // Create client const client = await orkesConductorClient({ - serverUrl: "https://play.orkes.io/api", + serverUrl: "https://play.orkes.io/api", keyId: "your-key-id", keySecret: "your-key-secret" -}); + }); // Create workflow executor -const executor = new WorkflowExecutor(client); + const executor = new WorkflowExecutor(client); const scheduler = new SchedulerClient(client); // Define order processing workflow @@ -1879,7 +1707,7 @@ const executor = new WorkflowExecutor(client); cronExpression: "0 0 2 * * ?", // Every day at 2 AM startWorkflowRequest: { name: "order_processing", - version: 1, + version: 1, input: { batchProcessing: true } } }); @@ -1933,9 +1761,9 @@ const executor = new WorkflowExecutor(client); manager.startPolling(); // Start workflow execution -const executionId = await executor.startWorkflow({ + const executionId = await executor.startWorkflow({ name: "order_processing", - version: 1, + version: 1, input: { orderId: "ORD-123", customerId: "CUST-456", From 981b57315535e7ec55d144e260f3a9949580b363 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Thu, 2 Oct 2025 16:58:05 +0300 Subject: [PATCH 05/60] Update README.md --- README.md | 305 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 177 insertions(+), 128 deletions(-) diff --git a/README.md b/README.md index efb45687..cabb427a 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,109 @@ -# Conductor JavaScript/TypeScript SDK +# Conductor OSS JavaScript/TypeScript SDK -The `@io-orkes/conductor-javascript` SDK provides a comprehensive TypeScript/JavaScript client for building workflows and task workers with Netflix Conductor and Orkes Conductor. +A comprehensive TypeScript/JavaScript client for [Netflix Conductor](https://github.com/conductor-oss/conductor) and [Orkes Conductor](https://orkes.io/content), enabling developers to build, orchestrate, and monitor distributed workflows with ease. + +[Conductor](https://www.conductor-oss.org/) is the leading open-source orchestration platform allowing developers to build highly scalable distributed applications. + +Check out the [official documentation for Conductor](https://orkes.io/content). + +## ⭐ Conductor OSS + +Show support for the Conductor OSS. Please help spread the awareness by starring Conductor repo. + +[![GitHub stars](https://img.shields.io/github/stars/conductor-oss/conductor.svg?style=social&label=Star&maxAge=)](https://GitHub.com/conductor-oss/conductor/) ## Table of Contents - [Installation](#installation) - [Quick Start](#quick-start) - [Authentication & Configuration](#authentication--configuration) + - [Access Control Setup](#access-control-setup) + - [Configuration Options](#configuration-options) + - [Environment Variables](#environment-variables) + - [Custom Fetch Function](#custom-fetch-function) - [Core Concepts](#core-concepts) + - [What are Tasks?](#what-are-tasks) + - [What are Workflows?](#what-are-workflows) + - [What are Workers?](#what-are-workers) + - [What is the Scheduler?](#what-is-the-scheduler) - [Task Types](#task-types) -- [Workflow Management](#workflow-management) -- [Worker Management](#worker-management) -- [Task Management](#task-management) -- [Scheduler Management](#scheduler-management) + - [Simple Task](#simple-task) + - [HTTP Task](#http-task) + - [Switch Task](#switch-task) + - [Fork-Join Task](#fork-join-task) + - [Do-While Task](#do-while-task) + - [Sub-Workflow Task](#sub-workflow-task) + - [Event Task](#event-task) + - [Wait Task](#wait-task) + - [Terminate Task](#terminate-task) + - [Set Variable Task](#set-variable-task) + - [JSON JQ Transform Task](#json-jq-transform-task) + - [Kafka Publish Task](#kafka-publish-task) + - [Inline Task](#inline-task) + - [Dynamic Fork Task](#dynamic-fork-task) + - [Join Task](#join-task) + - [Human Task](#human-task) +- [Workflows](#workflows) + - [WorkflowExecutor](#workflowexecutor) + - [Workflow Factory](#workflow-factory) + - [Register Workflow](#register-workflow) + - [Start Workflow](#start-workflow) + - [Get Workflow Status](#get-workflow-status) + - [Pause Workflow](#pause-workflow) + - [Resume Workflow](#resume-workflow) + - [Terminate Workflow](#terminate-workflow) + - [Workflow Search](#workflow-search) +- [Workers](#workers) + - [Overview](#overview) + - [Worker Design Principles](#worker-design-principles) + - [TaskManager (Recommended)](#taskmanager-recommended) + - [TaskRunner (Low-level)](#taskrunner-low-level) + - [Configuration Options](#configuration-options-1) +- [Tasks](#tasks) + - [TaskClient](#taskclient) + - [Task Status and Monitoring](#task-status-and-monitoring) + - [Task Search and Filtering](#task-search-and-filtering) + - [Task Debugging](#task-debugging) +- [Scheduling](#scheduling) + - [SchedulerClient](#schedulerclient) - [Service Registry](#service-registry) + - [ServiceRegistryClient](#serviceregistryclient) +- [Metadata](#metadata) + - [MetadataClient](#metadataclient) + - [Task Definition Factory](#task-definition-factory) + - [Register Task Definition](#register-task-definition) + - [Update Task Definition](#update-task-definition) + - [Unregister Task Definition](#unregister-task-definition) + - [Register Workflow Definition](#register-workflow-definition) + - [Unregister Workflow Definition](#unregister-workflow-definition) - [Human Tasks](#human-tasks) -- [Metadata Management](#metadata-management) + - [HumanExecutor](#humanexecutor) + - [TemplateClient](#templateclient) + - [Register Form Template](#register-form-template) + - [Register UI Template](#register-ui-template) - [Error Handling](#error-handling) + - [Worker Error Handling](#worker-error-handling) + - [Task Manager Error Handling](#task-manager-error-handling) + - [Workflow Error Handling](#workflow-error-handling) - [Logging](#logging) + - [Default Logger](#default-logger) + - [Custom Logger](#custom-logger) - [Best Practices](#best-practices) + - [Worker Design](#worker-design) + - [Workflow Design](#workflow-design) + - [Performance](#performance) + - [Security](#security) - [API Reference](#api-reference) + - [Core Classes](#core-classes) + - [Task Generators](#task-generators) + - [Factory Functions](#factory-functions) + - [Configuration Options](#configuration-options-2) - [Examples](#examples) + - [Complete Example: Order Processing Workflow](#complete-example-order-processing-workflow) - [Troubleshooting](#troubleshooting) + - [Common Issues](#common-issues) + - [Debug Mode](#debug-mode) + - [Health Checks](#health-checks) ## Installation @@ -122,25 +204,23 @@ const client = await orkesConductorClient(config, fetch); ## Core Concepts -### Tasks +### What are Tasks? Tasks are individual units of work that can be executed by workers or handled by the Conductor server. -### Workflows +### What are Workflows? Workflows are the main orchestration units in Conductor. They define a sequence of tasks and their dependencies. -### Workers +### What are Workers? Workers are applications that execute specific types of tasks. They poll for work and execute tasks assigned to them. -### Scheduler +### What is the Scheduler? The scheduler allows you to schedule workflows to run at specific times or intervals, enabling automated workflow execution based on time-based triggers. ## Task Types The SDK provides generators for various task types to build workflow definitions. These generators create workflow task references that are used within workflow definitions. -**Note:** These task generators create workflow task references, not task metadata definitions. To register task metadata (like task definitions with retry policies, timeouts, etc.), use the `taskDefinition()` factory function or plain objects with the `MetadataClient` (see [Metadata Management](#metadata-management) section). - -### Available Task Generators +**Note:** These task generators create workflow task references, not task metadata definitions. To register task metadata (like task definitions with retry policies, timeouts, etc.), use the `taskDefinition()` factory function or plain objects with the `MetadataClient` (see [Metadata](#metadata) section). ### Simple Task @@ -333,7 +413,7 @@ const myWorkflow = workflow("order_processing", [ ]); ``` -## Workflow Management +## Workflows ### WorkflowExecutor @@ -470,7 +550,7 @@ const searchResults = await executor.search( ); ``` -## Worker Management +## Workers ### Overview @@ -662,7 +742,7 @@ interface TaskRunnerOptions { - **Use TaskManager** when you have multiple workers or want the convenience of managing all workers together - **Use TaskRunner** when you need fine-grained control over a single worker or want to implement custom worker management logic -## Task Management +## Tasks ### TaskClient @@ -777,7 +857,7 @@ const inputTasks = await taskClient.search(0, 100, "", "*", "inputData.orderId:o const timeoutTasks = await taskClient.search(0, 100, "endTime:DESC", "*", "status:TIMED_OUT"); ``` -## Scheduler Management +## Scheduling ### SchedulerClient @@ -900,7 +980,7 @@ const methods = await serviceRegistry.discover("user-service", true); await serviceRegistry.removeService("user-service"); ``` -## Metadata Management +## Metadata ### MetadataClient @@ -1063,9 +1143,81 @@ await metadataClient.registerWorkflowDef(workflowDef); await metadataClient.unregisterWorkflow("order_processing_workflow", 1); ``` +## Human Tasks + +### HumanExecutor + +The `HumanExecutor` class provides comprehensive human task management: + +```typescript +import { HumanExecutor } from "@io-orkes/conductor-javascript"; + +const humanExecutor = new HumanExecutor(client); + +// Search human tasks +const tasks = await humanExecutor.search({ + states: ["PENDING", "ASSIGNED"], + assignees: [{ userType: "EXTERNAL_USER", user: "john@example.com" }], + taskRefNames: ["approval_task"], + taskInputQuery: "priority:high", + size: 10, + start: 0 +}); + +// Poll for tasks until found +const polledTasks = await humanExecutor.pollSearch({ + states: ["PENDING"], + assignees: [{ userType: "EXTERNAL_USER", user: "john@example.com" }] +}, { + pollInterval: 1000, + maxPollTimes: 30 +}); + +// Get task by ID +const task = await humanExecutor.getTaskById(taskId); + +// Claim task as external user +const claimedTask = await humanExecutor.claimTaskAsExternalUser( + taskId, + "john@example.com", + { overrideAssignment: false, withTemplate: true } +); + +// Claim task as conductor user +const conductorClaimedTask = await humanExecutor.claimTaskAsConductorUser( + taskId, + { overrideAssignment: false, withTemplate: true } +); + +// Release task +await humanExecutor.releaseTask(taskId); + +// Update task output +await humanExecutor.updateTaskOutput(taskId, { + output: { + approved: true, + comments: "Approved with conditions" + } +}); + +// Complete task +await humanExecutor.completeTask(taskId, { + output: { + approved: true, + comments: "Task completed" + } +}); + +// Get template by name and version +const template = await humanExecutor.getTemplateByNameVersion("approval_template", 1); + +// Get template by ID (deprecated, use getTemplateByNameVersion) +const templateById = await humanExecutor.getTemplateById("approval_template"); +``` + ### TemplateClient -The `TemplateClient` class provides methods for managing human task templates: +The `TemplateClient` class provides methods for managing human task templates (forms and UI): ```typescript import { TemplateClient } from "@io-orkes/conductor-javascript"; @@ -1349,110 +1501,6 @@ const uiTemplate = { await templateClient.registerTemplate(uiTemplate); ``` -## Human Tasks - -### HumanExecutor - -The `HumanExecutor` class provides comprehensive human task management: - -```typescript -import { HumanExecutor } from "@io-orkes/conductor-javascript"; - -const humanExecutor = new HumanExecutor(client); - -// Search human tasks -const tasks = await humanExecutor.search({ - states: ["PENDING", "ASSIGNED"], - assignees: [{ userType: "EXTERNAL_USER", user: "john@example.com" }], - taskRefNames: ["approval_task"], - taskInputQuery: "priority:high", - size: 10, - start: 0 -}); - -// Poll for tasks until found -const polledTasks = await humanExecutor.pollSearch({ - states: ["PENDING"], - assignees: [{ userType: "EXTERNAL_USER", user: "john@example.com" }] -}, { - pollInterval: 1000, - maxPollTimes: 30 -}); - -// Get task by ID -const task = await humanExecutor.getTaskById(taskId); - -// Claim task as external user -const claimedTask = await humanExecutor.claimTaskAsExternalUser( - taskId, - "john@example.com", - { overrideAssignment: false, withTemplate: true } -); - -// Claim task as conductor user -const conductorClaimedTask = await humanExecutor.claimTaskAsConductorUser( - taskId, - { overrideAssignment: false, withTemplate: true } -); - -// Release task -await humanExecutor.releaseTask(taskId); - -// Update task output -await humanExecutor.updateTaskOutput(taskId, { - output: { - approved: true, - comments: "Approved with conditions" - } -}); - -// Complete task -await humanExecutor.completeTask(taskId, { - output: { - approved: true, - comments: "Task completed" - } -}); - -// Get template by name and version -const template = await humanExecutor.getTemplateByNameVersion("approval_template", 1); - -// Get template by ID (deprecated, use getTemplateByNameVersion) -const templateById = await humanExecutor.getTemplateById("approval_template"); -``` - -## SDK Factory Functions - -### Workflow Factory - -```typescript -import { workflow } from "@io-orkes/conductor-javascript"; - -const myWorkflow = workflow("workflow_name", [ - simpleTask("task1", "process_1", {}), - simpleTask("task2", "process_2", {}) -]); -``` - -### Task Definition Factory - -```typescript -import { taskDefinition } from "@io-orkes/conductor-javascript"; - -const taskDef = taskDefinition({ - name: "task_name", - timeoutSeconds: 300, - retryCount: 3, - retryDelaySeconds: 60, - retryLogic: "FIXED", - timeoutPolicy: "RETRY", - inputKeys: ["param1", "param2"], - outputKeys: ["result"], - rateLimitPerFrequency: 100, - rateLimitFrequencyInSeconds: 60 -}); -``` - ## Error Handling ### Worker Error Handling @@ -1542,10 +1590,11 @@ const manager = new TaskManager(client, workers, { ### Worker Design -1. **Stateless**: Workers should be stateless and not maintain workflow-specific state -2. **Idempotent**: Workers should handle cases where tasks are rescheduled due to timeouts -3. **Specific**: Each worker should execute a very specific task with well-defined inputs/outputs -4. **No Retry Logic**: Let Conductor handle retries; workers should focus on task execution +Follow the [Worker Design Principles](#worker-design-principles) outlined in the Workers section: +- Keep workers **stateless** and avoid maintaining workflow-specific state +- Design workers to be **idempotent** to handle task rescheduling +- Ensure each worker is **specific** to one task type with well-defined inputs/outputs +- Let Conductor handle retries; workers should focus on task execution ### Workflow Design From 74eee53d0b3b08208a34c2808b951c3fad059d00 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Thu, 2 Oct 2025 17:11:25 +0300 Subject: [PATCH 06/60] Update README.md --- README.md | 410 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 353 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index cabb427a..30133e01 100644 --- a/README.md +++ b/README.md @@ -55,10 +55,18 @@ Show support for the Conductor OSS. Please help spread the awareness by starrin - [Workflow Search](#workflow-search) - [Workers](#workers) - [Overview](#overview) + - [Quick Start: Your First Worker](#quick-start-your-first-worker) + - [Understanding Worker Execution Flow](#understanding-worker-execution-flow) - [Worker Design Principles](#worker-design-principles) + - [Handling Task Results](#handling-task-results) + - [Working with Multiple Workers](#working-with-multiple-workers) - [TaskManager (Recommended)](#taskmanager-recommended) + - [Advanced Configuration](#advanced-configuration) + - [Dynamic Configuration Updates](#dynamic-configuration-updates) + - [Graceful Shutdown](#graceful-shutdown) - [TaskRunner (Low-level)](#taskrunner-low-level) - [Configuration Options](#configuration-options-1) + - [When to Use Each Approach](#when-to-use-each-approach) - [Tasks](#tasks) - [TaskClient](#taskclient) - [Task Status and Monitoring](#task-status-and-monitoring) @@ -554,10 +562,92 @@ const searchResults = await executor.search( ### Overview -Workers are applications that execute specific types of tasks. The SDK provides two main approaches for managing workers: +Workers are background processes that execute tasks in your workflows. Think of them as specialized functions that: -- **TaskManager** - High-level interface for managing multiple workers (recommended) -- **TaskRunner** - Low-level interface for individual worker control +1. **Poll** the Conductor server asking "Do you have any work for me?" +2. **Execute** the task logic when work is assigned +3. **Report** the results back to Conductor + +**How Workers Fit In:** +``` +Workflow → Creates Tasks → Workers Poll for Tasks → Execute Logic → Return Results → Workflow Continues +``` + +The SDK provides two approaches for managing workers: +- **TaskManager** - Easy-to-use interface for managing multiple workers (⭐ **recommended for most use cases**) +- **TaskRunner** - Low-level interface for fine-grained control of individual workers + +### Quick Start: Your First Worker + +Here's a complete, simple example to get you started: + +```typescript +import { + orkesConductorClient, + TaskManager, + ConductorWorker +} from "@io-orkes/conductor-javascript"; + +// Step 1: Create your client +const client = await orkesConductorClient({ + serverUrl: "https://play.orkes.io/api", + keyId: "your-key-id", + keySecret: "your-key-secret" +}); + +// Step 2: Define your worker(s) +const workers: ConductorWorker[] = [ + { + // This must match the task name in your workflow + taskDefName: "send_email", + + // This function executes when a task is assigned + execute: async (task) => { + // Get input data from the workflow + const { to, subject, body } = task.inputData; + + // Do your work (send email, call API, process data, etc.) + console.log(`Sending email to ${to}: ${subject}`); + await sendEmailViaAPI(to, subject, body); + + // Return the result + return { + outputData: { + sent: true, + timestamp: new Date().toISOString() + }, + status: "COMPLETED" + }; + } + } +]; + +// Step 3: Create TaskManager and start polling +const manager = new TaskManager(client, workers); +await manager.startPolling(); + +console.log("✅ Worker is now running and waiting for tasks!"); + +// When you're done (e.g., on shutdown): +// await manager.stopPolling(); +``` + +**That's it!** Your worker is now running and will automatically: +- Poll for tasks named `send_email` +- Execute the task logic +- Report results back to Conductor +- Handle errors and retries + +### Understanding Worker Execution Flow + +Here's what happens when a workflow creates a task: + +1. **Workflow runs** and creates a task (e.g., `send_email`) +2. **Worker polls** Conductor: "Any `send_email` tasks for me?" +3. **Conductor responds** with the task and its input data +4. **Worker executes** your `execute` function with the task data +5. **Worker returns** the result (`COMPLETED`, `FAILED`, etc.) +6. **Workflow continues** to the next task based on the result ### Worker Design Principles @@ -627,60 +717,242 @@ const genericWorker: ConductorWorker = { }; ``` -### TaskManager (Recommended) +### Handling Task Results -`TaskManager` is the high-level interface that manages multiple workers and their corresponding `TaskRunner` instances. It's the recommended approach for most use cases. +Your worker's `execute` function must return an object with at least these two properties: ```typescript -import { TaskManager, ConductorWorker, DefaultLogger } from "@io-orkes/conductor-javascript"; +{ + status: "COMPLETED" | "FAILED" | "FAILED_WITH_TERMINAL_ERROR" | "IN_PROGRESS", + outputData: { /* your result data */ } +} +``` + +#### Common Return Patterns + +**✅ Success:** +```typescript +return { + status: "COMPLETED", + outputData: { result: "success", data: processedData } +}; +``` + +**❌ Failure (will retry based on task configuration):** +```typescript +return { + status: "FAILED", + outputData: {}, + logs: [{ log: "Error details for debugging" }] +}; +``` + +**❌ Terminal Failure (no retry, workflow fails immediately):** +```typescript +return { + status: "FAILED_WITH_TERMINAL_ERROR", + outputData: { error: "Invalid input - cannot proceed" } +}; +``` + +**⏳ In Progress (for long-running tasks):** +```typescript +return { + status: "IN_PROGRESS", + outputData: { progress: 50, message: "Processing..." }, + callbackAfterSeconds: 30 // Conductor will check back after 30 seconds +}; +``` + +#### Error Handling in Workers + +Always wrap your worker logic in try-catch to handle errors gracefully: + +```typescript +const worker: ConductorWorker = { + taskDefName: "risky_operation", + execute: async (task) => { + try { + const result = await performRiskyOperation(task.inputData); + return { + status: "COMPLETED", + outputData: { result } + }; + } catch (error) { + console.error("Worker error:", error); + + // Decide: retry or fail permanently? + const shouldRetry = error.code !== 'INVALID_INPUT'; + + return { + status: shouldRetry ? "FAILED" : "FAILED_WITH_TERMINAL_ERROR", + outputData: { error: error.message }, + logs: [{ + log: `Error: ${error.message}`, + createdTime: Date.now() + }] + }; + } + } +}; +``` + +### Working with Multiple Workers + +In real applications, you'll typically have multiple workers for different tasks: + +```typescript +import { TaskManager, ConductorWorker } from "@io-orkes/conductor-javascript"; const workers: ConductorWorker[] = [ + // Worker 1: Send emails { - taskDefName: "greeting_task", + taskDefName: "send_email", execute: async (task) => { + const { to, subject, body } = task.inputData; + await emailService.send(to, subject, body); return { - outputData: { greeting: "Hello!" }, - status: "COMPLETED" + status: "COMPLETED", + outputData: { sent: true, messageId: "msg_123" } + }; + } + }, + + // Worker 2: Process payments + { + taskDefName: "process_payment", + execute: async (task) => { + const { amount, currency, cardToken } = task.inputData; + const charge = await paymentGateway.charge(amount, currency, cardToken); + return { + status: "COMPLETED", + outputData: { + transactionId: charge.id, + status: charge.status + } + }; + } + }, + + // Worker 3: Generate reports + { + taskDefName: "generate_report", + execute: async (task) => { + const { reportType, startDate, endDate } = task.inputData; + const reportUrl = await reportService.generate(reportType, startDate, endDate); + return { + status: "COMPLETED", + outputData: { reportUrl, generatedAt: new Date().toISOString() } }; } } ]; +// Start all workers with a single TaskManager +const manager = new TaskManager(client, workers); +await manager.startPolling(); + +console.log("✅ All 3 workers are now running!"); +``` + +**Key Points:** +- Each worker handles a specific task type (identified by `taskDefName`) +- All workers run concurrently and independently +- A single `TaskManager` manages all workers together +- Workers only pick up tasks that match their `taskDefName` + +### TaskManager (Recommended) + +`TaskManager` is the high-level interface that manages multiple workers. You've already seen the basic usage above. This section covers advanced configuration and features. + +#### Advanced Configuration + +```typescript +import { TaskManager, ConductorWorker, DefaultLogger } from "@io-orkes/conductor-javascript"; + const manager = new TaskManager(client, workers, { + // Custom logger for debugging and monitoring logger: new DefaultLogger(), + + // Polling and execution options options: { - pollInterval: 1000, - concurrency: 2, - workerID: "worker-group-1", - domain: "production", - batchPollingTimeout: 100 + pollInterval: 1000, // Poll every 1 second (default: 100ms) + concurrency: 5, // Execute up to 5 tasks concurrently per worker + workerID: "worker-group-1", // Unique identifier for this worker group + domain: "production", // Task domain for isolation (optional) + batchPollingTimeout: 100 // Timeout for batch polling in ms }, - onError: (error) => console.error("Worker error:", error), + + // Global error handler for all workers + onError: (error, task) => { + console.error(`Error in task ${task?.taskType}:`, error); + // Send to error tracking service + errorTracker.log(error, { taskId: task?.taskId }); + }, + + // Maximum retry attempts before giving up maxRetries: 3 }); -// Start all workers await manager.startPolling(); +``` -// Update polling options -manager.updatePollingOptions({ pollInterval: 500 }); +#### Dynamic Configuration Updates -// Stop all workers -await manager.stopPolling(); +You can update polling options at runtime without stopping workers: + +```typescript +// Adjust polling interval based on load +manager.updatePollingOptions({ + pollInterval: 2000, // Slow down during high load + concurrency: 10 // Increase parallelism +}); +``` + +#### Graceful Shutdown + +Properly stop workers when your application shuts down: + +```typescript +// Graceful shutdown handler +process.on('SIGTERM', async () => { + console.log('Shutting down workers...'); + await manager.stopPolling(); + console.log('Workers stopped gracefully'); + process.exit(0); +}); + +// Or with timeout +async function gracefulShutdown() { + const timeout = setTimeout(() => { + console.error('Force shutdown after timeout'); + process.exit(1); + }, 30000); // 30 second timeout + + await manager.stopPolling(); + clearTimeout(timeout); + process.exit(0); +} ``` ### TaskRunner (Low-level) -`TaskRunner` is the low-level interface used internally by `TaskManager`. It handles individual worker execution, polling the server for work, and updating results back to the server. Use this when you need fine-grained control over a single worker. +`TaskRunner` is the low-level interface used internally by `TaskManager`. **Most developers should use `TaskManager` instead.** Use `TaskRunner` only if you need: + +- Fine-grained control over a single worker's lifecycle +- Custom polling logic or worker management +- Integration with existing worker management systems + +**Basic Example:** ```typescript -import { TaskRunner, ConductorWorker, DefaultLogger } from "@io-orkes/conductor-javascript"; +import { TaskRunner, ConductorWorker } from "@io-orkes/conductor-javascript"; const worker: ConductorWorker = { - taskDefName: "HelloWorldWorker", - execute: async ({ inputData, taskId }) => { + taskDefName: "specialized_task", + execute: async (task) => { return { - outputData: { greeting: "Hello World" }, + outputData: { result: "processed" }, status: "COMPLETED" }; } @@ -688,59 +960,83 @@ const worker: ConductorWorker = { const taskRunner = new TaskRunner({ worker: worker, - taskResource: client.taskResource, + taskResource: client.taskResource, // Note: Direct access to taskResource options: { pollInterval: 1000, concurrency: 1, - workerID: "my-worker" - }, - logger: new DefaultLogger() + workerID: "specialized-worker" + } }); -// Start the worker await taskRunner.startPolling(); - -// Stop the worker +// ... later await taskRunner.stopPolling(); ``` +**Key Differences from TaskManager:** +- Manages only ONE worker (vs TaskManager managing multiple) +- Requires direct `taskResource` access +- No built-in error handling or retry logic +- More manual lifecycle management + ### Configuration Options #### TaskManager Configuration +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `logger` | `ConductorLogger` | - | Custom logger instance for monitoring and debugging | +| `options.pollInterval` | `number` | `100` | How often to poll for tasks (milliseconds) | +| `options.concurrency` | `number` | `1` | Max concurrent task executions per worker | +| `options.workerID` | `string` | - | Unique identifier for this worker group | +| `options.domain` | `string` | - | Task domain for isolation (optional) | +| `options.batchPollingTimeout` | `number` | `100` | Batch polling timeout in milliseconds | +| `onError` | `(error, task?) => void` | - | Global error handler called when workers fail | +| `maxRetries` | `number` | `3` | Max retry attempts for failed operations | + +**Example with all options:** ```typescript -interface TaskManagerConfig { - logger?: ConductorLogger; - options?: Partial; - onError?: TaskErrorHandler; - maxRetries?: number; -} - -interface TaskManagerOptions { - workerID?: string; // Unique worker identifier - pollInterval?: number; // Polling interval in milliseconds - domain?: string; // Task domain for isolation - concurrency?: number; // Number of concurrent executions - batchPollingTimeout?: number; // Batch polling timeout -} +const manager = new TaskManager(client, workers, { + logger: new CustomLogger(), + options: { + pollInterval: 1000, + concurrency: 5, + workerID: "prod-worker-1", + domain: "production", + batchPollingTimeout: 100 + }, + onError: (error, task) => console.error("Error:", error), + maxRetries: 3 +}); ``` #### TaskRunner Configuration -```typescript -interface TaskRunnerOptions { - workerID?: string; - pollInterval?: number; - domain?: string; - concurrency?: number; - batchPollingTimeout?: number; -} -``` +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `worker` | `ConductorWorker` | **required** | The worker definition to run | +| `taskResource` | `TaskResourceService` | **required** | Task resource service from client | +| `options.pollInterval` | `number` | `100` | Polling interval in milliseconds | +| `options.concurrency` | `number` | `1` | Max concurrent executions | +| `options.workerID` | `string` | - | Unique worker identifier | +| `options.domain` | `string` | - | Task domain for isolation | +| `logger` | `ConductorLogger` | - | Custom logger instance | ### When to Use Each Approach -- **Use TaskManager** when you have multiple workers or want the convenience of managing all workers together -- **Use TaskRunner** when you need fine-grained control over a single worker or want to implement custom worker management logic +**Use TaskManager when:** +- ✅ You have multiple workers (most common case) +- ✅ You want simple, high-level worker management +- ✅ You need built-in error handling and retries +- ✅ You're building a standard worker application + +**Use TaskRunner when:** +- 🔧 You need fine-grained control over a single worker +- 🔧 You're implementing custom worker management logic +- 🔧 You're integrating with existing polling/execution frameworks +- 🔧 You need direct access to low-level worker operations + +**💡 Recommendation:** Start with `TaskManager`. Only use `TaskRunner` if you have specific advanced requirements that TaskManager doesn't support. ## Tasks From 63b386f24635116dffa35d771d78f82cc80dc47e Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Thu, 2 Oct 2025 17:16:44 +0300 Subject: [PATCH 07/60] Update README.md --- README.md | 235 ++++++++++++++++++++++++++---------------------------- 1 file changed, 115 insertions(+), 120 deletions(-) diff --git a/README.md b/README.md index 30133e01..4f3fffb8 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,11 @@ Show support for the Conductor OSS. Please help spread the awareness by starrin - [Resume Workflow](#resume-workflow) - [Terminate Workflow](#terminate-workflow) - [Workflow Search](#workflow-search) + - [Monitoring & Debugging Tasks](#monitoring--debugging-tasks) + - [Task Statuses](#task-statuses) + - [Searching & Filtering Tasks](#searching--filtering-tasks) + - [Common Search Queries](#common-search-queries) + - [Debugging Task Execution](#debugging-task-execution) - [Workers](#workers) - [Overview](#overview) - [Quick Start: Your First Worker](#quick-start-your-first-worker) @@ -67,11 +72,6 @@ Show support for the Conductor OSS. Please help spread the awareness by starrin - [TaskRunner (Low-level)](#taskrunner-low-level) - [Configuration Options](#configuration-options-1) - [When to Use Each Approach](#when-to-use-each-approach) -- [Tasks](#tasks) - - [TaskClient](#taskclient) - - [Task Status and Monitoring](#task-status-and-monitoring) - - [Task Search and Filtering](#task-search-and-filtering) - - [Task Debugging](#task-debugging) - [Scheduling](#scheduling) - [SchedulerClient](#schedulerclient) - [Service Registry](#service-registry) @@ -558,6 +558,116 @@ const searchResults = await executor.search( ); ``` +### Monitoring & Debugging Tasks + +The `TaskClient` provides capabilities for monitoring and debugging tasks within your workflow executions: + +```typescript +import { TaskClient } from "@io-orkes/conductor-javascript"; + +const taskClient = new TaskClient(client); + +// Search tasks +const searchResults = await taskClient.search(0, 10, "", "*", "status:COMPLETED"); + +// Get task by ID +const task = await taskClient.getTask(taskId); + +// Update task result (advanced use case) +await taskClient.updateTaskResult( + workflowId, + taskReferenceName, + "COMPLETED", + { result: "success" } +); +``` + +#### Task Statuses + +Tasks in Conductor have various statuses that indicate their current state: + +- **SCHEDULED**: Task is scheduled for execution +- **IN_PROGRESS**: Task is currently being executed +- **COMPLETED**: Task completed successfully +- **COMPLETED_WITH_ERRORS**: Task completed but with errors +- **FAILED**: Task execution failed +- **FAILED_WITH_TERMINAL_ERROR**: Task failed with a terminal error (no retries) +- **TIMED_OUT**: Task execution timed out +- **CANCELED**: Task was canceled +- **SKIPPED**: Task was skipped + +#### Searching & Filtering Tasks + +You can search for tasks using various criteria: + +```typescript +// Search by status +const completedTasks = await taskClient.search(0, 10, "", "*", "status:COMPLETED"); + +// Search by workflow +const workflowTasks = await taskClient.search(0, 10, "", "*", "workflowId:workflow-123"); + +// Search by task type +const simpleTasks = await taskClient.search(0, 10, "", "*", "taskType:SIMPLE"); + +// Search by free text +const textSearch = await taskClient.search(0, 10, "", "error", ""); + +// Search with sorting +const sortedTasks = await taskClient.search(0, 10, "startTime:DESC", "*", "status:FAILED"); +``` + +**Search Parameters:** +- `start`: Starting index for pagination (default: 0) +- `size`: Number of results to return (default: 100) +- `sort`: Sort field and direction (e.g., "startTime:DESC", "status:ASC") +- `freeText`: Free text search term (use "*" for all) +- `query`: Structured query string (e.g., "status:FAILED", "workflowId:workflow-123") + +#### Common Search Queries + +```typescript +// Find all failed tasks +const failedTasks = await taskClient.search(0, 100, "startTime:DESC", "*", "status:FAILED"); + +// Find tasks for a specific workflow +const workflowTasks = await taskClient.search(0, 100, "", "*", "workflowId:my-workflow-123"); + +// Find tasks by worker ID +const workerTasks = await taskClient.search(0, 100, "", "*", "workerId:worker-123"); + +// Find tasks with specific input data +const inputTasks = await taskClient.search(0, 100, "", "*", "inputData.orderId:order-123"); + +// Find tasks that timed out +const timeoutTasks = await taskClient.search(0, 100, "endTime:DESC", "*", "status:TIMED_OUT"); +``` + +#### Debugging Task Execution + +When debugging task execution issues: + +```typescript +try { + // Get detailed task information + const task = await taskClient.getTask(taskId); + + console.log("Task Status:", task.status); + console.log("Task Input:", task.inputData); + console.log("Task Output:", task.outputData); + console.log("Retry Count:", task.retryCount); + console.log("Execution Time:", task.endTime - task.startTime); + + // Check for failed tasks in a workflow + const failedTasks = await taskClient.search(0, 50, "", "*", "status:FAILED"); + failedTasks.results.forEach(task => { + console.log(`Task ${task.taskId} failed: ${task.reasonForIncompletion}`); + }); +} catch (error) { + console.error("Error debugging tasks:", error); +} +``` + ## Workers ### Overview @@ -1038,121 +1148,6 @@ const manager = new TaskManager(client, workers, { **💡 Recommendation:** Start with `TaskManager`. Only use `TaskRunner` if you have specific advanced requirements that TaskManager doesn't support. -## Tasks - -### TaskClient - -The `TaskClient` provides additional task management capabilities for querying and updating existing tasks: - -```typescript -import { TaskClient } from "@io-orkes/conductor-javascript"; - -const taskClient = new TaskClient(client); - -// Search tasks -const searchResults = await taskClient.search(0, 10, "", "*", "status:COMPLETED"); - -// Get task by ID -const task = await taskClient.getTask(taskId); - -// Update task result -await taskClient.updateTaskResult( - workflowId, - taskReferenceName, - "COMPLETED", - { result: "success" } -); -``` - -### Task Status and Monitoring - -Tasks in Conductor have various statuses that indicate their current state: - -- **SCHEDULED**: Task is scheduled for execution -- **IN_PROGRESS**: Task is currently being executed -- **COMPLETED**: Task completed successfully -- **COMPLETED_WITH_ERRORS**: Task completed but with errors -- **FAILED**: Task execution failed -- **FAILED_WITH_TERMINAL_ERROR**: Task failed with a terminal error (no retries) -- **TIMED_OUT**: Task execution timed out -- **CANCELED**: Task was canceled -- **SKIPPED**: Task was skipped - -### Task Search and Filtering - -You can search for tasks using various criteria: - -```typescript -// Search by status -const completedTasks = await taskClient.search(0, 10, "", "*", "status:COMPLETED"); - -// Search by workflow -const workflowTasks = await taskClient.search(0, 10, "", "*", "workflowId:workflow-123"); - -// Search by task type -const simpleTasks = await taskClient.search(0, 10, "", "*", "taskType:SIMPLE"); - -// Search by free text -const textSearch = await taskClient.search(0, 10, "", "error", ""); - -// Search with sorting -const sortedTasks = await taskClient.search(0, 10, "startTime:DESC", "*", "status:FAILED"); -``` - -### Task Debugging - -When debugging task execution issues: - -```typescript -try { - // Get detailed task information - const task = await taskClient.getTask(taskId); - - console.log("Task Status:", task.status); - console.log("Task Input:", task.inputData); - console.log("Task Output:", task.outputData); - console.log("Retry Count:", task.retryCount); - console.log("Execution Time:", task.endTime - task.startTime); - - // Check for failed tasks - const failedTasks = await taskClient.search(0, 50, "", "*", "status:FAILED"); - failedTasks.results.forEach(task => { - console.log(`Task ${task.taskId} failed: ${task.reasonForIncompletion}`); - }); -} catch (error) { - console.error("Error debugging tasks:", error); -} -``` - -### Task Search Parameters - -The `search` method accepts the following parameters: - -- `start`: Starting index for pagination (default: 0) -- `size`: Number of results to return (default: 100) -- `sort`: Sort field and direction (e.g., "startTime:DESC", "status:ASC") -- `freeText`: Free text search term (use "*" for all) -- `query`: Structured query string (e.g., "status:FAILED", "workflowId:workflow-123") - -### Common Search Queries - -```typescript -// Find all failed tasks -const failedTasks = await taskClient.search(0, 100, "startTime:DESC", "*", "status:FAILED"); - -// Find tasks for a specific workflow -const workflowTasks = await taskClient.search(0, 100, "", "*", "workflowId:my-workflow-123"); - -// Find tasks by worker ID -const workerTasks = await taskClient.search(0, 100, "", "*", "workerId:worker-123"); - -// Find tasks with specific input data -const inputTasks = await taskClient.search(0, 100, "", "*", "inputData.orderId:order-123"); - -// Find tasks that timed out -const timeoutTasks = await taskClient.search(0, 100, "endTime:DESC", "*", "status:TIMED_OUT"); -``` - ## Scheduling ### SchedulerClient From 3dbfcc4b8365cc5a0edd55655b46ef6e200c407f Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Thu, 2 Oct 2025 17:24:24 +0300 Subject: [PATCH 08/60] Update README.md --- README.md | 130 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 111 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 4f3fffb8..d8b79fa0 100644 --- a/README.md +++ b/README.md @@ -23,10 +23,13 @@ Show support for the Conductor OSS. Please help spread the awareness by starrin - [Custom Fetch Function](#custom-fetch-function) - [Core Concepts](#core-concepts) - [What are Tasks?](#what-are-tasks) + - [System Tasks (Built-in Workers)](#1-system-tasks-built-in-workers) + - [SIMPLE Tasks (Custom/External Workers)](#2-simple-tasks-customexternal-workers) - [What are Workflows?](#what-are-workflows) - [What are Workers?](#what-are-workers) - [What is the Scheduler?](#what-is-the-scheduler) - [Task Types](#task-types) + - [Understanding Task Execution](#understanding-task-execution) - [Simple Task](#simple-task) - [HTTP Task](#http-task) - [Switch Task](#switch-task) @@ -213,13 +216,51 @@ const client = await orkesConductorClient(config, fetch); ## Core Concepts ### What are Tasks? -Tasks are individual units of work that can be executed by workers or handled by the Conductor server. + +Tasks are individual units of work in Conductor. There are **two types of tasks**: + +#### 1. System Tasks (Built-in Workers) +System tasks are executed by **built-in workers running inside the Conductor server**. You don't need to write or deploy any code - Conductor handles everything. + +**Examples:** HTTP, Event, Inline, JSON JQ Transform, Kafka, Switch, Fork/Join, Sub-Workflow, Set Variable, Wait, Terminate + +**Benefits:** +- ✅ No need to write or deploy workers +- ✅ Execute immediately (no polling delay) +- ✅ Built-in and optimized by Conductor +- ✅ Perfect for common operations (API calls, data transforms, flow control) + +**Technical Note:** These tasks are still executed by workers - they're just built-in workers managed by the Conductor server. + +#### 2. SIMPLE Tasks (Custom/External Workers) +SIMPLE tasks require **your own custom workers** that you write and deploy. These execute your business-specific logic. + +**Examples:** `send_email`, `process_payment`, `generate_report`, `process_order` + +**When to use:** +- Custom business logic +- Integration with internal systems +- Database operations +- File processing +- Any custom functionality unique to your application + +**How they work:** +1. Workflow creates a SIMPLE task +2. Your worker polls Conductor: "Any tasks for me?" +3. Conductor assigns the task +4. Worker executes your custom logic +5. Worker reports results back ### What are Workflows? -Workflows are the main orchestration units in Conductor. They define a sequence of tasks and their dependencies. +Workflows are the main orchestration units in Conductor. They define a sequence of tasks and their dependencies, combining both system tasks and worker tasks to accomplish complex business processes. ### What are Workers? -Workers are applications that execute specific types of tasks. They poll for work and execute tasks assigned to them. +Workers are applications that poll Conductor for tasks and execute them. There are two kinds: + +- **Built-in Workers** - These run inside the Conductor server and execute system tasks (HTTP, Event, Inline, etc.). You don't manage these. +- **Custom Workers** - These are applications **you write and deploy** to execute SIMPLE tasks with your custom business logic. + +This documentation focuses on custom workers since you need to implement and manage them yourself. ### What is the Scheduler? The scheduler allows you to schedule workflows to run at specific times or intervals, enabling automated workflow execution based on time-based triggers. @@ -228,9 +269,30 @@ The scheduler allows you to schedule workflows to run at specific times or inter The SDK provides generators for various task types to build workflow definitions. These generators create workflow task references that are used within workflow definitions. +### Understanding Task Execution + +Tasks fall into two categories based on **who provides the worker**: + +**🔵 System Tasks** - Executed by built-in workers in Conductor server +- HTTP, Event, Inline, JSON JQ Transform, Kafka, Switch, Fork-Join, Dynamic Fork, Join, Sub-Workflow, Set Variable, Wait, Terminate, Do-While +- Built-in workers handle these automatically +- Perfect for common operations, flow control, and data transformations +- No need to write or deploy any code + +**🟢 SIMPLE Tasks** - Executed by your custom workers +- SIMPLE tasks - for your custom business logic +- HUMAN tasks - for human interaction/approvals (special type of SIMPLE task) +- You must write, deploy, and manage workers for these tasks + +--- + **Note:** These task generators create workflow task references, not task metadata definitions. To register task metadata (like task definitions with retry policies, timeouts, etc.), use the `taskDefinition()` factory function or plain objects with the `MetadataClient` (see [Metadata](#metadata) section). -### Simple Task +--- + +### Simple Task 🟢 + +**Type:** SIMPLE Task (requires custom worker implementation) ```typescript import { simpleTask } from "@io-orkes/conductor-javascript"; @@ -240,7 +302,9 @@ const task = simpleTask("task_ref", "task_name", { }, false); // optional: if true, workflow continues even if task fails ``` -### HTTP Task +### HTTP Task 🔵 + +**Type:** System Task (executed by built-in workers) ```typescript import { httpTask } from "@io-orkes/conductor-javascript"; @@ -253,7 +317,9 @@ const task = httpTask("http_ref", "http://api.example.com/data", { }); ``` -### Switch Task +### Switch Task 🔵 + +**Type:** System Task (executed by built-in workers) ```typescript import { switchTask } from "@io-orkes/conductor-javascript"; @@ -265,7 +331,9 @@ const task = switchTask("switch_ref", "input.status", { }); ``` -### Fork-Join Task +### Fork-Join Task 🔵 + +**Type:** System Task (executed by built-in workers) ```typescript import { forkJoinTask } from "@io-orkes/conductor-javascript"; @@ -277,7 +345,9 @@ const task = forkJoinTask("fork_ref", [ ]); ``` -### Do-While Task +### Do-While Task 🔵 + +**Type:** System Task (executed by built-in workers) ```typescript import { doWhileTask } from "@io-orkes/conductor-javascript"; @@ -293,7 +363,9 @@ const task = doWhileTask("while_ref", "workflow.variables.counter < 10", [ ]); ``` -### Sub-Workflow Task +### Sub-Workflow Task 🔵 + +**Type:** System Task (executed by built-in workers) ```typescript import { subWorkflowTask } from "@io-orkes/conductor-javascript"; @@ -303,7 +375,9 @@ const task = subWorkflowTask("sub_ref", "child_workflow", 1, { }, "COMPLETED"); // wait for completion status ``` -### Event Task +### Event Task 🔵 + +**Type:** System Task (executed by built-in workers) ```typescript import { eventTask } from "@io-orkes/conductor-javascript"; @@ -314,7 +388,9 @@ const task = eventTask("event_ref", "event_name", { }); ``` -### Wait Task +### Wait Task 🔵 + +**Type:** System Task (executed by built-in workers) ```typescript import { waitTask } from "@io-orkes/conductor-javascript"; @@ -322,7 +398,9 @@ import { waitTask } from "@io-orkes/conductor-javascript"; const task = waitTask("wait_ref", 30); // wait 30 seconds ``` -### Terminate Task +### Terminate Task 🔵 + +**Type:** System Task (executed by built-in workers) ```typescript import { terminateTask } from "@io-orkes/conductor-javascript"; @@ -330,7 +408,9 @@ import { terminateTask } from "@io-orkes/conductor-javascript"; const task = terminateTask("terminate_ref", "FAILED", "Error message"); ``` -### Set Variable Task +### Set Variable Task 🔵 + +**Type:** System Task (executed by built-in workers) ```typescript import { setVariableTask } from "@io-orkes/conductor-javascript"; @@ -341,7 +421,9 @@ const task = setVariableTask("var_ref", { }); ``` -### JSON JQ Transform Task +### JSON JQ Transform Task 🔵 + +**Type:** System Task (executed by built-in workers) ```typescript import { jsonJqTask } from "@io-orkes/conductor-javascript"; @@ -349,7 +431,9 @@ import { jsonJqTask } from "@io-orkes/conductor-javascript"; const task = jsonJqTask("transform_ref", ".data.items[] | {id: .id, name: .name}"); ``` -### Kafka Publish Task +### Kafka Publish Task 🔵 + +**Type:** System Task (executed by built-in workers) ```typescript import { kafkaPublishTask } from "@io-orkes/conductor-javascript"; @@ -362,7 +446,9 @@ const task = kafkaPublishTask("kafka_ref", "topic_name", { }); ``` -### Inline Task +### Inline Task 🔵 + +**Type:** System Task (executed by built-in workers) ```typescript import { inlineTask } from "@io-orkes/conductor-javascript"; @@ -374,7 +460,9 @@ const task = inlineTask("inline_ref", ` `); ``` -### Dynamic Fork Task +### Dynamic Fork Task 🔵 + +**Type:** System Task (executed by built-in workers) ```typescript import { dynamicForkTask } from "@io-orkes/conductor-javascript"; @@ -382,7 +470,9 @@ import { dynamicForkTask } from "@io-orkes/conductor-javascript"; const task = dynamicForkTask("dynamic_ref", "input.tasks", "task_name"); ``` -### Join Task +### Join Task 🔵 + +**Type:** System Task (executed by built-in workers) ```typescript import { joinTask } from "@io-orkes/conductor-javascript"; @@ -390,7 +480,9 @@ import { joinTask } from "@io-orkes/conductor-javascript"; const task = joinTask("join_ref"); ``` -### Human Task +### Human Task 🟢 + +**Type:** SIMPLE Task (requires human interaction via custom UI) ```typescript import { humanTask } from "@io-orkes/conductor-javascript"; From 51184aa931be92a26f02d7f4008355920178be6e Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Thu, 2 Oct 2025 17:27:27 +0300 Subject: [PATCH 09/60] Update README.md --- README.md | 59 +++++++++++++++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index d8b79fa0..89ce1f87 100644 --- a/README.md +++ b/README.md @@ -220,47 +220,45 @@ const client = await orkesConductorClient(config, fetch); Tasks are individual units of work in Conductor. There are **two types of tasks**: #### 1. System Tasks (Built-in Workers) -System tasks are executed by **built-in workers running inside the Conductor server**. You don't need to write or deploy any code - Conductor handles everything. +These tasks are executed by **built-in workers that run inside the Conductor server**. Conductor provides and manages these workers for you. **Examples:** HTTP, Event, Inline, JSON JQ Transform, Kafka, Switch, Fork/Join, Sub-Workflow, Set Variable, Wait, Terminate -**Benefits:** -- ✅ No need to write or deploy workers -- ✅ Execute immediately (no polling delay) -- ✅ Built-in and optimized by Conductor +**What you get:** +- ✅ Ready to use - just reference them in your workflow +- ✅ No code to write or deploy +- ✅ Optimized and maintained by Conductor - ✅ Perfect for common operations (API calls, data transforms, flow control) -**Technical Note:** These tasks are still executed by workers - they're just built-in workers managed by the Conductor server. - -#### 2. SIMPLE Tasks (Custom/External Workers) -SIMPLE tasks require **your own custom workers** that you write and deploy. These execute your business-specific logic. +#### 2. SIMPLE Tasks (Custom Workers) +These tasks are executed by **custom workers that you write and deploy**. You implement the business logic and manage the worker lifecycle. **Examples:** `send_email`, `process_payment`, `generate_report`, `process_order` **When to use:** -- Custom business logic +- Custom business logic specific to your application - Integration with internal systems - Database operations - File processing -- Any custom functionality unique to your application +- Any functionality not provided by system tasks -**How they work:** +**Worker execution flow:** 1. Workflow creates a SIMPLE task 2. Your worker polls Conductor: "Any tasks for me?" -3. Conductor assigns the task -4. Worker executes your custom logic -5. Worker reports results back +3. Conductor assigns the task to your worker +4. Your worker executes the custom logic +5. Worker reports results back to Conductor ### What are Workflows? -Workflows are the main orchestration units in Conductor. They define a sequence of tasks and their dependencies, combining both system tasks and worker tasks to accomplish complex business processes. +Workflows are the main orchestration units in Conductor. They define a sequence of tasks and their dependencies. You can combine system tasks (like HTTP calls and data transforms) with your custom SIMPLE tasks to build complex business processes. ### What are Workers? -Workers are applications that poll Conductor for tasks and execute them. There are two kinds: +Workers are applications that poll Conductor for tasks and execute them. Conductor uses two types: -- **Built-in Workers** - These run inside the Conductor server and execute system tasks (HTTP, Event, Inline, etc.). You don't manage these. -- **Custom Workers** - These are applications **you write and deploy** to execute SIMPLE tasks with your custom business logic. +- **Built-in Workers** - Run inside the Conductor server to execute system tasks (HTTP, Event, Inline, etc.). Conductor manages these for you. +- **Custom Workers** - Applications **you write and deploy** to execute SIMPLE tasks with your custom business logic. You're responsible for implementing and running these. -This documentation focuses on custom workers since you need to implement and manage them yourself. +This SDK helps you build and manage custom workers for your SIMPLE tasks. ### What is the Scheduler? The scheduler allows you to schedule workflows to run at specific times or intervals, enabling automated workflow execution based on time-based triggers. @@ -271,18 +269,19 @@ The SDK provides generators for various task types to build workflow definitions ### Understanding Task Execution -Tasks fall into two categories based on **who provides the worker**: +All tasks in Conductor are executed by workers. The key difference is **who provides the worker**: -**🔵 System Tasks** - Executed by built-in workers in Conductor server +**🔵 System Tasks** - Use Conductor's built-in workers - HTTP, Event, Inline, JSON JQ Transform, Kafka, Switch, Fork-Join, Dynamic Fork, Join, Sub-Workflow, Set Variable, Wait, Terminate, Do-While -- Built-in workers handle these automatically -- Perfect for common operations, flow control, and data transformations -- No need to write or deploy any code - -**🟢 SIMPLE Tasks** - Executed by your custom workers -- SIMPLE tasks - for your custom business logic -- HUMAN tasks - for human interaction/approvals (special type of SIMPLE task) -- You must write, deploy, and manage workers for these tasks +- Workers are provided and managed by Conductor +- Just reference these tasks in your workflow - they work automatically +- Ideal for common operations, flow control, and data transformations + +**🟢 SIMPLE Tasks** - Use your custom workers +- For your unique business logic and integrations +- HUMAN tasks are a special type that requires human interaction +- You write, deploy, and manage these workers +- See the [Workers](#workers) section for implementation guide --- From 4256fa238fb6146cfed0583b3764b77ca9ef5385 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Thu, 2 Oct 2025 17:38:28 +0300 Subject: [PATCH 10/60] Update README.md --- README.md | 95 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 59 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 89ce1f87..73626da1 100644 --- a/README.md +++ b/README.md @@ -23,8 +23,8 @@ Show support for the Conductor OSS. Please help spread the awareness by starrin - [Custom Fetch Function](#custom-fetch-function) - [Core Concepts](#core-concepts) - [What are Tasks?](#what-are-tasks) - - [System Tasks (Built-in Workers)](#1-system-tasks-built-in-workers) - - [SIMPLE Tasks (Custom/External Workers)](#2-simple-tasks-customexternal-workers) + - [System Tasks - Managed by Conductor](#1-system-tasks---managed-by-conductor) + - [SIMPLE Tasks - Executed by Custom Workers](#2-simple-tasks---executed-by-custom-workers) - [What are Workflows?](#what-are-workflows) - [What are Workers?](#what-are-workers) - [What is the Scheduler?](#what-is-the-scheduler) @@ -217,20 +217,26 @@ const client = await orkesConductorClient(config, fetch); ### What are Tasks? -Tasks are individual units of work in Conductor. There are **two types of tasks**: +Tasks are individual units of work in Conductor. There are **two main categories**: -#### 1. System Tasks (Built-in Workers) -These tasks are executed by **built-in workers that run inside the Conductor server**. Conductor provides and manages these workers for you. +#### 1. System Tasks - Managed by Conductor +These tasks are managed by Conductor - you don't write or deploy any workers. They include: -**Examples:** HTTP, Event, Inline, JSON JQ Transform, Kafka, Switch, Fork/Join, Sub-Workflow, Set Variable, Wait, Terminate +**Automated System Tasks** (executed by built-in workers): +- HTTP, Event, Inline, JSON JQ Transform, Kafka, Switch, Fork/Join, Sub-Workflow, Set Variable, Wait, Terminate +- Execute automatically without human intervention + +**Human Tasks** (require human interaction): +- HUMAN tasks pause workflow and wait for a person to complete a form/approval +- Managed via HumanExecutor API **What you get:** - ✅ Ready to use - just reference them in your workflow - ✅ No code to write or deploy -- ✅ Optimized and maintained by Conductor -- ✅ Perfect for common operations (API calls, data transforms, flow control) +- ✅ Managed and maintained by Conductor +- ✅ Perfect for common operations, flow control, and human approvals -#### 2. SIMPLE Tasks (Custom Workers) +#### 2. SIMPLE Tasks - Executed by Custom Workers These tasks are executed by **custom workers that you write and deploy**. You implement the business logic and manage the worker lifecycle. **Examples:** `send_email`, `process_payment`, `generate_report`, `process_order` @@ -250,7 +256,7 @@ These tasks are executed by **custom workers that you write and deploy**. You im 5. Worker reports results back to Conductor ### What are Workflows? -Workflows are the main orchestration units in Conductor. They define a sequence of tasks and their dependencies. You can combine system tasks (like HTTP calls and data transforms) with your custom SIMPLE tasks to build complex business processes. +Workflows are the main orchestration units in Conductor. They define a sequence of tasks and their dependencies. You can combine system tasks (like HTTP calls and data transforms), custom SIMPLE tasks (your business logic), and HUMAN tasks (requiring human interaction) to build complex business processes. ### What are Workers? Workers are applications that poll Conductor for tasks and execute them. Conductor uses two types: @@ -269,18 +275,24 @@ The SDK provides generators for various task types to build workflow definitions ### Understanding Task Execution -All tasks in Conductor are executed by workers. The key difference is **who provides the worker**: +Tasks in Conductor are categorized by **who manages them**: + +**🔵 System Tasks** - Managed by Conductor (no custom workers) -**🔵 System Tasks** - Use Conductor's built-in workers -- HTTP, Event, Inline, JSON JQ Transform, Kafka, Switch, Fork-Join, Dynamic Fork, Join, Sub-Workflow, Set Variable, Wait, Terminate, Do-While -- Workers are provided and managed by Conductor +Includes two types: +- **Automated tasks**: HTTP, Event, Inline, JSON JQ Transform, Kafka, Switch, Fork-Join, Dynamic Fork, Join, Sub-Workflow, Set Variable, Wait, Terminate, Do-While + - Executed by built-in workers automatically +- **Human tasks**: HUMAN task type + - Pause workflow and wait for human action via HumanExecutor API + +**What you get:** - Just reference these tasks in your workflow - they work automatically -- Ideal for common operations, flow control, and data transformations +- No code to write or deploy +- Ideal for common operations, flow control, data transformations, and approvals -**🟢 SIMPLE Tasks** - Use your custom workers -- For your unique business logic and integrations -- HUMAN tasks are a special type that requires human interaction -- You write, deploy, and manage these workers +**🟢 SIMPLE Tasks** - Managed by you (custom workers required) +- For your unique business logic and integrations +- You write, deploy, and manage the workers - See the [Workers](#workers) section for implementation guide --- @@ -291,7 +303,7 @@ All tasks in Conductor are executed by workers. The key difference is **who prov ### Simple Task 🟢 -**Type:** SIMPLE Task (requires custom worker implementation) +**Type:** SIMPLE Task (executed by Custom Workers) ```typescript import { simpleTask } from "@io-orkes/conductor-javascript"; @@ -303,7 +315,7 @@ const task = simpleTask("task_ref", "task_name", { ### HTTP Task 🔵 -**Type:** System Task (executed by built-in workers) +**Type:** System Task (executed by Built-in Workers) ```typescript import { httpTask } from "@io-orkes/conductor-javascript"; @@ -318,7 +330,7 @@ const task = httpTask("http_ref", "http://api.example.com/data", { ### Switch Task 🔵 -**Type:** System Task (executed by built-in workers) +**Type:** System Task (executed by Built-in Workers) ```typescript import { switchTask } from "@io-orkes/conductor-javascript"; @@ -332,7 +344,7 @@ const task = switchTask("switch_ref", "input.status", { ### Fork-Join Task 🔵 -**Type:** System Task (executed by built-in workers) +**Type:** System Task (executed by Built-in Workers) ```typescript import { forkJoinTask } from "@io-orkes/conductor-javascript"; @@ -346,7 +358,7 @@ const task = forkJoinTask("fork_ref", [ ### Do-While Task 🔵 -**Type:** System Task (executed by built-in workers) +**Type:** System Task (executed by Built-in Workers) ```typescript import { doWhileTask } from "@io-orkes/conductor-javascript"; @@ -364,7 +376,7 @@ const task = doWhileTask("while_ref", "workflow.variables.counter < 10", [ ### Sub-Workflow Task 🔵 -**Type:** System Task (executed by built-in workers) +**Type:** System Task (executed by Built-in Workers) ```typescript import { subWorkflowTask } from "@io-orkes/conductor-javascript"; @@ -376,7 +388,7 @@ const task = subWorkflowTask("sub_ref", "child_workflow", 1, { ### Event Task 🔵 -**Type:** System Task (executed by built-in workers) +**Type:** System Task (executed by Built-in Workers) ```typescript import { eventTask } from "@io-orkes/conductor-javascript"; @@ -389,7 +401,7 @@ const task = eventTask("event_ref", "event_name", { ### Wait Task 🔵 -**Type:** System Task (executed by built-in workers) +**Type:** System Task (executed by Built-in Workers) ```typescript import { waitTask } from "@io-orkes/conductor-javascript"; @@ -399,7 +411,7 @@ const task = waitTask("wait_ref", 30); // wait 30 seconds ### Terminate Task 🔵 -**Type:** System Task (executed by built-in workers) +**Type:** System Task (executed by Built-in Workers) ```typescript import { terminateTask } from "@io-orkes/conductor-javascript"; @@ -409,7 +421,7 @@ const task = terminateTask("terminate_ref", "FAILED", "Error message"); ### Set Variable Task 🔵 -**Type:** System Task (executed by built-in workers) +**Type:** System Task (executed by Built-in Workers) ```typescript import { setVariableTask } from "@io-orkes/conductor-javascript"; @@ -422,7 +434,7 @@ const task = setVariableTask("var_ref", { ### JSON JQ Transform Task 🔵 -**Type:** System Task (executed by built-in workers) +**Type:** System Task (executed by Built-in Workers) ```typescript import { jsonJqTask } from "@io-orkes/conductor-javascript"; @@ -432,7 +444,7 @@ const task = jsonJqTask("transform_ref", ".data.items[] | {id: .id, name: .name} ### Kafka Publish Task 🔵 -**Type:** System Task (executed by built-in workers) +**Type:** System Task (executed by Built-in Workers) ```typescript import { kafkaPublishTask } from "@io-orkes/conductor-javascript"; @@ -447,7 +459,7 @@ const task = kafkaPublishTask("kafka_ref", "topic_name", { ### Inline Task 🔵 -**Type:** System Task (executed by built-in workers) +**Type:** System Task (executed by Built-in Workers) ```typescript import { inlineTask } from "@io-orkes/conductor-javascript"; @@ -461,7 +473,7 @@ const task = inlineTask("inline_ref", ` ### Dynamic Fork Task 🔵 -**Type:** System Task (executed by built-in workers) +**Type:** System Task (executed by Built-in Workers) ```typescript import { dynamicForkTask } from "@io-orkes/conductor-javascript"; @@ -471,7 +483,7 @@ const task = dynamicForkTask("dynamic_ref", "input.tasks", "task_name"); ### Join Task 🔵 -**Type:** System Task (executed by built-in workers) +**Type:** System Task (executed by Built-in Workers) ```typescript import { joinTask } from "@io-orkes/conductor-javascript"; @@ -479,9 +491,9 @@ import { joinTask } from "@io-orkes/conductor-javascript"; const task = joinTask("join_ref"); ``` -### Human Task 🟢 +### Human Task 🟡 -**Type:** SIMPLE Task (requires human interaction via custom UI) +**Type:** System Task for human interaction (managed via HumanExecutor API) ```typescript import { humanTask } from "@io-orkes/conductor-javascript"; @@ -1527,6 +1539,17 @@ await metadataClient.unregisterWorkflow("order_processing_workflow", 1); ## Human Tasks +HUMAN tasks are a **type of system task** that enable human interaction within workflows. They pause execution until a person completes an action like approving a request, filling out a form, or reviewing data. + +**As System Tasks:** +- **No custom workers needed** - Managed by Conductor, not by your code +- **Configured, not coded** - Use the `HumanExecutor` API to manage task lifecycle +- **Form-based** - Users interact through forms you define with TemplateClient +- **Assignment-based** - Tasks are assigned to users or groups +- **State management** - Tasks can be claimed, released, updated, and completed via API + +**Unlike other system tasks** (which execute automatically), HUMAN tasks wait for user action via the HumanExecutor API. + ### HumanExecutor The `HumanExecutor` class provides comprehensive human task management: From f9675f0e147c9f24f3c13a0305855b6b1c3a669b Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Thu, 2 Oct 2025 17:41:35 +0300 Subject: [PATCH 11/60] Update README.md --- README.md | 67 ++++++++++++++----------------------------------------- 1 file changed, 17 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index 73626da1..f2f8076a 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,6 @@ Show support for the Conductor OSS. Please help spread the awareness by starrin - [What are Workers?](#what-are-workers) - [What is the Scheduler?](#what-is-the-scheduler) - [Task Types](#task-types) - - [Understanding Task Execution](#understanding-task-execution) - [Simple Task](#simple-task) - [HTTP Task](#http-task) - [Switch Task](#switch-task) @@ -220,40 +219,28 @@ const client = await orkesConductorClient(config, fetch); Tasks are individual units of work in Conductor. There are **two main categories**: #### 1. System Tasks - Managed by Conductor -These tasks are managed by Conductor - you don't write or deploy any workers. They include: +Tasks managed by Conductor - no custom workers needed. -**Automated System Tasks** (executed by built-in workers): +**Automated System Tasks:** - HTTP, Event, Inline, JSON JQ Transform, Kafka, Switch, Fork/Join, Sub-Workflow, Set Variable, Wait, Terminate -- Execute automatically without human intervention +- Executed automatically by built-in workers -**Human Tasks** (require human interaction): -- HUMAN tasks pause workflow and wait for a person to complete a form/approval +**Human Tasks:** +- HUMAN task type - pauses workflow for human interaction (approvals, forms, reviews) - Managed via HumanExecutor API -**What you get:** -- ✅ Ready to use - just reference them in your workflow -- ✅ No code to write or deploy -- ✅ Managed and maintained by Conductor -- ✅ Perfect for common operations, flow control, and human approvals - #### 2. SIMPLE Tasks - Executed by Custom Workers -These tasks are executed by **custom workers that you write and deploy**. You implement the business logic and manage the worker lifecycle. +Tasks executed by **custom workers you write and deploy**. **Examples:** `send_email`, `process_payment`, `generate_report`, `process_order` -**When to use:** -- Custom business logic specific to your application -- Integration with internal systems -- Database operations -- File processing -- Any functionality not provided by system tasks - -**Worker execution flow:** +**How it works:** 1. Workflow creates a SIMPLE task -2. Your worker polls Conductor: "Any tasks for me?" -3. Conductor assigns the task to your worker -4. Your worker executes the custom logic -5. Worker reports results back to Conductor +2. Your worker polls Conductor for work +3. Worker executes your custom logic +4. Worker reports results back + +**Use for:** Custom business logic, internal integrations, database operations, file processing, or any functionality not provided by system tasks. ### What are Workflows? Workflows are the main orchestration units in Conductor. They define a sequence of tasks and their dependencies. You can combine system tasks (like HTTP calls and data transforms), custom SIMPLE tasks (your business logic), and HUMAN tasks (requiring human interaction) to build complex business processes. @@ -271,33 +258,13 @@ The scheduler allows you to schedule workflows to run at specific times or inter ## Task Types -The SDK provides generators for various task types to build workflow definitions. These generators create workflow task references that are used within workflow definitions. - -### Understanding Task Execution - -Tasks in Conductor are categorized by **who manages them**: +The SDK provides generators for various task types to build workflow definitions. Below are the available task types with usage examples: -**🔵 System Tasks** - Managed by Conductor (no custom workers) - -Includes two types: -- **Automated tasks**: HTTP, Event, Inline, JSON JQ Transform, Kafka, Switch, Fork-Join, Dynamic Fork, Join, Sub-Workflow, Set Variable, Wait, Terminate, Do-While - - Executed by built-in workers automatically -- **Human tasks**: HUMAN task type - - Pause workflow and wait for human action via HumanExecutor API - -**What you get:** -- Just reference these tasks in your workflow - they work automatically -- No code to write or deploy -- Ideal for common operations, flow control, data transformations, and approvals - -**🟢 SIMPLE Tasks** - Managed by you (custom workers required) -- For your unique business logic and integrations -- You write, deploy, and manage the workers -- See the [Workers](#workers) section for implementation guide - ---- +- **🔵 System Tasks** - Managed by Conductor (no custom workers needed) +- **🟢 SIMPLE Tasks** - Require custom workers you implement +- **🟡 HUMAN Tasks** - System tasks requiring human interaction via API -**Note:** These task generators create workflow task references, not task metadata definitions. To register task metadata (like task definitions with retry policies, timeouts, etc.), use the `taskDefinition()` factory function or plain objects with the `MetadataClient` (see [Metadata](#metadata) section). +**Note:** These generators create workflow task references for use in workflow definitions. To register task metadata with retry policies, timeouts, rate limits, etc., use the `taskDefinition()` factory function or the `MetadataClient` (see [Metadata](#metadata) section). --- From 73ae50c65ec2af1f0f9f6394b0c791b0df32c6d5 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Thu, 2 Oct 2025 17:45:48 +0300 Subject: [PATCH 12/60] Update README.md --- README.md | 95 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index f2f8076a..187511b9 100644 --- a/README.md +++ b/README.md @@ -23,12 +23,14 @@ Show support for the Conductor OSS. Please help spread the awareness by starrin - [Custom Fetch Function](#custom-fetch-function) - [Core Concepts](#core-concepts) - [What are Tasks?](#what-are-tasks) - - [System Tasks - Managed by Conductor](#1-system-tasks---managed-by-conductor) - - [SIMPLE Tasks - Executed by Custom Workers](#2-simple-tasks---executed-by-custom-workers) - [What are Workflows?](#what-are-workflows) - [What are Workers?](#what-are-workers) - [What is the Scheduler?](#what-is-the-scheduler) - [Task Types](#task-types) + - [Task Categories](#task-categories) + - [System Tasks - Managed by Conductor](#system-tasks---managed-by-conductor) + - [SIMPLE Tasks - Require Custom Workers](#simple-tasks---require-custom-workers) + - [Task Reference](#task-reference) - [Simple Task](#simple-task) - [HTTP Task](#http-task) - [Switch Task](#switch-task) @@ -216,55 +218,78 @@ const client = await orkesConductorClient(config, fetch); ### What are Tasks? -Tasks are individual units of work in Conductor. There are **two main categories**: +Tasks are individual units of work in Conductor workflows. Each task performs a specific operation, such as making an HTTP call, transforming data, executing custom business logic, or waiting for human approval. Tasks can be executed automatically by Conductor's built-in workers or by custom workers you implement. -#### 1. System Tasks - Managed by Conductor -Tasks managed by Conductor - no custom workers needed. +### What are Workflows? -**Automated System Tasks:** -- HTTP, Event, Inline, JSON JQ Transform, Kafka, Switch, Fork/Join, Sub-Workflow, Set Variable, Wait, Terminate -- Executed automatically by built-in workers +Workflows are the main orchestration units in Conductor. They define a sequence of tasks and their dependencies, creating automated business processes. Workflows coordinate task execution, handle failures, manage retries, and ensure your business logic flows correctly from start to finish. -**Human Tasks:** -- HUMAN task type - pauses workflow for human interaction (approvals, forms, reviews) -- Managed via HumanExecutor API +### What are Workers? -#### 2. SIMPLE Tasks - Executed by Custom Workers -Tasks executed by **custom workers you write and deploy**. +Workers are applications that poll Conductor for tasks and execute them. Conductor has built-in workers for common operations (HTTP calls, data transforms, etc.), and you can implement custom workers to execute your business-specific logic. This SDK provides tools to build and manage custom workers. -**Examples:** `send_email`, `process_payment`, `generate_report`, `process_order` +### What is the Scheduler? +The scheduler allows you to schedule workflows to run at specific times or intervals, enabling automated workflow execution based on time-based triggers. -**How it works:** -1. Workflow creates a SIMPLE task -2. Your worker polls Conductor for work -3. Worker executes your custom logic -4. Worker reports results back +## Task Types -**Use for:** Custom business logic, internal integrations, database operations, file processing, or any functionality not provided by system tasks. +Conductor provides various task types to build workflows. Understanding which tasks require custom workers and which are managed by Conductor is essential for effective workflow design. -### What are Workflows? -Workflows are the main orchestration units in Conductor. They define a sequence of tasks and their dependencies. You can combine system tasks (like HTTP calls and data transforms), custom SIMPLE tasks (your business logic), and HUMAN tasks (requiring human interaction) to build complex business processes. +### Task Categories -### What are Workers? -Workers are applications that poll Conductor for tasks and execute them. Conductor uses two types: +Tasks are categorized by **who manages their execution**: -- **Built-in Workers** - Run inside the Conductor server to execute system tasks (HTTP, Event, Inline, etc.). Conductor manages these for you. -- **Custom Workers** - Applications **you write and deploy** to execute SIMPLE tasks with your custom business logic. You're responsible for implementing and running these. +#### System Tasks 🔵 - Managed by Conductor -This SDK helps you build and manage custom workers for your SIMPLE tasks. +System tasks are fully managed by Conductor - you don't need to write or deploy any workers. Just reference them in your workflow and they execute automatically. -### What is the Scheduler? -The scheduler allows you to schedule workflows to run at specific times or intervals, enabling automated workflow execution based on time-based triggers. +**Automated System Tasks** (executed by built-in workers): +- **HTTP** - Make HTTP/REST API calls +- **Inline** - Execute JavaScript expressions +- **JSON JQ Transform** - Transform JSON data using JQ expressions +- **Kafka Publish** - Publish messages to Kafka topics +- **Event** - Publish events to eventing systems +- **Switch** - Conditional branching based on input +- **Fork-Join** - Execute tasks in parallel and wait for completion +- **Dynamic Fork** - Dynamically create parallel task executions +- **Join** - Join point for forked tasks +- **Sub-Workflow** - Execute another workflow as a task +- **Do-While** - Loop execution with conditions +- **Set Variable** - Set workflow variables +- **Wait** - Pause workflow for a specified duration +- **Terminate** - End workflow with success or failure -## Task Types +**Human Tasks** 🟡 (require human interaction): +- **HUMAN** - Pause workflow until a person completes a form or approval +- Managed via `HumanExecutor` API (see [Human Tasks](#human-tasks) section) +- No custom workers needed - uses form templates and assignment policies + +#### SIMPLE Tasks 🟢 - Require Custom Workers + +SIMPLE tasks execute **your custom business logic**. You must implement workers to poll for and execute these tasks. + +**Use SIMPLE tasks for:** +- Custom business logic specific to your application +- Integration with internal systems and databases +- File processing, data validation, notifications +- Any functionality not provided by system tasks + +**How they work:** +1. Define a SIMPLE task in your workflow +2. Implement a worker that polls Conductor for this task type +3. Worker executes your custom logic when task is assigned +4. Worker reports results back to Conductor +5. Workflow continues based on task result + +See the [Workers](#workers) section for implementation details. + +--- -The SDK provides generators for various task types to build workflow definitions. Below are the available task types with usage examples: +### Task Reference -- **🔵 System Tasks** - Managed by Conductor (no custom workers needed) -- **🟢 SIMPLE Tasks** - Require custom workers you implement -- **🟡 HUMAN Tasks** - System tasks requiring human interaction via API +Below are code examples for each task type. -**Note:** These generators create workflow task references for use in workflow definitions. To register task metadata with retry policies, timeouts, rate limits, etc., use the `taskDefinition()` factory function or the `MetadataClient` (see [Metadata](#metadata) section). +**Note:** These generators create workflow task references. To register task metadata (retry policies, timeouts, rate limits), use `taskDefinition()` or `MetadataClient` (see [Metadata](#metadata)). --- From 2704576bb0d4c31aeb04884f492c53d300286a18 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Thu, 2 Oct 2025 17:50:19 +0300 Subject: [PATCH 13/60] Update README.md --- README.md | 129 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 66 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index 187511b9..68da7c43 100644 --- a/README.md +++ b/README.md @@ -237,16 +237,35 @@ Conductor provides various task types to build workflows. Understanding which ta ### Task Categories -Tasks are categorized by **who manages their execution**: +Tasks in Conductor are divided into two main categories based on **who executes them**: -#### System Tasks 🔵 - Managed by Conductor +#### 🟢 SIMPLE Tasks - Require Custom Workers -System tasks are fully managed by Conductor - you don't need to write or deploy any workers. Just reference them in your workflow and they execute automatically. +SIMPLE tasks execute **your custom business logic**. You must implement and deploy workers to handle these tasks. -**Automated System Tasks** (executed by built-in workers): +**When to use:** +- Custom business logic specific to your application +- Integration with internal systems and databases +- File processing, data validation, notifications +- Any functionality not provided by system tasks + +**How they work:** +1. Define a SIMPLE task in your workflow +2. Implement a worker that polls Conductor for this task type +3. Worker executes your custom logic when task is assigned +4. Worker reports results back to Conductor +5. Workflow continues based on task result + +See the [Workers](#workers) section for implementation details. + +#### 🔵 System Tasks - Managed by Conductor + +System tasks are fully managed by Conductor. No custom workers needed - just reference them in your workflow and they execute automatically. + +**Available System Tasks:** - **HTTP** - Make HTTP/REST API calls - **Inline** - Execute JavaScript expressions -- **JSON JQ Transform** - Transform JSON data using JQ expressions +- **JSON JQ Transform** - Transform JSON data using JQ expressions - **Kafka Publish** - Publish messages to Kafka topics - **Event** - Publish events to eventing systems - **Switch** - Conditional branching based on input @@ -259,43 +278,25 @@ System tasks are fully managed by Conductor - you don't need to write or deploy - **Wait** - Pause workflow for a specified duration - **Terminate** - End workflow with success or failure -**Human Tasks** 🟡 (require human interaction): -- **HUMAN** - Pause workflow until a person completes a form or approval -- Managed via `HumanExecutor` API (see [Human Tasks](#human-tasks) section) -- No custom workers needed - uses form templates and assignment policies +#### 🟡 Human Tasks - Managed by Conductor -#### SIMPLE Tasks 🟢 - Require Custom Workers +HUMAN tasks are a special type of system task that pause workflow execution until a person completes an action (approval, form submission, etc.). Managed via the `HumanExecutor` API - no custom workers needed. -SIMPLE tasks execute **your custom business logic**. You must implement workers to poll for and execute these tasks. - -**Use SIMPLE tasks for:** -- Custom business logic specific to your application -- Integration with internal systems and databases -- File processing, data validation, notifications -- Any functionality not provided by system tasks - -**How they work:** -1. Define a SIMPLE task in your workflow -2. Implement a worker that polls Conductor for this task type -3. Worker executes your custom logic when task is assigned -4. Worker reports results back to Conductor -5. Workflow continues based on task result - -See the [Workers](#workers) section for implementation details. +See the [Human Tasks](#human-tasks) section for details. --- ### Task Reference -Below are code examples for each task type. +Below are code examples for each task type. The emoji indicates whether the task requires custom workers (🟢), is managed by Conductor (🔵), or requires human interaction (🟡). **Note:** These generators create workflow task references. To register task metadata (retry policies, timeouts, rate limits), use `taskDefinition()` or `MetadataClient` (see [Metadata](#metadata)). --- -### Simple Task 🟢 +### 🟢 Simple Task -**Type:** SIMPLE Task (executed by Custom Workers) +Executes custom business logic via workers you implement. ```typescript import { simpleTask } from "@io-orkes/conductor-javascript"; @@ -305,9 +306,9 @@ const task = simpleTask("task_ref", "task_name", { }, false); // optional: if true, workflow continues even if task fails ``` -### HTTP Task 🔵 +### 🔵 HTTP Task -**Type:** System Task (executed by Built-in Workers) +Makes HTTP/REST API calls. ```typescript import { httpTask } from "@io-orkes/conductor-javascript"; @@ -320,9 +321,9 @@ const task = httpTask("http_ref", "http://api.example.com/data", { }); ``` -### Switch Task 🔵 +### 🔵 Switch Task -**Type:** System Task (executed by Built-in Workers) +Provides conditional branching based on input values. ```typescript import { switchTask } from "@io-orkes/conductor-javascript"; @@ -334,9 +335,9 @@ const task = switchTask("switch_ref", "input.status", { }); ``` -### Fork-Join Task 🔵 +### 🔵 Fork-Join Task -**Type:** System Task (executed by Built-in Workers) +Executes multiple task branches in parallel and waits for all to complete. ```typescript import { forkJoinTask } from "@io-orkes/conductor-javascript"; @@ -348,9 +349,9 @@ const task = forkJoinTask("fork_ref", [ ]); ``` -### Do-While Task 🔵 +### 🔵 Do-While Task -**Type:** System Task (executed by Built-in Workers) +Executes a loop with a condition evaluated after each iteration. ```typescript import { doWhileTask } from "@io-orkes/conductor-javascript"; @@ -366,9 +367,9 @@ const task = doWhileTask("while_ref", "workflow.variables.counter < 10", [ ]); ``` -### Sub-Workflow Task 🔵 +### 🔵 Sub-Workflow Task -**Type:** System Task (executed by Built-in Workers) +Executes another workflow as a task. ```typescript import { subWorkflowTask } from "@io-orkes/conductor-javascript"; @@ -378,9 +379,9 @@ const task = subWorkflowTask("sub_ref", "child_workflow", 1, { }, "COMPLETED"); // wait for completion status ``` -### Event Task 🔵 +### 🔵 Event Task -**Type:** System Task (executed by Built-in Workers) +Publishes events to external eventing systems. ```typescript import { eventTask } from "@io-orkes/conductor-javascript"; @@ -391,9 +392,9 @@ const task = eventTask("event_ref", "event_name", { }); ``` -### Wait Task 🔵 +### 🔵 Wait Task -**Type:** System Task (executed by Built-in Workers) +Pauses workflow execution for a specified duration. ```typescript import { waitTask } from "@io-orkes/conductor-javascript"; @@ -401,9 +402,9 @@ import { waitTask } from "@io-orkes/conductor-javascript"; const task = waitTask("wait_ref", 30); // wait 30 seconds ``` -### Terminate Task 🔵 +### 🔵 Terminate Task -**Type:** System Task (executed by Built-in Workers) +Terminates workflow execution with a specified status. ```typescript import { terminateTask } from "@io-orkes/conductor-javascript"; @@ -411,9 +412,9 @@ import { terminateTask } from "@io-orkes/conductor-javascript"; const task = terminateTask("terminate_ref", "FAILED", "Error message"); ``` -### Set Variable Task 🔵 +### 🔵 Set Variable Task -**Type:** System Task (executed by Built-in Workers) +Sets or updates workflow variables. ```typescript import { setVariableTask } from "@io-orkes/conductor-javascript"; @@ -424,9 +425,9 @@ const task = setVariableTask("var_ref", { }); ``` -### JSON JQ Transform Task 🔵 +### 🔵 JSON JQ Transform Task -**Type:** System Task (executed by Built-in Workers) +Transforms JSON data using JQ expressions. ```typescript import { jsonJqTask } from "@io-orkes/conductor-javascript"; @@ -434,9 +435,9 @@ import { jsonJqTask } from "@io-orkes/conductor-javascript"; const task = jsonJqTask("transform_ref", ".data.items[] | {id: .id, name: .name}"); ``` -### Kafka Publish Task 🔵 +### 🔵 Kafka Publish Task -**Type:** System Task (executed by Built-in Workers) +Publishes messages to Kafka topics. ```typescript import { kafkaPublishTask } from "@io-orkes/conductor-javascript"; @@ -449,9 +450,9 @@ const task = kafkaPublishTask("kafka_ref", "topic_name", { }); ``` -### Inline Task 🔵 +### 🔵 Inline Task -**Type:** System Task (executed by Built-in Workers) +Executes JavaScript code inline within the workflow. ```typescript import { inlineTask } from "@io-orkes/conductor-javascript"; @@ -463,9 +464,9 @@ const task = inlineTask("inline_ref", ` `); ``` -### Dynamic Fork Task 🔵 +### 🔵 Dynamic Fork Task -**Type:** System Task (executed by Built-in Workers) +Dynamically creates parallel task executions based on input. ```typescript import { dynamicForkTask } from "@io-orkes/conductor-javascript"; @@ -473,9 +474,9 @@ import { dynamicForkTask } from "@io-orkes/conductor-javascript"; const task = dynamicForkTask("dynamic_ref", "input.tasks", "task_name"); ``` -### Join Task 🔵 +### 🔵 Join Task -**Type:** System Task (executed by Built-in Workers) +Synchronization point for forked tasks. ```typescript import { joinTask } from "@io-orkes/conductor-javascript"; @@ -483,9 +484,9 @@ import { joinTask } from "@io-orkes/conductor-javascript"; const task = joinTask("join_ref"); ``` -### Human Task 🟡 +### 🟡 Human Task -**Type:** System Task for human interaction (managed via HumanExecutor API) +Pauses workflow until a person completes an action (approval, form submission, etc.). ```typescript import { humanTask } from "@io-orkes/conductor-javascript"; @@ -501,18 +502,20 @@ const task = humanTask("human_ref", "approval_task", { }); ``` -### Usage Example: Creating Workflows +--- + +### Usage Example: Combining Task Types in Workflows ```typescript import { workflow, simpleTask, httpTask } from "@io-orkes/conductor-javascript"; const myWorkflow = workflow("order_processing", [ - simpleTask("validate_order", "validate_order_task", {}), - httpTask("call_payment", "https://api.payment.com/charge", { + simpleTask("validate_order", "validate_order_task", {}), // 🟢 Custom worker + httpTask("call_payment", "https://api.payment.com/charge", { // 🔵 System task method: "POST", headers: { "Authorization": "Bearer token" } }), - simpleTask("send_confirmation", "send_email_task", {}) + simpleTask("send_confirmation", "send_email_task", {}) // 🟢 Custom worker ]); ``` From c16852adf67989253c1ef2bd621e14b2c326633b Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Thu, 2 Oct 2025 17:54:47 +0300 Subject: [PATCH 14/60] Update README.md --- README.md | 83 ++++++++++++++++++++++++++----------------------------- 1 file changed, 39 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 68da7c43..d7d9d9b2 100644 --- a/README.md +++ b/README.md @@ -239,7 +239,7 @@ Conductor provides various task types to build workflows. Understanding which ta Tasks in Conductor are divided into two main categories based on **who executes them**: -#### 🟢 SIMPLE Tasks - Require Custom Workers +#### SIMPLE Tasks - Require Custom Workers SIMPLE tasks execute **your custom business logic**. You must implement and deploy workers to handle these tasks. @@ -258,7 +258,7 @@ SIMPLE tasks execute **your custom business logic**. You must implement and depl See the [Workers](#workers) section for implementation details. -#### 🔵 System Tasks - Managed by Conductor +#### System Tasks - Managed by Conductor System tasks are fully managed by Conductor. No custom workers needed - just reference them in your workflow and they execute automatically. @@ -277,26 +277,21 @@ System tasks are fully managed by Conductor. No custom workers needed - just ref - **Set Variable** - Set workflow variables - **Wait** - Pause workflow for a specified duration - **Terminate** - End workflow with success or failure - -#### 🟡 Human Tasks - Managed by Conductor - -HUMAN tasks are a special type of system task that pause workflow execution until a person completes an action (approval, form submission, etc.). Managed via the `HumanExecutor` API - no custom workers needed. - -See the [Human Tasks](#human-tasks) section for details. +- **Human** - Pause workflow until a person completes an action (approval, form submission, etc.). Managed via the `HumanExecutor` API. See [Human Tasks](#human-tasks) section for details. --- ### Task Reference -Below are code examples for each task type. The emoji indicates whether the task requires custom workers (🟢), is managed by Conductor (🔵), or requires human interaction (🟡). +Below are code examples for each task type. **Note:** These generators create workflow task references. To register task metadata (retry policies, timeouts, rate limits), use `taskDefinition()` or `MetadataClient` (see [Metadata](#metadata)). --- -### 🟢 Simple Task +### Simple Task -Executes custom business logic via workers you implement. +*Requires Custom Workers* - Executes custom business logic via workers you implement. ```typescript import { simpleTask } from "@io-orkes/conductor-javascript"; @@ -306,9 +301,9 @@ const task = simpleTask("task_ref", "task_name", { }, false); // optional: if true, workflow continues even if task fails ``` -### 🔵 HTTP Task +### HTTP Task -Makes HTTP/REST API calls. +*System Task* - Makes HTTP/REST API calls. ```typescript import { httpTask } from "@io-orkes/conductor-javascript"; @@ -321,9 +316,9 @@ const task = httpTask("http_ref", "http://api.example.com/data", { }); ``` -### 🔵 Switch Task +### Switch Task -Provides conditional branching based on input values. +*System Task* - Provides conditional branching based on input values. ```typescript import { switchTask } from "@io-orkes/conductor-javascript"; @@ -335,9 +330,9 @@ const task = switchTask("switch_ref", "input.status", { }); ``` -### 🔵 Fork-Join Task +### Fork-Join Task -Executes multiple task branches in parallel and waits for all to complete. +*System Task* - Executes multiple task branches in parallel and waits for all to complete. ```typescript import { forkJoinTask } from "@io-orkes/conductor-javascript"; @@ -349,9 +344,9 @@ const task = forkJoinTask("fork_ref", [ ]); ``` -### 🔵 Do-While Task +### Do-While Task -Executes a loop with a condition evaluated after each iteration. +*System Task* - Executes a loop with a condition evaluated after each iteration. ```typescript import { doWhileTask } from "@io-orkes/conductor-javascript"; @@ -367,9 +362,9 @@ const task = doWhileTask("while_ref", "workflow.variables.counter < 10", [ ]); ``` -### 🔵 Sub-Workflow Task +### Sub-Workflow Task -Executes another workflow as a task. +*System Task* - Executes another workflow as a task. ```typescript import { subWorkflowTask } from "@io-orkes/conductor-javascript"; @@ -379,9 +374,9 @@ const task = subWorkflowTask("sub_ref", "child_workflow", 1, { }, "COMPLETED"); // wait for completion status ``` -### 🔵 Event Task +### Event Task -Publishes events to external eventing systems. +*System Task* - Publishes events to external eventing systems. ```typescript import { eventTask } from "@io-orkes/conductor-javascript"; @@ -392,9 +387,9 @@ const task = eventTask("event_ref", "event_name", { }); ``` -### 🔵 Wait Task +### Wait Task -Pauses workflow execution for a specified duration. +*System Task* - Pauses workflow execution for a specified duration. ```typescript import { waitTask } from "@io-orkes/conductor-javascript"; @@ -402,9 +397,9 @@ import { waitTask } from "@io-orkes/conductor-javascript"; const task = waitTask("wait_ref", 30); // wait 30 seconds ``` -### 🔵 Terminate Task +### Terminate Task -Terminates workflow execution with a specified status. +*System Task* - Terminates workflow execution with a specified status. ```typescript import { terminateTask } from "@io-orkes/conductor-javascript"; @@ -412,9 +407,9 @@ import { terminateTask } from "@io-orkes/conductor-javascript"; const task = terminateTask("terminate_ref", "FAILED", "Error message"); ``` -### 🔵 Set Variable Task +### Set Variable Task -Sets or updates workflow variables. +*System Task* - Sets or updates workflow variables. ```typescript import { setVariableTask } from "@io-orkes/conductor-javascript"; @@ -425,9 +420,9 @@ const task = setVariableTask("var_ref", { }); ``` -### 🔵 JSON JQ Transform Task +### JSON JQ Transform Task -Transforms JSON data using JQ expressions. +*System Task* - Transforms JSON data using JQ expressions. ```typescript import { jsonJqTask } from "@io-orkes/conductor-javascript"; @@ -435,9 +430,9 @@ import { jsonJqTask } from "@io-orkes/conductor-javascript"; const task = jsonJqTask("transform_ref", ".data.items[] | {id: .id, name: .name}"); ``` -### 🔵 Kafka Publish Task +### Kafka Publish Task -Publishes messages to Kafka topics. +*System Task* - Publishes messages to Kafka topics. ```typescript import { kafkaPublishTask } from "@io-orkes/conductor-javascript"; @@ -450,9 +445,9 @@ const task = kafkaPublishTask("kafka_ref", "topic_name", { }); ``` -### 🔵 Inline Task +### Inline Task -Executes JavaScript code inline within the workflow. +*System Task* - Executes JavaScript code inline within the workflow. ```typescript import { inlineTask } from "@io-orkes/conductor-javascript"; @@ -464,9 +459,9 @@ const task = inlineTask("inline_ref", ` `); ``` -### 🔵 Dynamic Fork Task +### Dynamic Fork Task -Dynamically creates parallel task executions based on input. +*System Task* - Dynamically creates parallel task executions based on input. ```typescript import { dynamicForkTask } from "@io-orkes/conductor-javascript"; @@ -474,9 +469,9 @@ import { dynamicForkTask } from "@io-orkes/conductor-javascript"; const task = dynamicForkTask("dynamic_ref", "input.tasks", "task_name"); ``` -### 🔵 Join Task +### Join Task -Synchronization point for forked tasks. +*System Task* - Synchronization point for forked tasks. ```typescript import { joinTask } from "@io-orkes/conductor-javascript"; @@ -484,9 +479,9 @@ import { joinTask } from "@io-orkes/conductor-javascript"; const task = joinTask("join_ref"); ``` -### 🟡 Human Task +### Human Task -Pauses workflow until a person completes an action (approval, form submission, etc.). +*System Task* - Pauses workflow until a person completes an action (approval, form submission, etc.). ```typescript import { humanTask } from "@io-orkes/conductor-javascript"; @@ -510,12 +505,12 @@ const task = humanTask("human_ref", "approval_task", { import { workflow, simpleTask, httpTask } from "@io-orkes/conductor-javascript"; const myWorkflow = workflow("order_processing", [ - simpleTask("validate_order", "validate_order_task", {}), // 🟢 Custom worker - httpTask("call_payment", "https://api.payment.com/charge", { // 🔵 System task + simpleTask("validate_order", "validate_order_task", {}), // Custom worker + httpTask("call_payment", "https://api.payment.com/charge", { // System task method: "POST", headers: { "Authorization": "Bearer token" } }), - simpleTask("send_confirmation", "send_email_task", {}) // 🟢 Custom worker + simpleTask("send_confirmation", "send_email_task", {}) // Custom worker ]); ``` From d18b0cb89172cfb5f523dcbce7476a0068286da9 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Thu, 2 Oct 2025 18:00:07 +0300 Subject: [PATCH 15/60] Update README.md --- README.md | 48 +++++++++++++++++++++--------------------------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index d7d9d9b2..b1d2e3bb 100644 --- a/README.md +++ b/README.md @@ -233,32 +233,9 @@ The scheduler allows you to schedule workflows to run at specific times or inter ## Task Types -Conductor provides various task types to build workflows. Understanding which tasks require custom workers and which are managed by Conductor is essential for effective workflow design. +Conductor provides various task types to build workflows. Understanding which tasks require custom workers and which are managed by Conductor is essential for effective workflow design. Tasks in Conductor are divided into two main categories based on **who executes them**: -### Task Categories - -Tasks in Conductor are divided into two main categories based on **who executes them**: - -#### SIMPLE Tasks - Require Custom Workers - -SIMPLE tasks execute **your custom business logic**. You must implement and deploy workers to handle these tasks. - -**When to use:** -- Custom business logic specific to your application -- Integration with internal systems and databases -- File processing, data validation, notifications -- Any functionality not provided by system tasks - -**How they work:** -1. Define a SIMPLE task in your workflow -2. Implement a worker that polls Conductor for this task type -3. Worker executes your custom logic when task is assigned -4. Worker reports results back to Conductor -5. Workflow continues based on task result - -See the [Workers](#workers) section for implementation details. - -#### System Tasks - Managed by Conductor +### System Tasks - Managed by Conductor Server System tasks are fully managed by Conductor. No custom workers needed - just reference them in your workflow and they execute automatically. @@ -279,6 +256,25 @@ System tasks are fully managed by Conductor. No custom workers needed - just ref - **Terminate** - End workflow with success or failure - **Human** - Pause workflow until a person completes an action (approval, form submission, etc.). Managed via the `HumanExecutor` API. See [Human Tasks](#human-tasks) section for details. +### SIMPLE Tasks - Require Custom Workers + +SIMPLE tasks execute **your custom business logic**. You must implement and deploy workers to handle these tasks. + +**When to use:** +- Custom business logic specific to your application +- Integration with internal systems and databases +- File processing, data validation, notifications +- Any functionality not provided by system tasks + +**How they work:** +1. Define a SIMPLE task in your workflow +2. Implement a worker that polls Conductor for this task type +3. Worker executes your custom logic when task is assigned +4. Worker reports results back to Conductor +5. Workflow continues based on task result + +See the [Workers](#workers) section for implementation details. + --- ### Task Reference @@ -287,8 +283,6 @@ Below are code examples for each task type. **Note:** These generators create workflow task references. To register task metadata (retry policies, timeouts, rate limits), use `taskDefinition()` or `MetadataClient` (see [Metadata](#metadata)). ---- - ### Simple Task *Requires Custom Workers* - Executes custom business logic via workers you implement. From 5de81b0296a01f6af644444d96dc912b72d60f78 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Thu, 2 Oct 2025 18:05:01 +0300 Subject: [PATCH 16/60] Update README.md --- README.md | 131 ++++++++++++++++++++++++++---------------------------- 1 file changed, 63 insertions(+), 68 deletions(-) diff --git a/README.md b/README.md index b1d2e3bb..cb850900 100644 --- a/README.md +++ b/README.md @@ -27,29 +27,28 @@ Show support for the Conductor OSS. Please help spread the awareness by starrin - [What are Workers?](#what-are-workers) - [What is the Scheduler?](#what-is-the-scheduler) - [Task Types](#task-types) - - [Task Categories](#task-categories) - - [System Tasks - Managed by Conductor](#system-tasks---managed-by-conductor) - - [SIMPLE Tasks - Require Custom Workers](#simple-tasks---require-custom-workers) - - [Task Reference](#task-reference) - - [Simple Task](#simple-task) - - [HTTP Task](#http-task) - - [Switch Task](#switch-task) - - [Fork-Join Task](#fork-join-task) - - [Do-While Task](#do-while-task) - - [Sub-Workflow Task](#sub-workflow-task) - - [Event Task](#event-task) - - [Wait Task](#wait-task) - - [Terminate Task](#terminate-task) - - [Set Variable Task](#set-variable-task) - - [JSON JQ Transform Task](#json-jq-transform-task) - - [Kafka Publish Task](#kafka-publish-task) - - [Inline Task](#inline-task) - - [Dynamic Fork Task](#dynamic-fork-task) - - [Join Task](#join-task) - - [Human Task](#human-task) + - [System Tasks - Managed by Conductor Server](#system-tasks---managed-by-conductor-server) + - [SIMPLE Tasks - Require Custom Workers](#simple-tasks---require-custom-workers) - [Workflows](#workflows) - [WorkflowExecutor](#workflowexecutor) - [Workflow Factory](#workflow-factory) + - [Building Workflows with Task Generators](#building-workflows-with-task-generators) + - [Simple Task](#simple-task) + - [HTTP Task](#http-task) + - [Switch Task](#switch-task) + - [Fork-Join Task](#fork-join-task) + - [Do-While Task](#do-while-task) + - [Sub-Workflow Task](#sub-workflow-task) + - [Event Task](#event-task) + - [Wait Task](#wait-task) + - [Terminate Task](#terminate-task) + - [Set Variable Task](#set-variable-task) + - [JSON JQ Transform Task](#json-jq-transform-task) + - [Kafka Publish Task](#kafka-publish-task) + - [Inline Task](#inline-task) + - [Dynamic Fork Task](#dynamic-fork-task) + - [Join Task](#join-task) + - [Human Task](#human-task) - [Register Workflow](#register-workflow) - [Start Workflow](#start-workflow) - [Get Workflow Status](#get-workflow-status) @@ -275,15 +274,38 @@ SIMPLE tasks execute **your custom business logic**. You must implement and depl See the [Workers](#workers) section for implementation details. ---- +## Workflows + +### WorkflowExecutor + +The `WorkflowExecutor` class provides methods for managing workflows: + +```typescript +import { WorkflowExecutor } from "@io-orkes/conductor-javascript"; + +const executor = new WorkflowExecutor(client); +``` + +### Workflow Factory -### Task Reference +The `workflow` function provides a convenient way to create workflow definitions: -Below are code examples for each task type. +```typescript +import { workflow, simpleTask } from "@io-orkes/conductor-javascript"; + +const myWorkflow = workflow("workflow_name", [ + simpleTask("task1", "process_1", {}), + simpleTask("task2", "process_2", {}) +]); +``` + +### Building Workflows with Task Generators + +This section provides code examples for each task type generator. Use these to build your workflow task lists. **Note:** These generators create workflow task references. To register task metadata (retry policies, timeouts, rate limits), use `taskDefinition()` or `MetadataClient` (see [Metadata](#metadata)). -### Simple Task +#### Simple Task *Requires Custom Workers* - Executes custom business logic via workers you implement. @@ -295,7 +317,7 @@ const task = simpleTask("task_ref", "task_name", { }, false); // optional: if true, workflow continues even if task fails ``` -### HTTP Task +#### HTTP Task *System Task* - Makes HTTP/REST API calls. @@ -310,7 +332,7 @@ const task = httpTask("http_ref", "http://api.example.com/data", { }); ``` -### Switch Task +#### Switch Task *System Task* - Provides conditional branching based on input values. @@ -324,7 +346,7 @@ const task = switchTask("switch_ref", "input.status", { }); ``` -### Fork-Join Task +#### Fork-Join Task *System Task* - Executes multiple task branches in parallel and waits for all to complete. @@ -338,7 +360,7 @@ const task = forkJoinTask("fork_ref", [ ]); ``` -### Do-While Task +#### Do-While Task *System Task* - Executes a loop with a condition evaluated after each iteration. @@ -356,7 +378,7 @@ const task = doWhileTask("while_ref", "workflow.variables.counter < 10", [ ]); ``` -### Sub-Workflow Task +#### Sub-Workflow Task *System Task* - Executes another workflow as a task. @@ -368,7 +390,7 @@ const task = subWorkflowTask("sub_ref", "child_workflow", 1, { }, "COMPLETED"); // wait for completion status ``` -### Event Task +#### Event Task *System Task* - Publishes events to external eventing systems. @@ -381,7 +403,7 @@ const task = eventTask("event_ref", "event_name", { }); ``` -### Wait Task +#### Wait Task *System Task* - Pauses workflow execution for a specified duration. @@ -391,7 +413,7 @@ import { waitTask } from "@io-orkes/conductor-javascript"; const task = waitTask("wait_ref", 30); // wait 30 seconds ``` -### Terminate Task +#### Terminate Task *System Task* - Terminates workflow execution with a specified status. @@ -401,7 +423,7 @@ import { terminateTask } from "@io-orkes/conductor-javascript"; const task = terminateTask("terminate_ref", "FAILED", "Error message"); ``` -### Set Variable Task +#### Set Variable Task *System Task* - Sets or updates workflow variables. @@ -414,7 +436,7 @@ const task = setVariableTask("var_ref", { }); ``` -### JSON JQ Transform Task +#### JSON JQ Transform Task *System Task* - Transforms JSON data using JQ expressions. @@ -424,7 +446,7 @@ import { jsonJqTask } from "@io-orkes/conductor-javascript"; const task = jsonJqTask("transform_ref", ".data.items[] | {id: .id, name: .name}"); ``` -### Kafka Publish Task +#### Kafka Publish Task *System Task* - Publishes messages to Kafka topics. @@ -439,7 +461,7 @@ const task = kafkaPublishTask("kafka_ref", "topic_name", { }); ``` -### Inline Task +#### Inline Task *System Task* - Executes JavaScript code inline within the workflow. @@ -453,7 +475,7 @@ const task = inlineTask("inline_ref", ` `); ``` -### Dynamic Fork Task +#### Dynamic Fork Task *System Task* - Dynamically creates parallel task executions based on input. @@ -463,7 +485,7 @@ import { dynamicForkTask } from "@io-orkes/conductor-javascript"; const task = dynamicForkTask("dynamic_ref", "input.tasks", "task_name"); ``` -### Join Task +#### Join Task *System Task* - Synchronization point for forked tasks. @@ -473,7 +495,7 @@ import { joinTask } from "@io-orkes/conductor-javascript"; const task = joinTask("join_ref"); ``` -### Human Task +#### Human Task *System Task* - Pauses workflow until a person completes an action (approval, form submission, etc.). @@ -491,9 +513,7 @@ const task = humanTask("human_ref", "approval_task", { }); ``` ---- - -### Usage Example: Combining Task Types in Workflows +#### Example: Combining Task Types in Workflows ```typescript import { workflow, simpleTask, httpTask } from "@io-orkes/conductor-javascript"; @@ -508,32 +528,7 @@ const myWorkflow = workflow("order_processing", [ ]); ``` -## Workflows - -### WorkflowExecutor - -The `WorkflowExecutor` class provides methods for managing workflows: - -```typescript -import { WorkflowExecutor } from "@io-orkes/conductor-javascript"; - -const executor = new WorkflowExecutor(client); -``` - -### Workflow Factory - -The `workflow` function provides a convenient way to create workflow definitions: - -```typescript -import { workflow, simpleTask } from "@io-orkes/conductor-javascript"; - -const myWorkflow = workflow("workflow_name", [ - simpleTask("task1", "process_1", {}), - simpleTask("task2", "process_2", {}) -]); -``` - -#### Register Workflow +### Register Workflow ```typescript const workflowDef = { From ea702c93a924353818255faf7a4bbe96be0aaf53 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Thu, 2 Oct 2025 18:12:54 +0300 Subject: [PATCH 17/60] Update README.md --- README.md | 93 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 59 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index cb850900..e73527d3 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,10 @@ Show support for the Conductor OSS. Please help spread the awareness by starrin - [SIMPLE Tasks - Require Custom Workers](#simple-tasks---require-custom-workers) - [Workflows](#workflows) - [WorkflowExecutor](#workflowexecutor) - - [Workflow Factory](#workflow-factory) - - [Building Workflows with Task Generators](#building-workflows-with-task-generators) + - [Creating Workflows](#creating-workflows) + - [Workflow Factory](#workflow-factory) + - [Example: Combining Task Types](#example-combining-task-types) + - [Task Generators Reference](#task-generators-reference) - [Simple Task](#simple-task) - [HTTP Task](#http-task) - [Switch Task](#switch-task) @@ -49,13 +51,14 @@ Show support for the Conductor OSS. Please help spread the awareness by starrin - [Dynamic Fork Task](#dynamic-fork-task) - [Join Task](#join-task) - [Human Task](#human-task) - - [Register Workflow](#register-workflow) - - [Start Workflow](#start-workflow) - - [Get Workflow Status](#get-workflow-status) - - [Pause Workflow](#pause-workflow) - - [Resume Workflow](#resume-workflow) - - [Terminate Workflow](#terminate-workflow) - - [Workflow Search](#workflow-search) + - [Workflow Lifecycle Operations](#workflow-lifecycle-operations) + - [Register Workflow](#register-workflow) + - [Start Workflow](#start-workflow) + - [Get Workflow Status](#get-workflow-status) + - [Pause Workflow](#pause-workflow) + - [Resume Workflow](#resume-workflow) + - [Terminate Workflow](#terminate-workflow) + - [Search Workflows](#search-workflows) - [Monitoring & Debugging Tasks](#monitoring--debugging-tasks) - [Task Statuses](#task-statuses) - [Searching & Filtering Tasks](#searching--filtering-tasks) @@ -74,6 +77,8 @@ Show support for the Conductor OSS. Please help spread the awareness by starrin - [Graceful Shutdown](#graceful-shutdown) - [TaskRunner (Low-level)](#taskrunner-low-level) - [Configuration Options](#configuration-options-1) + - [TaskManager Configuration](#taskmanager-configuration) + - [TaskRunner Configuration](#taskrunner-configuration) - [When to Use Each Approach](#when-to-use-each-approach) - [Scheduling](#scheduling) - [SchedulerClient](#schedulerclient) @@ -90,8 +95,8 @@ Show support for the Conductor OSS. Please help spread the awareness by starrin - [Human Tasks](#human-tasks) - [HumanExecutor](#humanexecutor) - [TemplateClient](#templateclient) - - [Register Form Template](#register-form-template) - - [Register UI Template](#register-ui-template) + - [Register Form Template](#register-form-template) + - [Register UI Template](#register-ui-template) - [Error Handling](#error-handling) - [Worker Error Handling](#worker-error-handling) - [Task Manager Error Handling](#task-manager-error-handling) @@ -276,9 +281,14 @@ See the [Workers](#workers) section for implementation details. ## Workflows +Workflows in Conductor are defined using task generators and managed through the `WorkflowExecutor` class. This section covers: +- **Creating workflows** using the workflow factory and task generators +- **Managing workflow lifecycle** (register, start, pause, resume, terminate, search) +- **Monitoring and debugging** workflow tasks + ### WorkflowExecutor -The `WorkflowExecutor` class provides methods for managing workflows: +The `WorkflowExecutor` class provides methods for managing workflow lifecycle: ```typescript import { WorkflowExecutor } from "@io-orkes/conductor-javascript"; @@ -286,7 +296,9 @@ import { WorkflowExecutor } from "@io-orkes/conductor-javascript"; const executor = new WorkflowExecutor(client); ``` -### Workflow Factory +### Creating Workflows + +#### Workflow Factory The `workflow` function provides a convenient way to create workflow definitions: @@ -299,7 +311,24 @@ const myWorkflow = workflow("workflow_name", [ ]); ``` -### Building Workflows with Task Generators +#### Example: Combining Task Types + +Here's how to combine different task types in a workflow: + +```typescript +import { workflow, simpleTask, httpTask } from "@io-orkes/conductor-javascript"; + +const myWorkflow = workflow("order_processing", [ + simpleTask("validate_order", "validate_order_task", {}), // Custom worker + httpTask("call_payment", "https://api.payment.com/charge", { // System task + method: "POST", + headers: { "Authorization": "Bearer token" } + }), + simpleTask("send_confirmation", "send_email_task", {}) // Custom worker +]); +``` + +### Task Generators Reference This section provides code examples for each task type generator. Use these to build your workflow task lists. @@ -513,22 +542,11 @@ const task = humanTask("human_ref", "approval_task", { }); ``` -#### Example: Combining Task Types in Workflows +### Workflow Lifecycle Operations -```typescript -import { workflow, simpleTask, httpTask } from "@io-orkes/conductor-javascript"; +This section covers all operations for managing workflow execution and lifecycle. -const myWorkflow = workflow("order_processing", [ - simpleTask("validate_order", "validate_order_task", {}), // Custom worker - httpTask("call_payment", "https://api.payment.com/charge", { // System task - method: "POST", - headers: { "Authorization": "Bearer token" } - }), - simpleTask("send_confirmation", "send_email_task", {}) // Custom worker -]); -``` - -### Register Workflow +#### Register Workflow ```typescript const workflowDef = { @@ -628,7 +646,7 @@ await executor.resume(executionId); await executor.terminate(executionId, "Terminating due to error"); ``` -#### Workflow Search +#### Search Workflows ```typescript const searchResults = await executor.search( @@ -640,6 +658,13 @@ const searchResults = await executor.search( ); ``` +**Search Parameters:** +- `start`: Starting index for pagination (default: 0) +- `size`: Number of results to return (default: 100) +- `sort`: Query string (e.g., "status:RUNNING", "workflowType:my_workflow") +- `freeText`: Free text search term (use "*" for all) +- `orderBy`: Sort field and direction (e.g., "startTime:DESC", "status:ASC") + ### Monitoring & Debugging Tasks The `TaskClient` provides capabilities for monitoring and debugging tasks within your workflow executions: @@ -1400,7 +1425,7 @@ const taskDef = taskDefinition({ }); ``` -#### Register Task Definition +### Register Task Definition ```typescript const taskDef = { @@ -1435,7 +1460,7 @@ const taskDef = { await metadataClient.registerTask(taskDef); ``` -#### Update Task Definition +### Update Task Definition ```typescript const updatedTaskDef = { @@ -1447,13 +1472,13 @@ const updatedTaskDef = { await metadataClient.updateTask(updatedTaskDef); ``` -#### Unregister Task Definition +### Unregister Task Definition ```typescript await metadataClient.unregisterTask("process_order"); ``` -#### Register Workflow Definition +### Register Workflow Definition ```typescript const workflowDef = { @@ -1510,7 +1535,7 @@ const workflowDef = { await metadataClient.registerWorkflowDef(workflowDef); ``` -#### Unregister Workflow Definition +### Unregister Workflow Definition ```typescript await metadataClient.unregisterWorkflow("order_processing_workflow", 1); From af46bdef075917fcaebb924b3f5c6580c2d5c63b Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Thu, 2 Oct 2025 18:32:02 +0300 Subject: [PATCH 18/60] Update README.md --- README.md | 308 ------------------------------------------------------ 1 file changed, 308 deletions(-) diff --git a/README.md b/README.md index e73527d3..2ed9c655 100644 --- a/README.md +++ b/README.md @@ -1994,311 +1994,3 @@ const manager = new TaskManager(client, workers, { logger: new CustomLogger() }); ``` - -## Best Practices - -### Worker Design - -Follow the [Worker Design Principles](#worker-design-principles) outlined in the Workers section: -- Keep workers **stateless** and avoid maintaining workflow-specific state -- Design workers to be **idempotent** to handle task rescheduling -- Ensure each worker is **specific** to one task type with well-defined inputs/outputs -- Let Conductor handle retries; workers should focus on task execution - -### Workflow Design - -1. **Clear Naming**: Use descriptive names for workflows and tasks -2. **Versioning**: Always version your workflows -3. **Input Validation**: Validate workflow inputs -4. **Error Handling**: Include proper error handling in workflows - -### Performance - -1. **Polling Intervals**: Adjust polling intervals based on your workload -2. **Concurrency**: Set appropriate concurrency levels for workers -3. **Batch Polling**: Use batch polling for better performance -4. **Connection Pooling**: Configure HTTP connection pooling with `maxHttp2Connections` - -### Security - -1. **Environment Variables**: Use environment variables for sensitive configuration -2. **Access Control**: Implement proper access control -3. **Input Validation**: Validate all inputs to prevent injection attacks -4. **Secure Communication**: Use HTTPS for all communications - -## API Reference - -### Core Classes - -- `WorkflowExecutor`: Main class for workflow management -- `TaskManager`: Manages multiple workers -- `TaskRunner`: Handles individual worker execution -- `TaskClient`: Additional task management capabilities -- `SchedulerClient`: Manages workflow scheduling -- `ServiceRegistryClient`: Manages service registrations and circuit breakers -- `HumanExecutor`: Comprehensive human task management -- `TemplateClient`: Manages human task templates -- `MetadataClient`: Manages task and workflow definitions -- `ConductorWorker`: Interface for defining workers - -### Task Generators - -- `simpleTask()`: Creates simple tasks -- `httpTask()`: Creates HTTP tasks -- `switchTask()`: Creates conditional tasks -- `forkJoinTask()`: Creates parallel execution tasks -- `doWhileTask()`: Creates loop tasks -- `subWorkflowTask()`: Creates sub-workflow tasks -- `eventTask()`: Creates event-driven tasks -- `waitTask()`: Creates wait tasks -- `terminateTask()`: Creates termination tasks -- `setVariableTask()`: Creates variable setting tasks -- `jsonJqTask()`: Creates JSON transformation tasks -- `kafkaPublishTask()`: Creates Kafka publishing tasks -- `inlineTask()`: Creates inline script tasks -- `dynamicForkTask()`: Creates dynamic fork tasks -- `joinTask()`: Creates join tasks -- `humanTask()`: Creates human tasks - -### Factory Functions - -- `workflow()`: Creates workflow definitions -- `taskDefinition()`: Creates task definitions - -### Configuration Options - -#### OrkesApiConfig -- `serverUrl`: Conductor server URL -- `keyId`: Authentication key ID -- `keySecret`: Authentication key secret -- `refreshTokenInterval`: Token refresh interval in milliseconds (0 = no refresh, default: 30 minutes) -- `maxHttp2Connections`: Maximum HTTP2 connections (default: 1) - -#### TaskManagerOptions -- `pollInterval`: Polling interval in milliseconds -- `concurrency`: Number of concurrent task executions -- `workerID`: Unique worker identifier -- `domain`: Task domain -- `batchPollingTimeout`: Batch polling timeout - -#### Environment Variables -- `CONDUCTOR_SERVER_URL`: Server URL -- `CONDUCTOR_AUTH_KEY`: Authentication key -- `CONDUCTOR_AUTH_SECRET`: Authentication secret -- `CONDUCTOR_MAX_HTTP2_CONNECTIONS`: Maximum HTTP2 connections - -## Examples - -### Complete Example: Order Processing Workflow - -```typescript -import { - orkesConductorClient, - WorkflowExecutor, - TaskManager, - SchedulerClient, - workflow, - simpleTask, - switchTask, - forkJoinTask, - httpTask -} from "@io-orkes/conductor-javascript"; - -async function setupOrderProcessing() { - // Create client - const client = await orkesConductorClient({ - serverUrl: "https://play.orkes.io/api", - keyId: "your-key-id", - keySecret: "your-key-secret" - }); - - // Create workflow executor - const executor = new WorkflowExecutor(client); - const scheduler = new SchedulerClient(client); - - // Define order processing workflow - const orderWorkflow = workflow("order_processing", [ - // Validate order - simpleTask("validate_order", "order_validation", {}), - - // Check inventory - httpTask("check_inventory", "https://inventory-api.com/check", { - method: "POST" - }), - - // Process payment based on payment method - switchTask("process_payment", "input.paymentMethod", { - "credit_card": [ - httpTask("charge_card", "https://payment-api.com/charge", { - method: "POST" - }) - ], - "paypal": [ - httpTask("paypal_payment", "https://paypal-api.com/pay", { - method: "POST" - }) - ], - "default": [ - simpleTask("manual_payment", "manual_payment_processing", {}) - ] - }), - - // Parallel fulfillment tasks - forkJoinTask("fulfillment", [ - [httpTask("update_inventory", "https://inventory-api.com/update", { - method: "POST" - })], - [simpleTask("send_notification", "email_notification", {})], - [simpleTask("create_shipment", "shipping_task", {})] - ]), - - // Final confirmation - simpleTask("confirm_order", "order_confirmation", {}) - ]); - - // Register workflow - await executor.registerWorkflow(true, orderWorkflow); - - // Schedule daily order processing - await scheduler.saveSchedule({ - name: "daily_order_processing", - cronExpression: "0 0 2 * * ?", // Every day at 2 AM - startWorkflowRequest: { - name: "order_processing", - version: 1, - input: { batchProcessing: true } - } - }); - - // Define workers - const workers = [ - { - taskDefName: "order_validation", - execute: async (task) => { - const order = task.inputData; - // Validate order logic - return { - outputData: { valid: true, orderId: order.id }, - status: "COMPLETED" - }; - } - }, - { - taskDefName: "email_notification", - execute: async (task) => { - // Send email logic - return { - outputData: { sent: true }, - status: "COMPLETED" - }; - } - }, - { - taskDefName: "shipping_task", - execute: async (task) => { - // Create shipping label logic - return { - outputData: { trackingNumber: "TRK123456" }, - status: "COMPLETED" - }; - } - } - ]; - - // Create task manager - const manager = new TaskManager(client, workers, { - options: { - pollInterval: 1000, - concurrency: 2, - workerID: "order-processor", - domain: "production" - } - }); - - // Start workers - manager.startPolling(); - - // Start workflow execution - const executionId = await executor.startWorkflow({ - name: "order_processing", - version: 1, - input: { - orderId: "ORD-123", - customerId: "CUST-456", - paymentMethod: "credit_card", - items: [{ id: "ITEM-1", quantity: 2 }] - } - }); - - console.log(`Order processing started: ${executionId}`); - - return { executor, manager, scheduler, executionId }; -} -``` - -## Troubleshooting - -### Common Issues - -#### Authentication Errors - -```typescript -// Check your credentials -const client = await orkesConductorClient({ - serverUrl: "https://play.orkes.io/api", - keyId: "your-key-id", // Make sure this is correct - keySecret: "your-key-secret" // Make sure this is correct -}); -``` - -#### Worker Not Receiving Tasks - -1. Check if the worker is registered with the correct task definition name -2. Verify the task manager is polling -3. Check the Conductor server logs for errors -4. Verify the domain configuration matches - -#### Workflow Execution Issues - -1. Verify the workflow is registered -2. Check workflow input parameters -3. Review task dependencies -4. Check scheduler status if using scheduled workflows - -#### Service Registry Issues - -1. Verify service is properly registered -2. Check circuit breaker status -3. Ensure service URI is accessible -4. Verify proto files for gRPC services - -### Debug Mode - -Enable debug logging to troubleshoot issues: - -```typescript -import { DefaultLogger } from "@io-orkes/conductor-javascript"; - -const logger = new DefaultLogger(); -logger.debug("Debug message", { data: "value" }); -``` - -### Health Checks - -```typescript -// Check if the Conductor server is healthy -const health = await client.healthCheckResource.healthCheck(); -console.log("Server health:", health); - -// Check scheduler status -const schedules = await scheduler.getAllSchedules(); -console.log("Active schedules:", schedules.length); - -// Check service registry -const services = await serviceRegistry.getRegisteredServices(); -console.log("Registered services:", services.length); -``` - ---- - -For more examples and detailed API documentation, visit the [GitHub repository](https://github.com/conductor-sdk/conductor-javascript) or the [Orkes Conductor documentation](https://orkes.io/content/docs/). \ No newline at end of file From 66d6c0336574158ddb040221bccc262ea9fb70d2 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Thu, 2 Oct 2025 18:49:39 +0300 Subject: [PATCH 19/60] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2ed9c655..6f76d7df 100644 --- a/README.md +++ b/README.md @@ -262,7 +262,7 @@ System tasks are fully managed by Conductor. No custom workers needed - just ref ### SIMPLE Tasks - Require Custom Workers -SIMPLE tasks execute **your custom business logic**. You must implement and deploy workers to handle these tasks. +SIMPLE tasks execute **your custom business logic**. You must implement and workers to handle these tasks. **When to use:** - Custom business logic specific to your application From 4d0f21aa246914dd5fe885a05f00b43d5177cff5 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Thu, 2 Oct 2025 19:04:27 +0300 Subject: [PATCH 20/60] Update README.md --- README.md | 87 +------------------------------------------------------ 1 file changed, 1 insertion(+), 86 deletions(-) diff --git a/README.md b/README.md index 6f76d7df..5af767e6 100644 --- a/README.md +++ b/README.md @@ -262,7 +262,7 @@ System tasks are fully managed by Conductor. No custom workers needed - just ref ### SIMPLE Tasks - Require Custom Workers -SIMPLE tasks execute **your custom business logic**. You must implement and workers to handle these tasks. +SIMPLE tasks execute **your custom business logic**. You must implement workers to handle these tasks. **When to use:** - Custom business logic specific to your application @@ -1909,88 +1909,3 @@ const uiTemplate = { await templateClient.registerTemplate(uiTemplate); ``` - -## Error Handling - -### Worker Error Handling - -```typescript -const worker: ConductorWorker = { - taskDefName: "error_prone_task", - execute: async (task) => { - try { - const result = await riskyOperation(task.inputData); - return { - outputData: result, - status: "COMPLETED" - }; - } catch (error) { - return { - outputData: {}, - status: "FAILED", - reasonForIncompletion: error.message - }; - } - } -}; -``` - -### Task Manager Error Handling - -```typescript -const manager = new TaskManager(client, workers, { - onError: (error, task) => { - console.error(`Error processing task ${task.taskId}:`, error); - // Custom error handling logic - }, - maxRetries: 3 -}); -``` - -### Workflow Error Handling - -```typescript -try { - const executionId = await executor.startWorkflow({ - name: "workflow_name", - version: 1, - input: {} - }); -} catch (error) { - console.error("Failed to start workflow:", error); -} -``` - -## Logging - -### Default Logger - -```typescript -import { DefaultLogger } from "@io-orkes/conductor-javascript"; - -const logger = new DefaultLogger(); -``` - -### Custom Logger - -```typescript -import { ConductorLogger } from "@io-orkes/conductor-javascript"; - -class CustomLogger implements ConductorLogger { - info(message: string, ...args: any[]): void { - console.log(`[INFO] ${message}`, ...args); - } - - error(message: string, ...args: any[]): void { - console.error(`[ERROR] ${message}`, ...args); - } - - debug(message: string, ...args: any[]): void { - console.debug(`[DEBUG] ${message}`, ...args); - } -} - -const manager = new TaskManager(client, workers, { - logger: new CustomLogger() -}); -``` From 477cf419dc192985389092eeb584049645e9ff38 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Thu, 2 Oct 2025 19:05:04 +0300 Subject: [PATCH 21/60] Update README.md --- README.md | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/README.md b/README.md index 5af767e6..e55d5682 100644 --- a/README.md +++ b/README.md @@ -97,29 +97,6 @@ Show support for the Conductor OSS. Please help spread the awareness by starrin - [TemplateClient](#templateclient) - [Register Form Template](#register-form-template) - [Register UI Template](#register-ui-template) -- [Error Handling](#error-handling) - - [Worker Error Handling](#worker-error-handling) - - [Task Manager Error Handling](#task-manager-error-handling) - - [Workflow Error Handling](#workflow-error-handling) -- [Logging](#logging) - - [Default Logger](#default-logger) - - [Custom Logger](#custom-logger) -- [Best Practices](#best-practices) - - [Worker Design](#worker-design) - - [Workflow Design](#workflow-design) - - [Performance](#performance) - - [Security](#security) -- [API Reference](#api-reference) - - [Core Classes](#core-classes) - - [Task Generators](#task-generators) - - [Factory Functions](#factory-functions) - - [Configuration Options](#configuration-options-2) -- [Examples](#examples) - - [Complete Example: Order Processing Workflow](#complete-example-order-processing-workflow) -- [Troubleshooting](#troubleshooting) - - [Common Issues](#common-issues) - - [Debug Mode](#debug-mode) - - [Health Checks](#health-checks) ## Installation From ba64b4ee681f3ca3a09a330e03d194751ad1d127 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Thu, 2 Oct 2025 19:09:30 +0300 Subject: [PATCH 22/60] Update README.md --- README.md | 88 +++---------------------------------------------------- 1 file changed, 4 insertions(+), 84 deletions(-) diff --git a/README.md b/README.md index e55d5682..566ddc9f 100644 --- a/README.md +++ b/README.md @@ -71,15 +71,11 @@ Show support for the Conductor OSS. Please help spread the awareness by starrin - [Worker Design Principles](#worker-design-principles) - [Handling Task Results](#handling-task-results) - [Working with Multiple Workers](#working-with-multiple-workers) - - [TaskManager (Recommended)](#taskmanager-recommended) + - [TaskManager](#taskmanager) - [Advanced Configuration](#advanced-configuration) - [Dynamic Configuration Updates](#dynamic-configuration-updates) - [Graceful Shutdown](#graceful-shutdown) - - [TaskRunner (Low-level)](#taskrunner-low-level) - [Configuration Options](#configuration-options-1) - - [TaskManager Configuration](#taskmanager-configuration) - - [TaskRunner Configuration](#taskrunner-configuration) - - [When to Use Each Approach](#when-to-use-each-approach) - [Scheduling](#scheduling) - [SchedulerClient](#schedulerclient) - [Service Registry](#service-registry) @@ -767,9 +763,7 @@ Workers are background processes that execute tasks in your workflows. Think of Workflow → Creates Tasks → Workers Poll for Tasks → Execute Logic → Return Results → Workflow Continues ``` -The SDK provides two approaches for managing workers: -- **TaskManager** - Easy-to-use interface for managing multiple workers (⭐ **recommended for most use cases**) -- **TaskRunner** - Low-level interface for fine-grained control of individual workers +The SDK provides the **TaskManager** class - an easy-to-use interface for managing multiple workers efficiently. ### Quick Start: Your First Worker @@ -1055,9 +1049,9 @@ console.log("✅ All 3 workers are now running!"); - A single `TaskManager` manages all workers together - Workers only pick up tasks that match their `taskDefName` -### TaskManager (Recommended) +### TaskManager -`TaskManager` is the high-level interface that manages multiple workers. You've already seen the basic usage above. This section covers advanced configuration and features. +`TaskManager` is the interface that manages multiple workers. You've already seen the basic usage above. This section covers advanced configuration and features. #### Advanced Configuration @@ -1129,54 +1123,8 @@ async function gracefulShutdown() { } ``` -### TaskRunner (Low-level) - -`TaskRunner` is the low-level interface used internally by `TaskManager`. **Most developers should use `TaskManager` instead.** Use `TaskRunner` only if you need: - -- Fine-grained control over a single worker's lifecycle -- Custom polling logic or worker management -- Integration with existing worker management systems - -**Basic Example:** - -```typescript -import { TaskRunner, ConductorWorker } from "@io-orkes/conductor-javascript"; - -const worker: ConductorWorker = { - taskDefName: "specialized_task", - execute: async (task) => { - return { - outputData: { result: "processed" }, - status: "COMPLETED" - }; - } -}; - -const taskRunner = new TaskRunner({ - worker: worker, - taskResource: client.taskResource, // Note: Direct access to taskResource - options: { - pollInterval: 1000, - concurrency: 1, - workerID: "specialized-worker" - } -}); - -await taskRunner.startPolling(); -// ... later -await taskRunner.stopPolling(); -``` - -**Key Differences from TaskManager:** -- Manages only ONE worker (vs TaskManager managing multiple) -- Requires direct `taskResource` access -- No built-in error handling or retry logic -- More manual lifecycle management - ### Configuration Options -#### TaskManager Configuration - | Option | Type | Default | Description | |--------|------|---------|-------------| | `logger` | `ConductorLogger` | - | Custom logger instance for monitoring and debugging | @@ -1204,34 +1152,6 @@ const manager = new TaskManager(client, workers, { }); ``` -#### TaskRunner Configuration - -| Option | Type | Default | Description | -|--------|------|---------|-------------| -| `worker` | `ConductorWorker` | **required** | The worker definition to run | -| `taskResource` | `TaskResourceService` | **required** | Task resource service from client | -| `options.pollInterval` | `number` | `100` | Polling interval in milliseconds | -| `options.concurrency` | `number` | `1` | Max concurrent executions | -| `options.workerID` | `string` | - | Unique worker identifier | -| `options.domain` | `string` | - | Task domain for isolation | -| `logger` | `ConductorLogger` | - | Custom logger instance | - -### When to Use Each Approach - -**Use TaskManager when:** -- ✅ You have multiple workers (most common case) -- ✅ You want simple, high-level worker management -- ✅ You need built-in error handling and retries -- ✅ You're building a standard worker application - -**Use TaskRunner when:** -- 🔧 You need fine-grained control over a single worker -- 🔧 You're implementing custom worker management logic -- 🔧 You're integrating with existing polling/execution frameworks -- 🔧 You need direct access to low-level worker operations - -**💡 Recommendation:** Start with `TaskManager`. Only use `TaskRunner` if you have specific advanced requirements that TaskManager doesn't support. - ## Scheduling ### SchedulerClient From 0f5281082d7f81446d8268bb07b2258023b08eb4 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Thu, 2 Oct 2025 19:10:19 +0300 Subject: [PATCH 23/60] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 566ddc9f..c8652de6 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Conductor OSS JavaScript/TypeScript SDK -A comprehensive TypeScript/JavaScript client for [Netflix Conductor](https://github.com/conductor-oss/conductor) and [Orkes Conductor](https://orkes.io/content), enabling developers to build, orchestrate, and monitor distributed workflows with ease. +A comprehensive TypeScript/JavaScript client for [Conductor OSS](https://github.com/conductor-oss/conductor) and [Orkes Conductor](https://orkes.io/content), enabling developers to build, orchestrate, and monitor distributed workflows with ease. [Conductor](https://www.conductor-oss.org/) is the leading open-source orchestration platform allowing developers to build highly scalable distributed applications. From d0653f5361e6b9c523dc68618a2447633b946dda Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Thu, 2 Oct 2025 19:11:06 +0300 Subject: [PATCH 24/60] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c8652de6..6674e7b7 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Conductor OSS JavaScript/TypeScript SDK -A comprehensive TypeScript/JavaScript client for [Conductor OSS](https://github.com/conductor-oss/conductor) and [Orkes Conductor](https://orkes.io/content), enabling developers to build, orchestrate, and monitor distributed workflows with ease. +A comprehensive TypeScript/JavaScript client for [Conductor OSS](https://github.com/conductor-oss/conductor), enabling developers to build, orchestrate, and monitor distributed workflows with ease. [Conductor](https://www.conductor-oss.org/) is the leading open-source orchestration platform allowing developers to build highly scalable distributed applications. From 544737367578608fb419fcf53aa62d949a5ca0d7 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Fri, 3 Oct 2025 01:25:51 +0300 Subject: [PATCH 25/60] Update README.md --- README.md | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/README.md b/README.md index 6674e7b7..ec471220 100644 --- a/README.md +++ b/README.md @@ -257,7 +257,6 @@ See the [Workers](#workers) section for implementation details. Workflows in Conductor are defined using task generators and managed through the `WorkflowExecutor` class. This section covers: - **Creating workflows** using the workflow factory and task generators - **Managing workflow lifecycle** (register, start, pause, resume, terminate, search) -- **Monitoring and debugging** workflow tasks ### WorkflowExecutor @@ -723,31 +722,6 @@ const inputTasks = await taskClient.search(0, 100, "", "*", "inputData.orderId:o const timeoutTasks = await taskClient.search(0, 100, "endTime:DESC", "*", "status:TIMED_OUT"); ``` -#### Debugging Task Execution - -When debugging task execution issues: - -```typescript -try { - // Get detailed task information - const task = await taskClient.getTask(taskId); - - console.log("Task Status:", task.status); - console.log("Task Input:", task.inputData); - console.log("Task Output:", task.outputData); - console.log("Retry Count:", task.retryCount); - console.log("Execution Time:", task.endTime - task.startTime); - - // Check for failed tasks in a workflow - const failedTasks = await taskClient.search(0, 50, "", "*", "status:FAILED"); - failedTasks.results.forEach(task => { - console.log(`Task ${task.taskId} failed: ${task.reasonForIncompletion}`); - }); -} catch (error) { - console.error("Error debugging tasks:", error); -} -``` - ## Workers ### Overview From 41fddc27a3a4f21504f3f5186e47c8a43b152c79 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Fri, 3 Oct 2025 01:38:41 +0300 Subject: [PATCH 26/60] Update README.md --- README.md | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index ec471220..3595fb54 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,6 @@ Show support for the Conductor OSS. Please help spread the awareness by starrin - [Task Statuses](#task-statuses) - [Searching & Filtering Tasks](#searching--filtering-tasks) - [Common Search Queries](#common-search-queries) - - [Debugging Task Execution](#debugging-task-execution) - [Workers](#workers) - [Overview](#overview) - [Quick Start: Your First Worker](#quick-start-your-first-worker) @@ -71,8 +70,7 @@ Show support for the Conductor OSS. Please help spread the awareness by starrin - [Worker Design Principles](#worker-design-principles) - [Handling Task Results](#handling-task-results) - [Working with Multiple Workers](#working-with-multiple-workers) - - [TaskManager](#taskmanager) - - [Advanced Configuration](#advanced-configuration) + - [TaskManager Advanced Configuration](#taskmanager-advanced-configuration) - [Dynamic Configuration Updates](#dynamic-configuration-updates) - [Graceful Shutdown](#graceful-shutdown) - [Configuration Options](#configuration-options-1) @@ -737,11 +735,11 @@ Workers are background processes that execute tasks in your workflows. Think of Workflow → Creates Tasks → Workers Poll for Tasks → Execute Logic → Return Results → Workflow Continues ``` -The SDK provides the **TaskManager** class - an easy-to-use interface for managing multiple workers efficiently. +The SDK provides the **TaskManager** class - an easy-to-use interface for managing workers efficiently. ### Quick Start: Your First Worker -Here's a complete, simple example to get you started: +Here's a simple example to get you started: ```typescript import { @@ -1023,11 +1021,7 @@ console.log("✅ All 3 workers are now running!"); - A single `TaskManager` manages all workers together - Workers only pick up tasks that match their `taskDefName` -### TaskManager - -`TaskManager` is the interface that manages multiple workers. You've already seen the basic usage above. This section covers advanced configuration and features. - -#### Advanced Configuration +### TaskManager Advanced Configuration ```typescript import { TaskManager, ConductorWorker, DefaultLogger } from "@io-orkes/conductor-javascript"; @@ -1083,18 +1077,6 @@ process.on('SIGTERM', async () => { console.log('Workers stopped gracefully'); process.exit(0); }); - -// Or with timeout -async function gracefulShutdown() { - const timeout = setTimeout(() => { - console.error('Force shutdown after timeout'); - process.exit(1); - }, 30000); // 30 second timeout - - await manager.stopPolling(); - clearTimeout(timeout); - process.exit(0); -} ``` ### Configuration Options From 0e81f3e297d5f69812c433a408c4e87d6175f6b9 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Fri, 3 Oct 2025 01:46:09 +0300 Subject: [PATCH 27/60] Update README.md --- README.md | 42 ++++++------------------------------------ 1 file changed, 6 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 3595fb54..284834da 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,6 @@ Show support for the Conductor OSS. Please help spread the awareness by starrin - [TaskManager Advanced Configuration](#taskmanager-advanced-configuration) - [Dynamic Configuration Updates](#dynamic-configuration-updates) - [Graceful Shutdown](#graceful-shutdown) - - [Configuration Options](#configuration-options-1) - [Scheduling](#scheduling) - [SchedulerClient](#schedulerclient) - [Service Registry](#service-registry) @@ -1032,22 +1031,22 @@ const manager = new TaskManager(client, workers, { // Polling and execution options options: { - pollInterval: 1000, // Poll every 1 second (default: 100ms) - concurrency: 5, // Execute up to 5 tasks concurrently per worker + pollInterval: 1000, // How often to poll for tasks (milliseconds) (default: 100) + concurrency: 5, // Max concurrent task executions per worker (default: 1) workerID: "worker-group-1", // Unique identifier for this worker group domain: "production", // Task domain for isolation (optional) - batchPollingTimeout: 100 // Timeout for batch polling in ms + batchPollingTimeout: 10-0 // Batch polling timeout in milliseconds (default: 100) }, - // Global error handler for all workers + // Global error handler called when workers fail onError: (error, task) => { console.error(`Error in task ${task?.taskType}:`, error); // Send to error tracking service errorTracker.log(error, { taskId: task?.taskId }); }, - // Maximum retry attempts before giving up - maxRetries: 3 + // Maximum retry attempts before giving up (default: 3) + maxRetries: 5 }); await manager.startPolling(); @@ -1079,35 +1078,6 @@ process.on('SIGTERM', async () => { }); ``` -### Configuration Options - -| Option | Type | Default | Description | -|--------|------|---------|-------------| -| `logger` | `ConductorLogger` | - | Custom logger instance for monitoring and debugging | -| `options.pollInterval` | `number` | `100` | How often to poll for tasks (milliseconds) | -| `options.concurrency` | `number` | `1` | Max concurrent task executions per worker | -| `options.workerID` | `string` | - | Unique identifier for this worker group | -| `options.domain` | `string` | - | Task domain for isolation (optional) | -| `options.batchPollingTimeout` | `number` | `100` | Batch polling timeout in milliseconds | -| `onError` | `(error, task?) => void` | - | Global error handler called when workers fail | -| `maxRetries` | `number` | `3` | Max retry attempts for failed operations | - -**Example with all options:** -```typescript -const manager = new TaskManager(client, workers, { - logger: new CustomLogger(), - options: { - pollInterval: 1000, - concurrency: 5, - workerID: "prod-worker-1", - domain: "production", - batchPollingTimeout: 100 - }, - onError: (error, task) => console.error("Error:", error), - maxRetries: 3 -}); -``` - ## Scheduling ### SchedulerClient From f91620c61ef78403049e13264ddfc0d55bda052a Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Fri, 3 Oct 2025 01:56:44 +0300 Subject: [PATCH 28/60] Update README.md --- README.md | 578 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 466 insertions(+), 112 deletions(-) diff --git a/README.md b/README.md index 284834da..d180f686 100644 --- a/README.md +++ b/README.md @@ -158,16 +158,26 @@ The SDK supports authentication using API keys. See [Access Control](https://ork import { OrkesApiConfig, orkesConductorClient } from "@io-orkes/conductor-javascript"; const config: Partial = { - serverUrl: "https://play.orkes.io/api", // server api url - keyId: "your-key-id", // authentication key - keySecret: "your-key-secret", // authentication secret - refreshTokenInterval: 0, // optional: token refresh interval, 0 = no refresh (default: 30min) - maxHttp2Connections: 1 // optional: max HTTP2 connections (default: 1) + serverUrl: "https://play.orkes.io/api", // Required: server api url + keyId: "your-key-id", // Required: authentication key + keySecret: "your-key-secret", // Required: authentication secret + refreshTokenInterval: 1800000, // Optional: token refresh interval in ms (default: 1800000 = 30 minutes) + maxHttp2Connections: 1 // Optional: max HTTP2 connections (default: 1) }; const client = await orkesConductorClient(config); ``` +**Configuration Parameters:** + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `serverUrl` | string | Yes | - | Conductor server API URL | +| `keyId` | string | No* | - | Authentication key ID (*required for auth) | +| `keySecret` | string | No* | - | Authentication secret (*required for auth) | +| `refreshTokenInterval` | number | No | 1800000 | Token refresh interval in milliseconds (30 minutes) | +| `maxHttp2Connections` | number | No | 1 | Maximum simultaneous HTTP2 connections to Conductor server | + ### Environment Variables You can configure authentication using environment variables: @@ -269,17 +279,43 @@ const executor = new WorkflowExecutor(client); #### Workflow Factory -The `workflow` function provides a convenient way to create workflow definitions: +The `workflow` function provides a convenient way to create workflow definitions with sensible defaults: ```typescript import { workflow, simpleTask } from "@io-orkes/conductor-javascript"; -const myWorkflow = workflow("workflow_name", [ - simpleTask("task1", "process_1", {}), - simpleTask("task2", "process_2", {}) -]); +const myWorkflow = workflow( + "workflow_name", // name (required): workflow name + [ // tasks (required): array of task definitions + simpleTask("task1", "process_1", {}), + simpleTask("task2", "process_2", {}) + ] +); + +// Returns a WorkflowDef with these default values: +// { +// name: "workflow_name", +// version: 1, // Default: 1 +// tasks: [...], +// inputParameters: [], // Default: [] +// timeoutSeconds: 0 // Default: 0 (no timeout) +// } ``` +**Workflow Factory Parameters:** + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `name` | string | Yes | - | Workflow name (must be unique) | +| `tasks` | TaskDefTypes[] | Yes | - | Array of task definitions | + +**Default Values Set by Factory:** +- `version`: 1 +- `inputParameters`: [] +- `timeoutSeconds`: 0 (no timeout) + +**Note:** For more control over workflow definition, you can manually create a `WorkflowDef` object with additional properties like `ownerEmail`, `description`, `outputParameters`, `failureWorkflow`, `restartable`, etc. + #### Example: Combining Task Types Here's how to combine different task types in a workflow: @@ -288,12 +324,19 @@ Here's how to combine different task types in a workflow: import { workflow, simpleTask, httpTask } from "@io-orkes/conductor-javascript"; const myWorkflow = workflow("order_processing", [ - simpleTask("validate_order", "validate_order_task", {}), // Custom worker - httpTask("call_payment", "https://api.payment.com/charge", { // System task + // SIMPLE task - requires custom worker + simpleTask("validate_order", "validate_order_task", {}), + + // HTTP task - system task (no worker needed) + httpTask("call_payment", { + uri: "https://api.payment.com/charge", method: "POST", - headers: { "Authorization": "Bearer token" } + headers: { "Authorization": "Bearer token" }, + body: { amount: "${workflow.input.amount}" } }), - simpleTask("send_confirmation", "send_email_task", {}) // Custom worker + + // SIMPLE task - requires custom worker + simpleTask("send_confirmation", "send_email_task", {}) ]); ``` @@ -310,9 +353,14 @@ This section provides code examples for each task type generator. Use these to b ```typescript import { simpleTask } from "@io-orkes/conductor-javascript"; -const task = simpleTask("task_ref", "task_name", { - inputParam: "value" -}, false); // optional: if true, workflow continues even if task fails +const task = simpleTask( + "task_ref", // taskReferenceName (required) + "task_name", // name (required): must match worker's taskDefName + { // inputParameters (required) + inputParam: "value" + }, + false // optional (optional): if true, workflow continues on failure +); ``` #### HTTP Task @@ -322,12 +370,18 @@ const task = simpleTask("task_ref", "task_name", { ```typescript import { httpTask } from "@io-orkes/conductor-javascript"; -const task = httpTask("http_ref", "http://api.example.com/data", { - method: "GET", - headers: { "Authorization": "Bearer token" }, - connectionTimeOut: 5000, - readTimeOut: 10000 -}); +const task = httpTask( + "http_ref", + { + uri: "http://api.example.com/data", + method: "GET", + headers: { "Authorization": "Bearer token" }, + connectionTimeOut: 5000, + readTimeOut: 10000 + }, + false, // asyncComplete (optional) + false // optional (optional): workflow continues on failure +); ``` #### Switch Task @@ -337,11 +391,16 @@ const task = httpTask("http_ref", "http://api.example.com/data", { ```typescript import { switchTask } from "@io-orkes/conductor-javascript"; -const task = switchTask("switch_ref", "input.status", { - "active": [simpleTask("active_task", "process_active", {})], - "inactive": [simpleTask("inactive_task", "process_inactive", {})], - "default": [simpleTask("default_task", "process_default", {})] -}); +const task = switchTask( + "switch_ref", + "input.status", // expression to evaluate + { + "active": [simpleTask("active_task", "process_active", {})], + "inactive": [simpleTask("inactive_task", "process_inactive", {})] + }, + [simpleTask("default_task", "process_default", {})], // defaultCase (optional) + false // optional (optional): workflow continues on failure +); ``` #### Fork-Join Task @@ -383,9 +442,15 @@ const task = doWhileTask("while_ref", "workflow.variables.counter < 10", [ ```typescript import { subWorkflowTask } from "@io-orkes/conductor-javascript"; -const task = subWorkflowTask("sub_ref", "child_workflow", 1, { - inputParam: "value" -}, "COMPLETED"); // wait for completion status +const task = subWorkflowTask( + "sub_ref", + "child_workflow", // workflowName + 1, // version (optional): uses latest if not specified + false // optional (optional) +); + +// Set input parameters +task.inputParameters = { inputParam: "value" }; ``` #### Event Task @@ -403,12 +468,24 @@ const task = eventTask("event_ref", "event_name", { #### Wait Task -*System Task* - Pauses workflow execution for a specified duration. +*System Task* - Pauses workflow execution for a specified duration or until a specific time. ```typescript -import { waitTask } from "@io-orkes/conductor-javascript"; +import { waitTaskDuration, waitTaskUntil } from "@io-orkes/conductor-javascript"; + +// Wait for a duration (e.g., "30s", "5m", "1h", "2d") +const taskDuration = waitTaskDuration( + "wait_ref", + "30s", // duration string + false // optional (optional) +); -const task = waitTask("wait_ref", 30); // wait 30 seconds +// Wait until a specific time (ISO 8601 format) +const taskUntil = waitTaskUntil( + "wait_until_ref", + "2025-12-31T23:59:59Z", // ISO 8601 timestamp + false // optional (optional) +); ``` #### Terminate Task @@ -418,7 +495,11 @@ const task = waitTask("wait_ref", 30); // wait 30 seconds ```typescript import { terminateTask } from "@io-orkes/conductor-javascript"; -const task = terminateTask("terminate_ref", "FAILED", "Error message"); +const task = terminateTask( + "terminate_ref", + "FAILED", // status: "COMPLETED" or "FAILED" + "Error message" // terminationReason (optional) +); ``` #### Set Variable Task @@ -619,20 +700,175 @@ await executor.terminate(executionId, "Terminating due to error"); ```typescript const searchResults = await executor.search( - 0, // start - 10, // size - "status:RUNNING", // query - "*", // freeText - "startTime:DESC" // sort (optional) + 0, // start: starting index + 10, // size: number of results + "status:RUNNING", // query: e.g., "workflowType:my_workflow" + "*", // freeText: use "*" for all + "startTime:DESC", // sort (optional, default: "") + false // skipCache (optional, default: false) ); ``` -**Search Parameters:** -- `start`: Starting index for pagination (default: 0) -- `size`: Number of results to return (default: 100) -- `sort`: Query string (e.g., "status:RUNNING", "workflowType:my_workflow") -- `freeText`: Free text search term (use "*" for all) -- `orderBy`: Sort field and direction (e.g., "startTime:DESC", "status:ASC") +### WorkflowExecutor Complete API Reference + +The `WorkflowExecutor` provides the following methods for workflow management: + +#### Core Workflow Operations + +```typescript +// Register a workflow definition +await executor.registerWorkflow( + override: boolean, // If true, replaces existing definition + workflow: WorkflowDef +): Promise + +// Start a workflow +const workflowId = await executor.startWorkflow( + workflowRequest: StartWorkflowRequest +): Promise // Returns workflow instance ID + +// Execute workflow synchronously +const result = await executor.executeWorkflow( + workflowRequest: StartWorkflowRequest, + name: string, + version: number, + requestId: string, + waitUntilTaskRef?: string, // (optional) wait until specific task completes + waitForSeconds?: number, // (optional) max wait time + consistency?: Consistency, // (optional) + returnStrategy?: ReturnStrategy // (optional) +): Promise + +// Get workflow execution details +const workflow = await executor.getWorkflow( + workflowInstanceId: string, + includeTasks: boolean, + retry?: number // (optional, default: 0) +): Promise + +// Get workflow execution (alias for getWorkflow) +const execution = await executor.getExecution( + workflowInstanceId: string, + includeTasks?: boolean // (optional, default: true) +): Promise + +// Get workflow status summary +const status = await executor.getWorkflowStatus( + workflowInstanceId: string, + includeOutput: boolean, + includeVariables: boolean +): Promise +``` + +#### Workflow Control Operations + +```typescript +// Pause a running workflow +await executor.pause(workflowInstanceId: string): Promise + +// Resume a paused workflow +await executor.resume(workflowInstanceId: string): Promise + +// Terminate a workflow +await executor.terminate( + workflowInstanceId: string, + reason: string +): Promise + +// Restart a workflow +await executor.restart( + workflowInstanceId: string, + useLatestDefinitions: boolean +): Promise + +// Retry a failed workflow from last failed task +await executor.retry( + workflowInstanceId: string, + resumeSubworkflowTasks: boolean +): Promise + +// Rerun a workflow with new parameters +const newWorkflowId = await executor.reRun( + workflowInstanceId: string, + rerunWorkflowRequest?: Partial // (optional) +): Promise + +// Skip a task in a running workflow +await executor.skipTasksFromWorkflow( + workflowInstanceId: string, + taskReferenceName: string, + skipTaskRequest: Partial +): Promise +``` + +#### Task Operations + +```typescript +// Get task by ID +const task = await executor.getTask(taskId: string): Promise + +// Update task by ID +const result = await executor.updateTask( + taskId: string, + workflowInstanceId: string, + taskStatus: TaskResultStatus, // COMPLETED, FAILED, etc. + outputData: Record +): Promise + +// Update task by reference name +const result = await executor.updateTaskByRefName( + taskReferenceName: string, + workflowInstanceId: string, + status: TaskResultStatus, + taskOutput: Record +): Promise + +// Update task synchronously and return workflow +const workflow = await executor.updateTaskSync( + taskReferenceName: string, + workflowInstanceId: string, + status: TaskResultStatusEnum, + taskOutput: Record, + workerId?: string // (optional) +): Promise + +// Signal a workflow task +const response = await executor.signal( + workflowInstanceId: string, + status: TaskResultStatusEnum, + taskOutput: Record, + returnStrategy?: ReturnStrategy // (optional, default: TARGET_WORKFLOW) +): Promise + +// Signal a workflow task asynchronously (fire-and-forget) +await executor.signalAsync( + workflowInstanceId: string, + status: TaskResultStatusEnum, + taskOutput: Record +): Promise +``` + +#### Advanced Operations + +```typescript +// Go back to a specific task and rerun from there +await executor.goBackToTask( + workflowInstanceId: string, + taskFinderPredicate: (task: Task) => boolean, + rerunWorkflowRequestOverrides?: Partial // (optional) +): Promise + +// Go back to first task matching type +await executor.goBackToFirstTaskMatchingType( + workflowInstanceId: string, + taskType: string +): Promise + +// Start multiple workflows +const workflowIds = executor.startWorkflows( + workflowsRequest: StartWorkflowRequest[] +): Promise[] +``` ### Monitoring & Debugging Tasks @@ -1022,36 +1258,51 @@ console.log("✅ All 3 workers are now running!"); ### TaskManager Advanced Configuration +The `TaskManager` accepts configuration options to control worker behavior, polling, and error handling. + ```typescript import { TaskManager, ConductorWorker, DefaultLogger } from "@io-orkes/conductor-javascript"; const manager = new TaskManager(client, workers, { - // Custom logger for debugging and monitoring + // Custom logger for debugging and monitoring (optional) logger: new DefaultLogger(), - // Polling and execution options + // Polling and execution options (optional) options: { - pollInterval: 1000, // How often to poll for tasks (milliseconds) (default: 100) + pollInterval: 1000, // How often to poll for tasks in ms (default: 100) concurrency: 5, // Max concurrent task executions per worker (default: 1) - workerID: "worker-group-1", // Unique identifier for this worker group - domain: "production", // Task domain for isolation (optional) - batchPollingTimeout: 10-0 // Batch polling timeout in milliseconds (default: 100) + workerID: "worker-group-1", // Unique identifier for this worker group (default: hostname) + domain: undefined, // Task domain for isolation (default: undefined) + batchPollingTimeout: 100 // Batch polling timeout in ms (default: 100) }, - // Global error handler called when workers fail + // Global error handler called when workers fail (optional) onError: (error, task) => { console.error(`Error in task ${task?.taskType}:`, error); // Send to error tracking service errorTracker.log(error, { taskId: task?.taskId }); }, - // Maximum retry attempts before giving up (default: 3) + // Maximum retry attempts before giving up (optional, default: 3) maxRetries: 5 }); await manager.startPolling(); ``` +**Configuration Parameters:** + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `logger` | ConductorLogger | No | DefaultLogger | Logger instance for debugging and monitoring | +| `options.pollInterval` | number | No | 100 | Polling interval in milliseconds | +| `options.concurrency` | number | No | 1 | Maximum concurrent task executions per worker | +| `options.workerID` | string | No | hostname | Unique identifier for this worker group | +| `options.domain` | string | No | undefined | Task domain for isolation | +| `options.batchPollingTimeout` | number | No | 100 | Batch polling timeout in milliseconds | +| `onError` | TaskErrorHandler | No | noop | Global error handler function | +| `maxRetries` | number | No | 3 | Maximum retry attempts before giving up | + #### Dynamic Configuration Updates You can update polling options at runtime without stopping workers: @@ -1082,59 +1333,109 @@ process.on('SIGTERM', async () => { ### SchedulerClient -The `SchedulerClient` manages workflow scheduling: +The `SchedulerClient` manages workflow scheduling and provides methods for creating, managing, and monitoring scheduled workflows. ```typescript import { SchedulerClient } from "@io-orkes/conductor-javascript"; const scheduler = new SchedulerClient(client); +``` + +#### Schedule Management -// Create a schedule +```typescript +// Create or update a schedule await scheduler.saveSchedule({ - name: "daily_report", - cronExpression: "0 0 9 * * ?", // Every day at 9 AM + name: string, + cronExpression: string, // e.g., "0 0 9 * * ?" startWorkflowRequest: { - name: "report_workflow", - version: 1, - input: { reportType: "daily" } - } -}); + name: string, + version?: number, + input?: Record, + correlationId?: string, + priority?: number, + taskToDomain?: Record + }, + paused?: boolean, // (optional, default: false) + runCatchupScheduleInstances?: boolean, // (optional, default: false) + scheduleStartTime?: number, // (optional) epoch ms + scheduleEndTime?: number // (optional) epoch ms +}): Promise -// Get schedule -const schedule = await scheduler.getSchedule("daily_report"); +// Get a specific schedule +const schedule = await scheduler.getSchedule(name: string): Promise -// Pause schedule -await scheduler.pauseSchedule("daily_report"); +// Get all schedules +const schedules = await scheduler.getAllSchedules( + workflowName?: string // (optional) filter by workflow name +): Promise -// Resume schedule -await scheduler.resumeSchedule("daily_report"); +// Delete a schedule +await scheduler.deleteSchedule(name: string): Promise -// Delete schedule -await scheduler.deleteSchedule("daily_report"); +// Pause a schedule +await scheduler.pauseSchedule(name: string): Promise -// Get all schedules -const allSchedules = await scheduler.getAllSchedules(); +// Resume a paused schedule +await scheduler.resumeSchedule(name: string): Promise +``` -// Get next few execution times +#### Bulk Schedule Operations + +```typescript +// Pause all schedules (use with caution) +await scheduler.pauseAllSchedules(): Promise + +// Resume all schedules +await scheduler.resumeAllSchedules(): Promise + +// Requeue all execution records +await scheduler.requeueAllExecutionRecords(): Promise +``` + +#### Schedule Execution Preview + +```typescript +// Get next few execution times for a cron expression const nextExecutions = await scheduler.getNextFewSchedules( + cronExpression: string, + scheduleTime: number, // epoch ms + scheduleEndTime: number, // epoch ms + limit: number +): Promise // array of timestamps + +// Example: Get next 5 executions over the next 7 days +const nextTimes = await scheduler.getNextFewSchedules( "0 0 9 * * ?", Date.now(), - Date.now() + 7 * 24 * 60 * 60 * 1000, // Next 7 days + Date.now() + 7 * 24 * 60 * 60 * 1000, 5 ); +``` -// Search schedule executions -const executions = await scheduler.search(0, 10, "", "", "status:RUNNING"); - -// Pause all schedules (debugging only) -await scheduler.pauseAllSchedules(); - -// Resume all schedules -await scheduler.resumeAllSchedules(); +#### Search Schedule Executions -// Requeue all execution records -await scheduler.requeueAllExecutionRecords(); -``` +```typescript +// Search schedule execution history +const executions = await scheduler.search( + start: number, + size: number, + sort: string, // (optional, default: "") + freeText: string, // (default: "*") + query: string // e.g., "status:RUNNING" +): Promise + +// Example +const results = await scheduler.search(0, 10, "startTime:DESC", "*", "status:RUNNING"); +``` + +**Cron Expression Format:** +- Standard cron format: `second minute hour day month dayOfWeek` +- Examples: + - `"0 0 9 * * ?"` - Every day at 9 AM + - `"0 */30 * * * ?"` - Every 30 minutes + - `"0 0 0 1 * ?"` - First day of every month at midnight + - `"0 0 12 ? * MON-FRI"` - Weekdays at noon ## Service Registry @@ -1205,7 +1506,7 @@ await serviceRegistry.removeService("user-service"); ### MetadataClient -The `MetadataClient` class provides methods for managing task and workflow definitions: +The `MetadataClient` class provides methods for managing task and workflow definitions in Conductor. ```typescript import { MetadataClient } from "@io-orkes/conductor-javascript"; @@ -1213,41 +1514,94 @@ import { MetadataClient } from "@io-orkes/conductor-javascript"; const metadataClient = new MetadataClient(client); ``` +#### Complete MetadataClient API Reference + +```typescript +// Task Definition Management +await metadataClient.registerTask(taskDef: TaskDef): Promise +await metadataClient.updateTask(taskDef: TaskDef): Promise +const taskDef = await metadataClient.getTaskDef(taskName: string): Promise +const allTasks = await metadataClient.getAllTaskDefs(): Promise +await metadataClient.unregisterTask(taskName: string): Promise + +// Workflow Definition Management +await metadataClient.registerWorkflowDef( + workflowDef: WorkflowDef, + overwrite?: boolean // (optional, default: false) +): Promise + +await metadataClient.updateWorkflowDef(workflowDef: WorkflowDef): Promise + +const workflowDef = await metadataClient.getWorkflowDef( + workflowName: string, + version?: number // (optional) uses latest if not specified +): Promise + +const allVersions = await metadataClient.getAllWorkflowDefs( + workflowName: string +): Promise + +await metadataClient.unregisterWorkflow( + workflowName: string, + version: number +): Promise +``` + ### Task Definition Factory -The `taskDefinition` function provides a convenient way to create task definitions: +The `taskDefinition` function provides a convenient way to create task definitions with default values: ```typescript import { taskDefinition } from "@io-orkes/conductor-javascript"; const taskDef = taskDefinition({ - name: "task_name", - timeoutSeconds: 300, - retryCount: 3, - retryDelaySeconds: 60, - responseTimeoutSeconds: 300, - pollTimeoutSeconds: 300, - pollIntervalSeconds: 30, - concurrentExecLimit: 10, - rateLimitPerFrequency: 100, - rateLimitFrequencyInSeconds: 60, - ownerEmail: "owner@example.com", - description: "Task description", - inputTemplate: { - param1: "default_value" - }, - outputTemplate: { - result: "computed_value" - }, - inputKeys: ["param1", "param2"], - outputKeys: ["result"], - tags: ["tag1", "tag2"], - executionNameSpace: "namespace", - isolationGroupId: "isolation_group", - maxConcurrentExecutions: 5 + // Required fields + name: "task_name", // Task name (required) + + // Optional fields with defaults + ownerApp: "", // Optional: owner application (default: "") + description: "", // Optional: task description (default: "") + retryCount: 3, // Optional: number of retries (default: 3) + timeoutSeconds: 3600, // Optional: task timeout in seconds (default: 3600 = 1 hour) + inputKeys: [], // Optional: list of input keys (default: []) + outputKeys: [], // Optional: list of output keys (default: []) + timeoutPolicy: "TIME_OUT_WF", // Optional: "RETRY" | "TIME_OUT_WF" | "ALERT_ONLY" (default: "TIME_OUT_WF") + retryLogic: "FIXED", // Optional: "FIXED" | "EXPONENTIAL_BACKOFF" | "LINEAR_BACKOFF" (default: "FIXED") + retryDelaySeconds: 60, // Optional: delay between retries in seconds (default: 60) + responseTimeoutSeconds: 600, // Optional: response timeout in seconds (default: 600) + concurrentExecLimit: 0, // Optional: max concurrent executions (0 = unlimited) (default: 0) + inputTemplate: {}, // Optional: default input template (default: {}) + rateLimitPerFrequency: 0, // Optional: rate limit count (0 = no limit) (default: 0) + rateLimitFrequencyInSeconds: 1, // Optional: rate limit window in seconds (default: 1) + ownerEmail: "", // Optional: owner email (default: "") + pollTimeoutSeconds: 3600, // Optional: poll timeout in seconds (default: 3600) + backoffScaleFactor: 1 // Optional: backoff multiplier for retry (default: 1) }); ``` +**Task Definition Parameters:** + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `name` | string | Yes | - | Task name (must be unique) | +| `ownerApp` | string | No | "" | Owner application name | +| `description` | string | No | "" | Task description | +| `retryCount` | number | No | 3 | Number of retry attempts | +| `timeoutSeconds` | number | No | 3600 | Task timeout in seconds | +| `inputKeys` | string[] | No | [] | List of expected input keys | +| `outputKeys` | string[] | No | [] | List of output keys | +| `timeoutPolicy` | string | No | "TIME_OUT_WF" | Timeout policy: "RETRY", "TIME_OUT_WF", "ALERT_ONLY" | +| `retryLogic` | string | No | "FIXED" | Retry logic: "FIXED", "EXPONENTIAL_BACKOFF", "LINEAR_BACKOFF" | +| `retryDelaySeconds` | number | No | 60 | Delay between retries in seconds | +| `responseTimeoutSeconds` | number | No | 600 | Response timeout in seconds | +| `concurrentExecLimit` | number | No | 0 | Max concurrent executions (0 = unlimited) | +| `inputTemplate` | object | No | {} | Default input template | +| `rateLimitPerFrequency` | number | No | 0 | Rate limit count (0 = no limit) | +| `rateLimitFrequencyInSeconds` | number | No | 1 | Rate limit window in seconds | +| `ownerEmail` | string | No | "" | Owner email address | +| `pollTimeoutSeconds` | number | No | 3600 | Poll timeout in seconds | +| `backoffScaleFactor` | number | No | 1 | Backoff multiplier for exponential/linear retry | + ### Register Task Definition ```typescript From 0bb13aacff0f935bca3a6b96c8f415a33c2d7ca4 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Fri, 3 Oct 2025 02:11:39 +0300 Subject: [PATCH 29/60] update docs, add refresh token env var --- README.md | 86 ++----------------------- src/orkes/OrkesConductorClient.ts | 11 +++- src/orkes/helpers/resolveOrkesConfig.ts | 6 +- 3 files changed, 19 insertions(+), 84 deletions(-) diff --git a/README.md b/README.md index d180f686..f1c4d430 100644 --- a/README.md +++ b/README.md @@ -159,40 +159,31 @@ import { OrkesApiConfig, orkesConductorClient } from "@io-orkes/conductor-javasc const config: Partial = { serverUrl: "https://play.orkes.io/api", // Required: server api url - keyId: "your-key-id", // Required: authentication key - keySecret: "your-key-secret", // Required: authentication secret - refreshTokenInterval: 1800000, // Optional: token refresh interval in ms (default: 1800000 = 30 minutes) + keyId: "your-key-id", // Required for server with auth: authentication key + keySecret: "your-key-secret", // Required for server with auth: authentication secret + refreshTokenInterval: 0, // Optional: token refresh interval in ms (default: 30 minutes, 0 = no refresh) maxHttp2Connections: 1 // Optional: max HTTP2 connections (default: 1) }; const client = await orkesConductorClient(config); ``` -**Configuration Parameters:** - -| Parameter | Type | Required | Default | Description | -|-----------|------|----------|---------|-------------| -| `serverUrl` | string | Yes | - | Conductor server API URL | -| `keyId` | string | No* | - | Authentication key ID (*required for auth) | -| `keySecret` | string | No* | - | Authentication secret (*required for auth) | -| `refreshTokenInterval` | number | No | 1800000 | Token refresh interval in milliseconds (30 minutes) | -| `maxHttp2Connections` | number | No | 1 | Maximum simultaneous HTTP2 connections to Conductor server | - ### Environment Variables -You can configure authentication using environment variables: +You can configure client using environment variables: ```bash CONDUCTOR_SERVER_URL=https://play.orkes.io/api CONDUCTOR_AUTH_KEY=your-key-id CONDUCTOR_AUTH_SECRET=your-key-secret +CONDUCTOR_REFRESH_TOKEN_INTERVAL=0 CONDUCTOR_MAX_HTTP2_CONNECTIONS=1 ``` Environment variables are prioritized over config variables. ### Custom Fetch Function -You can provide a custom fetch function for HTTP requests: +You can provide a custom fetch function for SDK HTTP requests: ```typescript const client = await orkesConductorClient(config, fetch); @@ -275,71 +266,6 @@ import { WorkflowExecutor } from "@io-orkes/conductor-javascript"; const executor = new WorkflowExecutor(client); ``` -### Creating Workflows - -#### Workflow Factory - -The `workflow` function provides a convenient way to create workflow definitions with sensible defaults: - -```typescript -import { workflow, simpleTask } from "@io-orkes/conductor-javascript"; - -const myWorkflow = workflow( - "workflow_name", // name (required): workflow name - [ // tasks (required): array of task definitions - simpleTask("task1", "process_1", {}), - simpleTask("task2", "process_2", {}) - ] -); - -// Returns a WorkflowDef with these default values: -// { -// name: "workflow_name", -// version: 1, // Default: 1 -// tasks: [...], -// inputParameters: [], // Default: [] -// timeoutSeconds: 0 // Default: 0 (no timeout) -// } -``` - -**Workflow Factory Parameters:** - -| Parameter | Type | Required | Default | Description | -|-----------|------|----------|---------|-------------| -| `name` | string | Yes | - | Workflow name (must be unique) | -| `tasks` | TaskDefTypes[] | Yes | - | Array of task definitions | - -**Default Values Set by Factory:** -- `version`: 1 -- `inputParameters`: [] -- `timeoutSeconds`: 0 (no timeout) - -**Note:** For more control over workflow definition, you can manually create a `WorkflowDef` object with additional properties like `ownerEmail`, `description`, `outputParameters`, `failureWorkflow`, `restartable`, etc. - -#### Example: Combining Task Types - -Here's how to combine different task types in a workflow: - -```typescript -import { workflow, simpleTask, httpTask } from "@io-orkes/conductor-javascript"; - -const myWorkflow = workflow("order_processing", [ - // SIMPLE task - requires custom worker - simpleTask("validate_order", "validate_order_task", {}), - - // HTTP task - system task (no worker needed) - httpTask("call_payment", { - uri: "https://api.payment.com/charge", - method: "POST", - headers: { "Authorization": "Bearer token" }, - body: { amount: "${workflow.input.amount}" } - }), - - // SIMPLE task - requires custom worker - simpleTask("send_confirmation", "send_email_task", {}) -]); -``` - ### Task Generators Reference This section provides code examples for each task type generator. Use these to build your workflow task lists. diff --git a/src/orkes/OrkesConductorClient.ts b/src/orkes/OrkesConductorClient.ts index 1e9145c7..4454b762 100644 --- a/src/orkes/OrkesConductorClient.ts +++ b/src/orkes/OrkesConductorClient.ts @@ -16,8 +16,13 @@ export const orkesConductorClient = async ( config?: Partial, customFetch?: FetchFn ) => { - const { serverUrl, keyId, keySecret, maxHttp2Connections } = - resolveOrkesConfig(config); + const { + serverUrl, + keyId, + keySecret, + maxHttp2Connections, + refreshTokenInterval, + } = resolveOrkesConfig(config); if (!serverUrl) throw new Error("Conductor server URL is not set"); @@ -32,7 +37,7 @@ export const orkesConductorClient = async ( await conductorClientWithAuth.authorize( keyId, keySecret, - config?.refreshTokenInterval || REFRESH_TOKEN_IN_MILLISECONDS + refreshTokenInterval || REFRESH_TOKEN_IN_MILLISECONDS ); } diff --git a/src/orkes/helpers/resolveOrkesConfig.ts b/src/orkes/helpers/resolveOrkesConfig.ts index bb652a59..3cbb958a 100644 --- a/src/orkes/helpers/resolveOrkesConfig.ts +++ b/src/orkes/helpers/resolveOrkesConfig.ts @@ -5,7 +5,11 @@ export const resolveOrkesConfig = (config?: Partial) => { serverUrl: process.env.CONDUCTOR_SERVER_URL || config?.serverUrl, keyId: process.env.CONDUCTOR_AUTH_KEY || config?.keyId, keySecret: process.env.CONDUCTOR_AUTH_SECRET || config?.keySecret, + refreshTokenInterval: + Number(process.env.CONDUCTOR_REFRESH_TOKEN_INTERVAL) || + config?.maxHttp2Connections, maxHttp2Connections: - Number(process.env.CONDUCTOR_MAX_HTTP2_CONNECTIONS) || config?.maxHttp2Connections, + Number(process.env.CONDUCTOR_MAX_HTTP2_CONNECTIONS) || + config?.maxHttp2Connections, }; }; From a093a788bfb3059d3de93741047e8cac83cecd9a Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Fri, 3 Oct 2025 02:15:18 +0300 Subject: [PATCH 30/60] Update README.md --- README.md | 211 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 205 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index f1c4d430..2ba3623f 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,6 @@ Show support for the Conductor OSS. Please help spread the awareness by starrin - [Workflows](#workflows) - [WorkflowExecutor](#workflowexecutor) - [Creating Workflows](#creating-workflows) - - [Workflow Factory](#workflow-factory) - - [Example: Combining Task Types](#example-combining-task-types) - [Task Generators Reference](#task-generators-reference) - [Simple Task](#simple-task) - [HTTP Task](#http-task) @@ -252,20 +250,221 @@ See the [Workers](#workers) section for implementation details. ## Workflows -Workflows in Conductor are defined using task generators and managed through the `WorkflowExecutor` class. This section covers: -- **Creating workflows** using the workflow factory and task generators -- **Managing workflow lifecycle** (register, start, pause, resume, terminate, search) +Workflows orchestrate the execution of multiple tasks in a coordinated sequence. This section explains: +- **What workflows are** and how they work +- **How to create workflows** step-by-step using task generators +- **How to manage workflow lifecycle** (register, start, pause, resume, terminate, search) +- **Task generator reference** for all available task types ### WorkflowExecutor -The `WorkflowExecutor` class provides methods for managing workflow lifecycle: +The `WorkflowExecutor` class is your main interface for managing workflows. It provides methods to register, start, monitor, and control workflow execution. ```typescript import { WorkflowExecutor } from "@io-orkes/conductor-javascript"; +// Create executor instance const executor = new WorkflowExecutor(client); ``` +### Creating Workflows + +Creating a workflow in Conductor involves three main steps: + +#### Step 1: Define Your Workflow Structure + +A workflow definition is a JavaScript object that describes your workflow: + +```typescript +const workflowDef = { + name: "order_fulfillment", // Unique workflow name + version: 1, // Version number + description: "Process and fulfill customer orders", + ownerEmail: "team@example.com", // Optional: owner email + tasks: [ + // Tasks will be added here (Step 2) + ], + inputParameters: [], // Expected input parameter names + outputParameters: {}, // Output mapping from task results + timeoutSeconds: 3600, // Workflow timeout (0 = no timeout) + timeoutPolicy: "ALERT_ONLY" // What to do on timeout +}; +``` + +#### Step 2: Build Your Task List + +Use **task generators** to create the task list for your workflow. Task generators are helper functions that create properly formatted task definitions: + +```typescript +import { + simpleTask, + httpTask, + switchTask +} from "@io-orkes/conductor-javascript"; + +const workflowDef = { + name: "order_fulfillment", + version: 1, + description: "Process and fulfill customer orders", + tasks: [ + // Task 1: Validate order (custom worker) + simpleTask( + "validate_order_ref", // taskReferenceName: unique within workflow + "validate_order", // taskName: matches worker's taskDefName + { // inputParameters: data for this task + orderId: "${workflow.input.orderId}", + customerId: "${workflow.input.customerId}" + } + ), + + // Task 2: Check inventory via HTTP API + httpTask( + "check_inventory_ref", + { + uri: "https://api.inventory.com/check", + method: "POST", + body: { + productId: "${workflow.input.productId}", + quantity: "${workflow.input.quantity}" + }, + headers: { + "Content-Type": "application/json" + } + } + ), + + // Task 3: Conditional routing based on inventory + switchTask( + "route_order_ref", + "${check_inventory_ref.output.inStock}", + { + "true": [ + simpleTask("fulfill_order_ref", "fulfill_order", { + orderId: "${workflow.input.orderId}" + }) + ], + "false": [ + simpleTask("backorder_ref", "create_backorder", { + orderId: "${workflow.input.orderId}" + }) + ] + } + ) + ], + inputParameters: ["orderId", "customerId", "productId", "quantity"], + outputParameters: { + status: "${route_order_ref.output.status}", + fulfillmentId: "${fulfill_order_ref.output.fulfillmentId}" + } +}; +``` + +**Key Concepts:** + +- **taskReferenceName**: A unique identifier for the task within this workflow. Used to reference task outputs (e.g., `${task_ref.output.fieldName}`) +- **inputParameters**: Use `${workflow.input.fieldName}` to access workflow inputs and `${other_task_ref.output.fieldName}` to access previous task outputs +- **Task Generators**: Each task type has a generator function (e.g., `simpleTask`, `httpTask`, `switchTask`). See [Task Generators Reference](#task-generators-reference) for all available types. + +#### Step 3: Register and Start Your Workflow + +Once your workflow definition is ready, register it with Conductor and start executing it: + +```typescript +// Register the workflow definition +await executor.registerWorkflow( + true, // overwrite: replace existing definition if it exists + workflowDef +); + +// Start a workflow execution +const executionId = await executor.startWorkflow({ + name: "order_fulfillment", + version: 1, + input: { + orderId: "ORDER-123", + customerId: "CUST-456", + productId: "PROD-789", + quantity: 2 + } +}); + +console.log(`Workflow started with ID: ${executionId}`); + +// Monitor workflow status +const workflow = await executor.getWorkflow(executionId, true); +console.log(`Workflow status: ${workflow.status}`); +``` + +**Complete Example - Simple Notification Workflow:** + +Here's a complete example showing all three steps: + +```typescript +import { + orkesConductorClient, + WorkflowExecutor, + simpleTask, + httpTask +} from "@io-orkes/conductor-javascript"; + +// Initialize client and executor +const client = await orkesConductorClient({ + serverUrl: "https://play.orkes.io/api", + keyId: "your-key-id", + keySecret: "your-key-secret" +}); + +const executor = new WorkflowExecutor(client); + +// Step 1 & 2: Define workflow with tasks +const notificationWorkflow = { + name: "user_notification", + version: 1, + description: "Send notification to user via multiple channels", + tasks: [ + // Fetch user preferences + httpTask("get_user_prefs_ref", { + uri: "https://api.example.com/users/${workflow.input.userId}/preferences", + method: "GET" + }), + + // Send email notification + simpleTask("send_email_ref", "send_email", { + to: "${get_user_prefs_ref.output.email}", + subject: "${workflow.input.subject}", + body: "${workflow.input.message}" + }), + + // Send SMS if enabled + simpleTask("send_sms_ref", "send_sms", { + phone: "${get_user_prefs_ref.output.phone}", + message: "${workflow.input.message}" + }, true) // optional=true: workflow continues even if SMS fails + ], + inputParameters: ["userId", "subject", "message"], + outputParameters: { + emailSent: "${send_email_ref.output.sent}", + smsSent: "${send_sms_ref.output.sent}" + }, + timeoutSeconds: 300 +}; + +// Step 3: Register and start +await executor.registerWorkflow(true, notificationWorkflow); + +const workflowId = await executor.startWorkflow({ + name: "user_notification", + version: 1, + input: { + userId: "user123", + subject: "Welcome!", + message: "Thanks for signing up" + } +}); + +console.log(`Notification workflow started: ${workflowId}`); +``` + ### Task Generators Reference This section provides code examples for each task type generator. Use these to build your workflow task lists. From d9ceeaeeeb116bf3370b363571933ac8fcdbbf15 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Fri, 3 Oct 2025 02:18:01 +0300 Subject: [PATCH 31/60] Update README.md --- README.md | 70 ------------------------------------------------------- 1 file changed, 70 deletions(-) diff --git a/README.md b/README.md index 2ba3623f..cdeb95c0 100644 --- a/README.md +++ b/README.md @@ -395,76 +395,6 @@ const workflow = await executor.getWorkflow(executionId, true); console.log(`Workflow status: ${workflow.status}`); ``` -**Complete Example - Simple Notification Workflow:** - -Here's a complete example showing all three steps: - -```typescript -import { - orkesConductorClient, - WorkflowExecutor, - simpleTask, - httpTask -} from "@io-orkes/conductor-javascript"; - -// Initialize client and executor -const client = await orkesConductorClient({ - serverUrl: "https://play.orkes.io/api", - keyId: "your-key-id", - keySecret: "your-key-secret" -}); - -const executor = new WorkflowExecutor(client); - -// Step 1 & 2: Define workflow with tasks -const notificationWorkflow = { - name: "user_notification", - version: 1, - description: "Send notification to user via multiple channels", - tasks: [ - // Fetch user preferences - httpTask("get_user_prefs_ref", { - uri: "https://api.example.com/users/${workflow.input.userId}/preferences", - method: "GET" - }), - - // Send email notification - simpleTask("send_email_ref", "send_email", { - to: "${get_user_prefs_ref.output.email}", - subject: "${workflow.input.subject}", - body: "${workflow.input.message}" - }), - - // Send SMS if enabled - simpleTask("send_sms_ref", "send_sms", { - phone: "${get_user_prefs_ref.output.phone}", - message: "${workflow.input.message}" - }, true) // optional=true: workflow continues even if SMS fails - ], - inputParameters: ["userId", "subject", "message"], - outputParameters: { - emailSent: "${send_email_ref.output.sent}", - smsSent: "${send_sms_ref.output.sent}" - }, - timeoutSeconds: 300 -}; - -// Step 3: Register and start -await executor.registerWorkflow(true, notificationWorkflow); - -const workflowId = await executor.startWorkflow({ - name: "user_notification", - version: 1, - input: { - userId: "user123", - subject: "Welcome!", - message: "Thanks for signing up" - } -}); - -console.log(`Notification workflow started: ${workflowId}`); -``` - ### Task Generators Reference This section provides code examples for each task type generator. Use these to build your workflow task lists. From 5e0dac6ff62817d34801581d3ebe2fd98e1a93af Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Fri, 3 Oct 2025 02:25:21 +0300 Subject: [PATCH 32/60] Update README.md --- README.md | 331 ++++++++++++------------------------------------------ 1 file changed, 71 insertions(+), 260 deletions(-) diff --git a/README.md b/README.md index cdeb95c0..da4727c8 100644 --- a/README.md +++ b/README.md @@ -49,14 +49,8 @@ Show support for the Conductor OSS. Please help spread the awareness by starrin - [Dynamic Fork Task](#dynamic-fork-task) - [Join Task](#join-task) - [Human Task](#human-task) - - [Workflow Lifecycle Operations](#workflow-lifecycle-operations) - - [Register Workflow](#register-workflow) - - [Start Workflow](#start-workflow) - - [Get Workflow Status](#get-workflow-status) - - [Pause Workflow](#pause-workflow) - - [Resume Workflow](#resume-workflow) - - [Terminate Workflow](#terminate-workflow) - - [Search Workflows](#search-workflows) + - [Managing Workflow Execution](#managing-workflow-execution) + - [WorkflowExecutor API Reference](#workflowexecutor-api-reference) - [Monitoring & Debugging Tasks](#monitoring--debugging-tasks) - [Task Statuses](#task-statuses) - [Searching & Filtering Tasks](#searching--filtering-tasks) @@ -647,283 +641,100 @@ const task = humanTask("human_ref", "approval_task", { }); ``` -### Workflow Lifecycle Operations +### Managing Workflow Execution -This section covers all operations for managing workflow execution and lifecycle. +Once your workflow is running, you can monitor and control its execution using these operations: -#### Register Workflow +#### Monitor Workflow Status ```typescript -const workflowDef = { - name: "my_workflow", - version: 1, - ownerEmail: "developer@example.com", - tasks: [/* task definitions */], - inputParameters: [], - outputParameters: {}, - timeoutSeconds: 0 -}; - -// Register workflow (overwrite=true means it will replace existing definition) -await executor.registerWorkflow(true, workflowDef); -``` - -#### Start Workflow - -```typescript -const executionId = await executor.startWorkflow({ - name: "my_workflow", - version: 1, - input: { /* workflow input */ } -}); -``` - -#### Get Workflow Status - -```typescript -const workflowStatus = await executor.getWorkflow(executionId, true); -console.log(`Status: ${workflowStatus.status}`); -``` - -The `getWorkflow()` method returns a `Workflow` object with the following properties: +// Get workflow execution details +const workflow = await executor.getWorkflow(executionId, true); +console.log(`Status: ${workflow.status}`); -```typescript -interface Workflow { - // Basic identification - workflowId?: string; - workflowName?: string; - workflowVersion?: number; - - // Status and timing - status?: 'RUNNING' | 'COMPLETED' | 'FAILED' | 'TIMED_OUT' | 'TERMINATED' | 'PAUSED'; - createTime?: number; - updateTime?: number; - startTime?: number; - endTime?: number; - lastRetriedTime?: number; - - // Data - input?: Record; - output?: Record; - variables?: Record; - - // Relationships - parentWorkflowId?: string; - parentWorkflowTaskId?: string; - reRunFromWorkflowId?: string; - correlationId?: string; - - // Tasks and execution - tasks?: Array; - failedReferenceTaskNames?: Array; - taskToDomain?: Record; - - // Configuration - priority?: number; - externalInputPayloadStoragePath?: string; - externalOutputPayloadStoragePath?: string; - - // Metadata - ownerApp?: string; - createdBy?: string; - updatedBy?: string; - reasonForIncompletion?: string; - event?: string; - workflowDefinition?: WorkflowDef; -} +// Get workflow status summary +const status = await executor.getWorkflowStatus( + executionId, + true, // includeOutput + true // includeVariables +); ``` -#### Pause Workflow +#### Control Workflow Execution ```typescript +// Pause a running workflow await executor.pause(executionId); -``` - -#### Resume Workflow -```typescript +// Resume a paused workflow await executor.resume(executionId); -``` -#### Terminate Workflow - -```typescript +// Terminate a workflow with a reason await executor.terminate(executionId, "Terminating due to error"); + +// Restart a completed/failed workflow +await executor.restart(executionId, true); // useLatestDefinitions + +// Retry a failed workflow from the last failed task +await executor.retry(executionId, false); // resumeSubworkflowTasks + +// Rerun a workflow with potentially modified parameters +const newWorkflowId = await executor.reRun(executionId); ``` #### Search Workflows ```typescript +// Search for workflows with filters const searchResults = await executor.search( 0, // start: starting index 10, // size: number of results - "status:RUNNING", // query: e.g., "workflowType:my_workflow" + "status:RUNNING", // query: e.g., "workflowType:my_workflow AND status:FAILED" "*", // freeText: use "*" for all - "startTime:DESC", // sort (optional, default: "") - false // skipCache (optional, default: false) + "startTime:DESC", // sort (optional) + false // skipCache (optional) ); -``` - -### WorkflowExecutor Complete API Reference -The `WorkflowExecutor` provides the following methods for workflow management: - -#### Core Workflow Operations - -```typescript -// Register a workflow definition -await executor.registerWorkflow( - override: boolean, // If true, replaces existing definition - workflow: WorkflowDef -): Promise - -// Start a workflow -const workflowId = await executor.startWorkflow( - workflowRequest: StartWorkflowRequest -): Promise // Returns workflow instance ID - -// Execute workflow synchronously -const result = await executor.executeWorkflow( - workflowRequest: StartWorkflowRequest, - name: string, - version: number, - requestId: string, - waitUntilTaskRef?: string, // (optional) wait until specific task completes - waitForSeconds?: number, // (optional) max wait time - consistency?: Consistency, // (optional) - returnStrategy?: ReturnStrategy // (optional) -): Promise - -// Get workflow execution details -const workflow = await executor.getWorkflow( - workflowInstanceId: string, - includeTasks: boolean, - retry?: number // (optional, default: 0) -): Promise - -// Get workflow execution (alias for getWorkflow) -const execution = await executor.getExecution( - workflowInstanceId: string, - includeTasks?: boolean // (optional, default: true) -): Promise - -// Get workflow status summary -const status = await executor.getWorkflowStatus( - workflowInstanceId: string, - includeOutput: boolean, - includeVariables: boolean -): Promise -``` - -#### Workflow Control Operations - -```typescript -// Pause a running workflow -await executor.pause(workflowInstanceId: string): Promise - -// Resume a paused workflow -await executor.resume(workflowInstanceId: string): Promise - -// Terminate a workflow -await executor.terminate( - workflowInstanceId: string, - reason: string -): Promise - -// Restart a workflow -await executor.restart( - workflowInstanceId: string, - useLatestDefinitions: boolean -): Promise - -// Retry a failed workflow from last failed task -await executor.retry( - workflowInstanceId: string, - resumeSubworkflowTasks: boolean -): Promise - -// Rerun a workflow with new parameters -const newWorkflowId = await executor.reRun( - workflowInstanceId: string, - rerunWorkflowRequest?: Partial // (optional) -): Promise - -// Skip a task in a running workflow -await executor.skipTasksFromWorkflow( - workflowInstanceId: string, - taskReferenceName: string, - skipTaskRequest: Partial -): Promise -``` - -#### Task Operations - -```typescript -// Get task by ID -const task = await executor.getTask(taskId: string): Promise - -// Update task by ID -const result = await executor.updateTask( - taskId: string, - workflowInstanceId: string, - taskStatus: TaskResultStatus, // COMPLETED, FAILED, etc. - outputData: Record -): Promise - -// Update task by reference name -const result = await executor.updateTaskByRefName( - taskReferenceName: string, - workflowInstanceId: string, - status: TaskResultStatus, - taskOutput: Record -): Promise - -// Update task synchronously and return workflow -const workflow = await executor.updateTaskSync( - taskReferenceName: string, - workflowInstanceId: string, - status: TaskResultStatusEnum, - taskOutput: Record, - workerId?: string // (optional) -): Promise - -// Signal a workflow task -const response = await executor.signal( - workflowInstanceId: string, - status: TaskResultStatusEnum, - taskOutput: Record, - returnStrategy?: ReturnStrategy // (optional, default: TARGET_WORKFLOW) -): Promise - -// Signal a workflow task asynchronously (fire-and-forget) -await executor.signalAsync( - workflowInstanceId: string, - status: TaskResultStatusEnum, - taskOutput: Record -): Promise -``` - -#### Advanced Operations - -```typescript -// Go back to a specific task and rerun from there -await executor.goBackToTask( - workflowInstanceId: string, - taskFinderPredicate: (task: Task) => boolean, - rerunWorkflowRequestOverrides?: Partial // (optional) -): Promise - -// Go back to first task matching type -await executor.goBackToFirstTaskMatchingType( - workflowInstanceId: string, - taskType: string -): Promise - -// Start multiple workflows -const workflowIds = executor.startWorkflows( - workflowsRequest: StartWorkflowRequest[] -): Promise[] -``` +// Common search patterns: +// - By status: "status:RUNNING" +// - By name: "workflowType:order_fulfillment" +// - By date: "startTime:[2025-01-01 TO 2025-12-31]" +// - Combined: "workflowType:my_workflow AND status:FAILED" +``` + +### WorkflowExecutor API Reference + +Complete method reference for the `WorkflowExecutor` class: + +**Workflow Lifecycle:** +- `registerWorkflow(override: boolean, workflow: WorkflowDef): Promise` - Register or update a workflow definition +- `startWorkflow(request: StartWorkflowRequest): Promise` - Start a new workflow execution +- `executeWorkflow(...)` - Execute workflow synchronously and wait for completion +- `getWorkflow(id: string, includeTasks: boolean): Promise` - Get workflow execution details +- `getWorkflowStatus(id: string, includeOutput: boolean, includeVariables: boolean): Promise` - Get status summary +- `search(start: number, size: number, query: string, freeText: string, sort?: string, skipCache?: boolean)` - Search workflows + +**Workflow Control:** +- `pause(workflowId: string): Promise` - Pause a running workflow +- `resume(workflowId: string): Promise` - Resume a paused workflow +- `terminate(workflowId: string, reason: string): Promise` - Terminate with reason +- `restart(workflowId: string, useLatestDefinitions: boolean): Promise` - Restart a workflow +- `retry(workflowId: string, resumeSubworkflowTasks: boolean): Promise` - Retry from last failed task +- `reRun(workflowId: string, request?: Partial): Promise` - Rerun with new parameters +- `skipTasksFromWorkflow(workflowId: string, taskRefName: string, request: Partial): Promise` - Skip a task + +**Task Operations:** +- `getTask(taskId: string): Promise` - Get task by ID +- `updateTask(taskId: string, workflowId: string, status: TaskResultStatus, output: Record): Promise` - Update task by ID +- `updateTaskByRefName(taskRefName: string, workflowId: string, status: TaskResultStatus, output: Record): Promise` - Update by reference name +- `updateTaskSync(taskRefName: string, workflowId: string, status: TaskResultStatusEnum, output: Record, workerId?: string): Promise` - Update and return workflow +- `signal(workflowId: string, status: TaskResultStatusEnum, output: Record, returnStrategy?: ReturnStrategy): Promise` - Send signal to workflow +- `signalAsync(workflowId: string, status: TaskResultStatusEnum, output: Record): Promise` - Signal asynchronously + +**Advanced:** +- `goBackToTask(workflowId: string, predicate: (task: Task) => boolean, overrides?: Partial): Promise` - Rerun from specific task +- `goBackToFirstTaskMatchingType(workflowId: string, taskType: string): Promise` - Rerun from first task of type +- `startWorkflows(requests: StartWorkflowRequest[]): Promise[]` - Start multiple workflows ### Monitoring & Debugging Tasks From 34e13835ac249986f8d3a982f6b3d0018d9d07f5 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Fri, 3 Oct 2025 02:34:17 +0300 Subject: [PATCH 33/60] Update README.md --- README.md | 216 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 187 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index da4727c8..5ef46104 100644 --- a/README.md +++ b/README.md @@ -660,6 +660,54 @@ const status = await executor.getWorkflowStatus( ); ``` +The `getWorkflow()` method returns a `Workflow` object with the following properties: + +```typescript +interface Workflow { + // Basic identification + workflowId?: string; + workflowName?: string; + workflowVersion?: number; + + // Status and timing + status?: 'RUNNING' | 'COMPLETED' | 'FAILED' | 'TIMED_OUT' | 'TERMINATED' | 'PAUSED'; + createTime?: number; + updateTime?: number; + startTime?: number; + endTime?: number; + lastRetriedTime?: number; + + // Data + input?: Record; + output?: Record; + variables?: Record; + + // Relationships + parentWorkflowId?: string; + parentWorkflowTaskId?: string; + reRunFromWorkflowId?: string; + correlationId?: string; + + // Tasks and execution + tasks?: Array; + failedReferenceTaskNames?: Array; + taskToDomain?: Record; + + // Configuration + priority?: number; + externalInputPayloadStoragePath?: string; + externalOutputPayloadStoragePath?: string; + + // Metadata + ownerApp?: string; + createdBy?: string; + updatedBy?: string; + reasonForIncompletion?: string; + event?: string; + workflowDefinition?: WorkflowDef; +} +``` + #### Control Workflow Execution ```typescript @@ -706,35 +754,145 @@ const searchResults = await executor.search( Complete method reference for the `WorkflowExecutor` class: -**Workflow Lifecycle:** -- `registerWorkflow(override: boolean, workflow: WorkflowDef): Promise` - Register or update a workflow definition -- `startWorkflow(request: StartWorkflowRequest): Promise` - Start a new workflow execution -- `executeWorkflow(...)` - Execute workflow synchronously and wait for completion -- `getWorkflow(id: string, includeTasks: boolean): Promise` - Get workflow execution details -- `getWorkflowStatus(id: string, includeOutput: boolean, includeVariables: boolean): Promise` - Get status summary -- `search(start: number, size: number, query: string, freeText: string, sort?: string, skipCache?: boolean)` - Search workflows - -**Workflow Control:** -- `pause(workflowId: string): Promise` - Pause a running workflow -- `resume(workflowId: string): Promise` - Resume a paused workflow -- `terminate(workflowId: string, reason: string): Promise` - Terminate with reason -- `restart(workflowId: string, useLatestDefinitions: boolean): Promise` - Restart a workflow -- `retry(workflowId: string, resumeSubworkflowTasks: boolean): Promise` - Retry from last failed task -- `reRun(workflowId: string, request?: Partial): Promise` - Rerun with new parameters -- `skipTasksFromWorkflow(workflowId: string, taskRefName: string, request: Partial): Promise` - Skip a task - -**Task Operations:** -- `getTask(taskId: string): Promise` - Get task by ID -- `updateTask(taskId: string, workflowId: string, status: TaskResultStatus, output: Record): Promise` - Update task by ID -- `updateTaskByRefName(taskRefName: string, workflowId: string, status: TaskResultStatus, output: Record): Promise` - Update by reference name -- `updateTaskSync(taskRefName: string, workflowId: string, status: TaskResultStatusEnum, output: Record, workerId?: string): Promise` - Update and return workflow -- `signal(workflowId: string, status: TaskResultStatusEnum, output: Record, returnStrategy?: ReturnStrategy): Promise` - Send signal to workflow -- `signalAsync(workflowId: string, status: TaskResultStatusEnum, output: Record): Promise` - Signal asynchronously - -**Advanced:** -- `goBackToTask(workflowId: string, predicate: (task: Task) => boolean, overrides?: Partial): Promise` - Rerun from specific task -- `goBackToFirstTaskMatchingType(workflowId: string, taskType: string): Promise` - Rerun from first task of type -- `startWorkflows(requests: StartWorkflowRequest[]): Promise[]` - Start multiple workflows +```typescript +import { WorkflowExecutor } from "@io-orkes/conductor-javascript"; + +const executor = new WorkflowExecutor(client); + +// ============================================================ +// Workflow Lifecycle +// ============================================================ + +// Register or update a workflow definition +await executor.registerWorkflow(override: boolean, workflow: WorkflowDef): Promise + +// Start a new workflow execution +const workflowId = await executor.startWorkflow(request: StartWorkflowRequest): Promise + +// Execute workflow synchronously and wait for completion +const result = await executor.executeWorkflow(...): Promise + +// Get workflow execution details with tasks +const workflow = await executor.getWorkflow(id: string, includeTasks: boolean): Promise + +// Get workflow status summary +const status = await executor.getWorkflowStatus( + id: string, + includeOutput: boolean, + includeVariables: boolean +): Promise + +// Search workflows with filters +const results = await executor.search( + start: number, + size: number, + query: string, + freeText: string, + sort?: string, + skipCache?: boolean +): Promise + +// ============================================================ +// Workflow Control +// ============================================================ + +// Pause a running workflow +await executor.pause(workflowId: string): Promise + +// Resume a paused workflow +await executor.resume(workflowId: string): Promise + +// Terminate a workflow with reason +await executor.terminate(workflowId: string, reason: string): Promise + +// Restart a workflow +await executor.restart(workflowId: string, useLatestDefinitions: boolean): Promise + +// Retry a failed workflow from last failed task +await executor.retry(workflowId: string, resumeSubworkflowTasks: boolean): Promise + +// Rerun a workflow with new parameters +const newId = await executor.reRun( + workflowId: string, + request?: Partial +): Promise + +// Skip a task in a running workflow +await executor.skipTasksFromWorkflow( + workflowId: string, + taskRefName: string, + request: Partial +): Promise + +// ============================================================ +// Task Operations +// ============================================================ + +// Get task by ID +const task = await executor.getTask(taskId: string): Promise + +// Update task by ID +await executor.updateTask( + taskId: string, + workflowId: string, + status: TaskResultStatus, + output: Record +): Promise + +// Update task by reference name +await executor.updateTaskByRefName( + taskRefName: string, + workflowId: string, + status: TaskResultStatus, + output: Record +): Promise + +// Update task and return updated workflow +const updatedWorkflow = await executor.updateTaskSync( + taskRefName: string, + workflowId: string, + status: TaskResultStatusEnum, + output: Record, + workerId?: string +): Promise + +// Send signal to workflow +const response = await executor.signal( + workflowId: string, + status: TaskResultStatusEnum, + output: Record, + returnStrategy?: ReturnStrategy +): Promise + +// Send signal asynchronously (fire-and-forget) +await executor.signalAsync( + workflowId: string, + status: TaskResultStatusEnum, + output: Record +): Promise + +// ============================================================ +// Advanced Operations +// ============================================================ + +// Rerun from a specific task found by predicate +await executor.goBackToTask( + workflowId: string, + predicate: (task: Task) => boolean, + overrides?: Partial +): Promise + +// Rerun from first task of specific type +await executor.goBackToFirstTaskMatchingType( + workflowId: string, + taskType: string +): Promise + +// Start multiple workflows at once +const workflowIds = executor.startWorkflows( + requests: StartWorkflowRequest[] +): Promise[] +``` ### Monitoring & Debugging Tasks From 204e25998cf6e19c965407248817652499a8a01b Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Fri, 3 Oct 2025 02:51:10 +0300 Subject: [PATCH 34/60] Update README.md --- README.md | 88 +++---------------------------------------------------- 1 file changed, 4 insertions(+), 84 deletions(-) diff --git a/README.md b/README.md index 5ef46104..0f79b07b 100644 --- a/README.md +++ b/README.md @@ -383,10 +383,6 @@ const executionId = await executor.startWorkflow({ }); console.log(`Workflow started with ID: ${executionId}`); - -// Monitor workflow status -const workflow = await executor.getWorkflow(executionId, true); -console.log(`Workflow status: ${workflow.status}`); ``` ### Task Generators Reference @@ -648,10 +644,6 @@ Once your workflow is running, you can monitor and control its execution using t #### Monitor Workflow Status ```typescript -// Get workflow execution details -const workflow = await executor.getWorkflow(executionId, true); -console.log(`Status: ${workflow.status}`); - // Get workflow status summary const status = await executor.getWorkflowStatus( executionId, @@ -660,51 +652,15 @@ const status = await executor.getWorkflowStatus( ); ``` -The `getWorkflow()` method returns a `Workflow` object with the following properties: +The `getWorkflowStatus()` method returns a `Workflow` object with the following properties: ```typescript -interface Workflow { - // Basic identification +interface WorkflowStatus { workflowId?: string; - workflowName?: string; - workflowVersion?: number; - - // Status and timing - status?: 'RUNNING' | 'COMPLETED' | 'FAILED' | 'TIMED_OUT' | 'TERMINATED' | 'PAUSED'; - createTime?: number; - updateTime?: number; - startTime?: number; - endTime?: number; - lastRetriedTime?: number; - - // Data - input?: Record; + correlationId?: string; output?: Record; variables?: Record; - - // Relationships - parentWorkflowId?: string; - parentWorkflowTaskId?: string; - reRunFromWorkflowId?: string; - correlationId?: string; - - // Tasks and execution - tasks?: Array; - failedReferenceTaskNames?: Array; - taskToDomain?: Record; - - // Configuration - priority?: number; - externalInputPayloadStoragePath?: string; - externalOutputPayloadStoragePath?: string; - - // Metadata - ownerApp?: string; - createdBy?: string; - updatedBy?: string; - reasonForIncompletion?: string; - event?: string; - workflowDefinition?: WorkflowDef; + status?: "RUNNING" | "COMPLETED" | "FAILED" | "TIMED_OUT" | "TERMINATED" | "PAUSED"; } ``` @@ -1314,19 +1270,6 @@ const manager = new TaskManager(client, workers, { await manager.startPolling(); ``` -**Configuration Parameters:** - -| Parameter | Type | Required | Default | Description | -|-----------|------|----------|---------|-------------| -| `logger` | ConductorLogger | No | DefaultLogger | Logger instance for debugging and monitoring | -| `options.pollInterval` | number | No | 100 | Polling interval in milliseconds | -| `options.concurrency` | number | No | 1 | Maximum concurrent task executions per worker | -| `options.workerID` | string | No | hostname | Unique identifier for this worker group | -| `options.domain` | string | No | undefined | Task domain for isolation | -| `options.batchPollingTimeout` | number | No | 100 | Batch polling timeout in milliseconds | -| `onError` | TaskErrorHandler | No | noop | Global error handler function | -| `maxRetries` | number | No | 3 | Maximum retry attempts before giving up | - #### Dynamic Configuration Updates You can update polling options at runtime without stopping workers: @@ -1603,29 +1546,6 @@ const taskDef = taskDefinition({ }); ``` -**Task Definition Parameters:** - -| Parameter | Type | Required | Default | Description | -|-----------|------|----------|---------|-------------| -| `name` | string | Yes | - | Task name (must be unique) | -| `ownerApp` | string | No | "" | Owner application name | -| `description` | string | No | "" | Task description | -| `retryCount` | number | No | 3 | Number of retry attempts | -| `timeoutSeconds` | number | No | 3600 | Task timeout in seconds | -| `inputKeys` | string[] | No | [] | List of expected input keys | -| `outputKeys` | string[] | No | [] | List of output keys | -| `timeoutPolicy` | string | No | "TIME_OUT_WF" | Timeout policy: "RETRY", "TIME_OUT_WF", "ALERT_ONLY" | -| `retryLogic` | string | No | "FIXED" | Retry logic: "FIXED", "EXPONENTIAL_BACKOFF", "LINEAR_BACKOFF" | -| `retryDelaySeconds` | number | No | 60 | Delay between retries in seconds | -| `responseTimeoutSeconds` | number | No | 600 | Response timeout in seconds | -| `concurrentExecLimit` | number | No | 0 | Max concurrent executions (0 = unlimited) | -| `inputTemplate` | object | No | {} | Default input template | -| `rateLimitPerFrequency` | number | No | 0 | Rate limit count (0 = no limit) | -| `rateLimitFrequencyInSeconds` | number | No | 1 | Rate limit window in seconds | -| `ownerEmail` | string | No | "" | Owner email address | -| `pollTimeoutSeconds` | number | No | 3600 | Poll timeout in seconds | -| `backoffScaleFactor` | number | No | 1 | Backoff multiplier for exponential/linear retry | - ### Register Task Definition ```typescript From 21410975b64752a03d9253d1e7ad0968f7f32296 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Fri, 3 Oct 2025 03:04:41 +0300 Subject: [PATCH 35/60] Update README.md --- README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index 0f79b07b..af4a3fde 100644 --- a/README.md +++ b/README.md @@ -650,11 +650,8 @@ const status = await executor.getWorkflowStatus( true, // includeOutput true // includeVariables ); -``` - -The `getWorkflowStatus()` method returns a `Workflow` object with the following properties: -```typescript +// The `getWorkflowStatus()` method returns a `Workflow` object with the following properties: interface WorkflowStatus { workflowId?: string; correlationId?: string; From 205f7ba05a1ab06e18e143a60f78cacdf9d4dab2 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Fri, 3 Oct 2025 03:05:57 +0300 Subject: [PATCH 36/60] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index af4a3fde..5f14235d 100644 --- a/README.md +++ b/README.md @@ -651,7 +651,7 @@ const status = await executor.getWorkflowStatus( true // includeVariables ); -// The `getWorkflowStatus()` method returns a `Workflow` object with the following properties: +// The `getWorkflowStatus()` method returns a `WorkflowStatus` object with the following properties: interface WorkflowStatus { workflowId?: string; correlationId?: string; From 1587ebe9a240c0fee272c5a7fe8beee725930fd7 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Fri, 3 Oct 2025 03:33:08 +0300 Subject: [PATCH 37/60] Update DECISIONS.md --- DECISIONS.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/DECISIONS.md b/DECISIONS.md index b1cf2f89..7e05dccc 100644 --- a/DECISIONS.md +++ b/DECISIONS.md @@ -15,18 +15,6 @@ I would suggest we maintain CJS compat for now, in the hope that there's either Because it's relatively simple to support interop between ESM and CJS, and the wide footprint. - -### Using `node-fetch` - -who: @nicktomlin - -I initially explored [`undici`](https://github.com/nodejs/undici) as a way of using the built-in `fetch` support in node. - -Unfortunately, at the time of writing there's no clean way to handle `tls` options. Additionally, `fetch` is experimental for node 16.x which results in warnings. - -At some point in the future, it would be nice to migrate over once undici's `fetch` implementation is a little more feature rich and things have stabalized. - - ### Using `jest` for testing `jest` can be a little magical, but it's relatively fast (with 28.x especially), ergonomic, and provides a simple maintenance story over a BYO setup. @@ -38,3 +26,16 @@ I considered `mocha` and `ava` (which i've also used and like) but `jest` struck who: @nicktomlin Typescript publication can be a wacky process and `tsup` simplifies a lot of this. + +### Using `undici` to make http2 requests if possible + +who: @dmitryborisov-sm + +Since Node.js use undici for built-in fetch functionality. +Undici supports H2 and covers all our needs. If undici fails to initialize SDK will fall back to default (HTTP/1.1) fetch in node18 / default (HTTP/2) fetch in browser. It's possible to provide any other custom fetch function while creating SDK so in theory SDK could work on older node version with some libs like node-fetch. + +### OpenAPI generated code should not be modified directly + +who: @dmitryborisov-sm + +SDK should follow layered architecture with all the modifications made in the top layers. This will allow us to easily update autogenerated code and stay in tune with API updates with minimum effort and risk. From dbf58351705d8371cfebb10c1c2280651d6aa424 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Fri, 3 Oct 2025 13:19:29 +0300 Subject: [PATCH 38/60] Update OPEN-API-README.md --- src/common/OPEN-API-README.md | 27 ++++----------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/src/common/OPEN-API-README.md b/src/common/OPEN-API-README.md index be9dcadf..92df1ee1 100644 --- a/src/common/OPEN-API-README.md +++ b/src/common/OPEN-API-README.md @@ -1,36 +1,17 @@ The client is generated using [this library](https://github.com/ferdikoomen/openapi-typescript-codegen). -## Overrides - -To enable TLS, we have overriden some of the types and options in the generated `core` files. - -Changes are commented with `conductor-client-modification` - -## Updating definitions +1. Run the latest conductor OSS server locally +2. Run the npm commands: +#### Updating definitions To update `service` and `model` definitions: -1. Run the latest conductor OSS server locally -2. Run the npm commands - ```text npm run generateClient:models ``` -You may need to restore `open-api/ConductorClient` if there is a diff after this command. - -## Updating the core +#### Updating the core ``` npm run generateClient:core ``` - -NOTE: There will be a diff on generation and you will manually need to restore those changes. Typically, this mean restoring the client and the core. Typically that will involve something like this: - -``` -# review diff and see if there are any meaningful changes you want to keep -# If there are no changes then check out -git checkout src/common/open-api/ConductorClient.ts -git checkout src/common/open-api/core -# Otherwise, selectively check out the old parts -``` From 83f90cc0ec52e7fc243784ce7f17263ae1bcbbce Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Fri, 3 Oct 2025 17:59:48 +0300 Subject: [PATCH 39/60] delete outdated files --- workers_sdk.md | 174 ------------------------------------------------ workflow_sdk.md | 135 ------------------------------------- 2 files changed, 309 deletions(-) delete mode 100644 workers_sdk.md delete mode 100644 workflow_sdk.md diff --git a/workers_sdk.md b/workers_sdk.md deleted file mode 100644 index f37be0e3..00000000 --- a/workers_sdk.md +++ /dev/null @@ -1,174 +0,0 @@ -# Writing Workers with the Javascript SDK - -A worker is responsible for executing a task. -Operator and System tasks are handled by the Conductor server, while user defined tasks needs to have a worker created that awaits the work to be scheduled by the server for it to be executed. - -Worker framework provides features such as polling threads, metrics and server communication. - -### Design Principles for Workers - -Each worker embodies design pattern and follows certain basic principles: - -1. Workers are stateless and do not implement a workflow specific logic. -2. Each worker executes a very specific task and produces well-defined output given specific inputs. -3. Workers are meant to be idempotent (or should handle cases where the task that is partially executed gets rescheduled due to timeouts etc.) -4. Workers do not implement the logic to handle retries etc, that is taken care by the Conductor server. - -### Creating Task Workers - -Task worker is implemented using a function that confirms to the following function - -```typescript -import { ConductorWorker, Task } from "@io-orkes/conductor-javascript"; - -const worker: ConductorWorker = { - taskDefName: "task-def-name", - execute: async ( - task: Task - ): Promise> => {}, -}; -``` - -Worker returns a object as the output of the task execution. The object is just a json that follows the TaskResult interface. -If an `error` is returned, the task is marked as `FAILED` - -#### Task worker that returns an object - -```typescript -import { ConductorWorker, Task } from "@io-orkes/conductor-javascript"; - -const worker: ConductorWorker = { - taskDefName: "task-def-name", - execute: async (task: Task) => { - // Sample output - return { - outputData: { - hello: "From your worker", - }, - status: "COMPLETED", - }; - }, -}; -``` - -#### Controlling execution for long-running tasks - -For the long-running tasks you might want to spawn another process/routine and update the status of the task at a later point and complete the -execution function without actually marking the task as `COMPLETED`. Use `TaskResult` Interface that allows you to specify more fined grained control. - -Here is an example of a task execution function that returns with `IN_PROGRESS` status asking server to push the task again in 60 seconds. - -```typescript -const worker: ConductorWorker = { - taskDefName: "task-def-name", - execute: async (task: Task) => { - // Sample output - return { - outputData: {}, - status: "IN_PROGRESS", - callbackAfterSeconds: 60, - }; - }, - pollInterval: 100, // optional - concurrency: 2, // optional -}; -``` - -## Starting Workers - -`TaskRunner` interface is used to start the workers, which takes care of polling server for the work, executing worker code and updating the results back to the server. - -```typescript -import { - OrkesApiConfig, - orkesConductorClient, - TaskRunner, -} from "@io-orkes/conductor-javascript"; - -const clientPromise = orkesConductorClient({ - keyId: "XXX", // optional - keySecret: "XXXX", // optional - serverUrl: "https://play.orkes.io/api", -}); - -const client = await clientPromise; - -const taskDefName = "HelloWorldWorker"; - -const customWorker: ConductorWorker = { -taskDefName, - execute: async ({ inputData, taskId }) => { - return { - outputData: { - greeting: "Hello World", - }, - status: "COMPLETED", - }; - }, -}; -// Worker Options will take precedence over options defined in the manager - -const manager = new TaskManager(client, [customWorker], { - options: { pollInterval: 100, concurrency: 1 }, -}); - -manager.startPolling(); -// You can update all worker settings at once using -manager.updatePollingOptions({ pollInterval: 100, concurrency: 1 }); - -// You can update a single worker setting using : -manager.updatePollingOptionForWorker(taskDefName, { - pollInterval: 100, - concurrency: 1, -}); - -manager.isPolling // Will resolve to true - -await manager.stopPolling(); - -manager.isPolling // Will resolve to false - -``` - -## Task Management APIs - -### Get Task Details - -```typescript -import { - WorkflowExecutor, - TaskResultStatus, -} from "@io-orkes/conductor-javascript"; - -const clientPromise = orkesConductorClient({ - keyId: "XXX", // optional - keySecret: "XXXX", // optional - serverUrl: "https://play.orkes.io/api", -}); - -const client = await clientPromise; -const executor = new WorkflowExecutor(client); - -const taskDetails = await executor.getTask(someTaskId); -``` - -### Updating the Task result outside the worker implementation - -#### Update task by Reference Name - -```typescript -executor.updateTaskByRefName( - taskReferenceName, - workflowInstanceId, - "COMPLETED", - { some: { output: "value" } } -); -``` - -#### Update task by id - -```typescript -await executor.updateTask(taskId, executionId, "COMPLETED", newChange); -``` - -### Next: [Create and Execute Workflows](workflow_sdk.md) diff --git a/workflow_sdk.md b/workflow_sdk.md deleted file mode 100644 index 40d88c86..00000000 --- a/workflow_sdk.md +++ /dev/null @@ -1,135 +0,0 @@ -# Authoring Workflows with the Javascript SDK - -## A simple two-step workflow - -```typescript -import { - OrkesApiConfig, - orkesConductorClient, - TaskRunner, - simpleTask, -} from "@io-orkes/conductor-javascript"; - -//API client instance with server address and authentication details -const clientPromise = orkesConductorClient({ - keyId: "XXX", // optional - keySecret: "XXXX", // optional - serverUrl: "https://play.orkes.io/api", -}); - -const client = await clientPromise; - -//Create new workflow executor -const executor = new WorkflowExecutor(client); - -// Using Factory function -const factoryWf = { - name: "my_first_workflow", - version: 1, - ownerEmail: "developers@orkes.io", - tasks: [simpleTask("simple_task_ref", "simple_task", {})], - inputParameters: [], - outputParameters: {}, - timeoutSeconds: 0, -}; -const workflow = executor.registerWorkflow(true, factoryWf); -``` - -### Execute Workflow - -#### Using Workflow Executor to start previously registered workflow - -```typescript -const executor = new WorkflowExecutor(client); -const executionId = await executor.startWorkflow({ name, version, input: {} }); -``` - -#### Using Workflow Executor to execute a workflow and get the output as a result - -```typescript -import { - orkesConductorClient, - WorkflowExecutor, - ConductorClient, - TaskType, -} from "@io-orkes/conductor-javascript"; - -//API client instance with server address and authentication details -const clientPromise = orkesConductorClient({ - keyId: "XXX", // optional - keySecret: "XXXX", // optional - serverUrl: "https://play.orkes.io/api", -}); - -const client = await clientPromise; - -//Create new workflow executor -const executor = new WorkflowExecutor(client); - -// Create a workflow -const factoryWf = { - name: "my_first_workflow", - version: 1, - ownerEmail: "developers@orkes.io", - tasks: [simpleTask("simple_task_ref", "simple_task", {})], - inputParameters: [], - outputParameters: {}, - timeoutSeconds: 0, -}; - -// Register workflow -const workflow = executor.registerWorkflow(true, factoryWf); - -// Start Workflow -const executionId = await executor.startWorkflow({ - name: factoryWf.name, - version: 1, - input: {}, -}); - -// Query Workflow status -const workflowStatus = await executor.getWorkflow(executionId, true); - -// The workflow status returns the following type -export type Workflow = { - ownerApp?: string; - createTime?: number; - updateTime?: number; - createdBy?: string; - updatedBy?: string; - status?: - | "RUNNING" - | "COMPLETED" - | "FAILED" - | "TIMED_OUT" - | "TERMINATED" - | "PAUSED"; - endTime?: number; - workflowId?: string; - parentWorkflowId?: string; - parentWorkflowTaskId?: string; - tasks?: Array; - input?: Record; - output?: Record; - correlationId?: string; - reRunFromWorkflowId?: string; - reasonForIncompletion?: string; - event?: string; - taskToDomain?: Record; - failedReferenceTaskNames?: Array; - workflowDefinition?: WorkflowDef; - externalInputPayloadStoragePath?: string; - externalOutputPayloadStoragePath?: string; - priority?: number; - variables?: Record; - lastRetriedTime?: number; - startTime?: number; - workflowVersion?: number; - workflowName?: string; -}; -``` - -### More Examples - -You can find more examples at the following GitHub repository: -https://github.com/conductor-sdk/javascript-sdk-examples From c3532e67da01c7943e8ba0e98ff0c808eace2b4e Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Fri, 3 Oct 2025 18:02:18 +0300 Subject: [PATCH 40/60] Update DECISIONS.md --- DECISIONS.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/DECISIONS.md b/DECISIONS.md index 7e05dccc..35b6eb72 100644 --- a/DECISIONS.md +++ b/DECISIONS.md @@ -27,15 +27,14 @@ who: @nicktomlin Typescript publication can be a wacky process and `tsup` simplifies a lot of this. -### Using `undici` to make http2 requests if possible +### Using `undici` to make http2 requests (if possible) who: @dmitryborisov-sm -Since Node.js use undici for built-in fetch functionality. -Undici supports H2 and covers all our needs. If undici fails to initialize SDK will fall back to default (HTTP/1.1) fetch in node18 / default (HTTP/2) fetch in browser. It's possible to provide any other custom fetch function while creating SDK so in theory SDK could work on older node version with some libs like node-fetch. +Since Node.js uses Undici for its built-in fetch functionality, Undici supports HTTP/2 and meets all our requirements. If Undici fails to initialize, the SDK will fall back to the default fetch: HTTP/1.1 in Node 18 and HTTP/2 in browsers. It's also possible to provide a custom fetch function when creating the SDK, so in theory, the SDK could work on older Node versions using libraries like node-fetch. ### OpenAPI generated code should not be modified directly who: @dmitryborisov-sm -SDK should follow layered architecture with all the modifications made in the top layers. This will allow us to easily update autogenerated code and stay in tune with API updates with minimum effort and risk. +The SDK should follow a layered architecture, with all modifications made in the top layers. This approach enables easy updates to autogenerated code and ensures alignment with API changes, minimizing both effort and risk. From 0a106efd87e9d8fac7130ea8652db51cb246590b Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Mon, 6 Oct 2025 17:03:59 +0300 Subject: [PATCH 41/60] add full api reference --- README.md | 16 +- docs/human-executor-api-reference.md | 182 +++++++++ docs/metadata-client-api-reference.md | 88 ++++ docs/scheduler-client-api-reference.md | 164 ++++++++ docs/service-registry-client-api-reference.md | 217 ++++++++++ docs/task-client-api-reference.md | 64 +++ docs/task-manager-api-reference.md | 64 +++ docs/template-client-api-reference.md | 30 ++ docs/workflow-executor-api-reference.md | 380 ++++++++++++++++++ 9 files changed, 1198 insertions(+), 7 deletions(-) create mode 100644 docs/human-executor-api-reference.md create mode 100644 docs/metadata-client-api-reference.md create mode 100644 docs/scheduler-client-api-reference.md create mode 100644 docs/service-registry-client-api-reference.md create mode 100644 docs/task-client-api-reference.md create mode 100644 docs/task-manager-api-reference.md create mode 100644 docs/template-client-api-reference.md create mode 100644 docs/workflow-executor-api-reference.md diff --git a/README.md b/README.md index 5f14235d..04fb9fc8 100644 --- a/README.md +++ b/README.md @@ -705,6 +705,8 @@ const searchResults = await executor.search( ### WorkflowExecutor API Reference +For a complete method reference for the `WorkflowExecutor` class, see the [WorkflowExecutor API Reference](./docs/workflow-executor-api-reference.md). + Complete method reference for the `WorkflowExecutor` class: ```typescript @@ -849,7 +851,7 @@ const workflowIds = executor.startWorkflows( ### Monitoring & Debugging Tasks -The `TaskClient` provides capabilities for monitoring and debugging tasks within your workflow executions: +The `TaskClient` provides capabilities for monitoring and debugging tasks within your workflow executions. For a complete method reference, see the [TaskClient API Reference](./docs/task-client-api-reference.md). ```typescript import { TaskClient } from "@io-orkes/conductor-javascript"; @@ -947,7 +949,7 @@ Workers are background processes that execute tasks in your workflows. Think of Workflow → Creates Tasks → Workers Poll for Tasks → Execute Logic → Return Results → Workflow Continues ``` -The SDK provides the **TaskManager** class - an easy-to-use interface for managing workers efficiently. +The SDK provides the **TaskManager** class - an easy-to-use interface for managing workers efficiently. For a complete method reference, see the [TaskManager API Reference](./docs/task-manager-api-reference.md). ### Quick Start: Your First Worker @@ -1297,7 +1299,7 @@ process.on('SIGTERM', async () => { ### SchedulerClient -The `SchedulerClient` manages workflow scheduling and provides methods for creating, managing, and monitoring scheduled workflows. +The `SchedulerClient` manages workflow scheduling and provides methods for creating, managing, and monitoring scheduled workflows. For a complete method reference, see the [SchedulerClient API Reference](./docs/scheduler-client-api-reference.md). ```typescript import { SchedulerClient } from "@io-orkes/conductor-javascript"; @@ -1405,7 +1407,7 @@ const results = await scheduler.search(0, 10, "startTime:DESC", "*", "status:RUN ### ServiceRegistryClient -The `ServiceRegistryClient` manages service registrations and circuit breakers: +The `ServiceRegistryClient` manages service registrations and circuit breakers. For a complete method reference, see the [ServiceRegistryClient API Reference](./docs/service-registry-client-api-reference.md). ```typescript import { ServiceRegistryClient } from "@io-orkes/conductor-javascript"; @@ -1470,7 +1472,7 @@ await serviceRegistry.removeService("user-service"); ### MetadataClient -The `MetadataClient` class provides methods for managing task and workflow definitions in Conductor. +The `MetadataClient` class provides methods for managing task and workflow definitions in Conductor. For a complete method reference, see the [MetadataClient API Reference](./docs/metadata-client-api-reference.md). ```typescript import { MetadataClient } from "@io-orkes/conductor-javascript"; @@ -1674,7 +1676,7 @@ HUMAN tasks are a **type of system task** that enable human interaction within w ### HumanExecutor -The `HumanExecutor` class provides comprehensive human task management: +The `HumanExecutor` class provides comprehensive human task management. For a complete method reference, see the [HumanExecutor API Reference](./docs/human-executor-api-reference.md). ```typescript import { HumanExecutor } from "@io-orkes/conductor-javascript"; @@ -1744,7 +1746,7 @@ const templateById = await humanExecutor.getTemplateById("approval_template"); ### TemplateClient -The `TemplateClient` class provides methods for managing human task templates (forms and UI): +The `TemplateClient` class provides methods for managing human task templates (forms and UI). For a complete method reference, see the [TemplateClient API Reference](./docs/template-client-api-reference.md). ```typescript import { TemplateClient } from "@io-orkes/conductor-javascript"; diff --git a/docs/human-executor-api-reference.md b/docs/human-executor-api-reference.md new file mode 100644 index 00000000..367c054b --- /dev/null +++ b/docs/human-executor-api-reference.md @@ -0,0 +1,182 @@ +# HumanExecutor API Reference + +The `HumanExecutor` class provides comprehensive human task management. + +## Constructor + +### `new HumanExecutor(client: ConductorClient)` + +Creates a new `HumanExecutor`. + +**Parameters:** + +- `client` (`ConductorClient`): An instance of `ConductorClient`. + +--- + +## Methods + +### `getTasksByFilter(state: "PENDING" | "ASSIGNED" | "IN_PROGRESS" | "COMPLETED" | "TIMED_OUT", assignee?: string, assigneeType?: "EXTERNAL_USER" | "EXTERNAL_GROUP" | "CONDUCTOR_USER" | "CONDUCTOR_GROUP", claimedBy?: string, taskName?: string, taskInputQuery?: string, taskOutputQuery?: string): Promise` + +Gets human tasks by a set of filter parameters. + +**Parameters:** + +- `state` (`"PENDING" | "ASSIGNED" | "IN_PROGRESS" | "COMPLETED" | "TIMED_OUT"`): The state of the tasks to filter by. +- `assignee` (`string`, optional): The assignee of the tasks. +- `assigneeType` (`"EXTERNAL_USER" | "EXTERNAL_GROUP" | "CONDUCTOR_USER" | "CONDUCTOR_GROUP"`, optional): The type of the assignee. +- `claimedBy` (`string`, optional): The user who has claimed the tasks. +- `taskName` (`string`, optional): The name of the tasks. +- `taskInputQuery` (`string`, optional): A query to filter tasks by their input data. +- `taskOutputQuery` (`string`, optional): A query to filter tasks by their output data. + +**Returns:** + +- `Promise`: An array of human task entries. + +--- + +### `search(searchParams: Partial): Promise` + +Searches for human tasks. + +**Parameters:** + +- `searchParams` (`Partial`): The search parameters. + +**Returns:** + +- `Promise`: An array of human task entries. + +--- + +### `pollSearch(searchParams: Partial, options: PollIntervalOptions = { pollInterval: 100, maxPollTimes: 20 }): Promise` + +Polls for human tasks until a result is returned. + +**Parameters:** + +- `searchParams` (`Partial`): The search parameters. +- `options` (`PollIntervalOptions`, optional): The polling options. + +**Returns:** + +- `Promise`: An array of human task entries. + +--- + +### `getTaskById(taskId: string): Promise` + +Gets a human task by its ID. + +**Parameters:** + +- `taskId` (`string`): The ID of the task. + +**Returns:** + +- `Promise`: The human task entry. + +--- + +### `claimTaskAsExternalUser(taskId: string, assignee: string, options?: Record): Promise` + +Claims a task as an external user. + +**Parameters:** + +- `taskId` (`string`): The ID of the task. +- `assignee` (`string`): The external user to assign the task to. +- `options` (`Record`, optional): Additional options. + +**Returns:** + +- `Promise`: The claimed human task entry. + +--- + +### `claimTaskAsConductorUser(taskId: string, options?: Record): Promise` + +Claims a task as a Conductor user. + +**Parameters:** + +- `taskId` (`string`): The ID of the task. +- `options` (`Record`, optional): Additional options. + +**Returns:** + +- `Promise`: The claimed human task entry. + +--- + +### `releaseTask(taskId: string): Promise` + +Releases a task. + +**Parameters:** + +- `taskId` (`string`): The ID of the task. + +**Returns:** + +- `Promise` + +--- + +### `getTemplateByNameVersion(name: string, version: number): Promise` + +Gets a human task template by name and version. + +**Parameters:** + +- `name` (`string`): The name of the template. +- `version` (`number`): The version of the template. + +**Returns:** + +- `Promise`: The human task template. + +--- + +### `getTemplateById(templateNameVersionOne: string): Promise` + +Gets a human task template by ID (name with version 1). + +**Parameters:** + +- `templateNameVersionOne` (`string`): The name of the template. + +**Returns:** + +- `Promise`: The human task template. + +--- + +### `updateTaskOutput(taskId: string, requestBody: Record>): Promise` + +Updates the output of a task. + +**Parameters:** + +- `taskId` (`string`): The ID of the task. +- `requestBody` (`Record>`): The new output data. + +**Returns:** + +- `Promise` + +--- + +### `completeTask(taskId: string, requestBody: Record> = {}): Promise` + +Completes a task. + +**Parameters:** + +- `taskId` (`string`): The ID of the task. +- `requestBody` (`Record>`, optional): The output data. + +**Returns:** + +- `Promise` diff --git a/docs/metadata-client-api-reference.md b/docs/metadata-client-api-reference.md new file mode 100644 index 00000000..e45101f0 --- /dev/null +++ b/docs/metadata-client-api-reference.md @@ -0,0 +1,88 @@ +# MetadataClient API Reference + +The `MetadataClient` class provides methods for managing task and workflow definitions in Conductor. + +## Constructor + +### `new MetadataClient(client: ConductorClient)` + +Creates a new `MetadataClient`. + +**Parameters:** + +- `client` (`ConductorClient`): An instance of `ConductorClient`. + +--- + +## Methods + +### `unregisterTask(name: string): Promise` + +Unregisters an existing task definition by name. + +**Parameters:** + +- `name` (`string`): The name of the task definition. + +**Returns:** + +- `Promise` + +--- + +### `registerTask(taskDef: TaskDef): Promise` + +Registers a new task definition. + +**Parameters:** + +- `taskDef` (`TaskDef`): The task definition to register. + +**Returns:** + +- `Promise` + +--- + +### `updateTask(taskDef: TaskDef): Promise` + +Updates an existing task definition. + +**Parameters:** + +- `taskDef` (`TaskDef`): The task definition to update. + +**Returns:** + +- `Promise` + +--- + +### `registerWorkflowDef(workflowDef: WorkflowDef, overwrite: boolean = false): Promise` + +Creates or updates a workflow definition. + +**Parameters:** + +- `workflowDef` (`WorkflowDef`): The workflow definition to register. +- `overwrite` (`boolean`, optional): Whether to overwrite an existing workflow definition. Defaults to `false`. + +**Returns:** + +- `Promise` + +--- + +### `unregisterWorkflow(workflowName: string, version: number = 1): Promise` + +Unregisters a workflow definition. + +**Parameters:** + +- `workflowName` (`string`): The name of the workflow to unregister. +- `version` (`number`, optional): The version of the workflow to unregister. Defaults to `1`. + +**Returns:** + +- `Promise` + diff --git a/docs/scheduler-client-api-reference.md b/docs/scheduler-client-api-reference.md new file mode 100644 index 00000000..0069f570 --- /dev/null +++ b/docs/scheduler-client-api-reference.md @@ -0,0 +1,164 @@ +# SchedulerClient API Reference + +The `SchedulerClient` manages workflow scheduling and provides methods for creating, managing, and monitoring scheduled workflows. + +## Constructor + +### `new SchedulerClient(client: ConductorClient)` + +Creates a new `SchedulerClient`. + +**Parameters:** + +- `client` (`ConductorClient`): An instance of `ConductorClient`. + +--- + +## Methods + +### `saveSchedule(param: SaveScheduleRequest): Promise` + +Creates or updates a schedule for a specified workflow. + +**Parameters:** + +- `param` (`SaveScheduleRequest`): The request to save a schedule. + +**Returns:** + +- `Promise` + +--- + +### `search(start: number, size: number, sort: string = "", freeText: string, query: string): Promise` + +Searches for scheduler executions. + +**Parameters:** + +- `start` (`number`): The starting offset. +- `size` (`number`): The number of results to return. +- `sort` (`string`, optional): The sort order. +- `freeText` (`string`): The free text to search for. +- `query` (`string`): The search query. + +**Returns:** + +- `Promise`: The search results. + +--- + +### `getSchedule(name: string): Promise` + +Gets an existing schedule by name. + +**Parameters:** + +- `name` (`string`): The name of the schedule. + +**Returns:** + +- `Promise`: The schedule. + +--- + +### `pauseSchedule(name: string): Promise` + +Pauses an existing schedule by name. + +**Parameters:** + +- `name` (`string`): The name of the schedule. + +**Returns:** + +- `Promise` + +--- + +### `resumeSchedule(name: string): Promise` + +Resumes a paused schedule by name. + +**Parameters:** + +- `name` (`string`): The name of the schedule. + +**Returns:** + +- `Promise` + +--- + +### `deleteSchedule(name: string): Promise` + +Deletes an existing schedule by name. + +**Parameters:** + +- `name` (`string`): The name of the schedule. + +**Returns:** + +- `Promise` + +--- + +### `getAllSchedules(workflowName?: string): Promise>` + +Gets all existing workflow schedules, optionally filtering by workflow name. + +**Parameters:** + +- `workflowName` (`string`, optional): The name of the workflow. + +**Returns:** + +- `Promise>`: An array of workflow schedules. + +--- + +### `getNextFewSchedules(cronExpression: string, scheduleStartTime?: number, scheduleEndTime?: number, limit: number = 3): Promise>` + +Gets a list of the next execution times for a schedule. + +**Parameters:** + +- `cronExpression` (`string`): The cron expression for the schedule. +- `scheduleStartTime` (`number`, optional): The start time for the schedule. +- `scheduleEndTime` (`number`, optional): The end time for the schedule. +- `limit` (`number`, optional): The number of execution times to return. Defaults to 3. + +**Returns:** + +- `Promise>`: An array of the next execution times. + +--- + +### `pauseAllSchedules(): Promise` + +Pauses all scheduling in the Conductor server instance. + +**Returns:** + +- `Promise` + +--- + +### `requeueAllExecutionRecords(): Promise` + +Requeues all execution records. + +**Returns:** + +- `Promise` + +--- + +### `resumeAllSchedules(): Promise` + +Resumes all scheduling in the Conductor server instance. + +**Returns:** + +- `Promise` diff --git a/docs/service-registry-client-api-reference.md b/docs/service-registry-client-api-reference.md new file mode 100644 index 00000000..c161c986 --- /dev/null +++ b/docs/service-registry-client-api-reference.md @@ -0,0 +1,217 @@ +# ServiceRegistryClient API Reference + +The `ServiceRegistryClient` manages service registrations and circuit breakers. + +## Constructor + +### `new ServiceRegistryClient(client: ConductorClient)` + +Creates a new `ServiceRegistryClient`. + +**Parameters:** + +- `client` (`ConductorClient`): An instance of `ConductorClient`. + +--- + +## Methods + +### `getRegisteredServices(): Promise` + +Retrieves all registered services. + +**Returns:** + +- `Promise`: An array of all registered services. + +--- + +### `removeService(name: string): Promise` + +Removes a service by name. + +**Parameters:** + +- `name` (`string`): The name of the service to remove. + +**Returns:** + +- `Promise` + +--- + +### `getService(name: string): Promise` + +Gets a service by name. + +**Parameters:** + +- `name` (`string`): The name of the service to retrieve. + +**Returns:** + +- `Promise`: The requested service registry. + +--- + +### `openCircuitBreaker(name: string): Promise` + +Opens the circuit breaker for a service. + +**Parameters:** + +- `name` (`string`): The name of the service. + +**Returns:** + +- `Promise`: A response with the circuit breaker status. + +--- + +### `closeCircuitBreaker(name: string): Promise` + +Closes the circuit breaker for a service. + +**Parameters:** + +- `name` (`string`): The name of the service. + +**Returns:** + +- `Promise`: A response with the circuit breaker status. + +--- + +### `getCircuitBreakerStatus(name: string): Promise` + +Gets the circuit breaker status for a service. + +**Parameters:** + +- `name` (`string`): The name of the service. + +**Returns:** + +- `Promise`: A response with the circuit breaker status. + +--- + +### `addOrUpdateService(serviceRegistry: ServiceRegistry): Promise` + +Adds or updates a service registry. + +**Parameters:** + +- `serviceRegistry` (`ServiceRegistry`): The service registry to add or update. + +**Returns:** + +- `Promise` + +--- + +### `addOrUpdateServiceMethod(registryName: string, method: ServiceMethod): Promise` + +Adds or updates a service method. + +**Parameters:** + +- `registryName` (`string`): The name of the registry. +- `method` (`ServiceMethod`): The service method to add or update. + +**Returns:** + +- `Promise` + +--- + +### `removeMethod(registryName: string, serviceName: string, method: string, methodType: string): Promise` + +Removes a service method. + +**Parameters:** + +- `registryName` (`string`): The name of the registry. +- `serviceName` (`string`): The name of the service. +- `method` (`string`): The name of the method. +- `methodType` (`string`): The type of the method. + +**Returns:** + +- `Promise` + +--- + +### `getProtoData(registryName: string, filename: string): Promise` + +Gets proto data. + +**Parameters:** + +- `registryName` (`string`): The name of the registry. +- `filename` (`string`): The name of the proto file. + +**Returns:** + +- `Promise`: The proto file data as a `Blob`. + +--- + +### `setProtoData(registryName: string, filename: string, data: Blob): Promise` + +Sets proto data. + +**Parameters:** + +- `registryName` (`string`): The name of the registry. +- `filename` (`string`): The name of the proto file. +- `data` (`Blob`): The proto file data. + +**Returns:** + +- `Promise` + +--- + +### `deleteProto(registryName: string, filename: string): Promise` + +Deletes a proto file. + +**Parameters:** + +- `registryName` (`string`): The name of the registry. +- `filename` (`string`): The name of the proto file. + +**Returns:** + +- `Promise` + +--- + +### `getAllProtos(registryName: string): Promise` + +Gets all proto files for a registry. + +**Parameters:** + +- `registryName` (`string`): The name of the registry. + +**Returns:** + +- `Promise`: A list of proto registry entries. + +--- + +### `discover(name: string, create: boolean = false): Promise` + +Discovers service methods. + +**Parameters:** + +- `name` (`string`): The name of the service. +- `create` (`boolean`, optional): Whether to create the discovered methods. Defaults to `false`. + +**Returns:** + +- `Promise`: The discovered service methods. + diff --git a/docs/task-client-api-reference.md b/docs/task-client-api-reference.md new file mode 100644 index 00000000..d717f0c8 --- /dev/null +++ b/docs/task-client-api-reference.md @@ -0,0 +1,64 @@ +# TaskClient API Reference + +The `TaskClient` provides capabilities for monitoring and debugging tasks within your workflow executions. + +## Constructor + +### `new TaskClient(client: ConductorClient)` + +Creates a new TaskClient. + +**Parameters:** + +- `client` (`ConductorClient`): An instance of `ConductorClient`. + +--- + +## Methods + +### `search(start: number, size: number, sort: string = "", freeText: string, query: string): Promise` + +Searches for tasks. + +**Parameters:** + +- `start` (`number`): The starting offset. +- `size` (`number`): The number of results to return. +- `sort` (`string`, optional): The sort order. +- `freeText` (`string`): The free text to search for. +- `query` (`string`): The search query. + +**Returns:** + +- `Promise`: The search results. + +--- + +### `getTask(taskId: string): Promise` + +Gets a task by its ID. + +**Parameters:** + +- `taskId` (`string`): The ID of the task. + +**Returns:** + +- `Promise`: The task. + +--- + +### `updateTaskResult(workflowId: string, taskReferenceName: string, status: TaskResultStatus, outputData: Record): Promise` + +Updates the result of a task. + +**Parameters:** + +- `workflowId` (`string`): The ID of the workflow instance. +- `taskReferenceName` (`string`): The reference name of the task. +- `status` (`TaskResultStatus`): The new status of the task. +- `outputData` (`Record`): The output data of the task. + +**Returns:** + +- `Promise`: The result of the task update. diff --git a/docs/task-manager-api-reference.md b/docs/task-manager-api-reference.md new file mode 100644 index 00000000..f6459a88 --- /dev/null +++ b/docs/task-manager-api-reference.md @@ -0,0 +1,64 @@ +# TaskManager API Reference + +The `TaskManager` is responsible for initializing and managing the runners that poll and work different task queues. + +## Constructor + +### `new TaskManager(client: ConductorClient, workers: Array, config: TaskManagerConfig = {})` + +Creates a new TaskManager. + +**Parameters:** + +- `client` (`ConductorClient`): An instance of `ConductorClient`. +- `workers` (`Array`): An array of `ConductorWorker` instances. +- `config` (`TaskManagerConfig`, optional): Configuration for the `TaskManager`. + +--- + +## Properties + +### `isPolling: boolean` + +Returns whether the `TaskManager` is currently polling for tasks. + +--- + +## Methods + +### `updatePollingOptionForWorker(workerTaskDefName: string, options: Partial): void` + +Updates the polling options for a specific worker. + +**Parameters:** + +- `workerTaskDefName` (`string`): The task definition name of the worker. +- `options` (`Partial`): The new polling options. + +--- + +### `updatePollingOptions(options: Partial): void` + +Updates the polling options for all workers. + +**Parameters:** + +- `options` (`Partial`): The new polling options. + +--- + +### `startPolling(): void` + +Starts polling for tasks for all workers. + +--- + +### `stopPolling(): Promise` + +Stops polling for tasks for all workers. + +--- + +### `sanityCheck(): void` + +Performs a sanity check on the workers, ensuring there are no duplicates and that at least one worker is present. Throws an error if the check fails. diff --git a/docs/template-client-api-reference.md b/docs/template-client-api-reference.md new file mode 100644 index 00000000..6e41f7a7 --- /dev/null +++ b/docs/template-client-api-reference.md @@ -0,0 +1,30 @@ +# TemplateClient API Reference + +The `TemplateClient` class provides methods for managing human task templates (forms and UI). + +## Constructor + +### `new TemplateClient(client: ConductorClient)` + +Creates a new `TemplateClient`. + +**Parameters:** + +- `client` (`ConductorClient`): An instance of `ConductorClient`. + +--- + +## Methods + +### `registerTemplate(template: HumanTaskTemplate, asNewVersion: boolean = false): Promise` + +Registers a new human task template. + +**Parameters:** + +- `template` (`HumanTaskTemplate`): The template to register. +- `asNewVersion` (`boolean`, optional): Whether to register the template as a new version. Defaults to `false`. + +**Returns:** + +- `Promise`: The registered template. diff --git a/docs/workflow-executor-api-reference.md b/docs/workflow-executor-api-reference.md new file mode 100644 index 00000000..ba1310b0 --- /dev/null +++ b/docs/workflow-executor-api-reference.md @@ -0,0 +1,380 @@ +# WorkflowExecutor API Reference + +The `WorkflowExecutor` class is your main interface for managing workflows. It provides methods to register, start, monitor, and control workflow execution. + +## Constructor + +### `new WorkflowExecutor(client: ConductorClient)` + +Creates a new WorkflowExecutor. + +**Parameters:** + +- `client` (`ConductorClient`): An instance of `ConductorClient`. + +--- + +## Methods + +### `registerWorkflow(override: boolean, workflow: WorkflowDef): Promise` + +Registers a workflow definition. + +**Parameters:** + +- `override` (`boolean`): Whether to override the existing workflow definition. +- `workflow` (`WorkflowDef`): The workflow definition. + +**Returns:** + +- `Promise` + +--- + +### `startWorkflow(workflowRequest: StartWorkflowRequest): Promise` + +Starts a new workflow execution. + +**Parameters:** + +- `workflowRequest` (`StartWorkflowRequest`): The request to start a workflow. + +**Returns:** + +- `Promise`: The ID of the workflow instance. + +--- + +### `executeWorkflow(workflowRequest: StartWorkflowRequest, name: string, version: number, requestId: string, waitUntilTaskRef?: string): Promise` +### `executeWorkflow(workflowRequest: StartWorkflowRequest, name: string, version: number, requestId: string, waitUntilTaskRef: string, waitForSeconds: number, consistency: Consistency, returnStrategy: ReturnStrategy): Promise` + +Executes a workflow synchronously and waits for completion. Can return different responses based on the provided parameters. + +**Parameters:** + +- `workflowRequest` (`StartWorkflowRequest`): The request to start a workflow. +- `name` (`string`): The name of the workflow. +- `version` (`number`): The version of the workflow. +- `requestId` (`string`): A unique ID for the request. +- `waitUntilTaskRef` (`string`, optional): The reference name of the task to wait for. +- `waitForSeconds` (`number`, optional): The number of seconds to wait for the task. +- `consistency` (`Consistency`, optional): The consistency level for the read operations. +- `returnStrategy` (`ReturnStrategy`, optional): The strategy for what data to return. + +**Returns:** + +- `Promise`: A `WorkflowRun` object or a `SignalResponse` object. + +--- + +### `startWorkflows(workflowsRequest: StartWorkflowRequest[]): Promise[]` + +Starts multiple workflows at once. + +**Parameters:** + +- `workflowsRequest` (`StartWorkflowRequest[]`): An array of workflow start requests. + +**Returns:** + +- `Promise[]`: An array of promises that resolve to the workflow instance IDs. + +--- + +### `goBackToTask(workflowInstanceId: string, taskFinderPredicate: TaskFinderPredicate, rerunWorkflowRequestOverrides: Partial = {}): Promise` + +Reruns a workflow from a specific task. + +**Parameters:** + +- `workflowInstanceId` (`string`): The ID of the workflow instance. +- `taskFinderPredicate` (`TaskFinderPredicate`): A function to find the task to rerun from. +- `rerunWorkflowRequestOverrides` (`Partial`, optional): Overrides for the rerun request. + +**Returns:** + +- `Promise` + +--- + +### `goBackToFirstTaskMatchingType(workflowInstanceId: string, taskType: string): Promise` + +Reruns a workflow from the first task of a specific type. + +**Parameters:** + +- `workflowInstanceId` (`string`): The ID of the workflow instance. +- `taskType` (`string`): The type of the task to rerun from. + +**Returns:** + +- `Promise` + +--- + +### `getWorkflow(workflowInstanceId: string, includeTasks: boolean, retry: number = 0): Promise` + +Gets the execution status of a workflow. + +**Parameters:** + +- `workflowInstanceId` (`string`): The ID of the workflow instance. +- `includeTasks` (`boolean`): Whether to include the tasks in the response. +- `retry` (`number`, optional): The number of times to retry on failure. + +**Returns:** + +- `Promise`: The workflow execution status. + +--- + +### `getWorkflowStatus(workflowInstanceId: string, includeOutput: boolean, includeVariables: boolean): Promise` + +Gets a summary of the current workflow status. + +**Parameters:** + +- `workflowInstanceId` (`string`): The ID of the workflow instance. +- `includeOutput` (`boolean`): Whether to include the output in the response. +- `includeVariables` (`boolean`): Whether to include the variables in the response. + +**Returns:** + +- `Promise`: The workflow status summary. + +--- + +### `getExecution(workflowInstanceId: string, includeTasks: boolean = true): Promise` + +Gets the execution status of a workflow, including tasks by default. + +**Parameters:** + +- `workflowInstanceId` (`string`): The ID of the workflow instance. +- `includeTasks` (`boolean`, optional): Whether to include the tasks in the response. Defaults to `true`. + +**Returns:** + +- `Promise`: The workflow execution status. + +--- + +### `pause(workflowInstanceId: string): Promise` + +Pauses a running workflow. + +**Parameters:** + +- `workflowInstanceId` (`string`): The ID of the workflow instance. + +**Returns:** + +- `Promise` + +--- + +### `reRun(workflowInstanceId: string, rerunWorkflowRequest: Partial = {}): Promise` + +Reruns a workflow with new parameters. + +**Parameters:** + +- `workflowInstanceId` (`string`): The ID of the workflow instance. +- `rerunWorkflowRequest` (`Partial`, optional): Overrides for the rerun request. + +**Returns:** + +- `Promise`: The ID of the new workflow instance. + +--- + +### `restart(workflowInstanceId: string, useLatestDefinitions: boolean): Promise` + +Restarts a workflow. + +**Parameters:** + +- `workflowInstanceId` (`string`): The ID of the workflow instance. +- `useLatestDefinitions` (`boolean`): Whether to use the latest workflow definition. + +**Returns:** + +- `Promise` + +--- + +### `resume(workflowInstanceId: string): Promise` + +Resumes a paused workflow. + +**Parameters:** + +- `workflowInstanceId` (`string`): The ID of the workflow instance. + +**Returns:** + +- `Promise` + +--- + +### `retry(workflowInstanceId: string, resumeSubworkflowTasks: boolean): Promise` + +Retries a workflow from the last failing task. + +**Parameters:** + +- `workflowInstanceId` (`string`): The ID of the workflow instance. +- `resumeSubworkflowTasks` (`boolean`): Whether to resume tasks in sub-workflows. + +**Returns:** + +- `Promise` + +--- + +### `search(start: number, size: number, query: string, freeText: string, sort: string = "", skipCache: boolean = false): Promise` + +Searches for workflows. + +**Parameters:** + +- `start` (`number`): The starting offset. +- `size` (`number`): The number of results to return. +- `query` (`string`): The search query. +- `freeText` (`string`): The free text to search for. +- `sort` (`string`, optional): The sort order. +- `skipCache` (`boolean`, optional): Whether to skip the cache. + +**Returns:** + +- `Promise`: The search results. + +--- + +### `skipTasksFromWorkflow(workflowInstanceId: string, taskReferenceName: string, skipTaskRequest: Partial): Promise` + +Skips a task in a running workflow. + +**Parameters:** + +- `workflowInstanceId` (`string`): The ID of the workflow instance. +- `taskReferenceName` (`string`): The reference name of the task to skip. +- `skipTaskRequest` (`Partial`): The request to skip the task. + +**Returns:** + +- `Promise` + +--- + +### `terminate(workflowInstanceId: string, reason: string): Promise` + +Terminates a running workflow. + +**Parameters:** + +- `workflowInstanceId` (`string`): The ID of the workflow instance. +- `reason` (`string`): The reason for termination. + +**Returns:** + +- `Promise` + +--- + +### `updateTask(taskId: string, workflowInstanceId: string, taskStatus: TaskResultStatus, outputData: Record): Promise` + +Updates a task by its ID. + +**Parameters:** + +- `taskId` (`string`): The ID of the task. +- `workflowInstanceId` (`string`): The ID of the workflow instance. +- `taskStatus` (`TaskResultStatus`): The new status of the task. +- `outputData` (`Record`): The output data of the task. + +**Returns:** + +- `Promise` + +--- + +### `updateTaskByRefName(taskReferenceName: string, workflowInstanceId: string, status: TaskResultStatus, taskOutput: Record): Promise` + +Updates a task by its reference name. + +**Parameters:** + +- `taskReferenceName` (`string`): The reference name of the task. +- `workflowInstanceId` (`string`): The ID of the workflow instance. +- `status` (`TaskResultStatus`): The new status of the task. +- `taskOutput` (`Record`): The output data of the task. + +**Returns:** + +- `Promise` + +--- + +### `getTask(taskId: string): Promise` + +Gets a task by its ID. + +**Parameters:** + +- `taskId` (`string`): The ID of the task. + +**Returns:** + +- `Promise`: The task. + +--- + +### `updateTaskSync(taskReferenceName: string, workflowInstanceId: string, status: TaskResultStatusEnum, taskOutput: Record, workerId?: string): Promise` + +Updates a task by its reference name synchronously and returns the complete workflow. + +**Parameters:** + +- `taskReferenceName` (`string`): The reference name of the task. +- `workflowInstanceId` (`string`): The ID of the workflow instance. +- `status` (`TaskResultStatusEnum`): The new status of the task. +- `taskOutput` (`Record`): The output data of the task. +- `workerId` (`string`, optional): The ID of the worker. + +**Returns:** + +- `Promise`: The updated workflow. + +--- + +### `signal(workflowInstanceId: string, status: TaskResultStatusEnum, taskOutput: Record, returnStrategy: ReturnStrategy = ReturnStrategy.TARGET_WORKFLOW): Promise` + +Signals a workflow task and returns data based on the specified return strategy. + +**Parameters:** + +- `workflowInstanceId` (`string`): The ID of the workflow instance to signal. +- `status` (`TaskResultStatusEnum`): The task status to set. +- `taskOutput` (`Record`): The output data for the task. +- `returnStrategy` (`ReturnStrategy`, optional): The strategy for what data to return. Defaults to `TARGET_WORKFLOW`. + +**Returns:** + +- `Promise`: The response from the signal. + +--- + +### `signalAsync(workflowInstanceId: string, status: TaskResultStatusEnum, taskOutput: Record): Promise` + +Signals a workflow task asynchronously (fire-and-forget). + +**Parameters:** + +- `workflowInstanceId` (`string`): The ID of the workflow instance to signal. +- `status` (`TaskResultStatusEnum`): The task status to set. +- `taskOutput` (`Record`): The output data for the task. + +**Returns:** + +- `Promise` From 9823f4d28f41d22651ac8ebb6abbaf6bb3140c6b Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Mon, 6 Oct 2025 17:12:15 +0300 Subject: [PATCH 42/60] add types for full api reference --- docs/human-executor-api-reference.md | 90 ++++++++ docs/metadata-client-api-reference.md | 32 +++ docs/scheduler-client-api-reference.md | 52 +++++ docs/service-registry-client-api-reference.md | 77 +++++++ docs/task-client-api-reference.md | 31 +++ docs/task-manager-api-reference.md | 34 +++ docs/template-client-api-reference.md | 16 ++ docs/workflow-executor-api-reference.md | 196 ++++++++++++++++++ 8 files changed, 528 insertions(+) diff --git a/docs/human-executor-api-reference.md b/docs/human-executor-api-reference.md index 367c054b..0689cd8c 100644 --- a/docs/human-executor-api-reference.md +++ b/docs/human-executor-api-reference.md @@ -180,3 +180,93 @@ Completes a task. **Returns:** - `Promise` + +--- + +## Type Definitions + +### `HumanTaskEntry` +| Property | Type | Description | +| --- | --- | --- | +| `assignee` | `HumanTaskUser` | The user assigned to the task. | +| `claimant`| `HumanTaskUser` | The user who has claimed the task. | +| `createdBy` | `string` | The user who created the task. | +| `createdOn` | `number` | The time the task was created. | +| `definitionName`| `string` | The name of the task definition. | +| `displayName` | `string` | The display name of the task. | +| `humanTaskDef`| `HumanTaskDefinition` | The task definition. | +| `input` | `Record` | The input data for the task. | +| `output`| `Record` | The output data for the task. | +| `state` | `'PENDING' \| 'ASSIGNED' \| 'IN_PROGRESS' \| 'COMPLETED' \| 'TIMED_OUT' \| 'DELETED'` | The state of the task. | +| `taskId`| `string` | The ID of the task. | +| `taskRefName` | `string` | The reference name of the task. | +| `updatedBy` | `string` | The user who last updated the task. | +| `updatedOn` | `number` | The time the task was last updated. | +| `workflowId`| `string` | The ID of the workflow instance. | +| `workflowName`| `string` | The name of the workflow. | + +### `HumanTaskUser` +| Property | Type | Description | +| --- | --- | --- | +| `user` | `string` | The user or group ID. | +| `userType`| `'EXTERNAL_USER' \| 'EXTERNAL_GROUP' \| 'CONDUCTOR_USER' \| 'CONDUCTOR_GROUP'` | The type of the user. | + +### `HumanTaskDefinition` +| Property | Type | Description | +| --- | --- | --- | +| `assignmentCompletionStrategy` | `'LEAVE_OPEN' \| 'TERMINATE'` | The strategy for completing the assignment. | +| `assignments` | `HumanTaskAssignment[]` | A list of assignments for the task. | +| `taskTriggers` | `HumanTaskTrigger[]` | A list of triggers for the task. | +| `userFormTemplate` | `UserFormTemplate` | The user form template for the task. | + +### `HumanTaskAssignment` +| Property | Type | Description | +| --- | --- | --- | +| `assignee` | `HumanTaskUser` | The user or group assigned to the task. | +| `slaMinutes` | `number` | The service level agreement in minutes. | + +### `HumanTaskTrigger` +| Property | Type | Description | +| --- | --- | --- | +| `startWorkflowRequest` | `StartWorkflowRequest` | The request to start a workflow. | +| `triggerType` | `'ASSIGNEE_CHANGED' \| 'PENDING' \| 'IN_PROGRESS' \| 'ASSIGNED' \| 'COMPLETED' \| 'TIMED_OUT'` | The type of the trigger. | + +### `UserFormTemplate` +| Property | Type | Description | +| --- | --- | --- | +| `name` | `string` | The name of the template. | +| `version` | `number` | The version of the template. | + +### `StartWorkflowRequest` +| Property | Type | Description | +| --- | --- | --- | +| `name` | `string` | The name of the workflow. | +| `version` | `number` | The version of the workflow. | +| `correlationId` | `string` | The correlation ID of the workflow. | +| `input` | `Record` | The input data for the workflow. | +| `taskToDomain` | `Record` | A map of task reference names to domains. | +| `workflowDef` | `WorkflowDef` | The workflow definition. | +| `externalInputPayloadStoragePath`| `string` | The path to the external input payload storage. | +| `idempotencyKey` | `string` | The idempotency key for the workflow. | +| `idempotencyStrategy` | `'FAIL' \| 'RETURN_EXISTING'` | The idempotency strategy for the workflow. | +| `priority` | `number` | The priority of the workflow. | +| `createdBy` | `string` | The user who created the workflow. | + +### `HumanTaskSearch` +| Property | Type | Description | +| --- | --- | --- | +| `size` | `number` | The number of results to return. | +| `states` | `string[]` | A list of states to filter by. | +| `taskInputQuery` | `string` | A query to filter tasks by their input data. | +| `taskOutputQuery` | `string` | A query to filter tasks by their output data. | +| `definitionNames` | `string[]` | A list of task definition names to filter by. | +| `taskRefNames` | `string[]` | A list of task reference names to filter by. | +| `claimants` | `HumanTaskUser[]` | A list of claimants to filter by. | +| `assignees` | `HumanTaskUser[]` | A list of assignees to filter by. | +| `start` | `number` | The starting offset. | + +### `PollIntervalOptions` +| Property | Type | Description | +| --- | --- | --- | +| `pollInterval` | `number` | The interval in milliseconds to poll for tasks. | +| `maxPollTimes` | `number` | The maximum number of times to poll for tasks. | diff --git a/docs/metadata-client-api-reference.md b/docs/metadata-client-api-reference.md index e45101f0..79132152 100644 --- a/docs/metadata-client-api-reference.md +++ b/docs/metadata-client-api-reference.md @@ -86,3 +86,35 @@ Unregisters a workflow definition. - `Promise` +--- + +## Type Definitions + +### `TaskDef` +| Property | Type | Description | +| --- | --- | --- | +| `ownerApp` | `string` | The owner app of the task. | +| `createTime` | `number` | The creation time of the task. | +| `updateTime` | `number` | The last update time of the task. | +| `createdBy` | `string` | The user who created the task. | +| `updatedBy` | `string` | The user who last updated the task. | +| `name` | `string` | The name of the task. | +| `description` | `string` | The description of the task. | +| `retryCount` | `number` | The retry count. | +| `timeoutSeconds` | `number` | The timeout in seconds. | +| `inputKeys` | `string[]` | The input keys of the task. | +| `outputKeys` | `string[]` | The output keys of the task. | +| `timeoutPolicy` | `'RETRY' \| 'TIME_OUT_WF' \| 'ALERT_ONLY'` | The timeout policy of the task. | +| `retryLogic` | `'FIXED' \| 'EXPONENTIAL_BACKOFF' \| 'LINEAR_BACKOFF'` | The retry logic of the task. | +| `retryDelaySeconds` | `number` | The retry delay in seconds. | +| `responseTimeoutSeconds` | `number` | The response timeout in seconds. | +| `concurrentExecLimit` | `number` | The concurrent execution limit. | +| `inputTemplate` | `Record` | The input template of the task. | +| `rateLimitPerFrequency` | `number` | The rate limit per frequency. | +| `rateLimitFrequencyInSeconds` | `number` | The rate limit frequency in seconds. | +| `isolationGroupId` | `string` | The isolation group ID. | +| `executionNameSpace` | `string` | The execution namespace. | +| `ownerEmail` | `string` | The owner email of the task. | +| `pollTimeoutSeconds` | `number` | The poll timeout in seconds. | +| `backoffScaleFactor` | `number` | The backoff scale factor. | + diff --git a/docs/scheduler-client-api-reference.md b/docs/scheduler-client-api-reference.md index 0069f570..2c0ea5a0 100644 --- a/docs/scheduler-client-api-reference.md +++ b/docs/scheduler-client-api-reference.md @@ -162,3 +162,55 @@ Resumes all scheduling in the Conductor server instance. **Returns:** - `Promise` + +--- + +## Type Definitions + +### `SaveScheduleRequest` +| Property | Type | Description | +| --- | --- | --- | +| `name` | `string` | The name of the schedule. | +| `cronExpression` | `string` | The cron expression for the schedule. | +| `runCatchupScheduleInstances` | `boolean` | Whether to run catch-up schedule instances. | +| `paused` | `boolean` | Whether the schedule is paused. | +| `startWorkflowRequest` | `StartWorkflowRequest` | The request to start a workflow. | +| `createdBy` | `string` | The user who created the schedule. | +| `updatedBy` | `string` | The user who last updated the schedule. | +| `scheduleStartTime` | `number` | The start time for the schedule. | +| `scheduleEndTime` | `number` | The end time for the schedule. | + +### `SearchResultWorkflowScheduleExecutionModel` +| Property | Type | Description | +| --- | --- | --- | +| `totalHits` | `number` | The total number of hits. | +| `results` | `WorkflowScheduleExecutionModel[]` | The search results. | + +### `WorkflowSchedule` +| Property | Type | Description | +| --- | --- | --- | +| `name` | `string` | The name of the schedule. | +| `cronExpression` | `string` | The cron expression for the schedule. | +| `runCatchupScheduleInstances` | `boolean` | Whether to run catch-up schedule instances. | +| `paused` | `boolean` | Whether the schedule is paused. | +| `startWorkflowRequest` | `StartWorkflowRequest` | The request to start a workflow. | +| `scheduleStartTime` | `number` | The start time for the schedule. | +| `scheduleEndTime` | `number` | The end time for the schedule. | +| `createTime` | `number` | The creation time of the schedule. | +| `updatedTime` | `number` | The last update time of the schedule. | +| `createdBy` | `string` | The user who created the schedule. | +| `updatedBy` | `string` | The user who last updated the schedule. | + +### `WorkflowScheduleExecutionModel` +| Property | Type | Description | +| --- | --- | --- | +| `executionId` | `string` | The ID of the execution. | +| `scheduleName` | `string` | The name of the schedule. | +| `scheduledTime` | `number` | The scheduled time of the execution. | +| `executionTime` | `number` | The execution time. | +| `workflowName` | `string` | The name of the workflow. | +| `workflowId` | `string` | The ID of the workflow instance. | +| `reason` | `string` | The reason for the execution status. | +| `stackTrace` | `string` | The stack trace for a failed execution. | +| `startWorkflowRequest` | `StartWorkflowRequest` | The request to start a workflow. | +| `state` | `'POLLED' \| 'FAILED' \| 'EXECUTED'` | The state of the execution. | diff --git a/docs/service-registry-client-api-reference.md b/docs/service-registry-client-api-reference.md index c161c986..386da62f 100644 --- a/docs/service-registry-client-api-reference.md +++ b/docs/service-registry-client-api-reference.md @@ -215,3 +215,80 @@ Discovers service methods. - `Promise`: The discovered service methods. +--- + +## Type Definitions + +### `ServiceRegistry` +| Property | Type | Description | +| --- | --- | --- | +| `name` | `string` | The name of the service. | +| `type` | `ServiceType` | The type of the service. | +| `serviceURI` | `string` | The URI of the service. | +| `methods` | `ServiceMethod[]` | The methods of the service. | +| `requestParams` | `RequestParam[]` | The request parameters of the service. | +| `config` | `ServiceRegistryConfig` | The configuration of the service. | + +### `ServiceType` +`ServiceType` is an enum that can be one of the following values: `'HTTP'`, `'gRPC'`. + +### `ServiceMethod` +| Property | Type | Description | +| --- | --- | --- | +| `id` | `number` | The ID of the method. | +| `operationName` | `string` | The name of the operation. | +| `methodName` | `string` | The name of the method. | +| `methodType` | `string` | The type of the method. | +| `inputType` | `string` | The input type of the method. | +| `outputType` | `string` | The output type of the method. | +| `requestParams` | `RequestParam[]` | The request parameters of the method. | +| `exampleInput` | `Record` | An example input for the method. | + +### `RequestParam` +| Property | Type | Description | +| --- | --- | --- | +| `name` | `string` | The name of the parameter. | +| `type` | `string` | The type of the parameter. | +| `required` | `boolean` | Whether the parameter is required. | +| `schema` | `RequestParamSchema` | The schema of the parameter. | + +### `RequestParamSchema` +| Property | Type | Description | +| --- | --- | --- | +| `type` | `string` | The type of the schema. | +| `format` | `string` | The format of the schema. | +| `defaultValue` | `any` | The default value of the schema. | + +### `ServiceRegistryConfig` +| Property | Type | Description | +| --- | --- | --- | +| `circuitBreakerConfig` | `OrkesCircuitBreakerConfig` | The circuit breaker configuration. | + +### `OrkesCircuitBreakerConfig` +| Property | Type | Description | +| --- | --- | --- | +| `failureRateThreshold` | `number` | The failure rate threshold. | +| `slidingWindowSize` | `number` | The sliding window size. | +| `minimumNumberOfCalls` | `number` | The minimum number of calls. | +| `waitDurationInOpenState` | `number` | The wait duration in the open state. | +| `permittedNumberOfCallsInHalfOpenState`| `number` | The permitted number of calls in the half-open state. | +| `slowCallRateThreshold` | `number` | The slow call rate threshold. | +| `slowCallDurationThreshold` | `number` | The slow call duration threshold. | +| `automaticTransitionFromOpenToHalfOpenEnabled` | `boolean` | Whether automatic transition from open to half-open is enabled. | +| `maxWaitDurationInHalfOpenState` | `number` | The maximum wait duration in the half-open state. | + +### `CircuitBreakerTransitionResponse` +| Property | Type | Description | +| --- | --- | --- | +| `service` | `string` | The name of the service. | +| `previousState` | `string` | The previous state of the circuit breaker. | +| `currentState` | `string` | The current state of the circuit breaker. | +| `transitionTimestamp` | `number` | The timestamp of the transition. | +| `message` | `string` | The transition message. | + +### `ProtoRegistryEntry` +| Property | Type | Description | +| --- | --- | --- | +| `filename` | `string` | The name of the proto file. | +| `lastUpdated` | `number` | The last update time of the proto file. | + diff --git a/docs/task-client-api-reference.md b/docs/task-client-api-reference.md index d717f0c8..7ec8431c 100644 --- a/docs/task-client-api-reference.md +++ b/docs/task-client-api-reference.md @@ -62,3 +62,34 @@ Updates the result of a task. **Returns:** - `Promise`: The result of the task update. + +--- + +## Type Definitions + +### `SearchResultTask` +| Property | Type | Description | +| --- | --- | --- | +| `totalHits` | `number` | The total number of hits. | +| `results` | `Task[]` | The search results. | + +### `TaskResult` +| Property | Type | Description | +| --- | --- | --- | +| `workflowInstanceId` | `string` | The ID of the workflow instance. | +| `taskId` | `string` | The ID of the task. | +| `reasonForIncompletion` | `string` | The reason for incompletion. | +| `callbackAfterSeconds` | `number` | The callback after seconds. | +| `workerId` | `string` | The ID of the worker. | +| `status` | `'IN_PROGRESS' \| 'FAILED' \| 'FAILED_WITH_TERMINAL_ERROR' \| 'COMPLETED'` | The status of the task. | +| `outputData` | `Record` | The output data of the task. | +| `logs` | `TaskExecLog[]` | The execution logs of the task. | +| `externalOutputPayloadStoragePath`| `string` | The path to the external output payload storage. | +| `subWorkflowId` | `string` | The ID of the sub-workflow. | + +### `TaskExecLog` +| Property | Type | Description | +| --- | --- | --- | +| `log` | `string` | The log message. | +| `taskId` | `string` | The ID of the task. | +| `createdTime` | `number` | The creation time of the log. | diff --git a/docs/task-manager-api-reference.md b/docs/task-manager-api-reference.md index f6459a88..c5b19e0f 100644 --- a/docs/task-manager-api-reference.md +++ b/docs/task-manager-api-reference.md @@ -62,3 +62,37 @@ Stops polling for tasks for all workers. ### `sanityCheck(): void` Performs a sanity check on the workers, ensuring there are no duplicates and that at least one worker is present. Throws an error if the check fails. + +--- + +## Type Definitions + +### `ConductorWorker` +| Property | Type | Description | +| --- | --- | --- | +| `taskDefName` | `string` | The name of the task definition. | +| `execute` | `(task: Task) => Promise>` | The function that executes the task. | +| `domain` | `string` | The domain of the worker. | +| `concurrency` | `number` | The number of polling instances to run concurrently. | +| `pollInterval` | `number` | The interval in milliseconds to poll for tasks. | + +### `TaskManagerConfig` +| Property | Type | Description | +| --- | --- | --- | +| `logger` | `ConductorLogger` | A logger instance. | +| `options` | `Partial` | The options for the `TaskManager`. | +| `onError` | `TaskErrorHandler` | A function to handle errors. | +| `maxRetries` | `number` | The maximum number of retries for a task. | + +### `TaskManagerOptions` +| Property | Type | Description | +| --- | --- | --- | +| `workerID` | `string` | The ID of the worker. | +| `domain` | `string` | The domain of the worker. | +| `pollInterval` | `number` | The interval in milliseconds to poll for tasks. | +| `concurrency` | `number` | The number of polling instances to run concurrently. | +| `batchPollingTimeout` | `number` | The timeout in milliseconds for batch polling. | + +### `TaskErrorHandler` +`TaskErrorHandler` is a function that takes an `Error` and an optional `Task` and handles the error. +`(error: Error, task?: Task) => void` diff --git a/docs/template-client-api-reference.md b/docs/template-client-api-reference.md index 6e41f7a7..5d06fc3c 100644 --- a/docs/template-client-api-reference.md +++ b/docs/template-client-api-reference.md @@ -28,3 +28,19 @@ Registers a new human task template. **Returns:** - `Promise`: The registered template. + +--- + +## Type Definitions + +### `HumanTaskTemplate` +| Property | Type | Description | +| --- | --- | --- | +| `createdBy` | `string` | The user who created the template. | +| `createdOn` | `number` | The creation time of the template. | +| `jsonSchema` | `Record` | The JSON schema of the template. | +| `name` | `string` | The name of the template. | +| `templateUI` | `Record` | The UI of the template. | +| `updatedBy` | `string` | The user who last updated the template. | +| `updatedOn` | `number` | The last update time of the template. | +| `version` | `number` | The version of the template. | diff --git a/docs/workflow-executor-api-reference.md b/docs/workflow-executor-api-reference.md index ba1310b0..2a5e316b 100644 --- a/docs/workflow-executor-api-reference.md +++ b/docs/workflow-executor-api-reference.md @@ -378,3 +378,199 @@ Signals a workflow task asynchronously (fire-and-forget). **Returns:** - `Promise` + +--- + +## Type Definitions + +### `WorkflowDef` +| Property | Type | Description | +| --- | --- | --- | +| `ownerApp` | `string` | The owner app of the workflow. | +| `createTime` | `number` | The creation time of the workflow. | +| `updateTime` | `number` | The last update time of the workflow. | +| `createdBy` | `string` | The user who created the workflow. | +| `updatedBy` | `string` | The user who last updated the workflow. | +| `name` | `string` | The name of the workflow. | +| `description` | `string` | The description of the workflow. | +| `version` | `number` | The version of the workflow. | +| `tasks` | `WorkflowTask[]` | The tasks in the workflow. | +| `inputParameters` | `string[]` | The input parameters of the workflow. | +| `outputParameters` | `Record` | The output parameters of the workflow. | +| `failureWorkflow` | `string` | The failure workflow. | +| `schemaVersion` | `number` | The schema version of the workflow. | +| `restartable` | `boolean` | Whether the workflow is restartable. | +| `workflowStatusListenerEnabled` | `boolean` | Whether the workflow status listener is enabled. | +| `ownerEmail` | `string` | The owner email of the workflow. | +| `timeoutPolicy` | `'TIME_OUT_WF' \| 'ALERT_ONLY'` | The timeout policy of the workflow. | +| `timeoutSeconds` | `number` | The timeout in seconds of the workflow. | +| `variables` | `Record` | The variables of the workflow. | +| `inputTemplate` | `Record` | The input template of the workflow. | + +### `WorkflowTask` +| Property | Type | Description | +| --- | --- | --- | +| `name` | `string` | The name of the task. | +| `taskReferenceName` | `string` | The reference name of the task. | +| `description` | `string` | The description of the task. | +| `inputParameters` | `Record` | The input parameters of the task. | +| `type` | `string` | The type of the task. | +| `dynamicTaskNameParam` | `string` | The dynamic task name parameter. | +| `caseValueParam` | `string` | The case value parameter. | +| `caseExpression` | `string` | The case expression. | +| `scriptExpression` | `string` | The script expression. | +| `decisionCases` | `Record>` | The decision cases. | +| `dynamicForkJoinTasksParam`| `string` | The dynamic fork join tasks parameter. | +| `dynamicForkTasksParam` | `string` | The dynamic fork tasks parameter. | +| `dynamicForkTasksInputParamName` | `string` | The dynamic fork tasks input parameter name. | +| `defaultCase` | `WorkflowTask[]` | The default case. | +| `forkTasks` | `WorkflowTask[][]` | The fork tasks. | +| `startDelay` | `number` | The start delay in seconds. | +| `subWorkflowParam` | `SubWorkflowParams` | The sub-workflow parameters. | +| `joinOn` | `string[]` | The join on tasks. | +| `sink` | `string` | The sink. | +| `optional` | `boolean` | Whether the task is optional. | +| `taskDefinition` | `TaskDef` | The task definition. | +| `rateLimited` | `boolean` | Whether the task is rate limited. | +| `defaultExclusiveJoinTask` | `string[]` | The default exclusive join task. | +| `asyncComplete` | `boolean` | Whether the task is async complete. | +| `loopCondition` | `string` | The loop condition. | +| `loopOver` | `WorkflowTask[]` | The loop over tasks. | +| `retryCount` | `number` | The retry count. | +| `evaluatorType` | `string` | The evaluator type. | +| `expression` | `string` | The expression. | +| `workflowTaskType` | `'SIMPLE' \| 'DYNAMIC' \| 'FORK_JOIN' \| 'FORK_JOIN_DYNAMIC' \| 'DECISION' \| 'SWITCH' \| 'JOIN' \| 'DO_WHILE' \| 'SUB_WORKFLOW' \| 'START_WORKFLOW' \| 'EVENT' \| 'WAIT' \| 'HUMAN' \| 'USER_DEFINED' \| 'HTTP' \| 'LAMBDA' \| 'INLINE' \| 'EXCLUSIVE_JOIN' \| 'TERMINATE' \| 'KAFKA_PUBLISH' \| 'JSON_JQ_TRANSFORM' \| 'SET_VARIABLE'` | The type of the workflow task. | + +### `WorkflowRun` +| Property | Type | Description | +| --- | --- | --- | +| `correlationId` | `string` | The correlation ID of the workflow. | +| `createTime` | `number` | The creation time of the workflow. | +| `createdBy` | `string` | The user who created the workflow. | +| `priority` | `number` | The priority of the workflow. | +| `requestId` | `string` | The request ID of the workflow. | +| `status` | `string` | The status of the workflow. | +| `tasks` | `Task[]` | The tasks in the workflow. | +| `updateTime` | `number` | The last update time of the workflow. | +| `workflowId` | `string` | The ID of the workflow instance. | +| `variables` | `Record` | The variables of the workflow. | +| `input` | `Record` | The input data for the workflow. | +| `output` | `Record` | The output data for the workflow. | + +### `SignalResponse` +`SignalResponse` represents a unified response from the signal API. It contains different fields depending on the `returnStrategy` used. It also has helper methods to extract the workflow or task details from the response. + +### `TaskFinderPredicate` +`TaskFinderPredicate` is a function that takes a `Task` and returns a boolean. It is used to find a specific task in a workflow. +` (task: Task) => boolean` + +### `RerunWorkflowRequest` +| Property | Type | Description | +| --- | --- | --- | +| `reRunFromWorkflowId` | `string` | The ID of the workflow to rerun from. | +| `workflowInput` | `Record` | The input data for the workflow. | +| `reRunFromTaskId` | `string` | The ID of the task to rerun from. | +| `taskInput` | `Record` | The input data for the task. | +| `correlationId` | `string` | The correlation ID of the workflow. | + +### `Workflow` +| Property | Type | Description | +| --- | --- | --- | +| `ownerApp` | `string` | The owner app of the workflow. | +| `createTime` | `number` | The creation time of the workflow. | +| `updateTime` | `number` | The last update time of the workflow. | +| `createdBy` | `string` | The user who created the workflow. | +| `updatedBy` | `string` | The user who last updated the workflow. | +| `status` | `'RUNNING' \| 'COMPLETED' \| 'FAILED' \| 'TIMED_OUT' \| 'TERMINATED' \| 'PAUSED'` | The status of the workflow. | +| `idempotencyKey` | `string` | The idempotency key for the workflow. | +| `endTime` | `number` | The end time of the workflow. | +| `workflowId` | `string` | The ID of the workflow instance. | +| `parentWorkflowId` | `string` | The ID of the parent workflow instance. | +| `parentWorkflowTaskId` | `string` | The ID of the parent workflow task. | +| `tasks` | `Task[]` | The tasks in the workflow. | +| `input` | `Record` | The input data for the workflow. | +| `output` | `Record` | The output data for the workflow. | +| `correlationId` | `string` | The correlation ID of the workflow. | +| `reRunFromWorkflowId` | `string` | The ID of the workflow to rerun from. | +| `reasonForIncompletion` | `string` | The reason for incompletion. | +| `event` | `string` | The event that triggered the workflow. | +| `taskToDomain` | `Record` | A map of task reference names to domains. | +| `failedReferenceTaskNames` | `string[]` | A list of failed task reference names. | +| `workflowDefinition` | `WorkflowDef` | The workflow definition. | +| `externalInputPayloadStoragePath`| `string` | The path to the external input payload storage. | +| `externalOutputPayloadStoragePath`| `string` | The path to the external output payload storage. | +| `priority` | `number` | The priority of the workflow. | +| `variables` | `Record` | The variables of the workflow. | +| `lastRetriedTime` | `number` | The last time the workflow was retried. | +| `startTime` | `number` | The start time of the workflow. | +| `workflowVersion` | `number` | The version of the workflow. | +| `workflowName` | `string` | The name of the workflow. | + +### `WorkflowStatus` +| Property | Type | Description | +| --- | --- | --- | +| `workflowId` | `string` | The ID of the workflow instance. | +| `correlationId` | `string` | The correlation ID of the workflow. | +| `output` | `Record` | The output data for the workflow. | +| `variables` | `Record` | The variables of the workflow. | +| `status` | `'RUNNING' \| 'COMPLETED' \| 'FAILED' \| 'TIMED_OUT' \| 'TERMINATED' \| 'PAUSED'` | The status of the workflow. | + +### `ScrollableSearchResultWorkflowSummary` +| Property | Type | Description | +| --- | --- | --- | +| `results` | `WorkflowSummary[]` | The search results. | +| `totalHits` | `number` | The total number of hits. | + +### `SkipTaskRequest` +| Property | Type | Description | +| --- | --- | --- | +| `taskInput` | `Record` | The input data for the task. | +| `taskOutput` | `Record` | The output data for the task. | + +### `TaskResultStatus` +`TaskResultStatus` is a string that represents the status of a task result. It can be one of the following values: `'IN_PROGRESS'`, `'FAILED'`, `'FAILED_WITH_TERMINAL_ERROR'`, `'COMPLETED'`. + +### `Task` +| Property | Type | Description | +| --- | --- | --- | +| `taskType` | `string` | The type of the task. | +| `status` | `'IN_PROGRESS' \| 'CANCELED' \| 'FAILED' \| 'FAILED_WITH_TERMINAL_ERROR' \| 'COMPLETED' \| 'COMPLETED_WITH_ERRORS' \| 'SCHEDULED' \| 'TIMED_OUT' \| 'SKIPPED'` | The status of the task. | +| `inputData` | `Record` | The input data for the task. | +| `referenceTaskName` | `string` | The reference name of the task. | +| `retryCount` | `number` | The retry count. | +| `seq` | `number` | The sequence number of the task. | +| `correlationId` | `string` | The correlation ID of the task. | +| `pollCount` | `number` | The poll count. | +| `taskDefName` | `string` | The name of the task definition. | +| `scheduledTime` | `number` | The scheduled time of the task. | +| `startTime` | `number` | The start time of the task. | +| `endTime` | `number` | The end time of the task. | +| `updateTime` | `number` | The last update time of the task. | +| `startDelayInSeconds` | `number` | The start delay in seconds. | +| `retriedTaskId` | `string` | The ID of the retried task. | +| `retried` | `boolean` | Whether the task was retried. | +| `executed` | `boolean` | Whether the task was executed. | +| `callbackFromWorker` | `boolean` | Whether the callback is from a worker. | +| `responseTimeoutSeconds` | `number` | The response timeout in seconds. | +| `workflowInstanceId` | `string` | The ID of the workflow instance. | +| `workflowType` | `string` | The type of the workflow. | +| `taskId` | `string` | The ID of the task. | +| `reasonForIncompletion` | `string` | The reason for incompletion. | +| `callbackAfterSeconds` | `number` | The callback after seconds. | +| `workerId` | `string` | The ID of the worker. | +| `outputData` | `Record` | The output data of the task. | +| `workflowTask` | `WorkflowTask` | The workflow task. | +| `domain` | `string` | The domain of the task. | +| `rateLimitPerFrequency` | `number` | The rate limit per frequency. | +| `rateLimitFrequencyInSeconds` | `number` | The rate limit frequency in seconds. | +| `externalInputPayloadStoragePath`| `string` | The path to the external input payload storage. | +| `externalOutputPayloadStoragePath`| `string` | The path to the external output payload storage. | +| `workflowPriority` | `number` | The priority of the workflow. | +| `executionNameSpace` | `string` | The execution namespace. | +| `isolationGroupId` | `string` | The isolation group ID. | +| `iteration` | `number` | The iteration number. | +| `subWorkflowId` | `string` | The ID of the sub-workflow. | +| `subworkflowChanged` | `boolean` | Whether the sub-workflow was changed. | +| `queueWaitTime` | `number` | The queue wait time. | +| `taskDefinition` | `TaskDef` | The task definition. | +| `loopOverTask` | `boolean` | Whether the task is a loop over task. | From e339a83001d97c93abdc87f2021d4b5180758435 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Mon, 6 Oct 2025 17:27:29 +0300 Subject: [PATCH 43/60] move full api reference to it's own folder --- README.md | 16 ++++++++-------- .../human-executor.md} | 0 .../metadata-client.md} | 1 - .../scheduler-client.md} | 0 .../service-registry-client.md} | 1 - .../task-client.md} | 0 .../task-manager.md} | 0 .../template-client.md} | 0 .../workflow-executor.md} | 0 9 files changed, 8 insertions(+), 10 deletions(-) rename docs/{human-executor-api-reference.md => api-reference/human-executor.md} (100%) rename docs/{metadata-client-api-reference.md => api-reference/metadata-client.md} (99%) rename docs/{scheduler-client-api-reference.md => api-reference/scheduler-client.md} (100%) rename docs/{service-registry-client-api-reference.md => api-reference/service-registry-client.md} (99%) rename docs/{task-client-api-reference.md => api-reference/task-client.md} (100%) rename docs/{task-manager-api-reference.md => api-reference/task-manager.md} (100%) rename docs/{template-client-api-reference.md => api-reference/template-client.md} (100%) rename docs/{workflow-executor-api-reference.md => api-reference/workflow-executor.md} (100%) diff --git a/README.md b/README.md index 04fb9fc8..b037a835 100644 --- a/README.md +++ b/README.md @@ -705,7 +705,7 @@ const searchResults = await executor.search( ### WorkflowExecutor API Reference -For a complete method reference for the `WorkflowExecutor` class, see the [WorkflowExecutor API Reference](./docs/workflow-executor-api-reference.md). +For a complete method reference for the `WorkflowExecutor` class, see the [WorkflowExecutor API Reference](./docs/api-reference/workflow-executor.md). Complete method reference for the `WorkflowExecutor` class: @@ -851,7 +851,7 @@ const workflowIds = executor.startWorkflows( ### Monitoring & Debugging Tasks -The `TaskClient` provides capabilities for monitoring and debugging tasks within your workflow executions. For a complete method reference, see the [TaskClient API Reference](./docs/task-client-api-reference.md). +The `TaskClient` provides capabilities for monitoring and debugging tasks within your workflow executions. For a complete method reference, see the [TaskClient API Reference](./docs/api-reference/task-client.md). ```typescript import { TaskClient } from "@io-orkes/conductor-javascript"; @@ -949,7 +949,7 @@ Workers are background processes that execute tasks in your workflows. Think of Workflow → Creates Tasks → Workers Poll for Tasks → Execute Logic → Return Results → Workflow Continues ``` -The SDK provides the **TaskManager** class - an easy-to-use interface for managing workers efficiently. For a complete method reference, see the [TaskManager API Reference](./docs/task-manager-api-reference.md). +The SDK provides the **TaskManager** class - an easy-to-use interface for managing workers efficiently. For a complete method reference, see the [TaskManager API Reference](./docs/api-reference/task-manager.md). ### Quick Start: Your First Worker @@ -1299,7 +1299,7 @@ process.on('SIGTERM', async () => { ### SchedulerClient -The `SchedulerClient` manages workflow scheduling and provides methods for creating, managing, and monitoring scheduled workflows. For a complete method reference, see the [SchedulerClient API Reference](./docs/scheduler-client-api-reference.md). +The `SchedulerClient` manages workflow scheduling and provides methods for creating, managing, and monitoring scheduled workflows. For a complete method reference, see the [SchedulerClient API Reference](./docs/api-reference/scheduler-client.md). ```typescript import { SchedulerClient } from "@io-orkes/conductor-javascript"; @@ -1407,7 +1407,7 @@ const results = await scheduler.search(0, 10, "startTime:DESC", "*", "status:RUN ### ServiceRegistryClient -The `ServiceRegistryClient` manages service registrations and circuit breakers. For a complete method reference, see the [ServiceRegistryClient API Reference](./docs/service-registry-client-api-reference.md). +The `ServiceRegistryClient` manages service registrations and circuit breakers. For a complete method reference, see the [ServiceRegistryClient API Reference](./docs/api-reference/service-registry-client.md). ```typescript import { ServiceRegistryClient } from "@io-orkes/conductor-javascript"; @@ -1472,7 +1472,7 @@ await serviceRegistry.removeService("user-service"); ### MetadataClient -The `MetadataClient` class provides methods for managing task and workflow definitions in Conductor. For a complete method reference, see the [MetadataClient API Reference](./docs/metadata-client-api-reference.md). +The `MetadataClient` class provides methods for managing task and workflow definitions in Conductor. For a complete method reference, see the [MetadataClient API Reference](./docs/api-reference/metadata-client.md). ```typescript import { MetadataClient } from "@io-orkes/conductor-javascript"; @@ -1676,7 +1676,7 @@ HUMAN tasks are a **type of system task** that enable human interaction within w ### HumanExecutor -The `HumanExecutor` class provides comprehensive human task management. For a complete method reference, see the [HumanExecutor API Reference](./docs/human-executor-api-reference.md). +The `HumanExecutor` class provides comprehensive human task management. For a complete method reference, see the [HumanExecutor API Reference](./docs/api-reference/human-executor.md). ```typescript import { HumanExecutor } from "@io-orkes/conductor-javascript"; @@ -1746,7 +1746,7 @@ const templateById = await humanExecutor.getTemplateById("approval_template"); ### TemplateClient -The `TemplateClient` class provides methods for managing human task templates (forms and UI). For a complete method reference, see the [TemplateClient API Reference](./docs/template-client-api-reference.md). +The `TemplateClient` class provides methods for managing human task templates (forms and UI). For a complete method reference, see the [TemplateClient API Reference](./docs/api-reference/template-client.md). ```typescript import { TemplateClient } from "@io-orkes/conductor-javascript"; diff --git a/docs/human-executor-api-reference.md b/docs/api-reference/human-executor.md similarity index 100% rename from docs/human-executor-api-reference.md rename to docs/api-reference/human-executor.md diff --git a/docs/metadata-client-api-reference.md b/docs/api-reference/metadata-client.md similarity index 99% rename from docs/metadata-client-api-reference.md rename to docs/api-reference/metadata-client.md index 79132152..faa3d4a0 100644 --- a/docs/metadata-client-api-reference.md +++ b/docs/api-reference/metadata-client.md @@ -117,4 +117,3 @@ Unregisters a workflow definition. | `ownerEmail` | `string` | The owner email of the task. | | `pollTimeoutSeconds` | `number` | The poll timeout in seconds. | | `backoffScaleFactor` | `number` | The backoff scale factor. | - diff --git a/docs/scheduler-client-api-reference.md b/docs/api-reference/scheduler-client.md similarity index 100% rename from docs/scheduler-client-api-reference.md rename to docs/api-reference/scheduler-client.md diff --git a/docs/service-registry-client-api-reference.md b/docs/api-reference/service-registry-client.md similarity index 99% rename from docs/service-registry-client-api-reference.md rename to docs/api-reference/service-registry-client.md index 386da62f..7d887f96 100644 --- a/docs/service-registry-client-api-reference.md +++ b/docs/api-reference/service-registry-client.md @@ -291,4 +291,3 @@ Discovers service methods. | --- | --- | --- | | `filename` | `string` | The name of the proto file. | | `lastUpdated` | `number` | The last update time of the proto file. | - diff --git a/docs/task-client-api-reference.md b/docs/api-reference/task-client.md similarity index 100% rename from docs/task-client-api-reference.md rename to docs/api-reference/task-client.md diff --git a/docs/task-manager-api-reference.md b/docs/api-reference/task-manager.md similarity index 100% rename from docs/task-manager-api-reference.md rename to docs/api-reference/task-manager.md diff --git a/docs/template-client-api-reference.md b/docs/api-reference/template-client.md similarity index 100% rename from docs/template-client-api-reference.md rename to docs/api-reference/template-client.md diff --git a/docs/workflow-executor-api-reference.md b/docs/api-reference/workflow-executor.md similarity index 100% rename from docs/workflow-executor-api-reference.md rename to docs/api-reference/workflow-executor.md From cbffb9fb4e68e5409a83b37dcc0d40f3bf5be85e Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Mon, 6 Oct 2025 17:33:00 +0300 Subject: [PATCH 44/60] Update README.md --- README.md | 173 +++--------------------------------------------------- 1 file changed, 9 insertions(+), 164 deletions(-) diff --git a/README.md b/README.md index b037a835..c6f3efbf 100644 --- a/README.md +++ b/README.md @@ -707,146 +707,24 @@ const searchResults = await executor.search( For a complete method reference for the `WorkflowExecutor` class, see the [WorkflowExecutor API Reference](./docs/api-reference/workflow-executor.md). -Complete method reference for the `WorkflowExecutor` class: +Here is a quick example of how to interact with a running workflow: ```typescript import { WorkflowExecutor } from "@io-orkes/conductor-javascript"; const executor = new WorkflowExecutor(client); - -// ============================================================ -// Workflow Lifecycle -// ============================================================ - -// Register or update a workflow definition -await executor.registerWorkflow(override: boolean, workflow: WorkflowDef): Promise - -// Start a new workflow execution -const workflowId = await executor.startWorkflow(request: StartWorkflowRequest): Promise - -// Execute workflow synchronously and wait for completion -const result = await executor.executeWorkflow(...): Promise - -// Get workflow execution details with tasks -const workflow = await executor.getWorkflow(id: string, includeTasks: boolean): Promise +const executionId = "your-workflow-execution-id"; // Get workflow status summary const status = await executor.getWorkflowStatus( - id: string, - includeOutput: boolean, - includeVariables: boolean -): Promise - -// Search workflows with filters -const results = await executor.search( - start: number, - size: number, - query: string, - freeText: string, - sort?: string, - skipCache?: boolean -): Promise - -// ============================================================ -// Workflow Control -// ============================================================ - -// Pause a running workflow -await executor.pause(workflowId: string): Promise - -// Resume a paused workflow -await executor.resume(workflowId: string): Promise - -// Terminate a workflow with reason -await executor.terminate(workflowId: string, reason: string): Promise - -// Restart a workflow -await executor.restart(workflowId: string, useLatestDefinitions: boolean): Promise - -// Retry a failed workflow from last failed task -await executor.retry(workflowId: string, resumeSubworkflowTasks: boolean): Promise - -// Rerun a workflow with new parameters -const newId = await executor.reRun( - workflowId: string, - request?: Partial -): Promise - -// Skip a task in a running workflow -await executor.skipTasksFromWorkflow( - workflowId: string, - taskRefName: string, - request: Partial -): Promise - -// ============================================================ -// Task Operations -// ============================================================ + executionId, + true, // includeOutput + true // includeVariables +); +console.log(`Workflow status: ${status.status}`); -// Get task by ID -const task = await executor.getTask(taskId: string): Promise - -// Update task by ID -await executor.updateTask( - taskId: string, - workflowId: string, - status: TaskResultStatus, - output: Record -): Promise - -// Update task by reference name -await executor.updateTaskByRefName( - taskRefName: string, - workflowId: string, - status: TaskResultStatus, - output: Record -): Promise - -// Update task and return updated workflow -const updatedWorkflow = await executor.updateTaskSync( - taskRefName: string, - workflowId: string, - status: TaskResultStatusEnum, - output: Record, - workerId?: string -): Promise - -// Send signal to workflow -const response = await executor.signal( - workflowId: string, - status: TaskResultStatusEnum, - output: Record, - returnStrategy?: ReturnStrategy -): Promise - -// Send signal asynchronously (fire-and-forget) -await executor.signalAsync( - workflowId: string, - status: TaskResultStatusEnum, - output: Record -): Promise - -// ============================================================ -// Advanced Operations -// ============================================================ - -// Rerun from a specific task found by predicate -await executor.goBackToTask( - workflowId: string, - predicate: (task: Task) => boolean, - overrides?: Partial -): Promise - -// Rerun from first task of specific type -await executor.goBackToFirstTaskMatchingType( - workflowId: string, - taskType: string -): Promise - -// Start multiple workflows at once -const workflowIds = executor.startWorkflows( - requests: StartWorkflowRequest[] -): Promise[] +// Terminate a workflow with a reason +await executor.terminate(executionId, "Terminating due to error"); ``` ### Monitoring & Debugging Tasks @@ -1480,39 +1358,6 @@ import { MetadataClient } from "@io-orkes/conductor-javascript"; const metadataClient = new MetadataClient(client); ``` -#### Complete MetadataClient API Reference - -```typescript -// Task Definition Management -await metadataClient.registerTask(taskDef: TaskDef): Promise -await metadataClient.updateTask(taskDef: TaskDef): Promise -const taskDef = await metadataClient.getTaskDef(taskName: string): Promise -const allTasks = await metadataClient.getAllTaskDefs(): Promise -await metadataClient.unregisterTask(taskName: string): Promise - -// Workflow Definition Management -await metadataClient.registerWorkflowDef( - workflowDef: WorkflowDef, - overwrite?: boolean // (optional, default: false) -): Promise - -await metadataClient.updateWorkflowDef(workflowDef: WorkflowDef): Promise - -const workflowDef = await metadataClient.getWorkflowDef( - workflowName: string, - version?: number // (optional) uses latest if not specified -): Promise - -const allVersions = await metadataClient.getAllWorkflowDefs( - workflowName: string -): Promise - -await metadataClient.unregisterWorkflow( - workflowName: string, - version: number -): Promise -``` - ### Task Definition Factory The `taskDefinition` function provides a convenient way to create task definitions with default values: From 5fa2966f78712faa56aeccf95c9da4583fde2c07 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Mon, 6 Oct 2025 17:42:41 +0300 Subject: [PATCH 45/60] move task generator reference to it's own file --- README.md | 256 +------------------------- docs/api-reference/task-generators.md | 251 +++++++++++++++++++++++++ 2 files changed, 253 insertions(+), 254 deletions(-) create mode 100644 docs/api-reference/task-generators.md diff --git a/README.md b/README.md index c6f3efbf..74355fc3 100644 --- a/README.md +++ b/README.md @@ -247,8 +247,8 @@ See the [Workers](#workers) section for implementation details. Workflows orchestrate the execution of multiple tasks in a coordinated sequence. This section explains: - **What workflows are** and how they work - **How to create workflows** step-by-step using task generators +- **Task generators** for different task types - **How to manage workflow lifecycle** (register, start, pause, resume, terminate, search) -- **Task generator reference** for all available task types ### WorkflowExecutor @@ -357,7 +357,7 @@ const workflowDef = { - **taskReferenceName**: A unique identifier for the task within this workflow. Used to reference task outputs (e.g., `${task_ref.output.fieldName}`) - **inputParameters**: Use `${workflow.input.fieldName}` to access workflow inputs and `${other_task_ref.output.fieldName}` to access previous task outputs -- **Task Generators**: Each task type has a generator function (e.g., `simpleTask`, `httpTask`, `switchTask`). See [Task Generators Reference](#task-generators-reference) for all available types. +- **Task Generators**: Each task type has a generator function (e.g., `simpleTask`, `httpTask`, `switchTask`). See [Task Generators Reference](./docs/api-reference/task-generators.md) for all available types. #### Step 3: Register and Start Your Workflow @@ -385,258 +385,6 @@ const executionId = await executor.startWorkflow({ console.log(`Workflow started with ID: ${executionId}`); ``` -### Task Generators Reference - -This section provides code examples for each task type generator. Use these to build your workflow task lists. - -**Note:** These generators create workflow task references. To register task metadata (retry policies, timeouts, rate limits), use `taskDefinition()` or `MetadataClient` (see [Metadata](#metadata)). - -#### Simple Task - -*Requires Custom Workers* - Executes custom business logic via workers you implement. - -```typescript -import { simpleTask } from "@io-orkes/conductor-javascript"; - -const task = simpleTask( - "task_ref", // taskReferenceName (required) - "task_name", // name (required): must match worker's taskDefName - { // inputParameters (required) - inputParam: "value" - }, - false // optional (optional): if true, workflow continues on failure -); -``` - -#### HTTP Task - -*System Task* - Makes HTTP/REST API calls. - -```typescript -import { httpTask } from "@io-orkes/conductor-javascript"; - -const task = httpTask( - "http_ref", - { - uri: "http://api.example.com/data", - method: "GET", - headers: { "Authorization": "Bearer token" }, - connectionTimeOut: 5000, - readTimeOut: 10000 - }, - false, // asyncComplete (optional) - false // optional (optional): workflow continues on failure -); -``` - -#### Switch Task - -*System Task* - Provides conditional branching based on input values. - -```typescript -import { switchTask } from "@io-orkes/conductor-javascript"; - -const task = switchTask( - "switch_ref", - "input.status", // expression to evaluate - { - "active": [simpleTask("active_task", "process_active", {})], - "inactive": [simpleTask("inactive_task", "process_inactive", {})] - }, - [simpleTask("default_task", "process_default", {})], // defaultCase (optional) - false // optional (optional): workflow continues on failure -); -``` - -#### Fork-Join Task - -*System Task* - Executes multiple task branches in parallel and waits for all to complete. - -```typescript -import { forkJoinTask } from "@io-orkes/conductor-javascript"; - -const task = forkJoinTask("fork_ref", [ - [simpleTask("task1", "process_1", {})], - [simpleTask("task2", "process_2", {})], - [simpleTask("task3", "process_3", {})] -]); -``` - -#### Do-While Task - -*System Task* - Executes a loop with a condition evaluated after each iteration. - -```typescript -import { doWhileTask } from "@io-orkes/conductor-javascript"; - -const task = doWhileTask("while_ref", "workflow.variables.counter < 10", [ - simpleTask("loop_task", "process_item", { - index: "${workflow.variables.counter}" - }), - setVariableTask("increment", { - variableName: "counter", - value: "${workflow.variables.counter + 1}" - }) -]); -``` - -#### Sub-Workflow Task - -*System Task* - Executes another workflow as a task. - -```typescript -import { subWorkflowTask } from "@io-orkes/conductor-javascript"; - -const task = subWorkflowTask( - "sub_ref", - "child_workflow", // workflowName - 1, // version (optional): uses latest if not specified - false // optional (optional) -); - -// Set input parameters -task.inputParameters = { inputParam: "value" }; -``` - -#### Event Task - -*System Task* - Publishes events to external eventing systems. - -```typescript -import { eventTask } from "@io-orkes/conductor-javascript"; - -const task = eventTask("event_ref", "event_name", { - sink: "event_sink", - asyncComplete: true -}); -``` - -#### Wait Task - -*System Task* - Pauses workflow execution for a specified duration or until a specific time. - -```typescript -import { waitTaskDuration, waitTaskUntil } from "@io-orkes/conductor-javascript"; - -// Wait for a duration (e.g., "30s", "5m", "1h", "2d") -const taskDuration = waitTaskDuration( - "wait_ref", - "30s", // duration string - false // optional (optional) -); - -// Wait until a specific time (ISO 8601 format) -const taskUntil = waitTaskUntil( - "wait_until_ref", - "2025-12-31T23:59:59Z", // ISO 8601 timestamp - false // optional (optional) -); -``` - -#### Terminate Task - -*System Task* - Terminates workflow execution with a specified status. - -```typescript -import { terminateTask } from "@io-orkes/conductor-javascript"; - -const task = terminateTask( - "terminate_ref", - "FAILED", // status: "COMPLETED" or "FAILED" - "Error message" // terminationReason (optional) -); -``` - -#### Set Variable Task - -*System Task* - Sets or updates workflow variables. - -```typescript -import { setVariableTask } from "@io-orkes/conductor-javascript"; - -const task = setVariableTask("var_ref", { - variableName: "result", - value: "computed_value" -}); -``` - -#### JSON JQ Transform Task - -*System Task* - Transforms JSON data using JQ expressions. - -```typescript -import { jsonJqTask } from "@io-orkes/conductor-javascript"; - -const task = jsonJqTask("transform_ref", ".data.items[] | {id: .id, name: .name}"); -``` - -#### Kafka Publish Task - -*System Task* - Publishes messages to Kafka topics. - -```typescript -import { kafkaPublishTask } from "@io-orkes/conductor-javascript"; - -const task = kafkaPublishTask("kafka_ref", "topic_name", { - message: "Hello Kafka!" -}, { - key: "message_key", - partition: 0 -}); -``` - -#### Inline Task - -*System Task* - Executes JavaScript code inline within the workflow. - -```typescript -import { inlineTask } from "@io-orkes/conductor-javascript"; - -const task = inlineTask("inline_ref", ` - function execute(input) { - return { result: input.value * 2 }; - } -`); -``` - -#### Dynamic Fork Task - -*System Task* - Dynamically creates parallel task executions based on input. - -```typescript -import { dynamicForkTask } from "@io-orkes/conductor-javascript"; - -const task = dynamicForkTask("dynamic_ref", "input.tasks", "task_name"); -``` - -#### Join Task - -*System Task* - Synchronization point for forked tasks. - -```typescript -import { joinTask } from "@io-orkes/conductor-javascript"; - -const task = joinTask("join_ref"); -``` - -#### Human Task - -*System Task* - Pauses workflow until a person completes an action (approval, form submission, etc.). - -```typescript -import { humanTask } from "@io-orkes/conductor-javascript"; - -const task = humanTask("human_ref", "approval_task", { - assignee: "user@example.com", - form: { - fields: [ - { name: "approved", type: "boolean", required: true }, - { name: "comments", type: "text", required: false } - ] - } -}); -``` - ### Managing Workflow Execution Once your workflow is running, you can monitor and control its execution using these operations: diff --git a/docs/api-reference/task-generators.md b/docs/api-reference/task-generators.md new file mode 100644 index 00000000..b5e66e5d --- /dev/null +++ b/docs/api-reference/task-generators.md @@ -0,0 +1,251 @@ +### Task Generators Reference + +This section provides code examples for each task type generator. Use these to build your workflow task lists. + +**Note:** These generators create workflow task references. To register task metadata (retry policies, timeouts, rate limits), use `taskDefinition()` or `MetadataClient` (see [Metadata](#metadata)). + +#### Simple Task + +*Requires Custom Workers* - Executes custom business logic via workers you implement. + +```typescript +import { simpleTask } from "@io-orkes/conductor-javascript"; + +const task = simpleTask( + "task_ref", // taskReferenceName (required) + "task_name", // name (required): must match worker's taskDefName + { // inputParameters (required) + inputParam: "value" + }, + false // optional (optional): if true, workflow continues on failure +); +``` + +#### HTTP Task + +*System Task* - Makes HTTP/REST API calls. + +```typescript +import { httpTask } from "@io-orkes/conductor-javascript"; + +const task = httpTask( + "http_ref", + { + uri: "http://api.example.com/data", + method: "GET", + headers: { "Authorization": "Bearer token" }, + connectionTimeOut: 5000, + readTimeOut: 10000 + }, + false, // asyncComplete (optional) + false // optional (optional): workflow continues on failure +); +``` + +#### Switch Task + +*System Task* - Provides conditional branching based on input values. + +```typescript +import { switchTask } from "@io-orkes/conductor-javascript"; + +const task = switchTask( + "switch_ref", + "input.status", // expression to evaluate + { + "active": [simpleTask("active_task", "process_active", {})], + "inactive": [simpleTask("inactive_task", "process_inactive", {})] + }, + [simpleTask("default_task", "process_default", {})], // defaultCase (optional) + false // optional (optional): workflow continues on failure +); +``` + +#### Fork-Join Task + +*System Task* - Executes multiple task branches in parallel and waits for all to complete. + +```typescript +import { forkJoinTask } from "@io-orkes/conductor-javascript"; + +const task = forkJoinTask("fork_ref", [ + [simpleTask("task1", "process_1", {})], + [simpleTask("task2", "process_2", {})], + [simpleTask("task3", "process_3", {})] +]); +``` + +#### Do-While Task + +*System Task* - Executes a loop with a condition evaluated after each iteration. + +```typescript +import { doWhileTask } from "@io-orkes/conductor-javascript"; + +const task = doWhileTask("while_ref", "workflow.variables.counter < 10", [ + simpleTask("loop_task", "process_item", { + index: "${workflow.variables.counter}" + }), + setVariableTask("increment", { + variableName: "counter", + value: "${workflow.variables.counter + 1}" + }) +]); +``` + +#### Sub-Workflow Task + +*System Task* - Executes another workflow as a task. + +```typescript +import { subWorkflowTask } from "@io-orkes/conductor-javascript"; + +const task = subWorkflowTask( + "sub_ref", + "child_workflow", // workflowName + 1, // version (optional): uses latest if not specified + false // optional (optional) +); + +// Set input parameters +task.inputParameters = { inputParam: "value" }; +``` + +#### Event Task + +*System Task* - Publishes events to external eventing systems. + +```typescript +import { eventTask } from "@io-orkes/conductor-javascript"; + +const task = eventTask("event_ref", "event_name", { + sink: "event_sink", + asyncComplete: true +}); +``` + +#### Wait Task + +*System Task* - Pauses workflow execution for a specified duration or until a specific time. + +```typescript +import { waitTaskDuration, waitTaskUntil } from "@io-orkes/conductor-javascript"; + +// Wait for a duration (e.g., "30s", "5m", "1h", "2d") +const taskDuration = waitTaskDuration( + "wait_ref", + "30s", // duration string + false // optional (optional) +); + +// Wait until a specific time (ISO 8601 format) +const taskUntil = waitTaskUntil( + "wait_until_ref", + "2025-12-31T23:59:59Z", // ISO 8601 timestamp + false // optional (optional) +); +``` + +#### Terminate Task + +*System Task* - Terminates workflow execution with a specified status. + +```typescript +import { terminateTask } from "@io-orkes/conductor-javascript"; + +const task = terminateTask( + "terminate_ref", + "FAILED", // status: "COMPLETED" or "FAILED" + "Error message" // terminationReason (optional) +); +``` + +#### Set Variable Task + +*System Task* - Sets or updates workflow variables. + +```typescript +import { setVariableTask } from "@io-orkes/conductor-javascript"; + +const task = setVariableTask("var_ref", { + variableName: "result", + value: "computed_value" +}); +``` + +#### JSON JQ Transform Task + +*System Task* - Transforms JSON data using JQ expressions. + +```typescript +import { jsonJqTask } from "@io-orkes/conductor-javascript"; + +const task = jsonJqTask("transform_ref", ".data.items[] | {id: .id, name: .name}"); +``` + +#### Kafka Publish Task + +*System Task* - Publishes messages to Kafka topics. + +```typescript +import { kafkaPublishTask } from "@io-orkes/conductor-javascript"; + +const task = kafkaPublishTask("kafka_ref", "topic_name", { + message: "Hello Kafka!" +}, { + key: "message_key", + partition: 0 +}); +``` + +#### Inline Task + +*System Task* - Executes JavaScript code inline within the workflow. + +```typescript +import { inlineTask } from "@io-orkes/conductor-javascript"; + +const task = inlineTask("inline_ref", ` + function execute(input) { + return { result: input.value * 2 }; + } +`); +``` + +#### Dynamic Fork Task + +*System Task* - Dynamically creates parallel task executions based on input. + +```typescript +import { dynamicForkTask } from "@io-orkes/conductor-javascript"; + +const task = dynamicForkTask("dynamic_ref", "input.tasks", "task_name"); +``` + +#### Join Task + +*System Task* - Synchronization point for forked tasks. + +```typescript +import { joinTask } from "@io-orkes/conductor-javascript"; + +const task = joinTask("join_ref"); +``` + +#### Human Task + +*System Task* - Pauses workflow until a person completes an action (approval, form submission, etc.). + +```typescript +import { humanTask } from "@io-orkes/conductor-javascript"; + +const task = humanTask("human_ref", "approval_task", { + assignee: "user@example.com", + form: { + fields: [ + { name: "approved", type: "boolean", required: true }, + { name: "comments", type: "text", required: false } + ] + } +}); +``` From 64c1c5c4ee594c8a10850207fabebd5ecf036ed5 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Mon, 6 Oct 2025 17:54:23 +0300 Subject: [PATCH 46/60] update Scheduling section --- README.md | 119 +++++++++++++++++------------------------------------- 1 file changed, 37 insertions(+), 82 deletions(-) diff --git a/README.md b/README.md index 74355fc3..85cfeb0b 100644 --- a/README.md +++ b/README.md @@ -923,9 +923,15 @@ process.on('SIGTERM', async () => { ## Scheduling -### SchedulerClient +The Conductor Scheduler allows you to run workflows at specific times or intervals, defined by a CRON expression. This is useful for tasks like nightly data processing, weekly reports, or any time-based automation. -The `SchedulerClient` manages workflow scheduling and provides methods for creating, managing, and monitoring scheduled workflows. For a complete method reference, see the [SchedulerClient API Reference](./docs/api-reference/scheduler-client.md). +### Quick Start: Scheduling a Workflow + +Here’s how to schedule a workflow in three steps: + +#### Step 1: Create a SchedulerClient + +First, create an instance of the `SchedulerClient`: ```typescript import { SchedulerClient } from "@io-orkes/conductor-javascript"; @@ -933,101 +939,50 @@ import { SchedulerClient } from "@io-orkes/conductor-javascript"; const scheduler = new SchedulerClient(client); ``` -#### Schedule Management +#### Step 2: Define the Schedule + +Next, define the schedule, specifying the workflow to run and the CRON expression for its timing. ```typescript -// Create or update a schedule +// Schedule a workflow to run every day at 9 AM await scheduler.saveSchedule({ - name: string, - cronExpression: string, // e.g., "0 0 9 * * ?" + name: "daily_report_schedule", + cronExpression: "0 0 9 * * ?", // Everyday at 9am startWorkflowRequest: { - name: string, - version?: number, - input?: Record, - correlationId?: string, - priority?: number, - taskToDomain?: Record + name: "generate_daily_report", + version: 1, + input: { + reportType: "SALES", + period: "DAILY" + }, }, - paused?: boolean, // (optional, default: false) - runCatchupScheduleInstances?: boolean, // (optional, default: false) - scheduleStartTime?: number, // (optional) epoch ms - scheduleEndTime?: number // (optional) epoch ms -}): Promise - -// Get a specific schedule -const schedule = await scheduler.getSchedule(name: string): Promise - -// Get all schedules -const schedules = await scheduler.getAllSchedules( - workflowName?: string // (optional) filter by workflow name -): Promise - -// Delete a schedule -await scheduler.deleteSchedule(name: string): Promise - -// Pause a schedule -await scheduler.pauseSchedule(name: string): Promise - -// Resume a paused schedule -await scheduler.resumeSchedule(name: string): Promise +}); ``` -#### Bulk Schedule Operations - -```typescript -// Pause all schedules (use with caution) -await scheduler.pauseAllSchedules(): Promise - -// Resume all schedules -await scheduler.resumeAllSchedules(): Promise +**Cron Expression Format:** +- Standard cron format: `second minute hour day month dayOfWeek` +- Examples: + - `"0 0 9 * * ?"` - Every day at 9 AM + - `"0 */30 * * * ?"` - Every 30 minutes + - `"0 0 0 1 * ?"` - First day of every month at midnight + - `"0 0 12 ? * MON-FRI"` - Weekdays at noon -// Requeue all execution records -await scheduler.requeueAllExecutionRecords(): Promise -``` +#### Step 3: Manage the Schedule -#### Schedule Execution Preview +You can easily manage your schedules: ```typescript -// Get next few execution times for a cron expression -const nextExecutions = await scheduler.getNextFewSchedules( - cronExpression: string, - scheduleTime: number, // epoch ms - scheduleEndTime: number, // epoch ms - limit: number -): Promise // array of timestamps - -// Example: Get next 5 executions over the next 7 days -const nextTimes = await scheduler.getNextFewSchedules( - "0 0 9 * * ?", - Date.now(), - Date.now() + 7 * 24 * 60 * 60 * 1000, - 5 -); -``` +// Pause a schedule +await scheduler.pauseSchedule("daily_report_schedule"); -#### Search Schedule Executions +// Resume a paused schedule +await scheduler.resumeSchedule("daily_report_schedule"); -```typescript -// Search schedule execution history -const executions = await scheduler.search( - start: number, - size: number, - sort: string, // (optional, default: "") - freeText: string, // (default: "*") - query: string // e.g., "status:RUNNING" -): Promise - -// Example -const results = await scheduler.search(0, 10, "startTime:DESC", "*", "status:RUNNING"); +// Delete a schedule +await scheduler.deleteSchedule("daily_report_schedule"); ``` -**Cron Expression Format:** -- Standard cron format: `second minute hour day month dayOfWeek` -- Examples: - - `"0 0 9 * * ?"` - Every day at 9 AM - - `"0 */30 * * * ?"` - Every 30 minutes - - `"0 0 0 1 * ?"` - First day of every month at midnight - - `"0 0 12 ? * MON-FRI"` - Weekdays at noon +For a complete method reference, see the [SchedulerClient API Reference](./docs/api-reference/scheduler-client.md). ## Service Registry From 72611d8cf0eac03dc3aa6d5b9d7c486011fc9502 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Mon, 6 Oct 2025 17:56:17 +0300 Subject: [PATCH 47/60] update Service Registry section --- README.md | 67 +++++++++++++++++++++++-------------------------------- 1 file changed, 28 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 85cfeb0b..781ea120 100644 --- a/README.md +++ b/README.md @@ -986,16 +986,28 @@ For a complete method reference, see the [SchedulerClient API Reference](./docs/ ## Service Registry -### ServiceRegistryClient +The Service Registry in Conductor allows you to manage and discover microservices. It also provides built-in circuit breaker functionality to improve the resilience of your distributed system. -The `ServiceRegistryClient` manages service registrations and circuit breakers. For a complete method reference, see the [ServiceRegistryClient API Reference](./docs/api-reference/service-registry-client.md). +### Quick Start: Using the Service Registry + +Here’s how to get started with the `ServiceRegistryClient`: + +#### Step 1: Create a ServiceRegistryClient + +First, create an instance of the `ServiceRegistryClient`: ```typescript import { ServiceRegistryClient } from "@io-orkes/conductor-javascript"; const serviceRegistry = new ServiceRegistryClient(client); +``` + +#### Step 2: Register a Service + +Next, register your service with Conductor. This example registers an HTTP service with a circuit breaker configuration. -// Register a service +```typescript +// Register a service with circuit breaker config await serviceRegistry.addOrUpdateService({ name: "user-service", type: "HTTP", @@ -1004,51 +1016,28 @@ await serviceRegistry.addOrUpdateService({ failureRateThreshold: 50.0, slidingWindowSize: 10, minimumNumberOfCalls: 5, - waitDurationInOpenState: 60000 - } + waitDurationInOpenState: 60000, // 1 minute + }, }); +``` -// Get all registered services -const services = await serviceRegistry.getRegisteredServices(); - -// Get specific service -const service = await serviceRegistry.getService("user-service"); - -// Add service method -await serviceRegistry.addOrUpdateServiceMethod("user-service", { - operationName: "getUser", - methodName: "getUser", - methodType: "GET", - inputType: "string", - outputType: "User", - requestParams: [ - { - name: "id", - type: "Path", - required: true, - schema: { type: "string" } - } - ] -}); +#### Step 3: Manage Services -// Circuit breaker management -await serviceRegistry.openCircuitBreaker("user-service"); -await serviceRegistry.closeCircuitBreaker("user-service"); -const status = await serviceRegistry.getCircuitBreakerStatus("user-service"); +You can easily manage your registered services: -// Proto file management (for gRPC services) -await serviceRegistry.setProtoData("grpc-service", "user.proto", protoBlob); -const protoData = await serviceRegistry.getProtoData("grpc-service", "user.proto"); -const allProtos = await serviceRegistry.getAllProtos("grpc-service"); -await serviceRegistry.deleteProto("grpc-service", "user.proto"); +```typescript +// Get a list of all registered services +const services = await serviceRegistry.getRegisteredServices(); -// Service discovery -const methods = await serviceRegistry.discover("user-service", true); +// Get details for a specific service +const service = await serviceRegistry.getService("user-service"); -// Remove service +// Remove a service await serviceRegistry.removeService("user-service"); ``` +For a complete method reference, see the [ServiceRegistryClient API Reference](./docs/api-reference/service-registry-client.md). + ## Metadata ### MetadataClient From d8071b1f541ebdb2e109e945057dd00c2dfe6f86 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Mon, 6 Oct 2025 17:58:42 +0300 Subject: [PATCH 48/60] update Metadata section --- README.md | 168 +++++++++++------------------------------------------- 1 file changed, 34 insertions(+), 134 deletions(-) diff --git a/README.md b/README.md index 781ea120..d02a725b 100644 --- a/README.md +++ b/README.md @@ -1040,163 +1040,63 @@ For a complete method reference, see the [ServiceRegistryClient API Reference](. ## Metadata -### MetadataClient +In Conductor, "metadata" refers to the definitions of your tasks and workflows. Before you can execute a workflow, you must register its definition with Conductor. The `MetadataClient` provides the tools to manage these definitions. -The `MetadataClient` class provides methods for managing task and workflow definitions in Conductor. For a complete method reference, see the [MetadataClient API Reference](./docs/api-reference/metadata-client.md). +### Quick Start: Managing Metadata -```typescript -import { MetadataClient } from "@io-orkes/conductor-javascript"; - -const metadataClient = new MetadataClient(client); -``` +Here’s how to manage your task and workflow definitions: -### Task Definition Factory +#### Step 1: Create a MetadataClient -The `taskDefinition` function provides a convenient way to create task definitions with default values: +First, create an instance of the `MetadataClient`: ```typescript -import { taskDefinition } from "@io-orkes/conductor-javascript"; +import { MetadataClient, taskDefinition, workflowDef } from "@io-orkes/conductor-javascript"; -const taskDef = taskDefinition({ - // Required fields - name: "task_name", // Task name (required) - - // Optional fields with defaults - ownerApp: "", // Optional: owner application (default: "") - description: "", // Optional: task description (default: "") - retryCount: 3, // Optional: number of retries (default: 3) - timeoutSeconds: 3600, // Optional: task timeout in seconds (default: 3600 = 1 hour) - inputKeys: [], // Optional: list of input keys (default: []) - outputKeys: [], // Optional: list of output keys (default: []) - timeoutPolicy: "TIME_OUT_WF", // Optional: "RETRY" | "TIME_OUT_WF" | "ALERT_ONLY" (default: "TIME_OUT_WF") - retryLogic: "FIXED", // Optional: "FIXED" | "EXPONENTIAL_BACKOFF" | "LINEAR_BACKOFF" (default: "FIXED") - retryDelaySeconds: 60, // Optional: delay between retries in seconds (default: 60) - responseTimeoutSeconds: 600, // Optional: response timeout in seconds (default: 600) - concurrentExecLimit: 0, // Optional: max concurrent executions (0 = unlimited) (default: 0) - inputTemplate: {}, // Optional: default input template (default: {}) - rateLimitPerFrequency: 0, // Optional: rate limit count (0 = no limit) (default: 0) - rateLimitFrequencyInSeconds: 1, // Optional: rate limit window in seconds (default: 1) - ownerEmail: "", // Optional: owner email (default: "") - pollTimeoutSeconds: 3600, // Optional: poll timeout in seconds (default: 3600) - backoffScaleFactor: 1 // Optional: backoff multiplier for retry (default: 1) -}); +const metadataClient = new MetadataClient(client); ``` -### Register Task Definition +#### Step 2: Define and Register a Task + +Create a task definition and register it. The `taskDefinition` factory provides sensible defaults for optional fields. ```typescript -const taskDef = { - name: "process_order", - description: "Process customer order", - timeoutSeconds: 300, +// Define a task +const taskDef = taskDefinition({ + name: "my_sdk_task", + description: "A task created via the SDK", + ownerEmail: "dev@example.com", retryCount: 3, - retryDelaySeconds: 60, - responseTimeoutSeconds: 300, - pollTimeoutSeconds: 300, - pollIntervalSeconds: 30, - concurrentExecLimit: 10, - rateLimitPerFrequency: 100, - rateLimitFrequencyInSeconds: 60, - ownerEmail: "owner@example.com", - inputTemplate: { - orderId: "string", - customerId: "string" - }, - outputTemplate: { - processedOrderId: "string", - status: "string" - }, - inputKeys: ["orderId", "customerId"], - outputKeys: ["processedOrderId", "status"], - tags: ["order", "processing"], - executionNameSpace: "orders", - isolationGroupId: "order_processing", - maxConcurrentExecutions: 5 -}; +}); +// Register the task definition await metadataClient.registerTask(taskDef); ``` -### Update Task Definition +#### Step 3: Define and Register a Workflow -```typescript -const updatedTaskDef = { - ...taskDef, - timeoutSeconds: 600, // Increased timeout - retryCount: 5 // Increased retry count -}; - -await metadataClient.updateTask(updatedTaskDef); -``` - -### Unregister Task Definition +Define your workflow using the task you just registered, and then register the workflow definition. ```typescript -await metadataClient.unregisterTask("process_order"); -``` - -### Register Workflow Definition - -```typescript -const workflowDef = { - name: "order_processing_workflow", - version: 1, - description: "Complete order processing workflow", - tasks: [ - { - name: "validate_order", - taskReferenceName: "validate_order_ref", - type: "SIMPLE", - inputParameters: { - orderId: "${workflow.input.orderId}" - } - }, - { - name: "process_payment", - taskReferenceName: "process_payment_ref", - type: "SIMPLE", - inputParameters: { - orderId: "${workflow.input.orderId}", - amount: "${workflow.input.amount}" - } - }, - { - name: "send_confirmation", - taskReferenceName: "send_confirmation_ref", - type: "SIMPLE", - inputParameters: { - orderId: "${workflow.input.orderId}", - customerEmail: "${workflow.input.customerEmail}" - } - } - ], - inputParameters: ["orderId", "amount", "customerEmail"], - outputParameters: { - processedOrderId: "${validate_order_ref.output.processedOrderId}", - paymentStatus: "${process_payment_ref.output.status}", - confirmationSent: "${send_confirmation_ref.output.sent}" - }, - failureWorkflow: "order_failure_workflow", - restartable: true, - workflowStatusListenerEnabled: true, - schemaVersion: 2, - ownerEmail: "workflow-owner@example.com", - timeoutPolicy: "ALERT_ONLY", - timeoutSeconds: 3600, - variables: { - maxRetries: 3, - retryDelay: 60 - } +// Define a workflow that uses the task +const wf = { + name: "my_sdk_workflow", + version: 1, + ownerEmail: "dev@example.com", + tasks: [{ + name: "my_sdk_task", + taskReferenceName: "my_sdk_task_ref", + type: "SIMPLE", + }], + inputParameters: [], + timeoutSeconds: 0, }; -await metadataClient.registerWorkflowDef(workflowDef); +// Register the workflow definition +await metadataClient.registerWorkflowDef(wf); ``` -### Unregister Workflow Definition - -```typescript -await metadataClient.unregisterWorkflow("order_processing_workflow", 1); -``` +For a complete method reference, see the [MetadataClient API Reference](./docs/api-reference/metadata-client.md). ## Human Tasks From 7ac9132aee67e046f38ed94cf5d95171da8d4ccf Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Mon, 6 Oct 2025 18:11:33 +0300 Subject: [PATCH 49/60] update workflows section --- README.md | 332 ++++++++++++++++-------------------------------------- 1 file changed, 100 insertions(+), 232 deletions(-) diff --git a/README.md b/README.md index d02a725b..9eba39a2 100644 --- a/README.md +++ b/README.md @@ -244,131 +244,104 @@ See the [Workers](#workers) section for implementation details. ## Workflows -Workflows orchestrate the execution of multiple tasks in a coordinated sequence. This section explains: -- **What workflows are** and how they work -- **How to create workflows** step-by-step using task generators -- **Task generators** for different task types -- **How to manage workflow lifecycle** (register, start, pause, resume, terminate, search) +Workflows are the heart of Conductor, orchestrating tasks to perform complex processes. This guide walks you through the entire lifecycle of a workflow, from creation to monitoring. -### WorkflowExecutor +### The WorkflowExecutor -The `WorkflowExecutor` class is your main interface for managing workflows. It provides methods to register, start, monitor, and control workflow execution. +The `WorkflowExecutor` is your primary tool for interacting with workflows. It allows you to register, start, and manage their execution. ```typescript import { WorkflowExecutor } from "@io-orkes/conductor-javascript"; -// Create executor instance +// Create an executor instance const executor = new WorkflowExecutor(client); ``` -### Creating Workflows +### Step 1: Define Your Workflow Structure -Creating a workflow in Conductor involves three main steps: - -#### Step 1: Define Your Workflow Structure - -A workflow definition is a JavaScript object that describes your workflow: - -```typescript -const workflowDef = { - name: "order_fulfillment", // Unique workflow name - version: 1, // Version number - description: "Process and fulfill customer orders", - ownerEmail: "team@example.com", // Optional: owner email - tasks: [ - // Tasks will be added here (Step 2) - ], - inputParameters: [], // Expected input parameter names - outputParameters: {}, // Output mapping from task results - timeoutSeconds: 3600, // Workflow timeout (0 = no timeout) - timeoutPolicy: "ALERT_ONLY" // What to do on timeout -}; -``` - -#### Step 2: Build Your Task List - -Use **task generators** to create the task list for your workflow. Task generators are helper functions that create properly formatted task definitions: +A workflow definition is a blueprint for your process. It outlines the workflow's properties and the sequence of tasks. ```typescript -import { - simpleTask, - httpTask, - switchTask -} from "@io-orkes/conductor-javascript"; - const workflowDef = { name: "order_fulfillment", version: 1, description: "Process and fulfill customer orders", + ownerEmail: "team@example.com", tasks: [ - // Task 1: Validate order (custom worker) - simpleTask( - "validate_order_ref", // taskReferenceName: unique within workflow - "validate_order", // taskName: matches worker's taskDefName - { // inputParameters: data for this task - orderId: "${workflow.input.orderId}", - customerId: "${workflow.input.customerId}" - } - ), - - // Task 2: Check inventory via HTTP API - httpTask( - "check_inventory_ref", - { - uri: "https://api.inventory.com/check", - method: "POST", - body: { - productId: "${workflow.input.productId}", - quantity: "${workflow.input.quantity}" - }, - headers: { - "Content-Type": "application/json" - } - } - ), - - // Task 3: Conditional routing based on inventory - switchTask( - "route_order_ref", - "${check_inventory_ref.output.inStock}", - { - "true": [ - simpleTask("fulfill_order_ref", "fulfill_order", { - orderId: "${workflow.input.orderId}" - }) - ], - "false": [ - simpleTask("backorder_ref", "create_backorder", { - orderId: "${workflow.input.orderId}" - }) - ] - } - ) + // Tasks will be added in the next step ], inputParameters: ["orderId", "customerId", "productId", "quantity"], outputParameters: { status: "${route_order_ref.output.status}", fulfillmentId: "${fulfill_order_ref.output.fulfillmentId}" - } + }, + timeoutSeconds: 3600, + timeoutPolicy: "ALERT_ONLY" }; ``` -**Key Concepts:** +### Step 2: Build Your Task List + +Use **Task Generators** to populate the `tasks` array. These helper functions simplify the creation of different task types. + +```typescript +import { simpleTask, httpTask, switchTask } from "@io-orkes/conductor-javascript"; + +const tasks = [ + // Task 1: A custom task to validate the order + simpleTask( + "validate_order_ref", + "validate_order", + { + orderId: "${workflow.input.orderId}", + customerId: "${workflow.input.customerId}" + } + ), + + // Task 2: An HTTP task to check inventory + httpTask( + "check_inventory_ref", + { + uri: "https://api.inventory.com/check", + method: "POST", + body: { + productId: "${workflow.input.productId}", + quantity: "${workflow.input.quantity}" + } + } + ), + + // Task 3: A switch task for conditional logic + switchTask( + "route_order_ref", + "${check_inventory_ref.output.inStock}", + { + "true": [ + simpleTask("fulfill_order_ref", "fulfill_order", {}) + ], + "false": [ + simpleTask("backorder_ref", "create_backorder", {}) + ] + } + ) +]; -- **taskReferenceName**: A unique identifier for the task within this workflow. Used to reference task outputs (e.g., `${task_ref.output.fieldName}`) -- **inputParameters**: Use `${workflow.input.fieldName}` to access workflow inputs and `${other_task_ref.output.fieldName}` to access previous task outputs -- **Task Generators**: Each task type has a generator function (e.g., `simpleTask`, `httpTask`, `switchTask`). See [Task Generators Reference](./docs/api-reference/task-generators.md) for all available types. +// Add the tasks to your workflow definition +workflowDef.tasks = tasks; +``` + +**Key Concepts:** +- **`taskReferenceName`**: A unique identifier for a task instance within a workflow. Used for data flow (e.g., `${check_inventory_ref.output.inStock}`). +- **Input Parameters**: Use `${workflow.input.fieldName}` to access initial workflow inputs and `${task_ref.output.fieldName}` to access outputs from previous tasks. +- **Task Generators**: Helper functions like `simpleTask`, `httpTask`, etc., that create task definitions. For a complete list, see the [Task Generators Reference](./docs/api-reference/task-generators.md). -#### Step 3: Register and Start Your Workflow +### Step 3: Register and Start Your Workflow -Once your workflow definition is ready, register it with Conductor and start executing it: +With the definition complete, register it with Conductor and start an execution. ```typescript -// Register the workflow definition -await executor.registerWorkflow( - true, // overwrite: replace existing definition if it exists - workflowDef -); +// Register the workflow definition (overwrite if it exists) +await executor.registerWorkflow(true, workflowDef); // Start a workflow execution const executionId = await executor.startWorkflow({ @@ -385,32 +358,27 @@ const executionId = await executor.startWorkflow({ console.log(`Workflow started with ID: ${executionId}`); ``` -### Managing Workflow Execution +### Step 4: Manage and Monitor Execution -Once your workflow is running, you can monitor and control its execution using these operations: +Once a workflow is running, you can monitor its status, control its execution, and debug individual tasks. -#### Monitor Workflow Status +#### Check Workflow Status + +Retrieve the current status and output of a running workflow. ```typescript -// Get workflow status summary const status = await executor.getWorkflowStatus( executionId, - true, // includeOutput - true // includeVariables + true, // includeOutput + true // includeVariables ); - -// The `getWorkflowStatus()` method returns a `WorkflowStatus` object with the following properties: -interface WorkflowStatus { - workflowId?: string; - correlationId?: string; - output?: Record; - variables?: Record; - status?: "RUNNING" | "COMPLETED" | "FAILED" | "TIMED_OUT" | "TERMINATED" | "PAUSED"; -} +console.log(`Workflow status is: ${status.status}`); ``` #### Control Workflow Execution +You can pause, resume, or terminate workflows as needed. + ```typescript // Pause a running workflow await executor.pause(executionId); @@ -418,147 +386,47 @@ await executor.pause(executionId); // Resume a paused workflow await executor.resume(executionId); -// Terminate a workflow with a reason -await executor.terminate(executionId, "Terminating due to error"); - -// Restart a completed/failed workflow -await executor.restart(executionId, true); // useLatestDefinitions - -// Retry a failed workflow from the last failed task -await executor.retry(executionId, false); // resumeSubworkflowTasks - -// Rerun a workflow with potentially modified parameters -const newWorkflowId = await executor.reRun(executionId); -``` - -#### Search Workflows - -```typescript -// Search for workflows with filters -const searchResults = await executor.search( - 0, // start: starting index - 10, // size: number of results - "status:RUNNING", // query: e.g., "workflowType:my_workflow AND status:FAILED" - "*", // freeText: use "*" for all - "startTime:DESC", // sort (optional) - false // skipCache (optional) -); - -// Common search patterns: -// - By status: "status:RUNNING" -// - By name: "workflowType:order_fulfillment" -// - By date: "startTime:[2025-01-01 TO 2025-12-31]" -// - Combined: "workflowType:my_workflow AND status:FAILED" +// Terminate a workflow +await executor.terminate(executionId, "Aborted due to customer cancellation"); ``` -### WorkflowExecutor API Reference +#### Search for Workflows -For a complete method reference for the `WorkflowExecutor` class, see the [WorkflowExecutor API Reference](./docs/api-reference/workflow-executor.md). - -Here is a quick example of how to interact with a running workflow: +Search for workflow executions based on various criteria. ```typescript -import { WorkflowExecutor } from "@io-orkes/conductor-javascript"; - -const executor = new WorkflowExecutor(client); -const executionId = "your-workflow-execution-id"; - -// Get workflow status summary -const status = await executor.getWorkflowStatus( - executionId, - true, // includeOutput - true // includeVariables +const searchResults = await executor.search( + 0, + 10, + "status:RUNNING AND workflowType:order_fulfillment", + "*", + "startTime:DESC" ); -console.log(`Workflow status: ${status.status}`); - -// Terminate a workflow with a reason -await executor.terminate(executionId, "Terminating due to error"); ``` -### Monitoring & Debugging Tasks +#### Monitor and Debug Tasks -The `TaskClient` provides capabilities for monitoring and debugging tasks within your workflow executions. For a complete method reference, see the [TaskClient API Reference](./docs/api-reference/task-client.md). +For a deeper look into the tasks within a workflow, use the `TaskClient`. ```typescript import { TaskClient } from "@io-orkes/conductor-javascript"; const taskClient = new TaskClient(client); -// Search tasks -const searchResults = await taskClient.search(0, 10, "", "*", "status:COMPLETED"); - -// Get task by ID -const task = await taskClient.getTask(taskId); - -// Update task result (advanced use case) -await taskClient.updateTaskResult( - workflowId, - taskReferenceName, - "COMPLETED", - { result: "success" } +// Find all failed tasks for a specific workflow run +const failedTasks = await taskClient.search( + 0, + 100, + "startTime:DESC", + "*", + `status:FAILED AND workflowId:${executionId}` ); -``` - -#### Task Statuses - -Tasks in Conductor have various statuses that indicate their current state: - -- **SCHEDULED**: Task is scheduled for execution -- **IN_PROGRESS**: Task is currently being executed -- **COMPLETED**: Task completed successfully -- **COMPLETED_WITH_ERRORS**: Task completed but with errors -- **FAILED**: Task execution failed -- **FAILED_WITH_TERMINAL_ERROR**: Task failed with a terminal error (no retries) -- **TIMED_OUT**: Task execution timed out -- **CANCELED**: Task was canceled -- **SKIPPED**: Task was skipped - -#### Searching & Filtering Tasks - -You can search for tasks using various criteria: - -```typescript -// Search by status -const completedTasks = await taskClient.search(0, 10, "", "*", "status:COMPLETED"); -// Search by workflow -const workflowTasks = await taskClient.search(0, 10, "", "*", "workflowId:workflow-123"); - -// Search by task type -const simpleTasks = await taskClient.search(0, 10, "", "*", "taskType:SIMPLE"); - -// Search by free text -const textSearch = await taskClient.search(0, 10, "", "error", ""); - -// Search with sorting -const sortedTasks = await taskClient.search(0, 10, "startTime:DESC", "*", "status:FAILED"); +// Get details of a specific task by its ID +const taskDetails = await taskClient.getTask(failedTasks.results[0].taskId); ``` -**Search Parameters:** -- `start`: Starting index for pagination (default: 0) -- `size`: Number of results to return (default: 100) -- `sort`: Sort field and direction (e.g., "startTime:DESC", "status:ASC") -- `freeText`: Free text search term (use "*" for all) -- `query`: Structured query string (e.g., "status:FAILED", "workflowId:workflow-123") - -#### Common Search Queries - -```typescript -// Find all failed tasks -const failedTasks = await taskClient.search(0, 100, "startTime:DESC", "*", "status:FAILED"); - -// Find tasks for a specific workflow -const workflowTasks = await taskClient.search(0, 100, "", "*", "workflowId:my-workflow-123"); - -// Find tasks by worker ID -const workerTasks = await taskClient.search(0, 100, "", "*", "workerId:worker-123"); - -// Find tasks with specific input data -const inputTasks = await taskClient.search(0, 100, "", "*", "inputData.orderId:order-123"); - -// Find tasks that timed out -const timeoutTasks = await taskClient.search(0, 100, "endTime:DESC", "*", "status:TIMED_OUT"); -``` +For a complete list of methods, see the [WorkflowExecutor API Reference](./docs/api-reference/workflow-executor.md) and the [TaskClient API Reference](./docs/api-reference/task-client.md). ## Workers From 83de391b448609f121dfe5e6af5d424c06a859e5 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Mon, 6 Oct 2025 18:17:38 +0300 Subject: [PATCH 50/60] update human task section --- README.md | 409 ++++++++++-------------------------------------------- 1 file changed, 73 insertions(+), 336 deletions(-) diff --git a/README.md b/README.md index 9eba39a2..8b5c2db2 100644 --- a/README.md +++ b/README.md @@ -968,369 +968,106 @@ For a complete method reference, see the [MetadataClient API Reference](./docs/a ## Human Tasks -HUMAN tasks are a **type of system task** that enable human interaction within workflows. They pause execution until a person completes an action like approving a request, filling out a form, or reviewing data. +Human tasks integrate human interaction into your automated workflows. They pause a workflow until a person provides input, such as an approval, a correction, or additional information. -**As System Tasks:** -- **No custom workers needed** - Managed by Conductor, not by your code -- **Configured, not coded** - Use the `HumanExecutor` API to manage task lifecycle -- **Form-based** - Users interact through forms you define with TemplateClient -- **Assignment-based** - Tasks are assigned to users or groups -- **State management** - Tasks can be claimed, released, updated, and completed via API +Unlike other tasks, human tasks are managed through a dedicated API (`HumanExecutor`) and often involve UI forms (`TemplateClient`). Because they are a type of **system task**, you don't need to create a custom worker to handle them. -**Unlike other system tasks** (which execute automatically), HUMAN tasks wait for user action via the HumanExecutor API. +### Quick Start: Creating and Managing a Human Task -### HumanExecutor +This guide walks through creating a simple approval workflow. -The `HumanExecutor` class provides comprehensive human task management. For a complete method reference, see the [HumanExecutor API Reference](./docs/api-reference/human-executor.md). +#### Step 1: Create API Clients -```typescript -import { HumanExecutor } from "@io-orkes/conductor-javascript"; - -const humanExecutor = new HumanExecutor(client); - -// Search human tasks -const tasks = await humanExecutor.search({ - states: ["PENDING", "ASSIGNED"], - assignees: [{ userType: "EXTERNAL_USER", user: "john@example.com" }], - taskRefNames: ["approval_task"], - taskInputQuery: "priority:high", - size: 10, - start: 0 -}); - -// Poll for tasks until found -const polledTasks = await humanExecutor.pollSearch({ - states: ["PENDING"], - assignees: [{ userType: "EXTERNAL_USER", user: "john@example.com" }] -}, { - pollInterval: 1000, - maxPollTimes: 30 -}); - -// Get task by ID -const task = await humanExecutor.getTaskById(taskId); - -// Claim task as external user -const claimedTask = await humanExecutor.claimTaskAsExternalUser( - taskId, - "john@example.com", - { overrideAssignment: false, withTemplate: true } -); - -// Claim task as conductor user -const conductorClaimedTask = await humanExecutor.claimTaskAsConductorUser( - taskId, - { overrideAssignment: false, withTemplate: true } -); - -// Release task -await humanExecutor.releaseTask(taskId); - -// Update task output -await humanExecutor.updateTaskOutput(taskId, { - output: { - approved: true, - comments: "Approved with conditions" - } -}); - -// Complete task -await humanExecutor.completeTask(taskId, { - output: { - approved: true, - comments: "Task completed" - } -}); - -// Get template by name and version -const template = await humanExecutor.getTemplateByNameVersion("approval_template", 1); - -// Get template by ID (deprecated, use getTemplateByNameVersion) -const templateById = await humanExecutor.getTemplateById("approval_template"); -``` - -### TemplateClient - -The `TemplateClient` class provides methods for managing human task templates (forms and UI). For a complete method reference, see the [TemplateClient API Reference](./docs/api-reference/template-client.md). +You'll need a `TemplateClient` to manage UI forms and a `HumanExecutor` to interact with the tasks themselves. ```typescript -import { TemplateClient } from "@io-orkes/conductor-javascript"; +import { HumanExecutor, TemplateClient } from "@io-orkes/conductor-javascript"; const templateClient = new TemplateClient(client); +const humanExecutor = new HumanExecutor(client); ``` -#### Register Form Template +#### Step 2: Register a Form Template + +Define and register a form that will be presented to the user. ```typescript const formTemplate = { - name: "approval_form", + name: "simple_approval_form", version: 1, - description: "Order approval form template", + description: "A simple form for approvals", formTemplate: { - name: "Order Approval Form", - fields: [ - { + name: "Approval Form", + fields: [{ name: "approved", type: "boolean", required: true, - label: "Approve Order", - description: "Check to approve the order" - }, - { - name: "comments", - type: "text", - required: false, - label: "Comments", - description: "Additional comments about the approval decision", - maxLength: 500 - }, - { - name: "approver_name", - type: "text", - required: true, - label: "Approver Name", - description: "Name of the person approving the order" - }, - { - name: "approval_date", - type: "date", - required: true, - label: "Approval Date", - description: "Date of approval" - } - ], - validationRules: [ - { - field: "approved", - rule: "required", - message: "Approval decision is required" - }, - { - field: "approver_name", - rule: "minLength:2", - message: "Approver name must be at least 2 characters" - } - ] - }, - uiTemplate: { - name: "Order Approval UI", - template: ` -
-

Order Approval

-
- -
-
- - -
-
- - -
-
- - -
-
- `, - styles: ` - .approval-form { - max-width: 600px; - margin: 0 auto; - padding: 20px; - font-family: Arial, sans-serif; - } - .form-group { - margin-bottom: 15px; - } - .form-group label { - display: block; - margin-bottom: 5px; - font-weight: bold; - } - .form-group input, - .form-group textarea { - width: 100%; - padding: 8px; - border: 1px solid #ddd; - border-radius: 4px; - } - .form-group input[type="checkbox"] { - width: auto; - margin-right: 8px; - } - ` + label: "Approve Request", + }], }, - tags: ["approval", "order", "form"], - ownerEmail: "template-owner@example.com" }; await templateClient.registerTemplate(formTemplate); ``` -#### Register UI Template +#### Step 3: Create a Workflow with a Human Task + +Now, define a workflow that uses the `humanTask` generator. The `taskDefinition` for the human task should specify the template to use. ```typescript -const uiTemplate = { - name: "custom_ui_template", - version: 1, - description: "Custom UI template for human tasks", - uiTemplate: { - name: "Custom Task UI", - template: ` -
-

Custom Task Interface

-
-

Task: {{taskName}}

-

Description: {{taskDescription}}

-
- - {{#each taskInputs}} -
- - -
- {{/each}} -
-
- - -
-
-
- `, - styles: ` - .custom-task-ui { - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; - max-width: 800px; - margin: 0 auto; - padding: 20px; - background-color: #f5f5f5; - border-radius: 8px; - box-shadow: 0 2px 10px rgba(0,0,0,0.1); - } - .custom-task-ui h1 { - color: #333; - text-align: center; - margin-bottom: 30px; - } - .task-content { - background-color: white; - padding: 20px; - border-radius: 6px; - margin-bottom: 20px; - } - .input-field { - margin-bottom: 15px; - } - .input-field label { - display: block; - margin-bottom: 5px; - font-weight: 600; - color: #555; - } - .input-field input { - width: 100%; - padding: 10px; - border: 2px solid #e1e1e1; - border-radius: 4px; - font-size: 14px; - transition: border-color 0.3s ease; - } - .input-field input:focus { - outline: none; - border-color: #007bff; - } - .task-actions { - text-align: center; - margin-top: 20px; - } - .task-actions button { - background-color: #007bff; - color: white; - border: none; - padding: 12px 24px; - margin: 0 10px; - border-radius: 4px; - cursor: pointer; - font-size: 14px; - transition: background-color 0.3s ease; - } - .task-actions button:hover { - background-color: #0056b3; - } - .task-actions button:last-child { - background-color: #dc3545; - } - .task-actions button:last-child:hover { - background-color: #c82333; - } - `, - scripts: ` - function completeTask() { - const formData = new FormData(document.querySelector('.custom-task-ui')); - const outputData = {}; - for (let [key, value] of formData.entries()) { - outputData[key] = value; - } - - // Send completion data to Conductor - fetch('/api/tasks/complete', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - taskId: '{{taskId}}', - outputData: outputData, - status: 'COMPLETED' - }) - }) - .then(response => response.json()) - .then(data => { - alert('Task completed successfully!'); - window.close(); - }) - .catch(error => { - console.error('Error completing task:', error); - alert('Error completing task. Please try again.'); - }); - } - - function failTask() { - const reason = prompt('Please provide a reason for failing the task:'); - if (reason) { - fetch('/api/tasks/fail', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - taskId: '{{taskId}}', - reasonForIncompletion: reason, - status: 'FAILED' - }) - }) - .then(response => response.json()) - .then(data => { - alert('Task marked as failed.'); - window.close(); - }) - .catch(error => { - console.error('Error failing task:', error); - alert('Error failing task. Please try again.'); - }); - } - } - ` - }, - tags: ["ui", "custom", "template"], - ownerEmail: "ui-developer@example.com" +import { humanTask } from "@io-orkes/conductor-javascript"; + +// Define the human task +const approvalTask = humanTask( + "human_approval_ref", + "human_approval_task", + { template: "simple_approval_form" } +); + +// Define the workflow +const approvalWorkflow = { + name: "human_approval_workflow", + version: 1, + tasks: [approvalTask], + inputParameters: [], + ownerEmail: "dev@example.com", }; -await templateClient.registerTemplate(uiTemplate); +// Register and start the workflow +await executor.registerWorkflow(true, approvalWorkflow); +const executionId = await executor.startWorkflow({ + name: "human_approval_workflow", + version: 1, +}); +``` + +#### Step 4: Find and Complete the Task + +In a real application, your backend or UI would search for pending tasks and present them to the user. + +```typescript +// Search for pending tasks for a user +const pendingTasks = await humanExecutor.search({ + states: ["PENDING"], + // assignees: [{ userType: "EXTERNAL_USER", user: "user@example.com" }], +}); + +if (pendingTasks.results.length > 0) { + const taskId = pendingTasks.results[0].taskId; + + // Claim the task + await humanExecutor.claimTaskAsExternalUser(taskId, "user@example.com"); + + // Complete the task with output + await humanExecutor.completeTask(taskId, { + output: { + approved: true, + comments: "Looks good, approved." + } + }); + + console.log(`Task ${taskId} completed.`); +} ``` + +For a complete list of methods, see the [HumanExecutor API Reference](./docs/api-reference/human-executor.md) and the [TemplateClient API Reference](./docs/api-reference/template-client.md). From 3fec4f93f91375d3964a0d5cc5bc07755700bdf8 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Mon, 6 Oct 2025 18:28:52 +0300 Subject: [PATCH 51/60] Update Workers section --- README.md | 362 ++++++++---------------------------------------------- 1 file changed, 52 insertions(+), 310 deletions(-) diff --git a/README.md b/README.md index 8b5c2db2..a76ce736 100644 --- a/README.md +++ b/README.md @@ -430,175 +430,61 @@ For a complete list of methods, see the [WorkflowExecutor API Reference](./docs/ ## Workers -### Overview +Workers are background processes that execute your custom code as part of a workflow. Think of them as specialized functions that: -Workers are background processes that execute tasks in your workflows. Think of them as specialized functions that: - -1. **Poll** the Conductor server asking "Do you have any work for me?" -2. **Execute** the task logic when work is assigned -3. **Report** the results back to Conductor +1. **Poll** the Conductor server for tasks. +2. **Execute** your business logic when a task is assigned. +3. **Report** the results back to the Conductor server. **How Workers Fit In:** ``` Workflow → Creates Tasks → Workers Poll for Tasks → Execute Logic → Return Results → Workflow Continues ``` -The SDK provides the **TaskManager** class - an easy-to-use interface for managing workers efficiently. For a complete method reference, see the [TaskManager API Reference](./docs/api-reference/task-manager.md). - -### Quick Start: Your First Worker - -Here's a simple example to get you started: - -```typescript -import { - orkesConductorClient, - TaskManager, - ConductorWorker -} from "@io-orkes/conductor-javascript"; - -// Step 1: Create your client -const client = await orkesConductorClient({ - serverUrl: "https://play.orkes.io/api", - keyId: "your-key-id", - keySecret: "your-key-secret" -}); - -// Step 2: Define your worker(s) -const workers: ConductorWorker[] = [ - { - // This must match the task name in your workflow - taskDefName: "send_email", - - // This function executes when a task is assigned - execute: async (task) => { - // Get input data from the workflow - const { to, subject, body } = task.inputData; - - // Do your work (send email, call API, process data, etc.) - console.log(`Sending email to ${to}: ${subject}`); - await sendEmailViaAPI(to, subject, body); - - // Return the result - return { - outputData: { - sent: true, - timestamp: new Date().toISOString() - }, - status: "COMPLETED" - }; - } - } -]; - -// Step 3: Create TaskManager and start polling -const manager = new TaskManager(client, workers); -await manager.startPolling(); - -console.log("✅ Worker is now running and waiting for tasks!"); - -// When you're done (e.g., on shutdown): -// await manager.stopPolling(); -``` - -**That's it!** Your worker is now running and will automatically: -- Poll for tasks named `send_email` -- Execute the task logic -- Report results back to Conductor -- Handle errors and retries - -### Understanding Worker Execution Flow +The `TaskManager` class in this SDK simplifies the process of creating and managing workers. -Here's what happens when a workflow creates a task: +### Quick Start: Building a Worker -1. **Workflow runs** and creates a task (e.g., `send_email`) -2. **Worker polls** Conductor: "Any `send_email` tasks for me?" -3. **Conductor responds** with the task and its input data -4. **Worker executes** your `execute` function with the task data -5. **Worker returns** the result (`COMPLETED`, `FAILED`, etc.) -6. **Workflow continues** to the next task based on the result +Building a robust worker involves defining its logic, handling outcomes, and managing its execution. -### Worker Design Principles +#### Step 1: Define the Worker's Logic -When creating workers, follow these principles: +A worker is an object that defines a `taskDefName` (which must match the task name in your workflow) and an `execute` function containing your business logic. When designing workers, it's best to follow these principles: -#### 1. Stateless Workers -Workers should be stateless and not rely on external state: +- **Stateless**: Workers should not rely on local state. +- **Idempotent**: The same task input should always produce the same result. +- **Single Responsibility**: Each worker should be responsible for one specific task type. ```typescript -// ✅ Good - Stateless -const worker: ConductorWorker = { - taskDefName: "process_data", - execute: async (task) => { - const result = await processData(task.inputData); - return { outputData: result, status: "COMPLETED" }; - } -}; - -// ❌ Bad - Stateful -let processedCount = 0; -const worker: ConductorWorker = { - taskDefName: "process_data", - execute: async (task) => { - processedCount++; // This creates state dependency - return { outputData: { count: processedCount }, status: "COMPLETED" }; - } -}; -``` - -#### 2. Idempotent Operations -Workers should produce the same result when executed multiple times: - -```typescript -// ✅ Good - Idempotent -const worker: ConductorWorker = { - taskDefName: "update_user", - execute: async (task) => { - const { userId, data } = task.inputData; - await updateUser(userId, data); // Safe to retry - return { outputData: { updated: true }, status: "COMPLETED" }; - } -}; -``` +import { ConductorWorker } from "@io-orkes/conductor-javascript"; -#### 3. Specific Task Types -Each worker should handle one specific task type: - -```typescript -// ✅ Good - Specific const emailWorker: ConductorWorker = { + // 1. Specify the task name taskDefName: "send_email", + + // 2. Implement the execution logic execute: async (task) => { - await sendEmail(task.inputData); - return { outputData: { sent: true }, status: "COMPLETED" }; - } -}; - -// ❌ Bad - Generic -const genericWorker: ConductorWorker = { - taskDefName: "do_anything", - execute: async (task) => { - // Handles multiple different operations - hard to maintain - if (task.inputData.type === "email") { /* ... */ } - else if (task.inputData.type === "sms") { /* ... */ } - // ... + const { to, subject, body } = task.inputData; + + console.log(`Sending email to ${to}: ${subject}`); + await emailService.send(to, subject, body); // Your business logic + + // 3. Return a result (covered in the next step) + return { + status: "COMPLETED", + outputData: { sent: true, timestamp: new Date().toISOString() } + }; } }; ``` -### Handling Task Results - -Your worker's `execute` function must return an object with at least these two properties: +#### Step 2: Handle Task Outcomes and Errors -```typescript -{ - status: "COMPLETED" | "FAILED" | "FAILED_WITH_TERMINAL_ERROR" | "IN_PROGRESS", - outputData: { /* your result data */ } -} -``` +The `execute` function must return an object indicating the task's outcome. -#### Common Return Patterns +**✅ On Success:** +Return a `COMPLETED` status and any relevant output data. -**✅ Success:** ```typescript return { status: "COMPLETED", @@ -606,188 +492,44 @@ return { }; ``` -**❌ Failure (will retry based on task configuration):** -```typescript -return { - status: "FAILED", - outputData: {}, - logs: [{ log: "Error details for debugging" }] -}; -``` - -**❌ Terminal Failure (no retry, workflow fails immediately):** -```typescript -return { - status: "FAILED_WITH_TERMINAL_ERROR", - outputData: { error: "Invalid input - cannot proceed" } -}; -``` +**❌ On Failure:** +You can control the retry behavior. `FAILED` allows for retries, while `FAILED_WITH_TERMINAL_ERROR` stops the workflow immediately. -**⏳ In Progress (for long-running tasks):** ```typescript -return { - status: "IN_PROGRESS", - outputData: { progress: 50, message: "Processing..." }, - callbackAfterSeconds: 30 // Conductor will check back after 30 seconds -}; +try { + // Risky operation +} catch (error) { + return { + status: "FAILED", // Allows for retries + logs: [{ log: `Error executing task: ${error.message}` }] + }; +} ``` -#### Error Handling in Workers +#### Step 3: Run the Worker with TaskManager -Always wrap your worker logic in try-catch to handle errors gracefully: +The `TaskManager` is responsible for polling Conductor, managing task execution, and reporting back results. You can run a single worker or multiple workers with one manager. ```typescript -const worker: ConductorWorker = { - taskDefName: "risky_operation", - execute: async (task) => { - try { - const result = await performRiskyOperation(task.inputData); - return { - status: "COMPLETED", - outputData: { result } - }; - } catch (error) { - console.error("Worker error:", error); - - // Decide: retry or fail permanently? - const shouldRetry = error.code !== 'INVALID_INPUT'; - - return { - status: shouldRetry ? "FAILED" : "FAILED_WITH_TERMINAL_ERROR", - outputData: { error: error.message }, - logs: [{ - log: `Error: ${error.message}`, - createdTime: Date.now() - }] - }; - } - } -}; -``` - -### Working with Multiple Workers +import { TaskManager } from "@io-orkes/conductor-javascript"; -In real applications, you'll typically have multiple workers for different tasks: - -```typescript -import { TaskManager, ConductorWorker } from "@io-orkes/conductor-javascript"; - -const workers: ConductorWorker[] = [ - // Worker 1: Send emails - { - taskDefName: "send_email", - execute: async (task) => { - const { to, subject, body } = task.inputData; - await emailService.send(to, subject, body); - return { - status: "COMPLETED", - outputData: { sent: true, messageId: "msg_123" } - }; - } - }, - - // Worker 2: Process payments - { - taskDefName: "process_payment", - execute: async (task) => { - const { amount, currency, cardToken } = task.inputData; - const charge = await paymentGateway.charge(amount, currency, cardToken); - return { - status: "COMPLETED", - outputData: { - transactionId: charge.id, - status: charge.status - } - }; - } - }, - - // Worker 3: Generate reports - { - taskDefName: "generate_report", - execute: async (task) => { - const { reportType, startDate, endDate } = task.inputData; - const reportUrl = await reportService.generate(reportType, startDate, endDate); - return { - status: "COMPLETED", - outputData: { reportUrl, generatedAt: new Date().toISOString() } - }; - } - } -]; - -// Start all workers with a single TaskManager -const manager = new TaskManager(client, workers); -await manager.startPolling(); - -console.log("✅ All 3 workers are now running!"); -``` - -**Key Points:** -- Each worker handles a specific task type (identified by `taskDefName`) -- All workers run concurrently and independently -- A single `TaskManager` manages all workers together -- Workers only pick up tasks that match their `taskDefName` - -### TaskManager Advanced Configuration - -The `TaskManager` accepts configuration options to control worker behavior, polling, and error handling. - -```typescript -import { TaskManager, ConductorWorker, DefaultLogger } from "@io-orkes/conductor-javascript"; +// You can pass a single worker or an array of workers +const workers = [emailWorker, anotherWorker, ...]; +// Create the TaskManager const manager = new TaskManager(client, workers, { - // Custom logger for debugging and monitoring (optional) - logger: new DefaultLogger(), - - // Polling and execution options (optional) options: { - pollInterval: 1000, // How often to poll for tasks in ms (default: 100) - concurrency: 5, // Max concurrent task executions per worker (default: 1) - workerID: "worker-group-1", // Unique identifier for this worker group (default: hostname) - domain: undefined, // Task domain for isolation (default: undefined) - batchPollingTimeout: 100 // Batch polling timeout in ms (default: 100) - }, - - // Global error handler called when workers fail (optional) - onError: (error, task) => { - console.error(`Error in task ${task?.taskType}:`, error); - // Send to error tracking service - errorTracker.log(error, { taskId: task?.taskId }); - }, - - // Maximum retry attempts before giving up (optional, default: 3) - maxRetries: 5 + concurrency: 5, // Process up to 5 tasks concurrently + pollInterval: 100, // Poll every 100ms + } }); +// Start polling for tasks await manager.startPolling(); +console.log("Worker is running!"); ``` -#### Dynamic Configuration Updates - -You can update polling options at runtime without stopping workers: - -```typescript -// Adjust polling interval based on load -manager.updatePollingOptions({ - pollInterval: 2000, // Slow down during high load - concurrency: 10 // Increase parallelism -}); -``` - -#### Graceful Shutdown - -Properly stop workers when your application shuts down: - -```typescript -// Graceful shutdown handler -process.on('SIGTERM', async () => { - console.log('Shutting down workers...'); - await manager.stopPolling(); - console.log('Workers stopped gracefully'); - process.exit(0); -}); -``` +For a complete method reference, see the [TaskManager API Reference](./docs/api-reference/task-manager.md). ## Scheduling From f5da7ea28451e2b0aee7bb130c8987346fbc66da Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Mon, 6 Oct 2025 18:32:25 +0300 Subject: [PATCH 52/60] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a76ce736..1d3e8bef 100644 --- a/README.md +++ b/README.md @@ -430,7 +430,7 @@ For a complete list of methods, see the [WorkflowExecutor API Reference](./docs/ ## Workers -Workers are background processes that execute your custom code as part of a workflow. Think of them as specialized functions that: +Workers are background processes that execute tasks in your workflows. Think of them as specialized functions that: 1. **Poll** the Conductor server for tasks. 2. **Execute** your business logic when a task is assigned. From e0c59a9705c51666d867667a0b633263b10202f8 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Mon, 6 Oct 2025 18:39:11 +0300 Subject: [PATCH 53/60] update table of content --- README.md | 57 ++++++++++--------------------------------------------- 1 file changed, 10 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 1d3e8bef..eaff7293 100644 --- a/README.md +++ b/README.md @@ -30,58 +30,21 @@ Show support for the Conductor OSS. Please help spread the awareness by starrin - [System Tasks - Managed by Conductor Server](#system-tasks---managed-by-conductor-server) - [SIMPLE Tasks - Require Custom Workers](#simple-tasks---require-custom-workers) - [Workflows](#workflows) - - [WorkflowExecutor](#workflowexecutor) - - [Creating Workflows](#creating-workflows) - - [Task Generators Reference](#task-generators-reference) - - [Simple Task](#simple-task) - - [HTTP Task](#http-task) - - [Switch Task](#switch-task) - - [Fork-Join Task](#fork-join-task) - - [Do-While Task](#do-while-task) - - [Sub-Workflow Task](#sub-workflow-task) - - [Event Task](#event-task) - - [Wait Task](#wait-task) - - [Terminate Task](#terminate-task) - - [Set Variable Task](#set-variable-task) - - [JSON JQ Transform Task](#json-jq-transform-task) - - [Kafka Publish Task](#kafka-publish-task) - - [Inline Task](#inline-task) - - [Dynamic Fork Task](#dynamic-fork-task) - - [Join Task](#join-task) - - [Human Task](#human-task) - - [Managing Workflow Execution](#managing-workflow-execution) - - [WorkflowExecutor API Reference](#workflowexecutor-api-reference) - - [Monitoring & Debugging Tasks](#monitoring--debugging-tasks) - - [Task Statuses](#task-statuses) - - [Searching & Filtering Tasks](#searching--filtering-tasks) - - [Common Search Queries](#common-search-queries) + - [The WorkflowExecutor](#the-workflowexecutor) + - [Step 1: Define Your Workflow Structure](#step-1-define-your-workflow-structure) + - [Step 2: Build Your Task List](#step-2-build-your-task-list) + - [Step 3: Register and Start Your Workflow](#step-3-register-and-start-your-workflow) + - [Step 4: Manage and Monitor Execution](#step-4-manage-and-monitor-execution) - [Workers](#workers) - - [Overview](#overview) - - [Quick Start: Your First Worker](#quick-start-your-first-worker) - - [Understanding Worker Execution Flow](#understanding-worker-execution-flow) - - [Worker Design Principles](#worker-design-principles) - - [Handling Task Results](#handling-task-results) - - [Working with Multiple Workers](#working-with-multiple-workers) - - [TaskManager Advanced Configuration](#taskmanager-advanced-configuration) - - [Dynamic Configuration Updates](#dynamic-configuration-updates) - - [Graceful Shutdown](#graceful-shutdown) + - [Quick Start: Building a Worker](#quick-start-building-a-worker) - [Scheduling](#scheduling) - - [SchedulerClient](#schedulerclient) + - [Quick Start: Scheduling a Workflow](#quick-start-scheduling-a-workflow) - [Service Registry](#service-registry) - - [ServiceRegistryClient](#serviceregistryclient) + - [Quick Start: Using the Service Registry](#quick-start-using-the-service-registry) - [Metadata](#metadata) - - [MetadataClient](#metadataclient) - - [Task Definition Factory](#task-definition-factory) - - [Register Task Definition](#register-task-definition) - - [Update Task Definition](#update-task-definition) - - [Unregister Task Definition](#unregister-task-definition) - - [Register Workflow Definition](#register-workflow-definition) - - [Unregister Workflow Definition](#unregister-workflow-definition) + - [Quick Start: Managing Metadata](#quick-start-managing-metadata) - [Human Tasks](#human-tasks) - - [HumanExecutor](#humanexecutor) - - [TemplateClient](#templateclient) - - [Register Form Template](#register-form-template) - - [Register UI Template](#register-ui-template) + - [Quick Start: Creating and Managing a Human Task](#quick-start-creating-and-managing-a-human-task) ## Installation From bf9dcc0e5dee765aaaf204b1ecbb0abe23be5f27 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Mon, 6 Oct 2025 18:50:22 +0300 Subject: [PATCH 54/60] Update README.md --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index eaff7293..be39c4de 100644 --- a/README.md +++ b/README.md @@ -37,14 +37,30 @@ Show support for the Conductor OSS. Please help spread the awareness by starrin - [Step 4: Manage and Monitor Execution](#step-4-manage-and-monitor-execution) - [Workers](#workers) - [Quick Start: Building a Worker](#quick-start-building-a-worker) + - [Step 1: Define the Worker's Logic](#step-1-define-the-workers-logic) + - [Step 2: Handle Task Outcomes and Errors](#step-2-handle-task-outcomes-and-errors) + - [Step 3: Run the Worker with TaskManager](#step-3-run-the-worker-with-taskmanager) - [Scheduling](#scheduling) - [Quick Start: Scheduling a Workflow](#quick-start-scheduling-a-workflow) + - [Step 1: Create a SchedulerClient](#step-1-create-a-schedulerclient) + - [Step 2: Define the Schedule](#step-2-define-the-schedule) + - [Step 3: Manage the Schedule](#step-3-manage-the-schedule) - [Service Registry](#service-registry) - [Quick Start: Using the Service Registry](#quick-start-using-the-service-registry) + - [Step 1: Create a ServiceRegistryClient](#step-1-create-a-serviceregistryclient) + - [Step 2: Register a Service](#step-2-register-a-service) + - [Step 3: Manage Services](#step-3-manage-services) - [Metadata](#metadata) - [Quick Start: Managing Metadata](#quick-start-managing-metadata) + - [Step 1: Create a MetadataClient](#step-1-create-a-metadataclient) + - [Step 2: Define and Register a Task](#step-2-define-and-register-a-task) + - [Step 3: Define and Register a Workflow](#step-3-define-and-register-a-workflow) - [Human Tasks](#human-tasks) - [Quick Start: Creating and Managing a Human Task](#quick-start-creating-and-managing-a-human-task) + - [Step 1: Create API Clients](#step-1-create-api-clients) + - [Step 2: Register a Form Template](#step-2-register-a-form-template) + - [Step 3: Create a Workflow with a Human Task](#step-3-create-a-workflow-with-a-human-task) + - [Step 4: Find and Complete the Task](#step-4-find-and-complete-the-task) ## Installation From 901b04a737ffa5066033875db5c0359b49165451 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Mon, 6 Oct 2025 18:58:09 +0300 Subject: [PATCH 55/60] Update README.md --- README.md | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/README.md b/README.md index be39c4de..47abbb13 100644 --- a/README.md +++ b/README.md @@ -36,26 +36,31 @@ Show support for the Conductor OSS. Please help spread the awareness by starrin - [Step 3: Register and Start Your Workflow](#step-3-register-and-start-your-workflow) - [Step 4: Manage and Monitor Execution](#step-4-manage-and-monitor-execution) - [Workers](#workers) + - [The TaskManager](#the-taskmanager) - [Quick Start: Building a Worker](#quick-start-building-a-worker) - [Step 1: Define the Worker's Logic](#step-1-define-the-workers-logic) - [Step 2: Handle Task Outcomes and Errors](#step-2-handle-task-outcomes-and-errors) - [Step 3: Run the Worker with TaskManager](#step-3-run-the-worker-with-taskmanager) - [Scheduling](#scheduling) + - [The SchedulerClient](#the-schedulerclient) - [Quick Start: Scheduling a Workflow](#quick-start-scheduling-a-workflow) - [Step 1: Create a SchedulerClient](#step-1-create-a-schedulerclient) - [Step 2: Define the Schedule](#step-2-define-the-schedule) - [Step 3: Manage the Schedule](#step-3-manage-the-schedule) - [Service Registry](#service-registry) + - [The ServiceRegistryClient](#the-serviceregistryclient) - [Quick Start: Using the Service Registry](#quick-start-using-the-service-registry) - [Step 1: Create a ServiceRegistryClient](#step-1-create-a-serviceregistryclient) - [Step 2: Register a Service](#step-2-register-a-service) - [Step 3: Manage Services](#step-3-manage-services) - [Metadata](#metadata) + - [The MetadataClient](#the-metadataclient) - [Quick Start: Managing Metadata](#quick-start-managing-metadata) - [Step 1: Create a MetadataClient](#step-1-create-a-metadataclient) - [Step 2: Define and Register a Task](#step-2-define-and-register-a-task) - [Step 3: Define and Register a Workflow](#step-3-define-and-register-a-workflow) - [Human Tasks](#human-tasks) + - [The HumanExecutor and TemplateClient](#the-humanexecutor-and-templateclient) - [Quick Start: Creating and Managing a Human Task](#quick-start-creating-and-managing-a-human-task) - [Step 1: Create API Clients](#step-1-create-api-clients) - [Step 2: Register a Form Template](#step-2-register-a-form-template) @@ -422,6 +427,21 @@ Workflow → Creates Tasks → Workers Poll for Tasks → Execute Logic → Retu The `TaskManager` class in this SDK simplifies the process of creating and managing workers. +### The TaskManager + +The `TaskManager` is the primary tool for managing workers. It handles polling, task execution, and result reporting, allowing you to run multiple workers concurrently. + +```typescript +import { TaskManager } from "@io-orkes/conductor-javascript"; + +const manager = new TaskManager(client, workers, { + options: { + concurrency: 5, + pollInterval: 100, + } +}); +``` + ### Quick Start: Building a Worker Building a robust worker involves defining its logic, handling outcomes, and managing its execution. @@ -514,6 +534,16 @@ For a complete method reference, see the [TaskManager API Reference](./docs/api- The Conductor Scheduler allows you to run workflows at specific times or intervals, defined by a CRON expression. This is useful for tasks like nightly data processing, weekly reports, or any time-based automation. +### The SchedulerClient + +The `SchedulerClient` is used to create, manage, and delete workflow schedules. + +```typescript +import { SchedulerClient } from "@io-orkes/conductor-javascript"; + +const scheduler = new SchedulerClient(client); +``` + ### Quick Start: Scheduling a Workflow Here’s how to schedule a workflow in three steps: @@ -577,6 +607,16 @@ For a complete method reference, see the [SchedulerClient API Reference](./docs/ The Service Registry in Conductor allows you to manage and discover microservices. It also provides built-in circuit breaker functionality to improve the resilience of your distributed system. +### The ServiceRegistryClient + +The `ServiceRegistryClient` is used to register, manage, and discover services. + +```typescript +import { ServiceRegistryClient } from "@io-orkes/conductor-javascript"; + +const serviceRegistry = new ServiceRegistryClient(client); +``` + ### Quick Start: Using the Service Registry Here’s how to get started with the `ServiceRegistryClient`: @@ -631,6 +671,16 @@ For a complete method reference, see the [ServiceRegistryClient API Reference](. In Conductor, "metadata" refers to the definitions of your tasks and workflows. Before you can execute a workflow, you must register its definition with Conductor. The `MetadataClient` provides the tools to manage these definitions. +### The MetadataClient + +The `MetadataClient` is used to register and manage task and workflow definitions. + +```typescript +import { MetadataClient, taskDefinition, workflowDef } from "@io-orkes/conductor-javascript"; + +const metadataClient = new MetadataClient(client); +``` + ### Quick Start: Managing Metadata Here’s how to manage your task and workflow definitions: @@ -693,6 +743,18 @@ Human tasks integrate human interaction into your automated workflows. They paus Unlike other tasks, human tasks are managed through a dedicated API (`HumanExecutor`) and often involve UI forms (`TemplateClient`). Because they are a type of **system task**, you don't need to create a custom worker to handle them. +### The HumanExecutor and TemplateClient + +- **`HumanExecutor`**: Manages the lifecycle of human tasks—searching, claiming, and completing them. +- **`TemplateClient`**: Manages the UI forms and templates that are presented to users. + +```typescript +import { HumanExecutor, TemplateClient } from "@io-orkes/conductor-javascript"; + +const humanExecutor = new HumanExecutor(client); +const templateClient = new TemplateClient(client); +``` + ### Quick Start: Creating and Managing a Human Task This guide walks through creating a simple approval workflow. From f6c72e1aff04f302d259c30a5ec86070ebf0b6da Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Mon, 6 Oct 2025 19:10:14 +0300 Subject: [PATCH 56/60] Update README.md --- README.md | 90 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 49 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 47abbb13..a661b86d 100644 --- a/README.md +++ b/README.md @@ -31,16 +31,18 @@ Show support for the Conductor OSS. Please help spread the awareness by starrin - [SIMPLE Tasks - Require Custom Workers](#simple-tasks---require-custom-workers) - [Workflows](#workflows) - [The WorkflowExecutor](#the-workflowexecutor) - - [Step 1: Define Your Workflow Structure](#step-1-define-your-workflow-structure) - - [Step 2: Build Your Task List](#step-2-build-your-task-list) - - [Step 3: Register and Start Your Workflow](#step-3-register-and-start-your-workflow) - - [Step 4: Manage and Monitor Execution](#step-4-manage-and-monitor-execution) + - [Quick Start: Creating a Workflow](#quick-start-creating-a-workflow) + - [Step 1: Define Your Workflow Structure](#step-1-define-your-workflow-structure) + - [Step 2: Build Your Task List](#step-2-build-your-task-list) + - [Step 3: Register and Start Your Workflow](#step-3-register-and-start-your-workflow) + - [Step 4: Manage and Monitor Execution](#step-4-manage-and-monitor-execution) - [Workers](#workers) - [The TaskManager](#the-taskmanager) - [Quick Start: Building a Worker](#quick-start-building-a-worker) - [Step 1: Define the Worker's Logic](#step-1-define-the-workers-logic) - [Step 2: Handle Task Outcomes and Errors](#step-2-handle-task-outcomes-and-errors) - [Step 3: Run the Worker with TaskManager](#step-3-run-the-worker-with-taskmanager) + - [Worker Design Principles](#worker-design-principles) - [Scheduling](#scheduling) - [The SchedulerClient](#the-schedulerclient) - [Quick Start: Scheduling a Workflow](#quick-start-scheduling-a-workflow) @@ -241,7 +243,9 @@ import { WorkflowExecutor } from "@io-orkes/conductor-javascript"; const executor = new WorkflowExecutor(client); ``` -### Step 1: Define Your Workflow Structure +### Quick Start: Creating a Workflow + +#### Step 1: Define Your Workflow Structure A workflow definition is a blueprint for your process. It outlines the workflow's properties and the sequence of tasks. @@ -264,7 +268,7 @@ const workflowDef = { }; ``` -### Step 2: Build Your Task List +#### Step 2: Build Your Task List Use **Task Generators** to populate the `tasks` array. These helper functions simplify the creation of different task types. @@ -273,37 +277,37 @@ import { simpleTask, httpTask, switchTask } from "@io-orkes/conductor-javascript const tasks = [ // Task 1: A custom task to validate the order - simpleTask( + simpleTask( "validate_order_ref", "validate_order", { - orderId: "${workflow.input.orderId}", - customerId: "${workflow.input.customerId}" - } - ), - + orderId: "${workflow.input.orderId}", + customerId: "${workflow.input.customerId}" + } + ), + // Task 2: An HTTP task to check inventory - httpTask( - "check_inventory_ref", - { - uri: "https://api.inventory.com/check", - method: "POST", - body: { - productId: "${workflow.input.productId}", - quantity: "${workflow.input.quantity}" + httpTask( + "check_inventory_ref", + { + uri: "https://api.inventory.com/check", + method: "POST", + body: { + productId: "${workflow.input.productId}", + quantity: "${workflow.input.quantity}" + } } - } - ), - + ), + // Task 3: A switch task for conditional logic - switchTask( - "route_order_ref", - "${check_inventory_ref.output.inStock}", - { - "true": [ + switchTask( + "route_order_ref", + "${check_inventory_ref.output.inStock}", + { + "true": [ simpleTask("fulfill_order_ref", "fulfill_order", {}) - ], - "false": [ + ], + "false": [ simpleTask("backorder_ref", "create_backorder", {}) ] } @@ -319,7 +323,7 @@ workflowDef.tasks = tasks; - **Input Parameters**: Use `${workflow.input.fieldName}` to access initial workflow inputs and `${task_ref.output.fieldName}` to access outputs from previous tasks. - **Task Generators**: Helper functions like `simpleTask`, `httpTask`, etc., that create task definitions. For a complete list, see the [Task Generators Reference](./docs/api-reference/task-generators.md). -### Step 3: Register and Start Your Workflow +#### Step 3: Register and Start Your Workflow With the definition complete, register it with Conductor and start an execution. @@ -342,11 +346,11 @@ const executionId = await executor.startWorkflow({ console.log(`Workflow started with ID: ${executionId}`); ``` -### Step 4: Manage and Monitor Execution +#### Step 4: Manage and Monitor Execution Once a workflow is running, you can monitor its status, control its execution, and debug individual tasks. -#### Check Workflow Status +##### Check Workflow Status Retrieve the current status and output of a running workflow. @@ -359,7 +363,7 @@ const status = await executor.getWorkflowStatus( console.log(`Workflow status is: ${status.status}`); ``` -#### Control Workflow Execution +##### Control Workflow Execution You can pause, resume, or terminate workflows as needed. @@ -374,7 +378,7 @@ await executor.resume(executionId); await executor.terminate(executionId, "Aborted due to customer cancellation"); ``` -#### Search for Workflows +##### Search for Workflows Search for workflow executions based on various criteria. @@ -388,7 +392,7 @@ const searchResults = await executor.search( ); ``` -#### Monitor and Debug Tasks +##### Monitor and Debug Tasks For a deeper look into the tasks within a workflow, use the `TaskClient`. @@ -448,11 +452,7 @@ Building a robust worker involves defining its logic, handling outcomes, and man #### Step 1: Define the Worker's Logic -A worker is an object that defines a `taskDefName` (which must match the task name in your workflow) and an `execute` function containing your business logic. When designing workers, it's best to follow these principles: - -- **Stateless**: Workers should not rely on local state. -- **Idempotent**: The same task input should always produce the same result. -- **Single Responsibility**: Each worker should be responsible for one specific task type. +A worker is an object that defines a `taskDefName` (which must match the task name in your workflow) and an `execute` function containing your business logic. ```typescript import { ConductorWorker } from "@io-orkes/conductor-javascript"; @@ -530,6 +530,14 @@ console.log("Worker is running!"); For a complete method reference, see the [TaskManager API Reference](./docs/api-reference/task-manager.md). +### Worker Design Principles + +When designing workers, it's best to follow these principles: + +- **Stateless**: Workers should not rely on local state. +- **Idempotent**: The same task input should always produce the same result. +- **Single Responsibility**: Each worker should be responsible for one specific task type. + ## Scheduling The Conductor Scheduler allows you to run workflows at specific times or intervals, defined by a CRON expression. This is useful for tasks like nightly data processing, weekly reports, or any time-based automation. From c2a912634574d5037638114633540094816f31d4 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Mon, 6 Oct 2025 19:35:42 +0300 Subject: [PATCH 57/60] Update README.md --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a661b86d..51db5ce8 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Show support for the Conductor OSS. Please help spread the awareness by starrin - [System Tasks - Managed by Conductor Server](#system-tasks---managed-by-conductor-server) - [SIMPLE Tasks - Require Custom Workers](#simple-tasks---require-custom-workers) - [Workflows](#workflows) - - [The WorkflowExecutor](#the-workflowexecutor) + - [The WorkflowExecutor and TaskClient](#the-workflowexecutor-and-taskclient) - [Quick Start: Creating a Workflow](#quick-start-creating-a-workflow) - [Step 1: Define Your Workflow Structure](#step-1-define-your-workflow-structure) - [Step 2: Build Your Task List](#step-2-build-your-task-list) @@ -232,15 +232,17 @@ See the [Workers](#workers) section for implementation details. Workflows are the heart of Conductor, orchestrating tasks to perform complex processes. This guide walks you through the entire lifecycle of a workflow, from creation to monitoring. -### The WorkflowExecutor +### The WorkflowExecutor and TaskClient -The `WorkflowExecutor` is your primary tool for interacting with workflows. It allows you to register, start, and manage their execution. +- **`WorkflowExecutor`**: The primary tool for managing the workflow lifecycle (e.g., registering, starting, and stopping). +- **`TaskClient`**: Used for searching and retrieving details of individual tasks within a workflow execution. ```typescript -import { WorkflowExecutor } from "@io-orkes/conductor-javascript"; +import { WorkflowExecutor, TaskClient } from "@io-orkes/conductor-javascript"; // Create an executor instance const executor = new WorkflowExecutor(client); +const taskClient = new TaskClient(client); ``` ### Quick Start: Creating a Workflow From e4562a5c12e836f851816fe7ff28ebb81feb2c08 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Mon, 6 Oct 2025 19:42:32 +0300 Subject: [PATCH 58/60] Update README.md --- README.md | 61 +++++++++++++------------------------------------------ 1 file changed, 14 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 51db5ce8..74d515ce 100644 --- a/README.md +++ b/README.md @@ -234,18 +234,21 @@ Workflows are the heart of Conductor, orchestrating tasks to perform complex pro ### The WorkflowExecutor and TaskClient -- **`WorkflowExecutor`**: The primary tool for managing the workflow lifecycle (e.g., registering, starting, and stopping). -- **`TaskClient`**: Used for searching and retrieving details of individual tasks within a workflow execution. +- **`WorkflowExecutor`**: The primary tool for managing the workflow lifecycle (e.g., registering, starting, and stopping). For a complete method reference, see the [WorkflowExecutor API Reference](./docs/api-reference/workflow-executor.md). +- **`TaskClient`**: Used for searching and retrieving details of individual tasks within a workflow execution. For a complete method reference, see the [TaskClient API Reference](./docs/api-reference/task-client.md). + +### Quick Start: Creating a Workflow + +First, create instances of the `WorkflowExecutor` and `TaskClient`: ```typescript import { WorkflowExecutor, TaskClient } from "@io-orkes/conductor-javascript"; -// Create an executor instance const executor = new WorkflowExecutor(client); const taskClient = new TaskClient(client); ``` -### Quick Start: Creating a Workflow +Now, you can create and manage a workflow in four steps: #### Step 1: Define Your Workflow Structure @@ -270,7 +273,7 @@ const workflowDef = { }; ``` -#### Step 2: Build Your Task List +#### Step 2: Build Your Task List Using Task Generators Use **Task Generators** to populate the `tasks` array. These helper functions simplify the creation of different task types. @@ -435,18 +438,7 @@ The `TaskManager` class in this SDK simplifies the process of creating and manag ### The TaskManager -The `TaskManager` is the primary tool for managing workers. It handles polling, task execution, and result reporting, allowing you to run multiple workers concurrently. - -```typescript -import { TaskManager } from "@io-orkes/conductor-javascript"; - -const manager = new TaskManager(client, workers, { - options: { - concurrency: 5, - pollInterval: 100, - } -}); -``` +The `TaskManager` is the primary tool for managing workers. It handles polling, task execution, and result reporting, allowing you to run multiple workers concurrently. For a complete method reference, see the [TaskManager API Reference](./docs/api-reference/task-manager.md). ### Quick Start: Building a Worker @@ -546,13 +538,7 @@ The Conductor Scheduler allows you to run workflows at specific times or interva ### The SchedulerClient -The `SchedulerClient` is used to create, manage, and delete workflow schedules. - -```typescript -import { SchedulerClient } from "@io-orkes/conductor-javascript"; - -const scheduler = new SchedulerClient(client); -``` +The `SchedulerClient` is used to create, manage, and delete workflow schedules. For a complete method reference, see the [SchedulerClient API Reference](./docs/api-reference/scheduler-client.md). ### Quick Start: Scheduling a Workflow @@ -619,13 +605,7 @@ The Service Registry in Conductor allows you to manage and discover microservice ### The ServiceRegistryClient -The `ServiceRegistryClient` is used to register, manage, and discover services. - -```typescript -import { ServiceRegistryClient } from "@io-orkes/conductor-javascript"; - -const serviceRegistry = new ServiceRegistryClient(client); -``` +The `ServiceRegistryClient` is used to register, manage, and discover services. For a complete method reference, see the [ServiceRegistryClient API Reference](./docs/api-reference/service-registry-client.md). ### Quick Start: Using the Service Registry @@ -683,13 +663,7 @@ In Conductor, "metadata" refers to the definitions of your tasks and workflows. ### The MetadataClient -The `MetadataClient` is used to register and manage task and workflow definitions. - -```typescript -import { MetadataClient, taskDefinition, workflowDef } from "@io-orkes/conductor-javascript"; - -const metadataClient = new MetadataClient(client); -``` +The `MetadataClient` is used to register and manage task and workflow definitions. For a complete method reference, see the [MetadataClient API Reference](./docs/api-reference/metadata-client.md). ### Quick Start: Managing Metadata @@ -755,15 +729,8 @@ Unlike other tasks, human tasks are managed through a dedicated API (`HumanExecu ### The HumanExecutor and TemplateClient -- **`HumanExecutor`**: Manages the lifecycle of human tasks—searching, claiming, and completing them. -- **`TemplateClient`**: Manages the UI forms and templates that are presented to users. - -```typescript -import { HumanExecutor, TemplateClient } from "@io-orkes/conductor-javascript"; - -const humanExecutor = new HumanExecutor(client); -const templateClient = new TemplateClient(client); -``` +- **`HumanExecutor`**: Manages the lifecycle of human tasks—searching, claiming, and completing them. For a complete method reference, see the [HumanExecutor API Reference](./docs/api-reference/human-executor.md). +- **`TemplateClient`**: Manages the UI forms and templates that are presented to users. For a complete method reference, see the [TemplateClient API Reference](./docs/api-reference/template-client.md). ### Quick Start: Creating and Managing a Human Task From a8b0ce909618e5ebb3cae81f7656460b67e053e2 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Mon, 6 Oct 2025 19:54:52 +0300 Subject: [PATCH 59/60] Update README.md --- README.md | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 74d515ce..75d380df 100644 --- a/README.md +++ b/README.md @@ -33,9 +33,10 @@ Show support for the Conductor OSS. Please help spread the awareness by starrin - [The WorkflowExecutor and TaskClient](#the-workflowexecutor-and-taskclient) - [Quick Start: Creating a Workflow](#quick-start-creating-a-workflow) - [Step 1: Define Your Workflow Structure](#step-1-define-your-workflow-structure) - - [Step 2: Build Your Task List](#step-2-build-your-task-list) + - [Step 2: Use Task Generators to Build Your Task List](#step-2-use-task-generators-to-build-your-task-list) - [Step 3: Register and Start Your Workflow](#step-3-register-and-start-your-workflow) - [Step 4: Manage and Monitor Execution](#step-4-manage-and-monitor-execution) + - [Use TaskClient to Monitor and Debug Tasks](#use-taskclient-to-monitor-and-debug-tasks) - [Workers](#workers) - [The TaskManager](#the-taskmanager) - [Quick Start: Building a Worker](#quick-start-building-a-worker) @@ -239,17 +240,6 @@ Workflows are the heart of Conductor, orchestrating tasks to perform complex pro ### Quick Start: Creating a Workflow -First, create instances of the `WorkflowExecutor` and `TaskClient`: - -```typescript -import { WorkflowExecutor, TaskClient } from "@io-orkes/conductor-javascript"; - -const executor = new WorkflowExecutor(client); -const taskClient = new TaskClient(client); -``` - -Now, you can create and manage a workflow in four steps: - #### Step 1: Define Your Workflow Structure A workflow definition is a blueprint for your process. It outlines the workflow's properties and the sequence of tasks. @@ -273,7 +263,7 @@ const workflowDef = { }; ``` -#### Step 2: Build Your Task List Using Task Generators +#### Step 2: Use Task Generators to Build Your Task List Use **Task Generators** to populate the `tasks` array. These helper functions simplify the creation of different task types. @@ -333,6 +323,11 @@ workflowDef.tasks = tasks; With the definition complete, register it with Conductor and start an execution. ```typescript +import { WorkflowExecutor } from "@io-orkes/conductor-javascript"; + +// Create WorkflowExecutor instance +const executor = new WorkflowExecutor(client); + // Register the workflow definition (overwrite if it exists) await executor.registerWorkflow(true, workflowDef); @@ -397,7 +392,7 @@ const searchResults = await executor.search( ); ``` -##### Monitor and Debug Tasks +##### Use TaskClient to Monitor and Debug Tasks For a deeper look into the tasks within a workflow, use the `TaskClient`. From 7c899a61a10bb3ba6dba2e92226ec5b72d5b37b6 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Mon, 6 Oct 2025 19:58:23 +0300 Subject: [PATCH 60/60] update links --- README.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 75d380df..8b97f250 100644 --- a/README.md +++ b/README.md @@ -235,8 +235,8 @@ Workflows are the heart of Conductor, orchestrating tasks to perform complex pro ### The WorkflowExecutor and TaskClient -- **`WorkflowExecutor`**: The primary tool for managing the workflow lifecycle (e.g., registering, starting, and stopping). For a complete method reference, see the [WorkflowExecutor API Reference](./docs/api-reference/workflow-executor.md). -- **`TaskClient`**: Used for searching and retrieving details of individual tasks within a workflow execution. For a complete method reference, see the [TaskClient API Reference](./docs/api-reference/task-client.md). +- **`WorkflowExecutor`**: The primary tool for managing the workflow lifecycle (e.g., registering, starting, and stopping). For a complete method reference, see the [WorkflowExecutor API Reference](docs/api-reference/workflow-executor.md). +- **`TaskClient`**: Used for searching and retrieving details of individual tasks within a workflow execution. For a complete method reference, see the [TaskClient API Reference](docs/api-reference/task-client.md). ### Quick Start: Creating a Workflow @@ -316,7 +316,7 @@ workflowDef.tasks = tasks; **Key Concepts:** - **`taskReferenceName`**: A unique identifier for a task instance within a workflow. Used for data flow (e.g., `${check_inventory_ref.output.inStock}`). - **Input Parameters**: Use `${workflow.input.fieldName}` to access initial workflow inputs and `${task_ref.output.fieldName}` to access outputs from previous tasks. -- **Task Generators**: Helper functions like `simpleTask`, `httpTask`, etc., that create task definitions. For a complete list, see the [Task Generators Reference](./docs/api-reference/task-generators.md). +- **Task Generators**: Helper functions like `simpleTask`, `httpTask`, etc., that create task definitions. For a complete list, see the [Task Generators Reference](docs/api-reference/task-generators.md). #### Step 3: Register and Start Your Workflow @@ -414,7 +414,7 @@ const failedTasks = await taskClient.search( const taskDetails = await taskClient.getTask(failedTasks.results[0].taskId); ``` -For a complete list of methods, see the [WorkflowExecutor API Reference](./docs/api-reference/workflow-executor.md) and the [TaskClient API Reference](./docs/api-reference/task-client.md). +For a complete list of methods, see the [WorkflowExecutor API Reference](docs/api-reference/workflow-executor.md) and the [TaskClient API Reference](docs/api-reference/task-client.md). ## Workers @@ -433,7 +433,7 @@ The `TaskManager` class in this SDK simplifies the process of creating and manag ### The TaskManager -The `TaskManager` is the primary tool for managing workers. It handles polling, task execution, and result reporting, allowing you to run multiple workers concurrently. For a complete method reference, see the [TaskManager API Reference](./docs/api-reference/task-manager.md). +The `TaskManager` is the primary tool for managing workers. It handles polling, task execution, and result reporting, allowing you to run multiple workers concurrently. For a complete method reference, see the [TaskManager API Reference](docs/api-reference/task-manager.md). ### Quick Start: Building a Worker @@ -517,7 +517,7 @@ await manager.startPolling(); console.log("Worker is running!"); ``` -For a complete method reference, see the [TaskManager API Reference](./docs/api-reference/task-manager.md). +For a complete method reference, see the [TaskManager API Reference](docs/api-reference/task-manager.md). ### Worker Design Principles @@ -533,7 +533,7 @@ The Conductor Scheduler allows you to run workflows at specific times or interva ### The SchedulerClient -The `SchedulerClient` is used to create, manage, and delete workflow schedules. For a complete method reference, see the [SchedulerClient API Reference](./docs/api-reference/scheduler-client.md). +The `SchedulerClient` is used to create, manage, and delete workflow schedules. For a complete method reference, see the [SchedulerClient API Reference](docs/api-reference/scheduler-client.md). ### Quick Start: Scheduling a Workflow @@ -592,7 +592,7 @@ await scheduler.resumeSchedule("daily_report_schedule"); await scheduler.deleteSchedule("daily_report_schedule"); ``` -For a complete method reference, see the [SchedulerClient API Reference](./docs/api-reference/scheduler-client.md). +For a complete method reference, see the [SchedulerClient API Reference](docs/api-reference/scheduler-client.md). ## Service Registry @@ -600,7 +600,7 @@ The Service Registry in Conductor allows you to manage and discover microservice ### The ServiceRegistryClient -The `ServiceRegistryClient` is used to register, manage, and discover services. For a complete method reference, see the [ServiceRegistryClient API Reference](./docs/api-reference/service-registry-client.md). +The `ServiceRegistryClient` is used to register, manage, and discover services. For a complete method reference, see the [ServiceRegistryClient API Reference](docs/api-reference/service-registry-client.md). ### Quick Start: Using the Service Registry @@ -650,7 +650,7 @@ const service = await serviceRegistry.getService("user-service"); await serviceRegistry.removeService("user-service"); ``` -For a complete method reference, see the [ServiceRegistryClient API Reference](./docs/api-reference/service-registry-client.md). +For a complete method reference, see the [ServiceRegistryClient API Reference](docs/api-reference/service-registry-client.md). ## Metadata @@ -658,7 +658,7 @@ In Conductor, "metadata" refers to the definitions of your tasks and workflows. ### The MetadataClient -The `MetadataClient` is used to register and manage task and workflow definitions. For a complete method reference, see the [MetadataClient API Reference](./docs/api-reference/metadata-client.md). +The `MetadataClient` is used to register and manage task and workflow definitions. For a complete method reference, see the [MetadataClient API Reference](docs/api-reference/metadata-client.md). ### Quick Start: Managing Metadata @@ -714,7 +714,7 @@ const wf = { await metadataClient.registerWorkflowDef(wf); ``` -For a complete method reference, see the [MetadataClient API Reference](./docs/api-reference/metadata-client.md). +For a complete method reference, see the [MetadataClient API Reference](docs/api-reference/metadata-client.md). ## Human Tasks @@ -724,8 +724,8 @@ Unlike other tasks, human tasks are managed through a dedicated API (`HumanExecu ### The HumanExecutor and TemplateClient -- **`HumanExecutor`**: Manages the lifecycle of human tasks—searching, claiming, and completing them. For a complete method reference, see the [HumanExecutor API Reference](./docs/api-reference/human-executor.md). -- **`TemplateClient`**: Manages the UI forms and templates that are presented to users. For a complete method reference, see the [TemplateClient API Reference](./docs/api-reference/template-client.md). +- **`HumanExecutor`**: Manages the lifecycle of human tasks—searching, claiming, and completing them. For a complete method reference, see the [HumanExecutor API Reference](docs/api-reference/human-executor.md). +- **`TemplateClient`**: Manages the UI forms and templates that are presented to users. For a complete method reference, see the [TemplateClient API Reference](docs/api-reference/template-client.md). ### Quick Start: Creating and Managing a Human Task @@ -825,4 +825,4 @@ if (pendingTasks.results.length > 0) { } ``` -For a complete list of methods, see the [HumanExecutor API Reference](./docs/api-reference/human-executor.md) and the [TemplateClient API Reference](./docs/api-reference/template-client.md). +For a complete list of methods, see the [HumanExecutor API Reference](docs/api-reference/human-executor.md) and the [TemplateClient API Reference](docs/api-reference/template-client.md).