Skip to content

Commit 80d0bba

Browse files
Merge pull request #167 from castore-dev/prevent-reserved-event-types
patch: prevent reserved event types
2 parents 80c3a1f + 2b2773b commit 80d0bba

11 files changed

Lines changed: 101 additions & 39 deletions

File tree

packages/core/src/event/eventType.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { EventDetail } from './eventDetail';
2+
import { reservedEventTypes } from './reservedEventTypes';
23

34
export class EventType<
45
TYPE extends string = string,
@@ -22,6 +23,12 @@ export class EventType<
2223
};
2324

2425
constructor({ type }: { type: TYPE }) {
26+
if (reservedEventTypes.has(type)) {
27+
throw new Error(
28+
`${type} is a reserved event type. Please chose another one.`,
29+
);
30+
}
31+
2532
this.type = type;
2633
}
2734
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { EventType } from './eventType';
2+
import { __AGGREGATE_EXISTS__, __REPLAYED__ } from './reservedEventTypes';
3+
4+
const eventType = new EventType({ type: 'some type' });
5+
6+
describe('event store', () => {
7+
it('has correct properties', () => {
8+
expect(new Set(Object.keys(eventType))).toStrictEqual(new Set(['type']));
9+
});
10+
11+
it('raises an error if type is reserved', () => {
12+
expect(() => new EventType({ type: __REPLAYED__ })).toThrow();
13+
expect(() => new EventType({ type: __AGGREGATE_EXISTS__ })).toThrow();
14+
});
15+
});
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export const __REPLAYED__ = '__REPLAYED__';
2+
export type __REPLAYED__ = typeof __REPLAYED__;
3+
4+
export const __AGGREGATE_EXISTS__ = '__AGGREGATE_EXISTS__';
5+
export type __AGGREGATE_EXISTS__ = typeof __AGGREGATE_EXISTS__;
6+
7+
export const reservedEventTypes = new Set<string>([
8+
__REPLAYED__,
9+
__AGGREGATE_EXISTS__,
10+
]);

packages/core/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export type { Aggregate } from './aggregate';
22
export { EventType } from './event/eventType';
33
export type { EventTypeDetail, EventTypeDetails } from './event/eventType';
44
export { GroupedEvent } from './event/groupedEvent';
5+
export { __REPLAYED__, __AGGREGATE_EXISTS__ } from './event/reservedEventTypes';
56
export type { EventDetail, OptionalTimestamp } from './event/eventDetail';
67
export type { EventStorageAdapter } from './eventStorageAdapter';
78
export type {

packages/event-type-json-schema/src/eventType.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
1+
import type { FromSchema, JSONSchema } from 'json-schema-to-ts';
22

3-
import { EventDetail, EventType } from '@castore/core';
3+
import { EventType } from '@castore/core';
44

55
export class JSONSchemaEventType<
66
TYPE extends string = string,
@@ -20,12 +20,7 @@ export class JSONSchemaEventType<
2020
: METADATA_SCHEMA extends JSONSchema
2121
? FromSchema<METADATA_SCHEMA>
2222
: never,
23-
> implements EventType<TYPE, PAYLOAD, METADATA>
24-
{
25-
_types?: {
26-
detail: EventDetail<TYPE, PAYLOAD, METADATA>;
27-
};
28-
type: TYPE;
23+
> extends EventType<TYPE, PAYLOAD, METADATA> {
2924
payloadSchema?: PAYLOAD_SCHEMA;
3025
metadataSchema?: METADATA_SCHEMA;
3126

@@ -38,7 +33,7 @@ export class JSONSchemaEventType<
3833
payloadSchema?: PAYLOAD_SCHEMA;
3934
metadataSchema?: METADATA_SCHEMA;
4035
}) {
41-
this.type = type;
36+
super({ type });
4237
this.payloadSchema = payloadSchema;
4338
this.metadataSchema = metadataSchema;
4439
}

packages/event-type-json-schema/src/eventType.unit.test.ts

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
1-
import { FromSchema } from 'json-schema-to-ts';
2-
import { A } from 'ts-toolbelt';
1+
import type { FromSchema } from 'json-schema-to-ts';
2+
import type { A } from 'ts-toolbelt';
33

4-
import { EventTypeDetail } from '@castore/core';
4+
import type { EventTypeDetail } from '@castore/core';
55

66
import { JSONSchemaEventType } from './eventType';
77

8+
const expectedProperties = new Set([
9+
// applying super(...) apparently adds { _types: undefined, parseEventDetail: undefined } to the class
10+
'_types',
11+
'parseEventDetail',
12+
'type',
13+
'payloadSchema',
14+
'metadataSchema',
15+
]);
16+
817
describe('jsonSchemaEvent implementation', () => {
918
const type = 'SOMETHING_HAPPENED';
1019

@@ -51,7 +60,9 @@ describe('jsonSchemaEvent implementation', () => {
5160
> = 1;
5261
assertSimpleEventTypeDetail;
5362

54-
expect(Object.keys(simpleEventType)).toHaveLength(3);
63+
expect(new Set(Object.keys(simpleEventType))).toStrictEqual(
64+
expectedProperties,
65+
);
5566
expect(simpleEventType.type).toStrictEqual(type);
5667
expect(simpleEventType.payloadSchema).toStrictEqual(undefined);
5768
expect(simpleEventType.metadataSchema).toStrictEqual(undefined);
@@ -82,7 +93,9 @@ describe('jsonSchemaEvent implementation', () => {
8293
> = 1;
8394
assertPayloadEventTypeDetail;
8495

85-
expect(Object.keys(payloadEventType)).toHaveLength(3);
96+
expect(new Set(Object.keys(payloadEventType))).toStrictEqual(
97+
expectedProperties,
98+
);
8699
expect(payloadEventType.type).toStrictEqual(type);
87100
expect(payloadEventType.payloadSchema).toStrictEqual(payloadSchema);
88101
expect(payloadEventType.metadataSchema).toStrictEqual(undefined);
@@ -113,7 +126,9 @@ describe('jsonSchemaEvent implementation', () => {
113126
> = 1;
114127
assertMetadataEventTypeDetail;
115128

116-
expect(Object.keys(metadataEventType)).toHaveLength(3);
129+
expect(new Set(Object.keys(metadataEventType))).toStrictEqual(
130+
expectedProperties,
131+
);
117132
expect(metadataEventType.type).toStrictEqual(type);
118133
expect(metadataEventType.payloadSchema).toStrictEqual(undefined);
119134
expect(metadataEventType.metadataSchema).toStrictEqual(metadataSchema);
@@ -146,7 +161,9 @@ describe('jsonSchemaEvent implementation', () => {
146161
> = 1;
147162
assertFullEventTypeDetail;
148163

149-
expect(Object.keys(fullEventType)).toHaveLength(3);
164+
expect(new Set(Object.keys(fullEventType))).toStrictEqual(
165+
expectedProperties,
166+
);
150167
expect(fullEventType.type).toStrictEqual(type);
151168
expect(fullEventType.payloadSchema).toStrictEqual(payloadSchema);
152169
expect(fullEventType.metadataSchema).toStrictEqual(metadataSchema);

packages/event-type-zod/src/eventType.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { z, ZodType } from 'zod';
1+
import type { z, ZodType } from 'zod';
22

3-
import { EventDetail, EventType } from '@castore/core';
3+
import { EventType } from '@castore/core';
44

55
export class ZodEventType<
66
TYPE extends string = string,
@@ -20,12 +20,7 @@ export class ZodEventType<
2020
: METADATA_SCHEMA extends ZodType
2121
? z.infer<METADATA_SCHEMA>
2222
: never,
23-
> implements EventType<TYPE, PAYLOAD, METADATA>
24-
{
25-
_types?: {
26-
detail: EventDetail<TYPE, PAYLOAD, METADATA>;
27-
};
28-
type: TYPE;
23+
> extends EventType<TYPE, PAYLOAD, METADATA> {
2924
payloadSchema?: PAYLOAD_SCHEMA;
3025
metadataSchema?: METADATA_SCHEMA;
3126

@@ -38,7 +33,7 @@ export class ZodEventType<
3833
payloadSchema?: PAYLOAD_SCHEMA;
3934
metadataSchema?: METADATA_SCHEMA;
4035
}) {
41-
this.type = type;
36+
super({ type });
4237
this.payloadSchema = payloadSchema;
4338
this.metadataSchema = metadataSchema;
4439
}

packages/event-type-zod/src/eventType.unit.test.ts

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
1-
import { A } from 'ts-toolbelt';
1+
import type { A } from 'ts-toolbelt';
22
import { z } from 'zod';
33

4-
import { EventTypeDetail } from '@castore/core';
4+
import type { EventTypeDetail } from '@castore/core';
55

66
import { ZodEventType } from './eventType';
77

8+
const expectedProperties = new Set([
9+
// applying super(...) apparently adds { _types: undefined, parseEventDetail: undefined } to the class
10+
'_types',
11+
'parseEventDetail',
12+
'type',
13+
'payloadSchema',
14+
'metadataSchema',
15+
]);
16+
817
describe('zodEvent implementation', () => {
918
const type = 'SOMETHING_HAPPENED';
1019

@@ -34,7 +43,9 @@ describe('zodEvent implementation', () => {
3443
> = 1;
3544
assertSimpleEventTypeDetail;
3645

37-
expect(Object.keys(simpleEventType)).toHaveLength(3);
46+
expect(new Set(Object.keys(simpleEventType))).toStrictEqual(
47+
expectedProperties,
48+
);
3849
expect(simpleEventType.type).toStrictEqual(type);
3950
expect(simpleEventType.payloadSchema).toStrictEqual(undefined);
4051
expect(simpleEventType.metadataSchema).toStrictEqual(undefined);
@@ -59,7 +70,9 @@ describe('zodEvent implementation', () => {
5970
> = 1;
6071
assertPayloadEventTypeDetail;
6172

62-
expect(Object.keys(payloadEventType)).toHaveLength(3);
73+
expect(new Set(Object.keys(payloadEventType))).toStrictEqual(
74+
expectedProperties,
75+
);
6376
expect(payloadEventType.type).toStrictEqual(type);
6477
expect(payloadEventType.payloadSchema).toStrictEqual(payloadSchema);
6578
expect(payloadEventType.metadataSchema).toStrictEqual(undefined);
@@ -84,7 +97,9 @@ describe('zodEvent implementation', () => {
8497
> = 1;
8598
assertMetadataEventTypeDetail;
8699

87-
expect(Object.keys(metadataEventType)).toHaveLength(3);
100+
expect(new Set(Object.keys(metadataEventType))).toStrictEqual(
101+
expectedProperties,
102+
);
88103
expect(metadataEventType.type).toStrictEqual(type);
89104
expect(metadataEventType.payloadSchema).toStrictEqual(undefined);
90105
expect(metadataEventType.metadataSchema).toStrictEqual(metadataSchema);
@@ -114,7 +129,9 @@ describe('zodEvent implementation', () => {
114129
> = 1;
115130
assertFullEventTypeDetail;
116131

117-
expect(Object.keys(fullEventType)).toHaveLength(3);
132+
expect(new Set(Object.keys(fullEventType))).toStrictEqual(
133+
expectedProperties,
134+
);
118135
expect(fullEventType.type).toStrictEqual(type);
119136
expect(fullEventType.payloadSchema).toStrictEqual(payloadSchema);
120137
expect(fullEventType.metadataSchema).toStrictEqual(metadataSchema);

packages/message-bus-adapter-event-bridge/src/adapter.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import type {
1111
PublishMessageOptions,
1212
} from '@castore/core';
1313
import {
14+
__REPLAYED__,
15+
__AGGREGATE_EXISTS__,
1416
isAggregateExistsMessage,
1517
isEventCarryingMessage,
1618
} from '@castore/core';
@@ -22,11 +24,11 @@ const getDetailType = (
2224
{ replay = false }: PublishMessageOptions = {},
2325
): string => {
2426
if (isEventCarryingMessage(message)) {
25-
return replay ? '__REPLAYED__' : message.event.type;
27+
return replay ? __REPLAYED__ : message.event.type;
2628
}
2729

2830
if (isAggregateExistsMessage(message)) {
29-
return '__AGGREGATE_EXISTS__';
31+
return __AGGREGATE_EXISTS__;
3032
}
3133

3234
throw new Error('Unable to infer detail-type from message');

packages/message-bus-adapter-event-bridge/src/adapter.unit.test.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { mockClient } from 'aws-sdk-client-mock';
66
import type { A } from 'ts-toolbelt';
77

88
import type { Message, PublishMessageOptions } from '@castore/core';
9+
import { __REPLAYED__ } from '@castore/core';
910

1011
import {
1112
EventBridgeMessageBusAdapter,
@@ -89,7 +90,7 @@ describe('EventBridgeMessageBusAdapter', () => {
8990
{
9091
EventBusName: eventBusNameMock,
9192
Source: eventStoreIdMock,
92-
DetailType: '__REPLAYED__',
93+
DetailType: __REPLAYED__,
9394
Detail: JSON.stringify(messageMock),
9495
},
9596
],
@@ -128,13 +129,13 @@ describe('EventBridgeMessageBusAdapter', () => {
128129
{
129130
EventBusName: eventBusNameMock,
130131
Source: eventStoreIdMock,
131-
DetailType: '__REPLAYED__',
132+
DetailType: __REPLAYED__,
132133
Detail: JSON.stringify(messageMock),
133134
},
134135
{
135136
EventBusName: eventBusNameMock,
136137
Source: eventStoreIdMock,
137-
DetailType: '__REPLAYED__',
138+
DetailType: __REPLAYED__,
138139
Detail: JSON.stringify(otherMessageMock),
139140
},
140141
],

0 commit comments

Comments
 (0)