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

refactor: message types #229

Merged
merged 14 commits into from
Sep 22, 2024
9 changes: 5 additions & 4 deletions services/api/src/app/messages/command.service.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { Command } from '@entities/command';
import { User } from '@entities/users/user';
import { Injectable } from '@nestjs/common';
import { HelpCommandUseCase } from '@usecases/commands/help';
import { RenameRoomUseCase } from '@usecases/rooms/rename';
import { RenameUserUseCase } from '@usecases/users/rename';
import { Dispatcher } from '@entities/messages/message';
import { LoremCommandUseCase } from '@usecases/commands/lorem';
import { ParseCommandUseCase } from '@usecases/commands/parse';
import { ParseCommandUseCase } from '@usecases/commands/parse/parse-command';
import { ConfigureRoomUseCase } from '@usecases/rooms/configure-room';
import { P, match } from 'ts-pattern';
import { InviteUseCase } from '@usecases/memberships/invite';
import { LeaveRoomUseCase } from '@usecases/memberships/leave';
import { AboutRoomUseCase } from '@usecases/rooms/about-room';
import { ApproveRequestUseCase } from '@usecases/memberships/approve-request';
import { IncomingCommand } from '@entities/commands';

@Injectable()
export class CommandService {
Expand All @@ -30,9 +30,10 @@ export class CommandService {
readonly dispatcher: Dispatcher,
) {}

async exec(command: Command, authenticatedUser: User): Promise<void> {
async exec(command: IncomingCommand, authenticatedUser: User): Promise<void> {
const { roomId } = command;
const parsedCommand = await this.parse.exec(command);
const parsedCommand = this.parse.exec(command);

return match(parsedCommand)
.with({ tag: 'help' }, () =>
this.help.exec({ roomId, authenticatedUser }),
Expand Down
2 changes: 1 addition & 1 deletion services/api/src/app/messages/messages.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { MessagesController } from './messages.controller';
import { AuthModule } from '@app/auth/auth.module';
import { SendMessageUseCase } from '@usecases/messages/send';
import { GetMessagesUseCase } from '@usecases/messages/get-messages';
import { ParseCommandUseCase } from '@usecases/commands/parse';
import { ParseCommandUseCase } from '@usecases/commands/parse/parse-command';
import { CommandService } from '@app/messages/command.service';
import { RenameRoomUseCase } from '@usecases/rooms/rename';
import { RenameUserUseCase } from '@usecases/users/rename';
Expand Down
8 changes: 4 additions & 4 deletions services/api/src/app/messages/messages.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import { User } from '@entities/users/user';
import { mock, MockProxy } from 'jest-mock-extended';
import { SendMessageUseCase } from '@usecases/messages/send';
import { CommandService } from './command.service';
import { Command } from '@entities/command';
import { UnauthorizedException } from '@nestjs/common';
import { systemUser } from '@entities/users/system-user';
import { IncomingCommand } from '@entities/commands';

describe('MessagesService', () => {
let service: MessagesService;
Expand Down Expand Up @@ -60,10 +60,10 @@ describe('MessagesService', () => {

await service.handleMessage(message, authenticatedUser);

const expectedCommand: Command = {
tokens: ['help'],
canonicalInput: '/help',
const expectedCommand: IncomingCommand = {
content: '/help',
roomId,
authorId: authenticatedUser.id,
};
expect(command.exec).toHaveBeenCalledWith(
expectedCommand,
Expand Down
29 changes: 17 additions & 12 deletions services/api/src/app/messages/messages.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import { HttpException, Injectable } from '@nestjs/common';
import { CreateMessageDto } from './dto/messages';
import { SendMessageUseCase } from '@usecases/messages/send';
import { CommandService } from '@app/messages/command.service';
import { isCommand, parseMessage } from '@usecases/messages/parse-message';
import { systemUser } from '@entities/users/system-user';
import { User } from '@entities/users/user';
import { IncomingMessage } from '@entities/messages/message';
import { isCommand } from '@entities/commands';

@Injectable()
export class MessagesService {
Expand All @@ -17,10 +18,10 @@ export class MessagesService {
incoming: CreateMessageDto,
authenticatedUser: User,
): Promise<void> {
const message = parseMessage({
const message: IncomingMessage = {
...incoming,
authorId: authenticatedUser.id,
});
};

try {
if (isCommand(message)) {
Expand All @@ -30,18 +31,22 @@ export class MessagesService {
}
} catch (e) {
if (e instanceof HttpException) {
await this.send.exec(
{
content: e.message,
roomId: incoming.roomId,
recipientId: authenticatedUser.id,
authorId: 'system',
},
return await this.send.exec(
this.getErrorMessage(e, message),
systemUser,
);
} else {
throw e;
}

throw e;
}
}

private getErrorMessage(e: HttpException, message: IncomingMessage) {
return {
content: e.message,
roomId: message.roomId,
recipientId: message.authorId,
authorId: 'system',
};
}
}
22 changes: 0 additions & 22 deletions services/api/src/domain/entities/command.ts

This file was deleted.

14 changes: 14 additions & 0 deletions services/api/src/domain/entities/commands.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { IncomingMessage } from '@entities/messages/message';

/**
* An incoming message which is a command. Any message with content prefixed by a forward slash is
* considered a command, where or not it can be parsed. If it cannot be parsed, it is an invalid
* command.
*/
export type IncomingCommand = IncomingMessage & {
content: `/${string}`;
};

export const isCommand = (
message: IncomingMessage,
): message is IncomingCommand => message.content.startsWith('/');
39 changes: 22 additions & 17 deletions services/api/src/domain/entities/messages/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,9 @@ export enum UpdatedEntity {
}

/**
* A message that has been sent by a user and is stored in the system.
* A message received by the system but not yet stored, executed or dispatched to clients.
*/
export type SentMessage = {
/**
* Unique identifier for the message.
*/
id: string;

/**
* The time the message was sent (in ms since the epoch).
*/
time: number;

export type IncomingMessage = {
/**
* The text content of the message.
*/
Expand All @@ -34,7 +24,12 @@ export type SentMessage = {
* The room the message was sent to.
*/
roomId: string;
};

/**
* Type for draft messages which have not yet been sent by the system.
*/
export type DraftMessage = IncomingMessage & {
/**
* The recipient of the message.
* If undefined, this is a public message sent to the room.
Expand All @@ -50,6 +45,21 @@ export type SentMessage = {
updatedEntities?: UpdatedEntity[];
};

/**
* A message that has been sent by a user and is stored in the system.
*/
export type SentMessage = DraftMessage & {
/**
* Unique identifier for the message.
*/
id: string;

/**
* The time the message was sent (in ms since the epoch).
*/
time: number;
};

/**
* A refinement of the `Message` type to indicate it is private.
*/
Expand All @@ -66,11 +76,6 @@ export const isPrivate = (message: SentMessage): message is PrivateMessage => {
return (message as PrivateMessage).recipientId !== undefined;
};

/**
* Type for draft messages which have not yet been sent by the system.
*/
export type DraftMessage = Omit<SentMessage, 'id' | 'time'>;

/**
* Abstract class for dispatching and subscribing to messages. All messages will be sent and
* delivered to subscribers through a `Dispatcher`.
Expand Down
9 changes: 5 additions & 4 deletions services/api/src/domain/usecases/commands/help.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Dispatcher } from '@entities/messages/message';
import { User } from '@entities/users/user';
import { Injectable } from '@nestjs/common';
import { parsers } from './parse/parsers';

import { commands } from '@usecases/commands/parse/commands';

export type HelpParams = {
authenticatedUser: User;
Expand All @@ -24,9 +25,9 @@ export class HelpCommandUseCase {

private generateContent() {
const title = 'Type to chat, or enter one of the following commands:';
const commands = parsers.map(
(parser) => `* \`${parser.signature}\`: ${parser.summary}`,
const commandSummaries = commands.map(
(command) => `* \`${command.signature}\`: ${command.summary}`,
);
return [title, ...commands].join('\n');
return [title, ...commandSummaries].join('\n');
}
}
Loading
Loading