@cratis/chronicle 3.0.0 → 3.1.0
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/EventStore.ts +17 -1
- package/IEventStore.ts +16 -0
- package/auditing/CausationType.ts +10 -0
- package/compliance/ComplianceContracts.ts +84 -0
- package/compliance/IPIIManager.ts +17 -0
- package/compliance/PIIManager.ts +31 -0
- package/compliance/index.ts +2 -0
- package/connection/ChronicleConnection.ts +7 -1
- package/connection/ChronicleServices.ts +2 -0
- package/connection/Guid.ts +18 -0
- package/dist/EventStore.d.ts +8 -0
- package/dist/EventStore.d.ts.map +1 -1
- package/dist/EventStore.js +13 -1
- package/dist/EventStore.js.map +1 -1
- package/dist/IEventStore.d.ts +12 -0
- package/dist/IEventStore.d.ts.map +1 -1
- package/dist/auditing/CausationType.d.ts +8 -0
- package/dist/auditing/CausationType.d.ts.map +1 -1
- package/dist/auditing/CausationType.js +8 -0
- package/dist/auditing/CausationType.js.map +1 -1
- package/dist/compliance/ComplianceContracts.d.ts +40 -0
- package/dist/compliance/ComplianceContracts.d.ts.map +1 -1
- package/dist/compliance/ComplianceContracts.js +71 -0
- package/dist/compliance/ComplianceContracts.js.map +1 -1
- package/dist/compliance/IPIIManager.d.ts +15 -0
- package/dist/compliance/IPIIManager.d.ts.map +1 -0
- package/dist/compliance/IPIIManager.js +4 -0
- package/dist/compliance/IPIIManager.js.map +1 -0
- package/dist/compliance/PIIManager.d.ts +20 -0
- package/dist/compliance/PIIManager.d.ts.map +1 -0
- package/dist/compliance/PIIManager.js +30 -0
- package/dist/compliance/PIIManager.js.map +1 -0
- package/dist/compliance/index.d.ts +2 -0
- package/dist/compliance/index.d.ts.map +1 -1
- package/dist/compliance/index.js +1 -0
- package/dist/compliance/index.js.map +1 -1
- package/dist/connection/ChronicleConnection.d.ts +1 -0
- package/dist/connection/ChronicleConnection.d.ts.map +1 -1
- package/dist/connection/ChronicleConnection.js +6 -1
- package/dist/connection/ChronicleConnection.js.map +1 -1
- package/dist/connection/ChronicleServices.d.ts +2 -0
- package/dist/connection/ChronicleServices.d.ts.map +1 -1
- package/dist/connection/Guid.d.ts +5 -0
- package/dist/connection/Guid.d.ts.map +1 -1
- package/dist/connection/Guid.js +15 -0
- package/dist/connection/Guid.js.map +1 -1
- package/dist/eventSequences/AppendOperationsBroadcaster.d.ts +16 -0
- package/dist/eventSequences/AppendOperationsBroadcaster.d.ts.map +1 -0
- package/dist/eventSequences/AppendOperationsBroadcaster.js +77 -0
- package/dist/eventSequences/AppendOperationsBroadcaster.js.map +1 -0
- package/dist/eventSequences/AppendOptions.d.ts +7 -0
- package/dist/eventSequences/AppendOptions.d.ts.map +1 -1
- package/dist/eventSequences/AppendResult.d.ts +14 -0
- package/dist/eventSequences/AppendResult.d.ts.map +1 -1
- package/dist/eventSequences/AppendedEventWithResult.d.ts +13 -0
- package/dist/eventSequences/AppendedEventWithResult.d.ts.map +1 -0
- package/dist/eventSequences/AppendedEventWithResult.js +4 -0
- package/dist/eventSequences/AppendedEventWithResult.js.map +1 -0
- package/dist/eventSequences/CompleteStreamError.d.ts +17 -0
- package/dist/eventSequences/CompleteStreamError.d.ts.map +1 -0
- package/dist/eventSequences/CompleteStreamError.js +20 -0
- package/dist/eventSequences/CompleteStreamError.js.map +1 -0
- package/dist/eventSequences/CompleteStreamResult.d.ts +14 -0
- package/dist/eventSequences/CompleteStreamResult.d.ts.map +1 -0
- package/dist/eventSequences/CompleteStreamResult.js +4 -0
- package/dist/eventSequences/CompleteStreamResult.js.map +1 -0
- package/dist/eventSequences/ConcurrencyViolation.d.ts +13 -0
- package/dist/eventSequences/ConcurrencyViolation.d.ts.map +1 -0
- package/dist/eventSequences/ConcurrencyViolation.js +4 -0
- package/dist/eventSequences/ConcurrencyViolation.js.map +1 -0
- package/dist/eventSequences/EventForEventSourceId.d.ts +8 -0
- package/dist/eventSequences/EventForEventSourceId.d.ts.map +1 -1
- package/dist/eventSequences/EventSequence.d.ts +35 -1
- package/dist/eventSequences/EventSequence.d.ts.map +1 -1
- package/dist/eventSequences/EventSequence.js +350 -19
- package/dist/eventSequences/EventSequence.js.map +1 -1
- package/dist/eventSequences/IEventSequence.d.ts +85 -1
- package/dist/eventSequences/IEventSequence.d.ts.map +1 -1
- package/dist/eventSequences/WaitForCompletionResult.d.ts +11 -0
- package/dist/eventSequences/WaitForCompletionResult.d.ts.map +1 -0
- package/dist/eventSequences/WaitForCompletionResult.js +4 -0
- package/dist/eventSequences/WaitForCompletionResult.js.map +1 -0
- package/dist/eventSequences/index.d.ts +5 -0
- package/dist/eventSequences/index.d.ts.map +1 -1
- package/dist/eventSequences/index.js +1 -0
- package/dist/eventSequences/index.js.map +1 -1
- package/dist/eventStoreSubscriptions/EventStoreSubscriptions.d.ts +3 -0
- package/dist/eventStoreSubscriptions/EventStoreSubscriptions.d.ts.map +1 -1
- package/dist/eventStoreSubscriptions/EventStoreSubscriptions.js +9 -0
- package/dist/eventStoreSubscriptions/EventStoreSubscriptions.js.map +1 -1
- package/dist/eventStoreSubscriptions/IEventStoreSubscriptions.d.ts +6 -0
- package/dist/eventStoreSubscriptions/IEventStoreSubscriptions.d.ts.map +1 -1
- package/dist/eventStoreSubscriptions/contracts.d.ts +18 -0
- package/dist/eventStoreSubscriptions/contracts.d.ts.map +1 -1
- package/dist/eventStoreSubscriptions/contracts.js +106 -0
- package/dist/eventStoreSubscriptions/contracts.js.map +1 -1
- package/dist/events/constraints/Constraints.js +1 -1
- package/dist/events/constraints/Constraints.js.map +1 -1
- package/dist/externalServices/ExternalServiceBuilder.d.ts +42 -0
- package/dist/externalServices/ExternalServiceBuilder.d.ts.map +1 -0
- package/dist/externalServices/ExternalServiceBuilder.js +110 -0
- package/dist/externalServices/ExternalServiceBuilder.js.map +1 -0
- package/dist/externalServices/ExternalServices.d.ts +19 -0
- package/dist/externalServices/ExternalServices.d.ts.map +1 -0
- package/dist/externalServices/ExternalServices.js +30 -0
- package/dist/externalServices/ExternalServices.js.map +1 -0
- package/dist/externalServices/ExternalServicesContracts.d.ts +137 -0
- package/dist/externalServices/ExternalServicesContracts.d.ts.map +1 -0
- package/dist/externalServices/ExternalServicesContracts.js +654 -0
- package/dist/externalServices/ExternalServicesContracts.js.map +1 -0
- package/dist/externalServices/IExternalServiceBuilder.d.ts +67 -0
- package/dist/externalServices/IExternalServiceBuilder.d.ts.map +1 -0
- package/dist/externalServices/IExternalServiceBuilder.js +4 -0
- package/dist/externalServices/IExternalServiceBuilder.js.map +1 -0
- package/dist/externalServices/IExternalServices.d.ts +13 -0
- package/dist/externalServices/IExternalServices.d.ts.map +1 -0
- package/dist/externalServices/IExternalServices.js +4 -0
- package/dist/externalServices/IExternalServices.js.map +1 -0
- package/dist/externalServices/index.d.ts +5 -0
- package/dist/externalServices/index.d.ts.map +1 -0
- package/dist/externalServices/index.js +5 -0
- package/dist/externalServices/index.js.map +1 -0
- package/dist/identities/IIdentityManager.d.ts +17 -0
- package/dist/identities/IIdentityManager.d.ts.map +1 -0
- package/dist/identities/IIdentityManager.js +4 -0
- package/dist/identities/IIdentityManager.js.map +1 -0
- package/dist/identities/IdentityManager.d.ts +20 -0
- package/dist/identities/IdentityManager.d.ts.map +1 -0
- package/dist/identities/IdentityManager.js +31 -0
- package/dist/identities/IdentityManager.js.map +1 -0
- package/dist/identities/index.d.ts +3 -0
- package/dist/identities/index.d.ts.map +1 -0
- package/dist/identities/index.js +4 -0
- package/dist/identities/index.js.map +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/observation/FailedPartition.d.ts +15 -0
- package/dist/observation/FailedPartition.d.ts.map +1 -0
- package/dist/observation/FailedPartition.js +4 -0
- package/dist/observation/FailedPartition.js.map +1 -0
- package/dist/observation/FailedPartitionAttempt.d.ts +15 -0
- package/dist/observation/FailedPartitionAttempt.d.ts.map +1 -0
- package/dist/observation/FailedPartitionAttempt.js +4 -0
- package/dist/observation/FailedPartitionAttempt.js.map +1 -0
- package/dist/observation/FailedPartitions.d.ts +24 -0
- package/dist/observation/FailedPartitions.d.ts.map +1 -0
- package/dist/observation/FailedPartitions.js +39 -0
- package/dist/observation/FailedPartitions.js.map +1 -0
- package/dist/observation/ICanBeNotifiedWhenPartitionReplayed.d.ts +19 -0
- package/dist/observation/ICanBeNotifiedWhenPartitionReplayed.d.ts.map +1 -0
- package/dist/observation/ICanBeNotifiedWhenPartitionReplayed.js +4 -0
- package/dist/observation/ICanBeNotifiedWhenPartitionReplayed.js.map +1 -0
- package/dist/observation/ICanBeNotifiedWhenReplay.d.ts +17 -0
- package/dist/observation/ICanBeNotifiedWhenReplay.d.ts.map +1 -0
- package/dist/observation/ICanBeNotifiedWhenReplay.js +4 -0
- package/dist/observation/ICanBeNotifiedWhenReplay.js.map +1 -0
- package/dist/observation/IFailedPartitions.d.ts +18 -0
- package/dist/observation/IFailedPartitions.d.ts.map +1 -0
- package/dist/observation/IFailedPartitions.js +4 -0
- package/dist/observation/IFailedPartitions.js.map +1 -0
- package/dist/observation/index.d.ts +6 -0
- package/dist/observation/index.d.ts.map +1 -1
- package/dist/observation/index.js +1 -0
- package/dist/observation/index.js.map +1 -1
- package/dist/observation/notifyReplayLifecycle.d.ts +13 -0
- package/dist/observation/notifyReplayLifecycle.d.ts.map +1 -0
- package/dist/observation/notifyReplayLifecycle.js +54 -0
- package/dist/observation/notifyReplayLifecycle.js.map +1 -0
- package/dist/observation/toClientFailedPartition.d.ts +9 -0
- package/dist/observation/toClientFailedPartition.d.ts.map +1 -0
- package/dist/observation/toClientFailedPartition.js +23 -0
- package/dist/observation/toClientFailedPartition.js.map +1 -0
- package/dist/projections/Projections.d.ts +0 -4
- package/dist/projections/Projections.d.ts.map +1 -1
- package/dist/projections/Projections.js +25 -103
- package/dist/projections/Projections.js.map +1 -1
- package/dist/projections/declarative/AddBuilder.d.ts +17 -0
- package/dist/projections/declarative/AddBuilder.d.ts.map +1 -0
- package/dist/projections/declarative/AddBuilder.js +28 -0
- package/dist/projections/declarative/AddBuilder.js.map +1 -0
- package/dist/projections/declarative/AddChildBuilder.d.ts +56 -0
- package/dist/projections/declarative/AddChildBuilder.d.ts.map +1 -0
- package/dist/projections/declarative/AddChildBuilder.js +72 -0
- package/dist/projections/declarative/AddChildBuilder.js.map +1 -0
- package/dist/projections/declarative/ChildrenBuilder.d.ts +28 -0
- package/dist/projections/declarative/ChildrenBuilder.d.ts.map +1 -0
- package/dist/projections/declarative/ChildrenBuilder.js +72 -0
- package/dist/projections/declarative/ChildrenBuilder.js.map +1 -0
- package/dist/projections/declarative/CompositeKeyBuilder.d.ts +16 -0
- package/dist/projections/declarative/CompositeKeyBuilder.d.ts.map +1 -0
- package/dist/projections/declarative/CompositeKeyBuilder.js +29 -0
- package/dist/projections/declarative/CompositeKeyBuilder.js.map +1 -0
- package/dist/projections/declarative/FromBuilder.d.ts +8 -6
- package/dist/projections/declarative/FromBuilder.d.ts.map +1 -1
- package/dist/projections/declarative/FromBuilder.js +44 -14
- package/dist/projections/declarative/FromBuilder.js.map +1 -1
- package/dist/projections/declarative/ICompositeKeyBuilder.d.ts +5 -0
- package/dist/projections/declarative/ICompositeKeyBuilder.d.ts.map +1 -1
- package/dist/projections/declarative/JoinBuilder.d.ts +7 -5
- package/dist/projections/declarative/JoinBuilder.d.ts.map +1 -1
- package/dist/projections/declarative/JoinBuilder.js +50 -16
- package/dist/projections/declarative/JoinBuilder.js.map +1 -1
- package/dist/projections/declarative/NestedBuilder.d.ts +24 -0
- package/dist/projections/declarative/NestedBuilder.d.ts.map +1 -0
- package/dist/projections/declarative/NestedBuilder.js +60 -0
- package/dist/projections/declarative/NestedBuilder.js.map +1 -0
- package/dist/projections/declarative/ProjectionBuilderCore.d.ts +127 -0
- package/dist/projections/declarative/ProjectionBuilderCore.d.ts.map +1 -0
- package/dist/projections/declarative/ProjectionBuilderCore.js +180 -0
- package/dist/projections/declarative/ProjectionBuilderCore.js.map +1 -0
- package/dist/projections/declarative/ProjectionBuilderFor.d.ts +6 -32
- package/dist/projections/declarative/ProjectionBuilderFor.d.ts.map +1 -1
- package/dist/projections/declarative/ProjectionBuilderFor.js +24 -118
- package/dist/projections/declarative/ProjectionBuilderFor.js.map +1 -1
- package/dist/projections/declarative/SubtractBuilder.d.ts +17 -0
- package/dist/projections/declarative/SubtractBuilder.d.ts.map +1 -0
- package/dist/projections/declarative/SubtractBuilder.js +28 -0
- package/dist/projections/declarative/SubtractBuilder.js.map +1 -0
- package/dist/projections/declarative/index.d.ts +9 -0
- package/dist/projections/declarative/index.d.ts.map +1 -1
- package/dist/projections/declarative/index.js +8 -0
- package/dist/projections/declarative/index.js.map +1 -1
- package/dist/projections/modelBound/childrenAndNestedBuilder.d.ts +90 -0
- package/dist/projections/modelBound/childrenAndNestedBuilder.d.ts.map +1 -0
- package/dist/projections/modelBound/childrenAndNestedBuilder.js +290 -0
- package/dist/projections/modelBound/childrenAndNestedBuilder.js.map +1 -0
- package/dist/reactors/ReactorSideEffects.d.ts +29 -0
- package/dist/reactors/ReactorSideEffects.d.ts.map +1 -0
- package/dist/reactors/ReactorSideEffects.js +70 -0
- package/dist/reactors/ReactorSideEffects.js.map +1 -0
- package/dist/reactors/Reactors.d.ts +4 -1
- package/dist/reactors/Reactors.d.ts.map +1 -1
- package/dist/reactors/Reactors.js +23 -4
- package/dist/reactors/Reactors.js.map +1 -1
- package/dist/reducers/Reducers.d.ts.map +1 -1
- package/dist/reducers/Reducers.js +15 -3
- package/dist/reducers/Reducers.js.map +1 -1
- package/dist/reducers/reducer.d.ts +9 -1
- package/dist/reducers/reducer.d.ts.map +1 -1
- package/dist/reducers/reducer.js +4 -2
- package/dist/reducers/reducer.js.map +1 -1
- package/dist/transactions/IUnitOfWork.d.ts +18 -0
- package/dist/transactions/IUnitOfWork.d.ts.map +1 -1
- package/dist/transactions/UnitOfWork.d.ts +9 -0
- package/dist/transactions/UnitOfWork.d.ts.map +1 -1
- package/dist/transactions/UnitOfWork.js +14 -0
- package/dist/transactions/UnitOfWork.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/eventSequences/AppendOperationsBroadcaster.ts +83 -0
- package/eventSequences/AppendOptions.ts +8 -0
- package/eventSequences/AppendResult.ts +16 -0
- package/eventSequences/AppendedEventWithResult.ts +17 -0
- package/eventSequences/CompleteStreamError.ts +20 -0
- package/eventSequences/CompleteStreamResult.ts +13 -0
- package/eventSequences/ConcurrencyViolation.ts +18 -0
- package/eventSequences/EventForEventSourceId.ts +12 -0
- package/eventSequences/EventSequence.spec.ts +588 -0
- package/eventSequences/EventSequence.ts +406 -20
- package/eventSequences/IEventSequence.ts +109 -1
- package/eventSequences/WaitForCompletionResult.ts +15 -0
- package/eventSequences/index.ts +5 -0
- package/eventStoreSubscriptions/EventStoreSubscriptions.spec.ts +52 -0
- package/eventStoreSubscriptions/EventStoreSubscriptions.ts +19 -1
- package/eventStoreSubscriptions/IEventStoreSubscriptions.ts +7 -0
- package/eventStoreSubscriptions/contracts.ts +126 -0
- package/events/constraints/Constraints.ts +1 -1
- package/externalServices/ExternalServiceBuilder.ts +128 -0
- package/externalServices/ExternalServices.ts +34 -0
- package/externalServices/ExternalServicesContracts.ts +709 -0
- package/externalServices/IExternalServiceBuilder.ts +76 -0
- package/externalServices/IExternalServices.ts +16 -0
- package/externalServices/index.ts +7 -0
- package/identities/IIdentityManager.ts +19 -0
- package/identities/IdentityManager.ts +32 -0
- package/identities/index.ts +5 -0
- package/index.ts +6 -0
- package/observation/FailedPartition.ts +21 -0
- package/observation/FailedPartitionAttempt.ts +21 -0
- package/observation/FailedPartitions.spec.ts +75 -0
- package/observation/FailedPartitions.ts +44 -0
- package/observation/ICanBeNotifiedWhenPartitionReplayed.ts +22 -0
- package/observation/ICanBeNotifiedWhenReplay.ts +20 -0
- package/observation/IFailedPartitions.ts +22 -0
- package/observation/index.ts +6 -0
- package/observation/notifyReplayLifecycle.spec.ts +98 -0
- package/observation/notifyReplayLifecycle.ts +61 -0
- package/observation/toClientFailedPartition.ts +26 -0
- package/package.json +12 -2
- package/projections/Projections.childrenAndNested.spec.ts +188 -0
- package/projections/Projections.spec.ts +131 -0
- package/projections/Projections.ts +37 -119
- package/projections/declarative/AddBuilder.ts +28 -0
- package/projections/declarative/AddChildBuilder.ts +97 -0
- package/projections/declarative/ChildrenBuilder.spec.ts +157 -0
- package/projections/declarative/ChildrenBuilder.ts +90 -0
- package/projections/declarative/CompositeKeyBuilder.spec.ts +117 -0
- package/projections/declarative/CompositeKeyBuilder.ts +38 -0
- package/projections/declarative/FromBuilder.ts +60 -15
- package/projections/declarative/ICompositeKeyBuilder.ts +6 -0
- package/projections/declarative/JoinBuilder.ts +66 -17
- package/projections/declarative/NestedBuilder.ts +76 -0
- package/projections/declarative/ProjectionBuilderCore.ts +257 -0
- package/projections/declarative/ProjectionBuilderFor.spec.ts +115 -0
- package/projections/declarative/ProjectionBuilderFor.ts +29 -168
- package/projections/declarative/SubtractBuilder.ts +28 -0
- package/projections/declarative/index.ts +9 -0
- package/projections/modelBound/childrenAndNestedBuilder.ts +354 -0
- package/reactors/ReactorSideEffects.spec.ts +182 -0
- package/reactors/ReactorSideEffects.ts +97 -0
- package/reactors/Reactors.ts +29 -4
- package/reducers/Reducers.spec.ts +87 -0
- package/reducers/Reducers.ts +15 -3
- package/reducers/reducer.spec.ts +40 -0
- package/reducers/reducer.ts +11 -2
- package/transactions/IUnitOfWork.ts +21 -0
- package/transactions/UnitOfWork.spec.ts +82 -0
- package/transactions/UnitOfWork.ts +20 -0
|
@@ -0,0 +1,188 @@
|
|
|
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 'reflect-metadata';
|
|
5
|
+
import { field } from '@cratis/fundamentals';
|
|
6
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
7
|
+
import { IClientArtifactsProvider } from '../artifacts';
|
|
8
|
+
import { ChronicleConnection } from '../connection';
|
|
9
|
+
import { eventType } from '../events/eventTypeDecorator';
|
|
10
|
+
import { readModel } from '../readModels/readModel';
|
|
11
|
+
import { childrenFrom } from './modelBound/childrenFrom';
|
|
12
|
+
import { clearWith } from './modelBound/clearWith';
|
|
13
|
+
import { fromEvent } from './modelBound/fromEvent';
|
|
14
|
+
import { nested } from './modelBound/nested';
|
|
15
|
+
import { setFrom } from './modelBound/setFrom';
|
|
16
|
+
import { Projections } from './Projections';
|
|
17
|
+
|
|
18
|
+
// Decorators are applied as plain function calls (rather than `@decorator` syntax) so these
|
|
19
|
+
// fixtures don't depend on the test runner's decorator-syntax support.
|
|
20
|
+
|
|
21
|
+
class LineAdded {
|
|
22
|
+
productId!: string;
|
|
23
|
+
quantity!: number;
|
|
24
|
+
}
|
|
25
|
+
eventType()(LineAdded);
|
|
26
|
+
|
|
27
|
+
class LineQuantityChanged {
|
|
28
|
+
quantity!: number;
|
|
29
|
+
}
|
|
30
|
+
eventType()(LineQuantityChanged);
|
|
31
|
+
|
|
32
|
+
class SummaryUpdated {
|
|
33
|
+
total!: number;
|
|
34
|
+
}
|
|
35
|
+
eventType()(SummaryUpdated);
|
|
36
|
+
|
|
37
|
+
class SummaryCleared {}
|
|
38
|
+
eventType()(SummaryCleared);
|
|
39
|
+
|
|
40
|
+
class NoteAdded {
|
|
41
|
+
text!: string;
|
|
42
|
+
}
|
|
43
|
+
eventType()(NoteAdded);
|
|
44
|
+
|
|
45
|
+
class NoteCleared {}
|
|
46
|
+
eventType()(NoteCleared);
|
|
47
|
+
|
|
48
|
+
class TagAdded {
|
|
49
|
+
name!: string;
|
|
50
|
+
}
|
|
51
|
+
eventType()(TagAdded);
|
|
52
|
+
|
|
53
|
+
class OrderCreated {}
|
|
54
|
+
eventType()(OrderCreated);
|
|
55
|
+
|
|
56
|
+
class OrderLine {
|
|
57
|
+
productId!: string;
|
|
58
|
+
quantity!: number;
|
|
59
|
+
}
|
|
60
|
+
setFrom(LineQuantityChanged)(OrderLine.prototype, 'quantity');
|
|
61
|
+
|
|
62
|
+
class OrderSummary {
|
|
63
|
+
total!: number;
|
|
64
|
+
}
|
|
65
|
+
setFrom(SummaryUpdated)(OrderSummary.prototype, 'total');
|
|
66
|
+
clearWith(SummaryCleared)(OrderSummary);
|
|
67
|
+
|
|
68
|
+
class OrderNote {
|
|
69
|
+
text!: string;
|
|
70
|
+
}
|
|
71
|
+
setFrom(NoteAdded)(OrderNote.prototype, 'text');
|
|
72
|
+
|
|
73
|
+
class Order {
|
|
74
|
+
id!: string;
|
|
75
|
+
lines!: OrderLine[];
|
|
76
|
+
summary!: OrderSummary | undefined;
|
|
77
|
+
note!: OrderNote | undefined;
|
|
78
|
+
tags!: string[];
|
|
79
|
+
}
|
|
80
|
+
childrenFrom(LineAdded, undefined, 'productId', undefined)(Order.prototype, 'lines');
|
|
81
|
+
field(Array, { enumerable: true, genericArguments: [OrderLine] })(Order.prototype, 'lines');
|
|
82
|
+
nested(Order.prototype, 'summary');
|
|
83
|
+
field(OrderSummary)(Order.prototype, 'summary');
|
|
84
|
+
nested(Order.prototype, 'note');
|
|
85
|
+
field(OrderNote)(Order.prototype, 'note');
|
|
86
|
+
clearWith(NoteCleared)(Order.prototype, 'note');
|
|
87
|
+
childrenFrom(TagAdded)(Order.prototype, 'tags');
|
|
88
|
+
fromEvent(OrderCreated)(Order);
|
|
89
|
+
readModel()(Order);
|
|
90
|
+
|
|
91
|
+
interface FromRecord {
|
|
92
|
+
Key: { Id: string };
|
|
93
|
+
Value: { Properties: Record<string, string>; Key: string; ParentKey: string };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
interface ChildrenDefinition {
|
|
97
|
+
IdentifiedBy: string;
|
|
98
|
+
From: FromRecord[];
|
|
99
|
+
RemovedWith: Array<{ Key: { Id: string } }>;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
interface BuiltDefinition {
|
|
103
|
+
Children: Record<string, ChildrenDefinition>;
|
|
104
|
+
Nested: Record<string, ChildrenDefinition>;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function createProjections(readModels: (new (...args: unknown[]) => unknown)[]) {
|
|
108
|
+
const registerMock = vi.fn().mockResolvedValue(undefined);
|
|
109
|
+
const registerManyMock = vi.fn().mockResolvedValue(undefined);
|
|
110
|
+
const connection = {
|
|
111
|
+
readModels: { registerMany: registerManyMock },
|
|
112
|
+
projections: { register: registerMock }
|
|
113
|
+
} as unknown as ChronicleConnection;
|
|
114
|
+
|
|
115
|
+
const clientArtifacts: IClientArtifactsProvider = {
|
|
116
|
+
eventTypes: [],
|
|
117
|
+
readModels: readModels as unknown as IClientArtifactsProvider['readModels'],
|
|
118
|
+
reactors: [],
|
|
119
|
+
reducers: [],
|
|
120
|
+
seeders: [],
|
|
121
|
+
constraints: [],
|
|
122
|
+
projections: [],
|
|
123
|
+
webhooks: [],
|
|
124
|
+
eventTypeMigrations: []
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const projections = new Projections('test-store', connection, clientArtifacts, 'test-sink');
|
|
128
|
+
return { projections, registerMock };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
describe('Projections with childrenFrom, nested and clearWith', () => {
|
|
132
|
+
describe('when registering a model-bound projection using them', () => {
|
|
133
|
+
it('should not throw', async () => {
|
|
134
|
+
const { projections } = createProjections([Order]);
|
|
135
|
+
await expect(projections.register()).resolves.not.toThrow();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should build a children definition with the resolved child type wired up', async () => {
|
|
139
|
+
const { projections, registerMock } = createProjections([Order]);
|
|
140
|
+
await projections.register();
|
|
141
|
+
|
|
142
|
+
const definition = registerMock.mock.calls[0][0].Projections[0] as BuiltDefinition;
|
|
143
|
+
const lines = definition.Children.lines;
|
|
144
|
+
|
|
145
|
+
expect(lines.IdentifiedBy).toBe('productId');
|
|
146
|
+
const creationEntry = lines.From.find(candidate => candidate.Key.Id === 'LineAdded')!;
|
|
147
|
+
expect(creationEntry.Value.Key).toBe('$eventSourceId');
|
|
148
|
+
expect(creationEntry.Value.ParentKey).toBe('$eventSourceId');
|
|
149
|
+
|
|
150
|
+
const updateEntry = lines.From.find(candidate => candidate.Key.Id === 'LineQuantityChanged')!;
|
|
151
|
+
expect(updateEntry.Value.Properties.quantity).toBe('quantity');
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('should build a children definition even when the child element type cannot be resolved', async () => {
|
|
155
|
+
const { projections, registerMock } = createProjections([Order]);
|
|
156
|
+
await projections.register();
|
|
157
|
+
|
|
158
|
+
const definition = registerMock.mock.calls[0][0].Projections[0] as BuiltDefinition;
|
|
159
|
+
const tags = definition.Children.tags;
|
|
160
|
+
|
|
161
|
+
expect(tags.From.some(candidate => candidate.Key.Id === 'TagAdded')).toBe(true);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('should build a nested definition honoring a class-level clearWith on the nested type', async () => {
|
|
165
|
+
const { projections, registerMock } = createProjections([Order]);
|
|
166
|
+
await projections.register();
|
|
167
|
+
|
|
168
|
+
const definition = registerMock.mock.calls[0][0].Projections[0] as BuiltDefinition;
|
|
169
|
+
const summary = definition.Nested.summary;
|
|
170
|
+
|
|
171
|
+
const updateEntry = summary.From.find(candidate => candidate.Key.Id === 'SummaryUpdated')!;
|
|
172
|
+
expect(updateEntry.Value.Properties.total).toBe('total');
|
|
173
|
+
expect(summary.RemovedWith.some(candidate => candidate.Key.Id === 'SummaryCleared')).toBe(true);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('should build a nested definition honoring a property-level clearWith', async () => {
|
|
177
|
+
const { projections, registerMock } = createProjections([Order]);
|
|
178
|
+
await projections.register();
|
|
179
|
+
|
|
180
|
+
const definition = registerMock.mock.calls[0][0].Projections[0] as BuiltDefinition;
|
|
181
|
+
const note = definition.Nested.note;
|
|
182
|
+
|
|
183
|
+
const updateEntry = note.From.find(candidate => candidate.Key.Id === 'NoteAdded')!;
|
|
184
|
+
expect(updateEntry.Value.Properties.text).toBe('text');
|
|
185
|
+
expect(note.RemovedWith.some(candidate => candidate.Key.Id === 'NoteCleared')).toBe(true);
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
});
|
|
@@ -0,0 +1,131 @@
|
|
|
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 'reflect-metadata';
|
|
5
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
6
|
+
import { IClientArtifactsProvider } from '../artifacts';
|
|
7
|
+
import { ChronicleConnection } from '../connection';
|
|
8
|
+
import { eventType } from '../events/eventTypeDecorator';
|
|
9
|
+
import { readModel } from '../readModels/readModel';
|
|
10
|
+
import { addFrom } from './modelBound/addFrom';
|
|
11
|
+
import { count } from './modelBound/count';
|
|
12
|
+
import { decrement } from './modelBound/decrement';
|
|
13
|
+
import { fromEvent } from './modelBound/fromEvent';
|
|
14
|
+
import { increment } from './modelBound/increment';
|
|
15
|
+
import { subtractFrom } from './modelBound/subtractFrom';
|
|
16
|
+
import { Projections } from './Projections';
|
|
17
|
+
|
|
18
|
+
// Decorators are applied as plain function calls (rather than `@decorator` syntax) so these
|
|
19
|
+
// fixtures don't depend on the test runner's decorator-syntax support - they exercise exactly
|
|
20
|
+
// the same decorator functions and metadata storage that `@decorator` syntax would invoke.
|
|
21
|
+
|
|
22
|
+
class ItemAdded {
|
|
23
|
+
quantity!: number;
|
|
24
|
+
}
|
|
25
|
+
eventType()(ItemAdded);
|
|
26
|
+
|
|
27
|
+
class ItemRemoved {
|
|
28
|
+
amount!: number;
|
|
29
|
+
}
|
|
30
|
+
eventType()(ItemRemoved);
|
|
31
|
+
|
|
32
|
+
class CounterIncremented {}
|
|
33
|
+
eventType()(CounterIncremented);
|
|
34
|
+
|
|
35
|
+
class CounterDecremented {}
|
|
36
|
+
eventType()(CounterDecremented);
|
|
37
|
+
|
|
38
|
+
class ThingHappened {}
|
|
39
|
+
eventType()(ThingHappened);
|
|
40
|
+
|
|
41
|
+
class KeyedThingHappened {}
|
|
42
|
+
eventType()(KeyedThingHappened);
|
|
43
|
+
|
|
44
|
+
class Inventory {
|
|
45
|
+
id!: string;
|
|
46
|
+
total!: number;
|
|
47
|
+
totalFromNamedProperty!: number;
|
|
48
|
+
removedTotal!: number;
|
|
49
|
+
incrementedCount!: number;
|
|
50
|
+
decrementedCount!: number;
|
|
51
|
+
thingsHappenedCount!: number;
|
|
52
|
+
constantKeyedCount!: number;
|
|
53
|
+
}
|
|
54
|
+
addFrom(ItemAdded)(Inventory.prototype, 'total');
|
|
55
|
+
addFrom(ItemAdded, 'quantity')(Inventory.prototype, 'totalFromNamedProperty');
|
|
56
|
+
subtractFrom(ItemRemoved, 'amount')(Inventory.prototype, 'removedTotal');
|
|
57
|
+
increment(CounterIncremented)(Inventory.prototype, 'incrementedCount');
|
|
58
|
+
decrement(CounterDecremented)(Inventory.prototype, 'decrementedCount');
|
|
59
|
+
count(ThingHappened)(Inventory.prototype, 'thingsHappenedCount');
|
|
60
|
+
count(KeyedThingHappened, 'all-things')(Inventory.prototype, 'constantKeyedCount');
|
|
61
|
+
fromEvent(ItemAdded)(Inventory);
|
|
62
|
+
readModel()(Inventory);
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Builds a {@link Projections} instance wired to capture the registration payload sent
|
|
66
|
+
* to the kernel, without requiring a real gRPC connection.
|
|
67
|
+
*/
|
|
68
|
+
function createProjections(readModels: (new (...args: unknown[]) => unknown)[]) {
|
|
69
|
+
const registerMock = vi.fn().mockResolvedValue(undefined);
|
|
70
|
+
const registerManyMock = vi.fn().mockResolvedValue(undefined);
|
|
71
|
+
const connection = {
|
|
72
|
+
readModels: { registerMany: registerManyMock },
|
|
73
|
+
projections: { register: registerMock }
|
|
74
|
+
} as unknown as ChronicleConnection;
|
|
75
|
+
|
|
76
|
+
const clientArtifacts: IClientArtifactsProvider = {
|
|
77
|
+
eventTypes: [],
|
|
78
|
+
readModels: readModels as unknown as IClientArtifactsProvider['readModels'],
|
|
79
|
+
reactors: [],
|
|
80
|
+
reducers: [],
|
|
81
|
+
seeders: [],
|
|
82
|
+
constraints: [],
|
|
83
|
+
projections: [],
|
|
84
|
+
webhooks: [],
|
|
85
|
+
eventTypeMigrations: []
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const projections = new Projections('test-store', connection, clientArtifacts, 'test-sink');
|
|
89
|
+
return { projections, registerMock };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function findFromEntry(definition: { From: Array<{ Key: { Id: string }; Value: { Properties: Record<string, string>; Key: string } }> }, eventTypeId: string) {
|
|
93
|
+
const entry = definition.From.find(candidate => candidate.Key.Id === eventTypeId);
|
|
94
|
+
if (!entry) {
|
|
95
|
+
throw new Error(`No From entry found for event type '${eventTypeId}'.`);
|
|
96
|
+
}
|
|
97
|
+
return entry;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
describe('Projections', () => {
|
|
101
|
+
describe('when registering a model-bound projection with arithmetic decorators', () => {
|
|
102
|
+
it('should not throw and should produce the correct wire expressions', async () => {
|
|
103
|
+
const { projections, registerMock } = createProjections([Inventory]);
|
|
104
|
+
|
|
105
|
+
await expect(projections.register()).resolves.not.toThrow();
|
|
106
|
+
|
|
107
|
+
const request = registerMock.mock.calls[0][0] as { Projections: unknown[] };
|
|
108
|
+
const definition = request.Projections[0] as { From: Array<{ Key: { Id: string }; Value: { Properties: Record<string, string>; Key: string } }> };
|
|
109
|
+
|
|
110
|
+
const itemAdded = findFromEntry(definition, 'ItemAdded');
|
|
111
|
+
expect(itemAdded.Value.Properties.total).toBe('$add(total)');
|
|
112
|
+
expect(itemAdded.Value.Properties.totalFromNamedProperty).toBe('$add(quantity)');
|
|
113
|
+
|
|
114
|
+
const itemRemoved = findFromEntry(definition, 'ItemRemoved');
|
|
115
|
+
expect(itemRemoved.Value.Properties.removedTotal).toBe('$subtract(amount)');
|
|
116
|
+
|
|
117
|
+
const counterIncremented = findFromEntry(definition, 'CounterIncremented');
|
|
118
|
+
expect(counterIncremented.Value.Properties.incrementedCount).toBe('$increment');
|
|
119
|
+
|
|
120
|
+
const counterDecremented = findFromEntry(definition, 'CounterDecremented');
|
|
121
|
+
expect(counterDecremented.Value.Properties.decrementedCount).toBe('$decrement');
|
|
122
|
+
|
|
123
|
+
const thingHappened = findFromEntry(definition, 'ThingHappened');
|
|
124
|
+
expect(thingHappened.Value.Properties.thingsHappenedCount).toBe('$count');
|
|
125
|
+
|
|
126
|
+
const keyedThingHappened = findFromEntry(definition, 'KeyedThingHappened');
|
|
127
|
+
expect(keyedThingHappened.Value.Properties.constantKeyedCount).toBe('$count');
|
|
128
|
+
expect(keyedThingHappened.Value.Key).toBe('$value(all-things)');
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
});
|
|
@@ -12,21 +12,25 @@ import { ChronicleConnection } from '../connection';
|
|
|
12
12
|
import { toContractsGuid } from '../connection/Guid';
|
|
13
13
|
import { WellKnownSinks } from '../sinks';
|
|
14
14
|
import { EventSequenceId } from '../eventSequences/EventSequenceId';
|
|
15
|
-
import { getEventTypeFor } from '../events/eventTypeDecorator';
|
|
16
15
|
import { getReadModelMetadata } from '../readModels';
|
|
17
16
|
import { TypeIntrospector } from '../types';
|
|
18
17
|
import { IProjections } from './IProjections';
|
|
19
18
|
import { getProjectionMetadata } from './declarative/projection';
|
|
20
19
|
import { ProjectionBuilderFor } from './declarative/ProjectionBuilderFor';
|
|
21
20
|
import type { IProjectionFor } from './declarative/IProjectionFor';
|
|
22
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
applyPropertyMappings,
|
|
23
|
+
buildChildrenEntry,
|
|
24
|
+
buildNestedEntry,
|
|
25
|
+
ChildrenDefinitionLike,
|
|
26
|
+
ContractEventType,
|
|
27
|
+
FromRecord,
|
|
28
|
+
getEventTypeMapKey,
|
|
29
|
+
toContractEventType
|
|
30
|
+
} from './modelBound/childrenAndNestedBuilder';
|
|
23
31
|
import { getChildrenFromMetadata } from './modelBound/childrenFrom';
|
|
24
|
-
import { getClearWithClassMetadata, getClearWithPropertyMetadata } from './modelBound/clearWith';
|
|
25
|
-
import { getCountMetadata } from './modelBound/count';
|
|
26
|
-
import { getDecrementMetadata } from './modelBound/decrement';
|
|
27
32
|
import { getFromEveryMetadata } from './modelBound/fromEvery';
|
|
28
33
|
import { getFromEventMetadata, hasFromEventMetadata } from './modelBound/fromEvent';
|
|
29
|
-
import { getIncrementMetadata } from './modelBound/increment';
|
|
30
34
|
import { getJoinMetadata } from './modelBound/join';
|
|
31
35
|
import { ProjectionId } from './ProjectionId';
|
|
32
36
|
import { isNested } from './modelBound/nested';
|
|
@@ -34,12 +38,6 @@ import { isNotRewindable } from './modelBound/notRewindable';
|
|
|
34
38
|
import { isPassive } from './modelBound/passive';
|
|
35
39
|
import { getRemovedWithClassMetadata, getRemovedWithPropertyMetadata } from './modelBound/removedWith';
|
|
36
40
|
import { getRemovedWithJoinClassMetadata, getRemovedWithJoinPropertyMetadata } from './modelBound/removedWithJoin';
|
|
37
|
-
import { getSetFromMetadata } from './modelBound/setFrom';
|
|
38
|
-
import { getSetFromContextMetadata } from './modelBound/setFromContext';
|
|
39
|
-
import { getSetValueMetadata } from './modelBound/setValue';
|
|
40
|
-
import { getSubtractFromMetadata } from './modelBound/subtractFrom';
|
|
41
|
-
|
|
42
|
-
type ContractEventType = { Id: string; Generation: number; Tombstone: boolean };
|
|
43
41
|
|
|
44
42
|
interface ResolvedModelBoundMetadata {
|
|
45
43
|
id: ProjectionId;
|
|
@@ -277,15 +275,17 @@ export class Projections implements IProjections {
|
|
|
277
275
|
const properties = TypeIntrospector.getTrackedProperties(type);
|
|
278
276
|
const prototype = type.prototype;
|
|
279
277
|
|
|
280
|
-
const fromByEventType = new Map<string,
|
|
278
|
+
const fromByEventType = new Map<string, FromRecord>();
|
|
281
279
|
const joinByEventType = new Map<string, { Key: ContractEventType; Value: { On: string; Properties: Record<string, string>; Key: string } }>();
|
|
282
280
|
const removedWithByEventType = new Map<string, { Key: ContractEventType; Value: { Key: string; ParentKey: string } }>();
|
|
283
281
|
const removedWithJoinByEventType = new Map<string, { Key: ContractEventType; Value: { Key: string } }>();
|
|
282
|
+
const childrenByProperty: Record<string, ChildrenDefinitionLike> = {};
|
|
283
|
+
const nestedByProperty: Record<string, ChildrenDefinitionLike> = {};
|
|
284
284
|
|
|
285
285
|
const fromEvents = getFromEventMetadata(type);
|
|
286
286
|
for (const fromEvent of fromEvents) {
|
|
287
|
-
const eventType =
|
|
288
|
-
const eventKey =
|
|
287
|
+
const eventType = toContractEventType(fromEvent.eventType);
|
|
288
|
+
const eventKey = getEventTypeMapKey(eventType);
|
|
289
289
|
fromByEventType.set(eventKey, {
|
|
290
290
|
Key: eventType,
|
|
291
291
|
Value: {
|
|
@@ -296,15 +296,10 @@ export class Projections implements IProjections {
|
|
|
296
296
|
});
|
|
297
297
|
}
|
|
298
298
|
|
|
299
|
-
const clearWithClass = getClearWithClassMetadata(type);
|
|
300
|
-
if (clearWithClass.length > 0) {
|
|
301
|
-
throw new Error(`Model-bound projection '${type.name}' uses @clearWith on class level, which is not implemented yet.`);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
299
|
const removedWithClass = getRemovedWithClassMetadata(type);
|
|
305
300
|
for (const removed of removedWithClass) {
|
|
306
|
-
const eventType =
|
|
307
|
-
removedWithByEventType.set(
|
|
301
|
+
const eventType = toContractEventType(removed.eventType);
|
|
302
|
+
removedWithByEventType.set(getEventTypeMapKey(eventType), {
|
|
308
303
|
Key: eventType,
|
|
309
304
|
Value: { Key: removed.key ?? '$eventSourceId', ParentKey: removed.parentKey ?? '' }
|
|
310
305
|
});
|
|
@@ -312,30 +307,15 @@ export class Projections implements IProjections {
|
|
|
312
307
|
|
|
313
308
|
const removedWithJoinClass = getRemovedWithJoinClassMetadata(type);
|
|
314
309
|
for (const removed of removedWithJoinClass) {
|
|
315
|
-
const eventType =
|
|
316
|
-
removedWithJoinByEventType.set(
|
|
310
|
+
const eventType = toContractEventType(removed.eventType);
|
|
311
|
+
removedWithJoinByEventType.set(getEventTypeMapKey(eventType), {
|
|
317
312
|
Key: eventType,
|
|
318
313
|
Value: { Key: removed.key ?? '$eventSourceId' }
|
|
319
314
|
});
|
|
320
315
|
}
|
|
321
316
|
|
|
322
317
|
for (const property of properties) {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
for (const mapping of getSetFromMetadata(prototype, property)) {
|
|
326
|
-
const entry = this.ensureFromEntry(fromByEventType, mapping.eventType);
|
|
327
|
-
entry.Value.Properties[property] = mapping.eventPropertyName ?? property;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
for (const mapping of getSetFromContextMetadata(prototype, property)) {
|
|
331
|
-
const entry = this.ensureFromEntry(fromByEventType, mapping.eventType);
|
|
332
|
-
entry.Value.Properties[property] = mapping.contextPropertyName ?? property;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
for (const mapping of getSetValueMetadata(prototype, property)) {
|
|
336
|
-
const entry = this.ensureFromEntry(fromByEventType, mapping.eventType);
|
|
337
|
-
entry.Value.Properties[property] = JSON.stringify(mapping.value);
|
|
338
|
-
}
|
|
318
|
+
applyPropertyMappings(prototype, property, fromByEventType);
|
|
339
319
|
|
|
340
320
|
for (const mapping of getJoinMetadata(prototype, property)) {
|
|
341
321
|
const entry = this.ensureJoinEntry(joinByEventType, mapping.eventType);
|
|
@@ -343,26 +323,30 @@ export class Projections implements IProjections {
|
|
|
343
323
|
entry.Value.Properties[property] = mapping.eventPropertyName ?? property;
|
|
344
324
|
}
|
|
345
325
|
|
|
346
|
-
const fromEvery = getFromEveryMetadata(prototype, property);
|
|
347
|
-
if (fromEvery) {
|
|
348
|
-
// Applied later to projection.All
|
|
349
|
-
}
|
|
350
|
-
|
|
351
326
|
for (const removed of getRemovedWithPropertyMetadata(prototype, property)) {
|
|
352
|
-
const eventType =
|
|
353
|
-
removedWithByEventType.set(
|
|
327
|
+
const eventType = toContractEventType(removed.eventType);
|
|
328
|
+
removedWithByEventType.set(getEventTypeMapKey(eventType), {
|
|
354
329
|
Key: eventType,
|
|
355
330
|
Value: { Key: removed.key ?? '$eventSourceId', ParentKey: removed.parentKey ?? '' }
|
|
356
331
|
});
|
|
357
332
|
}
|
|
358
333
|
|
|
359
334
|
for (const removed of getRemovedWithJoinPropertyMetadata(prototype, property)) {
|
|
360
|
-
const eventType =
|
|
361
|
-
removedWithJoinByEventType.set(
|
|
335
|
+
const eventType = toContractEventType(removed.eventType);
|
|
336
|
+
removedWithJoinByEventType.set(getEventTypeMapKey(eventType), {
|
|
362
337
|
Key: eventType,
|
|
363
338
|
Value: { Key: removed.key ?? '$eventSourceId' }
|
|
364
339
|
});
|
|
365
340
|
}
|
|
341
|
+
|
|
342
|
+
const childrenFromList = getChildrenFromMetadata(prototype, property);
|
|
343
|
+
if (childrenFromList.length > 0) {
|
|
344
|
+
childrenByProperty[property] = buildChildrenEntry(type, property, childrenFromList);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (isNested(prototype, property)) {
|
|
348
|
+
nestedByProperty[property] = buildNestedEntry(type, property);
|
|
349
|
+
}
|
|
366
350
|
}
|
|
367
351
|
|
|
368
352
|
const allProperties: Record<string, string> = {};
|
|
@@ -384,7 +368,7 @@ export class Projections implements IProjections {
|
|
|
384
368
|
InitialModelState: '{}',
|
|
385
369
|
From: Array.from(fromByEventType.values()),
|
|
386
370
|
Join: Array.from(joinByEventType.values()),
|
|
387
|
-
Children:
|
|
371
|
+
Children: childrenByProperty,
|
|
388
372
|
FromEvery: [],
|
|
389
373
|
All: {
|
|
390
374
|
Properties: allProperties,
|
|
@@ -396,7 +380,7 @@ export class Projections implements IProjections {
|
|
|
396
380
|
LastUpdated: { Value: '' },
|
|
397
381
|
Tags: [],
|
|
398
382
|
AutoMap: AutoMap.Enabled,
|
|
399
|
-
Nested:
|
|
383
|
+
Nested: nestedByProperty
|
|
400
384
|
};
|
|
401
385
|
definition.LastUpdated = { Value: this.computeStableLastUpdated(definition) };
|
|
402
386
|
return definition;
|
|
@@ -416,62 +400,12 @@ export class Projections implements IProjections {
|
|
|
416
400
|
return undefined;
|
|
417
401
|
}
|
|
418
402
|
|
|
419
|
-
private throwIfUnsupportedModelBoundDecorators(typeName: string, prototype: object, property: string): void {
|
|
420
|
-
if (getAddFromMetadata(prototype, property).length > 0) {
|
|
421
|
-
throw new Error(`Model-bound projection '${typeName}' uses @addFrom on '${property}', which is not implemented yet.`);
|
|
422
|
-
}
|
|
423
|
-
if (getSubtractFromMetadata(prototype, property).length > 0) {
|
|
424
|
-
throw new Error(`Model-bound projection '${typeName}' uses @subtractFrom on '${property}', which is not implemented yet.`);
|
|
425
|
-
}
|
|
426
|
-
if (getIncrementMetadata(prototype, property).length > 0) {
|
|
427
|
-
throw new Error(`Model-bound projection '${typeName}' uses @increment on '${property}', which is not implemented yet.`);
|
|
428
|
-
}
|
|
429
|
-
if (getDecrementMetadata(prototype, property).length > 0) {
|
|
430
|
-
throw new Error(`Model-bound projection '${typeName}' uses @decrement on '${property}', which is not implemented yet.`);
|
|
431
|
-
}
|
|
432
|
-
if (getCountMetadata(prototype, property).length > 0) {
|
|
433
|
-
throw new Error(`Model-bound projection '${typeName}' uses @count on '${property}', which is not implemented yet.`);
|
|
434
|
-
}
|
|
435
|
-
if (getChildrenFromMetadata(prototype, property).length > 0) {
|
|
436
|
-
throw new Error(`Model-bound projection '${typeName}' uses @childrenFrom on '${property}', which is not implemented yet.`);
|
|
437
|
-
}
|
|
438
|
-
if (isNested(prototype, property)) {
|
|
439
|
-
throw new Error(`Model-bound projection '${typeName}' uses @nested on '${property}', which is not implemented yet.`);
|
|
440
|
-
}
|
|
441
|
-
if (getClearWithPropertyMetadata(prototype, property).length > 0) {
|
|
442
|
-
throw new Error(`Model-bound projection '${typeName}' uses @clearWith on '${property}', which is not implemented yet.`);
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
private ensureFromEntry(
|
|
447
|
-
fromByEventType: Map<string, { Key: ContractEventType; Value: { Properties: Record<string, string>; Key: string; ParentKey: string } }>,
|
|
448
|
-
eventTypeConstructor: Function
|
|
449
|
-
): { Key: ContractEventType; Value: { Properties: Record<string, string>; Key: string; ParentKey: string } } {
|
|
450
|
-
const eventType = this.toContractEventType(eventTypeConstructor);
|
|
451
|
-
const key = this.getEventTypeMapKey(eventType);
|
|
452
|
-
const existing = fromByEventType.get(key);
|
|
453
|
-
if (existing) {
|
|
454
|
-
return existing;
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
const created = {
|
|
458
|
-
Key: eventType,
|
|
459
|
-
Value: {
|
|
460
|
-
Properties: {},
|
|
461
|
-
Key: '$eventSourceId',
|
|
462
|
-
ParentKey: ''
|
|
463
|
-
}
|
|
464
|
-
};
|
|
465
|
-
fromByEventType.set(key, created);
|
|
466
|
-
return created;
|
|
467
|
-
}
|
|
468
|
-
|
|
469
403
|
private ensureJoinEntry(
|
|
470
404
|
joinByEventType: Map<string, { Key: ContractEventType; Value: { On: string; Properties: Record<string, string>; Key: string } }>,
|
|
471
405
|
eventTypeConstructor: Function
|
|
472
406
|
): { Key: ContractEventType; Value: { On: string; Properties: Record<string, string>; Key: string } } {
|
|
473
|
-
const eventType =
|
|
474
|
-
const key =
|
|
407
|
+
const eventType = toContractEventType(eventTypeConstructor);
|
|
408
|
+
const key = getEventTypeMapKey(eventType);
|
|
475
409
|
const existing = joinByEventType.get(key);
|
|
476
410
|
if (existing) {
|
|
477
411
|
return existing;
|
|
@@ -489,22 +423,6 @@ export class Projections implements IProjections {
|
|
|
489
423
|
return created;
|
|
490
424
|
}
|
|
491
425
|
|
|
492
|
-
private toContractEventType(eventTypeConstructor: Function): ContractEventType {
|
|
493
|
-
const eventType = getEventTypeFor(eventTypeConstructor);
|
|
494
|
-
if (eventType.id.value === '') {
|
|
495
|
-
throw new Error(`Event type '${eventTypeConstructor.name}' is not decorated with @eventType().`);
|
|
496
|
-
}
|
|
497
|
-
return {
|
|
498
|
-
Id: eventType.id.value,
|
|
499
|
-
Generation: eventType.generation.value,
|
|
500
|
-
Tombstone: false
|
|
501
|
-
};
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
private getEventTypeMapKey(eventType: ContractEventType): string {
|
|
505
|
-
return `${eventType.Id}:${eventType.Generation}:${eventType.Tombstone ? '1' : '0'}`;
|
|
506
|
-
}
|
|
507
|
-
|
|
508
426
|
/**
|
|
509
427
|
* Computes a stable, deterministic ISO timestamp from the projection definition content,
|
|
510
428
|
* excluding the LastUpdated field itself. This ensures the server does not interpret
|
|
@@ -0,0 +1,28 @@
|
|
|
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 { PropertyAccessor, PropertyPathResolverProxyHandler } from '@cratis/fundamentals';
|
|
5
|
+
import { IAddBuilder } from './IAddBuilder';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Concrete implementation of {@link IAddBuilder} that records an add expression
|
|
9
|
+
* into the owning from/join builder.
|
|
10
|
+
* @template TEvent - The event type.
|
|
11
|
+
* @template TParentBuilder - The parent builder type.
|
|
12
|
+
*/
|
|
13
|
+
export class AddBuilder<TEvent, TParentBuilder> implements IAddBuilder<TEvent, TParentBuilder> {
|
|
14
|
+
constructor(
|
|
15
|
+
private readonly _readModelProperty: string,
|
|
16
|
+
private readonly _setProperty: (readModelProp: string, expression: string) => void,
|
|
17
|
+
private readonly _parent: TParentBuilder
|
|
18
|
+
) {}
|
|
19
|
+
|
|
20
|
+
/** @inheritdoc */
|
|
21
|
+
with(eventPropertyAccessor: PropertyAccessor<TEvent>): TParentBuilder {
|
|
22
|
+
const handler = new PropertyPathResolverProxyHandler();
|
|
23
|
+
const proxy = new Proxy({}, handler);
|
|
24
|
+
eventPropertyAccessor(proxy as TEvent);
|
|
25
|
+
this._setProperty(this._readModelProperty, `$add(${handler.property})`);
|
|
26
|
+
return this._parent;
|
|
27
|
+
}
|
|
28
|
+
}
|