Skip to content

Commit

Permalink
feat: add FindDeviceQuery
Browse files Browse the repository at this point in the history
  • Loading branch information
adrigzr committed Mar 23, 2023
1 parent 23ff324 commit 49eaaa3
Show file tree
Hide file tree
Showing 15 changed files with 105 additions and 31 deletions.
4 changes: 2 additions & 2 deletions packages/adapter-tcp/src/tcp.server.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { imock, instance } from '@johanblumenberg/ts-mockito';
import { TCPServer } from './tcp.server';
import type { Commands, ConnectionRepository, DeviceRepository } from '@agnoc/domain';
import type { CommandsOrQueries, ConnectionRepository, DeviceRepository } from '@agnoc/domain';
import type { EventHandlerRegistry, TaskHandlerRegistry } from '@agnoc/toolkit';

describe('TCPServer', function () {
let domainEventHandlerRegistry: EventHandlerRegistry;
let commandHandlerRegistry: TaskHandlerRegistry<Commands>;
let commandHandlerRegistry: TaskHandlerRegistry<CommandsOrQueries>;
let deviceRepository: DeviceRepository;
let connectionRepository: ConnectionRepository;
let tcpAdapter: TCPServer;
Expand Down
6 changes: 3 additions & 3 deletions packages/adapter-tcp/src/tcp.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import { DeviceVersionUpdateEventHandler } from './packet-event-handlers/device-
import { DeviceWlanUpdateEventHandler } from './packet-event-handlers/device-wlan-update.event-handler';
import { PackerServerConnectionHandler } from './packet-server.connection-handler';
import { PacketEventBus } from './packet.event-bus';
import type { Commands, ConnectionRepository, DeviceRepository } from '@agnoc/domain';
import type { CommandsOrQueries, ConnectionRepository, DeviceRepository } from '@agnoc/domain';
import type { Server, TaskHandlerRegistry } from '@agnoc/toolkit';
import type { AddressInfo } from 'net';

Expand All @@ -56,7 +56,7 @@ export class TCPServer implements Server {
private readonly deviceRepository: DeviceRepository,
private readonly connectionRepository: ConnectionRepository,
private readonly domainEventHandlerRegistry: EventHandlerRegistry,
private readonly commandHandlerRegistry: TaskHandlerRegistry<Commands>,
private readonly commandQueryHandlerRegistry: TaskHandlerRegistry<CommandsOrQueries>,
) {
// Packet foundation
const payloadMapper = new PayloadMapper(new PayloadObjectParserService(getProtobufRoot(), getCustomDecoders()));
Expand Down Expand Up @@ -142,7 +142,7 @@ export class TCPServer implements Server {
);

// Command event handlers
this.commandHandlerRegistry.register(new LocateDeviceEventHandler(connectionRepository));
this.commandQueryHandlerRegistry.register(new LocateDeviceEventHandler(connectionRepository));
}

async listen(options: TCPAdapterListenOptions = listenDefaultOptions): Promise<TCPAdapterListenReturn> {
Expand Down
10 changes: 5 additions & 5 deletions packages/adapter-tcp/test/integration/tcp.server.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable import/no-extraneous-dependencies */
import { once } from 'events';
import { CommandBus, ConnectionRepository, Device, DeviceRepository, DomainEventBus } from '@agnoc/domain';
import { CommandQueryBus, ConnectionRepository, Device, DeviceRepository, DomainEventBus } from '@agnoc/domain';
import { givenSomeDeviceProps } from '@agnoc/domain/test-support';
import { EventHandlerRegistry, ID, MemoryAdapter, TaskHandlerRegistry } from '@agnoc/toolkit';
import {
Expand All @@ -15,15 +15,15 @@ import {
import { expect } from 'chai';
import { TCPServer } from '@agnoc/adapter-tcp';
import type { TCPAdapterListenOptions } from '@agnoc/adapter-tcp';
import type { Commands } from '@agnoc/domain';
import type { CommandsOrQueries } from '@agnoc/domain';
import type { ICLIENT_ONLINE_REQ, IDEVICE_REGISTER_REQ } from '@agnoc/schemas-tcp';
import type { CreatePacketProps, Packet } from '@agnoc/transport-tcp';

describe('Integration', function () {
let domainEventBus: DomainEventBus;
let commandBus: CommandBus;
let commandBus: CommandQueryBus;
let domainEventHandlerRegistry: EventHandlerRegistry;
let commandHandlerRegistry: TaskHandlerRegistry<Commands>;
let commandHandlerRegistry: TaskHandlerRegistry<CommandsOrQueries>;
let deviceRepository: DeviceRepository;
let connectionRepository: ConnectionRepository;
let tcpAdapter: TCPServer;
Expand All @@ -34,7 +34,7 @@ describe('Integration', function () {
beforeEach(function () {
// Server blocks
domainEventBus = new DomainEventBus();
commandBus = new CommandBus();
commandBus = new CommandQueryBus();

domainEventHandlerRegistry = new EventHandlerRegistry(domainEventBus);
commandHandlerRegistry = new TaskHandlerRegistry(commandBus);
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/agnoc.server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ describe('AgnocServer', function () {
expect(container.deviceRepository).to.be.instanceOf(DeviceRepository);
expect(container.connectionRepository).to.be.instanceOf(ConnectionRepository);
expect(container.domainEventHandlerRegistry).to.be.instanceOf(EventHandlerRegistry);
expect(container.commandHandlerRegistry).to.be.instanceOf(TaskHandlerRegistry);
expect(container.commandQueryHandlerRegistry).to.be.instanceOf(TaskHandlerRegistry);

return instance(server);
});
Expand Down Expand Up @@ -78,7 +78,7 @@ describe('AgnocServer', function () {

when(taskHandler.forName).thenReturn('LocateDeviceCommand');

agnocServer.buildAdapter(({ commandHandlerRegistry }) => {
agnocServer.buildAdapter(({ commandQueryHandlerRegistry: commandHandlerRegistry }) => {
commandHandlerRegistry.register(instance(taskHandler));

return instance(server);
Expand Down
28 changes: 18 additions & 10 deletions packages/core/src/agnoc.server.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,42 @@
import { CommandBus, ConnectionRepository, DeviceRepository, DomainEventBus } from '@agnoc/domain';
import { CommandQueryBus, ConnectionRepository, DeviceRepository, DomainEventBus } from '@agnoc/domain';
import { EventHandlerRegistry, MemoryAdapter, TaskHandlerRegistry } from '@agnoc/toolkit';
import type { DomainEventNames, DomainEvents, Commands } from '@agnoc/domain';
import { FindDeviceQueryHandler } from './query-handlers/find-device.query-handler';
import type { DomainEventNames, DomainEvents, CommandsOrQueries } from '@agnoc/domain';
import type { Server, TaskOutput } from '@agnoc/toolkit';

export class AgnocServer implements Server {
private readonly domainEventBus: DomainEventBus;
private readonly domainEventHandlerRegistry: EventHandlerRegistry;
private readonly commandBus: CommandBus;
private readonly commandHandlerRegistry: TaskHandlerRegistry<Commands>;
private readonly commandQueryBus: CommandQueryBus;
private readonly commandQueryHandlerRegistry: TaskHandlerRegistry<CommandsOrQueries>;
private readonly deviceRepository: DeviceRepository;
private readonly connectionRepository: ConnectionRepository;
private readonly adapters = new Set<Server>();

constructor() {
this.domainEventBus = new DomainEventBus();
this.domainEventHandlerRegistry = new EventHandlerRegistry(this.domainEventBus);
this.commandBus = new CommandBus();
this.commandHandlerRegistry = new TaskHandlerRegistry(this.commandBus);
this.commandQueryBus = new CommandQueryBus();
this.commandQueryHandlerRegistry = new TaskHandlerRegistry(this.commandQueryBus);
this.deviceRepository = new DeviceRepository(this.domainEventBus, new MemoryAdapter());
this.connectionRepository = new ConnectionRepository(this.domainEventBus, new MemoryAdapter());
this.registerHandlers();
}

subscribe<Name extends DomainEventNames>(eventName: Name, handler: SubscribeHandler<Name>): void {
this.domainEventBus.on(eventName, handler);
}

trigger<Command extends Commands[keyof Commands]>(command: Command): Promise<TaskOutput<Command>> {
return this.commandBus.trigger(command);
trigger<CommandOrQuery extends CommandsOrQueries[keyof CommandsOrQueries]>(
command: CommandOrQuery,
): Promise<TaskOutput<CommandOrQuery>> {
return this.commandQueryBus.trigger(command);
}

buildAdapter(builder: AdapterFactory): void {
const adapter = builder({
domainEventHandlerRegistry: this.domainEventHandlerRegistry,
commandHandlerRegistry: this.commandHandlerRegistry,
commandQueryHandlerRegistry: this.commandQueryHandlerRegistry,
deviceRepository: this.deviceRepository,
connectionRepository: this.connectionRepository,
});
Expand All @@ -47,6 +51,10 @@ export class AgnocServer implements Server {
async close(): Promise<void> {
await Promise.all([...this.adapters].map((adapter) => adapter.close()));
}

private registerHandlers(): void {
this.commandQueryHandlerRegistry.register(new FindDeviceQueryHandler(this.deviceRepository));
}
}

export interface AgnocServerListenOptions {
Expand All @@ -57,7 +65,7 @@ type AdapterFactory = (container: Container) => Server;

export type Container = {
domainEventHandlerRegistry: EventHandlerRegistry;
commandHandlerRegistry: TaskHandlerRegistry<Commands>;
commandQueryHandlerRegistry: TaskHandlerRegistry<CommandsOrQueries>;
deviceRepository: DeviceRepository;
connectionRepository: ConnectionRepository;
};
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './agnoc.server';
export * from './query-handlers/find-device.query-handler';
18 changes: 18 additions & 0 deletions packages/core/src/query-handlers/find-device.query-handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { DomainException } from '@agnoc/toolkit';
import type { DeviceRepository, FindDeviceQuery, FindDeviceQueryOutput, QueryHandler } from '@agnoc/domain';

export class FindDeviceQueryHandler implements QueryHandler {
readonly forName = 'FindDeviceQuery';

constructor(private readonly deviceRepository: DeviceRepository) {}

async handle(event: FindDeviceQuery): Promise<FindDeviceQueryOutput> {
const device = await this.deviceRepository.findOneById(event.deviceId);

if (!device) {
throw new DomainException(`Unable to find a device with id ${event.deviceId.value}`);
}

return { device };
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { TaskBus } from '@agnoc/toolkit';
import { expect } from 'chai';
import { CommandBus } from './command.event-bus';
import { CommandQueryBus } from './command-query.task-bus';

describe('CommandBus', function () {
let commandBus: CommandBus;
let commandBus: CommandQueryBus;

beforeEach(function () {
commandBus = new CommandBus();
commandBus = new CommandQueryBus();
});

it('should be created', function () {
Expand Down
8 changes: 8 additions & 0 deletions packages/domain/src/event-buses/command-query.task-bus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { TaskBus } from '@agnoc/toolkit';
import type { Commands } from '../commands/commands';
import type { Queries } from '../queries/queries';

export type CommandsOrQueries = Commands & Queries;
export type CommandOrQueryNames = keyof CommandsOrQueries;

export class CommandQueryBus extends TaskBus<CommandsOrQueries> {}
4 changes: 0 additions & 4 deletions packages/domain/src/event-buses/command.event-bus.ts

This file was deleted.

7 changes: 7 additions & 0 deletions packages/domain/src/event-handlers/query.task-handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { Queries, QueryNames } from '../queries/queries';
import type { TaskHandler } from '@agnoc/toolkit';

export abstract class QueryHandler implements TaskHandler {
abstract forName: QueryNames;
abstract handle(event: Queries[this['forName']]): void;
}
7 changes: 5 additions & 2 deletions packages/domain/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ export * from './entities/device-map.entity';
export * from './entities/device-order.entity';
export * from './entities/room.entity';
export * from './entities/zone.entity';
export * from './event-buses/command.event-bus';
export * from './event-buses/command-query.task-bus';
export * from './event-buses/domain.event-bus';
export * from './event-handlers/command.event-handler';
export * from './event-handlers/command.task-handler';
export * from './event-handlers/domain.event-handler';
export * from './repositories/connection.repository';
export * from './repositories/device.repository';
Expand All @@ -38,3 +38,6 @@ export * from './value-objects/map-pixel.value-object';
export * from './value-objects/map-position.value-object';
export * from './value-objects/quiet-hours-setting.value-object';
export * from './value-objects/voice-setting.value-object';
export * from './queries/find-device.query';
export * from './queries/queries';
export * from './event-handlers/query.task-handler';
26 changes: 26 additions & 0 deletions packages/domain/src/queries/find-device.query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { ID, Query } from '@agnoc/toolkit';
import { Device } from '../aggregate-roots/device.aggregate-root';

export interface FindDeviceQueryInput {
deviceId: ID;
}

export interface FindDeviceQueryOutput {
device: Device;
}

export class FindDeviceQuery extends Query<FindDeviceQueryInput, FindDeviceQueryOutput> {
get deviceId(): ID {
return this.props.deviceId;
}

protected validate(props: FindDeviceQueryInput): void {
this.validateDefinedProp(props, 'deviceId');
this.validateInstanceProp(props, 'deviceId', ID);
}

override validateOutput(output: FindDeviceQueryOutput): void {
this.validateDefinedProp(output, 'device');
this.validateInstanceProp(output, 'device', Device);
}
}
7 changes: 7 additions & 0 deletions packages/domain/src/queries/queries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { FindDeviceQuery } from './find-device.query';

export type Queries = {
FindDeviceQuery: FindDeviceQuery;
};

export type QueryNames = keyof Queries;

0 comments on commit 49eaaa3

Please sign in to comment.