Skip to content

Commit

Permalink
fix: mock reactions calls
Browse files Browse the repository at this point in the history
  • Loading branch information
ttypic committed Jan 2, 2024
1 parent 26f4f0d commit 34ab922
Show file tree
Hide file tree
Showing 7 changed files with 285 additions and 40 deletions.
12 changes: 12 additions & 0 deletions demo/api/conversations/controllers/conversationsController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Request, Response } from 'express';
import { createConversation, getConversation } from '../inMemoryDb';

export const handleCreateConversation = (req: Request, res: Response) => {
const conversationId = req.params.conversationId;
res.json(createConversation(conversationId));
};

export const handleGetConversation = (req: Request, res: Response) => {
const conversationId = req.params.conversationId;
res.json(getConversation(conversationId));
};
62 changes: 62 additions & 0 deletions demo/api/conversations/controllers/messagesController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import * as Ably from 'ably/promises';
import { Request, Response } from 'express';
import { createMessage, deleteMessage, editMessage, findMessages } from '../inMemoryDb';

export const handleCreateMessage = (req: Request, res: Response) => {
const conversationId = req.params.conversationId;
const ablyToken = req.headers.authorization.split(' ')[1];

const message = createMessage({
...JSON.parse(req.body),
client_id: req.headers['ably-clientid'] as string,
conversation_id: conversationId,
});

const client = new Ably.Rest(ablyToken);

client.channels.get(`conversations:${conversationId}`).publish('message.created', message);

res.json({ id: message.id });

res.status(201).end();
};

export const handleQueryMessages = (req: Request, res: Response) => {
const conversationId = req.params.conversationId;
res.json(findMessages(conversationId, req.headers['ably-clientid'] as string));
};

export const handleEditMessages = (req: Request, res: Response) => {
const conversationId = req.params.conversationId;
const ablyToken = req.headers.authorization.split(' ')[1];

const message = editMessage({
id: req.params.messageId,
conversation_id: conversationId,
...JSON.parse(req.body),
});

const client = new Ably.Rest(ablyToken);

client.channels.get(`conversations:${conversationId}`).publish('message.updated', message);

res.json({ id: message.id });

res.status(201).end();
};

export const handleDeleteMessages = (req: Request, res: Response) => {
const conversationId = req.params.conversationId;
const ablyToken = req.headers.authorization.split(' ')[1];

const message = deleteMessage({
id: req.params.messageId,
conversation_id: conversationId,
});

const client = new Ably.Rest(ablyToken);

client.channels.get(`conversations:${conversationId}`).publish('message.deleted', message);

res.status(201).end();
};
34 changes: 34 additions & 0 deletions demo/api/conversations/controllers/reactionsController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Request, Response } from 'express';
import * as Ably from 'ably/promises';
import { addReaction, deleteReaction } from '../inMemoryDb';

export const handleAddReaction = (req: Request, res: Response) => {
const conversationId = req.params.conversationId;
const ablyToken = req.headers.authorization.split(' ')[1];

const reaction = addReaction({
message_id: req.params.messageId,
conversation_id: conversationId,
client_id: req.headers['ably-clientid'] as string,
...JSON.parse(req.body),
});

const client = new Ably.Rest(ablyToken);

client.channels.get(`conversations:${conversationId}`).publish('reaction.added', reaction);

res.status(201).end();
};

export const handleDeleteReaction = (req: Request, res: Response) => {
const reactionId = req.params.reactionId;
const ablyToken = req.headers.authorization.split(' ')[1];

const reaction = deleteReaction(reactionId);

const client = new Ably.Rest(ablyToken);

client.channels.get(`conversations:${reaction.conversation_id}`).publish('reaction.deleted', reaction);

res.status(201).end();
};
139 changes: 139 additions & 0 deletions demo/api/conversations/inMemoryDb.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import { ulid } from 'ulidx';
import exp = require('constants');

export interface Conversation {
id: string;
application_id: string;
ttl: number | null;
created_at: number;
}

export interface Message {
id: string;
client_id: string;
conversation_id: string;
content: string;
reactions: {
counts: Record<string, number>;
latest: Reaction[];
mine: Reaction[];
};
created_at: number;
updated_at: number | null;
deleted_at: number | null;
}

export interface Reaction {
id: string;
message_id: string;
conversation_id: string;
type: string;
client_id: string;
updated_at: number | null;
deleted_at: number | null;
}

const conversations: Conversation[] = [];
const conversationIdToMessages: Record<string, Message[]> = {};
const reactions: Reaction[] = [];

export const createConversation = (id: string): Conversation => {
const existing = conversations.find((conv) => conv.id === id);
if (existing) return existing;
const conversation = {
id,
application_id: 'demo',
ttl: null,
created_at: Date.now(),
};
conversationIdToMessages[id] = [];
conversations.push(conversation);
return conversation;
};

createConversation('conversation1');

export const getConversation = (id: string): Conversation => {
return conversations.find((conv) => conv.id === id);
};

