Skip to content

Commit

Permalink
Refactored Backend Structure & Fixed Tests
Browse files Browse the repository at this point in the history
  • Loading branch information
R1c4rdCo5t4 committed Mar 23, 2024
1 parent 0962481 commit bb45ef8
Show file tree
Hide file tree
Showing 25 changed files with 221 additions and 145 deletions.
2 changes: 1 addition & 1 deletion code/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"serve": "vite preview"
},
"dependencies": {
"@notespace/shared": "link:..\\shared",
"@notespace/shared": "file:..\\shared",
"eslint-plugin-playwright": "^1.5.4",
"lodash": "^4.17.21",
"react": "^18.2.0",
Expand Down
10 changes: 10 additions & 0 deletions code/server/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module.exports = {
root: true,
env: { node: true },
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
rules: {
'@typescript-eslint/no-explicit-any': 'off',
},
};
4 changes: 4 additions & 0 deletions code/server/jest.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { pathsToModuleNameMapper } = require('ts-jest');

module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
moduleNameMapper: pathsToModuleNameMapper({ '@src/*': ['./src/*'] }, { prefix: '<rootDir>/' }),
};
11 changes: 11 additions & 0 deletions code/server/src/controllers/http/document/deleteDocument.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Request, Response } from 'express';
import { DocumentService } from '@src/types';

function deleteDocument(service: DocumentService) {
return (req: Request, res: Response) => {
service.deleteTree();
res.status(200).send();
};
}

export default deleteDocument;
11 changes: 11 additions & 0 deletions code/server/src/controllers/http/document/getDocument.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Request, Response } from 'express';
import { DocumentService } from '@src/types';

function getDocument(service: DocumentService) {
return async (req: Request, res: Response) => {
const tree = await service.getTree();
res.status(200).send(tree);
};
}

export default getDocument;
20 changes: 20 additions & 0 deletions code/server/src/controllers/http/router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import express from 'express';
import { DocumentService } from '@src/types';
import getDocument from '@src/controllers/http/document/getDocument';
import deleteDocument from '@src/controllers/http/document/deleteDocument';

export default function (service: DocumentService) {
if (!service) {
throw new Error('Service parameter is required');
}
const router = express.Router();
router.use(express.urlencoded({ extended: true }));

router.get('/', (req, res) => {
res.send('Welcome to NoteSpace');
});
router.get('/document', getDocument(service));
router.delete('/document', deleteDocument(service));

return router;
}
16 changes: 16 additions & 0 deletions code/server/src/controllers/socket/document/onCursorChange.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Socket } from 'socket.io';

const cursorColorsMap = new Map<string, string>();

function onCursorChange() {
return (socket: Socket, position: CursorChangeData) => {
if (!cursorColorsMap.has(socket.id)) {
const randomColor = 'hsl(' + Math.random() * 360 + ', 100%, 75%)';
cursorColorsMap.set(socket.id, randomColor);
}
const color = cursorColorsMap.get(socket.id);
socket.broadcast.emit('cursorChange', { position, id: socket.id, color });
};
}

export default onCursorChange;
29 changes: 29 additions & 0 deletions code/server/src/controllers/socket/document/onOperation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Socket } from 'socket.io';
import { DocumentService } from '@src/types';
import { Operation } from 'shared/crdt/types/operations';

function onOperation(service: DocumentService) {
return (socket: Socket, operation: Operation) => {
switch (operation.type) {
case 'insert': {
service.insertCharacter(operation);
socket.broadcast.emit('operation', operation);
break;
}
case 'delete': {
service.deleteCharacter(operation);
socket.broadcast.emit('operation', operation);
break;
}
case 'style': {
service.updateStyle(operation);
socket.broadcast.emit('operation', operation);
break;
}
default:
throw new Error('Invalid operation type');
}
};
}

export default onOperation;
13 changes: 13 additions & 0 deletions code/server/src/controllers/socket/events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import onOperation from '@src/controllers/socket/document/onOperation';
import onCursorChange from '@src/controllers/socket/document/onCursorChange';
import { DocumentService, SocketHandler } from '@src/types';

export default function events(service: DocumentService): Record<string, SocketHandler> {
if (!service) {
throw new Error('Service parameter is required');
}
return {
operation: onOperation(service),
cursorChange: onCursorChange,
};
}
31 changes: 31 additions & 0 deletions code/server/src/controllers/socket/onConnection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Socket } from 'socket.io';
import { DocumentService, SocketHandler } from '@src/types';

function onConnection(service: DocumentService, events: Record<string, SocketHandler>) {
return async (socket: Socket) => {
console.log('a client connected');

if (socket.connected) {
const tree = await service.getTree();
socket.emit('document', tree);
}

Object.entries(events).forEach(([event, handler]) => {
socket.on(event, data => {
try {
console.log(event);
handler(socket, data);
} catch (e) {
socket.emit('error');
console.error(e);
}
});
});

socket.on('disconnect', reason => {
console.log('a client disconnected', reason);
});
};
}

