@hatchet-dev/typescript-sdk 0.1.31 → 0.2.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 +6 -1
- package/clients/admin/admin-client.js +2 -5
- package/clients/dispatcher/action-listener.d.ts +11 -0
- package/clients/dispatcher/action-listener.js +71 -1
- package/clients/hatchet-client/hatchet-client.js +1 -1
- package/clients/listener/listener-client.d.ts +32 -9
- package/clients/listener/listener-client.js +187 -53
- package/clients/worker/worker.js +2 -2
- package/package.json +10 -6
- package/protoc/dispatcher/dispatcher.d.ts +93 -2
- package/protoc/dispatcher/dispatcher.js +437 -266
- package/protoc/events/events.d.ts +2 -2
- package/protoc/events/events.js +57 -53
- package/protoc/google/protobuf/timestamp.d.ts +1 -1
- package/protoc/google/protobuf/timestamp.js +2 -2
- package/protoc/google/protobuf/wrappers.d.ts +1 -1
- package/protoc/google/protobuf/wrappers.js +10 -10
- package/protoc/workflows/workflows.d.ts +32 -2
- package/protoc/workflows/workflows.js +298 -138
- package/step.d.ts +18 -5
- package/step.js +89 -4
|
@@ -39,7 +39,12 @@ export declare class AdminClient {
|
|
|
39
39
|
* @param input an object containing the input to the workflow
|
|
40
40
|
* @returns the ID of the new workflow run
|
|
41
41
|
*/
|
|
42
|
-
run_workflow(workflowName: string, input:
|
|
42
|
+
run_workflow<T = object>(workflowName: string, input: T, options?: {
|
|
43
|
+
parentId?: string | undefined;
|
|
44
|
+
parentStepRunId?: string | undefined;
|
|
45
|
+
childIndex?: number | undefined;
|
|
46
|
+
childKey?: string | undefined;
|
|
47
|
+
}): Promise<string>;
|
|
43
48
|
/**
|
|
44
49
|
* List workflows in the tenant associated with the API token.
|
|
45
50
|
* @returns a list of all workflows in the tenant
|
|
@@ -63,14 +63,11 @@ class AdminClient {
|
|
|
63
63
|
* @param input an object containing the input to the workflow
|
|
64
64
|
* @returns the ID of the new workflow run
|
|
65
65
|
*/
|
|
66
|
-
run_workflow(workflowName, input) {
|
|
66
|
+
run_workflow(workflowName, input, options) {
|
|
67
67
|
return __awaiter(this, void 0, void 0, function* () {
|
|
68
68
|
try {
|
|
69
69
|
const inputStr = JSON.stringify(input);
|
|
70
|
-
const resp = yield this.client.triggerWorkflow({
|
|
71
|
-
name: workflowName,
|
|
72
|
-
input: inputStr,
|
|
73
|
-
});
|
|
70
|
+
const resp = yield this.client.triggerWorkflow(Object.assign({ name: workflowName, input: inputStr }, options));
|
|
74
71
|
return resp.workflowRunId;
|
|
75
72
|
}
|
|
76
73
|
catch (e) {
|
|
@@ -2,6 +2,10 @@ import { DispatcherClient as PbDispatcherClient, AssignedAction } from '../../pr
|
|
|
2
2
|
import { ClientConfig } from '../hatchet-client/client-config';
|
|
3
3
|
import { Logger } from '../../util/logger';
|
|
4
4
|
import { DispatcherClient } from './dispatcher-client';
|
|
5
|
+
declare enum ListenStrategy {
|
|
6
|
+
LISTEN_STRATEGY_V1 = 1,
|
|
7
|
+
LISTEN_STRATEGY_V2 = 2
|
|
8
|
+
}
|
|
5
9
|
export interface Action {
|
|
6
10
|
tenantId: string;
|
|
7
11
|
jobId: string;
|
|
@@ -26,9 +30,16 @@ export declare class ActionListener {
|
|
|
26
30
|
retryInterval: number;
|
|
27
31
|
retryCount: number;
|
|
28
32
|
done: boolean;
|
|
33
|
+
listenStrategy: ListenStrategy;
|
|
34
|
+
heartbeatInterval: any;
|
|
29
35
|
constructor(client: DispatcherClient, workerId: string, retryInterval?: number, retryCount?: number);
|
|
30
36
|
actions: () => AsyncGenerator<Action, void, unknown>;
|
|
37
|
+
setListenStrategy(strategy: ListenStrategy): Promise<void>;
|
|
38
|
+
getListenStrategy(): Promise<ListenStrategy>;
|
|
31
39
|
incrementRetries(): Promise<void>;
|
|
40
|
+
heartbeat(): Promise<void>;
|
|
41
|
+
closeHeartbeat(): void;
|
|
32
42
|
getListenClient(): Promise<AsyncIterable<AssignedAction>>;
|
|
33
43
|
unregister(): Promise<import("../../protoc/dispatcher").WorkerUnsubscribeResponse>;
|
|
34
44
|
}
|
|
45
|
+
export {};
|
|
@@ -39,6 +39,12 @@ const hatchet_error_1 = __importDefault(require("../../util/errors/hatchet-error
|
|
|
39
39
|
const logger_1 = require("../../util/logger");
|
|
40
40
|
const DEFAULT_ACTION_LISTENER_RETRY_INTERVAL = 5000; // milliseconds
|
|
41
41
|
const DEFAULT_ACTION_LISTENER_RETRY_COUNT = 5;
|
|
42
|
+
// eslint-disable-next-line no-shadow
|
|
43
|
+
var ListenStrategy;
|
|
44
|
+
(function (ListenStrategy) {
|
|
45
|
+
ListenStrategy[ListenStrategy["LISTEN_STRATEGY_V1"] = 1] = "LISTEN_STRATEGY_V1";
|
|
46
|
+
ListenStrategy[ListenStrategy["LISTEN_STRATEGY_V2"] = 2] = "LISTEN_STRATEGY_V2";
|
|
47
|
+
})(ListenStrategy || (ListenStrategy = {}));
|
|
42
48
|
class ActionListener {
|
|
43
49
|
constructor(client, workerId, retryInterval = DEFAULT_ACTION_LISTENER_RETRY_INTERVAL, retryCount = DEFAULT_ACTION_LISTENER_RETRY_COUNT) {
|
|
44
50
|
this.lastConnectionAttempt = 0;
|
|
@@ -46,6 +52,7 @@ class ActionListener {
|
|
|
46
52
|
this.retryInterval = DEFAULT_ACTION_LISTENER_RETRY_INTERVAL;
|
|
47
53
|
this.retryCount = DEFAULT_ACTION_LISTENER_RETRY_COUNT;
|
|
48
54
|
this.done = false;
|
|
55
|
+
this.listenStrategy = ListenStrategy.LISTEN_STRATEGY_V2;
|
|
49
56
|
this.actions = () => (function gen(client) {
|
|
50
57
|
return __asyncGenerator(this, arguments, function* gen_1() {
|
|
51
58
|
var _a, e_1, _b, _c;
|
|
@@ -79,6 +86,10 @@ class ActionListener {
|
|
|
79
86
|
if (e.code === nice_grpc_1.Status.CANCELLED) {
|
|
80
87
|
break;
|
|
81
88
|
}
|
|
89
|
+
if ((yield __await(client.getListenStrategy())) === ListenStrategy.LISTEN_STRATEGY_V2 &&
|
|
90
|
+
e.code === nice_grpc_1.Status.UNIMPLEMENTED) {
|
|
91
|
+
client.setListenStrategy(ListenStrategy.LISTEN_STRATEGY_V1);
|
|
92
|
+
}
|
|
82
93
|
client.incrementRetries();
|
|
83
94
|
}
|
|
84
95
|
}
|
|
@@ -91,11 +102,62 @@ class ActionListener {
|
|
|
91
102
|
this.retryInterval = retryInterval;
|
|
92
103
|
this.retryCount = retryCount;
|
|
93
104
|
}
|
|
105
|
+
setListenStrategy(strategy) {
|
|
106
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
107
|
+
this.listenStrategy = strategy;
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
getListenStrategy() {
|
|
111
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
112
|
+
return this.listenStrategy;
|
|
113
|
+
});
|
|
114
|
+
}
|
|
94
115
|
incrementRetries() {
|
|
95
116
|
return __awaiter(this, void 0, void 0, function* () {
|
|
96
117
|
this.retries += 1;
|
|
97
118
|
});
|
|
98
119
|
}
|
|
120
|
+
heartbeat() {
|
|
121
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
122
|
+
if (this.heartbeatInterval) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
// start with a heartbeat
|
|
126
|
+
try {
|
|
127
|
+
yield this.client.heartbeat({
|
|
128
|
+
workerId: this.workerId,
|
|
129
|
+
heartbeatAt: new Date(),
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
catch (e) {
|
|
133
|
+
this.logger.error(`Failed to send heartbeat: ${e.message}`);
|
|
134
|
+
if (e.code === nice_grpc_1.Status.UNIMPLEMENTED) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
this.heartbeatInterval = setInterval(() => __awaiter(this, void 0, void 0, function* () {
|
|
139
|
+
try {
|
|
140
|
+
yield this.client.heartbeat({
|
|
141
|
+
workerId: this.workerId,
|
|
142
|
+
heartbeatAt: new Date(),
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
catch (e) {
|
|
146
|
+
if (e.code === nice_grpc_1.Status.UNIMPLEMENTED) {
|
|
147
|
+
// break out of interval
|
|
148
|
+
this.closeHeartbeat();
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
this.logger.error(`Failed to send heartbeat: ${e.message}`);
|
|
152
|
+
}
|
|
153
|
+
}), 4000);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
closeHeartbeat() {
|
|
157
|
+
if (this.heartbeatInterval) {
|
|
158
|
+
clearInterval(this.heartbeatInterval);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
99
161
|
getListenClient() {
|
|
100
162
|
return __awaiter(this, void 0, void 0, function* () {
|
|
101
163
|
const currentTime = Math.floor(Date.now());
|
|
@@ -112,9 +174,16 @@ class ActionListener {
|
|
|
112
174
|
yield (0, sleep_1.default)(DEFAULT_ACTION_LISTENER_RETRY_INTERVAL);
|
|
113
175
|
}
|
|
114
176
|
try {
|
|
115
|
-
|
|
177
|
+
if (this.listenStrategy === ListenStrategy.LISTEN_STRATEGY_V1) {
|
|
178
|
+
return this.client.listen({
|
|
179
|
+
workerId: this.workerId,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
const res = this.client.listenV2({
|
|
116
183
|
workerId: this.workerId,
|
|
117
184
|
});
|
|
185
|
+
this.heartbeat();
|
|
186
|
+
return res;
|
|
118
187
|
}
|
|
119
188
|
catch (e) {
|
|
120
189
|
this.retries += 1;
|
|
@@ -126,6 +195,7 @@ class ActionListener {
|
|
|
126
195
|
unregister() {
|
|
127
196
|
return __awaiter(this, void 0, void 0, function* () {
|
|
128
197
|
this.done = true;
|
|
198
|
+
this.closeHeartbeat();
|
|
129
199
|
try {
|
|
130
200
|
return this.client.unsubscribe({
|
|
131
201
|
workerId: this.workerId,
|
|
@@ -110,7 +110,7 @@ class HatchetClient {
|
|
|
110
110
|
this.event = new event_client_1.EventClient(this.config, this.channel, clientFactory);
|
|
111
111
|
this.dispatcher = new dispatcher_client_1.DispatcherClient(this.config, this.channel, clientFactory);
|
|
112
112
|
this.admin = new admin_client_1.AdminClient(this.config, this.channel, clientFactory, this.api, this.tenantId);
|
|
113
|
-
this.listener = new listener_client_1.ListenerClient(this.config, this.channel, clientFactory);
|
|
113
|
+
this.listener = new listener_client_1.ListenerClient(this.config, this.channel, clientFactory, this.api);
|
|
114
114
|
this.logger = new logger_1.default('HatchetClient', this.config.log_level);
|
|
115
115
|
this.logger.info(`Initialized HatchetClient`);
|
|
116
116
|
}
|
|
@@ -1,26 +1,49 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
1
2
|
import { Channel, ClientFactory } from 'nice-grpc';
|
|
3
|
+
import { EventEmitter } from 'events';
|
|
2
4
|
import { DispatcherClient as PbDispatcherClient } from '../../protoc/dispatcher';
|
|
3
5
|
import { ClientConfig } from '../hatchet-client/client-config';
|
|
4
6
|
import { Logger } from '../../util/logger';
|
|
5
|
-
|
|
7
|
+
import { Api } from '../rest';
|
|
8
|
+
export declare enum RunEventType {
|
|
6
9
|
STEP_RUN_EVENT_TYPE_STARTED = "STEP_RUN_EVENT_TYPE_STARTED",
|
|
7
10
|
STEP_RUN_EVENT_TYPE_COMPLETED = "STEP_RUN_EVENT_TYPE_COMPLETED",
|
|
8
11
|
STEP_RUN_EVENT_TYPE_FAILED = "STEP_RUN_EVENT_TYPE_FAILED",
|
|
9
12
|
STEP_RUN_EVENT_TYPE_CANCELLED = "STEP_RUN_EVENT_TYPE_CANCELLED",
|
|
10
|
-
STEP_RUN_EVENT_TYPE_TIMED_OUT = "STEP_RUN_EVENT_TYPE_TIMED_OUT"
|
|
13
|
+
STEP_RUN_EVENT_TYPE_TIMED_OUT = "STEP_RUN_EVENT_TYPE_TIMED_OUT",
|
|
14
|
+
WORKFLOW_RUN_EVENT_TYPE_STARTED = "WORKFLOW_RUN_EVENT_TYPE_STARTED",
|
|
15
|
+
WORKFLOW_RUN_EVENT_TYPE_COMPLETED = "WORKFLOW_RUN_EVENT_TYPE_COMPLETED",
|
|
16
|
+
WORKFLOW_RUN_EVENT_TYPE_FAILED = "WORKFLOW_RUN_EVENT_TYPE_FAILED",
|
|
17
|
+
WORKFLOW_RUN_EVENT_TYPE_CANCELLED = "WORKFLOW_RUN_EVENT_TYPE_CANCELLED",
|
|
18
|
+
WORKFLOW_RUN_EVENT_TYPE_TIMED_OUT = "WORKFLOW_RUN_EVENT_TYPE_TIMED_OUT"
|
|
11
19
|
}
|
|
12
20
|
export interface StepRunEvent {
|
|
13
|
-
type:
|
|
21
|
+
type: RunEventType;
|
|
14
22
|
payload: string;
|
|
15
23
|
}
|
|
24
|
+
export declare class PollingAsyncListener {
|
|
25
|
+
client: ListenerClient;
|
|
26
|
+
q: Array<StepRunEvent>;
|
|
27
|
+
eventEmitter: EventEmitter;
|
|
28
|
+
pollInterval: any;
|
|
29
|
+
constructor(workflowRunid: string, client: ListenerClient);
|
|
30
|
+
emit(event: StepRunEvent): void;
|
|
31
|
+
listen(workflowRunId: string): Promise<void>;
|
|
32
|
+
retrySubscribe(workflowRunId: string): Promise<AsyncIterable<import("../../protoc/dispatcher").WorkflowEvent>>;
|
|
33
|
+
getWorkflowRun(workflowRunId: string): Promise<{
|
|
34
|
+
type: RunEventType;
|
|
35
|
+
payload: string;
|
|
36
|
+
} | undefined>;
|
|
37
|
+
polling(workflowRunId: string): Promise<void>;
|
|
38
|
+
close(): void;
|
|
39
|
+
stream(): AsyncGenerator<StepRunEvent, void, unknown>;
|
|
40
|
+
}
|
|
16
41
|
export declare class ListenerClient {
|
|
17
42
|
config: ClientConfig;
|
|
18
43
|
client: PbDispatcherClient;
|
|
19
44
|
logger: Logger;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}, void, unknown>;
|
|
25
|
-
retrySubscribe(workflowRunId: string): Promise<AsyncIterable<import("../../protoc/dispatcher").WorkflowEvent>>;
|
|
45
|
+
api: Api;
|
|
46
|
+
constructor(config: ClientConfig, channel: Channel, factory: ClientFactory, api: Api);
|
|
47
|
+
get(workflowRunId: string): PollingAsyncListener;
|
|
48
|
+
stream(workflowRunId: string): Promise<AsyncGenerator<StepRunEvent, void, unknown>>;
|
|
26
49
|
}
|
|
@@ -32,94 +32,137 @@ 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.ListenerClient = exports.
|
|
35
|
+
exports.ListenerClient = exports.PollingAsyncListener = exports.RunEventType = void 0;
|
|
36
|
+
// eslint-disable-next-line max-classes-per-file
|
|
36
37
|
const nice_grpc_1 = require("nice-grpc");
|
|
38
|
+
const events_1 = require("events");
|
|
37
39
|
const dispatcher_1 = require("../../protoc/dispatcher");
|
|
38
40
|
const hatchet_error_1 = __importDefault(require("../../util/errors/hatchet-error"));
|
|
39
41
|
const logger_1 = require("../../util/logger");
|
|
40
42
|
const sleep_1 = __importDefault(require("../../util/sleep"));
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
+
const data_contracts_1 = require("../rest/generated/data-contracts");
|
|
44
|
+
const DEFAULT_EVENT_LISTENER_RETRY_INTERVAL = 5; // seconds
|
|
45
|
+
const DEFAULT_EVENT_LISTENER_RETRY_COUNT = 5;
|
|
46
|
+
const DEFAULT_EVENT_LISTENER_POLL_INTERVAL = 5000; // milliseconds
|
|
43
47
|
// eslint-disable-next-line no-shadow
|
|
44
|
-
var
|
|
45
|
-
(function (
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
48
|
+
var RunEventType;
|
|
49
|
+
(function (RunEventType) {
|
|
50
|
+
RunEventType["STEP_RUN_EVENT_TYPE_STARTED"] = "STEP_RUN_EVENT_TYPE_STARTED";
|
|
51
|
+
RunEventType["STEP_RUN_EVENT_TYPE_COMPLETED"] = "STEP_RUN_EVENT_TYPE_COMPLETED";
|
|
52
|
+
RunEventType["STEP_RUN_EVENT_TYPE_FAILED"] = "STEP_RUN_EVENT_TYPE_FAILED";
|
|
53
|
+
RunEventType["STEP_RUN_EVENT_TYPE_CANCELLED"] = "STEP_RUN_EVENT_TYPE_CANCELLED";
|
|
54
|
+
RunEventType["STEP_RUN_EVENT_TYPE_TIMED_OUT"] = "STEP_RUN_EVENT_TYPE_TIMED_OUT";
|
|
55
|
+
RunEventType["WORKFLOW_RUN_EVENT_TYPE_STARTED"] = "WORKFLOW_RUN_EVENT_TYPE_STARTED";
|
|
56
|
+
RunEventType["WORKFLOW_RUN_EVENT_TYPE_COMPLETED"] = "WORKFLOW_RUN_EVENT_TYPE_COMPLETED";
|
|
57
|
+
RunEventType["WORKFLOW_RUN_EVENT_TYPE_FAILED"] = "WORKFLOW_RUN_EVENT_TYPE_FAILED";
|
|
58
|
+
RunEventType["WORKFLOW_RUN_EVENT_TYPE_CANCELLED"] = "WORKFLOW_RUN_EVENT_TYPE_CANCELLED";
|
|
59
|
+
RunEventType["WORKFLOW_RUN_EVENT_TYPE_TIMED_OUT"] = "WORKFLOW_RUN_EVENT_TYPE_TIMED_OUT";
|
|
60
|
+
})(RunEventType || (exports.RunEventType = RunEventType = {}));
|
|
61
|
+
const stepEventTypeMap = {
|
|
62
|
+
[dispatcher_1.ResourceEventType.RESOURCE_EVENT_TYPE_STARTED]: RunEventType.STEP_RUN_EVENT_TYPE_STARTED,
|
|
63
|
+
[dispatcher_1.ResourceEventType.RESOURCE_EVENT_TYPE_COMPLETED]: RunEventType.STEP_RUN_EVENT_TYPE_COMPLETED,
|
|
64
|
+
[dispatcher_1.ResourceEventType.RESOURCE_EVENT_TYPE_FAILED]: RunEventType.STEP_RUN_EVENT_TYPE_FAILED,
|
|
65
|
+
[dispatcher_1.ResourceEventType.RESOURCE_EVENT_TYPE_CANCELLED]: RunEventType.STEP_RUN_EVENT_TYPE_CANCELLED,
|
|
66
|
+
[dispatcher_1.ResourceEventType.RESOURCE_EVENT_TYPE_TIMED_OUT]: RunEventType.STEP_RUN_EVENT_TYPE_TIMED_OUT,
|
|
67
|
+
[dispatcher_1.ResourceEventType.RESOURCE_EVENT_TYPE_UNKNOWN]: undefined,
|
|
68
|
+
[dispatcher_1.ResourceEventType.UNRECOGNIZED]: undefined,
|
|
69
|
+
};
|
|
70
|
+
const workflowEventTypeMap = {
|
|
71
|
+
[dispatcher_1.ResourceEventType.RESOURCE_EVENT_TYPE_STARTED]: RunEventType.WORKFLOW_RUN_EVENT_TYPE_STARTED,
|
|
72
|
+
[dispatcher_1.ResourceEventType.RESOURCE_EVENT_TYPE_COMPLETED]: RunEventType.WORKFLOW_RUN_EVENT_TYPE_COMPLETED,
|
|
73
|
+
[dispatcher_1.ResourceEventType.RESOURCE_EVENT_TYPE_FAILED]: RunEventType.WORKFLOW_RUN_EVENT_TYPE_FAILED,
|
|
74
|
+
[dispatcher_1.ResourceEventType.RESOURCE_EVENT_TYPE_CANCELLED]: RunEventType.WORKFLOW_RUN_EVENT_TYPE_CANCELLED,
|
|
75
|
+
[dispatcher_1.ResourceEventType.RESOURCE_EVENT_TYPE_TIMED_OUT]: RunEventType.WORKFLOW_RUN_EVENT_TYPE_TIMED_OUT,
|
|
76
|
+
[dispatcher_1.ResourceEventType.RESOURCE_EVENT_TYPE_UNKNOWN]: undefined,
|
|
77
|
+
[dispatcher_1.ResourceEventType.UNRECOGNIZED]: undefined,
|
|
78
|
+
};
|
|
79
|
+
const resourceTypeMap = {
|
|
80
|
+
[dispatcher_1.ResourceType.RESOURCE_TYPE_STEP_RUN]: stepEventTypeMap,
|
|
81
|
+
[dispatcher_1.ResourceType.RESOURCE_TYPE_WORKFLOW_RUN]: workflowEventTypeMap,
|
|
82
|
+
[dispatcher_1.ResourceType.RESOURCE_TYPE_UNKNOWN]: undefined,
|
|
83
|
+
[dispatcher_1.ResourceType.UNRECOGNIZED]: undefined,
|
|
84
|
+
};
|
|
85
|
+
const workflowStatusMap = {
|
|
86
|
+
[data_contracts_1.WorkflowRunStatus.SUCCEEDED]: RunEventType.WORKFLOW_RUN_EVENT_TYPE_COMPLETED,
|
|
87
|
+
[data_contracts_1.WorkflowRunStatus.FAILED]: RunEventType.WORKFLOW_RUN_EVENT_TYPE_FAILED,
|
|
88
|
+
[data_contracts_1.WorkflowRunStatus.CANCELLED]: RunEventType.WORKFLOW_RUN_EVENT_TYPE_CANCELLED,
|
|
89
|
+
[data_contracts_1.WorkflowRunStatus.PENDING]: undefined,
|
|
90
|
+
[data_contracts_1.WorkflowRunStatus.RUNNING]: undefined,
|
|
91
|
+
};
|
|
92
|
+
class PollingAsyncListener {
|
|
93
|
+
constructor(workflowRunid, client) {
|
|
94
|
+
this.q = [];
|
|
95
|
+
this.eventEmitter = new events_1.EventEmitter();
|
|
96
|
+
this.client = client;
|
|
97
|
+
this.listen(workflowRunid);
|
|
98
|
+
this.polling(workflowRunid);
|
|
57
99
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
100
|
+
emit(event) {
|
|
101
|
+
this.q.push(event);
|
|
102
|
+
this.eventEmitter.emit('event');
|
|
103
|
+
}
|
|
104
|
+
listen(workflowRunId) {
|
|
105
|
+
var _a, e_1, _b, _c;
|
|
106
|
+
var _d;
|
|
107
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
108
|
+
let listener = this.client.client.subscribeToWorkflowEvents({
|
|
62
109
|
workflowRunId,
|
|
63
110
|
});
|
|
111
|
+
const res = yield this.getWorkflowRun(workflowRunId);
|
|
112
|
+
if (res) {
|
|
113
|
+
this.emit(res);
|
|
114
|
+
this.close();
|
|
115
|
+
}
|
|
64
116
|
try {
|
|
65
117
|
try {
|
|
66
|
-
for (var
|
|
118
|
+
for (var _e = true, listener_1 = __asyncValues(listener), listener_1_1; listener_1_1 = yield listener_1.next(), _a = listener_1_1.done, !_a; _e = true) {
|
|
67
119
|
_c = listener_1_1.value;
|
|
68
|
-
|
|
120
|
+
_e = false;
|
|
69
121
|
const workflowEvent = _c;
|
|
70
|
-
|
|
71
|
-
switch (workflowEvent.eventType) {
|
|
72
|
-
case dispatcher_1.ResourceEventType.RESOURCE_EVENT_TYPE_STARTED:
|
|
73
|
-
eventType = StepRunEventType.STEP_RUN_EVENT_TYPE_STARTED;
|
|
74
|
-
break;
|
|
75
|
-
case dispatcher_1.ResourceEventType.RESOURCE_EVENT_TYPE_COMPLETED:
|
|
76
|
-
eventType = StepRunEventType.STEP_RUN_EVENT_TYPE_COMPLETED;
|
|
77
|
-
break;
|
|
78
|
-
case dispatcher_1.ResourceEventType.RESOURCE_EVENT_TYPE_FAILED:
|
|
79
|
-
eventType = StepRunEventType.STEP_RUN_EVENT_TYPE_FAILED;
|
|
80
|
-
break;
|
|
81
|
-
case dispatcher_1.ResourceEventType.RESOURCE_EVENT_TYPE_CANCELLED:
|
|
82
|
-
eventType = StepRunEventType.STEP_RUN_EVENT_TYPE_CANCELLED;
|
|
83
|
-
break;
|
|
84
|
-
case dispatcher_1.ResourceEventType.RESOURCE_EVENT_TYPE_TIMED_OUT:
|
|
85
|
-
eventType = StepRunEventType.STEP_RUN_EVENT_TYPE_TIMED_OUT;
|
|
86
|
-
break;
|
|
87
|
-
default:
|
|
88
|
-
// no nothing
|
|
89
|
-
}
|
|
122
|
+
const eventType = (_d = resourceTypeMap[workflowEvent.resourceType]) === null || _d === void 0 ? void 0 : _d[workflowEvent.eventType];
|
|
90
123
|
if (eventType) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
124
|
+
if (eventType === RunEventType.WORKFLOW_RUN_EVENT_TYPE_COMPLETED) {
|
|
125
|
+
// OPTIMZATION - consider including the workflow run data in the event?
|
|
126
|
+
const data = yield this.getWorkflowRun(workflowRunId);
|
|
127
|
+
if (data) {
|
|
128
|
+
this.emit(data);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
this.emit({
|
|
133
|
+
type: eventType,
|
|
134
|
+
payload: workflowEvent.eventPayload,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
95
137
|
}
|
|
96
138
|
}
|
|
97
139
|
}
|
|
98
140
|
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
99
141
|
finally {
|
|
100
142
|
try {
|
|
101
|
-
if (!
|
|
143
|
+
if (!_e && !_a && (_b = listener_1.return)) yield _b.call(listener_1);
|
|
102
144
|
}
|
|
103
145
|
finally { if (e_1) throw e_1.error; }
|
|
104
146
|
}
|
|
105
147
|
}
|
|
106
148
|
catch (e) {
|
|
107
149
|
if (e.code === nice_grpc_1.Status.CANCELLED) {
|
|
108
|
-
return
|
|
150
|
+
return;
|
|
109
151
|
}
|
|
110
152
|
if (e.code === nice_grpc_1.Status.UNAVAILABLE) {
|
|
111
|
-
listener = yield
|
|
153
|
+
listener = yield this.retrySubscribe(workflowRunId);
|
|
112
154
|
}
|
|
113
155
|
}
|
|
156
|
+
setTimeout(() => this.close(), DEFAULT_EVENT_LISTENER_POLL_INTERVAL * 5);
|
|
114
157
|
});
|
|
115
158
|
}
|
|
116
159
|
retrySubscribe(workflowRunId) {
|
|
117
160
|
return __awaiter(this, void 0, void 0, function* () {
|
|
118
161
|
let retries = 0;
|
|
119
|
-
while (retries <
|
|
162
|
+
while (retries < DEFAULT_EVENT_LISTENER_RETRY_COUNT) {
|
|
120
163
|
try {
|
|
121
|
-
yield (0, sleep_1.default)(
|
|
122
|
-
const listener = this.client.subscribeToWorkflowEvents({
|
|
164
|
+
yield (0, sleep_1.default)(DEFAULT_EVENT_LISTENER_RETRY_INTERVAL);
|
|
165
|
+
const listener = this.client.client.subscribeToWorkflowEvents({
|
|
123
166
|
workflowRunId,
|
|
124
167
|
});
|
|
125
168
|
return listener;
|
|
@@ -128,7 +171,98 @@ class ListenerClient {
|
|
|
128
171
|
retries += 1;
|
|
129
172
|
}
|
|
130
173
|
}
|
|
131
|
-
throw new hatchet_error_1.default(`Could not subscribe to the worker after ${
|
|
174
|
+
throw new hatchet_error_1.default(`Could not subscribe to the worker after ${DEFAULT_EVENT_LISTENER_RETRY_COUNT} retries`);
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
getWorkflowRun(workflowRunId) {
|
|
178
|
+
var _a, _b, _c;
|
|
179
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
180
|
+
try {
|
|
181
|
+
const res = yield this.client.api.workflowRunGet(this.client.config.tenant_id, workflowRunId);
|
|
182
|
+
const stepRuns = (_c = (_b = (_a = res.data.jobRuns) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.stepRuns) !== null && _c !== void 0 ? _c : [];
|
|
183
|
+
const stepRunOutput = stepRuns.reduce((acc, stepRun) => {
|
|
184
|
+
var _a;
|
|
185
|
+
acc[((_a = stepRun.step) === null || _a === void 0 ? void 0 : _a.readableId) || ''] = JSON.parse(stepRun.output || '{}');
|
|
186
|
+
return acc;
|
|
187
|
+
}, {});
|
|
188
|
+
if (Object.keys(workflowStatusMap).includes(res.data.status)) {
|
|
189
|
+
const type = workflowStatusMap[res.data.status];
|
|
190
|
+
if (!type)
|
|
191
|
+
return undefined;
|
|
192
|
+
return {
|
|
193
|
+
type,
|
|
194
|
+
payload: JSON.stringify(stepRunOutput),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
return undefined;
|
|
198
|
+
}
|
|
199
|
+
catch (e) {
|
|
200
|
+
throw new hatchet_error_1.default(e.message);
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
polling(workflowRunId) {
|
|
205
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
206
|
+
this.pollInterval = setInterval(() => __awaiter(this, void 0, void 0, function* () {
|
|
207
|
+
try {
|
|
208
|
+
const result = yield this.getWorkflowRun(workflowRunId);
|
|
209
|
+
if (result) {
|
|
210
|
+
this.emit(result);
|
|
211
|
+
this.close();
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
catch (e) {
|
|
215
|
+
// TODO error handling
|
|
216
|
+
}
|
|
217
|
+
}), DEFAULT_EVENT_LISTENER_POLL_INTERVAL);
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
close() {
|
|
221
|
+
if (this.pollInterval) {
|
|
222
|
+
clearInterval(this.pollInterval);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
stream() {
|
|
226
|
+
return __asyncGenerator(this, arguments, function* stream_1() {
|
|
227
|
+
var _a, e_2, _b, _c;
|
|
228
|
+
try {
|
|
229
|
+
for (var _d = true, _e = __asyncValues((0, events_1.on)(this.eventEmitter, 'event')), _f; _f = yield __await(_e.next()), _a = _f.done, !_a; _d = true) {
|
|
230
|
+
_c = _f.value;
|
|
231
|
+
_d = false;
|
|
232
|
+
const _ = _c;
|
|
233
|
+
while (this.q.length > 0) {
|
|
234
|
+
const r = this.q.shift();
|
|
235
|
+
if (r) {
|
|
236
|
+
yield yield __await(r);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
242
|
+
finally {
|
|
243
|
+
try {
|
|
244
|
+
if (!_d && !_a && (_b = _e.return)) yield __await(_b.call(_e));
|
|
245
|
+
}
|
|
246
|
+
finally { if (e_2) throw e_2.error; }
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
exports.PollingAsyncListener = PollingAsyncListener;
|
|
252
|
+
class ListenerClient {
|
|
253
|
+
constructor(config, channel, factory, api) {
|
|
254
|
+
this.config = config;
|
|
255
|
+
this.client = factory.create(dispatcher_1.DispatcherDefinition, channel);
|
|
256
|
+
this.logger = new logger_1.Logger(`Listener`, config.log_level);
|
|
257
|
+
this.api = api;
|
|
258
|
+
}
|
|
259
|
+
get(workflowRunId) {
|
|
260
|
+
const listener = new PollingAsyncListener(workflowRunId, this);
|
|
261
|
+
return listener;
|
|
262
|
+
}
|
|
263
|
+
stream(workflowRunId) {
|
|
264
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
265
|
+
return this.get(workflowRunId).stream();
|
|
132
266
|
});
|
|
133
267
|
}
|
|
134
268
|
}
|
package/clients/worker/worker.js
CHANGED
|
@@ -104,7 +104,7 @@ class Worker {
|
|
|
104
104
|
handleStartStepRun(action) {
|
|
105
105
|
const { actionId } = action;
|
|
106
106
|
try {
|
|
107
|
-
const context = new step_1.Context(action, this.client
|
|
107
|
+
const context = new step_1.Context(action, this.client);
|
|
108
108
|
this.contexts[action.stepRunId] = context;
|
|
109
109
|
const step = this.action_registry[actionId];
|
|
110
110
|
if (!step) {
|
|
@@ -153,7 +153,7 @@ class Worker {
|
|
|
153
153
|
handleStartGroupKeyRun(action) {
|
|
154
154
|
const { actionId } = action;
|
|
155
155
|
try {
|
|
156
|
-
const context = new step_1.Context(action, this.client
|
|
156
|
+
const context = new step_1.Context(action, this.client);
|
|
157
157
|
const key = action.getGroupKeyRunId;
|
|
158
158
|
this.contexts[key] = context;
|
|
159
159
|
this.logger.debug(`Starting group key run ${key}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hatchet-dev/typescript-sdk",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Background task orchestration & visibility for developers",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"files": [
|
|
@@ -26,14 +26,15 @@
|
|
|
26
26
|
"generate-protoc": "./generate-protoc.sh",
|
|
27
27
|
"lint:check": "npm run eslint:check && npm run prettier:check",
|
|
28
28
|
"lint:fix": "npm run eslint:fix && npm run prettier:fix",
|
|
29
|
-
"eslint:check": "eslint \"{
|
|
30
|
-
"eslint:fix": "eslint \"{
|
|
31
|
-
"prettier:check": "prettier \"
|
|
32
|
-
"prettier:fix": "prettier \"
|
|
29
|
+
"eslint:check": "eslint \"{src,tests}/**/*.{ts,tsx,js}\"",
|
|
30
|
+
"eslint:fix": "eslint \"{src,tests}/**/*.{ts,tsx,js}\" --fix",
|
|
31
|
+
"prettier:check": "prettier \"src/**/*.{ts,tsx}\" --list-different",
|
|
32
|
+
"prettier:fix": "prettier \"src/**/*.{ts,tsx}\" --write",
|
|
33
33
|
"exec": "npx dotenv -- ts-node -r tsconfig-paths/register --project tsconfig.json",
|
|
34
34
|
"example:event": "npm run exec -- ./examples/example-event.ts",
|
|
35
35
|
"example:event-listen": "npm run exec -- ./examples/example-event-with-results.ts",
|
|
36
36
|
"worker:namespaced": "npm run exec -- ./examples/namespaced-worker.ts",
|
|
37
|
+
"worker:fanout": "npm run exec -- ./examples/fanout-worker.ts",
|
|
37
38
|
"worker:simple": "npm run exec -- ./examples/simple-worker.ts",
|
|
38
39
|
"manual:trigger": "npm run exec -- ./examples/manual-trigger.ts",
|
|
39
40
|
"worker:dag": "npm run exec -- ./examples/dag-worker.ts",
|
|
@@ -54,8 +55,8 @@
|
|
|
54
55
|
"author": "",
|
|
55
56
|
"license": "MIT",
|
|
56
57
|
"devDependencies": {
|
|
57
|
-
"@types/node": "^20.11.16",
|
|
58
58
|
"@types/jest": "^29.5.11",
|
|
59
|
+
"@types/node": "^20.11.16",
|
|
59
60
|
"@typescript-eslint/eslint-plugin": "^6.4.0",
|
|
60
61
|
"autoprefixer": "^10.4.16",
|
|
61
62
|
"dotenv-cli": "^7.3.0",
|
|
@@ -69,6 +70,9 @@
|
|
|
69
70
|
"eslint-plugin-n": "^15.0.0 || ^16.0.0 ",
|
|
70
71
|
"eslint-plugin-prettier": "^5.0.1",
|
|
71
72
|
"eslint-plugin-promise": "^6.0.0",
|
|
73
|
+
"eslint-plugin-react": "^7.34.1",
|
|
74
|
+
"eslint-plugin-react-hooks": "^4.6.0",
|
|
75
|
+
"eslint-plugin-react-refresh": "^0.4.6",
|
|
72
76
|
"eslint-plugin-unused-imports": "^3.0.0",
|
|
73
77
|
"grpc-tools": "^1.12.4",
|
|
74
78
|
"jest": "^29.7.0",
|