export const findMessages = (conversationId: string, clientId: string) =>
enrichMessagesWithReactions(conversationIdToMessages[conversationId], clientId);

export const createMessage = (message: Pick<Message, 'client_id' | 'conversation_id' | 'content'>) => {
const created: Message = {
...message,
id: ulid(),
reactions: {
counts: {},
latest: [],
mine: [],
},
created_at: Date.now(),
updated_at: null,
deleted_at: null,
};
conversationIdToMessages[created.conversation_id].push(created);
return created;
};

export const editMessage = (message: Pick<Message, 'id' | 'conversation_id' | 'content'>) => {
const edited = conversationIdToMessages[message.conversation_id].find(({ id }) => message.id === id);
edited.content = message.content;
return edited;
};

export const deleteMessage = (message: Pick<Message, 'id' | 'conversation_id'>) => {
const deletedIndex = conversationIdToMessages[message.conversation_id].findIndex(({ id }) => message.id === id);
const deleted = conversationIdToMessages[message.conversation_id][deletedIndex];
conversationIdToMessages[message.conversation_id].splice(deletedIndex, 1);
return deleted;
};

export const addReaction = (
reaction: Pick<Reaction, 'id' | 'message_id' | 'type' | 'client_id' | 'conversation_id'>,
) => {
const created: Reaction = {
...reaction,
id: ulid(),
updated_at: null,
deleted_at: null,
};
reactions.push(created);
return created;
};

export const deleteReaction = (reactionId: string) => {
const deletedIndex = reactions.findIndex((reaction) => reaction.id === reactionId);
const deleted = reactions[deletedIndex];
reactions.splice(deletedIndex, 1);
return deleted;
};

const enrichMessageWithReactions = (message: Message, clientId: string): Message => {
const messageReactions = reactions.filter((reaction) => reaction.message_id === message.id);
const mine = messageReactions.filter((reaction) => reaction.client_id === clientId);
const counts = messageReactions.reduce(
(acc, reaction) => {
if (acc[reaction.type]) {
acc[reaction.type]++;
} else {
acc[reaction.type] = 1;
}
return acc;
},
{} as Record<string, number>,
);
return {
...message,
reactions: {
counts,
latest: messageReactions,
mine,
},
};
};

const enrichMessagesWithReactions = (messages: Message[], clientId: string) => {
return messages.map((message) => enrichMessageWithReactions(message, clientId));
};
42 changes: 2 additions & 40 deletions demo/api/conversations/index.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,12 @@
import * as dotenv from 'dotenv';
import * as Ably from 'ably/promises';
import { ulid } from 'ulidx';
import express, { Router } from 'express';
import express from 'express';
import serverless from 'serverless-http';
import { router } from './routes';

dotenv.config();

const messages = [];

const api = express();

const router = Router();
router.post('/conversations/:conversationId/messages', (req, res) => {
const conversationId = req.params.conversationId;
const ablyToken = req.headers.authorization.split(' ')[1];

const message = {
id: ulid(),
...JSON.parse(req.body),
client_id: req.headers['ably-clientid'],
conversation_id: conversationId,
reactions: {
counts: {},
latest: [],
mine: [],
},
created_at: Date.now(),
updated_at: null,
deleted_at: null,
};

messages.push(message);

const client = new Ably.Rest(ablyToken);

client.channels.get(`conversations:${conversationId}`).publish('message.created', message);

res.json({ id: message.id });

res.status(201).end();
});

router.get('/conversations/:conversationId/messages', (req, res) => {
res.json(messages);
});

api.use('/api/conversations/v1', router);

export const handler = serverless(api);
35 changes: 35 additions & 0 deletions demo/api/conversations/routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Router } from 'express';
import {
handleCreateMessage,
handleDeleteMessages,
handleEditMessages,
handleQueryMessages,
} from './controllers/messagesController';
import { handleCreateConversation, handleGetConversation } from './controllers/conversationsController';
import { handleAddReaction, handleDeleteReaction } from './controllers/reactionsController';

const router = Router();

// Conversations

router.post('/conversations/:conversationId', handleCreateConversation);

router.get('/conversations/:conversationId', handleGetConversation);

// Messages

router.post('/conversations/:conversationId/messages', handleCreateMessage);

router.get('/conversations/:conversationId/messages', handleQueryMessages);

router.post('/conversations/:conversationId/messages/:messageId', handleEditMessages);

router.delete('/conversations/:conversationId/messages/:messageId', handleDeleteMessages);

// Reactions

router.post('/conversations/:conversationId/messages/:messageId/reactions', handleAddReaction);

router.delete('/reactions/:reactionId', handleDeleteReaction);

export { router };
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ export const ConversationProvider: FC<ConversationProviderProps> = ({ client, co
}),
[client, conversationId],
);

return <ConversationContext.Provider value={value}>{children}</ConversationContext.Provider>;
};

0 comments on commit 34ab922

Please sign in to comment.