export default onConnection;
15 changes: 3 additions & 12 deletions code/server/src/database/firestore/firestore.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Node, Nodes } from '@notespace/shared/crdt/types';
import { Node, Nodes } from '@notespace/shared/crdt/types/nodes';
import { cert, initializeApp, ServiceAccount } from 'firebase-admin/app';
import serviceAccount from '../../../firestore-key-5cddf-472039f8dbb6.json';
import { getFirestore } from 'firebase-admin/firestore';
import { FugueTree } from '@notespace/shared/crdt/fugueTree';
import { rootNode } from '@notespace/shared/crdt/utils';

initializeApp({
credential: cert(serviceAccount as ServiceAccount),
Expand All @@ -15,17 +16,7 @@ export async function getDocument() {
if (docRef.exists) {
return docRef.data() as Nodes<string>;
}
const root: Node<string> = {
id: { sender: 'root', counter: 0 },
value: null,
isDeleted: true,
parent: null,
side: 'R',
leftChildren: [],
rightChildren: [],
depth: 0,
styles: [],
};
const root: Node<string> = rootNode();
const nodes = { root: [root] } as Nodes<string>;
setDocument(nodes);
return nodes;
Expand Down
2 changes: 1 addition & 1 deletion code/server/src/database/firestore/operations.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { InsertOperation, DeleteOperation, StyleOperation } from '@notespace/shared/crdt/operations';
import { InsertOperation, DeleteOperation, StyleOperation } from '@notespace/shared/crdt/types/operations';
import { getTreeInstance, setDocument, updateTree } from '@src/database/firestore/firestore';

async function getTree() {
Expand Down
5 changes: 3 additions & 2 deletions code/server/src/database/memory/operations.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { FugueTree } from '@notespace/shared/crdt/FugueTree';
import { DeleteOperation, InsertOperation, StyleOperation } from '@notespace/shared/crdt/operations';
import { DeleteOperation, InsertOperation, StyleOperation } from '@notespace/shared/crdt/types/operations';
import { Nodes } from '@notespace/shared/crdt/types/nodes';

let tree = new FugueTree<string>();

async function getTree() {
async function getTree(): Promise<Nodes<string>> {
return Object.fromEntries(Array.from(tree.nodes.entries()));
}

Expand Down
4 changes: 4 additions & 0 deletions code/server/src/domain/document.types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
type CursorChangeData = {
line: number;
column: number;
};
29 changes: 0 additions & 29 deletions code/server/src/http/router.ts

This file was deleted.

38 changes: 8 additions & 30 deletions code/server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@ import http from 'http';
import { Server } from 'socket.io';
import { config } from 'dotenv';
import cors from 'cors';
import eventsInit from './ws/events';
import servicesInit from './services/services';
import serviceInit from './services/documentService';
import eventsInit from './controllers/socket/events';
import database from './database/memory/operations';
import router from './http/router';
import router from '@src/controllers/http/router';
import onConnection from '@src/controllers/socket/onConnection';

config();
const PORT = process.env.PORT || 8080;
const services = servicesInit(database);
const api = router(services);
const events = eventsInit(services);
const service = serviceInit(database);
const events = eventsInit(service);
const api = router(service);
const app = express();
const server = http.createServer(app);
const io = new Server(server, {
Expand All @@ -23,30 +24,7 @@ const io = new Server(server, {
app.use(cors({ origin: '*' }));
app.use('/', api);

io.on('connection', async socket => {
console.log('a client connected');

if (socket.connected) {
const tree = await services.getTree();
socket.emit('document', tree);
}

Object.entries(events).forEach(([event, handler]) => {
socket.on(event, data => {
try {
console.log(event, data);
handler(socket, data);
} catch (e) {
socket.emit('error');
console.error(e);
}
});
});

socket.on('disconnect', reason => {
console.log('a client disconnected', reason);
});
});
io.on('connection', onConnection(service, events));

server.listen(PORT, () => {
console.log(`listening on http://localhost:${PORT}`);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Database } from '@src/types';
import { DeleteOperation, InsertOperation, StyleOperation } from '@notespace/shared/crdt/operations';
import { DocumentDatabase } from '@src/types';
import { DeleteOperation, InsertOperation, StyleOperation } from '@notespace/shared/crdt/types/operations';

export default function Services(database: Database) {
export default function DocumentService(database: DocumentDatabase) {
async function getTree() {
return await database.getTree();
}
Expand Down
7 changes: 5 additions & 2 deletions code/server/src/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import { Nodes } from '@notespace/shared/crdt/types';
import { InsertOperation, DeleteOperation, StyleOperation } from '@notespace/shared/crdt/operations';
import { Socket } from 'socket.io';

type Database = {
type DocumentDatabase = {
getTree: () => Promise<Nodes<string>>;
deleteTree: () => void;
insertCharacter: (operation: InsertOperation<string>) => void;
deleteCharacter: (operation: DeleteOperation) => void;
updateStyle: (operation: StyleOperation) => void;
};

type Service = {
type DocumentService = {
getTree: () => Promise<Nodes<string>>;
deleteTree: () => void;
insertCharacter: (operation: InsertOperation) => void;
deleteCharacter: (operation: DeleteOperation) => void;
updateStyle: (operation: StyleOperation) => void;
};

type SocketHandler = (socket: Socket, data: any) => void;
48 changes: 0 additions & 48 deletions code/server/src/ws/events.ts

This file was deleted.

Loading

0 comments on commit bb45ef8

Please sign in to comment.