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

Publish events #623

Merged
merged 8 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 33 additions & 2 deletions apps/api/src/app/controllers/__tests__/createTestModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import { MockJwtAuthGuard } from '../../../authorization/mock-jwt-auth-guard';
import { MockJwtStrategy } from '../../../authorization/mock-jwt.strategy';
import { OptionalJwtAuthGuard } from '../../../authorization/optional-jwt-auth-guard';
import { ConsoleCoscradCliLogger } from '../../../coscrad-cli/logging';
import { CoscradEventFactory, CoscradEventUnion } from '../../../domain/common';
import { CoscradEventFactory, CoscradEventUnion, EventModule } from '../../../domain/common';
import { ObservableInMemoryEventPublisher } from '../../../domain/common/events/in-memory-event-publisher';
import { EVENT_PUBLISHER_TOKEN } from '../../../domain/common/events/interfaces';
import { SyncInMemoryEventPublisher } from '../../../domain/common/events/sync-in-memory-event-publisher';
import { ID_MANAGER_TOKEN } from '../../../domain/interfaces/id-manager.interface';
import { AudioItemController } from '../../../domain/models/audio-visual/application/audio-item.controller';
import { VideoController } from '../../../domain/models/audio-visual/application/video.controller';
Expand Down Expand Up @@ -143,8 +146,12 @@ import {
UnpublishResource,
UnpublishResourceCommandHandler,
} from '../../../domain/models/shared/common-commands';
import { ResourceReadAccessGrantedToUserEventHandler } from '../../../domain/models/shared/common-commands/grant-resource-read-access-to-user/resource-read-access-granted-to-user.event-handler';
import { ResourcePublished } from '../../../domain/models/shared/common-commands/publish-resource/resource-published.event';
import { IQueryRepositoryProvider } from '../../../domain/models/shared/common-commands/publish-resource/resource-published.event-handler';
import {
IQueryRepositoryProvider,
ResourcePublishedEventHandler,
} from '../../../domain/models/shared/common-commands/publish-resource/resource-published.event-handler';
import {
AddLyricsForSong,
AddLyricsForSongCommandHandler,
Expand Down Expand Up @@ -187,10 +194,15 @@ import {
PromptTermCreated,
TermCreated,
TermElicitedFromPrompt,
TermElicitedFromPromptEventHandler,
TermTranslated,
TranslateTerm,
TranslateTermCommandHandler,
} from '../../../domain/models/term/commands';
import { AudioAddedForTermEventHandler } from '../../../domain/models/term/commands/add-audio-for-term/audio-added-for-term.event-handler';
import { PromptTermCreatedEventHandler } from '../../../domain/models/term/commands/create-prompt-term/prompt-term-created.event-handler';
import { TermCreatedEventHandler } from '../../../domain/models/term/commands/create-term/term-created.event-handler';
import { TermTranslatedEventHandler } from '../../../domain/models/term/commands/translate-term/term-translated.event-handler';
import { Term } from '../../../domain/models/term/entities/term.entity';
import {
ITermQueryRepository,
Expand Down Expand Up @@ -380,6 +392,7 @@ export default async (
CommandModule,
PassportModule.register({ defaultStrategy: 'jwt' }),
DynamicDataTypeModule,
EventModule,
],
providers: [
CommandInfoService,
Expand All @@ -391,6 +404,10 @@ export default async (
buildConfigFilePath(Environment.test)
),
},
{
provide: EVENT_PUBLISHER_TOKEN,
useValue: new SyncInMemoryEventPublisher(new ConsoleCoscradCliLogger()),
},
{
provide: ArangoConnectionProvider,
useFactory: async (configService: ConfigService) => {
Expand Down Expand Up @@ -674,7 +691,13 @@ export default async (
DynamicDataTypeFinderService,
],
},
{
provide: 'COSCRAD_EVENT_PUBLISHER',
useFactory: () =>
new ObservableInMemoryEventPublisher(new ConsoleCoscradCliLogger()),
},
...dataClassProviders,

/**
* TODO [https://www.pivotaltracker.com/story/show/182576828]
*
Expand Down Expand Up @@ -799,6 +822,14 @@ export default async (
AddContentToDigitalTextPageCommandHandler,
CreatePhotograph,
CreatePhotographCommandHandler,
// Event Handlers
ResourcePublishedEventHandler,
ResourceReadAccessGrantedToUserEventHandler,
TermCreatedEventHandler,
TermTranslatedEventHandler,
PromptTermCreatedEventHandler,
TermElicitedFromPromptEventHandler,
AudioAddedForTermEventHandler,
],

controllers: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { CommandHandlerService, FluxStandardAction } from '@coscrad/commands';
import { CoscradUserRole } from '@coscrad/data-types';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { ConsoleCoscradCliLogger } from '../../../coscrad-cli/logging';
import getValidAggregateInstanceForTest from '../../../domain/__tests__/utilities/getValidAggregateInstanceForTest';
import { ObservableInMemoryEventPublisher } from '../../../domain/common/events/in-memory-event-publisher';
import { IIdManager } from '../../../domain/interfaces/id-manager.interface';
import { buildFakeTimersConfig } from '../../../domain/models/__tests__/utilities/buildFakeTimersConfig';
import { CreateSong } from '../../../domain/models/song/commands/create-song.command';
Expand Down Expand Up @@ -80,7 +82,11 @@ describe('The Command Controller', () => {
*/
commandHandlerService.registerHandler(
'CREATE_SONG',
new CreateSongCommandHandler(testRepositoryProvider, idManager)
new CreateSongCommandHandler(
testRepositoryProvider,
idManager,
new ObservableInMemoryEventPublisher(new ConsoleCoscradCliLogger())
)
);

jest.useFakeTimers(buildFakeTimersConfig());
Expand Down
10 changes: 4 additions & 6 deletions apps/api/src/domain/common/events/event.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@ import { SyncInMemoryEventPublisher } from './sync-in-memory-event-publisher';
{
provide: EVENT_PUBLISHER_TOKEN,
useFactory: () => new SyncInMemoryEventPublisher(new ConsoleCoscradCliLogger()),
// new InMemoryEventPublisher(
// // TODO We need to work on our logging strategy
// new ConsoleCoscradCliLogger()
// ),
},
],
exports: [CoscradEventFactory, EVENT_PUBLISHER_TOKEN],
Expand All @@ -45,13 +41,15 @@ export class EventModule {
) {}

onApplicationBootstrap() {
const handlersAndTypes = this.discoveryService
const allInstancesAndMeta = this.discoveryService
.getProviders()
.filter((provider) => provider.instance)
.map((provider) => [
provider.instance,
getCoscradEventConsumerMeta(Object.getPrototypeOf(provider.instance).constructor),
])
]);

const handlersAndTypes = allInstancesAndMeta
.filter(
(
instanceAndMeta
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ export class ObservableInMemoryEventPublisher
super();
}

// todo make this async
publish(eventOrEvents: ICoscradEvent<unknown> | ICoscradEvent<unknown>[]): void {
const eventsToPublish = Array.isArray(eventOrEvents) ? eventOrEvents : [eventOrEvents];

Expand Down Expand Up @@ -56,7 +55,11 @@ export class ObservableInMemoryEventPublisher
}

private ofEventType(type: string) {
return this.subject$.pipe(filter((event) => event.isOfType(type)));
return this.subject$.pipe(
filter((event) => {
return event.isOfType(type);
})
);
}

onModuleDestroy() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { ICoscradEventHandler } from '../coscrad-event-handler.interface';
import { ICoscradEvent } from '../coscrad-event.interface';

export const EVENT_PUBLISHER_TOKEN = 'EVENT_PUBLISHER_TOKEN';

export interface ICoscradEventPublisher {
publish(eventsToPublish: ICoscradEvent | ICoscradEvent[]): void;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,19 @@ export class SyncInMemoryEventPublisher implements ICoscradEventPublisher {
this.eventTypeToConsumers.set(eventType, []);
}

this.eventTypeToConsumers.get(eventType).push(eventConsumer);
/**
* We want registration to be idempotent in case this module is somehow
aaron-plahn marked this conversation as resolved.
Show resolved Hide resolved
* initialized multiple times.
*/
if (!this.has(eventType, eventConsumer))
this.eventTypeToConsumers.get(eventType).push(eventConsumer);
}

private has(eventType: string, eventConsumer: ICoscradEventHandler) {
return (
this.eventTypeToConsumers.has(eventType) &&
// Compare by reference
this.eventTypeToConsumers.get(eventType).some((handler) => handler === eventConsumer)
);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { CommandHandler } from '@coscrad/commands';
import { Inject } from '@nestjs/common';
import {
EVENT_PUBLISHER_TOKEN,
ICoscradEventPublisher,
} from '../../../../../../../domain/common/events/interfaces';
import { InternalError } from '../../../../../../../lib/errors/InternalError';
import { isNotFound } from '../../../../../../../lib/types/not-found';
import { REPOSITORY_PROVIDER_TOKEN } from '../../../../../../../persistence/constants/persistenceConstants';
Expand Down Expand Up @@ -31,9 +35,10 @@ export class AddLineItemtoTranscriptCommandHandler extends BaseCommandHandler<Tr
constructor(
@Inject(REPOSITORY_PROVIDER_TOKEN)
protected readonly repositoryProvider: IRepositoryProvider,
@Inject(ID_MANAGER_TOKEN) protected readonly idManager: IIdManager
@Inject(ID_MANAGER_TOKEN) protected readonly idManager: IIdManager,
@Inject(EVENT_PUBLISHER_TOKEN) protected readonly eventPublisher: ICoscradEventPublisher
) {
super(repositoryProvider, idManager);
super(repositoryProvider, idManager, eventPublisher);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, this is unfortunate.

}

protected fetchRequiredExternalState(): Promise<InMemorySnapshot> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { CommandHandler } from '@coscrad/commands';
import { Inject } from '@nestjs/common';
import { EVENT_PUBLISHER_TOKEN } from '../../../../../../../domain/common';
import { ICoscradEventPublisher } from '../../../../../../../domain/common/events/interfaces';
import { InternalError } from '../../../../../../../lib/errors/InternalError';
import { isNotFound } from '../../../../../../../lib/types/not-found';
import { REPOSITORY_PROVIDER_TOKEN } from '../../../../../../../persistence/constants/persistenceConstants';
Expand Down Expand Up @@ -34,9 +36,10 @@ export class AddParticipantToTranscriptCommandHandler extends BaseCommandHandler
constructor(
@Inject(REPOSITORY_PROVIDER_TOKEN)
protected readonly repositoryProvider: IRepositoryProvider,
@Inject(ID_MANAGER_TOKEN) protected readonly idManager: IIdManager
@Inject(ID_MANAGER_TOKEN) protected readonly idManager: IIdManager,
@Inject(EVENT_PUBLISHER_TOKEN) protected readonly eventPublisher: ICoscradEventPublisher
) {
super(repositoryProvider, idManager);
super(repositoryProvider, idManager, eventPublisher);
}

protected async createOrFetchWriteContext({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { CommandHandler } from '@coscrad/commands';
import { Inject } from '@nestjs/common';
import { EVENT_PUBLISHER_TOKEN } from '../../../../../../../domain/common';
import { ICoscradEventPublisher } from '../../../../../../../domain/common/events/interfaces';
import { InternalError } from '../../../../../../../lib/errors/InternalError';
import { isNotFound } from '../../../../../../../lib/types/not-found';
import { REPOSITORY_PROVIDER_TOKEN } from '../../../../../../../persistence/constants/persistenceConstants';
Expand Down Expand Up @@ -28,9 +30,10 @@ export class CreateTranscriptCommandHandler extends BaseCommandHandler<AudioItem
constructor(
@Inject(REPOSITORY_PROVIDER_TOKEN)
protected readonly repositoryProvider: IRepositoryProvider,
@Inject(ID_MANAGER_TOKEN) protected readonly idManager: IIdManager
@Inject(ID_MANAGER_TOKEN) protected readonly idManager: IIdManager,
@Inject(EVENT_PUBLISHER_TOKEN) protected readonly eventPublisher: ICoscradEventPublisher
) {
super(repositoryProvider, idManager);
super(repositoryProvider, idManager, eventPublisher);
}

protected async createOrFetchWriteContext({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { Inject } from '@nestjs/common';
import { EVENT_PUBLISHER_TOKEN } from '../../../../../domain/common';
import { ICoscradEventPublisher } from '../../../../../domain/common/events/interfaces';
import { InternalError } from '../../../../../lib/errors/InternalError';
import { REPOSITORY_PROVIDER_TOKEN } from '../../../../../persistence/constants/persistenceConstants';
import { Valid } from '../../../../domainModelValidators/Valid';
Expand All @@ -23,9 +25,10 @@ export abstract class BaseCreateBibliographicCitation extends BaseCreateCommandH
constructor(
@Inject(REPOSITORY_PROVIDER_TOKEN)
protected readonly repositoryProvider: IRepositoryProvider,
@Inject('ID_MANAGER') protected readonly idManager: IIdManager
@Inject('ID_MANAGER') protected readonly idManager: IIdManager,
@Inject(EVENT_PUBLISHER_TOKEN) protected readonly eventPublisher: ICoscradEventPublisher
) {
super(repositoryProvider, idManager);
super(repositoryProvider, idManager, eventPublisher);

this.repositoryForCommandsTargetAggregate = this.repositoryProvider.forResource(
this.aggregateType
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { CommandHandler } from '@coscrad/commands';
import { Inject } from '@nestjs/common';
import { EVENT_PUBLISHER_TOKEN } from '../../../../../../domain/common';
import { ICoscradEventPublisher } from '../../../../../../domain/common/events/interfaces';
import { REPOSITORY_PROVIDER_TOKEN } from '../../../../../../persistence/constants/persistenceConstants';
import { DTO } from '../../../../../../types/DTO';
import { ResultOrError } from '../../../../../../types/ResultOrError';
Expand All @@ -23,9 +25,10 @@ export class CreateCourtCaseBibliographicCitationCommandHandler extends BaseCrea
constructor(
@Inject(REPOSITORY_PROVIDER_TOKEN)
protected readonly repositoryProvider: IRepositoryProvider,
@Inject('ID_MANAGER') protected readonly idManager: IIdManager
@Inject('ID_MANAGER') protected readonly idManager: IIdManager,
@Inject(EVENT_PUBLISHER_TOKEN) protected readonly eventPublisher: ICoscradEventPublisher
) {
super(repositoryProvider, idManager);
super(repositoryProvider, idManager, eventPublisher);

this.repositoryForCommandsTargetAggregate = this.repositoryProvider.forResource(
this.aggregateType
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { CommandHandler } from '@coscrad/commands';
import { Inject } from '@nestjs/common';
import { EVENT_PUBLISHER_TOKEN } from '../../../../../domain/common';
import { ICoscradEventPublisher } from '../../../../../domain/common/events/interfaces';
import { REPOSITORY_PROVIDER_TOKEN } from '../../../../../persistence/constants/persistenceConstants';
import { DTO } from '../../../../../types/DTO';
import { ResultOrError } from '../../../../../types/ResultOrError';
Expand All @@ -24,9 +26,10 @@ export class CreateJournalArticleBibliographicCitationCommandHandler extends Bas
constructor(
@Inject(REPOSITORY_PROVIDER_TOKEN)
protected readonly repositoryProvider: IRepositoryProvider,
@Inject('ID_MANAGER') protected readonly idManager: IIdManager
@Inject('ID_MANAGER') protected readonly idManager: IIdManager,
@Inject(EVENT_PUBLISHER_TOKEN) protected readonly eventPublisher: ICoscradEventPublisher
) {
super(repositoryProvider, idManager);
super(repositoryProvider, idManager, eventPublisher);

this.repositoryForCommandsTargetAggregate = this.repositoryProvider.forResource(
this.aggregateType
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { ResourceType } from '@coscrad/api-interfaces';
import { CommandHandler } from '@coscrad/commands';
import { Inject } from '@nestjs/common';
import { EVENT_PUBLISHER_TOKEN } from '../../../../../domain/common';
import { ICoscradEventPublisher } from '../../../../../domain/common/events/interfaces';
import { Valid } from '../../../../../domain/domainModelValidators/Valid';
import {
ID_MANAGER_TOKEN,
Expand All @@ -27,9 +29,10 @@ export class AddAudioItemToPlaylistCommandHandler extends BaseUpdateCommandHandl
constructor(
@Inject(REPOSITORY_PROVIDER_TOKEN)
protected readonly repositoryProvider: IRepositoryProvider,
@Inject(ID_MANAGER_TOKEN) protected readonly idManager: IIdManager
@Inject(ID_MANAGER_TOKEN) protected readonly idManager: IIdManager,
@Inject(EVENT_PUBLISHER_TOKEN) protected readonly eventPublisher: ICoscradEventPublisher
) {
super(repositoryProvider, idManager);
super(repositoryProvider, idManager, eventPublisher);
}

protected actOnInstance(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import { InternalError, isInternalError } from '../../../../lib/errors/InternalE
import { REPOSITORY_PROVIDER_TOKEN } from '../../../../persistence/constants/persistenceConstants';
import { DTO } from '../../../../types/DTO';
import { ResultOrError } from '../../../../types/ResultOrError';
import { EVENT_PUBLISHER_TOKEN } from '../../../common';
import { buildMultilingualTextWithSingleItem } from '../../../common/build-multilingual-text-with-single-item';
import { ICoscradEventPublisher } from '../../../common/events/interfaces';
import { Valid } from '../../../domainModelValidators/Valid';
import getInstanceFactoryForResource from '../../../factories/get-instance-factory-for-resource';
import { IIdManager } from '../../../interfaces/id-manager.interface';
Expand Down Expand Up @@ -36,9 +38,10 @@ export class CreatePlayListCommandHandler extends BaseCreateCommandHandler<Playl
@Inject(REPOSITORY_PROVIDER_TOKEN)
protected readonly repositoryProvider: IRepositoryProvider,
// TODO export a constant for ID manager token
@Inject('ID_MANAGER') protected readonly idManager: IIdManager
@Inject('ID_MANAGER') protected readonly idManager: IIdManager,
@Inject(EVENT_PUBLISHER_TOKEN) protected readonly eventPublisher: ICoscradEventPublisher
) {
super(repositoryProvider, idManager);
super(repositoryProvider, idManager, eventPublisher);

this.repositoryForCommandsTargetAggregate = this.repositoryProvider.forResource<Playlist>(
ResourceType.playlist
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { CommandHandler } from '@coscrad/commands';
import { Inject } from '@nestjs/common';
import { EVENT_PUBLISHER_TOKEN } from '../../../../../domain/common';
import {
MultilingualTextItem,
MultilingualTextItemRole,
} from '../../../../../domain/common/entities/multilingual-text';
import { ICoscradEventPublisher } from '../../../../../domain/common/events/interfaces';
import { Valid } from '../../../../../domain/domainModelValidators/Valid';
import {
ID_MANAGER_TOKEN,
Expand All @@ -27,9 +29,10 @@ export class TranslatePlaylistNameCommandHandler extends BaseUpdateCommandHandle
constructor(
@Inject(REPOSITORY_PROVIDER_TOKEN)
protected readonly repositoryProvider: IRepositoryProvider,
@Inject(ID_MANAGER_TOKEN) protected readonly idManager: IIdManager
@Inject(ID_MANAGER_TOKEN) protected readonly idManager: IIdManager,
@Inject(EVENT_PUBLISHER_TOKEN) protected readonly eventPublisher: ICoscradEventPublisher
) {
super(repositoryProvider, idManager);
super(repositoryProvider, idManager, eventPublisher);
}

// note that we don't prevent duplication in translation of a name, only the orignal
Expand Down
Loading