Handle arrays of event spreading in EventStore.pushEventGroup
jeremycare opened this issue · 0 comments
jeremycare commented
Describe the bug
A clear and concise description of what the bug is.
We are caching the events before sending them to the eventStore.pushGroupEvent
Our Encapsulation of Castore
import {
Reducer,
EventStore as CastoreEventStoreImpl,
EventType,
EventDetail,
EventTypeDetails,
$Contravariant,
Aggregate,
EventStorageAdapter,
OptionalTimestamp,
} from '@castore/core';
import { inject } from '@warehouse/di-container';
import { EventStore } from '../EventStore';
import { AggregateRoot } from '../AggregateRoot';
import DateAdapterImpl, { DateAdapter } from '../DateAdapter';
import { storageAdapterToken } from '../StorageAdapters/StorageAdapter';
export class EventStoreImpl<
EVENT_STORE_ID extends string = string,
EVENT_TYPES extends EventType[] = EventType[],
EVENT_DETAILS extends EventDetail = EventTypeDetails<EVENT_TYPES>,
$EVENT_DETAILS extends EventDetail = $Contravariant<EVENT_DETAILS, EventDetail>,
REDUCER extends Reducer<Aggregate, $EVENT_DETAILS> = Reducer<Aggregate, $EVENT_DETAILS>,
AGGREGATE extends Aggregate = ReturnType<REDUCER>,
$AGGREGATE extends Aggregate = $Contravariant<AGGREGATE, Aggregate>,
AGGREGATE_CLASS extends AggregateRoot<AGGREGATE, EVENT_DETAILS> = AggregateRoot<AGGREGATE, EVENT_DETAILS>,
> implements EventStore<AGGREGATE, EVENT_DETAILS, AGGREGATE_CLASS> {
private castoreEventStore: CastoreEventStoreImpl<EVENT_STORE_ID, EVENT_TYPES, EVENT_DETAILS, $EVENT_DETAILS, REDUCER, AGGREGATE, $AGGREGATE>;
private readonly factory: (state: AGGREGATE) => AGGREGATE_CLASS;
private readonly dateAdapter: DateAdapter;
constructor({
eventStoreId, eventTypes, mutate, eventStorageAdapter = inject(storageAdapterToken), factory, dateAdapter = new DateAdapterImpl(),
}: {
eventStoreId: EVENT_STORE_ID;
eventTypes: EVENT_TYPES;
mutate: REDUCER;
eventStorageAdapter?: EventStorageAdapter;
factory: (state: AGGREGATE) => AGGREGATE_CLASS;
dateAdapter?: DateAdapter;
}) {
this.castoreEventStore = new CastoreEventStoreImpl({
eventStoreId, eventTypes, reducer: mutate, eventStorageAdapter,
});
this.factory = factory;
this.dateAdapter = dateAdapter;
}
public async getState(id: string): Promise<AGGREGATE | undefined> {
const state = await this.castoreEventStore.getAggregate(id);
return state.aggregate;
}
public async getAggregate(id: string): Promise<AGGREGATE_CLASS | undefined> {
const state = await this.castoreEventStore.getAggregate(id);
if (!state.aggregate) {
return undefined;
}
return this.factory(state.aggregate);
}
public async getEvents(id: string): Promise<EVENT_DETAILS[]> {
const aggregate = await this.castoreEventStore.getAggregate(id);
return aggregate.events;
}
public async saveEvents(events: OptionalTimestamp<EVENT_DETAILS>[]): Promise<void> {
if (events.length === 0) {
return;
}
const eventsWithTimestamp = events.map((event) => this.assignTimestamp(event));
// as OptionalTimestamp cast because of a weird typescript issue where EVENT_DETAILS is not recognized as a valid OptionalTimestamp<EVENT_DETAILS>
const groupedEvents = eventsWithTimestamp.map((event) => this.castoreEventStore.groupEvent(event as EVENT_DETAILS));
await CastoreEventStoreImpl.pushEventGroup({}, ...groupedEvents);
}
public async save(aggregate: AGGREGATE_CLASS): Promise<void> {
const events = aggregate.popChanges();
await this.saveEvents(events);
}
private assignTimestamp(event: OptionalTimestamp<EVENT_DETAILS>): EVENT_DETAILS {
if (event.timestamp) {
return event;
}
return { ...event, timestamp: this.dateAdapter.now() };
}
}
To Reproduce
const pushArrayPokemonsEventGroupWithOptions = () => {
const arrayOfPokemonsEvent = [
pokemonsEventStore.groupEvent(pikachuAppearedEvent),
pokemonsEventStore.groupEvent(pikachuCaughtEvent),
];
return EventStore.pushEventGroup(
{ force: true },
...arrayOfPokemonsEvent,
);
};
const assertPushArrayPokemonsEventGroupWithOptions: A.Equals<
Awaited<ReturnType<typeof pushArrayPokemonsEventGroupWithOptions>>,
{
eventGroup: [
{ event: PokemonEventDetails; nextAggregate?: PokemonAggregate },
{ event: PokemonEventDetails; nextAggregate?: PokemonAggregate },
];
}
> = 1;
assertPushArrayPokemonsEventGroupWithOptions;

Expected behavior
Typescript should understand that we have a rest parameter, but it doesn't. It wasn't like that before upgrading to v2. I suspect it to be related to the addition of the option parameter.
Additional context
Add any other context about the problem here.