@hatchet-dev/typescript-sdk 0.7.4 → 0.8.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/clients/admin/admin-client.d.ts +2 -5
- package/clients/admin/admin-client.js +5 -0
- package/clients/dispatcher/action-listener.d.ts +60 -3
- package/clients/dispatcher/action-listener.js +18 -1
- package/clients/hatchet-client/hatchet-client.d.ts +1 -0
- package/clients/hatchet-client/hatchet-client.js +6 -0
- package/clients/rest/generated/Api.d.ts +18 -1
- package/clients/rest/generated/Api.js +17 -0
- package/clients/rest/generated/data-contracts.d.ts +25 -0
- package/clients/worker/handler.d.ts +56 -0
- package/clients/worker/handler.js +160 -0
- package/clients/worker/worker.d.ts +10 -3
- package/clients/worker/worker.js +202 -153
- package/package.json +3 -3
- package/util/parse.js +5 -0
|
@@ -3,7 +3,7 @@ import { CreateWorkflowVersionOpts, RateLimitDuration, WorkflowServiceClient } f
|
|
|
3
3
|
import { ClientConfig } from '../hatchet-client/client-config';
|
|
4
4
|
import { Logger } from '../../util/logger';
|
|
5
5
|
import { Api } from '../rest';
|
|
6
|
-
import {
|
|
6
|
+
import { WebhookWorkerCreateRequest, WorkflowRunStatus } from '../rest/generated/data-contracts';
|
|
7
7
|
type WorkflowMetricsQuery = {
|
|
8
8
|
workflowId?: string;
|
|
9
9
|
workflowName?: string;
|
|
@@ -40,6 +40,7 @@ export declare class AdminClient {
|
|
|
40
40
|
*/
|
|
41
41
|
put_workflow(workflow: CreateWorkflowVersionOpts): Promise<void>;
|
|
42
42
|
put_rate_limit(key: string, limit: number, duration?: RateLimitDuration): Promise<void>;
|
|
43
|
+
webhook_create(data: WebhookWorkerCreateRequest): Promise<import("axios").AxiosResponse<import("../rest/generated/data-contracts").WebhookWorker, any>>;
|
|
43
44
|
/**
|
|
44
45
|
* Run a new instance of a workflow with the given input. This will create a new workflow run and return the ID of the
|
|
45
46
|
* new run.
|
|
@@ -88,10 +89,6 @@ export declare class AdminClient {
|
|
|
88
89
|
limit?: number | undefined;
|
|
89
90
|
eventId?: string | undefined;
|
|
90
91
|
workflowId?: string | undefined;
|
|
91
|
-
parentWorkflowRunId?: string | undefined;
|
|
92
|
-
parentStepRunId?: string | undefined;
|
|
93
|
-
statuses?: WorkflowRunStatusList | undefined;
|
|
94
|
-
additionalMetadata?: string[] | undefined;
|
|
95
92
|
}): Promise<import("../rest/generated/data-contracts").WorkflowRunList>;
|
|
96
93
|
/**
|
|
97
94
|
* Schedule a workflow to run at a specific time or times.
|
|
@@ -72,6 +72,11 @@ class AdminClient {
|
|
|
72
72
|
}
|
|
73
73
|
});
|
|
74
74
|
}
|
|
75
|
+
webhook_create(data) {
|
|
76
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
77
|
+
return this.api.webhookCreate(this.tenantId, data);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
75
80
|
/**
|
|
76
81
|
* Run a new instance of a workflow with the given input. This will create a new workflow run and return the ID of the
|
|
77
82
|
* new run.
|
|
@@ -1,14 +1,57 @@
|
|
|
1
1
|
import { DispatcherClient as PbDispatcherClient, AssignedAction } from '../../protoc/dispatcher';
|
|
2
2
|
import { ClientConfig } from '../hatchet-client/client-config';
|
|
3
3
|
import { Logger } from '../../util/logger';
|
|
4
|
+
import { z } from 'zod';
|
|
4
5
|
import { DispatcherClient } from './dispatcher-client';
|
|
5
6
|
import { Heartbeat } from './heartbeat/heartbeat-controller';
|
|
6
7
|
declare enum ListenStrategy {
|
|
7
8
|
LISTEN_STRATEGY_V1 = 1,
|
|
8
9
|
LISTEN_STRATEGY_V2 = 2
|
|
9
10
|
}
|
|
10
|
-
export
|
|
11
|
-
|
|
11
|
+
export declare const ActionObject: z.ZodObject<{
|
|
12
|
+
tenantId: z.ZodString;
|
|
13
|
+
jobId: z.ZodString;
|
|
14
|
+
jobName: z.ZodString;
|
|
15
|
+
jobRunId: z.ZodString;
|
|
16
|
+
stepId: z.ZodString;
|
|
17
|
+
stepRunId: z.ZodString;
|
|
18
|
+
actionId: z.ZodString;
|
|
19
|
+
actionType: z.ZodEffects<z.ZodOptional<z.ZodNumber>, number | undefined, unknown>;
|
|
20
|
+
actionPayload: z.ZodString;
|
|
21
|
+
workflowRunId: z.ZodString;
|
|
22
|
+
getGroupKeyRunId: z.ZodOptional<z.ZodString>;
|
|
23
|
+
stepName: z.ZodString;
|
|
24
|
+
retryCount: z.ZodNumber;
|
|
25
|
+
}, "strip", z.ZodTypeAny, {
|
|
26
|
+
tenantId: string;
|
|
27
|
+
stepRunId: string;
|
|
28
|
+
workflowRunId: string;
|
|
29
|
+
jobId: string;
|
|
30
|
+
jobName: string;
|
|
31
|
+
jobRunId: string;
|
|
32
|
+
stepId: string;
|
|
33
|
+
actionId: string;
|
|
34
|
+
actionPayload: string;
|
|
35
|
+
stepName: string;
|
|
36
|
+
retryCount: number;
|
|
37
|
+
getGroupKeyRunId?: string | undefined;
|
|
38
|
+
actionType?: number | undefined;
|
|
39
|
+
}, {
|
|
40
|
+
tenantId: string;
|
|
41
|
+
stepRunId: string;
|
|
42
|
+
workflowRunId: string;
|
|
43
|
+
jobId: string;
|
|
44
|
+
jobName: string;
|
|
45
|
+
jobRunId: string;
|
|
46
|
+
stepId: string;
|
|
47
|
+
actionId: string;
|
|
48
|
+
actionPayload: string;
|
|
49
|
+
stepName: string;
|
|
50
|
+
retryCount: number;
|
|
51
|
+
getGroupKeyRunId?: string | undefined;
|
|
52
|
+
actionType?: unknown;
|
|
53
|
+
}>;
|
|
54
|
+
export type Action = z.infer<typeof ActionObject>;
|
|
12
55
|
export declare class ActionListener {
|
|
13
56
|
config: ClientConfig;
|
|
14
57
|
client: PbDispatcherClient;
|
|
@@ -22,7 +65,21 @@ export declare class ActionListener {
|
|
|
22
65
|
listenStrategy: ListenStrategy;
|
|
23
66
|
heartbeat: Heartbeat;
|
|
24
67
|
constructor(client: DispatcherClient, workerId: string, retryInterval?: number, retryCount?: number);
|
|
25
|
-
actions: () => AsyncGenerator<
|
|
68
|
+
actions: () => AsyncGenerator<{
|
|
69
|
+
tenantId: string;
|
|
70
|
+
stepRunId: string;
|
|
71
|
+
workflowRunId: string;
|
|
72
|
+
jobId: string;
|
|
73
|
+
jobName: string;
|
|
74
|
+
jobRunId: string;
|
|
75
|
+
stepId: string;
|
|
76
|
+
actionId: string;
|
|
77
|
+
actionPayload: string;
|
|
78
|
+
stepName: string;
|
|
79
|
+
retryCount: number;
|
|
80
|
+
getGroupKeyRunId?: string | undefined;
|
|
81
|
+
actionType?: number | undefined;
|
|
82
|
+
}, void, unknown>;
|
|
26
83
|
setListenStrategy(strategy: ListenStrategy): Promise<void>;
|
|
27
84
|
getListenStrategy(): Promise<ListenStrategy>;
|
|
28
85
|
incrementRetries(): Promise<void>;
|
|
@@ -32,11 +32,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
32
32
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
33
33
|
};
|
|
34
34
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
-
exports.ActionListener = void 0;
|
|
35
|
+
exports.ActionListener = exports.ActionObject = void 0;
|
|
36
|
+
const dispatcher_1 = require("../../protoc/dispatcher");
|
|
36
37
|
const nice_grpc_1 = require("nice-grpc");
|
|
37
38
|
const sleep_1 = __importDefault(require("../../util/sleep"));
|
|
38
39
|
const hatchet_error_1 = __importDefault(require("../../util/errors/hatchet-error"));
|
|
39
40
|
const logger_1 = require("../../util/logger");
|
|
41
|
+
const zod_1 = require("zod");
|
|
40
42
|
const heartbeat_controller_1 = require("./heartbeat/heartbeat-controller");
|
|
41
43
|
const DEFAULT_ACTION_LISTENER_RETRY_INTERVAL = 5000; // milliseconds
|
|
42
44
|
const DEFAULT_ACTION_LISTENER_RETRY_COUNT = 20;
|
|
@@ -46,6 +48,21 @@ var ListenStrategy;
|
|
|
46
48
|
ListenStrategy[ListenStrategy["LISTEN_STRATEGY_V1"] = 1] = "LISTEN_STRATEGY_V1";
|
|
47
49
|
ListenStrategy[ListenStrategy["LISTEN_STRATEGY_V2"] = 2] = "LISTEN_STRATEGY_V2";
|
|
48
50
|
})(ListenStrategy || (ListenStrategy = {}));
|
|
51
|
+
exports.ActionObject = zod_1.z.object({
|
|
52
|
+
tenantId: zod_1.z.string(),
|
|
53
|
+
jobId: zod_1.z.string(),
|
|
54
|
+
jobName: zod_1.z.string(),
|
|
55
|
+
jobRunId: zod_1.z.string(),
|
|
56
|
+
stepId: zod_1.z.string(),
|
|
57
|
+
stepRunId: zod_1.z.string(),
|
|
58
|
+
actionId: zod_1.z.string(),
|
|
59
|
+
actionType: zod_1.z.preprocess((s) => (0, dispatcher_1.actionTypeFromJSON)(s), zod_1.z.number().optional()),
|
|
60
|
+
actionPayload: zod_1.z.string(),
|
|
61
|
+
workflowRunId: zod_1.z.string(),
|
|
62
|
+
getGroupKeyRunId: zod_1.z.string().optional(),
|
|
63
|
+
stepName: zod_1.z.string(),
|
|
64
|
+
retryCount: zod_1.z.number(),
|
|
65
|
+
});
|
|
49
66
|
class ActionListener {
|
|
50
67
|
constructor(client, workerId, retryInterval = DEFAULT_ACTION_LISTENER_RETRY_INTERVAL, retryCount = DEFAULT_ACTION_LISTENER_RETRY_COUNT) {
|
|
51
68
|
this.lastConnectionAttempt = 0;
|
|
@@ -29,4 +29,5 @@ export declare class HatchetClient {
|
|
|
29
29
|
static init(config?: Partial<ClientConfig>, options?: HatchetClientOptions, axiosConfig?: AxiosRequestConfig): HatchetClient;
|
|
30
30
|
run(workflow: string | Workflow): Promise<Worker>;
|
|
31
31
|
worker(workflow: string | Workflow, maxRuns?: number): Promise<Worker>;
|
|
32
|
+
webhooks(workflow: Workflow): import("../worker/handler").WebhookHandler;
|
|
32
33
|
}
|
|
@@ -142,5 +142,11 @@ class HatchetClient {
|
|
|
142
142
|
return worker;
|
|
143
143
|
});
|
|
144
144
|
}
|
|
145
|
+
webhooks(workflow) {
|
|
146
|
+
const worker = new worker_1.Worker(this, {
|
|
147
|
+
name: workflow.id,
|
|
148
|
+
});
|
|
149
|
+
return worker.getHandler(workflow);
|
|
150
|
+
}
|
|
145
151
|
}
|
|
146
152
|
exports.HatchetClient = HatchetClient;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { APIMeta, AcceptInviteRequest, CreateAPITokenRequest, CreateAPITokenResponse, CreatePullRequestFromStepRun, CreateSNSIntegrationRequest, CreateTenantAlertEmailGroupRequest, CreateTenantInviteRequest, CreateTenantRequest, EventData, EventKey, EventKeyList, EventList, EventOrderByDirection, EventOrderByField, EventSearch, GetStepRunDiffResponse, LinkGithubRepositoryRequest, ListAPIMetaIntegration, ListAPITokensResponse, ListGithubAppInstallationsResponse, ListGithubBranchesResponse, ListGithubReposResponse, ListPullRequestsResponse, ListSNSIntegrations, ListSlackWebhooks, LogLineLevelField, LogLineList, LogLineOrderByDirection, LogLineOrderByField, LogLineSearch, PullRequestState, RejectInviteRequest, ReplayEventRequest, RerunStepRunRequest, SNSIntegration, StepRun, StepRunEventList, Tenant, TenantAlertEmailGroup, TenantAlertEmailGroupList, TenantAlertingSettings, TenantInvite, TenantInviteList, TenantMember, TenantMemberList, TenantResourcePolicy, TriggerWorkflowRunRequest, UpdateTenantAlertEmailGroupRequest, UpdateTenantInviteRequest, UpdateTenantRequest, User, UserChangePasswordRequest, UserLoginRequest, UserRegisterRequest, UserTenantMembershipsList, Worker, WorkerList, Workflow, WorkflowID, WorkflowList, WorkflowMetrics, WorkflowRun, WorkflowRunList, WorkflowRunStatus, WorkflowRunStatusList, WorkflowRunsCancelRequest, WorkflowRunsMetrics, WorkflowVersion, WorkflowVersionDefinition } from './data-contracts';
|
|
1
|
+
import { APIMeta, AcceptInviteRequest, CreateAPITokenRequest, CreateAPITokenResponse, CreatePullRequestFromStepRun, CreateSNSIntegrationRequest, CreateTenantAlertEmailGroupRequest, CreateTenantInviteRequest, CreateTenantRequest, EventData, EventKey, EventKeyList, EventList, EventOrderByDirection, EventOrderByField, EventSearch, GetStepRunDiffResponse, LinkGithubRepositoryRequest, ListAPIMetaIntegration, ListAPITokensResponse, ListGithubAppInstallationsResponse, ListGithubBranchesResponse, ListGithubReposResponse, ListPullRequestsResponse, ListSNSIntegrations, ListSlackWebhooks, LogLineLevelField, LogLineList, LogLineOrderByDirection, LogLineOrderByField, LogLineSearch, PullRequestState, RejectInviteRequest, ReplayEventRequest, RerunStepRunRequest, SNSIntegration, StepRun, StepRunEventList, Tenant, TenantAlertEmailGroup, TenantAlertEmailGroupList, TenantAlertingSettings, TenantInvite, TenantInviteList, TenantMember, TenantMemberList, TenantResourcePolicy, TriggerWorkflowRunRequest, UpdateTenantAlertEmailGroupRequest, UpdateTenantInviteRequest, UpdateTenantRequest, User, UserChangePasswordRequest, UserLoginRequest, UserRegisterRequest, UserTenantMembershipsList, WebhookWorker, WebhookWorkerCreateRequest, WebhookWorkerListResponse, Worker, WorkerList, Workflow, WorkflowID, WorkflowList, WorkflowMetrics, WorkflowRun, WorkflowRunList, WorkflowRunStatus, WorkflowRunStatusList, WorkflowRunsCancelRequest, WorkflowRunsMetrics, WorkflowVersion, WorkflowVersionDefinition } from './data-contracts';
|
|
2
2
|
import { HttpClient, RequestParams } from './http-client';
|
|
3
3
|
export declare class Api<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
|
|
4
4
|
/**
|
|
@@ -919,4 +919,21 @@ export declare class Api<SecurityDataType = unknown> extends HttpClient<Security
|
|
|
919
919
|
* @secure
|
|
920
920
|
*/
|
|
921
921
|
githubAppListBranches: (ghInstallation: string, ghRepoOwner: string, ghRepoName: string, params?: RequestParams) => Promise<import("axios").AxiosResponse<ListGithubBranchesResponse, any>>;
|
|
922
|
+
/**
|
|
923
|
+
* @description Lists all webhooks
|
|
924
|
+
*
|
|
925
|
+
* @name WebhookList
|
|
926
|
+
* @summary List webhooks
|
|
927
|
+
* @request GET:/api/v1/webhook-workers/{tenant}
|
|
928
|
+
* @secure
|
|
929
|
+
*/
|
|
930
|
+
webhookList: (tenant: string, params?: RequestParams) => Promise<import("axios").AxiosResponse<WebhookWorkerListResponse, any>>;
|
|
931
|
+
/**
|
|
932
|
+
* @description Creates a webhook
|
|
933
|
+
*
|
|
934
|
+
* @name WebhookCreate
|
|
935
|
+
* @summary Create a webhook
|
|
936
|
+
* @request POST:/api/v1/webhook-workers/{tenant}/create
|
|
937
|
+
*/
|
|
938
|
+
webhookCreate: (tenant: string, data: WebhookWorkerCreateRequest, params?: RequestParams) => Promise<import("axios").AxiosResponse<WebhookWorker, any>>;
|
|
922
939
|
}
|
|
@@ -761,6 +761,23 @@ class Api extends http_client_1.HttpClient {
|
|
|
761
761
|
* @secure
|
|
762
762
|
*/
|
|
763
763
|
this.githubAppListBranches = (ghInstallation, ghRepoOwner, ghRepoName, params = {}) => this.request(Object.assign({ path: `/api/v1/github-app/installations/${ghInstallation}/repos/${ghRepoOwner}/${ghRepoName}/branches`, method: 'GET', secure: true, format: 'json' }, params));
|
|
764
|
+
/**
|
|
765
|
+
* @description Lists all webhooks
|
|
766
|
+
*
|
|
767
|
+
* @name WebhookList
|
|
768
|
+
* @summary List webhooks
|
|
769
|
+
* @request GET:/api/v1/webhook-workers/{tenant}
|
|
770
|
+
* @secure
|
|
771
|
+
*/
|
|
772
|
+
this.webhookList = (tenant, params = {}) => this.request(Object.assign({ path: `/api/v1/webhook-workers/${tenant}`, method: 'GET', secure: true, format: 'json' }, params));
|
|
773
|
+
/**
|
|
774
|
+
* @description Creates a webhook
|
|
775
|
+
*
|
|
776
|
+
* @name WebhookCreate
|
|
777
|
+
* @summary Create a webhook
|
|
778
|
+
* @request POST:/api/v1/webhook-workers/{tenant}/create
|
|
779
|
+
*/
|
|
780
|
+
this.webhookCreate = (tenant, data, params = {}) => this.request(Object.assign({ path: `/api/v1/webhook-workers/${tenant}/create`, method: 'POST', body: data, type: http_client_1.ContentType.Json, format: 'json' }, params));
|
|
764
781
|
}
|
|
765
782
|
}
|
|
766
783
|
exports.Api = Api;
|
|
@@ -894,3 +894,28 @@ export interface WorkflowMetrics {
|
|
|
894
894
|
/** The total number of concurrency group keys. */
|
|
895
895
|
groupKeyCount?: number;
|
|
896
896
|
}
|
|
897
|
+
export interface WebhookWorker {
|
|
898
|
+
metadata: APIResourceMeta;
|
|
899
|
+
/** The webhook url. */
|
|
900
|
+
url: string;
|
|
901
|
+
/** The secret key for validation. */
|
|
902
|
+
secret: string;
|
|
903
|
+
}
|
|
904
|
+
export interface WebhookWorkerCreateRequest {
|
|
905
|
+
/** The webhook url. */
|
|
906
|
+
url: string;
|
|
907
|
+
/** The workflow IDs or names to register for this webhook worker. If not provided, workflows will be automatically detected. */
|
|
908
|
+
workflows?: string[];
|
|
909
|
+
/**
|
|
910
|
+
* The secret key for validation. If not provided, a random secret will be generated.
|
|
911
|
+
* @minLength 32
|
|
912
|
+
*/
|
|
913
|
+
secret?: string;
|
|
914
|
+
}
|
|
915
|
+
export interface WebhookWorkerCreateResponse {
|
|
916
|
+
worker?: WebhookWorker;
|
|
917
|
+
}
|
|
918
|
+
export interface WebhookWorkerListResponse {
|
|
919
|
+
pagination?: PaginationResponse;
|
|
920
|
+
rows?: WebhookWorker[];
|
|
921
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { IncomingMessage, ServerResponse } from 'http';
|
|
3
|
+
import { Worker } from './worker';
|
|
4
|
+
export interface HandlerOpts {
|
|
5
|
+
secret: string;
|
|
6
|
+
}
|
|
7
|
+
export declare class WebhookHandler {
|
|
8
|
+
private worker;
|
|
9
|
+
constructor(worker: Worker);
|
|
10
|
+
/**
|
|
11
|
+
* Handles a request with a provided body, secret, and signature.
|
|
12
|
+
*
|
|
13
|
+
* @param {string | undefined} body - The body of the request.
|
|
14
|
+
* @param {string | undefined} secret - The secret used for signature verification.
|
|
15
|
+
* @param {string | string[] | undefined | null} signature - The signature of the request.
|
|
16
|
+
*
|
|
17
|
+
* @throws {HatchetError} - If no signature is provided or the signature is not a string.
|
|
18
|
+
* @throws {HatchetError} - If no secret is provided.
|
|
19
|
+
* @throws {HatchetError} - If no body is provided.
|
|
20
|
+
*/
|
|
21
|
+
handle(body: string | undefined, secret: string | undefined, signature: string | string[] | undefined | null): Promise<void>;
|
|
22
|
+
private getHealthcheckResponse;
|
|
23
|
+
/**
|
|
24
|
+
* Express Handler
|
|
25
|
+
*
|
|
26
|
+
* This method is an asynchronous function that returns an Express middleware handler.
|
|
27
|
+
* The handler function is responsible for handling incoming requests and invoking the
|
|
28
|
+
* corresponding logic based on the provided secret.
|
|
29
|
+
*
|
|
30
|
+
* @param {string} secret - The secret key used to authenticate and authorize the incoming requests.
|
|
31
|
+
*
|
|
32
|
+
* @return {Function} - An Express middleware handler function that receives the request and response objects.
|
|
33
|
+
*/
|
|
34
|
+
expressHandler({ secret }: HandlerOpts): (req: any, res: any) => void;
|
|
35
|
+
/**
|
|
36
|
+
* A method that returns an HTTP request handler.
|
|
37
|
+
*
|
|
38
|
+
* @param {string} secret - The secret key used for verification.
|
|
39
|
+
*
|
|
40
|
+
* @returns {function} - An HTTP request handler function.
|
|
41
|
+
*/
|
|
42
|
+
httpHandler({ secret }: HandlerOpts): (req: IncomingMessage, res: ServerResponse) => void;
|
|
43
|
+
/**
|
|
44
|
+
* A method that returns a Next.js request handler.
|
|
45
|
+
*
|
|
46
|
+
* @param {any} req - The request object received from Next.js.
|
|
47
|
+
* @param {string} secret - The secret key used to verify the request.
|
|
48
|
+
* @return {Promise<Response>} - A Promise that resolves with a Response object.
|
|
49
|
+
*/
|
|
50
|
+
nextJSHandler({ secret }: HandlerOpts): {
|
|
51
|
+
GET: () => Promise<Response>;
|
|
52
|
+
POST: (req: Request) => Promise<Response>;
|
|
53
|
+
PUT: (req: Request) => Promise<Response>;
|
|
54
|
+
};
|
|
55
|
+
private getBody;
|
|
56
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.WebhookHandler = void 0;
|
|
16
|
+
const hatchet_error_1 = __importDefault(require("../../util/errors/hatchet-error"));
|
|
17
|
+
const crypto_1 = require("crypto");
|
|
18
|
+
const action_listener_1 = require("../dispatcher/action-listener");
|
|
19
|
+
class WebhookHandler {
|
|
20
|
+
// eslint-disable-next-line no-useless-constructor,no-empty-function
|
|
21
|
+
constructor(worker) {
|
|
22
|
+
this.worker = worker;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Handles a request with a provided body, secret, and signature.
|
|
26
|
+
*
|
|
27
|
+
* @param {string | undefined} body - The body of the request.
|
|
28
|
+
* @param {string | undefined} secret - The secret used for signature verification.
|
|
29
|
+
* @param {string | string[] | undefined | null} signature - The signature of the request.
|
|
30
|
+
*
|
|
31
|
+
* @throws {HatchetError} - If no signature is provided or the signature is not a string.
|
|
32
|
+
* @throws {HatchetError} - If no secret is provided.
|
|
33
|
+
* @throws {HatchetError} - If no body is provided.
|
|
34
|
+
*/
|
|
35
|
+
handle(body, secret, signature) {
|
|
36
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
37
|
+
if (!signature || typeof signature !== 'string') {
|
|
38
|
+
throw new hatchet_error_1.default('No signature provided');
|
|
39
|
+
}
|
|
40
|
+
if (!secret) {
|
|
41
|
+
throw new hatchet_error_1.default('No secret provided');
|
|
42
|
+
}
|
|
43
|
+
if (!body) {
|
|
44
|
+
throw new hatchet_error_1.default('No body provided');
|
|
45
|
+
}
|
|
46
|
+
// verify hmac signature
|
|
47
|
+
const actualSignature = (0, crypto_1.createHmac)('sha256', secret).update(body).digest('hex');
|
|
48
|
+
if (actualSignature !== signature) {
|
|
49
|
+
throw new hatchet_error_1.default(`Invalid signature, expected ${actualSignature}, got ${signature}`);
|
|
50
|
+
}
|
|
51
|
+
const action = action_listener_1.ActionObject.parse(JSON.parse(body));
|
|
52
|
+
yield this.worker.handleAction(action);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
getHealthcheckResponse() {
|
|
56
|
+
return {
|
|
57
|
+
actions: Object.keys(this.worker.action_registry),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Express Handler
|
|
62
|
+
*
|
|
63
|
+
* This method is an asynchronous function that returns an Express middleware handler.
|
|
64
|
+
* The handler function is responsible for handling incoming requests and invoking the
|
|
65
|
+
* corresponding logic based on the provided secret.
|
|
66
|
+
*
|
|
67
|
+
* @param {string} secret - The secret key used to authenticate and authorize the incoming requests.
|
|
68
|
+
*
|
|
69
|
+
* @return {Function} - An Express middleware handler function that receives the request and response objects.
|
|
70
|
+
*/
|
|
71
|
+
expressHandler({ secret }) {
|
|
72
|
+
return (req, res) => {
|
|
73
|
+
if (req.method === 'GET') {
|
|
74
|
+
res.sendStatus(200);
|
|
75
|
+
res.json(this.getHealthcheckResponse());
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (req.method !== 'POST') {
|
|
79
|
+
res.sendStatus(405);
|
|
80
|
+
res.json({ error: 'Method not allowed' });
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
this.handle(req.body, req.headers['x-hatchet-signature'], secret)
|
|
84
|
+
.then(() => {
|
|
85
|
+
res.sendStatus(200);
|
|
86
|
+
})
|
|
87
|
+
.catch((e) => {
|
|
88
|
+
res.sendStatus(500);
|
|
89
|
+
this.worker.logger.error(`Error handling request: ${e.message}`);
|
|
90
|
+
});
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* A method that returns an HTTP request handler.
|
|
95
|
+
*
|
|
96
|
+
* @param {string} secret - The secret key used for verification.
|
|
97
|
+
*
|
|
98
|
+
* @returns {function} - An HTTP request handler function.
|
|
99
|
+
*/
|
|
100
|
+
httpHandler({ secret }) {
|
|
101
|
+
return (req, res) => {
|
|
102
|
+
const handle = () => __awaiter(this, void 0, void 0, function* () {
|
|
103
|
+
if (req.method === 'GET') {
|
|
104
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
105
|
+
res.write(JSON.stringify(this.getHealthcheckResponse()));
|
|
106
|
+
res.end();
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (req.method !== 'POST') {
|
|
110
|
+
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
111
|
+
res.write(JSON.stringify({ error: 'Method not allowed' }));
|
|
112
|
+
res.end();
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const body = yield this.getBody(req);
|
|
116
|
+
yield this.handle(body, secret, req.headers['x-hatchet-signature']);
|
|
117
|
+
res.writeHead(200, 'OK');
|
|
118
|
+
res.end();
|
|
119
|
+
});
|
|
120
|
+
handle().catch((e) => {
|
|
121
|
+
this.worker.logger.error(`Error handling request: ${e.message}`);
|
|
122
|
+
res.writeHead(500, 'Internal server error');
|
|
123
|
+
res.end();
|
|
124
|
+
});
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* A method that returns a Next.js request handler.
|
|
129
|
+
*
|
|
130
|
+
* @param {any} req - The request object received from Next.js.
|
|
131
|
+
* @param {string} secret - The secret key used to verify the request.
|
|
132
|
+
* @return {Promise<Response>} - A Promise that resolves with a Response object.
|
|
133
|
+
*/
|
|
134
|
+
nextJSHandler({ secret }) {
|
|
135
|
+
const healthcheck = () => __awaiter(this, void 0, void 0, function* () {
|
|
136
|
+
return new Response(JSON.stringify(this.getHealthcheckResponse()), { status: 200 });
|
|
137
|
+
});
|
|
138
|
+
const f = (req) => __awaiter(this, void 0, void 0, function* () {
|
|
139
|
+
yield this.handle(yield req.text(), secret, req.headers.get('x-hatchet-signature'));
|
|
140
|
+
return new Response('ok', { status: 200 });
|
|
141
|
+
});
|
|
142
|
+
return {
|
|
143
|
+
GET: healthcheck,
|
|
144
|
+
POST: f,
|
|
145
|
+
PUT: f,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
getBody(req) {
|
|
149
|
+
return new Promise((resolve) => {
|
|
150
|
+
let body = '';
|
|
151
|
+
req.on('data', (chunk) => {
|
|
152
|
+
body += chunk;
|
|
153
|
+
});
|
|
154
|
+
req.on('end', () => {
|
|
155
|
+
resolve(body);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
exports.WebhookHandler = WebhookHandler;
|
|
@@ -4,6 +4,8 @@ import { StepActionEvent, StepActionEventType, GroupKeyActionEvent, GroupKeyActi
|
|
|
4
4
|
import HatchetPromise from '../../util/hatchet-promise/hatchet-promise';
|
|
5
5
|
import { Workflow } from '../../workflow';
|
|
6
6
|
import { Logger } from '../../util/logger';
|
|
7
|
+
import { WebhookHandler } from './handler';
|
|
8
|
+
import { WebhookWorkerCreateRequest } from '../rest/generated/data-contracts';
|
|
7
9
|
import { Context, StepRunFunction } from '../../step';
|
|
8
10
|
export type ActionRegistry = Record<Action['actionId'], Function>;
|
|
9
11
|
export declare class Worker {
|
|
@@ -18,20 +20,25 @@ export declare class Worker {
|
|
|
18
20
|
maxRuns?: number;
|
|
19
21
|
logger: Logger;
|
|
20
22
|
registeredWorkflowPromises: Array<Promise<any>>;
|
|
23
|
+
registeredWorkflowIds: string[];
|
|
21
24
|
constructor(client: HatchetClient, options: {
|
|
22
25
|
name: string;
|
|
23
26
|
handleKill?: boolean;
|
|
24
27
|
maxRuns?: number;
|
|
25
28
|
});
|
|
29
|
+
private registerActions;
|
|
30
|
+
getHandler(workflow: Workflow): WebhookHandler;
|
|
31
|
+
registerWebhook(webhook: WebhookWorkerCreateRequest): Promise<import("axios").AxiosResponse<import("../rest/generated/data-contracts").WebhookWorker, any>>;
|
|
26
32
|
registerWorkflow(initWorkflow: Workflow): Promise<void>;
|
|
27
33
|
register_workflow(initWorkflow: Workflow): Promise<void>;
|
|
28
34
|
registerAction<T, K>(actionId: string, action: StepRunFunction<T, K>): void;
|
|
29
|
-
handleStartStepRun(action: Action): void
|
|
30
|
-
handleStartGroupKeyRun(action: Action): void
|
|
35
|
+
handleStartStepRun(action: Action): Promise<void>;
|
|
36
|
+
handleStartGroupKeyRun(action: Action): Promise<void>;
|
|
31
37
|
getStepActionEvent(action: Action, eventType: StepActionEventType, payload?: any): StepActionEvent;
|
|
32
38
|
getGroupKeyActionEvent(action: Action, eventType: GroupKeyActionEventType, payload?: any): GroupKeyActionEvent;
|
|
33
|
-
handleCancelStepRun(action: Action): void
|
|
39
|
+
handleCancelStepRun(action: Action): Promise<void>;
|
|
34
40
|
stop(): Promise<void>;
|
|
35
41
|
exitGracefully(handleKill: boolean): Promise<void>;
|
|
36
42
|
start(): Promise<void>;
|
|
43
|
+
handleAction(action: Action): Promise<void>;
|
|
37
44
|
}
|
package/clients/worker/worker.js
CHANGED
|
@@ -25,13 +25,14 @@ const dispatcher_1 = require("../../protoc/dispatcher");
|
|
|
25
25
|
const hatchet_promise_1 = __importDefault(require("../../util/hatchet-promise/hatchet-promise"));
|
|
26
26
|
const workflows_1 = require("../../protoc/workflows");
|
|
27
27
|
const logger_1 = require("../../util/logger");
|
|
28
|
-
|
|
28
|
+
const handler_1 = require("./handler");
|
|
29
29
|
const step_1 = require("../../step");
|
|
30
30
|
class Worker {
|
|
31
31
|
constructor(client, options) {
|
|
32
32
|
this.futures = {};
|
|
33
33
|
this.contexts = {};
|
|
34
34
|
this.registeredWorkflowPromises = [];
|
|
35
|
+
this.registeredWorkflowIds = [];
|
|
35
36
|
this.client = client;
|
|
36
37
|
this.name = this.client.config.namespace + options.name;
|
|
37
38
|
this.action_registry = {};
|
|
@@ -42,6 +43,31 @@ class Worker {
|
|
|
42
43
|
this.handle_kill = options.handleKill === undefined ? true : options.handleKill;
|
|
43
44
|
this.logger = new logger_1.Logger(`Worker/${this.name}`, this.client.config.log_level);
|
|
44
45
|
}
|
|
46
|
+
registerActions(workflow) {
|
|
47
|
+
var _a;
|
|
48
|
+
const newActions = workflow.steps.reduce((acc, step) => {
|
|
49
|
+
acc[`${workflow.id}:${step.name}`] = step.run;
|
|
50
|
+
return acc;
|
|
51
|
+
}, {});
|
|
52
|
+
const onFailureAction = workflow.onFailure
|
|
53
|
+
? {
|
|
54
|
+
[`${workflow.id}-on-failure:${workflow.onFailure.name}`]: workflow.onFailure.run,
|
|
55
|
+
}
|
|
56
|
+
: {};
|
|
57
|
+
this.action_registry = Object.assign(Object.assign(Object.assign({}, this.action_registry), newActions), onFailureAction);
|
|
58
|
+
this.action_registry = ((_a = workflow.concurrency) === null || _a === void 0 ? void 0 : _a.name)
|
|
59
|
+
? Object.assign(Object.assign({}, this.action_registry), { [`${workflow.id}:${workflow.concurrency.name}`]: workflow.concurrency.key }) : Object.assign({}, this.action_registry);
|
|
60
|
+
}
|
|
61
|
+
getHandler(workflow) {
|
|
62
|
+
const wf = Object.assign(Object.assign({}, workflow), { id: this.client.config.namespace + workflow.id });
|
|
63
|
+
this.registerActions(wf);
|
|
64
|
+
return new handler_1.WebhookHandler(this);
|
|
65
|
+
}
|
|
66
|
+
registerWebhook(webhook) {
|
|
67
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
68
|
+
return this.client.admin.webhook_create(Object.assign(Object.assign({}, webhook), { workflows: this.registeredWorkflowIds }));
|
|
69
|
+
});
|
|
70
|
+
}
|
|
45
71
|
// @deprecated
|
|
46
72
|
registerWorkflow(initWorkflow) {
|
|
47
73
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -50,7 +76,7 @@ class Worker {
|
|
|
50
76
|
}
|
|
51
77
|
register_workflow(initWorkflow) {
|
|
52
78
|
return __awaiter(this, void 0, void 0, function* () {
|
|
53
|
-
var _a, _b
|
|
79
|
+
var _a, _b;
|
|
54
80
|
const workflow = Object.assign(Object.assign({}, initWorkflow), { id: this.client.config.namespace + initWorkflow.id });
|
|
55
81
|
try {
|
|
56
82
|
const concurrency = ((_a = workflow.concurrency) === null || _a === void 0 ? void 0 : _a.name)
|
|
@@ -78,6 +104,7 @@ class Worker {
|
|
|
78
104
|
],
|
|
79
105
|
}
|
|
80
106
|
: undefined;
|
|
107
|
+
this.registeredWorkflowIds.push(workflow.id);
|
|
81
108
|
const registeredWorkflow = this.client.admin.put_workflow({
|
|
82
109
|
name: workflow.id,
|
|
83
110
|
description: workflow.description,
|
|
@@ -114,145 +141,153 @@ class Worker {
|
|
|
114
141
|
catch (e) {
|
|
115
142
|
throw new hatchet_error_1.default(`Could not register workflow: ${e.message}`);
|
|
116
143
|
}
|
|
117
|
-
|
|
118
|
-
acc[`${workflow.id}:${step.name}`] = step.run;
|
|
119
|
-
return acc;
|
|
120
|
-
}, {});
|
|
121
|
-
const onFailureAction = workflow.onFailure
|
|
122
|
-
? {
|
|
123
|
-
[`${workflow.id}-on-failure:${workflow.onFailure.name}`]: workflow.onFailure.run,
|
|
124
|
-
}
|
|
125
|
-
: {};
|
|
126
|
-
this.action_registry = Object.assign(Object.assign(Object.assign({}, this.action_registry), newActions), onFailureAction);
|
|
127
|
-
this.action_registry = ((_c = workflow.concurrency) === null || _c === void 0 ? void 0 : _c.name)
|
|
128
|
-
? Object.assign(Object.assign({}, this.action_registry), { [`${workflow.id}:${workflow.concurrency.name}`]: workflow.concurrency.key }) : Object.assign({}, this.action_registry);
|
|
144
|
+
this.registerActions(workflow);
|
|
129
145
|
});
|
|
130
146
|
}
|
|
131
147
|
registerAction(actionId, action) {
|
|
132
148
|
this.action_registry[actionId] = action;
|
|
133
149
|
}
|
|
134
150
|
handleStartStepRun(action) {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
this.
|
|
151
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
152
|
+
const { actionId } = action;
|
|
153
|
+
try {
|
|
154
|
+
const context = new step_1.Context(action, this.client);
|
|
155
|
+
this.contexts[action.stepRunId] = context;
|
|
156
|
+
const step = this.action_registry[actionId];
|
|
157
|
+
if (!step) {
|
|
158
|
+
this.logger.error(`Could not find step '${actionId}'`);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const run = () => __awaiter(this, void 0, void 0, function* () {
|
|
162
|
+
return step(context);
|
|
163
|
+
});
|
|
164
|
+
const success = (result) => __awaiter(this, void 0, void 0, function* () {
|
|
165
|
+
this.logger.info(`Step run ${action.stepRunId} succeeded`);
|
|
166
|
+
try {
|
|
167
|
+
// Send the action event to the dispatcher
|
|
168
|
+
const event = this.getStepActionEvent(action, dispatcher_1.StepActionEventType.STEP_EVENT_TYPE_COMPLETED, result || null);
|
|
169
|
+
yield this.client.dispatcher.sendStepActionEvent(event);
|
|
170
|
+
// delete the run from the futures
|
|
171
|
+
delete this.futures[action.stepRunId];
|
|
172
|
+
}
|
|
173
|
+
catch (actionEventError) {
|
|
174
|
+
this.logger.error(`Could not send completed action event: ${actionEventError.message}`);
|
|
154
175
|
// send a failure event
|
|
155
|
-
const failureEvent = this.getStepActionEvent(action, dispatcher_1.StepActionEventType.STEP_EVENT_TYPE_FAILED,
|
|
156
|
-
|
|
157
|
-
this.
|
|
176
|
+
const failureEvent = this.getStepActionEvent(action, dispatcher_1.StepActionEventType.STEP_EVENT_TYPE_FAILED, actionEventError.message);
|
|
177
|
+
try {
|
|
178
|
+
yield this.client.dispatcher.sendStepActionEvent(failureEvent);
|
|
179
|
+
}
|
|
180
|
+
catch (failureEventError) {
|
|
181
|
+
this.logger.error(`Could not send failed action event: ${failureEventError.message}`);
|
|
182
|
+
}
|
|
183
|
+
this.logger.error(`Could not send action event: ${actionEventError.message}`);
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
const failure = (error) => __awaiter(this, void 0, void 0, function* () {
|
|
187
|
+
this.logger.error(`Step run ${action.stepRunId} failed: ${error.message}`);
|
|
188
|
+
if (error.stack) {
|
|
189
|
+
this.logger.error(error.stack);
|
|
190
|
+
}
|
|
191
|
+
try {
|
|
192
|
+
// Send the action event to the dispatcher
|
|
193
|
+
const event = this.getStepActionEvent(action, dispatcher_1.StepActionEventType.STEP_EVENT_TYPE_FAILED, {
|
|
194
|
+
message: error === null || error === void 0 ? void 0 : error.message,
|
|
195
|
+
stack: error === null || error === void 0 ? void 0 : error.stack,
|
|
158
196
|
});
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
this.logger.error(`Could not send action event: ${e.message}`);
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
const failure = (error) => {
|
|
168
|
-
this.logger.error(`Step run ${action.stepRunId} failed: ${error.message}`);
|
|
169
|
-
if (error.stack) {
|
|
170
|
-
this.logger.error(error.stack);
|
|
171
|
-
}
|
|
172
|
-
try {
|
|
173
|
-
// Send the action event to the dispatcher
|
|
174
|
-
const event = this.getStepActionEvent(action, dispatcher_1.StepActionEventType.STEP_EVENT_TYPE_FAILED, {
|
|
175
|
-
message: error === null || error === void 0 ? void 0 : error.message,
|
|
176
|
-
stack: error === null || error === void 0 ? void 0 : error.stack,
|
|
177
|
-
});
|
|
178
|
-
this.client.dispatcher.sendStepActionEvent(event).catch((e) => {
|
|
197
|
+
yield this.client.dispatcher.sendStepActionEvent(event);
|
|
198
|
+
// delete the run from the futures
|
|
199
|
+
delete this.futures[action.stepRunId];
|
|
200
|
+
}
|
|
201
|
+
catch (e) {
|
|
179
202
|
this.logger.error(`Could not send action event: ${e.message}`);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
const future = new hatchet_promise_1.default((() => __awaiter(this, void 0, void 0, function* () {
|
|
206
|
+
try {
|
|
207
|
+
yield run();
|
|
208
|
+
}
|
|
209
|
+
catch (e) {
|
|
210
|
+
yield failure(e);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
yield success(null);
|
|
214
|
+
}))());
|
|
215
|
+
this.futures[action.stepRunId] = future;
|
|
216
|
+
// Send the action event to the dispatcher
|
|
217
|
+
const event = this.getStepActionEvent(action, dispatcher_1.StepActionEventType.STEP_EVENT_TYPE_STARTED);
|
|
218
|
+
this.client.dispatcher.sendStepActionEvent(event).catch((e) => {
|
|
185
219
|
this.logger.error(`Could not send action event: ${e.message}`);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
// Send the action event to the dispatcher
|
|
191
|
-
const event = this.getStepActionEvent(action, dispatcher_1.StepActionEventType.STEP_EVENT_TYPE_STARTED);
|
|
192
|
-
this.client.dispatcher.sendStepActionEvent(event).catch((e) => {
|
|
220
|
+
});
|
|
221
|
+
yield future.promise;
|
|
222
|
+
}
|
|
223
|
+
catch (e) {
|
|
193
224
|
this.logger.error(`Could not send action event: ${e.message}`);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
catch (e) {
|
|
197
|
-
this.logger.error(`Could not send action event: ${e.message}`);
|
|
198
|
-
}
|
|
225
|
+
}
|
|
226
|
+
});
|
|
199
227
|
}
|
|
200
228
|
handleStartGroupKeyRun(action) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
this.logger.error(`Could not find step '${actionId}'`);
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
const run = () => __awaiter(this, void 0, void 0, function* () {
|
|
213
|
-
return step(context);
|
|
214
|
-
});
|
|
215
|
-
const success = (result) => {
|
|
216
|
-
this.logger.info(`Step run ${action.stepRunId} succeeded`);
|
|
217
|
-
try {
|
|
218
|
-
// Send the action event to the dispatcher
|
|
219
|
-
const event = this.getGroupKeyActionEvent(action, dispatcher_1.GroupKeyActionEventType.GROUP_KEY_EVENT_TYPE_COMPLETED, result);
|
|
220
|
-
this.client.dispatcher.sendGroupKeyActionEvent(event).catch((e) => {
|
|
221
|
-
this.logger.error(`Could not send action event: ${e.message}`);
|
|
222
|
-
});
|
|
223
|
-
// delete the run from the futures
|
|
224
|
-
delete this.futures[key];
|
|
229
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
230
|
+
const { actionId } = action;
|
|
231
|
+
try {
|
|
232
|
+
const context = new step_1.Context(action, this.client);
|
|
233
|
+
const key = action.getGroupKeyRunId;
|
|
234
|
+
if (!key) {
|
|
235
|
+
this.logger.error(`No group key run id provided for action ${actionId}`);
|
|
236
|
+
return;
|
|
225
237
|
}
|
|
226
|
-
|
|
227
|
-
|
|
238
|
+
this.contexts[key] = context;
|
|
239
|
+
this.logger.debug(`Starting group key run ${key}`);
|
|
240
|
+
const step = this.action_registry[actionId];
|
|
241
|
+
if (!step) {
|
|
242
|
+
this.logger.error(`Could not find step '${actionId}'`);
|
|
243
|
+
return;
|
|
228
244
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
245
|
+
const run = () => __awaiter(this, void 0, void 0, function* () {
|
|
246
|
+
return step(context);
|
|
247
|
+
});
|
|
248
|
+
const success = (result) => {
|
|
249
|
+
this.logger.info(`Step run ${action.stepRunId} succeeded`);
|
|
250
|
+
try {
|
|
251
|
+
// Send the action event to the dispatcher
|
|
252
|
+
const event = this.getGroupKeyActionEvent(action, dispatcher_1.GroupKeyActionEventType.GROUP_KEY_EVENT_TYPE_COMPLETED, result);
|
|
253
|
+
this.client.dispatcher.sendGroupKeyActionEvent(event).catch((e) => {
|
|
254
|
+
this.logger.error(`Could not send action event: ${e.message}`);
|
|
255
|
+
});
|
|
256
|
+
// delete the run from the futures
|
|
257
|
+
delete this.futures[key];
|
|
258
|
+
}
|
|
259
|
+
catch (e) {
|
|
236
260
|
this.logger.error(`Could not send action event: ${e.message}`);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
const failure = (error) => {
|
|
264
|
+
this.logger.error(`Step run ${key} failed: ${error.message}`);
|
|
265
|
+
try {
|
|
266
|
+
// Send the action event to the dispatcher
|
|
267
|
+
const event = this.getGroupKeyActionEvent(action, dispatcher_1.GroupKeyActionEventType.GROUP_KEY_EVENT_TYPE_FAILED, error);
|
|
268
|
+
this.client.dispatcher.sendGroupKeyActionEvent(event).catch((e) => {
|
|
269
|
+
this.logger.error(`Could not send action event: ${e.message}`);
|
|
270
|
+
});
|
|
271
|
+
// delete the run from the futures
|
|
272
|
+
delete this.futures[key];
|
|
273
|
+
}
|
|
274
|
+
catch (e) {
|
|
275
|
+
this.logger.error(`Could not send action event: ${e.message}`);
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
const future = new hatchet_promise_1.default(run().then(success).catch(failure));
|
|
279
|
+
this.futures[key] = future;
|
|
280
|
+
// Send the action event to the dispatcher
|
|
281
|
+
const event = this.getGroupKeyActionEvent(action, dispatcher_1.GroupKeyActionEventType.GROUP_KEY_EVENT_TYPE_STARTED);
|
|
282
|
+
this.client.dispatcher.sendGroupKeyActionEvent(event).catch((e) => {
|
|
242
283
|
this.logger.error(`Could not send action event: ${e.message}`);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
// Send the action event to the dispatcher
|
|
248
|
-
const event = this.getGroupKeyActionEvent(action, dispatcher_1.GroupKeyActionEventType.GROUP_KEY_EVENT_TYPE_STARTED);
|
|
249
|
-
this.client.dispatcher.sendGroupKeyActionEvent(event).catch((e) => {
|
|
284
|
+
});
|
|
285
|
+
yield future.promise;
|
|
286
|
+
}
|
|
287
|
+
catch (e) {
|
|
250
288
|
this.logger.error(`Could not send action event: ${e.message}`);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
catch (e) {
|
|
254
|
-
this.logger.error(`Could not send action event: ${e.message}`);
|
|
255
|
-
}
|
|
289
|
+
}
|
|
290
|
+
});
|
|
256
291
|
}
|
|
257
292
|
getStepActionEvent(action, eventType, payload = '') {
|
|
258
293
|
return {
|
|
@@ -268,6 +303,9 @@ class Worker {
|
|
|
268
303
|
};
|
|
269
304
|
}
|
|
270
305
|
getGroupKeyActionEvent(action, eventType, payload = '') {
|
|
306
|
+
if (!action.getGroupKeyRunId) {
|
|
307
|
+
throw new hatchet_error_1.default('No group key run id provided');
|
|
308
|
+
}
|
|
271
309
|
return {
|
|
272
310
|
workerId: this.name,
|
|
273
311
|
workflowRunId: action.workflowRunId,
|
|
@@ -279,26 +317,29 @@ class Worker {
|
|
|
279
317
|
};
|
|
280
318
|
}
|
|
281
319
|
handleCancelStepRun(action) {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
context.controller
|
|
289
|
-
|
|
320
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
321
|
+
try {
|
|
322
|
+
this.logger.info(`Cancelling step run ${action.stepRunId}`);
|
|
323
|
+
const { stepRunId } = action;
|
|
324
|
+
const future = this.futures[stepRunId];
|
|
325
|
+
const context = this.contexts[stepRunId];
|
|
326
|
+
if (context && context.controller) {
|
|
327
|
+
context.controller.abort('Cancelled by worker');
|
|
328
|
+
delete this.contexts[stepRunId];
|
|
329
|
+
}
|
|
330
|
+
if (future) {
|
|
331
|
+
future.promise.catch(() => {
|
|
332
|
+
this.logger.info(`Cancelled step run ${action.stepRunId}`);
|
|
333
|
+
});
|
|
334
|
+
future.cancel('Cancelled by worker');
|
|
335
|
+
yield future.promise;
|
|
336
|
+
delete this.futures[stepRunId];
|
|
337
|
+
}
|
|
290
338
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
this.logger.info(`Cancelled step run ${action.stepRunId}`);
|
|
294
|
-
});
|
|
295
|
-
future.cancel('Cancelled by worker');
|
|
296
|
-
delete this.futures[stepRunId];
|
|
339
|
+
catch (e) {
|
|
340
|
+
this.logger.error(`Could not cancel step run: ${e.message}`);
|
|
297
341
|
}
|
|
298
|
-
}
|
|
299
|
-
catch (e) {
|
|
300
|
-
this.logger.error(`Could not cancel step run: ${e.message}`);
|
|
301
|
-
}
|
|
342
|
+
});
|
|
302
343
|
}
|
|
303
344
|
stop() {
|
|
304
345
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -346,18 +387,7 @@ class Worker {
|
|
|
346
387
|
_d = false;
|
|
347
388
|
const action = _c;
|
|
348
389
|
this.logger.info(`Worker ${this.name} received action ${action.actionId}:${action.actionType}`);
|
|
349
|
-
|
|
350
|
-
this.handleStartStepRun(action);
|
|
351
|
-
}
|
|
352
|
-
else if (action.actionType === dispatcher_1.ActionType.CANCEL_STEP_RUN) {
|
|
353
|
-
this.handleCancelStepRun(action);
|
|
354
|
-
}
|
|
355
|
-
else if (action.actionType === dispatcher_1.ActionType.START_GET_GROUP_KEY) {
|
|
356
|
-
this.handleStartGroupKeyRun(action);
|
|
357
|
-
}
|
|
358
|
-
else {
|
|
359
|
-
this.logger.error(`Worker ${this.name} received unknown action type ${action.actionType}`);
|
|
360
|
-
}
|
|
390
|
+
void this.handleAction(action);
|
|
361
391
|
}
|
|
362
392
|
}
|
|
363
393
|
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
@@ -379,5 +409,24 @@ class Worker {
|
|
|
379
409
|
}
|
|
380
410
|
});
|
|
381
411
|
}
|
|
412
|
+
handleAction(action) {
|
|
413
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
414
|
+
const type = action.actionType
|
|
415
|
+
? (0, dispatcher_1.actionTypeFromJSON)(action.actionType)
|
|
416
|
+
: dispatcher_1.ActionType.START_STEP_RUN;
|
|
417
|
+
if (type === dispatcher_1.ActionType.START_STEP_RUN) {
|
|
418
|
+
yield this.handleStartStepRun(action);
|
|
419
|
+
}
|
|
420
|
+
else if (type === dispatcher_1.ActionType.CANCEL_STEP_RUN) {
|
|
421
|
+
yield this.handleCancelStepRun(action);
|
|
422
|
+
}
|
|
423
|
+
else if (type === dispatcher_1.ActionType.START_GET_GROUP_KEY) {
|
|
424
|
+
yield this.handleStartGroupKeyRun(action);
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
this.logger.error(`Worker ${this.name} received unknown action type ${type}`);
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
}
|
|
382
431
|
}
|
|
383
432
|
exports.Worker = Worker;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hatchet-dev/typescript-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0-alpha.1",
|
|
4
4
|
"description": "Background task orchestration & visibility for developers",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"files": [
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"prepare": "npm run build",
|
|
20
20
|
"tsc:build": "tsc && resolve-tspaths",
|
|
21
21
|
"test:unit": "jest --testMatch='**/*.test.ts'",
|
|
22
|
-
"test:e2e": "jest --testMatch='**/*.e2e.ts'",
|
|
22
|
+
"test:e2e": "jest --testMatch='**/*.e2e.ts' --runInBand --detectOpenHandles",
|
|
23
23
|
"test:unit:watch": "jest --testMatch='**/*.test.ts' --watch",
|
|
24
24
|
"generate": "pnpm run '/generate-.*/'",
|
|
25
25
|
"generate-api": "npx --yes swagger-cli bundle ./hatchet/api-contracts/openapi/openapi.yaml --outfile openapi.yaml --type yaml && npx swagger-typescript-api -p openapi.yaml -o src/clients/rest/generated -n hatchet.ts --modular --axios",
|
|
@@ -100,4 +100,4 @@
|
|
|
100
100
|
"yaml": "^2.3.4",
|
|
101
101
|
"zod": "^3.22.4"
|
|
102
102
|
}
|
|
103
|
-
}
|
|
103
|
+
}
|
package/util/parse.js
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.parseJSON = void 0;
|
|
4
4
|
function parseJSON(json) {
|
|
5
|
+
// TODO why is this needed?
|
|
6
|
+
if (json.startsWith('ey')) {
|
|
7
|
+
const decoded = Buffer.from(json, 'base64').toString('utf8');
|
|
8
|
+
return JSON.parse(decoded);
|
|
9
|
+
}
|
|
5
10
|
try {
|
|
6
11
|
const firstParse = JSON.parse(json);
|
|
7
12
|
// Hatchet engine versions <=0.14.0 return JSON as a quoted string which needs to be parsed again.
|