@cratis/chronicle 6.50.0 → 6.51.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ChronicleClient.ts +127 -99
- package/ChronicleOptions.ts +23 -0
- package/dist/ChronicleClient.d.ts +7 -0
- package/dist/ChronicleClient.d.ts.map +1 -1
- package/dist/ChronicleClient.js +115 -95
- package/dist/ChronicleClient.js.map +1 -1
- package/dist/ChronicleOptions.d.ts +14 -0
- package/dist/ChronicleOptions.d.ts.map +1 -1
- package/dist/ChronicleOptions.js +16 -0
- package/dist/ChronicleOptions.js.map +1 -1
- package/dist/eventSequences/EventSequence.d.ts +12 -0
- package/dist/eventSequences/EventSequence.d.ts.map +1 -1
- package/dist/eventSequences/EventSequence.js +202 -175
- package/dist/eventSequences/EventSequence.js.map +1 -1
- package/dist/eventSequences/ITransactionalEventSequence.d.ts +2 -2
- package/dist/eventSequences/PreparedBatchAppend.d.ts +4 -0
- package/dist/eventSequences/PreparedBatchAppend.d.ts.map +1 -0
- package/dist/eventSequences/PreparedBatchAppend.js +4 -0
- package/dist/eventSequences/PreparedBatchAppend.js.map +1 -0
- package/dist/eventSequences/PreparedSingleAppend.d.ts +4 -0
- package/dist/eventSequences/PreparedSingleAppend.d.ts.map +1 -0
- package/dist/eventSequences/PreparedSingleAppend.js +4 -0
- package/dist/eventSequences/PreparedSingleAppend.js.map +1 -0
- package/dist/eventSequences/WireBatchEvent.d.ts +6 -0
- package/dist/eventSequences/WireBatchEvent.d.ts.map +1 -0
- package/dist/eventSequences/WireBatchEvent.js +4 -0
- package/dist/eventSequences/WireBatchEvent.js.map +1 -0
- package/dist/eventSequences/toContractsCausation.d.ts +16 -0
- package/dist/eventSequences/toContractsCausation.d.ts.map +1 -0
- package/dist/eventSequences/toContractsCausation.js +15 -0
- package/dist/eventSequences/toContractsCausation.js.map +1 -0
- package/dist/events/constraints/ConstraintBuilder.d.ts +7 -0
- package/dist/events/constraints/ConstraintBuilder.d.ts.map +1 -1
- package/dist/events/constraints/ConstraintBuilder.js +13 -0
- package/dist/events/constraints/ConstraintBuilder.js.map +1 -1
- package/dist/events/constraints/ConstraintEventSequences.d.ts +19 -0
- package/dist/events/constraints/ConstraintEventSequences.d.ts.map +1 -0
- package/dist/events/constraints/ConstraintEventSequences.js +39 -0
- package/dist/events/constraints/ConstraintEventSequences.js.map +1 -0
- package/dist/events/constraints/Constraints.d.ts.map +1 -1
- package/dist/events/constraints/Constraints.js +24 -3
- package/dist/events/constraints/Constraints.js.map +1 -1
- package/dist/events/constraints/IConstraintBuilder.d.ts +18 -0
- package/dist/events/constraints/IConstraintBuilder.d.ts.map +1 -1
- package/dist/events/constraints/UniqueOptions.d.ts +15 -0
- package/dist/events/constraints/UniqueOptions.d.ts.map +1 -0
- package/dist/events/constraints/UniqueOptions.js +4 -0
- package/dist/events/constraints/UniqueOptions.js.map +1 -0
- package/dist/events/constraints/for_Constraints/when_scoping_to_event_sequences/given/registered_constraints.d.ts +9 -0
- package/dist/events/constraints/for_Constraints/when_scoping_to_event_sequences/given/registered_constraints.d.ts.map +1 -0
- package/dist/events/constraints/for_Constraints/when_scoping_to_event_sequences/given/registered_constraints.js +22 -0
- package/dist/events/constraints/for_Constraints/when_scoping_to_event_sequences/given/registered_constraints.js.map +1 -0
- package/dist/events/constraints/index.d.ts +2 -0
- package/dist/events/constraints/index.d.ts.map +1 -1
- package/dist/events/constraints/index.js.map +1 -1
- package/dist/events/constraints/unique.d.ts +10 -0
- package/dist/events/constraints/unique.d.ts.map +1 -1
- package/dist/events/constraints/unique.js +11 -10
- package/dist/events/constraints/unique.js.map +1 -1
- package/dist/reducers/ReducerObservation.d.ts +18 -0
- package/dist/reducers/ReducerObservation.d.ts.map +1 -0
- package/dist/reducers/ReducerObservation.js +4 -0
- package/dist/reducers/ReducerObservation.js.map +1 -0
- package/dist/reducers/ReducerObservationSession.d.ts +11 -0
- package/dist/reducers/ReducerObservationSession.d.ts.map +1 -0
- package/dist/reducers/ReducerObservationSession.js +4 -0
- package/dist/reducers/ReducerObservationSession.js.map +1 -0
- package/dist/reducers/Reducers.d.ts +9 -0
- package/dist/reducers/Reducers.d.ts.map +1 -1
- package/dist/reducers/Reducers.js +149 -154
- package/dist/reducers/Reducers.js.map +1 -1
- package/dist/reducers/ReductionOutcome.d.ts +25 -0
- package/dist/reducers/ReductionOutcome.d.ts.map +1 -0
- package/dist/reducers/ReductionOutcome.js +66 -0
- package/dist/reducers/ReductionOutcome.js.map +1 -0
- package/dist/testing/EventScenario.d.ts.map +1 -1
- package/dist/testing/EventScenario.js +8 -3
- package/dist/testing/EventScenario.js.map +1 -1
- package/dist/testing/InProcessConstraints.d.ts +4 -1
- package/dist/testing/InProcessConstraints.d.ts.map +1 -1
- package/dist/testing/InProcessConstraints.js +5 -1
- package/dist/testing/InProcessConstraints.js.map +1 -1
- package/dist/testing/InProcessEventSequence.d.ts.map +1 -1
- package/dist/testing/InProcessEventSequence.js +2 -1
- package/dist/testing/InProcessEventSequence.js.map +1 -1
- package/dist/transactions/ConflictingConcurrencyScopesInUnitOfWork.d.ts +14 -0
- package/dist/transactions/ConflictingConcurrencyScopesInUnitOfWork.d.ts.map +1 -0
- package/dist/transactions/ConflictingConcurrencyScopesInUnitOfWork.js +20 -0
- package/dist/transactions/ConflictingConcurrencyScopesInUnitOfWork.js.map +1 -0
- package/dist/transactions/IUnitOfWork.d.ts +2 -1
- package/dist/transactions/IUnitOfWork.d.ts.map +1 -1
- package/dist/transactions/TransactionalAppendOptions.d.ts +23 -2
- package/dist/transactions/TransactionalAppendOptions.d.ts.map +1 -1
- package/dist/transactions/UnitOfWork.d.ts +1 -0
- package/dist/transactions/UnitOfWork.d.ts.map +1 -1
- package/dist/transactions/UnitOfWork.js +28 -13
- package/dist/transactions/UnitOfWork.js.map +1 -1
- package/dist/transactions/concurrencyScopesAreEqual.d.ts +9 -0
- package/dist/transactions/concurrencyScopesAreEqual.d.ts.map +1 -0
- package/dist/transactions/concurrencyScopesAreEqual.js +19 -0
- package/dist/transactions/concurrencyScopesAreEqual.js.map +1 -0
- package/dist/transactions/for_UnitOfWork/given/a_unit_of_work_with_a_recording_store.d.ts +26 -0
- package/dist/transactions/for_UnitOfWork/given/a_unit_of_work_with_a_recording_store.d.ts.map +1 -0
- package/dist/transactions/for_UnitOfWork/given/a_unit_of_work_with_a_recording_store.js +31 -0
- package/dist/transactions/for_UnitOfWork/given/a_unit_of_work_with_a_recording_store.js.map +1 -0
- package/dist/transactions/index.d.ts +1 -0
- package/dist/transactions/index.d.ts.map +1 -1
- package/dist/transactions/index.js +1 -0
- package/dist/transactions/index.js.map +1 -1
- package/dist/transactions/toEventForEventSourceId.d.ts +13 -0
- package/dist/transactions/toEventForEventSourceId.d.ts.map +1 -0
- package/dist/transactions/toEventForEventSourceId.js +29 -0
- package/dist/transactions/toEventForEventSourceId.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/eventSequences/EventSequence.ts +265 -186
- package/eventSequences/ITransactionalEventSequence.ts +2 -2
- package/eventSequences/PreparedBatchAppend.ts +7 -0
- package/eventSequences/PreparedSingleAppend.ts +7 -0
- package/eventSequences/WireBatchEvent.ts +7 -0
- package/eventSequences/for_TransactionalEventSequence/when_appending_many_with_options.spec.ts +29 -0
- package/eventSequences/toContractsCausation.ts +17 -0
- package/events/constraints/ConstraintBuilder.ts +16 -0
- package/events/constraints/ConstraintEventSequences.ts +41 -0
- package/events/constraints/Constraints.ts +22 -3
- package/events/constraints/IConstraintBuilder.ts +20 -0
- package/events/constraints/UniqueOptions.ts +20 -0
- package/events/constraints/for_Constraints/against_a_kernel/when_appending_to_event_sequences_a_constraint_is_scoped_to.integration.spec.ts +118 -0
- package/events/constraints/for_Constraints/when_scoping_to_event_sequences/given/registered_constraints.ts +26 -0
- package/events/constraints/for_Constraints/when_scoping_to_event_sequences/with_fluent_builder.spec.ts +40 -0
- package/events/constraints/for_Constraints/when_scoping_to_event_sequences/with_legacy_decorators.legacy.spec.ts +47 -0
- package/events/constraints/for_Constraints/when_scoping_to_event_sequences/with_merged_declarations.spec.ts +36 -0
- package/events/constraints/for_Constraints/when_scoping_to_event_sequences/with_standard_decorators.standard.spec.ts +46 -0
- package/events/constraints/index.ts +2 -0
- package/events/constraints/unique.ts +22 -2
- package/for_ChronicleClient/messageSizes.spec.ts +37 -0
- package/for_ChronicleOptions/when_configuring_message_sizes/with_message_sizes.spec.ts +29 -0
- package/package.json +2 -2
- package/reducers/ReducerObservation.ts +25 -0
- package/reducers/ReducerObservationSession.ts +15 -0
- package/reducers/Reducers.ts +185 -171
- package/reducers/ReductionOutcome.ts +75 -0
- package/testing/EventScenario.fixtures.spec.ts +1 -1
- package/testing/EventScenario.ts +10 -4
- package/testing/InProcessConstraints.ts +5 -1
- package/testing/InProcessEventSequence.ts +2 -1
- package/testing/for_EventScenario/when_appending_event_sequence_constraints/event_sequence_scenario_behaviors.fixture.ts +101 -0
- package/testing/for_EventScenario/when_appending_event_sequence_constraints/other_sequence_extended_key.legacy.spec.ts +28 -0
- package/testing/for_EventScenario/when_appending_event_sequence_constraints/other_sequence_extended_key.standard.spec.ts +27 -0
- package/testing/for_EventScenario/when_appending_event_sequence_constraints/other_sequence_extended_key_behaviors.fixture.ts +40 -0
- package/testing/for_EventScenario/when_appending_event_sequence_constraints/with_legacy_decorators.legacy.spec.ts +55 -0
- package/testing/for_EventScenario/when_appending_event_sequence_constraints/with_standard_decorators.standard.spec.ts +54 -0
- package/transactions/ConflictingConcurrencyScopesInUnitOfWork.ts +19 -0
- package/transactions/IUnitOfWork.ts +2 -1
- package/transactions/TransactionalAppendOptions.ts +30 -2
- package/transactions/UnitOfWork.ts +29 -13
- package/transactions/concurrencyScopesAreEqual.ts +23 -0
- package/transactions/for_UnitOfWork/against_a_kernel/when_committing_with_append_metadata.integration.spec.ts +70 -0
- package/transactions/for_UnitOfWork/given/a_unit_of_work_with_a_recording_store.ts +54 -0
- package/transactions/for_UnitOfWork/when_adding_events_with_concurrency_scopes/and_the_event_type_filters_are_equivalent.spec.ts +32 -0
- package/transactions/for_UnitOfWork/when_adding_events_with_concurrency_scopes/and_the_event_type_filters_differ.spec.ts +40 -0
- package/transactions/for_UnitOfWork/when_adding_events_with_concurrency_scopes/and_the_scopes_are_equal_or_omitted.spec.ts +29 -0
- package/transactions/for_UnitOfWork/when_adding_events_with_concurrency_scopes/and_the_scopes_differ_for_one_event_source.spec.ts +29 -0
- package/transactions/for_UnitOfWork/when_committing_events_with_append_metadata/and_a_later_event_sequence_fails.spec.ts +30 -0
- package/transactions/for_UnitOfWork/when_committing_events_with_append_metadata/and_events_span_event_sequences.spec.ts +34 -0
- package/transactions/for_UnitOfWork/when_committing_events_with_append_metadata/and_options_differ_per_event.spec.ts +45 -0
- package/transactions/for_UnitOfWork/when_committing_events_with_append_metadata/and_the_append_is_rejected.spec.ts +26 -0
- package/transactions/for_UnitOfWork/when_rolling_back_events_with_append_metadata.spec.ts +24 -0
- package/transactions/index.ts +1 -0
- package/transactions/toEventForEventSourceId.ts +32 -0
- package/vitest.integration.config.ts +2 -0
- package/webhooks/for_Webhooks/against_a_kernel/given/ReceivedWebhookRequest.fixture.ts +21 -0
- package/webhooks/for_Webhooks/against_a_kernel/given/WebhookReceiver.fixture.ts +99 -0
- package/webhooks/for_Webhooks/against_a_kernel/when_delivering_events_to_a_failing_webhook.integration.spec.ts +101 -0
- package/webhooks/for_Webhooks/against_a_kernel/when_delivering_events_to_a_webhook.integration.spec.ts +130 -0
- package/webhooks/for_Webhooks/against_a_kernel/when_registering_a_webhook_that_is_not_replayable_and_not_active.integration.spec.ts +101 -0
- package/webhooks/for_Webhooks/when_registering_a_webhook_that_is_not_replayable_and_not_active.spec.ts +43 -0
|
@@ -11,6 +11,10 @@ import type {
|
|
|
11
11
|
import { Constructor } from '@cratis/fundamentals';
|
|
12
12
|
import { prepareSingleAppend } from './prepareSingleAppend.js';
|
|
13
13
|
import { prepareBatchAppend } from './prepareBatchAppend.js';
|
|
14
|
+
import type { PreparedBatchAppend } from './PreparedBatchAppend.js';
|
|
15
|
+
import type { PreparedSingleAppend } from './PreparedSingleAppend.js';
|
|
16
|
+
import type { WireBatchEvent } from './WireBatchEvent.js';
|
|
17
|
+
import { toContractsCausation } from './toContractsCausation.js';
|
|
14
18
|
import { createAppendNotification, mapAppendNotificationCausation } from './createAppendNotification.js';
|
|
15
19
|
import { getEventTypeFor } from '../events/eventTypeDecorator.js';
|
|
16
20
|
import type { AppendedEvent } from '../events/AppendedEvent.js';
|
|
@@ -46,7 +50,7 @@ import { ChronicleConventionMetrics } from '../Metrics.js';
|
|
|
46
50
|
import { identityProvider, Identity } from '../identity/index.js';
|
|
47
51
|
import { causationManager, CausationType } from '../auditing/index.js';
|
|
48
52
|
import { toContractsGuid } from '../connection/Guid.js';
|
|
49
|
-
import { ensureCommandResponse, ensureCommandSuccess, ensureQuerySuccess } from '../connection/callResults.js';
|
|
53
|
+
import { ensureCommandResponse, ensureCommandSuccess, ensureQuerySuccess, type CallResultLike } from '../connection/callResults.js';
|
|
50
54
|
import type { ConcurrencyScope } from './ConcurrencyScope.js';
|
|
51
55
|
import type { IEventSources } from '../eventSources/IEventSources.js';
|
|
52
56
|
import { deriveConcurrencyScope, planDerivedGuards, resolveBatchRouting, resolveSingleRouting } from './resolveEventSourceRouting.js';
|
|
@@ -77,13 +81,10 @@ export class EventSequence implements IEventSequence {
|
|
|
77
81
|
/** @inheritdoc */
|
|
78
82
|
async append(eventSourceId: string, event: object, options?: AppendOptions): Promise<AppendResult> {
|
|
79
83
|
const routing = resolveSingleRouting(this._eventSources, options);
|
|
80
|
-
const
|
|
84
|
+
const prepared = prepareSingleAppend(event, options);
|
|
85
|
+
const { eventType, correlationId, tags, namedTags, causationChain } = prepared;
|
|
81
86
|
|
|
82
|
-
const sequenceMetricAttributes =
|
|
83
|
-
[names.attributes.eventStore]: this._eventStoreName,
|
|
84
|
-
[names.attributes.namespace]: this._namespace,
|
|
85
|
-
[names.attributes.eventSequenceId]: this.id.value
|
|
86
|
-
};
|
|
87
|
+
const sequenceMetricAttributes = this.sequenceMetricAttributes;
|
|
87
88
|
const metricAttributes = { ...sequenceMetricAttributes, [names.attributes.eventTypeId]: eventType.id.value };
|
|
88
89
|
|
|
89
90
|
return observeOperation('append', async span => {
|
|
@@ -94,40 +95,7 @@ export class EventSequence implements IEventSequence {
|
|
|
94
95
|
if (options?.sourceType) span.setAttribute(names.attributes.eventSourceType, options.sourceType);
|
|
95
96
|
const startTime = performance.now();
|
|
96
97
|
try {
|
|
97
|
-
const
|
|
98
|
-
? await deriveConcurrencyScope(routing, eventSourceId, options?.streamId, this.tailReader)
|
|
99
|
-
: undefined);
|
|
100
|
-
const request = {
|
|
101
|
-
EventStore: this._eventStoreName,
|
|
102
|
-
Namespace: this._namespace,
|
|
103
|
-
EventSequenceId: this.id.value,
|
|
104
|
-
CorrelationId: toContractsGuid(correlationId),
|
|
105
|
-
EventSourceType: routing?.sourceType ?? options?.sourceType,
|
|
106
|
-
EventSourceId: eventSourceId,
|
|
107
|
-
EventStreamType: routing?.streamType ?? options?.streamType,
|
|
108
|
-
EventStreamId: options?.streamId,
|
|
109
|
-
EventType: {
|
|
110
|
-
Id: eventType.id.value,
|
|
111
|
-
Generation: eventType.generation.value,
|
|
112
|
-
Tombstone: eventType.tombstone
|
|
113
|
-
},
|
|
114
|
-
Content: content,
|
|
115
|
-
Causation: causationChain.map(c => ({
|
|
116
|
-
Occurred: { Value: c.occurred.toISOString() },
|
|
117
|
-
Type: c.type.name,
|
|
118
|
-
Properties: { ...c.properties }
|
|
119
|
-
})),
|
|
120
|
-
CausedBy: toContractsCausedBy(identity),
|
|
121
|
-
ConcurrencyScope: this.toContractConcurrencyScope(concurrencyScope),
|
|
122
|
-
EventSource: routing?.eventSource,
|
|
123
|
-
Tags: tags,
|
|
124
|
-
Occurred: options?.occurred === undefined ? undefined : { Value: options.occurred.toISOString() },
|
|
125
|
-
Subject: subject ?? eventSourceId
|
|
126
|
-
};
|
|
127
|
-
// Named tags travel on the dedicated call so a kernel without support rejects them instead of dropping them.
|
|
128
|
-
const response = namedTags.length === 0
|
|
129
|
-
? await this._connection.eventSequences.append(request)
|
|
130
|
-
: await this._connection.eventSequences.appendWithNamedTags({ ...request, NamedTags: toContractsNamedTags(namedTags) });
|
|
98
|
+
const response = await this.sendAppend(eventSourceId, prepared, routing, options);
|
|
131
99
|
|
|
132
100
|
const appendResponse = ensureCommandResponse('append event', response);
|
|
133
101
|
const durationInSeconds = (performance.now() - startTime) / 1000;
|
|
@@ -140,14 +108,7 @@ export class EventSequence implements IEventSequence {
|
|
|
140
108
|
setSequenceNumber(span, result.sequenceNumber.value);
|
|
141
109
|
span.setStatus({ code: SpanStatusCode.OK });
|
|
142
110
|
|
|
143
|
-
|
|
144
|
-
ChronicleConventionMetrics.appendDuration.record(durationInSeconds, metricAttributes);
|
|
145
|
-
if (result.constraintViolations.length > 0) {
|
|
146
|
-
ChronicleConventionMetrics.constraintViolations.add(result.constraintViolations.length, sequenceMetricAttributes);
|
|
147
|
-
}
|
|
148
|
-
if (result.errors.length > 0) {
|
|
149
|
-
ChronicleConventionMetrics.appendErrors.add(result.errors.length, sequenceMetricAttributes);
|
|
150
|
-
}
|
|
111
|
+
this.recordAppendMetrics(result, durationInSeconds, metricAttributes, sequenceMetricAttributes);
|
|
151
112
|
|
|
152
113
|
if (this.appendOperations.hasSubscribers) {
|
|
153
114
|
this.appendOperations.publish([createAppendNotification(eventSourceId, event, result,
|
|
@@ -165,6 +126,83 @@ export class EventSequence implements IEventSequence {
|
|
|
165
126
|
}, correlationId);
|
|
166
127
|
}
|
|
167
128
|
|
|
129
|
+
private get sequenceMetricAttributes(): Record<string, string> {
|
|
130
|
+
return {
|
|
131
|
+
[names.attributes.eventStore]: this._eventStoreName,
|
|
132
|
+
[names.attributes.namespace]: this._namespace,
|
|
133
|
+
[names.attributes.eventSequenceId]: this.id.value
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
private async sendAppend(
|
|
138
|
+
eventSourceId: string,
|
|
139
|
+
prepared: PreparedSingleAppend,
|
|
140
|
+
routing: ReturnType<typeof resolveSingleRouting>,
|
|
141
|
+
options?: AppendOptions
|
|
142
|
+
) {
|
|
143
|
+
const concurrencyScope = options?.concurrencyScope ?? (routing
|
|
144
|
+
? await deriveConcurrencyScope(routing, eventSourceId, options?.streamId, this.tailReader)
|
|
145
|
+
: undefined);
|
|
146
|
+
const request = this.createAppendRequest(eventSourceId, prepared, concurrencyScope, this.toAppendRouteFields(routing, options));
|
|
147
|
+
// Named tags travel on the dedicated call so a kernel without support rejects them instead of dropping them.
|
|
148
|
+
return prepared.namedTags.length === 0
|
|
149
|
+
? await this._connection.eventSequences.append(request)
|
|
150
|
+
: await this._connection.eventSequences.appendWithNamedTags({ ...request, NamedTags: toContractsNamedTags(prepared.namedTags) });
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private toAppendRouteFields(routing: ReturnType<typeof resolveSingleRouting>, options?: AppendOptions) {
|
|
154
|
+
return {
|
|
155
|
+
EventSourceType: routing?.sourceType ?? options?.sourceType,
|
|
156
|
+
EventStreamType: routing?.streamType ?? options?.streamType,
|
|
157
|
+
EventStreamId: options?.streamId,
|
|
158
|
+
EventSource: routing?.eventSource,
|
|
159
|
+
Occurred: toContractsOccurred(options?.occurred)
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
private createAppendRequest(
|
|
164
|
+
eventSourceId: string,
|
|
165
|
+
{ eventType, correlationId, content, tags, causationChain, identity, subject }: PreparedSingleAppend,
|
|
166
|
+
concurrencyScope: ConcurrencyScope | undefined,
|
|
167
|
+
routeFields: ReturnType<EventSequence['toAppendRouteFields']>
|
|
168
|
+
) {
|
|
169
|
+
return {
|
|
170
|
+
EventStore: this._eventStoreName,
|
|
171
|
+
Namespace: this._namespace,
|
|
172
|
+
EventSequenceId: this.id.value,
|
|
173
|
+
CorrelationId: toContractsGuid(correlationId),
|
|
174
|
+
EventSourceId: eventSourceId,
|
|
175
|
+
EventType: {
|
|
176
|
+
Id: eventType.id.value,
|
|
177
|
+
Generation: eventType.generation.value,
|
|
178
|
+
Tombstone: eventType.tombstone
|
|
179
|
+
},
|
|
180
|
+
Content: content,
|
|
181
|
+
Causation: toContractsCausation(causationChain),
|
|
182
|
+
CausedBy: toContractsCausedBy(identity),
|
|
183
|
+
ConcurrencyScope: this.toContractConcurrencyScope(concurrencyScope),
|
|
184
|
+
Tags: tags,
|
|
185
|
+
Subject: subject ?? eventSourceId,
|
|
186
|
+
...routeFields
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private recordAppendMetrics(
|
|
191
|
+
result: AppendResult,
|
|
192
|
+
durationInSeconds: number,
|
|
193
|
+
metricAttributes: Record<string, string>,
|
|
194
|
+
sequenceMetricAttributes: Record<string, string>
|
|
195
|
+
): void {
|
|
196
|
+
ChronicleConventionMetrics.eventsAppended.add(1, metricAttributes);
|
|
197
|
+
ChronicleConventionMetrics.appendDuration.record(durationInSeconds, metricAttributes);
|
|
198
|
+
if (result.constraintViolations.length > 0) {
|
|
199
|
+
ChronicleConventionMetrics.constraintViolations.add(result.constraintViolations.length, sequenceMetricAttributes);
|
|
200
|
+
}
|
|
201
|
+
if (result.errors.length > 0) {
|
|
202
|
+
ChronicleConventionMetrics.appendErrors.add(result.errors.length, sequenceMetricAttributes);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
168
206
|
/** @inheritdoc */
|
|
169
207
|
async appendMany(eventSourceId: string, events: object[], options?: AppendOptions): Promise<AppendResult[]>;
|
|
170
208
|
async appendMany(events: EventForEventSourceId[], options?: AppendOptions): Promise<AppendResult[]>;
|
|
@@ -173,21 +211,17 @@ export class EventSequence implements IEventSequence {
|
|
|
173
211
|
eventsOrOptions?: object[] | AppendOptions,
|
|
174
212
|
options?: AppendOptions
|
|
175
213
|
): Promise<AppendResult[]> {
|
|
176
|
-
const
|
|
177
|
-
|
|
214
|
+
const prepared = prepareBatchAppend(eventSourceIdOrEvents, eventsOrOptions, options);
|
|
215
|
+
const { eventsForEventSourceIds, appendOptions, correlationId, concurrencyScopes, eventsToAppend, namedTags } = prepared;
|
|
178
216
|
|
|
179
|
-
const wireEvents:
|
|
217
|
+
const wireEvents: WireBatchEvent[] = eventsToAppend;
|
|
180
218
|
const routings = eventsForEventSourceIds.map(event => resolveBatchRouting(this._eventSources, event, appendOptions));
|
|
181
219
|
ensureNamedTagBatchIsSupported(namedTags.some(tagsForEvent => tagsForEvent.length > 0), routings.some(routing => routing !== undefined));
|
|
182
220
|
const explicitIds = new Set([...concurrencyScopes].filter(([, scope]) => scope !== undefined).map(([id]) => id));
|
|
183
221
|
const derivedGuards = planDerivedGuards(routings, eventsForEventSourceIds, appendOptions?.streamId, explicitIds);
|
|
184
222
|
const distinctEventSourceIds = [...new Set(eventsForEventSourceIds.map(_ => _.eventSourceId))];
|
|
185
223
|
|
|
186
|
-
const batchMetricAttributes =
|
|
187
|
-
[names.attributes.eventStore]: this._eventStoreName,
|
|
188
|
-
[names.attributes.namespace]: this._namespace,
|
|
189
|
-
[names.attributes.eventSequenceId]: this.id.value
|
|
190
|
-
};
|
|
224
|
+
const batchMetricAttributes = this.sequenceMetricAttributes;
|
|
191
225
|
|
|
192
226
|
return observeOperation('appendMany', async span => {
|
|
193
227
|
this.setSequenceAttributes(span);
|
|
@@ -197,91 +231,19 @@ export class EventSequence implements IEventSequence {
|
|
|
197
231
|
setTelemetryAttribute(span, 'eventCount', eventsForEventSourceIds.length);
|
|
198
232
|
const startTime = performance.now();
|
|
199
233
|
try {
|
|
200
|
-
|
|
201
|
-
if (!routing) return;
|
|
202
|
-
wireEvents[index] = {
|
|
203
|
-
...wireEvents[index],
|
|
204
|
-
EventSourceType: routing.sourceType,
|
|
205
|
-
EventStreamType: routing.streamType ?? wireEvents[index].EventStreamType,
|
|
206
|
-
EventSource: routing.eventSource
|
|
207
|
-
};
|
|
208
|
-
});
|
|
234
|
+
this.applyBatchRouting(wireEvents, routings);
|
|
209
235
|
for (const [id, { routing, streamId }] of derivedGuards) {
|
|
210
236
|
concurrencyScopes.set(id, await deriveConcurrencyScope(routing, id, streamId, this.tailReader));
|
|
211
237
|
}
|
|
212
|
-
const
|
|
213
|
-
EventStore: this._eventStoreName,
|
|
214
|
-
Namespace: this._namespace,
|
|
215
|
-
EventSequenceId: this.id.value,
|
|
216
|
-
CorrelationId: toContractsGuid(correlationId),
|
|
217
|
-
Events: wireEvents,
|
|
218
|
-
Causation: batchCausationChain.map(c => ({
|
|
219
|
-
Occurred: { Value: c.occurred.toISOString() },
|
|
220
|
-
Type: c.type.name,
|
|
221
|
-
Properties: { ...c.properties }
|
|
222
|
-
})),
|
|
223
|
-
CausedBy: toContractsCausedBy(identity),
|
|
224
|
-
ConcurrencyScopes: [...concurrencyScopes].map(([eventSourceId, scope]) => ({
|
|
225
|
-
EventSourceId: eventSourceId,
|
|
226
|
-
Scope: this.toContractConcurrencyScope(scope)
|
|
227
|
-
}))
|
|
228
|
-
};
|
|
229
|
-
const response = namedTags.every(tagsForEvent => tagsForEvent.length === 0)
|
|
230
|
-
? await this._connection.eventSequences.appendManyForEventSources(request)
|
|
231
|
-
: await this._connection.eventSequences.appendManyForEventSourcesWithNamedTags({
|
|
232
|
-
...request,
|
|
233
|
-
Events: wireEvents.map((wireEvent, index) => ({ ...wireEvent, NamedTags: toContractsNamedTags(namedTags[index]) }))
|
|
234
|
-
});
|
|
238
|
+
const response = await this.sendAppendMany(prepared, wireEvents);
|
|
235
239
|
|
|
236
240
|
const appendManyResponse = ensureCommandResponse('append many events', response);
|
|
237
241
|
const durationInSeconds = (performance.now() - startTime) / 1000;
|
|
238
|
-
|
|
239
|
-
// constraint violations and the first concurrency violation of the whole batch —
|
|
240
|
-
// the wire response doesn't correlate either back to a specific event index.
|
|
241
|
-
const firstConcurrencyViolation: ContractsAppendManyResponse['ConcurrencyViolations'][number] | undefined =
|
|
242
|
-
(appendManyResponse.ConcurrencyViolations ?? [])[0];
|
|
243
|
-
const sequenceNumbers: ContractsAppendManyResponse['SequenceNumbers'] = appendManyResponse.SequenceNumbers ?? [];
|
|
244
|
-
const constraintViolations: ContractsAppendManyResponse['ConstraintViolations'] = appendManyResponse.ConstraintViolations ?? [];
|
|
245
|
-
const errors: ContractsAppendManyResponse['Errors'] = appendManyResponse.Errors ?? [];
|
|
246
|
-
const batchWasRejected = sequenceNumbers.length === 0 &&
|
|
247
|
-
(constraintViolations.length > 0 || errors.length > 0 || firstConcurrencyViolation !== undefined);
|
|
248
|
-
if (sequenceNumbers.length === 0 && eventsForEventSourceIds.length > 0 && !batchWasRejected) {
|
|
249
|
-
throw new Error('Append many events returned no sequence numbers or rejection details.');
|
|
250
|
-
}
|
|
251
|
-
const result = batchWasRejected
|
|
252
|
-
? eventsForEventSourceIds.map(() => this.mapAppendResponse(0n, constraintViolations, errors, firstConcurrencyViolation))
|
|
253
|
-
: sequenceNumbers.map((sequenceNumber, index) =>
|
|
254
|
-
this.mapAppendResponse(
|
|
255
|
-
sequenceNumber,
|
|
256
|
-
constraintViolations,
|
|
257
|
-
errors.filter((_, errorIndex) => errorIndex === index),
|
|
258
|
-
firstConcurrencyViolation
|
|
259
|
-
)
|
|
260
|
-
);
|
|
242
|
+
const result = this.toAppendManyResults(appendManyResponse, eventsForEventSourceIds.length);
|
|
261
243
|
span.setStatus({ code: SpanStatusCode.OK });
|
|
262
244
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
ChronicleConventionMetrics.appendManyDuration.record(durationInSeconds, batchMetricAttributes);
|
|
266
|
-
|
|
267
|
-
const totalViolations = result.reduce((sum: number, appendResult: AppendResult) => sum + appendResult.constraintViolations.length, 0);
|
|
268
|
-
if (totalViolations > 0) {
|
|
269
|
-
ChronicleConventionMetrics.constraintViolations.add(totalViolations, batchMetricAttributes);
|
|
270
|
-
}
|
|
271
|
-
const totalErrors = result.reduce((sum: number, appendResult: AppendResult) => sum + appendResult.errors.length, 0);
|
|
272
|
-
if (totalErrors > 0) {
|
|
273
|
-
ChronicleConventionMetrics.appendErrors.add(totalErrors, batchMetricAttributes);
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
if (this.appendOperations.hasSubscribers && result.length > 0) {
|
|
277
|
-
const occurredAt = new Date();
|
|
278
|
-
const causationEntries = mapAppendNotificationCausation(batchCausationChain);
|
|
279
|
-
this.appendOperations.publish(result.map((appendResult: AppendResult, index: number) => {
|
|
280
|
-
const { eventSourceId, event } = eventsForEventSourceIds[index];
|
|
281
|
-
return createAppendNotification(eventSourceId, event, appendResult, correlationId.toString(),
|
|
282
|
-
causationEntries, eventsToAppend[index].Tags, occurredAt, namedTags[index]);
|
|
283
|
-
}));
|
|
284
|
-
}
|
|
245
|
+
this.recordAppendManyMetrics(result, eventsForEventSourceIds.length, durationInSeconds, batchMetricAttributes);
|
|
246
|
+
this.publishAppendMany(prepared, result);
|
|
285
247
|
|
|
286
248
|
return result;
|
|
287
249
|
} catch (error) {
|
|
@@ -294,6 +256,106 @@ export class EventSequence implements IEventSequence {
|
|
|
294
256
|
}, correlationId);
|
|
295
257
|
}
|
|
296
258
|
|
|
259
|
+
private applyBatchRouting(wireEvents: WireBatchEvent[], routings: ReturnType<typeof resolveBatchRouting>[]): void {
|
|
260
|
+
routings.forEach((routing, index) => {
|
|
261
|
+
if (!routing) return;
|
|
262
|
+
wireEvents[index] = {
|
|
263
|
+
...wireEvents[index],
|
|
264
|
+
EventSourceType: routing.sourceType,
|
|
265
|
+
EventStreamType: routing.streamType ?? wireEvents[index].EventStreamType,
|
|
266
|
+
EventSource: routing.eventSource
|
|
267
|
+
};
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
private async sendAppendMany(
|
|
272
|
+
{ correlationId, batchCausationChain, identity, concurrencyScopes, namedTags }: PreparedBatchAppend,
|
|
273
|
+
wireEvents: WireBatchEvent[]
|
|
274
|
+
) {
|
|
275
|
+
const request = {
|
|
276
|
+
EventStore: this._eventStoreName,
|
|
277
|
+
Namespace: this._namespace,
|
|
278
|
+
EventSequenceId: this.id.value,
|
|
279
|
+
CorrelationId: toContractsGuid(correlationId),
|
|
280
|
+
Events: wireEvents,
|
|
281
|
+
Causation: toContractsCausation(batchCausationChain),
|
|
282
|
+
CausedBy: toContractsCausedBy(identity),
|
|
283
|
+
ConcurrencyScopes: [...concurrencyScopes].map(([eventSourceId, scope]) => ({
|
|
284
|
+
EventSourceId: eventSourceId,
|
|
285
|
+
Scope: this.toContractConcurrencyScope(scope)
|
|
286
|
+
}))
|
|
287
|
+
};
|
|
288
|
+
return namedTags.every(tagsForEvent => tagsForEvent.length === 0)
|
|
289
|
+
? await this._connection.eventSequences.appendManyForEventSources(request)
|
|
290
|
+
: await this._connection.eventSequences.appendManyForEventSourcesWithNamedTags({
|
|
291
|
+
...request,
|
|
292
|
+
Events: wireEvents.map((wireEvent, index) => ({ ...wireEvent, NamedTags: toContractsNamedTags(namedTags[index]) }))
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
private toAppendManyResults(appendManyResponse: ContractsAppendManyResponse, eventCount: number): AppendResult[] {
|
|
297
|
+
// Mirrors the C# client: every per-event AppendResult in a batch carries all
|
|
298
|
+
// constraint violations and the first concurrency violation of the whole batch —
|
|
299
|
+
// the wire response doesn't correlate either back to a specific event index.
|
|
300
|
+
const firstConcurrencyViolation: ContractsAppendManyResponse['ConcurrencyViolations'][number] | undefined =
|
|
301
|
+
(appendManyResponse.ConcurrencyViolations ?? [])[0];
|
|
302
|
+
const sequenceNumbers: ContractsAppendManyResponse['SequenceNumbers'] = appendManyResponse.SequenceNumbers ?? [];
|
|
303
|
+
const constraintViolations: ContractsAppendManyResponse['ConstraintViolations'] = appendManyResponse.ConstraintViolations ?? [];
|
|
304
|
+
const errors: ContractsAppendManyResponse['Errors'] = appendManyResponse.Errors ?? [];
|
|
305
|
+
if (sequenceNumbers.length === 0) {
|
|
306
|
+
const hasRejectionDetails = constraintViolations.length > 0 || errors.length > 0 || firstConcurrencyViolation !== undefined;
|
|
307
|
+
if (hasRejectionDetails) {
|
|
308
|
+
return Array.from({ length: eventCount }, () => this.mapAppendResponse(0n, constraintViolations, errors, firstConcurrencyViolation));
|
|
309
|
+
}
|
|
310
|
+
if (eventCount > 0) {
|
|
311
|
+
throw new Error('Append many events returned no sequence numbers or rejection details.');
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return sequenceNumbers.map((sequenceNumber, index) =>
|
|
315
|
+
this.mapAppendResponse(
|
|
316
|
+
sequenceNumber,
|
|
317
|
+
constraintViolations,
|
|
318
|
+
errors.filter((_, errorIndex) => errorIndex === index),
|
|
319
|
+
firstConcurrencyViolation
|
|
320
|
+
)
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
private recordAppendManyMetrics(
|
|
325
|
+
result: AppendResult[],
|
|
326
|
+
eventCount: number,
|
|
327
|
+
durationInSeconds: number,
|
|
328
|
+
batchMetricAttributes: Record<string, string>
|
|
329
|
+
): void {
|
|
330
|
+
ChronicleConventionMetrics.batchAppendsPerformed.add(1, batchMetricAttributes);
|
|
331
|
+
ChronicleConventionMetrics.eventsAppended.add(eventCount, batchMetricAttributes);
|
|
332
|
+
ChronicleConventionMetrics.appendManyDuration.record(durationInSeconds, batchMetricAttributes);
|
|
333
|
+
|
|
334
|
+
const totalViolations = result.reduce((sum: number, appendResult: AppendResult) => sum + appendResult.constraintViolations.length, 0);
|
|
335
|
+
if (totalViolations > 0) {
|
|
336
|
+
ChronicleConventionMetrics.constraintViolations.add(totalViolations, batchMetricAttributes);
|
|
337
|
+
}
|
|
338
|
+
const totalErrors = result.reduce((sum: number, appendResult: AppendResult) => sum + appendResult.errors.length, 0);
|
|
339
|
+
if (totalErrors > 0) {
|
|
340
|
+
ChronicleConventionMetrics.appendErrors.add(totalErrors, batchMetricAttributes);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
private publishAppendMany(
|
|
345
|
+
{ eventsForEventSourceIds, correlationId, batchCausationChain, eventsToAppend, namedTags }: PreparedBatchAppend,
|
|
346
|
+
result: AppendResult[]
|
|
347
|
+
): void {
|
|
348
|
+
if (!this.appendOperations.hasSubscribers || result.length === 0) return;
|
|
349
|
+
|
|
350
|
+
const occurredAt = new Date();
|
|
351
|
+
const causationEntries = mapAppendNotificationCausation(batchCausationChain);
|
|
352
|
+
this.appendOperations.publish(result.map((appendResult: AppendResult, index: number) => {
|
|
353
|
+
const { eventSourceId, event } = eventsForEventSourceIds[index];
|
|
354
|
+
return createAppendNotification(eventSourceId, event, appendResult, correlationId.toString(),
|
|
355
|
+
causationEntries, eventsToAppend[index].Tags, occurredAt, namedTags[index]);
|
|
356
|
+
}));
|
|
357
|
+
}
|
|
358
|
+
|
|
297
359
|
/** @inheritdoc */
|
|
298
360
|
async getNextSequenceNumber(): Promise<EventSequenceNumber> {
|
|
299
361
|
const tail = await this.getTailSequenceNumber();
|
|
@@ -447,61 +509,55 @@ export class EventSequence implements IEventSequence {
|
|
|
447
509
|
/** @inheritdoc */
|
|
448
510
|
async redact(sequenceNumber: EventSequenceNumber, reason: string): Promise<void> {
|
|
449
511
|
causationManager.add(CausationType.redact, { sequenceNumber: sequenceNumber.value.toString() });
|
|
450
|
-
const causationChain = causationManager.getCurrentChain();
|
|
451
|
-
const identity = identityProvider.getCurrent();
|
|
452
512
|
|
|
453
|
-
return
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
Causation: causationChain.map(c => ({
|
|
464
|
-
Occurred: { Value: c.occurred.toISOString() },
|
|
465
|
-
Type: c.type.name,
|
|
466
|
-
Properties: { ...c.properties }
|
|
467
|
-
})),
|
|
468
|
-
CausedBy: toContractsCausedBy(identity)
|
|
469
|
-
}));
|
|
470
|
-
span.setStatus({ code: SpanStatusCode.OK });
|
|
471
|
-
} catch (error) {
|
|
472
|
-
recordSafeException(span, error);
|
|
473
|
-
throw error;
|
|
474
|
-
} finally {
|
|
475
|
-
span.end();
|
|
476
|
-
}
|
|
477
|
-
});
|
|
513
|
+
return this.sendRedaction('redact', 'redact event',
|
|
514
|
+
span => setSequenceNumber(span, sequenceNumber.value),
|
|
515
|
+
causedBy => this._connection.eventSequences.redact({
|
|
516
|
+
EventStore: this._eventStoreName,
|
|
517
|
+
Namespace: this._namespace,
|
|
518
|
+
EventSequenceId: this.id.value,
|
|
519
|
+
SequenceNumber: sequenceNumber.value,
|
|
520
|
+
Reason: reason,
|
|
521
|
+
...causedBy
|
|
522
|
+
}));
|
|
478
523
|
}
|
|
479
524
|
|
|
480
525
|
/** @inheritdoc */
|
|
481
526
|
async redactForEventSource(eventSourceId: string, reason: string, eventTypes?: Constructor[]): Promise<void> {
|
|
482
527
|
causationManager.add(CausationType.redactForEventSource, { eventSourceId });
|
|
528
|
+
const wireEventTypeIds = (eventTypes ?? []).map(constructor => getEventTypeFor(constructor as unknown as Function).id.value);
|
|
529
|
+
|
|
530
|
+
return this.sendRedaction('redactForEventSource', 'redact event source',
|
|
531
|
+
span => setEventSourceId(span, eventSourceId, this._telemetry),
|
|
532
|
+
causedBy => this._connection.eventSequences.redactForEventSource({
|
|
533
|
+
EventStore: this._eventStoreName,
|
|
534
|
+
Namespace: this._namespace,
|
|
535
|
+
EventSequenceId: this.id.value,
|
|
536
|
+
EventSourceId: eventSourceId,
|
|
537
|
+
Reason: reason,
|
|
538
|
+
EventTypes: wireEventTypeIds,
|
|
539
|
+
...causedBy
|
|
540
|
+
}));
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
private async sendRedaction(
|
|
544
|
+
operationName: 'redact' | 'redactForEventSource',
|
|
545
|
+
description: string,
|
|
546
|
+
describeSpan: (span: Span) => void,
|
|
547
|
+
send: (causedBy: { Causation: ReturnType<typeof toContractsCausation>; CausedBy: object }) => Promise<CallResultLike>
|
|
548
|
+
): Promise<void> {
|
|
483
549
|
const causationChain = causationManager.getCurrentChain();
|
|
484
550
|
const identity = identityProvider.getCurrent();
|
|
485
|
-
const wireEventTypeIds = (eventTypes ?? []).map(constructor => getEventTypeFor(constructor as unknown as Function).id.value);
|
|
486
551
|
|
|
487
|
-
return observeOperation(
|
|
552
|
+
return observeOperation(operationName, async span => {
|
|
488
553
|
this.setSequenceAttributes(span);
|
|
489
|
-
|
|
554
|
+
describeSpan(span);
|
|
490
555
|
try {
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
Namespace: this._namespace,
|
|
494
|
-
EventSequenceId: this.id.value,
|
|
495
|
-
EventSourceId: eventSourceId,
|
|
496
|
-
Reason: reason,
|
|
497
|
-
EventTypes: wireEventTypeIds,
|
|
498
|
-
Causation: causationChain.map(c => ({
|
|
499
|
-
Occurred: { Value: c.occurred.toISOString() },
|
|
500
|
-
Type: c.type.name,
|
|
501
|
-
Properties: { ...c.properties }
|
|
502
|
-
})),
|
|
556
|
+
const causedBy = {
|
|
557
|
+
Causation: toContractsCausation(causationChain),
|
|
503
558
|
CausedBy: toContractsCausedBy(identity)
|
|
504
|
-
}
|
|
559
|
+
};
|
|
560
|
+
ensureCommandSuccess(description, await send(causedBy));
|
|
505
561
|
span.setStatus({ code: SpanStatusCode.OK });
|
|
506
562
|
} catch (error) {
|
|
507
563
|
recordSafeException(span, error);
|
|
@@ -607,8 +663,7 @@ export class EventSequence implements IEventSequence {
|
|
|
607
663
|
return { isSuccess: true, failedPartitions: [] };
|
|
608
664
|
}
|
|
609
665
|
|
|
610
|
-
const timeoutMs
|
|
611
|
-
const callerSignal = typeof options === 'number' ? undefined : options.signal;
|
|
666
|
+
const { timeoutMs, callerSignal } = this.resolveWaitOptions(options);
|
|
612
667
|
if (callerSignal?.aborted) throw callerSignal.reason;
|
|
613
668
|
|
|
614
669
|
const controller = new AbortController();
|
|
@@ -635,28 +690,52 @@ export class EventSequence implements IEventSequence {
|
|
|
635
690
|
}
|
|
636
691
|
}
|
|
637
692
|
|
|
693
|
+
private resolveWaitOptions(options: number | WaitForCompletionOptions): { timeoutMs: number; callerSignal: AbortSignal | undefined } {
|
|
694
|
+
if (typeof options === 'number') return { timeoutMs: options, callerSignal: undefined };
|
|
695
|
+
return { timeoutMs: options.timeoutMs ?? DEFAULT_WAIT_FOR_COMPLETION_TIMEOUT_MS, callerSignal: options.signal };
|
|
696
|
+
}
|
|
697
|
+
|
|
638
698
|
private readonly tailReader = (source?: string, sourceType?: string, streamType?: string, streamId?: string) =>
|
|
639
699
|
this.getTailSequenceNumber(source, sourceType, streamType, streamId);
|
|
640
700
|
|
|
641
701
|
private toContractConcurrencyScope(scope?: ConcurrencyScope) {
|
|
702
|
+
const expectsNoMatchingEvent = scope?.sequenceNumber === EventSequenceNumber.beforeFirst.value;
|
|
642
703
|
return {
|
|
643
|
-
SequenceNumber:
|
|
704
|
+
SequenceNumber: expectsNoMatchingEvent
|
|
644
705
|
? EventSequenceNumber.unset.value
|
|
645
706
|
: scope?.sequenceNumber ?? EventSequenceNumber.unset.value,
|
|
646
|
-
ExpectsNoMatchingEvent:
|
|
707
|
+
ExpectsNoMatchingEvent: expectsNoMatchingEvent,
|
|
647
708
|
EventSourceId: scope?.eventSourceId ?? false,
|
|
648
709
|
EventStreamType: scope?.eventStreamType ?? '',
|
|
649
710
|
EventStreamId: scope?.eventStreamId ?? '',
|
|
650
711
|
EventSourceType: scope?.eventSourceType ?? '',
|
|
651
|
-
EventTypes: (scope
|
|
652
|
-
Id: eventType.id.value,
|
|
653
|
-
Generation: eventType.generation.value,
|
|
654
|
-
Tombstone: eventType.tombstone
|
|
655
|
-
}))
|
|
712
|
+
EventTypes: toContractsEventTypes(scope)
|
|
656
713
|
};
|
|
657
714
|
}
|
|
658
715
|
}
|
|
659
716
|
|
|
717
|
+
/**
|
|
718
|
+
* Converts an optional occurrence time into the contracts timestamp shape.
|
|
719
|
+
* @param occurred - The occurrence time, if any.
|
|
720
|
+
* @returns The contracts timestamp, or undefined when no time was given.
|
|
721
|
+
*/
|
|
722
|
+
function toContractsOccurred(occurred: Date | undefined): { Value: string } | undefined {
|
|
723
|
+
return occurred === undefined ? undefined : { Value: occurred.toISOString() };
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
/**
|
|
727
|
+
* Converts the event types of a concurrency scope into the contracts shape.
|
|
728
|
+
* @param scope - The concurrency scope, if any.
|
|
729
|
+
* @returns The contracts event types.
|
|
730
|
+
*/
|
|
731
|
+
function toContractsEventTypes(scope: ConcurrencyScope | undefined) {
|
|
732
|
+
return (scope?.eventTypes ?? []).map(eventType => ({
|
|
733
|
+
Id: eventType.id.value,
|
|
734
|
+
Generation: eventType.generation.value,
|
|
735
|
+
Tombstone: eventType.tombstone
|
|
736
|
+
}));
|
|
737
|
+
}
|
|
738
|
+
|
|
660
739
|
/**
|
|
661
740
|
* Converts structured named tags into the sequence contract shape.
|
|
662
741
|
* @param namedTags - The validated named tags.
|
|
@@ -15,7 +15,7 @@ export interface ITransactionalEventSequence {
|
|
|
15
15
|
* Adds a single event to the current unit of work.
|
|
16
16
|
* @param eventSourceId - The identifier of the event source.
|
|
17
17
|
* @param event - The event to append.
|
|
18
|
-
* @param options - Optional
|
|
18
|
+
* @param options - Optional append metadata (routing, subject, occurrence time, tags, named tags, concurrency scope) kept with the event until commit.
|
|
19
19
|
*/
|
|
20
20
|
append(eventSourceId: string, event: object, options?: TransactionalAppendOptions): Promise<void>;
|
|
21
21
|
|
|
@@ -23,7 +23,7 @@ export interface ITransactionalEventSequence {
|
|
|
23
23
|
* Adds multiple events to the current unit of work.
|
|
24
24
|
* @param eventSourceId - The identifier of the event source.
|
|
25
25
|
* @param events - The events to append.
|
|
26
|
-
* @param options - Optional
|
|
26
|
+
* @param options - Optional append metadata (routing, subject, occurrence time, tags, named tags, concurrency scope) applied to every event.
|
|
27
27
|
*/
|
|
28
28
|
appendMany(eventSourceId: string, events: object[], options?: TransactionalAppendOptions): Promise<void>;
|
|
29
29
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import type { prepareBatchAppend } from './prepareBatchAppend.js';
|
|
5
|
+
|
|
6
|
+
/** The serialized form of a batch append, ready to be sent to the kernel. */
|
|
7
|
+
export type PreparedBatchAppend = ReturnType<typeof prepareBatchAppend>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import type { prepareSingleAppend } from './prepareSingleAppend.js';
|
|
5
|
+
|
|
6
|
+
/** The serialized form of a single append, ready to be sent to the kernel. */
|
|
7
|
+
export type PreparedSingleAppend = ReturnType<typeof prepareSingleAppend>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import type { PreparedBatchAppend } from './PreparedBatchAppend.js';
|
|
5
|
+
|
|
6
|
+
/** A wire event of a batch append, optionally carrying the definition event source it is routed to. */
|
|
7
|
+
export type WireBatchEvent = PreparedBatchAppend['eventsToAppend'][number] & { EventSource?: string };
|
package/eventSequences/for_TransactionalEventSequence/when_appending_many_with_options.spec.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { beforeEach, chai, describe, it, vi, type Mock } from 'vitest';
|
|
5
|
+
import { EventSequenceId } from '../EventSequenceId.js';
|
|
6
|
+
import type { IEventSequence } from '../IEventSequence.js';
|
|
7
|
+
import type { IUnitOfWorkManager } from '../../transactions/IUnitOfWorkManager.js';
|
|
8
|
+
import type { TransactionalAppendOptions } from '../../transactions/TransactionalAppendOptions.js';
|
|
9
|
+
import { TransactionalEventSequence } from '../TransactionalEventSequence.js';
|
|
10
|
+
|
|
11
|
+
chai.should();
|
|
12
|
+
|
|
13
|
+
describe('when appending many with options to a transactional event sequence', () => {
|
|
14
|
+
const outbox = new EventSequenceId('outbox');
|
|
15
|
+
const options: TransactionalAppendOptions = { subject: 'subject-1', occurred: new Date(0), tags: ['t'], concurrencyScope: { sequenceNumber: 2n } };
|
|
16
|
+
let addEvent: Mock;
|
|
17
|
+
|
|
18
|
+
beforeEach(async () => {
|
|
19
|
+
addEvent = vi.fn();
|
|
20
|
+
const manager = { current: { addEvent } } as unknown as IUnitOfWorkManager;
|
|
21
|
+
const sequence = new TransactionalEventSequence({ id: outbox } as unknown as IEventSequence, manager);
|
|
22
|
+
await sequence.appendMany('a-1', [{ order: 0 }, { order: 1 }], options);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should enroll every event with the options in order', () => addEvent.mock.calls.should.deep.equal([
|
|
26
|
+
[outbox, 'a-1', { order: 0 }, options],
|
|
27
|
+
[outbox, 'a-1', { order: 1 }, options]
|
|
28
|
+
]));
|
|
29
|
+
});
|