@cratis/chronicle 3.0.0 → 3.1.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 +4 -3
- package/EventStore.ts +20 -3
- package/IEventStore.ts +16 -0
- package/README.md +40 -28
- 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/connection/callResults.spec.ts +90 -0
- package/connection/callResults.ts +112 -0
- package/dist/ChronicleClient.d.ts.map +1 -1
- package/dist/ChronicleClient.js +4 -3
- package/dist/ChronicleClient.js.map +1 -1
- package/dist/EventStore.d.ts +8 -0
- package/dist/EventStore.d.ts.map +1 -1
- package/dist/EventStore.js +16 -3
- 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/connection/callResults.d.ts +69 -0
- package/dist/connection/callResults.d.ts.map +1 -0
- package/dist/connection/callResults.js +74 -0
- package/dist/connection/callResults.js.map +1 -0
- 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 +4 -4
- 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/jobs/IJobs.d.ts +4 -4
- package/dist/jobs/IJobs.d.ts.map +1 -1
- package/dist/jobs/Jobs.d.ts +4 -4
- package/dist/jobs/Jobs.d.ts.map +1 -1
- package/dist/jobs/Jobs.js +12 -10
- package/dist/jobs/Jobs.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 +4 -4
- 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/jobs/IJobs.ts +4 -4
- package/jobs/Jobs.ts +16 -14
- 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 +28 -3
- 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
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
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 { IAddChildBuilder } from './IAddChildBuilder';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A single `.addChild()` accumulation captured on a from/join builder, later merged into the
|
|
9
|
+
* owning projection builder's `Children` definitions once the enclosing `.from()`/`.join()`
|
|
10
|
+
* call resolves the event type it belongs to.
|
|
11
|
+
*/
|
|
12
|
+
export interface ChildAdditionEntry {
|
|
13
|
+
/** The read model property the child is added to. */
|
|
14
|
+
targetProperty: string;
|
|
15
|
+
/** The child model property used to identify instances, when set via the builder callback overload. */
|
|
16
|
+
identifiedBy?: string;
|
|
17
|
+
/** The event property used as the key, when set via the builder callback overload. */
|
|
18
|
+
usingKey?: string;
|
|
19
|
+
/** The event property whose value becomes the child directly, when set via the plain accessor overload. */
|
|
20
|
+
fromEventProperty?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Implements {@link IAddChildBuilder} and doubles as a {@link ProxyHandler} so a single probe
|
|
25
|
+
* object can resolve `.addChild()`'s two call shapes at runtime: a plain event-property
|
|
26
|
+
* accessor (`e => e.someProperty`), or a builder callback (`b => b.usingKey(...)`). Known
|
|
27
|
+
* builder members are dispatched to the real implementation below; any other property access
|
|
28
|
+
* (the plain-accessor shape) falls through to the same path-capturing proxy used everywhere
|
|
29
|
+
* else in this file for `PropertyAccessor<T>` resolution.
|
|
30
|
+
* @template TChildModel - The child model type.
|
|
31
|
+
* @template TEvent - The event type.
|
|
32
|
+
*/
|
|
33
|
+
export class AddChildBuilder<TChildModel, TEvent> implements IAddChildBuilder<TChildModel, TEvent>, ProxyHandler<object> {
|
|
34
|
+
private readonly _pathHandler = new PropertyPathResolverProxyHandler();
|
|
35
|
+
private readonly _pathProxy = new Proxy({}, this._pathHandler);
|
|
36
|
+
private _usedAsBuilder = false;
|
|
37
|
+
private _identifiedByProperty: string | undefined;
|
|
38
|
+
private _usingKeyProperty: string | undefined;
|
|
39
|
+
|
|
40
|
+
/** Whether any builder member (`identifiedBy`/`usingKey`) was invoked on the probe. */
|
|
41
|
+
get usedAsBuilder(): boolean {
|
|
42
|
+
return this._usedAsBuilder;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** The child model property captured via `identifiedBy()`, if any. */
|
|
46
|
+
get identifiedByProperty(): string | undefined {
|
|
47
|
+
return this._identifiedByProperty;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** The event property captured via `usingKey()`, if any. */
|
|
51
|
+
get usingKeyProperty(): string | undefined {
|
|
52
|
+
return this._usingKeyProperty;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** The event property path captured when the probe was used as a plain accessor. */
|
|
56
|
+
get capturedEventProperty(): string {
|
|
57
|
+
return this._pathHandler.property;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** @inheritdoc */
|
|
61
|
+
identifiedBy(childPropertyAccessor: PropertyAccessor<TChildModel>): IAddChildBuilder<TChildModel, TEvent> {
|
|
62
|
+
this._usedAsBuilder = true;
|
|
63
|
+
const handler = new PropertyPathResolverProxyHandler();
|
|
64
|
+
const proxy = new Proxy({}, handler);
|
|
65
|
+
childPropertyAccessor(proxy as TChildModel);
|
|
66
|
+
this._identifiedByProperty = handler.property;
|
|
67
|
+
return this;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** @inheritdoc */
|
|
71
|
+
usingKey(eventPropertyAccessor: PropertyAccessor<TEvent>): IAddChildBuilder<TChildModel, TEvent> {
|
|
72
|
+
this._usedAsBuilder = true;
|
|
73
|
+
const handler = new PropertyPathResolverProxyHandler();
|
|
74
|
+
const proxy = new Proxy({}, handler);
|
|
75
|
+
eventPropertyAccessor(proxy as TEvent);
|
|
76
|
+
this._usingKeyProperty = handler.property;
|
|
77
|
+
return this;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Proxy `get` trap: known builder members dispatch to the real implementation above;
|
|
82
|
+
* anything else (a plain event property accessed by the accessor overload) falls through
|
|
83
|
+
* to path-capturing so the accessor overload keeps working against the same probe.
|
|
84
|
+
* @param _target - The proxy's (unused) empty target object.
|
|
85
|
+
* @param prop - The accessed property name.
|
|
86
|
+
* @returns The resolved member.
|
|
87
|
+
*/
|
|
88
|
+
get(_target: object, prop: string | symbol): unknown {
|
|
89
|
+
if (prop === 'identifiedBy') {
|
|
90
|
+
return this.identifiedBy.bind(this);
|
|
91
|
+
}
|
|
92
|
+
if (prop === 'usingKey') {
|
|
93
|
+
return this.usingKey.bind(this);
|
|
94
|
+
}
|
|
95
|
+
return Reflect.get(this._pathProxy as object, prop);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
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 } from 'vitest';
|
|
6
|
+
import { eventType } from '../../events/eventTypeDecorator';
|
|
7
|
+
import { ProjectionBuilderFor } from './ProjectionBuilderFor';
|
|
8
|
+
|
|
9
|
+
// Decorators are applied as plain function calls (rather than `@decorator` syntax) so these
|
|
10
|
+
// fixtures don't depend on the test runner's decorator-syntax support.
|
|
11
|
+
|
|
12
|
+
class LineAdded {
|
|
13
|
+
productId!: string;
|
|
14
|
+
quantity!: number;
|
|
15
|
+
}
|
|
16
|
+
eventType()(LineAdded);
|
|
17
|
+
|
|
18
|
+
class TagAdded {
|
|
19
|
+
name!: string;
|
|
20
|
+
}
|
|
21
|
+
eventType()(TagAdded);
|
|
22
|
+
|
|
23
|
+
class SummaryUpdated {
|
|
24
|
+
total!: number;
|
|
25
|
+
}
|
|
26
|
+
eventType()(SummaryUpdated);
|
|
27
|
+
|
|
28
|
+
class SummaryCleared {}
|
|
29
|
+
eventType()(SummaryCleared);
|
|
30
|
+
|
|
31
|
+
class WholeLineReceived {
|
|
32
|
+
productId!: string;
|
|
33
|
+
}
|
|
34
|
+
eventType()(WholeLineReceived);
|
|
35
|
+
|
|
36
|
+
class OrderLine {
|
|
37
|
+
productId!: string;
|
|
38
|
+
quantity!: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
class OrderSummary {
|
|
42
|
+
total!: number;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
class Order {
|
|
46
|
+
id!: string;
|
|
47
|
+
lines!: OrderLine[];
|
|
48
|
+
tags!: string[];
|
|
49
|
+
summary!: OrderSummary | undefined;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface FromRecord {
|
|
53
|
+
Key: { Id: string };
|
|
54
|
+
Value: { Properties: Record<string, string>; Key: string; ParentKey: string };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
interface ChildrenDefinition {
|
|
58
|
+
IdentifiedBy: string;
|
|
59
|
+
From: FromRecord[];
|
|
60
|
+
RemovedWith: Array<{ Key: { Id: string } }>;
|
|
61
|
+
FromEventProperty?: { Event: { Id: string } | undefined; PropertyExpression: string };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
interface BuiltDefinition {
|
|
65
|
+
From: FromRecord[];
|
|
66
|
+
Children: Record<string, ChildrenDefinition>;
|
|
67
|
+
Nested: Record<string, ChildrenDefinition>;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
describe('ChildrenBuilder and NestedBuilder', () => {
|
|
71
|
+
describe('when using children on the top-level builder', () => {
|
|
72
|
+
it('should produce a children definition keyed by the target property', () => {
|
|
73
|
+
const builder = new ProjectionBuilderFor<Order>();
|
|
74
|
+
builder.children<OrderLine>(order => order.lines, children => children
|
|
75
|
+
.identifiedBy(line => line.productId)
|
|
76
|
+
.from(LineAdded, from => from.set(line => line.quantity).to(event => event.quantity)));
|
|
77
|
+
|
|
78
|
+
const definition = builder.build('order', 'Order') as unknown as BuiltDefinition;
|
|
79
|
+
const linesDefinition = definition.Children.lines;
|
|
80
|
+
|
|
81
|
+
expect(linesDefinition.IdentifiedBy).toBe('productId');
|
|
82
|
+
const fromEntry = linesDefinition.From.find(candidate => candidate.Key.Id === 'LineAdded')!;
|
|
83
|
+
expect(fromEntry.Value.Properties.quantity).toBe('quantity');
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('when using nested with clearWith on the top-level builder', () => {
|
|
88
|
+
it('should produce a nested definition with a RemovedWith entry', () => {
|
|
89
|
+
const builder = new ProjectionBuilderFor<Order>();
|
|
90
|
+
builder.nested<OrderSummary>(order => order.summary, nested => nested
|
|
91
|
+
.from(SummaryUpdated, from => from.set(summary => summary.total).to(event => event.total))
|
|
92
|
+
.clearWith(SummaryCleared));
|
|
93
|
+
|
|
94
|
+
const definition = builder.build('order', 'Order') as unknown as BuiltDefinition;
|
|
95
|
+
const summaryDefinition = definition.Nested.summary;
|
|
96
|
+
|
|
97
|
+
const fromEntry = summaryDefinition.From.find(candidate => candidate.Key.Id === 'SummaryUpdated')!;
|
|
98
|
+
expect(fromEntry.Value.Properties.total).toBe('total');
|
|
99
|
+
expect(summaryDefinition.RemovedWith[0].Key.Id).toBe('SummaryCleared');
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe('when using addChild with a builder callback', () => {
|
|
104
|
+
it('should produce a children definition keyed by identifiedBy and usingKey', () => {
|
|
105
|
+
const builder = new ProjectionBuilderFor<Order>();
|
|
106
|
+
builder.from(LineAdded, from => from.addChild<OrderLine>(order => order.lines, child => child
|
|
107
|
+
.identifiedBy(line => line.productId)
|
|
108
|
+
.usingKey(event => event.productId)));
|
|
109
|
+
|
|
110
|
+
const definition = builder.build('order', 'Order') as unknown as BuiltDefinition;
|
|
111
|
+
const linesDefinition = definition.Children.lines;
|
|
112
|
+
|
|
113
|
+
expect(linesDefinition.IdentifiedBy).toBe('productId');
|
|
114
|
+
const fromEntry = linesDefinition.From.find(candidate => candidate.Key.Id === 'LineAdded')!;
|
|
115
|
+
expect(fromEntry.Value.Key).toBe('productId');
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe('when using addChild with a plain event property accessor', () => {
|
|
120
|
+
it('should produce a children definition with a FromEventProperty expression', () => {
|
|
121
|
+
const builder = new ProjectionBuilderFor<Order>();
|
|
122
|
+
builder.from(TagAdded, from => from.addChild<string>(order => order.tags, event => event.name));
|
|
123
|
+
|
|
124
|
+
const definition = builder.build('order', 'Order') as unknown as BuiltDefinition;
|
|
125
|
+
const tagsDefinition = definition.Children.tags;
|
|
126
|
+
|
|
127
|
+
expect(tagsDefinition.FromEventProperty?.PropertyExpression).toBe('name');
|
|
128
|
+
expect(tagsDefinition.FromEventProperty?.Event?.Id).toBe('TagAdded');
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
describe('when using addChild on a join builder', () => {
|
|
133
|
+
it('should produce a children definition from the join clause', () => {
|
|
134
|
+
const builder = new ProjectionBuilderFor<Order>();
|
|
135
|
+
builder.join(WholeLineReceived, join => join.addChild<OrderLine>(order => order.lines, child => child
|
|
136
|
+
.identifiedBy(line => line.productId)));
|
|
137
|
+
|
|
138
|
+
const definition = builder.build('order', 'Order') as unknown as BuiltDefinition;
|
|
139
|
+
const linesDefinition = definition.Children.lines;
|
|
140
|
+
|
|
141
|
+
expect(linesDefinition.IdentifiedBy).toBe('productId');
|
|
142
|
+
expect(linesDefinition.From.some(candidate => candidate.Key.Id === 'WholeLineReceived')).toBe(true);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
describe('when using setThisValue on a from builder', () => {
|
|
147
|
+
it('should map the whole event property onto the $this well-known expression', () => {
|
|
148
|
+
const builder = new ProjectionBuilderFor<Order>();
|
|
149
|
+
builder.from(TagAdded, from => from.setThisValue().to(event => event.name));
|
|
150
|
+
|
|
151
|
+
const definition = builder.build('order', 'Order') as unknown as BuiltDefinition;
|
|
152
|
+
const fromEntry = definition.From.find(candidate => candidate.Key.Id === 'TagAdded')!;
|
|
153
|
+
|
|
154
|
+
expect(fromEntry.Value.Properties.$this).toBe('name');
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
});
|
|
@@ -0,0 +1,90 @@
|
|
|
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 { ChildrenDefinitionLike, ProjectionBuilderCore } from './ProjectionBuilderCore';
|
|
6
|
+
import { IChildrenBuilder } from './IChildrenBuilder';
|
|
7
|
+
import { INestedBuilder } from './INestedBuilder';
|
|
8
|
+
import { NestedBuilder } from './NestedBuilder';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Concrete implementation of {@link IChildrenBuilder}, building the `ChildrenDefinition` for a
|
|
12
|
+
* children collection sub-projection reached through `.children()`.
|
|
13
|
+
* @template TParentReadModel - The parent read model type.
|
|
14
|
+
* @template TChildReadModel - The child read model type.
|
|
15
|
+
*/
|
|
16
|
+
export class ChildrenBuilder<TParentReadModel, TChildReadModel>
|
|
17
|
+
extends ProjectionBuilderCore<TChildReadModel, IChildrenBuilder<TParentReadModel, TChildReadModel>>
|
|
18
|
+
implements IChildrenBuilder<TParentReadModel, TChildReadModel> {
|
|
19
|
+
private _identifiedBy: string | undefined;
|
|
20
|
+
private _fromEventPropertyPath: string | undefined;
|
|
21
|
+
|
|
22
|
+
/** @inheritdoc */
|
|
23
|
+
identifiedBy(propertyAccessor: PropertyAccessor<TChildReadModel>): IChildrenBuilder<TParentReadModel, TChildReadModel> {
|
|
24
|
+
const handler = new PropertyPathResolverProxyHandler();
|
|
25
|
+
const proxy = new Proxy({}, handler);
|
|
26
|
+
propertyAccessor(proxy as TChildReadModel);
|
|
27
|
+
this._identifiedBy = handler.property;
|
|
28
|
+
return this;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** @inheritdoc */
|
|
32
|
+
fromEventProperty<TEvent>(propertyAccessor: PropertyAccessor<TEvent>): IChildrenBuilder<TParentReadModel, TChildReadModel> {
|
|
33
|
+
const handler = new PropertyPathResolverProxyHandler();
|
|
34
|
+
const proxy = new Proxy({}, handler);
|
|
35
|
+
propertyAccessor(proxy as TEvent);
|
|
36
|
+
this._fromEventPropertyPath = handler.property;
|
|
37
|
+
return this;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** @inheritdoc */
|
|
41
|
+
children<TGrandchildModel>(
|
|
42
|
+
targetPropertyAccessor: PropertyAccessor<TChildReadModel>,
|
|
43
|
+
builderCallback: (builder: IChildrenBuilder<TChildReadModel, TGrandchildModel>) => void
|
|
44
|
+
): IChildrenBuilder<TParentReadModel, TChildReadModel> {
|
|
45
|
+
const handler = new PropertyPathResolverProxyHandler();
|
|
46
|
+
const proxy = new Proxy({}, handler);
|
|
47
|
+
targetPropertyAccessor(proxy as TChildReadModel);
|
|
48
|
+
|
|
49
|
+
const builder = new ChildrenBuilder<TChildReadModel, TGrandchildModel>();
|
|
50
|
+
builderCallback(builder);
|
|
51
|
+
this._children[handler.property] = builder.buildDefinition();
|
|
52
|
+
return this;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** @inheritdoc */
|
|
56
|
+
nested<TNestedModel>(
|
|
57
|
+
targetPropertyAccessor: PropertyAccessor<TChildReadModel>,
|
|
58
|
+
builderCallback: (builder: INestedBuilder<TChildReadModel, TNestedModel>) => void
|
|
59
|
+
): IChildrenBuilder<TParentReadModel, TChildReadModel> {
|
|
60
|
+
const handler = new PropertyPathResolverProxyHandler();
|
|
61
|
+
const proxy = new Proxy({}, handler);
|
|
62
|
+
targetPropertyAccessor(proxy as TChildReadModel);
|
|
63
|
+
|
|
64
|
+
const builder = new NestedBuilder<TChildReadModel, TNestedModel>();
|
|
65
|
+
builderCallback(builder);
|
|
66
|
+
this._nested[handler.property] = builder.buildDefinition();
|
|
67
|
+
return this;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Builds the {@link ChildrenDefinitionLike} representing this children collection.
|
|
72
|
+
* @returns The accumulated children definition.
|
|
73
|
+
*/
|
|
74
|
+
buildDefinition(): ChildrenDefinitionLike {
|
|
75
|
+
return {
|
|
76
|
+
IdentifiedBy: this._identifiedBy ?? '$eventSourceId',
|
|
77
|
+
From: this._from,
|
|
78
|
+
Join: this._join,
|
|
79
|
+
Children: this._children,
|
|
80
|
+
All: this._all,
|
|
81
|
+
FromEventProperty: this._fromEventPropertyPath === undefined
|
|
82
|
+
? undefined
|
|
83
|
+
: { Event: undefined, PropertyExpression: this._fromEventPropertyPath },
|
|
84
|
+
RemovedWith: this._removedWith,
|
|
85
|
+
RemovedWithJoin: this._removedWithJoin,
|
|
86
|
+
AutoMap: this._autoMap,
|
|
87
|
+
Nested: this._nested
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|