arvo-event-handler 1.0.1 → 1.0.2
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/CHANGELOG.md +4 -0
- package/README.md +46 -3
- package/dist/ArvoEventHandler/index.d.ts +1 -1
- package/dist/ArvoEventHandler/index.js +15 -9
- package/dist/ArvoEventRouter/helpers.d.ts +36 -0
- package/dist/ArvoEventRouter/helpers.js +41 -0
- package/dist/ArvoEventRouter/index.d.ts +85 -0
- package/dist/ArvoEventRouter/index.js +242 -0
- package/dist/ArvoEventRouter/types.d.ts +56 -0
- package/dist/ArvoEventRouter/types.js +2 -0
- package/dist/ArvoEventRouter/utils.d.ts +10 -0
- package/dist/ArvoEventRouter/utils.js +23 -0
- package/dist/MultiArvoEventHandler/index.d.ts +19 -7
- package/dist/MultiArvoEventHandler/index.js +33 -16
- package/dist/MultiArvoEventHandler/types.d.ts +9 -5
- package/dist/index.d.ts +4 -1
- package/dist/index.js +5 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
## [0.0.1] - 2024-09-07
|
|
4
4
|
|
|
5
5
|
- Finalised version 0.0.1 of the event handlers for Arvo
|
|
6
|
+
|
|
6
7
|
## [0.0.4] - 2024-09-09
|
|
7
8
|
|
|
8
9
|
- Finalised Event Handler implementation
|
|
@@ -14,4 +15,7 @@
|
|
|
14
15
|
## [1.0.0] - 2024-09-10
|
|
15
16
|
|
|
16
17
|
- First version rrelease
|
|
18
|
+
## [1.0.2] - 2024-09-10
|
|
19
|
+
|
|
20
|
+
- Added ArvoEventRouter as a mechanism to group ArvoEventHandlers
|
|
17
21
|
|
package/README.md
CHANGED
|
@@ -25,18 +25,61 @@ Whether you're building a small microservice or a large-scale distributed system
|
|
|
25
25
|
|
|
26
26
|
# Arvo - Event Handler
|
|
27
27
|
|
|
28
|
+
This package contains the event handler primitive required to enable an Arvo Event Driven System. These are light weight classes and types which take care of event listening, contract bound type inference, distributed open telemetry and much more.
|
|
29
|
+
|
|
30
|
+
## Documentation & Resources
|
|
31
|
+
|
|
32
|
+
| Source | Link |
|
|
33
|
+
| ------------ | ----------------------------------------------------------------- |
|
|
34
|
+
| Package | https://www.npmjs.com/package/arvo-event-handler?activeTab=readme |
|
|
35
|
+
| Github | https://github.com/SaadAhmad123/arvo-event-handler |
|
|
36
|
+
| Documenation | https://saadahmad123.github.io/arvo-event-handler/index.html |
|
|
37
|
+
|
|
28
38
|
## Installation
|
|
29
39
|
|
|
30
40
|
You can install the core package via `npm` or `yarn`
|
|
31
41
|
|
|
32
42
|
```bash
|
|
33
|
-
npm install arvo-event-handler
|
|
43
|
+
npm install arvo-event-handler arvo-core
|
|
34
44
|
```
|
|
35
45
|
|
|
36
46
|
```bash
|
|
37
|
-
yarn add arvo-event-handler
|
|
47
|
+
yarn add arvo-event-handler arvo-core
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Components
|
|
51
|
+
|
|
52
|
+
There 2 main types of event handlers in Arvo event driven system
|
|
53
|
+
|
|
54
|
+
- [ArvoEventHandler](src/ArvoEventHandler/README.md) is designed to facilitate the handling of events as per an `ArvoContract` (see [arvo-core](https://saadahmad123.github.io/arvo-core/documents/ArvoContract.html)). It provides a robust and flexible way to create, manage, and execute event handlers for Arvo-based event driven systems.
|
|
55
|
+
- [ArvoEventRouter](src/ArvoEventRouter/README.md) is designed to route ArvoEvents to appropriate ArvoEventHandlers. It provides a centralized mechanism for managing and executing multiple event handlers based on event types.
|
|
56
|
+
- [MultiArvoEventHandler](src/MultiArvoEventHandler/README.md) is a flexible and powerful event handling class designed to process multiple event types across different ArvoContracts. This handler offers greater versatility compared to the more specialized `ArvoEventHandler`, as it's not bound to a specific contract or event type.
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
## Getting Started
|
|
60
|
+
|
|
61
|
+
To start using Arvo Event Handlers in your project:
|
|
62
|
+
|
|
63
|
+
- Install the package as shown in the Installation section.
|
|
64
|
+
- Import the necessary components:
|
|
65
|
+
|
|
66
|
+
```javascript
|
|
67
|
+
import {
|
|
68
|
+
createArvoEvent,
|
|
69
|
+
createArvoContract,
|
|
70
|
+
createArvoContractLibrary,
|
|
71
|
+
createArvoEventFactory,
|
|
72
|
+
} from 'arvo-core';
|
|
73
|
+
|
|
74
|
+
import {
|
|
75
|
+
createArvoEventHandler,
|
|
76
|
+
createMultiArvoEventHandler,
|
|
77
|
+
createArvoEventRouter,
|
|
78
|
+
} from 'arvo-event-handler'
|
|
38
79
|
```
|
|
39
80
|
|
|
81
|
+
- Begin defining your events, contracts, and handlers using the provided classes.
|
|
82
|
+
|
|
40
83
|
## License
|
|
41
84
|
|
|
42
85
|
This package is available under the MIT License. For more details, refer to the [LICENSE.md](LICENSE.md) file in the project repository.
|
|
@@ -58,4 +101,4 @@ For a detailed list of changes and updates, please refer to the [document](CHANG
|
|
|
58
101
|
[](https://sonarcloud.io/summary/new_code?id=SaadAhmad123_arvo-event-handler)
|
|
59
102
|
[](https://sonarcloud.io/summary/new_code?id=SaadAhmad123_arvo-event-handler)
|
|
60
103
|
[](https://sonarcloud.io/summary/new_code?id=SaadAhmad123_arvo-event-handler)
|
|
61
|
-
[](https://sonarcloud.io/summary/new_code?id=SaadAhmad123_arvo-event-handler)
|
|
104
|
+
[](https://sonarcloud.io/summary/new_code?id=SaadAhmad123_arvo-event-handler)
|
|
@@ -22,7 +22,7 @@ export default class ArvoEventHandler<TContract extends ArvoContract> {
|
|
|
22
22
|
* @remarks
|
|
23
23
|
* For all the events which are emitted by the handler, this is
|
|
24
24
|
* the source field value of them all.
|
|
25
|
-
|
|
25
|
+
*/
|
|
26
26
|
readonly source: string;
|
|
27
27
|
readonly openInferenceSpanKind: OpenInferenceSpanKind;
|
|
28
28
|
readonly arvoExecutionSpanKind: ArvoExecutionSpanKind;
|
|
@@ -102,9 +102,12 @@ var ArvoEventHandler = /** @class */ (function () {
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
this.source = param.source || this.contract.accepts.type;
|
|
105
|
-
this.arvoExecutionSpanKind =
|
|
106
|
-
|
|
107
|
-
this.
|
|
105
|
+
this.arvoExecutionSpanKind =
|
|
106
|
+
((_a = param.spanKind) === null || _a === void 0 ? void 0 : _a.arvoExecution) || this.arvoExecutionSpanKind;
|
|
107
|
+
this.openInferenceSpanKind =
|
|
108
|
+
((_b = param.spanKind) === null || _b === void 0 ? void 0 : _b.openInference) || this.openInferenceSpanKind;
|
|
109
|
+
this.openTelemetrySpanKind =
|
|
110
|
+
((_c = param.spanKind) === null || _c === void 0 ? void 0 : _c.openTelemetry) || this.openTelemetrySpanKind;
|
|
108
111
|
}
|
|
109
112
|
/**
|
|
110
113
|
* Executes the event handler for a given event.
|
|
@@ -167,7 +170,7 @@ var ArvoEventHandler = /** @class */ (function () {
|
|
|
167
170
|
attributes: (_a = {},
|
|
168
171
|
_a[arvo_core_1.OpenInference.ATTR_SPAN_KIND] = this.openInferenceSpanKind,
|
|
169
172
|
_a[arvo_core_1.ArvoExecution.ATTR_SPAN_KIND] = this.arvoExecutionSpanKind,
|
|
170
|
-
_a)
|
|
173
|
+
_a),
|
|
171
174
|
};
|
|
172
175
|
if (event.traceparent) {
|
|
173
176
|
inheritedContext = (0, OpenTelemetry_1.extractContext)(event.traceparent, event.tracestate);
|
|
@@ -196,7 +199,10 @@ var ArvoEventHandler = /** @class */ (function () {
|
|
|
196
199
|
if (inputEventValidation.error) {
|
|
197
200
|
throw new Error("Invalid event payload: ".concat(inputEventValidation.error));
|
|
198
201
|
}
|
|
199
|
-
return [4 /*yield*/, this._handler({
|
|
202
|
+
return [4 /*yield*/, this._handler({
|
|
203
|
+
event: event,
|
|
204
|
+
source: this.source,
|
|
205
|
+
})];
|
|
200
206
|
case 2:
|
|
201
207
|
_handleOutput = _a.sent();
|
|
202
208
|
if (!_handleOutput)
|
|
@@ -212,9 +218,9 @@ var ArvoEventHandler = /** @class */ (function () {
|
|
|
212
218
|
var __extensions = output.__extensions, handlerResult = __rest(output, ["__extensions"]);
|
|
213
219
|
var result = eventFactory.emits(__assign(__assign({}, handlerResult), { traceparent: otelSpanHeaders.traceparent || undefined, tracestate: otelSpanHeaders.tracestate || undefined, source: _this.source, subject: event.subject,
|
|
214
220
|
// The user should be able to override the `to` field
|
|
215
|
-
// If that is not present then the 'redirectto' field
|
|
221
|
+
// If that is not present then the 'redirectto' field
|
|
216
222
|
// is referred to. Then, after all else, 'source' field
|
|
217
|
-
// is used as a form of reply.
|
|
223
|
+
// is used as a form of reply.
|
|
218
224
|
to: (0, utils_1.coalesceOrDefault)([handlerResult.to, event.redirectto], event.source), executionunits: (0, utils_1.coalesce)(handlerResult.executionunits, _this.executionunits) }), __extensions);
|
|
219
225
|
Object.entries(result.otelAttributes).forEach(function (_a) {
|
|
220
226
|
var key = _a[0], value = _a[1];
|
|
@@ -232,7 +238,7 @@ var ArvoEventHandler = /** @class */ (function () {
|
|
|
232
238
|
result = eventFactory.systemError({
|
|
233
239
|
source: this.source,
|
|
234
240
|
subject: event.subject,
|
|
235
|
-
// The system error must always got back to
|
|
241
|
+
// The system error must always got back to
|
|
236
242
|
// the source
|
|
237
243
|
to: event.source,
|
|
238
244
|
error: error_1,
|
|
@@ -276,7 +282,7 @@ var ArvoEventHandler = /** @class */ (function () {
|
|
|
276
282
|
get: function () {
|
|
277
283
|
return {
|
|
278
284
|
type: "sys.".concat(this.contract.accepts.type, ".error"),
|
|
279
|
-
schema: arvo_core_1.ArvoErrorSchema
|
|
285
|
+
schema: arvo_core_1.ArvoErrorSchema,
|
|
280
286
|
};
|
|
281
287
|
},
|
|
282
288
|
enumerable: false,
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ArvoEventRouter } from '.';
|
|
2
|
+
import { IArvoEventRouter } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Creates and returns a new instance of ArvoEventRouter.
|
|
5
|
+
*
|
|
6
|
+
* @param {IArvoEventRouter} param - Configuration object for the ArvoEventRouter.
|
|
7
|
+
* @returns {ArvoEventRouter} A new instance of ArvoEventRouter.
|
|
8
|
+
*
|
|
9
|
+
* @throws {Error} If there are duplicate handlers for the same event type.
|
|
10
|
+
* @throws {Error} If the provided source is an invalid string.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* const router = createArvoEventRouter({
|
|
14
|
+
* source: 'my-router',
|
|
15
|
+
* handlers: [handler1, handler2],
|
|
16
|
+
* executionunits: 10
|
|
17
|
+
* });
|
|
18
|
+
*
|
|
19
|
+
* @remarks
|
|
20
|
+
* This function is a factory method that simplifies the creation of an ArvoEventRouter instance.
|
|
21
|
+
* It encapsulates the instantiation process, allowing for a more concise and readable way to create routers.
|
|
22
|
+
*
|
|
23
|
+
* The `IArvoEventRouter` parameter should include:
|
|
24
|
+
* - `source`: (optional) A string identifying the source of the router. Used to match the `event.to` field.
|
|
25
|
+
* - `handlers`: An array of ArvoEventHandler instances that the router will use to process events.
|
|
26
|
+
* - `executionunits`: A number representing the default execution cost of the function.
|
|
27
|
+
*
|
|
28
|
+
* The created ArvoEventRouter will:
|
|
29
|
+
* - Validate the source string if provided.
|
|
30
|
+
* - Check for and prevent duplicate handlers for the same event type.
|
|
31
|
+
* - Set up internal data structures for efficient event routing.
|
|
32
|
+
*
|
|
33
|
+
* @see {@link ArvoEventRouter} for more details on the router's functionality.
|
|
34
|
+
* @see {@link IArvoEventRouter} for the structure of the configuration object.
|
|
35
|
+
*/
|
|
36
|
+
export declare const createArvoEventRouter: (param: IArvoEventRouter) => ArvoEventRouter;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createArvoEventRouter = void 0;
|
|
4
|
+
var _1 = require(".");
|
|
5
|
+
/**
|
|
6
|
+
* Creates and returns a new instance of ArvoEventRouter.
|
|
7
|
+
*
|
|
8
|
+
* @param {IArvoEventRouter} param - Configuration object for the ArvoEventRouter.
|
|
9
|
+
* @returns {ArvoEventRouter} A new instance of ArvoEventRouter.
|
|
10
|
+
*
|
|
11
|
+
* @throws {Error} If there are duplicate handlers for the same event type.
|
|
12
|
+
* @throws {Error} If the provided source is an invalid string.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* const router = createArvoEventRouter({
|
|
16
|
+
* source: 'my-router',
|
|
17
|
+
* handlers: [handler1, handler2],
|
|
18
|
+
* executionunits: 10
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* @remarks
|
|
22
|
+
* This function is a factory method that simplifies the creation of an ArvoEventRouter instance.
|
|
23
|
+
* It encapsulates the instantiation process, allowing for a more concise and readable way to create routers.
|
|
24
|
+
*
|
|
25
|
+
* The `IArvoEventRouter` parameter should include:
|
|
26
|
+
* - `source`: (optional) A string identifying the source of the router. Used to match the `event.to` field.
|
|
27
|
+
* - `handlers`: An array of ArvoEventHandler instances that the router will use to process events.
|
|
28
|
+
* - `executionunits`: A number representing the default execution cost of the function.
|
|
29
|
+
*
|
|
30
|
+
* The created ArvoEventRouter will:
|
|
31
|
+
* - Validate the source string if provided.
|
|
32
|
+
* - Check for and prevent duplicate handlers for the same event type.
|
|
33
|
+
* - Set up internal data structures for efficient event routing.
|
|
34
|
+
*
|
|
35
|
+
* @see {@link ArvoEventRouter} for more details on the router's functionality.
|
|
36
|
+
* @see {@link IArvoEventRouter} for the structure of the configuration object.
|
|
37
|
+
*/
|
|
38
|
+
var createArvoEventRouter = function (param) {
|
|
39
|
+
return new _1.ArvoEventRouter(param);
|
|
40
|
+
};
|
|
41
|
+
exports.createArvoEventRouter = createArvoEventRouter;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { ArvoContract, ArvoEvent, ArvoExecutionSpanKind, OpenInferenceSpanKind } from 'arvo-core';
|
|
2
|
+
import ArvoEventHandler from '../ArvoEventHandler';
|
|
3
|
+
import { IArvoEventRouter } from './types';
|
|
4
|
+
import { SpanKind } from '@opentelemetry/api';
|
|
5
|
+
/**
|
|
6
|
+
* ArvoEventRouter class handles routing of ArvoEvents to appropriate event handlers.
|
|
7
|
+
*/
|
|
8
|
+
export declare class ArvoEventRouter {
|
|
9
|
+
/**
|
|
10
|
+
* The source name of the router. The router attempts
|
|
11
|
+
* to match the `event.to` field with this value
|
|
12
|
+
* @property {string} [source]
|
|
13
|
+
*/
|
|
14
|
+
readonly source: string | null;
|
|
15
|
+
/**
|
|
16
|
+
* A list of all available event handlers to be used by the router.
|
|
17
|
+
* @property {ArvoEventHandler<ArvoContract>[]} handlers
|
|
18
|
+
*/
|
|
19
|
+
readonly handlers: ArvoEventHandler<ArvoContract>[];
|
|
20
|
+
/**
|
|
21
|
+
* The default execution cost of the function.
|
|
22
|
+
* This can represent a dollar value or some other number with a rate card.
|
|
23
|
+
*/
|
|
24
|
+
readonly executionunits: number;
|
|
25
|
+
/**
|
|
26
|
+
* A map of all the available event handlers
|
|
27
|
+
*/
|
|
28
|
+
readonly handlersMap: Record<string, ArvoEventHandler<ArvoContract>>;
|
|
29
|
+
readonly openInferenceSpanKind: OpenInferenceSpanKind;
|
|
30
|
+
readonly arvoExecutionSpanKind: ArvoExecutionSpanKind;
|
|
31
|
+
readonly openTelemetrySpanKind: SpanKind;
|
|
32
|
+
/**
|
|
33
|
+
* Creates an instance of ArvoEventRouter.
|
|
34
|
+
* @param {IArvoEventRouter} param - The parameters for initializing the router
|
|
35
|
+
* @throws {Error} If there are duplicate handlers for the same event type or the
|
|
36
|
+
* source in an invalid string
|
|
37
|
+
*/
|
|
38
|
+
constructor(param: IArvoEventRouter);
|
|
39
|
+
/**
|
|
40
|
+
* Executes the routing process for a given ArvoEvent.
|
|
41
|
+
*
|
|
42
|
+
* @param event - The ArvoEvent to be processed and routed.
|
|
43
|
+
* @returns A Promise that resolves to an array of ArvoEvents.
|
|
44
|
+
*
|
|
45
|
+
* @remarks
|
|
46
|
+
* This function performs the following steps:
|
|
47
|
+
* 1. Initializes OpenTelemetry span for tracing and monitoring.
|
|
48
|
+
* 2. Validates the event's destination ('to' field) against the router's source.
|
|
49
|
+
* 3. Finds the appropriate handler for the event based on its type.
|
|
50
|
+
* 4. Executes the handler and processes the results.
|
|
51
|
+
* 5. Handles any errors that occur during the process.
|
|
52
|
+
*
|
|
53
|
+
* @throws Error event if the event's 'to' field doesn't match the router's source.
|
|
54
|
+
* @throws Error event if no valid handler is found for the event type.
|
|
55
|
+
*
|
|
56
|
+
* **Telemetry**
|
|
57
|
+
*
|
|
58
|
+
* - Creates an OpenTelemetry span for the entire execution process.
|
|
59
|
+
* - Sets span attributes for OpenInference and ArvoExecution.
|
|
60
|
+
* - Propagates trace context from the input event if available.
|
|
61
|
+
* - Records any errors in the span and sets the span status accordingly.
|
|
62
|
+
* - Adds event attributes to the span for both input and output events.
|
|
63
|
+
*
|
|
64
|
+
* **Routing**
|
|
65
|
+
*
|
|
66
|
+
* The routing process involves:
|
|
67
|
+
* 1. Matching the event's 'to' field with the router's source (if specified).
|
|
68
|
+
* 2. Finding a handler that accepts the event's type.
|
|
69
|
+
* 3. Executing the matched handler with the event.
|
|
70
|
+
* 4. Processing the handler's results and creating new events.
|
|
71
|
+
*
|
|
72
|
+
* **Error Handling**
|
|
73
|
+
*
|
|
74
|
+
* If an error occurs during execution:
|
|
75
|
+
* 1. The error is recorded in the OpenTelemetry span.
|
|
76
|
+
* 2. A new error event is created and returned.
|
|
77
|
+
* 3. The error event is sent back to the original event's source.
|
|
78
|
+
*
|
|
79
|
+
* **Performance**
|
|
80
|
+
*
|
|
81
|
+
* - Execution units are tracked for both successful executions and errors.
|
|
82
|
+
* - The router's default execution units are used for error events.
|
|
83
|
+
*/
|
|
84
|
+
execute(event: ArvoEvent): Promise<ArvoEvent[]>;
|
|
85
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
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 __generator = (this && this.__generator) || function (thisArg, body) {
|
|
12
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
13
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
14
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
15
|
+
function step(op) {
|
|
16
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
17
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
18
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
19
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
20
|
+
switch (op[0]) {
|
|
21
|
+
case 0: case 1: t = op; break;
|
|
22
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
23
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
24
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
25
|
+
default:
|
|
26
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
27
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
28
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
29
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
30
|
+
if (t[2]) _.ops.pop();
|
|
31
|
+
_.trys.pop(); continue;
|
|
32
|
+
}
|
|
33
|
+
op = body.call(thisArg, _);
|
|
34
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
35
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.ArvoEventRouter = void 0;
|
|
40
|
+
var arvo_core_1 = require("arvo-core");
|
|
41
|
+
var utils_1 = require("../utils");
|
|
42
|
+
var api_1 = require("@opentelemetry/api");
|
|
43
|
+
var OpenTelemetry_1 = require("../OpenTelemetry");
|
|
44
|
+
var utils_2 = require("./utils");
|
|
45
|
+
var schema_1 = require("arvo-core/dist/ArvoEvent/schema");
|
|
46
|
+
/**
|
|
47
|
+
* ArvoEventRouter class handles routing of ArvoEvents to appropriate event handlers.
|
|
48
|
+
*/
|
|
49
|
+
var ArvoEventRouter = /** @class */ (function () {
|
|
50
|
+
/**
|
|
51
|
+
* Creates an instance of ArvoEventRouter.
|
|
52
|
+
* @param {IArvoEventRouter} param - The parameters for initializing the router
|
|
53
|
+
* @throws {Error} If there are duplicate handlers for the same event type or the
|
|
54
|
+
* source in an invalid string
|
|
55
|
+
*/
|
|
56
|
+
function ArvoEventRouter(param) {
|
|
57
|
+
/**
|
|
58
|
+
* A map of all the available event handlers
|
|
59
|
+
*/
|
|
60
|
+
this.handlersMap = {};
|
|
61
|
+
this.openInferenceSpanKind = arvo_core_1.OpenInferenceSpanKind.CHAIN;
|
|
62
|
+
this.arvoExecutionSpanKind = arvo_core_1.ArvoExecutionSpanKind.EVENT_HANDLER;
|
|
63
|
+
this.openTelemetrySpanKind = api_1.SpanKind.INTERNAL;
|
|
64
|
+
this.handlers = param.handlers;
|
|
65
|
+
if (param.source) {
|
|
66
|
+
var error = schema_1.CloudEventContextSchema.pick({
|
|
67
|
+
source: true,
|
|
68
|
+
}).safeParse({ source: param.source }).error;
|
|
69
|
+
if (error) {
|
|
70
|
+
throw new Error("The provided 'source' is not a valid string. Error: ".concat(error.message));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
this.source = (0, utils_1.isNullOrUndefined)(param.source) ? null : param.source;
|
|
74
|
+
this.executionunits = param.executionunits;
|
|
75
|
+
for (var _i = 0, _a = this.handlers; _i < _a.length; _i++) {
|
|
76
|
+
var handler = _a[_i];
|
|
77
|
+
if (this.handlersMap[handler.contract.accepts.type]) {
|
|
78
|
+
var existingHandler = this.handlersMap[handler.contract.accepts.type];
|
|
79
|
+
throw new Error((0, arvo_core_1.cleanString)("\n Duplicate handlers for event.type=".concat(handler.contract.accepts.type, " found. There are same 'contract.accept.types' in \n contracts 'uri=").concat(existingHandler.contract.uri, "' and 'uri=").concat(handler.contract.uri, "'. This router does not support handlers\n with the same 'contract.accept.type'.\n ")));
|
|
80
|
+
}
|
|
81
|
+
this.handlersMap[handler.contract.accepts.type] = handler;
|
|
82
|
+
}
|
|
83
|
+
Object.freeze(this.handlers);
|
|
84
|
+
Object.freeze(this.handlersMap);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Executes the routing process for a given ArvoEvent.
|
|
88
|
+
*
|
|
89
|
+
* @param event - The ArvoEvent to be processed and routed.
|
|
90
|
+
* @returns A Promise that resolves to an array of ArvoEvents.
|
|
91
|
+
*
|
|
92
|
+
* @remarks
|
|
93
|
+
* This function performs the following steps:
|
|
94
|
+
* 1. Initializes OpenTelemetry span for tracing and monitoring.
|
|
95
|
+
* 2. Validates the event's destination ('to' field) against the router's source.
|
|
96
|
+
* 3. Finds the appropriate handler for the event based on its type.
|
|
97
|
+
* 4. Executes the handler and processes the results.
|
|
98
|
+
* 5. Handles any errors that occur during the process.
|
|
99
|
+
*
|
|
100
|
+
* @throws Error event if the event's 'to' field doesn't match the router's source.
|
|
101
|
+
* @throws Error event if no valid handler is found for the event type.
|
|
102
|
+
*
|
|
103
|
+
* **Telemetry**
|
|
104
|
+
*
|
|
105
|
+
* - Creates an OpenTelemetry span for the entire execution process.
|
|
106
|
+
* - Sets span attributes for OpenInference and ArvoExecution.
|
|
107
|
+
* - Propagates trace context from the input event if available.
|
|
108
|
+
* - Records any errors in the span and sets the span status accordingly.
|
|
109
|
+
* - Adds event attributes to the span for both input and output events.
|
|
110
|
+
*
|
|
111
|
+
* **Routing**
|
|
112
|
+
*
|
|
113
|
+
* The routing process involves:
|
|
114
|
+
* 1. Matching the event's 'to' field with the router's source (if specified).
|
|
115
|
+
* 2. Finding a handler that accepts the event's type.
|
|
116
|
+
* 3. Executing the matched handler with the event.
|
|
117
|
+
* 4. Processing the handler's results and creating new events.
|
|
118
|
+
*
|
|
119
|
+
* **Error Handling**
|
|
120
|
+
*
|
|
121
|
+
* If an error occurs during execution:
|
|
122
|
+
* 1. The error is recorded in the OpenTelemetry span.
|
|
123
|
+
* 2. A new error event is created and returned.
|
|
124
|
+
* 3. The error event is sent back to the original event's source.
|
|
125
|
+
*
|
|
126
|
+
* **Performance**
|
|
127
|
+
*
|
|
128
|
+
* - Execution units are tracked for both successful executions and errors.
|
|
129
|
+
* - The router's default execution units are used for error events.
|
|
130
|
+
*/
|
|
131
|
+
ArvoEventRouter.prototype.execute = function (event) {
|
|
132
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
133
|
+
var spanName, spanOptions, span, inheritedContext;
|
|
134
|
+
var _a;
|
|
135
|
+
var _this = this;
|
|
136
|
+
var _b;
|
|
137
|
+
return __generator(this, function (_c) {
|
|
138
|
+
switch (_c.label) {
|
|
139
|
+
case 0:
|
|
140
|
+
spanName = "ArvoEventRouter.source<".concat((_b = this.source) !== null && _b !== void 0 ? _b : 'arvo.event.router', ">.execute<").concat(event.type, ">");
|
|
141
|
+
spanOptions = {
|
|
142
|
+
kind: this.openTelemetrySpanKind,
|
|
143
|
+
attributes: (_a = {},
|
|
144
|
+
_a[arvo_core_1.OpenInference.ATTR_SPAN_KIND] = this.openInferenceSpanKind,
|
|
145
|
+
_a[arvo_core_1.ArvoExecution.ATTR_SPAN_KIND] = this.arvoExecutionSpanKind,
|
|
146
|
+
_a),
|
|
147
|
+
};
|
|
148
|
+
if (event.traceparent) {
|
|
149
|
+
inheritedContext = (0, OpenTelemetry_1.extractContext)(event.traceparent, event.tracestate);
|
|
150
|
+
span = OpenTelemetry_1.ArvoEventHandlerTracer.startSpan(spanName, spanOptions, inheritedContext);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
span = OpenTelemetry_1.ArvoEventHandlerTracer.startSpan(spanName, spanOptions);
|
|
154
|
+
}
|
|
155
|
+
return [4 /*yield*/, api_1.context.with(api_1.trace.setSpan(api_1.context.active(), span), function () { return __awaiter(_this, void 0, void 0, function () {
|
|
156
|
+
var otelSpanHeaders, newEvent, results, error_1, result;
|
|
157
|
+
var _this = this;
|
|
158
|
+
var _a, _b;
|
|
159
|
+
return __generator(this, function (_c) {
|
|
160
|
+
switch (_c.label) {
|
|
161
|
+
case 0:
|
|
162
|
+
otelSpanHeaders = (0, arvo_core_1.currentOpenTelemetryHeaders)();
|
|
163
|
+
newEvent = (0, utils_2.deleteOtelHeaders)(event);
|
|
164
|
+
_c.label = 1;
|
|
165
|
+
case 1:
|
|
166
|
+
_c.trys.push([1, 3, 4, 5]);
|
|
167
|
+
span.setStatus({ code: api_1.SpanStatusCode.OK });
|
|
168
|
+
if (!(0, utils_1.isNullOrUndefined)(this.source) && newEvent.to !== this.source) {
|
|
169
|
+
throw new Error((0, arvo_core_1.cleanString)("\n Invalid event. The 'event.to' is ".concat(newEvent.to, " while this handler \n listens to only 'event.to' equal to ").concat(this.source, ". If this is a mistake,\n please update the 'source' field of the handler\n ")));
|
|
170
|
+
}
|
|
171
|
+
if (!this.handlersMap[newEvent.type]) {
|
|
172
|
+
throw new Error((0, arvo_core_1.cleanString)("\n Invalid event (type=".concat(newEvent.type, "). No valid handler \n <handler[*].contract.accepts.type> found in the router.\n ")));
|
|
173
|
+
}
|
|
174
|
+
return [4 /*yield*/, this.handlersMap[newEvent.type].execute(newEvent)];
|
|
175
|
+
case 2:
|
|
176
|
+
results = _c.sent();
|
|
177
|
+
return [2 /*return*/, results.map(function (event) {
|
|
178
|
+
var _a;
|
|
179
|
+
return new arvo_core_1.ArvoEvent({
|
|
180
|
+
id: event.id,
|
|
181
|
+
time: event.time,
|
|
182
|
+
source: (0, utils_1.getValueOrDefault)(_this.source, event.source),
|
|
183
|
+
specversion: '1.0',
|
|
184
|
+
type: event.type,
|
|
185
|
+
subject: event.subject,
|
|
186
|
+
datacontenttype: event.datacontenttype,
|
|
187
|
+
dataschema: event.dataschema,
|
|
188
|
+
to: event.to,
|
|
189
|
+
accesscontrol: event.accesscontrol,
|
|
190
|
+
redirectto: event.redirectto,
|
|
191
|
+
executionunits: ((_a = event.executionunits) !== null && _a !== void 0 ? _a : 0) + _this.executionunits,
|
|
192
|
+
traceparent: otelSpanHeaders.traceparent,
|
|
193
|
+
tracestate: otelSpanHeaders.tracestate,
|
|
194
|
+
}, event.data, event.cloudevent.extensions);
|
|
195
|
+
})];
|
|
196
|
+
case 3:
|
|
197
|
+
error_1 = _c.sent();
|
|
198
|
+
(0, arvo_core_1.exceptionToSpan)(error_1);
|
|
199
|
+
span.setStatus({
|
|
200
|
+
code: api_1.SpanStatusCode.ERROR,
|
|
201
|
+
message: error_1.message,
|
|
202
|
+
});
|
|
203
|
+
Object.entries(event.otelAttributes).forEach(function (_a) {
|
|
204
|
+
var key = _a[0], value = _a[1];
|
|
205
|
+
return span.setAttribute("to_process.0.".concat(key), value);
|
|
206
|
+
});
|
|
207
|
+
result = (0, arvo_core_1.createArvoEvent)({
|
|
208
|
+
type: "sys.arvo.event.router.error",
|
|
209
|
+
source: this.source || "arvo.event.router",
|
|
210
|
+
subject: event.subject,
|
|
211
|
+
// The system error must always got back to
|
|
212
|
+
// the source
|
|
213
|
+
to: event.source,
|
|
214
|
+
executionunits: this.executionunits,
|
|
215
|
+
traceparent: (_a = otelSpanHeaders.traceparent) !== null && _a !== void 0 ? _a : undefined,
|
|
216
|
+
tracestate: (_b = otelSpanHeaders.tracestate) !== null && _b !== void 0 ? _b : undefined,
|
|
217
|
+
data: {
|
|
218
|
+
errorName: error_1.name,
|
|
219
|
+
errorMessage: error_1.message,
|
|
220
|
+
errorStack: error_1.stack || null,
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
Object.entries(result.otelAttributes).forEach(function (_a) {
|
|
224
|
+
var key = _a[0], value = _a[1];
|
|
225
|
+
return span.setAttribute("to_emit.0.".concat(key), value);
|
|
226
|
+
});
|
|
227
|
+
return [2 /*return*/, [result]];
|
|
228
|
+
case 4:
|
|
229
|
+
span.end();
|
|
230
|
+
return [7 /*endfinally*/];
|
|
231
|
+
case 5: return [2 /*return*/];
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
}); })];
|
|
235
|
+
case 1: return [2 /*return*/, _c.sent()];
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
};
|
|
240
|
+
return ArvoEventRouter;
|
|
241
|
+
}());
|
|
242
|
+
exports.ArvoEventRouter = ArvoEventRouter;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { ArvoContract, ArvoExecutionSpanKind, OpenInferenceSpanKind } from 'arvo-core';
|
|
2
|
+
import ArvoEventHandler from '../ArvoEventHandler';
|
|
3
|
+
import { SpanKind } from '@opentelemetry/api';
|
|
4
|
+
/**
|
|
5
|
+
* Interface for defining an Arvo Event Router.
|
|
6
|
+
*
|
|
7
|
+
* @interface IArvoEventRouter
|
|
8
|
+
*/
|
|
9
|
+
export interface IArvoEventRouter {
|
|
10
|
+
/**
|
|
11
|
+
* Defines the source name of the router.
|
|
12
|
+
*
|
|
13
|
+
* @property {string} [source]
|
|
14
|
+
*
|
|
15
|
+
* @remarks
|
|
16
|
+
* If this field is defined:
|
|
17
|
+
* - The router will only listen to events with a `to` field matching this `source`.
|
|
18
|
+
* - If an event's `to` field doesn't match, a system error event will be emitted.
|
|
19
|
+
* - For all emitted events, the `source` field will be overridden by this value.
|
|
20
|
+
*
|
|
21
|
+
* If this field is not defined:
|
|
22
|
+
* - The router will listen to all events.
|
|
23
|
+
* - If no appropriate handler is found for an event, a system error event will be emitted.
|
|
24
|
+
* - The `source` field of emitted events will be set according to the configuration
|
|
25
|
+
* in the relevant `ArvoEventHandler`.
|
|
26
|
+
*/
|
|
27
|
+
source: string;
|
|
28
|
+
/**
|
|
29
|
+
* The default execution cost of the function.
|
|
30
|
+
* This can represent a dollar value or some other number with a rate card.
|
|
31
|
+
*/
|
|
32
|
+
executionunits: number;
|
|
33
|
+
/**
|
|
34
|
+
* A list of all available event handlers to be used by the router.
|
|
35
|
+
*
|
|
36
|
+
* @property {ArvoEventHandler<ArvoContract>[]} handlers
|
|
37
|
+
*
|
|
38
|
+
* @remarks
|
|
39
|
+
* This array contains instances of `ArvoEventHandler<ArvoContract>` which define
|
|
40
|
+
* how different types of events should be processed. The router will use these
|
|
41
|
+
* handlers to manage incoming events and generate appropriate responses or actions.
|
|
42
|
+
*/
|
|
43
|
+
handlers: ArvoEventHandler<ArvoContract<any, any, any, any>>[];
|
|
44
|
+
/**
|
|
45
|
+
* The OpenTelemetry span kind attributes for the handler
|
|
46
|
+
* executor.
|
|
47
|
+
* @param [openInference] - The OpenInference span kind. Default is "CHAIN"
|
|
48
|
+
* @param [arvoExecution] - The ArvoExecution span kind. Default is "EVENT_HANDLER"
|
|
49
|
+
* @param [openTelemetry] - The OpenTelemetry span kind. Default is "INTERNAL"
|
|
50
|
+
*/
|
|
51
|
+
spanKind?: {
|
|
52
|
+
openInference?: OpenInferenceSpanKind;
|
|
53
|
+
arvoExecution?: ArvoExecutionSpanKind;
|
|
54
|
+
openTelemetry?: SpanKind;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ArvoEvent } from 'arvo-core';
|
|
2
|
+
export declare const deleteOtelHeaders: (event: ArvoEvent) => ArvoEvent<Record<string, any>, Record<string, string | number | boolean | null> & {
|
|
3
|
+
to: string | null;
|
|
4
|
+
accesscontrol: string | null;
|
|
5
|
+
redirectto: string | null;
|
|
6
|
+
executionunits: number | null;
|
|
7
|
+
} & {
|
|
8
|
+
traceparent: string | null;
|
|
9
|
+
tracestate: string | null;
|
|
10
|
+
}, string>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deleteOtelHeaders = void 0;
|
|
4
|
+
var arvo_core_1 = require("arvo-core");
|
|
5
|
+
var deleteOtelHeaders = function (event) {
|
|
6
|
+
return new arvo_core_1.ArvoEvent({
|
|
7
|
+
id: event.id,
|
|
8
|
+
time: event.time,
|
|
9
|
+
source: event.source,
|
|
10
|
+
specversion: '1.0',
|
|
11
|
+
type: event.type,
|
|
12
|
+
subject: event.subject,
|
|
13
|
+
datacontenttype: event.datacontenttype,
|
|
14
|
+
dataschema: event.dataschema,
|
|
15
|
+
to: event.to,
|
|
16
|
+
accesscontrol: event.accesscontrol,
|
|
17
|
+
redirectto: event.redirectto,
|
|
18
|
+
executionunits: event.executionunits,
|
|
19
|
+
traceparent: null,
|
|
20
|
+
tracestate: null,
|
|
21
|
+
}, event.data, event.cloudevent.extensions);
|
|
22
|
+
};
|
|
23
|
+
exports.deleteOtelHeaders = deleteOtelHeaders;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SpanKind } from '@opentelemetry/api';
|
|
2
|
-
import { ArvoEvent, ArvoExecutionSpanKind, OpenInferenceSpanKind } from
|
|
3
|
-
import { IMultiArvoEventHandler } from
|
|
2
|
+
import { ArvoEvent, ArvoExecutionSpanKind, OpenInferenceSpanKind } from 'arvo-core';
|
|
3
|
+
import { IMultiArvoEventHandler } from './types';
|
|
4
4
|
/**
|
|
5
5
|
* Represents a Multi ArvoEvent handler that can process multiple event types.
|
|
6
6
|
*
|
|
@@ -17,9 +17,13 @@ export default class MultiArvoEventHandler {
|
|
|
17
17
|
* The source identifier for events produced by this handler
|
|
18
18
|
*
|
|
19
19
|
* @remarks
|
|
20
|
+
* The handler listens to the events with field `event.to` equal
|
|
21
|
+
* to the this `source` value. If the event does not confirm to
|
|
22
|
+
* this, a system error event is returned
|
|
23
|
+
*
|
|
20
24
|
* For all the events which are emitted by the handler, this is
|
|
21
25
|
* the source field value of them all.
|
|
22
|
-
|
|
26
|
+
*/
|
|
23
27
|
readonly source: string;
|
|
24
28
|
readonly openInferenceSpanKind: OpenInferenceSpanKind;
|
|
25
29
|
readonly arvoExecutionSpanKind: ArvoExecutionSpanKind;
|
|
@@ -41,9 +45,10 @@ export default class MultiArvoEventHandler {
|
|
|
41
45
|
* @remarks
|
|
42
46
|
* This method performs the following steps:
|
|
43
47
|
* 1. Creates an OpenTelemetry span for the execution.
|
|
44
|
-
* 2.
|
|
45
|
-
* 3.
|
|
46
|
-
* 4.
|
|
48
|
+
* 2. Validates that the event's 'to' field matches the handler's 'source'.
|
|
49
|
+
* 3. Executes the handler function.
|
|
50
|
+
* 4. Creates and returns the result event(s).
|
|
51
|
+
* 5. Handles any errors and creates an error event if necessary.
|
|
47
52
|
*
|
|
48
53
|
* All telemetry data is properly set and propagated throughout the execution.
|
|
49
54
|
*
|
|
@@ -57,7 +62,8 @@ export default class MultiArvoEventHandler {
|
|
|
57
62
|
* const resultEvents = await handler.execute(inputEvent);
|
|
58
63
|
* ```
|
|
59
64
|
*
|
|
60
|
-
* @throws
|
|
65
|
+
* @throws {Error} Throws an error if the event's 'to' field doesn't match the handler's 'source'.
|
|
66
|
+
* All other errors thrown during the execution are returned as a system error event.
|
|
61
67
|
*
|
|
62
68
|
* **Routing**
|
|
63
69
|
*
|
|
@@ -76,6 +82,12 @@ export default class MultiArvoEventHandler {
|
|
|
76
82
|
* - Sets span attributes for input and output events
|
|
77
83
|
* - Propagates trace context to output events
|
|
78
84
|
* - Handles error cases and sets appropriate span status
|
|
85
|
+
*
|
|
86
|
+
* **Event Validation**
|
|
87
|
+
*
|
|
88
|
+
* - Checks if the event's 'to' field matches the handler's 'source'.
|
|
89
|
+
* - If they don't match, an error is thrown with a descriptive message.
|
|
90
|
+
* - This ensures that the handler only processes events intended for it.
|
|
79
91
|
*/
|
|
80
92
|
execute(event: ArvoEvent): Promise<ArvoEvent[]>;
|
|
81
93
|
/**
|
|
@@ -87,15 +87,18 @@ var MultiArvoEventHandler = /** @class */ (function () {
|
|
|
87
87
|
this.executionunits = param.executionunits;
|
|
88
88
|
this._handler = param.handler;
|
|
89
89
|
var error = schema_1.CloudEventContextSchema.pick({
|
|
90
|
-
source: true
|
|
90
|
+
source: true,
|
|
91
91
|
}).safeParse({ source: param.source }).error;
|
|
92
92
|
if (error) {
|
|
93
93
|
throw new Error("The provided 'source' is not a valid string. Error: ".concat(error.message));
|
|
94
94
|
}
|
|
95
95
|
this.source = param.source;
|
|
96
|
-
this.arvoExecutionSpanKind =
|
|
97
|
-
|
|
98
|
-
this.
|
|
96
|
+
this.arvoExecutionSpanKind =
|
|
97
|
+
((_a = param.spanKind) === null || _a === void 0 ? void 0 : _a.arvoExecution) || this.arvoExecutionSpanKind;
|
|
98
|
+
this.openInferenceSpanKind =
|
|
99
|
+
((_b = param.spanKind) === null || _b === void 0 ? void 0 : _b.openInference) || this.openInferenceSpanKind;
|
|
100
|
+
this.openTelemetrySpanKind =
|
|
101
|
+
((_c = param.spanKind) === null || _c === void 0 ? void 0 : _c.openTelemetry) || this.openTelemetrySpanKind;
|
|
99
102
|
}
|
|
100
103
|
/**
|
|
101
104
|
* Executes the event handler for a given event.
|
|
@@ -106,9 +109,10 @@ var MultiArvoEventHandler = /** @class */ (function () {
|
|
|
106
109
|
* @remarks
|
|
107
110
|
* This method performs the following steps:
|
|
108
111
|
* 1. Creates an OpenTelemetry span for the execution.
|
|
109
|
-
* 2.
|
|
110
|
-
* 3.
|
|
111
|
-
* 4.
|
|
112
|
+
* 2. Validates that the event's 'to' field matches the handler's 'source'.
|
|
113
|
+
* 3. Executes the handler function.
|
|
114
|
+
* 4. Creates and returns the result event(s).
|
|
115
|
+
* 5. Handles any errors and creates an error event if necessary.
|
|
112
116
|
*
|
|
113
117
|
* All telemetry data is properly set and propagated throughout the execution.
|
|
114
118
|
*
|
|
@@ -122,7 +126,8 @@ var MultiArvoEventHandler = /** @class */ (function () {
|
|
|
122
126
|
* const resultEvents = await handler.execute(inputEvent);
|
|
123
127
|
* ```
|
|
124
128
|
*
|
|
125
|
-
* @throws
|
|
129
|
+
* @throws {Error} Throws an error if the event's 'to' field doesn't match the handler's 'source'.
|
|
130
|
+
* All other errors thrown during the execution are returned as a system error event.
|
|
126
131
|
*
|
|
127
132
|
* **Routing**
|
|
128
133
|
*
|
|
@@ -141,6 +146,12 @@ var MultiArvoEventHandler = /** @class */ (function () {
|
|
|
141
146
|
* - Sets span attributes for input and output events
|
|
142
147
|
* - Propagates trace context to output events
|
|
143
148
|
* - Handles error cases and sets appropriate span status
|
|
149
|
+
*
|
|
150
|
+
* **Event Validation**
|
|
151
|
+
*
|
|
152
|
+
* - Checks if the event's 'to' field matches the handler's 'source'.
|
|
153
|
+
* - If they don't match, an error is thrown with a descriptive message.
|
|
154
|
+
* - This ensures that the handler only processes events intended for it.
|
|
144
155
|
*/
|
|
145
156
|
MultiArvoEventHandler.prototype.execute = function (event) {
|
|
146
157
|
return __awaiter(this, void 0, void 0, function () {
|
|
@@ -156,7 +167,7 @@ var MultiArvoEventHandler = /** @class */ (function () {
|
|
|
156
167
|
attributes: (_a = {},
|
|
157
168
|
_a[arvo_core_1.OpenInference.ATTR_SPAN_KIND] = this.openInferenceSpanKind,
|
|
158
169
|
_a[arvo_core_1.ArvoExecution.ATTR_SPAN_KIND] = this.arvoExecutionSpanKind,
|
|
159
|
-
_a)
|
|
170
|
+
_a),
|
|
160
171
|
};
|
|
161
172
|
if (event.traceparent) {
|
|
162
173
|
inheritedContext = (0, OpenTelemetry_1.extractContext)(event.traceparent, event.tracestate);
|
|
@@ -180,7 +191,13 @@ var MultiArvoEventHandler = /** @class */ (function () {
|
|
|
180
191
|
var key = _a[0], value = _a[1];
|
|
181
192
|
return span.setAttribute("to_process.0.".concat(key), value);
|
|
182
193
|
});
|
|
183
|
-
|
|
194
|
+
if (event.to !== this.source) {
|
|
195
|
+
throw new Error((0, arvo_core_1.cleanString)("\n Invalid event. The 'event.to' is ".concat(event.to, " while this handler \n listens to only 'event.to' equal to ").concat(this.source, ". If this is a mistake,\n please update the 'source' field of the handler\n ")));
|
|
196
|
+
}
|
|
197
|
+
return [4 /*yield*/, this._handler({
|
|
198
|
+
event: event,
|
|
199
|
+
source: this.source,
|
|
200
|
+
})];
|
|
184
201
|
case 2:
|
|
185
202
|
_handlerOutput = _a.sent();
|
|
186
203
|
if (!_handlerOutput)
|
|
@@ -196,9 +213,9 @@ var MultiArvoEventHandler = /** @class */ (function () {
|
|
|
196
213
|
var __extensions = output.__extensions, handlerResult = __rest(output, ["__extensions"]);
|
|
197
214
|
var result = (0, arvo_core_1.createArvoEvent)(__assign(__assign({}, handlerResult), { traceparent: otelSpanHeaders.traceparent || undefined, tracestate: otelSpanHeaders.tracestate || undefined, source: _this.source, subject: event.subject,
|
|
198
215
|
// The user should be able to override the `to` field
|
|
199
|
-
// If that is not present then the 'redirectto' field
|
|
216
|
+
// If that is not present then the 'redirectto' field
|
|
200
217
|
// is referred to. Then, after all else, 'source' field
|
|
201
|
-
// is used as a form of reply.
|
|
218
|
+
// is used as a form of reply.
|
|
202
219
|
to: (0, utils_1.coalesceOrDefault)([handlerResult.to, event.redirectto], event.source), executionunits: (0, utils_1.coalesce)(handlerResult.executionunits, _this.executionunits) }), __extensions);
|
|
203
220
|
Object.entries(result.otelAttributes).forEach(function (_a) {
|
|
204
221
|
var key = _a[0], value = _a[1];
|
|
@@ -217,7 +234,7 @@ var MultiArvoEventHandler = /** @class */ (function () {
|
|
|
217
234
|
type: "sys.".concat(this.source, ".error"),
|
|
218
235
|
source: this.source,
|
|
219
236
|
subject: event.subject,
|
|
220
|
-
// The system error must always got back to
|
|
237
|
+
// The system error must always got back to
|
|
221
238
|
// the source
|
|
222
239
|
to: event.source,
|
|
223
240
|
executionunits: this.executionunits,
|
|
@@ -226,8 +243,8 @@ var MultiArvoEventHandler = /** @class */ (function () {
|
|
|
226
243
|
data: {
|
|
227
244
|
errorName: error_1.name,
|
|
228
245
|
errorMessage: error_1.message,
|
|
229
|
-
errorStack: error_1.stack || null
|
|
230
|
-
}
|
|
246
|
+
errorStack: error_1.stack || null,
|
|
247
|
+
},
|
|
231
248
|
});
|
|
232
249
|
Object.entries(result.otelAttributes).forEach(function (_a) {
|
|
233
250
|
var key = _a[0], value = _a[1];
|
|
@@ -265,7 +282,7 @@ var MultiArvoEventHandler = /** @class */ (function () {
|
|
|
265
282
|
get: function () {
|
|
266
283
|
return {
|
|
267
284
|
type: "sys.".concat(this.source, ".error"),
|
|
268
|
-
schema: arvo_core_1.ArvoErrorSchema
|
|
285
|
+
schema: arvo_core_1.ArvoErrorSchema,
|
|
269
286
|
};
|
|
270
287
|
},
|
|
271
288
|
enumerable: false,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { SpanKind } from
|
|
2
|
-
import { ArvoEvent, ArvoExecutionSpanKind, CreateArvoEvent, OpenInferenceSpanKind } from
|
|
1
|
+
import { SpanKind } from '@opentelemetry/api';
|
|
2
|
+
import { ArvoEvent, ArvoExecutionSpanKind, CreateArvoEvent, OpenInferenceSpanKind } from 'arvo-core';
|
|
3
3
|
/**
|
|
4
4
|
* Represents the input for a Multi ArvoEvent handler function.
|
|
5
5
|
*/
|
|
@@ -35,11 +35,15 @@ export type MultiArvoEventHandlerFunction = (param: MultiArvoEventHandlerFunctio
|
|
|
35
35
|
*/
|
|
36
36
|
export interface IMultiArvoEventHandler {
|
|
37
37
|
/**
|
|
38
|
-
* The source
|
|
38
|
+
* The source identifier for events produced by this handler
|
|
39
39
|
*
|
|
40
40
|
* @remarks
|
|
41
|
-
*
|
|
42
|
-
*
|
|
41
|
+
* The handler listens to the events with field `event.to` equal
|
|
42
|
+
* to the this `source` value. If the event does not confirm to
|
|
43
|
+
* this, a system error event is returned
|
|
44
|
+
*
|
|
45
|
+
* For all the events which are emitted by the handler, this is
|
|
46
|
+
* the source field value of them all.
|
|
43
47
|
*/
|
|
44
48
|
source: string;
|
|
45
49
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -6,4 +6,7 @@ import MultiArvoEventHandler from './MultiArvoEventHandler';
|
|
|
6
6
|
import { MultiArvoEventHandlerFunctionInput, MultiArvoEventHandlerFunctionOutput, MultiArvoEventHandlerFunction, IMultiArvoEventHandler } from './MultiArvoEventHandler/types';
|
|
7
7
|
import { createMultiArvoEventHandler } from './MultiArvoEventHandler/helpers';
|
|
8
8
|
import { isNullOrUndefined, getValueOrDefault, coalesce, coalesceOrDefault } from './utils';
|
|
9
|
-
|
|
9
|
+
import { IArvoEventRouter } from './ArvoEventRouter/types';
|
|
10
|
+
import { ArvoEventRouter } from './ArvoEventRouter';
|
|
11
|
+
import { createArvoEventRouter } from './ArvoEventRouter/helpers';
|
|
12
|
+
export { ArvoEventHandler, createArvoEventHandler, IArvoEventHandler, ArvoEventHandlerFunctionOutput, ArvoEventHandlerFunctionInput, ArvoEventHandlerFunction, PartialExcept, MultiArvoEventHandler, MultiArvoEventHandlerFunctionInput, MultiArvoEventHandlerFunctionOutput, MultiArvoEventHandlerFunction, IMultiArvoEventHandler, createMultiArvoEventHandler, isNullOrUndefined, getValueOrDefault, coalesce, coalesceOrDefault, IArvoEventRouter, ArvoEventRouter, createArvoEventRouter, };
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.coalesceOrDefault = exports.coalesce = exports.getValueOrDefault = exports.isNullOrUndefined = exports.createMultiArvoEventHandler = exports.MultiArvoEventHandler = exports.createArvoEventHandler = exports.ArvoEventHandler = void 0;
|
|
6
|
+
exports.createArvoEventRouter = exports.ArvoEventRouter = exports.coalesceOrDefault = exports.coalesce = exports.getValueOrDefault = exports.isNullOrUndefined = exports.createMultiArvoEventHandler = exports.MultiArvoEventHandler = exports.createArvoEventHandler = exports.ArvoEventHandler = void 0;
|
|
7
7
|
var ArvoEventHandler_1 = __importDefault(require("./ArvoEventHandler"));
|
|
8
8
|
exports.ArvoEventHandler = ArvoEventHandler_1.default;
|
|
9
9
|
var helpers_1 = require("./ArvoEventHandler/helpers");
|
|
@@ -17,3 +17,7 @@ Object.defineProperty(exports, "isNullOrUndefined", { enumerable: true, get: fun
|
|
|
17
17
|
Object.defineProperty(exports, "getValueOrDefault", { enumerable: true, get: function () { return utils_1.getValueOrDefault; } });
|
|
18
18
|
Object.defineProperty(exports, "coalesce", { enumerable: true, get: function () { return utils_1.coalesce; } });
|
|
19
19
|
Object.defineProperty(exports, "coalesceOrDefault", { enumerable: true, get: function () { return utils_1.coalesceOrDefault; } });
|
|
20
|
+
var ArvoEventRouter_1 = require("./ArvoEventRouter");
|
|
21
|
+
Object.defineProperty(exports, "ArvoEventRouter", { enumerable: true, get: function () { return ArvoEventRouter_1.ArvoEventRouter; } });
|
|
22
|
+
var helpers_3 = require("./ArvoEventRouter/helpers");
|
|
23
|
+
Object.defineProperty(exports, "createArvoEventRouter", { enumerable: true, get: function () { return helpers_3.createArvoEventRouter; } });
|