@webda/async 3.9.3 → 4.0.0-beta.10

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.
Files changed (41) hide show
  1. package/README.md +92 -8
  2. package/lib/asyncaction.model.d.ts +207 -0
  3. package/lib/asyncaction.model.d.ts.map +1 -0
  4. package/lib/asyncaction.model.js +269 -0
  5. package/lib/asyncaction.model.js.map +1 -0
  6. package/lib/index.d.ts +7 -5
  7. package/lib/index.d.ts.map +1 -0
  8. package/lib/index.js +6 -5
  9. package/lib/index.js.map +1 -1
  10. package/lib/services/asyncevents.service.d.ts +154 -0
  11. package/lib/services/asyncevents.service.d.ts.map +1 -0
  12. package/lib/services/asyncevents.service.js +219 -0
  13. package/lib/services/asyncevents.service.js.map +1 -0
  14. package/lib/services/{asyncjobservice.d.ts → asyncjobservice.service.d.ts} +81 -87
  15. package/lib/services/asyncjobservice.service.d.ts.map +1 -0
  16. package/lib/services/asyncjobservice.service.js +596 -0
  17. package/lib/services/asyncjobservice.service.js.map +1 -0
  18. package/lib/services/{localrunner.d.ts → localrunner.service.d.ts} +24 -9
  19. package/lib/services/localrunner.service.d.ts.map +1 -0
  20. package/lib/services/localrunner.service.js +87 -0
  21. package/lib/services/localrunner.service.js.map +1 -0
  22. package/lib/services/{runner.d.ts → runner.service.d.ts} +17 -11
  23. package/lib/services/runner.service.d.ts.map +1 -0
  24. package/lib/services/{runner.js → runner.service.js} +16 -13
  25. package/lib/services/runner.service.js.map +1 -0
  26. package/lib/services/{servicerunner.d.ts → servicerunner.service.d.ts} +39 -37
  27. package/lib/services/servicerunner.service.d.ts.map +1 -0
  28. package/lib/services/servicerunner.service.js +131 -0
  29. package/lib/services/servicerunner.service.js.map +1 -0
  30. package/package.json +35 -39
  31. package/webda.module.json +1910 -868
  32. package/lib/models.d.ts +0 -169
  33. package/lib/models.js +0 -177
  34. package/lib/models.js.map +0 -1
  35. package/lib/services/asyncjobservice.js +0 -521
  36. package/lib/services/asyncjobservice.js.map +0 -1
  37. package/lib/services/localrunner.js +0 -70
  38. package/lib/services/localrunner.js.map +0 -1
  39. package/lib/services/runner.js.map +0 -1
  40. package/lib/services/servicerunner.js +0 -152
  41. package/lib/services/servicerunner.js.map +0 -1
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  This module is part of Webda Application Framework that allows you to quickly develop applications with all modern prerequisites: Security, Extensibility, GraphQL, REST, CloudNative [https://webda.io](https://webda.io)
4
4
 
5
- <img src="https://webda.io/images/webda.svg" width="128" />
5
+ <img src="https://webda.io/img/webda.svg" width="128" />
6
6
 
7
7
  ![CI](https://github.com/loopingz/webda.io/workflows/CI/badge.svg)
8
8
 
@@ -13,12 +13,96 @@ This module is part of Webda Application Framework that allows you to quickly de
13
13
 
14
14
  <!-- README_HEADER -->
15
15
 
16
- This package is exposing a logic of asynchronous tasks management.
17
- You have different types of runner available.
18
- It allows you to launch other language/binary from within your application
19
- and capure the output.
16
+ > Durable asynchronous job orchestration for Webda — queue a service method call, track its status, and optionally schedule it for later execution.
17
+
18
+ ## When to use it
19
+
20
+ - You need to offload long-running work (report generation, data imports) to a background worker without blocking the HTTP response.
21
+ - You want to schedule a recurring or time-delayed operation (cron-based or timestamp-based).
22
+ - You need a persistent audit trail of job executions with status, logs, and results stored in a Webda Store.
23
+
24
+ ## Install
25
+
26
+ ```bash
27
+ pnpm add @webda/async
28
+ ```
29
+
30
+ ## Configuration
31
+
32
+ ```json
33
+ {
34
+ "services": {
35
+ "AsyncActionsQueue": {
36
+ "type": "MemoryQueue"
37
+ },
38
+ "asyncJobService": {
39
+ "type": "AsyncJobService",
40
+ "queue": "AsyncActionsQueue",
41
+ "url": "/async",
42
+ "runners": ["serviceRunner"],
43
+ "includeCron": true,
44
+ "logsLimit": 500
45
+ },
46
+ "serviceRunner": {
47
+ "type": "ServiceRunner"
48
+ }
49
+ }
50
+ }
51
+ ```
52
+
53
+ | Parameter | Type | Default | Required | Description |
54
+ |---|---|---|---|---|
55
+ | `queue` | string | `"AsyncActionsQueue"` | Yes | Name of the Queue service to dispatch jobs through |
56
+ | `url` | string | `"/async"` | No | URL prefix for the status hook endpoint |
57
+ | `runners` | string[] | `[]` | Yes | List of Runner service names to execute jobs |
58
+ | `localLaunch` | boolean | `false` | No | Execute jobs inline (no queue) — useful for development |
59
+ | `fallbackOnFirst` | boolean | `false` | No | Use first runner if no runner matches the job type |
60
+ | `concurrencyLimit` | number | — | No | Maximum number of jobs running concurrently |
61
+ | `includeCron` | boolean | `true` | No | Auto-schedule `@Cron`-annotated methods as async actions |
62
+ | `logsLimit` | number | `500` | No | Max log lines retained per job in the store |
63
+ | `asyncOperationDefinition` | string | — | No | Path to a JSON file of operation definitions generated by `webda operations` |
64
+ | `schedulerResolution` | number | `60000` | No | Scheduler tick interval in milliseconds |
65
+
66
+ ## Usage
67
+
68
+ ```typescript
69
+ import { Service } from "@webda/core";
70
+ import { Bean, Inject } from "@webda/core";
71
+ import AsyncJobService from "@webda/async/services/asyncjobservice";
72
+
73
+ @Bean
74
+ export class ReportService extends Service {
75
+ @Inject("asyncJobService")
76
+ asyncJobService: AsyncJobService;
77
+
78
+ // Expose an HTTP endpoint that queues a job and returns immediately
79
+ async generateReport(ctx: any): Promise<void> {
80
+ const action = await this.asyncJobService.launchAsAsyncAction(
81
+ "ReportService", // service name
82
+ "runReport", // method name
83
+ ctx.getParameters().reportId
84
+ );
85
+ ctx.write({ jobId: action.getUuid(), status: action.status });
86
+ }
87
+
88
+ // This method runs inside the worker process
89
+ async runReport(reportId: string): Promise<void> {
90
+ // ... long-running work here
91
+ }
92
+ }
93
+
94
+ // Start the worker (typically in a separate process)
95
+ // asyncJobService.worker().then(() => console.log("worker stopped"));
96
+ ```
97
+
98
+ ## Reference
99
+
100
+ - API reference: see the auto-generated typedoc at `docs/pages/Modules/async/`.
101
+ - Source: [`packages/async`](https://github.com/loopingz/webda.io/tree/main/packages/async)
102
+ - Related: [`@webda/amqp`](../amqp) for a RabbitMQ-backed queue, [`@webda/aws`](../aws) for SQS, [`@webda/core`](../core) for the base `Queue` and `Store` abstractions.
20
103
 
21
104
  <!-- README_FOOTER -->
105
+
22
106
  ## Sponsors
23
107
 
24
108
  <!--
@@ -27,12 +111,12 @@ Support this project by becoming a sponsor. Your logo will show up here with a l
27
111
 
28
112
  Arize AI is a machine learning observability and model monitoring platform. It helps you visualize, monitor, and explain your machine learning models. [Learn more](https://arize.com)
29
113
 
30
- [<img src="https://arize.com/hubfs/arize/brand/arize-logomark-1.png" width="200">](https://arize.com)
114
+ [<img src="https://webda.io/img/sponsors/arize.svg" width="200">](https://arize.com)
31
115
 
32
116
  Loopingz is a software development company that provides consulting and development services. [Learn more](https://loopingz.com)
33
117
 
34
- [<img src="https://loopingz.com/images/logo.png" width="200">](https://loopingz.com)
118
+ [<img src="https://webda.io/img/sponsors/loopingz.png" width="200">](https://loopingz.com)
35
119
 
36
120
  Tellae is an innovative consulting firm specialized in cities transportation issues. We provide our clients, both public and private, with solutions to support your strategic and operational decisions. [Learn more](https://tellae.fr)
37
121
 
38
- [<img src="https://tellae.fr/" width="200">](https://tellae.fr)
122
+ [<img src="https://webda.io/img/sponsors/tellae.svg" width="200">](https://tellae.fr)
@@ -0,0 +1,207 @@
1
+ import { CoreModel, IOperationContext, OperationContext, WebContext } from "@webda/core";
2
+ import { WorkerLogLevel } from "@webda/workout";
3
+ /**
4
+ * Represent an item for processing queue
5
+ */
6
+ export interface AsyncActionQueueItem {
7
+ /**
8
+ * Item to launch
9
+ */
10
+ uuid: string;
11
+ /**
12
+ * Secret key for status hook
13
+ */
14
+ __secretKey: string;
15
+ /**
16
+ * Type of action
17
+ */
18
+ type: string;
19
+ }
20
+ /**
21
+ * Define here a model that can be used along with Store service
22
+ * @WebdaModel
23
+ * @WebdaPlural AsyncActions
24
+ */
25
+ export default class AsyncAction extends CoreModel {
26
+ /**
27
+ * Create a new action
28
+ * @param data - initial data
29
+ */
30
+ constructor(data?: Partial<AsyncAction>);
31
+ /**
32
+ * By default AsyncAction are not considered internal
33
+ *
34
+ * Internal: That execute a method or operation of Webda application framework
35
+ * @returns false
36
+ */
37
+ isInternal(): boolean;
38
+ /**
39
+ * Current status
40
+ */
41
+ status: "RUNNING" | "SUCCESS" | "ERROR" | "QUEUED" | "STARTING" | "TIMEOUT" | "SCHEDULED";
42
+ /**
43
+ * Timestamp when the action was scheduled
44
+ */
45
+ scheduled?: number;
46
+ /**
47
+ * If an error occured it should contain the message
48
+ */
49
+ errorMessage?: string;
50
+ /**
51
+ * If an error occured it should contain the name
52
+ */
53
+ errorName?: string;
54
+ /**
55
+ * Job information
56
+ */
57
+ job: any;
58
+ /**
59
+ * Last time the job was updated
60
+ */
61
+ _lastJobUpdate: number;
62
+ /**
63
+ * Results from the job
64
+ */
65
+ results: any;
66
+ /**
67
+ * Job current status
68
+ */
69
+ statusDetails: any;
70
+ /**
71
+ * Type of action
72
+ */
73
+ type: string;
74
+ /**
75
+ * Arguments of the action
76
+ */
77
+ arguments?: any[];
78
+ /**
79
+ * Current logs
80
+ */
81
+ logs: string[];
82
+ /**
83
+ * Secret key to post feedback
84
+ */
85
+ __secretKey: string;
86
+ /**
87
+ * Expected action for the job
88
+ *
89
+ * It should be a verb
90
+ */
91
+ action?: "STOP" | string;
92
+ /**
93
+ * Maximum number of log lines kept
94
+ * @returns the limit
95
+ */
96
+ getLogsLimit(): number;
97
+ /**
98
+ * Allow to report status for a job
99
+ *
100
+ * Only a job runner with a valid HMAC can call it
101
+ * @param context - the web context of the job runner
102
+ */
103
+ statusAction(context?: OperationContext): Promise<void>;
104
+ /**
105
+ * Return the absolute url of this action status route ({uuid}/status)
106
+ *
107
+ * Remote runners report to AsyncJobService.getHookUrl() instead, as this route
108
+ * still has to be authorized by the application request filters
109
+ * @returns the absolute url of the status action
110
+ */
111
+ getHookUrl(): string;
112
+ /**
113
+ * Update the action with a status report
114
+ * @param body - the status report
115
+ * @param time - the job time header
116
+ * @returns this
117
+ */
118
+ update(body: any, time?: string): Promise<this>;
119
+ /**
120
+ * Verify the job HMAC headers
121
+ * @param context - the web context
122
+ */
123
+ protected verifyJobRequest(context: WebContext): Promise<void>;
124
+ /**
125
+ * Get the hmac for the job
126
+ * @param jobTime - the job time header
127
+ * @returns the hex hmac
128
+ */
129
+ getHmac(jobTime: string): string;
130
+ /**
131
+ * Only job runners with a valid HMAC can report status or use binaries
132
+ * @param context - the operation context
133
+ * @param action - the action
134
+ * @returns true or the reason of refusal
135
+ */
136
+ canAct(context: IOperationContext, action: string): Promise<string | boolean>;
137
+ /**
138
+ * Throw a Forbidden error if the action is not allowed
139
+ * @param context - the operation context
140
+ * @param action - the action
141
+ */
142
+ checkAct(context: IOperationContext, action: string): Promise<void>;
143
+ }
144
+ /**
145
+ * Define a Webda Async Action
146
+ *
147
+ * @WebdaModel
148
+ * @WebdaPlural AsyncWebdaActions
149
+ */
150
+ export declare class AsyncWebdaAction extends AsyncAction {
151
+ /**
152
+ * Log level to capture
153
+ */
154
+ logLevel: WorkerLogLevel;
155
+ /**
156
+ * Service to call
157
+ */
158
+ serviceName?: string;
159
+ /**
160
+ * Method to call
161
+ */
162
+ method?: string;
163
+ /**
164
+ * @param serviceName service to call or initial data
165
+ * @param method method to call
166
+ * @param args to call with the method
167
+ */
168
+ constructor(serviceName?: string | Partial<AsyncWebdaAction>, method?: string, ...args: any[]);
169
+ /**
170
+ * Execute a serviceName.method(...args) so this is internal
171
+ * @returns true
172
+ */
173
+ isInternal(): boolean;
174
+ }
175
+ /**
176
+ * Operation called asynchronously
177
+ *
178
+ * @WebdaModel
179
+ * @WebdaPlural AsyncOperationActions
180
+ */
181
+ export declare class AsyncOperationAction extends AsyncAction {
182
+ /**
183
+ * Operation to call
184
+ */
185
+ operationId: string;
186
+ /**
187
+ * Context to call the operation with
188
+ */
189
+ context: any;
190
+ /**
191
+ * Log level to capture
192
+ */
193
+ logLevel: WorkerLogLevel;
194
+ /**
195
+ * @param operationId operation to call or initial data
196
+ * @param context context to use
197
+ * @param logLevel log level to capture
198
+ */
199
+ constructor(operationId?: string | Partial<AsyncOperationAction>, context?: OperationContext, logLevel?: WorkerLogLevel);
200
+ /**
201
+ * Execute a webda.callOperation(context ,id) so this is internal
202
+ * @returns true
203
+ */
204
+ isInternal(): boolean;
205
+ }
206
+ export { AsyncAction };
207
+ //# sourceMappingURL=asyncaction.model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asyncaction.model.d.ts","sourceRoot":"","sources":["../src/asyncaction.model.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,SAAS,EACT,iBAAiB,EACjB,gBAAgB,EAChB,UAAU,EAMX,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGhD;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;CACd;AAmBD;;;;GAIG;AACH,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,SAAS;IAChD;;;OAGG;IACH,YAAY,IAAI,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,EAMtC;IAED;;;;;OAKG;IACH,UAAU,IAAI,OAAO,CAEpB;IACD;;OAEG;IACI,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,GAAG,WAAW,CAAC;IAEjG;;OAEG;IACI,SAAS,CAAC,EAAE,MAAM,CAAC;IAE1B;;OAEG;IACI,YAAY,CAAC,EAAE,MAAM,CAAC;IAC7B;;OAEG;IACI,SAAS,CAAC,EAAE,MAAM,CAAC;IAC1B;;OAEG;IACI,GAAG,EAAE,GAAG,CAAC;IAEhB;;OAEG;IACI,cAAc,EAAE,MAAM,CAAC;IAC9B;;OAEG;IACI,OAAO,EAAE,GAAG,CAAC;IAEpB;;OAEG;IACI,aAAa,EAAE,GAAG,CAAC;IAE1B;;OAEG;IACI,IAAI,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACI,SAAS,CAAC,EAAE,GAAG,EAAE,CAAC;IAEzB;;OAEG;IACI,IAAI,EAAE,MAAM,EAAE,CAAC;IAEtB;;OAEG;IACI,WAAW,EAAE,MAAM,CAAC;IAE3B;;;;OAIG;IACI,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAEhC;;;OAGG;IACH,YAAY,WAEX;IAED;;;;;OAKG;IAEU,YAAY,CAAC,OAAO,GAAE,gBAA+B,iBAOjE;IAED;;;;;;OAMG;IACI,UAAU,IAAI,MAAM,CAE1B;IAED;;;;;OAKG;IACU,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,GAAE,MAAkB,iBAsBtD;IAED;;;OAGG;IACH,UAAgB,gBAAgB,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAWnE;IAED;;;;OAIG;IACH,OAAO,CAAC,OAAO,EAAE,MAAM,UAEtB;IAED;;;;;OAKG;IACG,MAAM,CAAC,OAAO,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,CAalF;IAED;;;;OAIG;IACG,QAAQ,CAAC,OAAO,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKxE;CACF;AAED;;;;;GAKG;AACH,qBAAa,gBAAiB,SAAQ,WAAW;IAC/C;;OAEG;IACI,QAAQ,EAAE,cAAc,CAAC;IAChC;;OAEG;IACI,WAAW,CAAC,EAAE,MAAM,CAAC;IAC5B;;OAEG;IACI,MAAM,CAAC,EAAE,MAAM,CAAC;IAEvB;;;;OAIG;IACH,YAAY,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,EAQ5F;IAED;;;OAGG;IACH,UAAU,YAET;CACF;AAED;;;;;GAKG;AACH,qBAAa,oBAAqB,SAAQ,WAAW;IACnD;;OAEG;IACI,WAAW,EAAE,MAAM,CAAC;IAC3B;;OAEG;IACI,OAAO,EAAE,GAAG,CAAC;IACpB;;OAEG;IACI,QAAQ,EAAE,cAAc,CAAC;IAEhC;;;;OAIG;IACH,YACE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,EACpD,OAAO,CAAC,EAAE,gBAAgB,EAC1B,QAAQ,GAAE,cAAuB,EASlC;IAED;;;OAGG;IACH,UAAU,YAET;CACF;AAED,OAAO,EAAE,WAAW,EAAE,CAAC"}
@@ -0,0 +1,269 @@
1
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
2
+ var useValue = arguments.length > 2;
3
+ for (var i = 0; i < initializers.length; i++) {
4
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
5
+ }
6
+ return useValue ? value : void 0;
7
+ };
8
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
9
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
10
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
11
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
12
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
13
+ var _, done = false;
14
+ for (var i = decorators.length - 1; i >= 0; i--) {
15
+ var context = {};
16
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
17
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
18
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
19
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
20
+ if (kind === "accessor") {
21
+ if (result === void 0) continue;
22
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
23
+ if (_ = accept(result.get)) descriptor.get = _;
24
+ if (_ = accept(result.set)) descriptor.set = _;
25
+ if (_ = accept(result.init)) initializers.unshift(_);
26
+ }
27
+ else if (_ = accept(result)) {
28
+ if (kind === "field") initializers.unshift(_);
29
+ else descriptor[key] = _;
30
+ }
31
+ }
32
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
33
+ done = true;
34
+ };
35
+ import { Action, CoreModel, WebContext, WebdaError, useContext, useLog, useParameters, useRouter } from "@webda/core";
36
+ import * as crypto from "node:crypto";
37
+ /**
38
+ * Headers sent by a job runner to authenticate against the status hook
39
+ */
40
+ const JOB_HEADERS = ["X-Job-Hash", "X-Job-Time"];
41
+ /**
42
+ * Check if the context carries the job runner headers
43
+ * @param context - the context to check
44
+ * @returns true if both job headers are present
45
+ */
46
+ function hasJobHeaders(context) {
47
+ return (context instanceof WebContext &&
48
+ JOB_HEADERS.every(h => context.getHttpContext()?.getUniqueHeader(h) !== undefined));
49
+ }
50
+ let AsyncAction = (() => {
51
+ var _a;
52
+ let _classSuper = CoreModel;
53
+ let _instanceExtraInitializers = [];
54
+ let _statusAction_decorators;
55
+ return _a = class AsyncAction extends _classSuper {
56
+ /**
57
+ * Create a new action
58
+ * @param data - initial data
59
+ */
60
+ constructor(data) {
61
+ super(data);
62
+ /**
63
+ * Current status
64
+ */
65
+ this.status = __runInitializers(this, _instanceExtraInitializers);
66
+ if (data) {
67
+ Object.assign(this, data);
68
+ }
69
+ this.type ?? (this.type = this.constructor.name);
70
+ }
71
+ /**
72
+ * By default AsyncAction are not considered internal
73
+ *
74
+ * Internal: That execute a method or operation of Webda application framework
75
+ * @returns false
76
+ */
77
+ isInternal() {
78
+ return false;
79
+ }
80
+ /**
81
+ * Maximum number of log lines kept
82
+ * @returns the limit
83
+ */
84
+ getLogsLimit() {
85
+ return 1000;
86
+ }
87
+ /**
88
+ * Allow to report status for a job
89
+ *
90
+ * Only a job runner with a valid HMAC can call it
91
+ * @param context - the web context of the job runner
92
+ */
93
+ async statusAction(context = useContext()) {
94
+ if (!(context instanceof WebContext)) {
95
+ throw new Error("Only WebContext can call this action");
96
+ }
97
+ await this.checkAct(context, "status");
98
+ await this.update(await context.getRequestBody(), context.getHttpContext().getUniqueHeader("X-Job-Time"));
99
+ context.write({ ...this, logs: undefined, statusDetails: undefined });
100
+ }
101
+ /**
102
+ * Return the absolute url of this action status route ({uuid}/status)
103
+ *
104
+ * Remote runners report to AsyncJobService.getHookUrl() instead, as this route
105
+ * still has to be authorized by the application request filters
106
+ * @returns the absolute url of the status action
107
+ */
108
+ getHookUrl() {
109
+ return `${useParameters().apiUrl ?? ""}${useRouter().getModelUrl(this)}/${this.uuid}/status`;
110
+ }
111
+ /**
112
+ * Update the action with a status report
113
+ * @param body - the status report
114
+ * @param time - the job time header
115
+ * @returns this
116
+ */
117
+ async update(body, time = undefined) {
118
+ if (body.logs && Array.isArray(body.logs)) {
119
+ this.logs ?? (this.logs = []);
120
+ this.logs.push(...body.logs);
121
+ // Prevent having too much logs
122
+ if (this.logs.length > this.getLogsLimit()) {
123
+ this.logs = this.logs.slice(-1 * this.getLogsLimit());
124
+ }
125
+ }
126
+ this._lastJobUpdate = Number.parseInt(time || "0") || 0;
127
+ if (Date.now() - this._lastJobUpdate > 60) {
128
+ this._lastJobUpdate = Date.now();
129
+ }
130
+ await this.patch({
131
+ _lastJobUpdate: this._lastJobUpdate,
132
+ logs: this.logs,
133
+ status: body.status || this.status,
134
+ errorMessage: body.errorMessage || this.errorMessage,
135
+ statusDetails: body.statusDetails || this.statusDetails,
136
+ results: body.results || this.results
137
+ });
138
+ return this;
139
+ }
140
+ /**
141
+ * Verify the job HMAC headers
142
+ * @param context - the web context
143
+ */
144
+ async verifyJobRequest(context) {
145
+ const jobTime = context.getHttpContext().getUniqueHeader("X-Job-Time");
146
+ const jobHash = context.getHttpContext().getUniqueHeader("X-Job-Hash");
147
+ // Ensure hash mac is correct
148
+ if (jobHash !== this.getHmac(jobTime)) {
149
+ useLog("TRACE", "Invalid Job HMAC");
150
+ throw new WebdaError.Forbidden("Invalid Job HMAC");
151
+ }
152
+ // Set the context extension
153
+ context.setExtension("asyncJob", this);
154
+ }
155
+ /**
156
+ * Get the hmac for the job
157
+ * @param jobTime - the job time header
158
+ * @returns the hex hmac
159
+ */
160
+ getHmac(jobTime) {
161
+ return crypto.createHmac("sha256", this.__secretKey).update(jobTime).digest("hex");
162
+ }
163
+ /**
164
+ * Only job runners with a valid HMAC can report status or use binaries
165
+ * @param context - the operation context
166
+ * @param action - the action
167
+ * @returns true or the reason of refusal
168
+ */
169
+ async canAct(context, action) {
170
+ if ("status" === action || ["get_binary", "attach_binary", "update_binary_metadata"].includes(action)) {
171
+ if (!hasJobHeaders(context)) {
172
+ return "Only Job runner can call this action";
173
+ }
174
+ try {
175
+ await this.verifyJobRequest(context);
176
+ return true;
177
+ }
178
+ catch (err) {
179
+ return err.message;
180
+ }
181
+ }
182
+ return "This model does not support any action: override canAct";
183
+ }
184
+ /**
185
+ * Throw a Forbidden error if the action is not allowed
186
+ * @param context - the operation context
187
+ * @param action - the action
188
+ */
189
+ async checkAct(context, action) {
190
+ const result = await this.canAct(context, action);
191
+ if (result !== true) {
192
+ throw new WebdaError.Forbidden(typeof result === "string" ? result : "Forbidden");
193
+ }
194
+ }
195
+ },
196
+ (() => {
197
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
198
+ _statusAction_decorators = [Action({ name: "status", openapi: { hidden: true } })];
199
+ __esDecorate(_a, null, _statusAction_decorators, { kind: "method", name: "statusAction", static: false, private: false, access: { has: obj => "statusAction" in obj, get: obj => obj.statusAction }, metadata: _metadata }, null, _instanceExtraInitializers);
200
+ if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
201
+ })(),
202
+ _a;
203
+ })();
204
+ /**
205
+ * Define here a model that can be used along with Store service
206
+ * @WebdaModel
207
+ * @WebdaPlural AsyncActions
208
+ */
209
+ export default AsyncAction;
210
+ /**
211
+ * Define a Webda Async Action
212
+ *
213
+ * @WebdaModel
214
+ * @WebdaPlural AsyncWebdaActions
215
+ */
216
+ export class AsyncWebdaAction extends AsyncAction {
217
+ /**
218
+ * @param serviceName service to call or initial data
219
+ * @param method method to call
220
+ * @param args to call with the method
221
+ */
222
+ constructor(serviceName, method, ...args) {
223
+ super(typeof serviceName === "object" ? serviceName : undefined);
224
+ if (typeof serviceName !== "object") {
225
+ this.serviceName = serviceName;
226
+ this.method = method;
227
+ this.arguments = args;
228
+ }
229
+ this.logLevel ?? (this.logLevel = "INFO");
230
+ }
231
+ /**
232
+ * Execute a serviceName.method(...args) so this is internal
233
+ * @returns true
234
+ */
235
+ isInternal() {
236
+ return true;
237
+ }
238
+ }
239
+ /**
240
+ * Operation called asynchronously
241
+ *
242
+ * @WebdaModel
243
+ * @WebdaPlural AsyncOperationActions
244
+ */
245
+ export class AsyncOperationAction extends AsyncAction {
246
+ /**
247
+ * @param operationId operation to call or initial data
248
+ * @param context context to use
249
+ * @param logLevel log level to capture
250
+ */
251
+ constructor(operationId, context, logLevel = "INFO") {
252
+ super(typeof operationId === "object" ? operationId : undefined);
253
+ if (typeof operationId !== "object") {
254
+ this.operationId = operationId;
255
+ this.context = context;
256
+ this.logLevel = logLevel;
257
+ }
258
+ this.logLevel ?? (this.logLevel = "INFO");
259
+ }
260
+ /**
261
+ * Execute a webda.callOperation(context ,id) so this is internal
262
+ * @returns true
263
+ */
264
+ isInternal() {
265
+ return true;
266
+ }
267
+ }
268
+ export { AsyncAction };
269
+ //# sourceMappingURL=asyncaction.model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asyncaction.model.js","sourceRoot":"","sources":["../src/asyncaction.model.ts"],"names":[],"mappings":"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"}
package/lib/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
- export * from "./models.js";
2
- export * from "./services/asyncjobservice.js";
3
- export * from "./services/runner.js";
4
- export * from "./services/localrunner.js";
5
- export * from "./services/servicerunner.js";
1
+ export * from "./asyncaction.model.js";
2
+ export * from "./services/asyncjobservice.service.js";
3
+ export * from "./services/runner.service.js";
4
+ export * from "./services/localrunner.service.js";
5
+ export * from "./services/servicerunner.service.js";
6
+ export * from "./services/asyncevents.service.js";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC;AACvC,cAAc,uCAAuC,CAAC;AACtD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,mCAAmC,CAAC;AAClD,cAAc,qCAAqC,CAAC;AACpD,cAAc,mCAAmC,CAAC"}
package/lib/index.js CHANGED
@@ -1,6 +1,7 @@
1
- export * from "./models.js";
2
- export * from "./services/asyncjobservice.js";
3
- export * from "./services/runner.js";
4
- export * from "./services/localrunner.js";
5
- export * from "./services/servicerunner.js";
1
+ export * from "./asyncaction.model.js";
2
+ export * from "./services/asyncjobservice.service.js";
3
+ export * from "./services/runner.service.js";
4
+ export * from "./services/localrunner.service.js";
5
+ export * from "./services/servicerunner.service.js";
6
+ export * from "./services/asyncevents.service.js";
6
7
  //# sourceMappingURL=index.js.map
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,4BAA4B,CAAC;AAC3C,cAAc,mBAAmB,CAAC;AAClC,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC;AACvC,cAAc,uCAAuC,CAAC;AACtD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,mCAAmC,CAAC;AAClD,cAAc,qCAAqC,CAAC;AACpD,cAAc,mCAAmC,CAAC"}