arvo-event-handler 1.0.0 → 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 +3 -1
- package/dist/ArvoEventHandler/index.js +17 -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 +23 -8
- package/dist/MultiArvoEventHandler/index.js +37 -17
- package/dist/MultiArvoEventHandler/types.d.ts +9 -5
- package/dist/index.d.ts +4 -1
- package/dist/index.js +5 -1
- package/package.json +3 -2
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;
|
|
@@ -70,6 +70,7 @@ export default class ArvoEventHandler<TContract extends ArvoContract> {
|
|
|
70
70
|
* @throws All error throw during the execution are returned as a system error event
|
|
71
71
|
*
|
|
72
72
|
* **Routing**
|
|
73
|
+
*
|
|
73
74
|
* The routing of the resulting events is determined as follows:
|
|
74
75
|
* - The `to` field of the output event is set in this priority:
|
|
75
76
|
* 1. The `to` field provided by the handler result
|
|
@@ -78,6 +79,7 @@ export default class ArvoEventHandler<TContract extends ArvoContract> {
|
|
|
78
79
|
* - For system error events, the `to` field is always set to the `source` of the input event.
|
|
79
80
|
*
|
|
80
81
|
* **Telemetry**
|
|
82
|
+
*
|
|
81
83
|
* - Creates a new span for each execution as per the traceparent and tracestate field
|
|
82
84
|
* of the event. If those are not present, then a brand new span is created and distributed
|
|
83
85
|
* tracing is disabled
|
|
@@ -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.
|
|
@@ -136,6 +139,7 @@ var ArvoEventHandler = /** @class */ (function () {
|
|
|
136
139
|
* @throws All error throw during the execution are returned as a system error event
|
|
137
140
|
*
|
|
138
141
|
* **Routing**
|
|
142
|
+
*
|
|
139
143
|
* The routing of the resulting events is determined as follows:
|
|
140
144
|
* - The `to` field of the output event is set in this priority:
|
|
141
145
|
* 1. The `to` field provided by the handler result
|
|
@@ -144,6 +148,7 @@ var ArvoEventHandler = /** @class */ (function () {
|
|
|
144
148
|
* - For system error events, the `to` field is always set to the `source` of the input event.
|
|
145
149
|
*
|
|
146
150
|
* **Telemetry**
|
|
151
|
+
*
|
|
147
152
|
* - Creates a new span for each execution as per the traceparent and tracestate field
|
|
148
153
|
* of the event. If those are not present, then a brand new span is created and distributed
|
|
149
154
|
* tracing is disabled
|
|
@@ -165,7 +170,7 @@ var ArvoEventHandler = /** @class */ (function () {
|
|
|
165
170
|
attributes: (_a = {},
|
|
166
171
|
_a[arvo_core_1.OpenInference.ATTR_SPAN_KIND] = this.openInferenceSpanKind,
|
|
167
172
|
_a[arvo_core_1.ArvoExecution.ATTR_SPAN_KIND] = this.arvoExecutionSpanKind,
|
|
168
|
-
_a)
|
|
173
|
+
_a),
|
|
169
174
|
};
|
|
170
175
|
if (event.traceparent) {
|
|
171
176
|
inheritedContext = (0, OpenTelemetry_1.extractContext)(event.traceparent, event.tracestate);
|
|
@@ -194,7 +199,10 @@ var ArvoEventHandler = /** @class */ (function () {
|
|
|
194
199
|
if (inputEventValidation.error) {
|
|
195
200
|
throw new Error("Invalid event payload: ".concat(inputEventValidation.error));
|
|
196
201
|
}
|
|
197
|
-
return [4 /*yield*/, this._handler({
|
|
202
|
+
return [4 /*yield*/, this._handler({
|
|
203
|
+
event: event,
|
|
204
|
+
source: this.source,
|
|
205
|
+
})];
|
|
198
206
|
case 2:
|
|
199
207
|
_handleOutput = _a.sent();
|
|
200
208
|
if (!_handleOutput)
|
|
@@ -210,9 +218,9 @@ var ArvoEventHandler = /** @class */ (function () {
|
|
|
210
218
|
var __extensions = output.__extensions, handlerResult = __rest(output, ["__extensions"]);
|
|
211
219
|
var result = eventFactory.emits(__assign(__assign({}, handlerResult), { traceparent: otelSpanHeaders.traceparent || undefined, tracestate: otelSpanHeaders.tracestate || undefined, source: _this.source, subject: event.subject,
|
|
212
220
|
// The user should be able to override the `to` field
|
|
213
|
-
// If that is not present then the 'redirectto' field
|
|
221
|
+
// If that is not present then the 'redirectto' field
|
|
214
222
|
// is referred to. Then, after all else, 'source' field
|
|
215
|
-
// is used as a form of reply.
|
|
223
|
+
// is used as a form of reply.
|
|
216
224
|
to: (0, utils_1.coalesceOrDefault)([handlerResult.to, event.redirectto], event.source), executionunits: (0, utils_1.coalesce)(handlerResult.executionunits, _this.executionunits) }), __extensions);
|
|
217
225
|
Object.entries(result.otelAttributes).forEach(function (_a) {
|
|
218
226
|
var key = _a[0], value = _a[1];
|
|
@@ -230,7 +238,7 @@ var ArvoEventHandler = /** @class */ (function () {
|
|
|
230
238
|
result = eventFactory.systemError({
|
|
231
239
|
source: this.source,
|
|
232
240
|
subject: event.subject,
|
|
233
|
-
// The system error must always got back to
|
|
241
|
+
// The system error must always got back to
|
|
234
242
|
// the source
|
|
235
243
|
to: event.source,
|
|
236
244
|
error: error_1,
|
|
@@ -274,7 +282,7 @@ var ArvoEventHandler = /** @class */ (function () {
|
|
|
274
282
|
get: function () {
|
|
275
283
|
return {
|
|
276
284
|
type: "sys.".concat(this.contract.accepts.type, ".error"),
|
|
277
|
-
schema: arvo_core_1.ArvoErrorSchema
|
|
285
|
+
schema: arvo_core_1.ArvoErrorSchema,
|
|
278
286
|
};
|
|
279
287
|
},
|
|
280
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,22 +62,32 @@ 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**
|
|
69
|
+
*
|
|
63
70
|
* The routing of the resulting events is determined as follows:
|
|
64
71
|
* - The `to` field of the output event is set in this priority:
|
|
65
72
|
* 1. The `to` field provided by the handler result
|
|
66
|
-
* 2. The `
|
|
73
|
+
* 2. The `redirectto` field from the input event
|
|
74
|
+
* 3. The `source` field from the input event (as a form of reply)
|
|
67
75
|
* - For system error events, the `to` field is always set to the `source` of the input event.
|
|
68
76
|
*
|
|
69
77
|
* **Telemetry**
|
|
78
|
+
*
|
|
70
79
|
* - Creates a new span for each execution as per the traceparent and tracestate field
|
|
71
80
|
* of the event. If those are not present, then a brand new span is created and distributed
|
|
72
81
|
* tracing is disabled
|
|
73
82
|
* - Sets span attributes for input and output events
|
|
74
83
|
* - Propagates trace context to output events
|
|
75
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.
|
|
76
91
|
*/
|
|
77
92
|
execute(event: ArvoEvent): Promise<ArvoEvent[]>;
|
|
78
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,22 +126,32 @@ 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**
|
|
133
|
+
*
|
|
128
134
|
* The routing of the resulting events is determined as follows:
|
|
129
135
|
* - The `to` field of the output event is set in this priority:
|
|
130
136
|
* 1. The `to` field provided by the handler result
|
|
131
|
-
* 2. The `
|
|
137
|
+
* 2. The `redirectto` field from the input event
|
|
138
|
+
* 3. The `source` field from the input event (as a form of reply)
|
|
132
139
|
* - For system error events, the `to` field is always set to the `source` of the input event.
|
|
133
140
|
*
|
|
134
141
|
* **Telemetry**
|
|
142
|
+
*
|
|
135
143
|
* - Creates a new span for each execution as per the traceparent and tracestate field
|
|
136
144
|
* of the event. If those are not present, then a brand new span is created and distributed
|
|
137
145
|
* tracing is disabled
|
|
138
146
|
* - Sets span attributes for input and output events
|
|
139
147
|
* - Propagates trace context to output events
|
|
140
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.
|
|
141
155
|
*/
|
|
142
156
|
MultiArvoEventHandler.prototype.execute = function (event) {
|
|
143
157
|
return __awaiter(this, void 0, void 0, function () {
|
|
@@ -153,7 +167,7 @@ var MultiArvoEventHandler = /** @class */ (function () {
|
|
|
153
167
|
attributes: (_a = {},
|
|
154
168
|
_a[arvo_core_1.OpenInference.ATTR_SPAN_KIND] = this.openInferenceSpanKind,
|
|
155
169
|
_a[arvo_core_1.ArvoExecution.ATTR_SPAN_KIND] = this.arvoExecutionSpanKind,
|
|
156
|
-
_a)
|
|
170
|
+
_a),
|
|
157
171
|
};
|
|
158
172
|
if (event.traceparent) {
|
|
159
173
|
inheritedContext = (0, OpenTelemetry_1.extractContext)(event.traceparent, event.tracestate);
|
|
@@ -177,7 +191,13 @@ var MultiArvoEventHandler = /** @class */ (function () {
|
|
|
177
191
|
var key = _a[0], value = _a[1];
|
|
178
192
|
return span.setAttribute("to_process.0.".concat(key), value);
|
|
179
193
|
});
|
|
180
|
-
|
|
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
|
+
})];
|
|
181
201
|
case 2:
|
|
182
202
|
_handlerOutput = _a.sent();
|
|
183
203
|
if (!_handlerOutput)
|
|
@@ -193,9 +213,9 @@ var MultiArvoEventHandler = /** @class */ (function () {
|
|
|
193
213
|
var __extensions = output.__extensions, handlerResult = __rest(output, ["__extensions"]);
|
|
194
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,
|
|
195
215
|
// The user should be able to override the `to` field
|
|
196
|
-
// If that is not present then the 'redirectto' field
|
|
216
|
+
// If that is not present then the 'redirectto' field
|
|
197
217
|
// is referred to. Then, after all else, 'source' field
|
|
198
|
-
// is used as a form of reply.
|
|
218
|
+
// is used as a form of reply.
|
|
199
219
|
to: (0, utils_1.coalesceOrDefault)([handlerResult.to, event.redirectto], event.source), executionunits: (0, utils_1.coalesce)(handlerResult.executionunits, _this.executionunits) }), __extensions);
|
|
200
220
|
Object.entries(result.otelAttributes).forEach(function (_a) {
|
|
201
221
|
var key = _a[0], value = _a[1];
|
|
@@ -214,7 +234,7 @@ var MultiArvoEventHandler = /** @class */ (function () {
|
|
|
214
234
|
type: "sys.".concat(this.source, ".error"),
|
|
215
235
|
source: this.source,
|
|
216
236
|
subject: event.subject,
|
|
217
|
-
// The system error must always got back to
|
|
237
|
+
// The system error must always got back to
|
|
218
238
|
// the source
|
|
219
239
|
to: event.source,
|
|
220
240
|
executionunits: this.executionunits,
|
|
@@ -223,8 +243,8 @@ var MultiArvoEventHandler = /** @class */ (function () {
|
|
|
223
243
|
data: {
|
|
224
244
|
errorName: error_1.name,
|
|
225
245
|
errorMessage: error_1.message,
|
|
226
|
-
errorStack: error_1.stack || null
|
|
227
|
-
}
|
|
246
|
+
errorStack: error_1.stack || null,
|
|
247
|
+
},
|
|
228
248
|
});
|
|
229
249
|
Object.entries(result.otelAttributes).forEach(function (_a) {
|
|
230
250
|
var key = _a[0], value = _a[1];
|
|
@@ -262,7 +282,7 @@ var MultiArvoEventHandler = /** @class */ (function () {
|
|
|
262
282
|
get: function () {
|
|
263
283
|
return {
|
|
264
284
|
type: "sys.".concat(this.source, ".error"),
|
|
265
|
-
schema: arvo_core_1.ArvoErrorSchema
|
|
285
|
+
schema: arvo_core_1.ArvoErrorSchema,
|
|
266
286
|
};
|
|
267
287
|
},
|
|
268
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; } });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arvo-event-handler",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "This package contains class and function for event handlers in an Arvo Event Driven system",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -42,13 +42,14 @@
|
|
|
42
42
|
"ts-jest": "^29.2.5",
|
|
43
43
|
"ts-node": "^10.9.2",
|
|
44
44
|
"typedoc": "^0.26.6",
|
|
45
|
+
"typedoc-github-theme": "^0.1.2",
|
|
45
46
|
"typedoc-plugin-mermaid": "^1.12.0",
|
|
46
47
|
"typedoc-plugin-zod": "^1.2.1",
|
|
47
48
|
"typescript": "^5.5.4"
|
|
48
49
|
},
|
|
49
50
|
"dependencies": {
|
|
50
51
|
"@opentelemetry/api": "^1.9.0",
|
|
51
|
-
"arvo-core": "^1.0.
|
|
52
|
+
"arvo-core": "^1.0.30",
|
|
52
53
|
"uuid": "^10.0.0",
|
|
53
54
|
"zod": "^3.23.8"
|
|
54
55
|
}
|