Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle arrays of event spreading in EventStore.pushEventGroup #177

Open
jeremycare opened this issue Jan 10, 2024 · 0 comments
Open

Handle arrays of event spreading in EventStore.pushEventGroup #177

jeremycare opened this issue Jan 10, 2024 · 0 comments

Comments

@jeremycare
Copy link

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;
Screenshot 2024-01-10 at 12 15 35

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant