feat(workspace): add publishing workspace invite created

This commit is contained in:
Karol Sójko 2022-10-10 12:22:11 +02:00
parent 0ad605c906
commit 6f9683c41a
No known key found for this signature in database
GPG key ID: A50543BF560BDEB0
8 changed files with 120 additions and 1 deletions

View file

@ -8,4 +8,5 @@ export enum DomainEventService {
ApiGateway = 'api-gateway',
Files = 'files',
Scheduler = 'scheduler',
Workspace = 'workspace',
}

View file

@ -36,6 +36,8 @@ import { WorkspaceInviteRepositoryInterface } from '../Domain/Invite/WorkspaceIn
import { MySQLWorkspaceInviteRepository } from '../Infra/MySQL/MySQLWorkspaceInviteRepository'
import { WorkspaceInvite } from '../Domain/Invite/WorkspaceInvite'
import { InviteToWorkspace } from '../Domain/UseCase/InviteToWorkspace/InviteToWorkspace'
import { DomainEventFactory } from '../Domain/Event/DomainEventFactory'
import { DomainEventFactoryInterface } from '../Domain/Event/DomainEventFactoryInterface'
// eslint-disable-next-line @typescript-eslint/no-var-requires
const newrelicFormatter = require('@newrelic/winston-enricher')
@ -132,6 +134,7 @@ export class ContainerConfigLoader {
// Handlers
container.bind<UserRegisteredEventHandler>(TYPES.UserRegisteredEventHandler).to(UserRegisteredEventHandler)
// Services
container.bind<DomainEventFactoryInterface>(TYPES.DomainEventFactory).to(DomainEventFactory)
container.bind<TimerInterface>(TYPES.Timer).toConstantValue(new Timer())
container
.bind<TokenDecoderInterface<CrossServiceTokenData>>(TYPES.CrossServiceTokenDecoder)

View file

@ -36,6 +36,7 @@ const TYPES = {
DomainEventPublisher: Symbol.for('DomainEventPublisher'),
DomainEventSubscriberFactory: Symbol.for('DomainEventSubscriberFactory'),
DomainEventMessageHandler: Symbol.for('DomainEventMessageHandler'),
DomainEventFactory: Symbol.for('DomainEventFactory'),
}
export default TYPES

View file

@ -0,0 +1,44 @@
import 'reflect-metadata'
import { TimerInterface } from '@standardnotes/time'
import { DomainEventFactory } from './DomainEventFactory'
describe('DomainEventFactory', () => {
let timer: TimerInterface
const createFactory = () => new DomainEventFactory(timer)
beforeEach(() => {
timer = {} as jest.Mocked<TimerInterface>
timer.getTimestampInMicroseconds = jest.fn().mockReturnValue(1)
timer.getUTCDate = jest.fn().mockReturnValue(new Date(1))
})
it('should create a WORKSPACE_INVITE_CREATED event', () => {
expect(
createFactory().createWorkspaceInviteCreatedEvent({
inviterUuid: '1-2-3',
inviteeEmail: 'test@test.te',
inviteUuid: 'i-1-2-3',
workspaceUuid: 'w-1-2-3',
}),
).toEqual({
createdAt: expect.any(Date),
meta: {
correlation: {
userIdentifier: '1-2-3',
userIdentifierType: 'uuid',
},
origin: 'workspace',
},
payload: {
inviterUuid: '1-2-3',
inviteeEmail: 'test@test.te',
inviteUuid: 'i-1-2-3',
workspaceUuid: 'w-1-2-3',
},
type: 'WORKSPACE_INVITE_CREATED',
})
})
})

View file

@ -0,0 +1,32 @@
import { DomainEventService, WorkspaceInviteCreatedEvent } from '@standardnotes/domain-events'
import { TimerInterface } from '@standardnotes/time'
import { inject, injectable } from 'inversify'
import TYPES from '../../Bootstrap/Types'
import { DomainEventFactoryInterface } from './DomainEventFactoryInterface'
@injectable()
export class DomainEventFactory implements DomainEventFactoryInterface {
constructor(@inject(TYPES.Timer) private timer: TimerInterface) {}
createWorkspaceInviteCreatedEvent(dto: {
inviterUuid: string
inviteeEmail: string
inviteUuid: string
workspaceUuid: string
}): WorkspaceInviteCreatedEvent {
return {
type: 'WORKSPACE_INVITE_CREATED',
createdAt: this.timer.getUTCDate(),
meta: {
correlation: {
userIdentifier: dto.inviterUuid,
userIdentifierType: 'uuid',
},
origin: DomainEventService.Workspace,
},
payload: dto,
}
}
}

View file

@ -0,0 +1,10 @@
import { WorkspaceInviteCreatedEvent } from '@standardnotes/domain-events'
export interface DomainEventFactoryInterface {
createWorkspaceInviteCreatedEvent(dto: {
inviterUuid: string
inviteeEmail: string
inviteUuid: string
workspaceUuid: string
}): WorkspaceInviteCreatedEvent
}

View file

@ -4,12 +4,17 @@ import { TimerInterface } from '@standardnotes/time'
import { WorkspaceInviteRepositoryInterface } from '../../Invite/WorkspaceInviteRepositoryInterface'
import { InviteToWorkspace } from './InviteToWorkspace'
import { DomainEventFactoryInterface } from '../../Event/DomainEventFactoryInterface'
import { DomainEventPublisherInterface, WorkspaceInviteCreatedEvent } from '@standardnotes/domain-events'
describe('InviteToWorkspace', () => {
let workspaceInviteRepository: WorkspaceInviteRepositoryInterface
let timer: TimerInterface
let domainEventFactory: DomainEventFactoryInterface
let domainEventPublisher: DomainEventPublisherInterface
const createUseCase = () => new InviteToWorkspace(workspaceInviteRepository, timer)
const createUseCase = () =>
new InviteToWorkspace(workspaceInviteRepository, timer, domainEventFactory, domainEventPublisher)
beforeEach(() => {
workspaceInviteRepository = {} as jest.Mocked<WorkspaceInviteRepositoryInterface>
@ -22,6 +27,14 @@ describe('InviteToWorkspace', () => {
timer = {} as jest.Mocked<TimerInterface>
timer.getTimestampInMicroseconds = jest.fn().mockReturnValue(1)
domainEventPublisher = {} as jest.Mocked<DomainEventPublisherInterface>
domainEventPublisher.publish = jest.fn()
domainEventFactory = {} as jest.Mocked<DomainEventFactoryInterface>
domainEventFactory.createWorkspaceInviteCreatedEvent = jest
.fn()
.mockReturnValue({} as jest.Mocked<WorkspaceInviteCreatedEvent>)
})
it('should create an invite', async () => {
@ -41,5 +54,7 @@ describe('InviteToWorkspace', () => {
createdAt: 1,
updatedAt: 1,
})
expect(domainEventPublisher.publish).toHaveBeenCalled()
})
})

View file

@ -1,7 +1,9 @@
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
import { TimerInterface } from '@standardnotes/time'
import { inject, injectable } from 'inversify'
import TYPES from '../../../Bootstrap/Types'
import { DomainEventFactoryInterface } from '../../Event/DomainEventFactoryInterface'
import { WorkspaceInvite } from '../../Invite/WorkspaceInvite'
import { WorkspaceInviteRepositoryInterface } from '../../Invite/WorkspaceInviteRepositoryInterface'
import { WorkspaceInviteStatus } from '../../Invite/WorkspaceInviteStatus'
@ -15,6 +17,8 @@ export class InviteToWorkspace implements UseCaseInterface {
constructor(
@inject(TYPES.WorkspaceInviteRepository) private workspaceInviteRepository: WorkspaceInviteRepositoryInterface,
@inject(TYPES.Timer) private timer: TimerInterface,
@inject(TYPES.DomainEventFactory) private domainEventFactory: DomainEventFactoryInterface,
@inject(TYPES.DomainEventPublisher) private domainEventPublisher: DomainEventPublisherInterface,
) {}
async execute(dto: InviteToWorkspaceDTO): Promise<InviteToWorkspaceResponse> {
@ -30,6 +34,15 @@ export class InviteToWorkspace implements UseCaseInterface {
invite = await this.workspaceInviteRepository.save(invite)
await this.domainEventPublisher.publish(
this.domainEventFactory.createWorkspaceInviteCreatedEvent({
inviterUuid: dto.inviterUuid,
inviteeEmail: dto.inviteeEmail,
workspaceUuid: dto.workspaceUuid,
inviteUuid: invite.uuid,
}),
)
return {
uuid: invite.uuid,
}