-
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-radio-communication-system): introduce TETRA SDS, Call Out a…
…nd Status updates
- Loading branch information
1 parent
99a715d
commit aa6a4e1
Showing
26 changed files
with
939 additions
and
26 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
{ | ||
"extends": ["../../../.eslintrc.json"], | ||
"ignorePatterns": ["!**/*"], | ||
"overrides": [ | ||
{ | ||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"], | ||
"rules": {} | ||
}, | ||
{ | ||
"files": ["*.ts", "*.tsx"], | ||
"rules": {} | ||
}, | ||
{ | ||
"files": ["*.js", "*.jsx"], | ||
"rules": {} | ||
} | ||
] | ||
} |
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,16 @@ | ||
# Radio Communication System | ||
|
||
This module covers the radio communication currently over tetra control. The | ||
TetraControlService can send out Call Outs, SDS and Status changes. Also, a | ||
webhook handler for incoming status changes is implemented. The webhook has to | ||
be configured with a key via `TETRA_CONTROL_WEBHOOK_KEY`, which has to be | ||
provided in Tetra Control | ||
`<kordis-api-url>/webhooks/tetra-control?key=<safe-key>`. With | ||
`TETRA_CONTROL_WEBHOOK_VALID_IPS`, a list of allowed IPs can be provided | ||
(delimited by a comma). If the list is empty, all IPs are allowed. If a new SDS | ||
is received, a `NewTetraStatusEvent` is emitted. | ||
|
||
## Running unit tests | ||
|
||
Run `nx test api-radio-communication-system` to execute the unit tests via | ||
[Jest](https://jestjs.io). |
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,11 @@ | ||
/* eslint-disable */ | ||
export default { | ||
displayName: 'api-radio-communication-system', | ||
preset: '../../../jest.preset.js', | ||
testEnvironment: 'node', | ||
transform: { | ||
'^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }], | ||
}, | ||
moduleFileExtensions: ['ts', 'js', 'html'], | ||
coverageDirectory: '../../../coverage/libs/api/radio-communication-system', | ||
}; |
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,30 @@ | ||
{ | ||
"name": "api-radio-communication-system", | ||
"$schema": "../../../node_modules/nx/schemas/project-schema.json", | ||
"sourceRoot": "libs/api/radio-communication-system/src", | ||
"projectType": "library", | ||
"targets": { | ||
"lint": { | ||
"executor": "@nx/linter:eslint", | ||
"outputs": ["{options.outputFile}"], | ||
"options": { | ||
"lintFilePatterns": ["libs/api/radio-communication-system/**/*.ts"] | ||
} | ||
}, | ||
"test": { | ||
"executor": "@nx/jest:jest", | ||
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"], | ||
"options": { | ||
"jestConfig": "libs/api/radio-communication-system/jest.config.ts", | ||
"passWithNoTests": true | ||
}, | ||
"configurations": { | ||
"ci": { | ||
"ci": true, | ||
"codeCoverage": true | ||
} | ||
} | ||
} | ||
}, | ||
"tags": [] | ||
} |
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,2 @@ | ||
export * from './lib/radio-communication-system.module'; | ||
export * from './lib/core/event/new-tetra-status.event'; |
36 changes: 36 additions & 0 deletions
36
.../api/radio-communication-system/src/lib/core/command/publish-tetra-status.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,36 @@ | ||
import { createMock } from '@golevelup/ts-jest'; | ||
import { EventBus } from '@nestjs/cqrs'; | ||
|
||
import { NewTetraStatusEvent } from '../event/new-tetra-status.event'; | ||
import { | ||
PublishTetraStatusCommand, | ||
PublishTetraStatusHandler, | ||
} from './publish-tetra-status.command'; | ||
|
||
describe('PublishTetraStatusHandler', () => { | ||
let handler: PublishTetraStatusHandler; | ||
let eventBus: EventBus; | ||
|
||
beforeEach(() => { | ||
eventBus = createMock<EventBus>(); | ||
handler = new PublishTetraStatusHandler(eventBus); | ||
}); | ||
|
||
it('should publish NewRadioCallStatusReportEvent', async () => { | ||
const sendingIssi = '12345'; | ||
const fmsStatus = 1; | ||
const sentAt = new Date(); | ||
|
||
const command = new PublishTetraStatusCommand( | ||
sendingIssi, | ||
fmsStatus, | ||
sentAt, | ||
); | ||
const publishSpy = jest.spyOn(eventBus, 'publish'); | ||
await handler.execute(command); | ||
|
||
expect(publishSpy).toHaveBeenCalledWith( | ||
new NewTetraStatusEvent(sendingIssi, fmsStatus, sentAt), | ||
); | ||
}); | ||
}); |
33 changes: 33 additions & 0 deletions
33
libs/api/radio-communication-system/src/lib/core/command/publish-tetra-status.command.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,33 @@ | ||
import { | ||
CommandHandler, | ||
EventBus, | ||
ICommand, | ||
ICommandHandler, | ||
} from '@nestjs/cqrs'; | ||
|
||
import { NewTetraStatusEvent } from '../event/new-tetra-status.event'; | ||
|
||
export class PublishTetraStatusCommand implements ICommand { | ||
constructor( | ||
readonly sendingIssi: string, | ||
readonly fmsStatus: number, | ||
readonly sentAt: Date, | ||
) {} | ||
} | ||
|
||
@CommandHandler(PublishTetraStatusCommand) | ||
export class PublishTetraStatusHandler | ||
implements ICommandHandler<PublishTetraStatusCommand> | ||
{ | ||
constructor(private readonly eventBus: EventBus) {} | ||
|
||
async execute(command: PublishTetraStatusCommand): Promise<void> { | ||
this.eventBus.publish( | ||
new NewTetraStatusEvent( | ||
command.sendingIssi, | ||
command.fmsStatus, | ||
command.sentAt, | ||
), | ||
); | ||
} | ||
} |
48 changes: 48 additions & 0 deletions
48
libs/api/radio-communication-system/src/lib/core/command/send-tetra-sds.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,48 @@ | ||
import { DeepMocked, createMock } from '@golevelup/ts-jest'; | ||
|
||
import { TetraControlService } from '../../infra/service/tetra-control.service'; | ||
import { SdsNotAbleToSendException } from '../exception/sds-not-able-to-send.exception'; | ||
import { | ||
SendTetraSDSCommand, | ||
SendTetraSDSHandler, | ||
} from './send-tetra-sds.command'; | ||
|
||
describe('SendTetraSDSHandler', () => { | ||
let handler: SendTetraSDSHandler; | ||
let tetraServiceMock: DeepMocked<TetraControlService>; | ||
|
||
beforeEach(() => { | ||
tetraServiceMock = createMock<TetraControlService>(); | ||
handler = new SendTetraSDSHandler(tetraServiceMock); | ||
}); | ||
|
||
it('should call send sds through tetra service', async () => { | ||
const issi = '12345'; | ||
const message = 'Test message'; | ||
const isFlash = true; | ||
|
||
const command = new SendTetraSDSCommand(issi, message, isFlash); | ||
|
||
await handler.execute(command); | ||
|
||
expect(tetraServiceMock.sendSDS).toHaveBeenCalledWith( | ||
issi, | ||
message, | ||
isFlash, | ||
); | ||
}); | ||
|
||
it('should throw an error if tetraService.sendSDS throws an error', async () => { | ||
const issi = '12345'; | ||
const message = 'Test message'; | ||
const isFlash = true; | ||
|
||
const command = new SendTetraSDSCommand(issi, message, isFlash); | ||
|
||
tetraServiceMock.sendSDS.mockRejectedValue(new Error()); | ||
|
||
await expect(handler.execute(command)).rejects.toThrow( | ||
SdsNotAbleToSendException, | ||
); | ||
}); | ||
}); |
34 changes: 34 additions & 0 deletions
34
libs/api/radio-communication-system/src/lib/core/command/send-tetra-sds.command.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,34 @@ | ||
import { Inject } from '@nestjs/common'; | ||
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; | ||
|
||
import { SdsNotAbleToSendException } from '../exception/sds-not-able-to-send.exception'; | ||
import { TETRA_SERVICE, TetraService } from '../service/tetra.service'; | ||
|
||
export class SendTetraSDSCommand { | ||
constructor( | ||
readonly issi: string, | ||
readonly message: string, | ||
readonly isFlash: boolean, | ||
) {} | ||
} | ||
|
||
@CommandHandler(SendTetraSDSCommand) | ||
export class SendTetraSDSHandler | ||
implements ICommandHandler<SendTetraSDSCommand> | ||
{ | ||
constructor( | ||
@Inject(TETRA_SERVICE) private readonly tetraService: TetraService, | ||
) {} | ||
|
||
async execute(command: SendTetraSDSCommand): Promise<void> { | ||
try { | ||
await this.tetraService.sendSDS( | ||
command.issi, | ||
command.message, | ||
command.isFlash, | ||
); | ||
} catch (error) { | ||
throw new SdsNotAbleToSendException(error); | ||
} | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
libs/api/radio-communication-system/src/lib/core/event/new-tetra-status.event.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,9 @@ | ||
import { IEvent } from '@nestjs/cqrs'; | ||
|
||
export class NewTetraStatusEvent implements IEvent { | ||
constructor( | ||
public readonly issi: string, | ||
public readonly status: number, | ||
public readonly sentAt: Date, | ||
) {} | ||
} |
5 changes: 5 additions & 0 deletions
5
libs/api/radio-communication-system/src/lib/core/exception/sds-not-able-to-send.exception.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,5 @@ | ||
export class SdsNotAbleToSendException extends Error { | ||
constructor(error: unknown) { | ||
super(`Not able to send SDS: ${error}`); | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
libs/api/radio-communication-system/src/lib/core/service/tetra.service.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,17 @@ | ||
export const TETRA_SERVICE = Symbol('TETRA_SERVICE'); | ||
|
||
export interface TetraService { | ||
sendSDS(issi: string, message: string, isFlash?: boolean): Promise<void>; | ||
|
||
sendCallOut( | ||
issi: string, | ||
message: string, | ||
noReply: boolean, | ||
prio?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15, | ||
): Promise<void>; | ||
|
||
sendStatus( | ||
issi: string, | ||
status: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8, | ||
): Promise<void>; | ||
} |
63 changes: 63 additions & 0 deletions
63
libs/api/radio-communication-system/src/lib/infra/controller/rcs.resolver.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,63 @@ | ||
import { DeepMocked, createMock } from '@golevelup/ts-jest'; | ||
import { CommandBus } from '@nestjs/cqrs'; | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
|
||
import { SendTetraSDSCommand } from '../../core/command/send-tetra-sds.command'; | ||
import { SdsNotAbleToSendException } from '../../core/exception/sds-not-able-to-send.exception'; | ||
import { PresentableSdsNotSendException } from '../exception/presentable-sds-not-send.exception'; | ||
import { RCSResolver } from './rcs.resolver'; | ||
|
||
describe('RCSResolver', () => { | ||
let resolver: RCSResolver; | ||
let commandBus: DeepMocked<CommandBus>; | ||
|
||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
providers: [ | ||
RCSResolver, | ||
{ | ||
provide: CommandBus, | ||
useValue: createMock<CommandBus>(), | ||
}, | ||
], | ||
}).compile(); | ||
|
||
resolver = module.get<RCSResolver>(RCSResolver); | ||
commandBus = module.get<DeepMocked<CommandBus>>(CommandBus); | ||
}); | ||
|
||
describe('sendSDS', () => { | ||
it('should send a Send Tetra SDS command', async () => { | ||
const issi = '123456'; | ||
const message = 'Test message'; | ||
const isFlash = true; | ||
|
||
await resolver.sendSDS(issi, message, isFlash); | ||
|
||
expect(commandBus.execute).toHaveBeenCalledWith( | ||
new SendTetraSDSCommand(issi, message, isFlash), | ||
); | ||
}); | ||
|
||
it('should throw presentable error on SDS error', async () => { | ||
const issi = '123456'; | ||
const message = 'Test message'; | ||
const isFlash = false; | ||
|
||
commandBus.execute.mockRejectedValueOnce( | ||
new SdsNotAbleToSendException(new Error()), | ||
); | ||
|
||
await expect(resolver.sendSDS(issi, message, isFlash)).rejects.toThrow( | ||
PresentableSdsNotSendException, | ||
); | ||
|
||
const unknownError = new Error('i am an unknown error'); | ||
commandBus.execute.mockRejectedValueOnce(unknownError); | ||
|
||
await expect(resolver.sendSDS(issi, message, isFlash)).rejects.toThrow( | ||
unknownError, | ||
); | ||
}); | ||
}); | ||
}); |
32 changes: 32 additions & 0 deletions
32
libs/api/radio-communication-system/src/lib/infra/controller/rcs.resolver.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,32 @@ | ||
import { CommandBus } from '@nestjs/cqrs'; | ||
import { Args, Mutation, Resolver } from '@nestjs/graphql'; | ||
|
||
import { SendTetraSDSCommand } from '../../core/command/send-tetra-sds.command'; | ||
import { SdsNotAbleToSendException } from '../../core/exception/sds-not-able-to-send.exception'; | ||
import { PresentableSdsNotSendException } from '../exception/presentable-sds-not-send.exception'; | ||
|
||
@Resolver() | ||
export class RCSResolver { | ||
constructor(private readonly commandBus: CommandBus) {} | ||
|
||
@Mutation(() => Boolean) | ||
async sendSDS( | ||
@Args('issi') issi: string, | ||
@Args('message') message: string, | ||
@Args('isFlash') isFlash?: boolean, | ||
): Promise<boolean> { | ||
try { | ||
await this.commandBus.execute( | ||
new SendTetraSDSCommand(issi, message, !!isFlash), | ||
); | ||
} catch (error) { | ||
if (error instanceof SdsNotAbleToSendException) { | ||
throw new PresentableSdsNotSendException(); | ||
} | ||
|
||
throw error; | ||
} | ||
|
||
return true; | ||
} | ||
} |
Oops, something went wrong.