@cratis/chronicle 6.52.0 → 6.53.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/dist/reactors/Reactors.d.ts.map +1 -1
- package/dist/reactors/Reactors.js +2 -0
- package/dist/reactors/Reactors.js.map +1 -1
- package/dist/reactors/index.d.ts +1 -0
- package/dist/reactors/index.d.ts.map +1 -1
- package/dist/reactors/index.js +1 -0
- package/dist/reactors/index.js.map +1 -1
- package/dist/reactors/startAtTail.d.ts +18 -0
- package/dist/reactors/startAtTail.d.ts.map +1 -0
- package/dist/reactors/startAtTail.js +30 -0
- package/dist/reactors/startAtTail.js.map +1 -0
- package/dist/readModels/for_ReadModels/given/Book.d.ts +8 -0
- package/dist/readModels/for_ReadModels/given/Book.d.ts.map +1 -0
- package/dist/readModels/for_ReadModels/given/Book.js +25 -0
- package/dist/readModels/for_ReadModels/given/Book.js.map +1 -0
- package/dist/readModels/for_ReadModels/given/BookBorrowed.d.ts +3 -0
- package/dist/readModels/for_ReadModels/given/BookBorrowed.d.ts.map +1 -0
- package/dist/readModels/for_ReadModels/given/BookBorrowed.js +5 -0
- package/dist/readModels/for_ReadModels/given/BookBorrowed.js.map +1 -0
- package/dist/readModels/for_ReadModels/given/BookRegistered.d.ts +5 -0
- package/dist/readModels/for_ReadModels/given/BookRegistered.d.ts.map +1 -0
- package/dist/readModels/for_ReadModels/given/BookRegistered.js +7 -0
- package/dist/readModels/for_ReadModels/given/BookRegistered.js.map +1 -0
- package/dist/readModels/for_ReadModels/given/BookReturned.d.ts +3 -0
- package/dist/readModels/for_ReadModels/given/BookReturned.d.ts.map +1 -0
- package/dist/readModels/for_ReadModels/given/BookReturned.js +5 -0
- package/dist/readModels/for_ReadModels/given/BookReturned.js.map +1 -0
- package/dist/readModels/for_deserializeReadModel/given/Address.d.ts +6 -0
- package/dist/readModels/for_deserializeReadModel/given/Address.d.ts.map +1 -0
- package/dist/readModels/for_deserializeReadModel/given/Address.js +11 -0
- package/dist/readModels/for_deserializeReadModel/given/Address.js.map +1 -0
- package/dist/readModels/for_deserializeReadModel/given/a_read_model_with_defaults.d.ts +9 -0
- package/dist/readModels/for_deserializeReadModel/given/a_read_model_with_defaults.d.ts.map +1 -0
- package/dist/readModels/for_deserializeReadModel/given/a_read_model_with_defaults.js +16 -0
- package/dist/readModels/for_deserializeReadModel/given/a_read_model_with_defaults.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/webhooks/WebhookDefinitionBuilder.d.ts +1 -1
- package/dist/webhooks/WebhookDefinitionBuilder.d.ts.map +1 -1
- package/dist/webhooks/WebhookDefinitionBuilder.js +3 -1
- package/dist/webhooks/WebhookDefinitionBuilder.js.map +1 -1
- package/dist/webhooks/Webhooks.d.ts.map +1 -1
- package/dist/webhooks/Webhooks.js +2 -1
- package/dist/webhooks/Webhooks.js.map +1 -1
- package/package.json +4 -4
- package/projections/for_ProjectionDefinitionCompiler/against_a_kernel/when_subscribing_with_from_all_events.integration.spec.ts +3 -3
- package/reactors/Reactors.startAtTail.spec.ts +60 -0
- package/reactors/Reactors.ts +2 -0
- package/reactors/for_Reactors/against_a_kernel/when_starting_at_the_tail.integration.spec.ts +107 -0
- package/reactors/index.ts +1 -0
- package/reactors/startAtTail.spec.ts +31 -0
- package/reactors/startAtTail.ts +33 -0
- package/readModels/for_ReadModels/given/Book.ts +26 -0
- package/readModels/for_ReadModels/given/BookBorrowed.ts +4 -0
- package/readModels/for_ReadModels/given/BookRegistered.ts +7 -0
- package/readModels/for_ReadModels/given/BookReturned.ts +4 -0
- package/readModels/for_ReadModels/when_reading_a_model_bound_projection/without_registration_fields.spec.ts +30 -0
- package/readModels/for_deserializeReadModel/given/Address.ts +12 -0
- package/readModels/for_deserializeReadModel/given/IdentityDefaults.fixture.ts +15 -0
- package/readModels/for_deserializeReadModel/given/SetterDefaults.fixture.ts +22 -0
- package/readModels/for_deserializeReadModel/given/a_read_model_with_defaults.ts +18 -0
- package/readModels/for_deserializeReadModel/when_restoring/with_a_non_idempotent_setter_and_an_absent_sibling.spec.ts +20 -0
- package/readModels/for_deserializeReadModel/when_restoring/with_a_partial_nested_object.spec.ts +31 -0
- package/readModels/for_deserializeReadModel/when_restoring/with_absent_fields.spec.ts +22 -0
- package/readModels/for_deserializeReadModel/when_restoring/with_explicit_null.spec.ts +20 -0
- package/readModels/for_deserializeReadModel/when_restoring/with_identity_dependent_defaults.spec.ts +20 -0
- package/readModels/for_deserializeReadModel/when_restoring/with_partial_array_items.spec.ts +23 -0
- package/readModels/for_deserializeReadModel/when_restoring/with_present_fields.spec.ts +19 -0
- package/testing/EventScenario.constraint-values.spec.ts +2 -2
- package/testing/for_ReadModelScenario/when_evaluating_arithmetic/with_legacy_decorators.legacy.spec.ts +1 -1
- package/testing/for_ReadModelScenario/when_evaluating_arithmetic/with_standard_decorators.standard.spec.ts +1 -1
- package/testing/for_ReadModelScenario/when_evaluating_joins/with_a_fluent_projection.spec.ts +2 -2
- package/testing/for_ReadModelScenario/when_evaluating_joins/with_legacy_decorators.legacy.spec.ts +2 -2
- package/testing/for_ReadModelScenario/when_evaluating_joins/with_standard_decorators.standard.spec.ts +2 -2
- package/webhooks/WebhookDefinitionBuilder.ts +9 -7
- package/webhooks/Webhooks.ts +2 -1
|
@@ -0,0 +1,31 @@
|
|
|
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 { describe, expect, it } from 'vitest';
|
|
5
|
+
import { isStartAtTail, startAtTail } from './startAtTail.js';
|
|
6
|
+
|
|
7
|
+
@startAtTail()
|
|
8
|
+
class MarkedReactor {}
|
|
9
|
+
|
|
10
|
+
class OrdinaryReactor {}
|
|
11
|
+
|
|
12
|
+
class InheritedReactor extends MarkedReactor {}
|
|
13
|
+
|
|
14
|
+
describe('the start-at-tail decorator', () => {
|
|
15
|
+
it('marks the reactor class', () => {
|
|
16
|
+
expect(isStartAtTail(MarkedReactor)).toBe(true);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('leaves an unmarked class alone', () => {
|
|
20
|
+
expect(isStartAtTail(OrdinaryReactor)).toBe(false);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('is not inherited by a subclass', () => {
|
|
24
|
+
expect(isStartAtTail(InheritedReactor)).toBe(false);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('refuses to decorate anything but a class', () => {
|
|
28
|
+
const decorate = startAtTail() as unknown as (target: object, context: { kind: string }) => void;
|
|
29
|
+
expect(() => decorate(() => {}, { kind: 'method' })).toThrow(TypeError);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
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
|
+
|
|
6
|
+
const METADATA_KEY = 'chronicle:reactor:startAtTail';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A new reactor normally catches up from the beginning of the event sequence, so one whose handlers have side
|
|
10
|
+
* effects acts on every historical event the moment it is deployed. `@onceOnly()` does not prevent that, because it
|
|
11
|
+
* only excludes replay.
|
|
12
|
+
*
|
|
13
|
+
* Marks a reactor class so that the first time Chronicle sees it, it begins at the tail of the event sequence and
|
|
14
|
+
* observes only what is appended from then on. A reactor that already has a position keeps it, so the marker is safe
|
|
15
|
+
* to leave in place. Renaming the reactor or moving it changes its default id and makes it a new reactor, which
|
|
16
|
+
* starts at the tail again, unless an explicit id is given to `@reactor()`. An explicit replay still replays from the
|
|
17
|
+
* beginning. Both the Chronicle server and the client must support it; a server that predates it ignores the request.
|
|
18
|
+
* The marker is not inherited by subclasses.
|
|
19
|
+
* @returns A class decorator for legacy and standard TypeScript decorators.
|
|
20
|
+
*/
|
|
21
|
+
export function startAtTail(): ClassDecorator & ((value: Function, context: ClassDecoratorContext) => void) {
|
|
22
|
+
return (target: object, context?: ClassDecoratorContext) => {
|
|
23
|
+
if (typeof context === 'object' && context.kind !== 'class') {
|
|
24
|
+
throw new TypeError('Start-at-tail can only decorate a reactor class.');
|
|
25
|
+
}
|
|
26
|
+
Reflect.defineMetadata(METADATA_KEY, true, target);
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Whether this reactor class is marked to start at the tail (without inheriting the marker). */
|
|
31
|
+
export function isStartAtTail(target: Function): boolean {
|
|
32
|
+
return Reflect.getOwnMetadata(METADATA_KEY, target) === true;
|
|
33
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
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 { fromEvent } from '../../../projections/modelBound/fromEvent.js';
|
|
7
|
+
import { setValue } from '../../../projections/modelBound/setValue.js';
|
|
8
|
+
import { BookRegistered } from './BookRegistered.js';
|
|
9
|
+
import { BookBorrowed } from './BookBorrowed.js';
|
|
10
|
+
import { BookReturned } from './BookReturned.js';
|
|
11
|
+
|
|
12
|
+
export class Book {
|
|
13
|
+
id = '';
|
|
14
|
+
title: string | null = null;
|
|
15
|
+
isbn: string | null = null;
|
|
16
|
+
borrowed = false;
|
|
17
|
+
}
|
|
18
|
+
field(String)(Book.prototype, 'id');
|
|
19
|
+
field(String)(Book.prototype, 'title');
|
|
20
|
+
field(String)(Book.prototype, 'isbn');
|
|
21
|
+
field(Boolean)(Book.prototype, 'borrowed');
|
|
22
|
+
fromEvent(BookRegistered)(Book);
|
|
23
|
+
fromEvent(BookBorrowed)(Book);
|
|
24
|
+
fromEvent(BookReturned)(Book);
|
|
25
|
+
setValue(BookBorrowed, true)(Book.prototype, 'borrowed');
|
|
26
|
+
setValue(BookReturned, false)(Book.prototype, 'borrowed');
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { beforeEach, chai, describe, it, vi } from 'vitest';
|
|
5
|
+
import type { IClientArtifactsProvider } from '../../../artifacts/index.js';
|
|
6
|
+
import type { ChronicleConnection } from '../../../connection/index.js';
|
|
7
|
+
import { ReadModels } from '../../ReadModels.js';
|
|
8
|
+
import { Book } from '../given/Book.js';
|
|
9
|
+
|
|
10
|
+
const should = chai.should();
|
|
11
|
+
|
|
12
|
+
for (const borrowed of [true, false]) {
|
|
13
|
+
describe(`when reading a model-bound projection after a book is ${borrowed ? 'borrowed' : 'returned'} without registration fields`, () => {
|
|
14
|
+
let result: Book;
|
|
15
|
+
|
|
16
|
+
beforeEach(async () => {
|
|
17
|
+
const connection = {
|
|
18
|
+
readModels: { getInstanceByKey: vi.fn().mockResolvedValue({ ReadModel: JSON.stringify({ id: 'book', borrowed }) }) }
|
|
19
|
+
} as unknown as ChronicleConnection;
|
|
20
|
+
const artifacts = { readModels: [Book], projections: [], reducers: [] } as unknown as IClientArtifactsProvider;
|
|
21
|
+
const readModels = new ReadModels('store', 'tenant', connection, artifacts, 'sink');
|
|
22
|
+
result = await readModels.getInstanceById(Book, 'book');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should restore the model type', () => result.should.be.instanceOf(Book));
|
|
26
|
+
it('should keep the title null', () => should.equal(result.title, null));
|
|
27
|
+
it('should keep the isbn null', () => should.equal(result.isbn, null));
|
|
28
|
+
it('should restore the borrowed state', () => result.borrowed.should.equal(borrowed));
|
|
29
|
+
});
|
|
30
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
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
|
+
|
|
7
|
+
export class Address {
|
|
8
|
+
street = 'Default street';
|
|
9
|
+
city: string | null = null;
|
|
10
|
+
}
|
|
11
|
+
field(String)(Address.prototype, 'street');
|
|
12
|
+
field(String)(Address.prototype, 'city');
|
|
@@ -0,0 +1,15 @@
|
|
|
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
|
+
|
|
7
|
+
export class IdentityDefaults {
|
|
8
|
+
original = {};
|
|
9
|
+
mirror = this.original;
|
|
10
|
+
owner = this;
|
|
11
|
+
name = 'default';
|
|
12
|
+
}
|
|
13
|
+
field(Object)(IdentityDefaults.prototype, 'mirror');
|
|
14
|
+
field(Object)(IdentityDefaults.prototype, 'owner');
|
|
15
|
+
field(String)(IdentityDefaults.prototype, 'name');
|
|
@@ -0,0 +1,22 @@
|
|
|
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
|
+
|
|
7
|
+
export class SetterDefaults {
|
|
8
|
+
sibling = 'default';
|
|
9
|
+
assignments = 0;
|
|
10
|
+
private _name = 'default';
|
|
11
|
+
|
|
12
|
+
get name(): string {
|
|
13
|
+
return this._name;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
set name(value: string) {
|
|
17
|
+
this.assignments++;
|
|
18
|
+
this._name = `prefix:${value}`;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
field(String)(SetterDefaults.prototype, 'name');
|
|
22
|
+
field(String)(SetterDefaults.prototype, 'sibling');
|
|
@@ -0,0 +1,18 @@
|
|
|
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
|
+
|
|
7
|
+
import { Address } from './Address.js';
|
|
8
|
+
|
|
9
|
+
export class Model {
|
|
10
|
+
title: string | null = null;
|
|
11
|
+
status = 'available';
|
|
12
|
+
address = new Address();
|
|
13
|
+
children = [new Address()];
|
|
14
|
+
}
|
|
15
|
+
field(String)(Model.prototype, 'title');
|
|
16
|
+
field(String)(Model.prototype, 'status');
|
|
17
|
+
field(Address)(Model.prototype, 'address');
|
|
18
|
+
field(Array, { genericArguments: [Address] })(Model.prototype, 'children');
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { beforeEach, chai, describe, it } from 'vitest';
|
|
5
|
+
import { deserializeReadModel } from '../../deserializeReadModel.js';
|
|
6
|
+
import { SetterDefaults } from '../given/SetterDefaults.fixture.js';
|
|
7
|
+
|
|
8
|
+
chai.should();
|
|
9
|
+
|
|
10
|
+
describe('when restoring with a non-idempotent setter and an absent sibling', () => {
|
|
11
|
+
let result: SetterDefaults;
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
result = deserializeReadModel(SetterDefaults, '{"name":"stored"}');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should apply the setter transformation once', () => result.name.should.equal('prefix:stored'));
|
|
18
|
+
it('should invoke the setter once', () => result.assignments.should.equal(1));
|
|
19
|
+
it('should keep the absent sibling default', () => result.sibling.should.equal('default'));
|
|
20
|
+
});
|
package/readModels/for_deserializeReadModel/when_restoring/with_a_partial_nested_object.spec.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { beforeEach, chai, describe, it } from 'vitest';
|
|
5
|
+
import { deserializeReadModel } from '../../deserializeReadModel.js';
|
|
6
|
+
import { Model } from '../given/a_read_model_with_defaults.js';
|
|
7
|
+
import { Address } from '../given/Address.js';
|
|
8
|
+
|
|
9
|
+
const should = chai.should();
|
|
10
|
+
|
|
11
|
+
describe('when restoring with a partial nested object', () => {
|
|
12
|
+
let result: Model;
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
result = deserializeReadModel(Model, '{"address":{"city":"Oslo"}}');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should restore the nested type', () => result.address.should.be.instanceOf(Address));
|
|
19
|
+
it('should keep the absent nested field default', () => result.address.street.should.equal('Default street'));
|
|
20
|
+
it('should replace the present nested field default', () => result.address.city!.should.equal('Oslo'));
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe('when restoring with explicit null in a nested object', () => {
|
|
24
|
+
let result: Model;
|
|
25
|
+
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
result = deserializeReadModel(Model, '{"address":{"street":null}}');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should replace the non-null nested field default', () => should.equal(result.address.street, null));
|
|
31
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { beforeEach, chai, describe, it } from 'vitest';
|
|
5
|
+
import { deserializeReadModel } from '../../deserializeReadModel.js';
|
|
6
|
+
import { Model } from '../given/a_read_model_with_defaults.js';
|
|
7
|
+
import { Address } from '../given/Address.js';
|
|
8
|
+
|
|
9
|
+
const should = chai.should();
|
|
10
|
+
|
|
11
|
+
describe('when restoring with absent fields', () => {
|
|
12
|
+
let result: Model;
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
result = deserializeReadModel(Model, '{}');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should keep the null default', () => should.equal(result.title, null));
|
|
19
|
+
it('should keep the non-null default', () => result.status.should.equal('available'));
|
|
20
|
+
it('should keep the nested object default', () => result.address.should.deep.equal(new Address()));
|
|
21
|
+
it('should deserialize an absent array to an empty array', () => result.children.should.deep.equal([]));
|
|
22
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { beforeEach, chai, describe, it } from 'vitest';
|
|
5
|
+
import { deserializeReadModel } from '../../deserializeReadModel.js';
|
|
6
|
+
import { Model } from '../given/a_read_model_with_defaults.js';
|
|
7
|
+
|
|
8
|
+
const should = chai.should();
|
|
9
|
+
|
|
10
|
+
describe('when restoring with explicit null', () => {
|
|
11
|
+
let result: Model;
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
result = deserializeReadModel(Model, '{"status":null,"address":null,"children":null}');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should replace the non-null scalar default', () => should.equal(result.status, null));
|
|
18
|
+
it('should replace the nested object default', () => should.equal(result.address, null));
|
|
19
|
+
it('should replace the array default', () => should.equal(result.children, null));
|
|
20
|
+
});
|
package/readModels/for_deserializeReadModel/when_restoring/with_identity_dependent_defaults.spec.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { beforeEach, chai, describe, it } from 'vitest';
|
|
5
|
+
import { deserializeReadModel } from '../../deserializeReadModel.js';
|
|
6
|
+
import { IdentityDefaults } from '../given/IdentityDefaults.fixture.js';
|
|
7
|
+
|
|
8
|
+
chai.should();
|
|
9
|
+
|
|
10
|
+
describe('when restoring with identity-dependent defaults', () => {
|
|
11
|
+
let result: IdentityDefaults;
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
result = deserializeReadModel(IdentityDefaults, '{"name":"stored"}');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should keep the default referencing another member identical to that member', () => result.mirror.should.equal(result.original));
|
|
18
|
+
it('should keep the default referencing its owner identical to the returned instance', () => result.owner.should.equal(result));
|
|
19
|
+
it('should still replace a present decorated field', () => result.name.should.equal('stored'));
|
|
20
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { beforeEach, chai, describe, it } from 'vitest';
|
|
5
|
+
import { deserializeReadModel } from '../../deserializeReadModel.js';
|
|
6
|
+
import { Model } from '../given/a_read_model_with_defaults.js';
|
|
7
|
+
import { Address } from '../given/Address.js';
|
|
8
|
+
|
|
9
|
+
const should = chai.should();
|
|
10
|
+
|
|
11
|
+
describe('when restoring with partial array items', () => {
|
|
12
|
+
let result: Model;
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
result = deserializeReadModel(Model, '{"children":[{}, {"street":"Stored street","city":null}]}');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should replace the array default', () => result.children.should.have.lengthOf(2));
|
|
19
|
+
it('should restore the child type', () => result.children[0].should.be.instanceOf(Address));
|
|
20
|
+
it('should keep the absent child field default', () => result.children[0].street.should.equal('Default street'));
|
|
21
|
+
it('should replace the present child field default', () => result.children[1].street.should.equal('Stored street'));
|
|
22
|
+
it('should preserve the null child field', () => should.equal(result.children[1].city, null));
|
|
23
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { beforeEach, chai, describe, it } from 'vitest';
|
|
5
|
+
import { deserializeReadModel } from '../../deserializeReadModel.js';
|
|
6
|
+
import { Model } from '../given/a_read_model_with_defaults.js';
|
|
7
|
+
|
|
8
|
+
chai.should();
|
|
9
|
+
|
|
10
|
+
describe('when restoring with present fields', () => {
|
|
11
|
+
let result: Model;
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
result = deserializeReadModel(Model, '{"title":"Stored title","status":"borrowed"}');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should replace the null default', () => result.title!.should.equal('Stored title'));
|
|
18
|
+
it('should replace the non-null default', () => result.status.should.equal('borrowed'));
|
|
19
|
+
});
|
|
@@ -168,11 +168,11 @@ describe('fixture-backed unique scalar values', () => {
|
|
|
168
168
|
const results = scenario.results;
|
|
169
169
|
const unprovenKeys = ['!', '#', '%', '&', "'", '(', ')', '*', '+', ',', '/', ';', '<', '=', '>', '?', '[', ']', '^', '`', '~'];
|
|
170
170
|
const invalidContent: unknown[] = [1, 0, 1.5, undefined, [], {}, new Date(), 'É', 'e\u0301', '"', '\\', '\n'];
|
|
171
|
-
//
|
|
171
|
+
// Fundamentals preserves null in JSON, so checkedContent rejects it as unsupported string content.
|
|
172
172
|
for (const [value, operation] of [
|
|
173
173
|
...unprovenKeys.map(value => [value, 'artifacts.constraints']),
|
|
174
174
|
...invalidContent.map(value => [value, 'append.content']),
|
|
175
|
-
[null, 'append.
|
|
175
|
+
[null, 'append.content']
|
|
176
176
|
] as Array<[unknown, string]>) {
|
|
177
177
|
const bad = new OracleDomainText('valid');
|
|
178
178
|
Object.assign(bad, { key: value });
|
|
@@ -83,7 +83,7 @@ describe('when evaluating arithmetic with legacy model-bound decorators', () =>
|
|
|
83
83
|
it('should suppress aggregate-only AutoMap and restore it for mixed mappings', async () => {
|
|
84
84
|
const scenario = new ReadModelScenario(Totals, artifacts);
|
|
85
85
|
scenario.given.forEventSource('A').events(new Added(2));
|
|
86
|
-
(
|
|
86
|
+
(await scenario.instance)!.title.should.equal('');
|
|
87
87
|
scenario.given.forEventSource('A').events(new Renamed());
|
|
88
88
|
const result = (await scenario.instance)!;
|
|
89
89
|
[result.total, result.title, result.label].should.deep.equal([3, 'automapped', 'explicit']);
|
|
@@ -84,7 +84,7 @@ describe('when evaluating arithmetic with standard model-bound decorators', () =
|
|
|
84
84
|
it('should suppress aggregate-only AutoMap and restore it for mixed mappings', async () => {
|
|
85
85
|
const scenario = new ReadModelScenario(Totals, artifacts);
|
|
86
86
|
scenario.given.forEventSource('A').events(new Added(2));
|
|
87
|
-
should((await scenario.instance)!.title
|
|
87
|
+
should((await scenario.instance)!.title).equal('');
|
|
88
88
|
scenario.given.forEventSource('A').events(new Renamed());
|
|
89
89
|
const result = (await scenario.instance)!;
|
|
90
90
|
should([result.total, result.title, result.label]).deep.equal([3, 'automapped', 'explicit']);
|
package/testing/for_ReadModelScenario/when_evaluating_joins/with_a_fluent_projection.spec.ts
CHANGED
|
@@ -61,7 +61,7 @@ describe('when evaluating a fluent root join', () => {
|
|
|
61
61
|
scenario.given.forEventSource('customer').events(new CustomerNamed('name'));
|
|
62
62
|
(await scenario.instanceForEventSourceId('A'))!.customerName.should.equal('name');
|
|
63
63
|
(await scenario.instanceForEventSourceId('B'))!.customerName.should.equal('name');
|
|
64
|
-
(
|
|
64
|
+
(await scenario.instanceForEventSourceId('C'))!.customerName.should.equal('');
|
|
65
65
|
});
|
|
66
66
|
|
|
67
67
|
it('should retain joined history across removal without resurrecting the removed root', async () => {
|
|
@@ -95,7 +95,7 @@ describe('when evaluating a fluent root join', () => {
|
|
|
95
95
|
(await first.instance)!.customerName.should.equal('second');
|
|
96
96
|
const second = new ReadModelScenario(Order, artifacts);
|
|
97
97
|
second.given.forEventSource('A').events(new OrderPlaced('customer'));
|
|
98
|
-
(
|
|
98
|
+
(await second.instance)!.customerName.should.equal('');
|
|
99
99
|
});
|
|
100
100
|
|
|
101
101
|
it('should update the root keys when observing a committed event sequence', async () => {
|
package/testing/for_ReadModelScenario/when_evaluating_joins/with_legacy_decorators.legacy.spec.ts
CHANGED
|
@@ -47,8 +47,8 @@ describe('when evaluating joins with legacy model-bound decorators', () => {
|
|
|
47
47
|
const result = (await scenario.instance)!;
|
|
48
48
|
result.customerName.should.equal('before');
|
|
49
49
|
result.city.should.equal('Oslo');
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
result.name.should.equal('');
|
|
51
|
+
result.ignored.should.equal('');
|
|
52
52
|
});
|
|
53
53
|
|
|
54
54
|
it('should update existing roots and recreate a removed root from the latest joined history', async () => {
|
|
@@ -44,8 +44,8 @@ describe('when evaluating joins with standard model-bound decorators', () => {
|
|
|
44
44
|
const result = (await scenario.instance)!;
|
|
45
45
|
result.customerName.should.equal('before');
|
|
46
46
|
result.city.should.equal('Oslo');
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
result.name.should.equal('');
|
|
48
|
+
result.ignored.should.equal('');
|
|
49
49
|
});
|
|
50
50
|
|
|
51
51
|
it('should fan out subsequent join events to all matching roots', async () => {
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
// Copyright (c) Cratis. All rights reserved.
|
|
2
2
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
3
|
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
import {
|
|
5
|
+
EventGenerationDelivery,
|
|
6
|
+
type BasicAuthorization,
|
|
7
|
+
type BearerTokenAuthorization,
|
|
8
|
+
type OAuthAuthorization,
|
|
9
|
+
type OneOfBasicAuthorizationBearerTokenAuthorizationOAuthAuthorization,
|
|
10
|
+
type WebhookDefinition
|
|
10
11
|
} from '@cratis/chronicle.contracts';
|
|
11
12
|
import { Constructor } from '@cratis/fundamentals';
|
|
12
13
|
import { IEventTypes } from '../events/IEventTypes.js';
|
|
@@ -140,7 +141,8 @@ export class WebhookDefinitionBuilder implements IWebhookDefinitionBuilder {
|
|
|
140
141
|
IsReplayable: this._isReplayable,
|
|
141
142
|
IsActive: this._isActive,
|
|
142
143
|
IsNotReplayable: !this._isReplayable,
|
|
143
|
-
IsInactive: !this._isActive
|
|
144
|
+
IsInactive: !this._isActive,
|
|
145
|
+
GenerationDelivery: EventGenerationDelivery.Compatibility
|
|
144
146
|
};
|
|
145
147
|
}
|
|
146
148
|
}
|
package/webhooks/Webhooks.ts
CHANGED
|
@@ -104,7 +104,8 @@ export class Webhooks implements IWebhooks {
|
|
|
104
104
|
IsReplayable: webhook.IsReplayable,
|
|
105
105
|
IsActive: webhook.IsActive,
|
|
106
106
|
IsNotReplayable: !webhook.IsReplayable,
|
|
107
|
-
IsInactive: !webhook.IsActive
|
|
107
|
+
IsInactive: !webhook.IsActive,
|
|
108
|
+
GenerationDelivery: webhook.GenerationDelivery
|
|
108
109
|
}));
|
|
109
110
|
}
|
|
110
111
|
|