-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(api): add rescue station manager (#860)
- Loading branch information
1 parent
3a9c0b0
commit 83cc58f
Showing
64 changed files
with
3,353 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,8 @@ | ||
export * from './lib/protocol.module'; | ||
export { CreateRescueStationSignOnMessageCommand } from './lib/core/command/rescue-station/create-rescue-station-sign-on-message.command'; | ||
export { CreateRescueStationSignOffMessageCommand } from './lib/core/command/rescue-station/create-rescue-station-sign-off-message.command'; | ||
export { CreateRescueStationUpdateMessageCommand } from './lib/core/command/rescue-station/create-rescue-station-update-message.command'; | ||
export { BaseCreateMessageArgs } from './lib/infra/controller/base-create-message.args'; | ||
export { MessageUnit } from './lib/core/entity/partials/unit-partial.entity'; | ||
export { MessageCommandRescueStationDetails } from './lib/core/command/rescue-station/message-command-rescue-station-details.model'; | ||
export { UnitInput } from './lib/infra/view-model/unit-input.view-model'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
125 changes: 125 additions & 0 deletions
125
libs/api/protocol/src/lib/core/command/helper/rescue-station-message.factory.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
import { plainToClass, plainToInstance } from 'class-transformer'; | ||
|
||
import { AuthUser } from '@kordis/shared/model'; | ||
|
||
import { UserProducer } from '../../entity/partials/producer-partial.entity'; | ||
import { | ||
RegisteredUnit, | ||
UnknownUnit, | ||
} from '../../entity/partials/unit-partial.entity'; | ||
import { RescueStationMessagePayload } from '../../entity/protocol-entries/rescue-station/rescue-station-message-payload.entity'; | ||
import { RescueStationSignOnMessage } from '../../entity/protocol-entries/rescue-station/rescue-station-sign-on-message.entity'; | ||
import { RescueStationUpdateMessage } from '../../entity/protocol-entries/rescue-station/rescue-station-update-message.entity'; | ||
import { CreateRescueStationSignOnMessageCommand } from '../rescue-station/create-rescue-station-sign-on-message.command'; | ||
import { CreateRescueStationUpdateMessageCommand } from '../rescue-station/create-rescue-station-update-message.command'; | ||
import { RescueStationMessageFactory } from './rescue-station-message.factory'; | ||
|
||
const RESCUE_STATION_DETAILS = Object.freeze({ | ||
id: 'rescueStationId', | ||
name: 'rescueStationName', | ||
callSign: 'rescueStationCallSign', | ||
strength: { | ||
leaders: 1, | ||
subLeaders: 1, | ||
helpers: 1, | ||
}, | ||
units: [{ name: 'unitName1', callSign: 'unitCallSign1', id: 'unitId1' }], | ||
alertGroups: [ | ||
{ | ||
id: 'alertGroupId', | ||
name: 'alertGroupName', | ||
units: [{ name: 'unitName2', callSign: 'unitCallSign2', id: 'unitId2' }], | ||
}, | ||
], | ||
}); | ||
const AUTH_USER = Object.freeze({ | ||
id: 'userId', | ||
organizationId: 'organizationId', | ||
firstName: 'firstName', | ||
lastName: 'lastName', | ||
} as AuthUser); | ||
const SENDING_TIME = new Date(); | ||
const MESSAGE = Object.freeze({ | ||
orgId: 'organizationId', | ||
time: SENDING_TIME, | ||
sender: plainToInstance(RegisteredUnit, { | ||
unit: { id: 'knownSenderUnit' }, | ||
}), | ||
recipient: plainToInstance(UnknownUnit, { name: 'unknownReceivingUnit' }), | ||
channel: 'channel', | ||
producer: plainToInstance(UserProducer, { | ||
userId: 'userId', | ||
firstName: 'firstName', | ||
lastName: 'lastName', | ||
}), | ||
payload: plainToClass(RescueStationMessagePayload, { | ||
rescueStationId: 'rescueStationId', | ||
rescueStationName: 'rescueStationName', | ||
rescueStationCallSign: 'rescueStationCallSign', | ||
strength: { | ||
leaders: 1, | ||
subLeaders: 1, | ||
helpers: 1, | ||
}, | ||
units: [{ name: 'unitName1', callSign: 'unitCallSign1', id: 'unitId1' }], | ||
alertGroups: [ | ||
{ | ||
id: 'alertGroupId', | ||
name: 'alertGroupName', | ||
units: [ | ||
{ name: 'unitName2', callSign: 'unitCallSign2', id: 'unitId2' }, | ||
], | ||
}, | ||
], | ||
}), | ||
}); | ||
|
||
describe('RescueStationMessageFactory', () => { | ||
let factory: RescueStationMessageFactory; | ||
|
||
beforeEach(() => { | ||
factory = new RescueStationMessageFactory(); | ||
}); | ||
|
||
it('should create RescueStationSignOnMessage from command', async () => { | ||
const command = new CreateRescueStationSignOnMessageCommand( | ||
SENDING_TIME, | ||
{ unit: { id: 'knownSenderUnit' } }, | ||
{ name: 'unknownReceivingUnit' }, | ||
RESCUE_STATION_DETAILS, | ||
'channel', | ||
AUTH_USER, | ||
); | ||
|
||
const message = await factory.createSignOnMessageFromCommand(command); | ||
const expectedMessage = plainToInstance( | ||
RescueStationSignOnMessage, | ||
MESSAGE, | ||
); | ||
(expectedMessage as any).createdAt = expect.any(Date); | ||
expectedMessage.searchableText = | ||
'anmeldung rettungswache rescueStationName rescueStationCallSign stärke 1/1/1/3 einheiten unitName1 unitCallSign1 alarmgruppen alertGroupName unitName2 unitCallSign2'; | ||
expect(message).toEqual(expectedMessage); | ||
}); | ||
|
||
it('should create RescueStationUpdateMessage from command', async () => { | ||
const command = new CreateRescueStationUpdateMessageCommand( | ||
SENDING_TIME, | ||
{ unit: { id: 'knownSenderUnit' } }, | ||
{ name: 'unknownReceivingUnit' }, | ||
RESCUE_STATION_DETAILS, | ||
'channel', | ||
AUTH_USER, | ||
); | ||
|
||
const message = await factory.createUpdateMessageFromCommand(command); | ||
const expectedMessage = plainToInstance( | ||
RescueStationUpdateMessage, | ||
MESSAGE, | ||
); | ||
(expectedMessage as any).createdAt = expect.any(Date); | ||
expectedMessage.searchableText = | ||
'nachmeldung rettungswache rescueStationName rescueStationCallSign stärke 1/1/1/3 einheiten unitName1 unitCallSign1 alarmgruppen alertGroupName unitName2 unitCallSign2'; | ||
expect(message).toEqual(expectedMessage); | ||
}); | ||
}); |
75 changes: 75 additions & 0 deletions
75
libs/api/protocol/src/lib/core/command/helper/rescue-station-message.factory.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import { Injectable } from '@nestjs/common'; | ||
|
||
import { RescueStationMessagePayload } from '../../entity/protocol-entries/rescue-station/rescue-station-message-payload.entity'; | ||
import { RescueStationSignOnMessage } from '../../entity/protocol-entries/rescue-station/rescue-station-sign-on-message.entity'; | ||
import { RescueStationUpdateMessage } from '../../entity/protocol-entries/rescue-station/rescue-station-update-message.entity'; | ||
import { CreateRescueStationSignOnMessageCommand } from '../rescue-station/create-rescue-station-sign-on-message.command'; | ||
import { CreateRescueStationUpdateMessageCommand } from '../rescue-station/create-rescue-station-update-message.command'; | ||
import { setProtocolMessageBaseFromCommandHelper } from './set-protocol-message-base-from-command.helper'; | ||
|
||
@Injectable() | ||
export class RescueStationMessageFactory { | ||
async createSignOnMessageFromCommand( | ||
cmd: CreateRescueStationSignOnMessageCommand, | ||
): Promise<RescueStationSignOnMessage> { | ||
const msg = new RescueStationSignOnMessage(); | ||
msg.searchableText = this.makeSearchableText('anmeldung', cmd); | ||
setProtocolMessageBaseFromCommandHelper(cmd, msg); | ||
msg.payload = this.makeMessagePayload(cmd); | ||
|
||
return msg; | ||
} | ||
|
||
async createUpdateMessageFromCommand( | ||
cmd: CreateRescueStationUpdateMessageCommand, | ||
): Promise<RescueStationUpdateMessage> { | ||
const msg = new RescueStationUpdateMessage(); | ||
msg.searchableText = this.makeSearchableText('nachmeldung', cmd); | ||
setProtocolMessageBaseFromCommandHelper(cmd, msg); | ||
msg.payload = this.makeMessagePayload(cmd); | ||
|
||
return msg; | ||
} | ||
|
||
private makeMessagePayload( | ||
cmd: | ||
| CreateRescueStationSignOnMessageCommand | ||
| CreateRescueStationUpdateMessageCommand, | ||
): RescueStationMessagePayload { | ||
const msgPayload = new RescueStationMessagePayload(); | ||
msgPayload.rescueStationId = cmd.rescueStation.id; | ||
msgPayload.rescueStationName = cmd.rescueStation.name; | ||
msgPayload.rescueStationCallSign = cmd.rescueStation.callSign; | ||
msgPayload.strength = cmd.rescueStation.strength; | ||
msgPayload.units = cmd.rescueStation.units; | ||
msgPayload.alertGroups = cmd.rescueStation.alertGroups; | ||
return msgPayload; | ||
} | ||
|
||
private makeSearchableText( | ||
actionPrefix: string, | ||
{ rescueStation }: CreateRescueStationSignOnMessageCommand, | ||
): string { | ||
const { strength, units, alertGroups, name, callSign } = rescueStation; | ||
const strengthString = `${strength.leaders}/${strength.subLeaders}/${strength.helpers}/${strength.leaders + strength.subLeaders + strength.helpers}`; | ||
|
||
const unitStrings = units.map( | ||
({ name, callSign }) => `${name} ${callSign}`, | ||
); | ||
const unitString = unitStrings.length | ||
? `einheiten ${unitStrings.join(', ')} ` | ||
: ''; | ||
|
||
const alertGroupStrings = alertGroups.map(({ name, units }) => { | ||
const unitNames = units | ||
.map(({ name, callSign }) => `${name} ${callSign}`) | ||
.join(', '); | ||
return `${name} ${unitNames}`; | ||
}); | ||
const alertGroupString = alertGroupStrings.length | ||
? `alarmgruppen ${alertGroupStrings.join(', ')}` | ||
: ''; | ||
|
||
return `${actionPrefix} rettungswache ${name} ${callSign} stärke ${strengthString} ${unitString}${alertGroupString}`.trimEnd(); | ||
} | ||
} |
95 changes: 95 additions & 0 deletions
95
...rc/lib/core/command/rescue-station/create-rescue-station-sign-off-message.command.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import { DeepMocked, createMock } from '@golevelup/ts-jest'; | ||
import { EventBus } from '@nestjs/cqrs'; | ||
import { plainToClass, plainToInstance } from 'class-transformer'; | ||
|
||
import { AuthUser } from '@kordis/shared/model'; | ||
|
||
import { UserProducer } from '../../entity/partials/producer-partial.entity'; | ||
import { | ||
RegisteredUnit, | ||
UnknownUnit, | ||
} from '../../entity/partials/unit-partial.entity'; | ||
import { | ||
RescueStationSignOffMessage, | ||
RescueStationSignOffMessagePayload, | ||
} from '../../entity/protocol-entries/rescue-station/rescue-station-sign-off-message.entity'; | ||
import { ProtocolEntryRepository } from '../../repository/protocol-entry.repository'; | ||
import { | ||
CreateRescueStationSignOffMessageCommand, | ||
CreateRescueStationSignOffMessageHandler, | ||
} from './create-rescue-station-sign-off-message.command'; | ||
|
||
describe('CreateRescueStationSignOffMessageHandler', () => { | ||
let mockRepository: DeepMocked<ProtocolEntryRepository>; | ||
let handler: CreateRescueStationSignOffMessageHandler; | ||
|
||
let mockEventBus: DeepMocked<EventBus>; | ||
|
||
beforeEach(async () => { | ||
mockEventBus = createMock<EventBus>(); | ||
mockRepository = createMock<ProtocolEntryRepository>(); | ||
handler = new CreateRescueStationSignOffMessageHandler( | ||
mockRepository, | ||
mockEventBus, | ||
); | ||
}); | ||
|
||
it('should create a message and publish an event', async () => { | ||
const sendingTime = new Date(); | ||
const expectedMessage = plainToInstance(RescueStationSignOffMessage, { | ||
orgId: 'organizationId', | ||
time: sendingTime, | ||
sender: plainToInstance(RegisteredUnit, { | ||
unit: { id: 'knownSenderUnit' }, | ||
}), | ||
recipient: plainToInstance(UnknownUnit, { name: 'unknownReceivingUnit' }), | ||
channel: 'channel', | ||
producer: plainToInstance(UserProducer, { | ||
userId: 'userId', | ||
firstName: 'firstName', | ||
lastName: 'lastName', | ||
}), | ||
payload: plainToClass(RescueStationSignOffMessagePayload, { | ||
rescueStationId: 'rescueStationId', | ||
rescueStationName: 'rescueStationName', | ||
rescueStationCallSign: 'rescueStationCallSign', | ||
}), | ||
searchableText: `ausmeldung rettungswache rescueStationName rescueStationCallSign`, | ||
}); | ||
(expectedMessage as any).createdAt = expect.any(Date); | ||
|
||
mockRepository.create.mockResolvedValueOnce(expectedMessage); | ||
|
||
const res = await handler.execute( | ||
new CreateRescueStationSignOffMessageCommand( | ||
sendingTime, | ||
plainToInstance(RegisteredUnit, { | ||
unit: { id: 'knownSenderUnit' }, | ||
}), | ||
plainToInstance(UnknownUnit, { | ||
name: 'unknownReceivingUnit', | ||
}), | ||
{ | ||
id: 'rescueStationId', | ||
name: 'rescueStationName', | ||
callSign: 'rescueStationCallSign', | ||
}, | ||
'channel', | ||
{ | ||
id: 'userId', | ||
organizationId: 'organizationId', | ||
firstName: 'firstName', | ||
lastName: 'lastName', | ||
} as AuthUser, | ||
), | ||
); | ||
|
||
expect(mockRepository.create).toHaveBeenCalledWith(expectedMessage); | ||
expect(res).toEqual(expectedMessage); | ||
expect(mockEventBus.publish).toHaveBeenCalledWith( | ||
expect.objectContaining({ | ||
protocolEntry: expectedMessage, | ||
}), | ||
); | ||
}); | ||
}); |
Oops, something went wrong.