Skip to content

Commit

Permalink
Merge pull request #309 from itenium-be/issue-269-socketio
Browse files Browse the repository at this point in the history
Issue 269 socketio
  • Loading branch information
Laoujin authored Jan 14, 2025
2 parents 62b2108 + a4eb864 commit 5d385fb
Show file tree
Hide file tree
Showing 47 changed files with 1,052 additions and 84 deletions.
322 changes: 308 additions & 14 deletions backend/package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"pug": "^2.0.4",
"regenerator-runtime": "^0.13.3",
"slugify": "^1.1.0",
"socket.io": "^4.8.1",
"tmp": "^0.2.1",
"ubl-builder": "github:pipesanta/ubl-builder",
"uuid": "^10.0.0"
Expand Down Expand Up @@ -101,6 +102,7 @@
"lint-staged": "^10.0.1",
"mongodb-memory-server": "^8.12.1",
"nodemon": "^1.11.0",
"socket.io-mock-ts": "^1.0.2",
"supertest": "^6.3.3",
"ts-jest": "^29.0.5",
"typescript": "4.3"
Expand Down
8 changes: 6 additions & 2 deletions backend/src/controllers/clients.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import slugify from 'slugify';
import fetch from 'node-fetch';
import {ObjectID} from 'mongodb';
import {IClient} from '../models/clients';
import {CollectionNames, updateAudit, createAudit} from '../models/common';
import {CollectionNames, updateAudit, createAudit, SocketEventTypes} from '../models/common';
import {ConfacRequest} from '../models/technical';
import {saveAudit} from './utils/audit-logs';
import {emitEntityEvent} from './utils/entity-events';


export const getClients = async (req: Request, res: Response) => {
Expand All @@ -31,13 +32,16 @@ export const saveClient = async (req: ConfacRequest, res: Response) => {
const {value: originalClient} = await clientsCollection.findOneAndUpdate({_id: new ObjectID(_id)}, {$set: client}, {returnOriginal: true});

await saveAudit(req, 'client', originalClient, client);
return res.send({_id, ...client});
const clientResponse = {_id, ...client};
emitEntityEvent(req, SocketEventTypes.EntityUpdated, CollectionNames.CLIENTS, _id, clientResponse);
return res.send(clientResponse);
}


client.slug = slugify(client.name).toLowerCase();
client.audit = createAudit(req.user);
const inserted = await req.db.collection(CollectionNames.CLIENTS).insertOne(client);
const [createdClient] = inserted.ops;
emitEntityEvent(req, SocketEventTypes.EntityCreated, CollectionNames.CLIENTS, createdClient._id, createdClient);
return res.send(createdClient);
};
11 changes: 8 additions & 3 deletions backend/src/controllers/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import {ObjectID} from 'mongodb';
import appConfig from '../config';
import {ICompanyConfig} from '../models/config';
import {getTemplatesPath} from './utils';
import {CollectionNames, updateAudit} from '../models/common';
import {CollectionNames, SocketEventTypes, updateAudit} from '../models/common';
import {ConfacRequest} from '../models/technical';
import {saveAudit} from './utils/audit-logs';
import { emitEntityEvent } from './utils/entity-events';

export const getCompanyConfig = async (req: Request, res: Response) => {
const companyConfig: ICompanyConfig | null = await req.db.collection(CollectionNames.CONFIG).findOne({key: 'conf'});
Expand Down Expand Up @@ -41,11 +42,15 @@ export const saveCompanyConfig = async (req: ConfacRequest, res: Response) => {
.findOneAndUpdate({_id: new ObjectID(_id)}, {$set: config}, {returnOriginal: true});

await saveAudit(req, 'config', originalConfig, config);
return res.send({_id, ...config});
const responseConfig = {_id, ...config};
emitEntityEvent(req, SocketEventTypes.EntityUpdated, CollectionNames.CONFIG, _id, responseConfig);
return res.send(responseConfig);
}

const inserted = await req.db.collection<ICompanyConfig>(CollectionNames.CONFIG).insertOne(config);
return res.send(inserted.ops[0]);
const responseConfig = inserted.ops[0];
emitEntityEvent(req, SocketEventTypes.EntityCreated, CollectionNames.CONFIG, responseConfig._id, responseConfig);
return res.send(responseConfig);
};


Expand Down
8 changes: 6 additions & 2 deletions backend/src/controllers/consultants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import {Request, Response} from 'express';
import slugify from 'slugify';
import {ObjectID} from 'mongodb';
import {IConsultant} from '../models/consultants';
import {CollectionNames, createAudit, updateAudit} from '../models/common';
import {CollectionNames, createAudit, SocketEventTypes, updateAudit} from '../models/common';
import {ConfacRequest} from '../models/technical';
import {saveAudit} from './utils/audit-logs';
import {emitEntityEvent} from './utils/entity-events';

export const getConsultants = async (req: Request, res: Response) => {
const consultants = await req.db.collection<IConsultant>(CollectionNames.CONSULTANTS).find().toArray();
Expand All @@ -20,7 +21,9 @@ export const saveConsultant = async (req: ConfacRequest, res: Response) => {
.findOneAndUpdate({_id: new ObjectID(_id)}, {$set: consultant}, {returnOriginal: true});

await saveAudit(req, 'consultant', originalConsultant, consultant);
return res.send({_id, ...consultant});
const responseConsultant = {_id, ...consultant};
emitEntityEvent(req, SocketEventTypes.EntityUpdated, CollectionNames.CONSULTANTS, _id, responseConsultant);
return res.send(responseConsultant);
}

const slug = slugify(`${consultant.firstName}-${consultant.name}`).toLowerCase();
Expand All @@ -31,5 +34,6 @@ export const saveConsultant = async (req: ConfacRequest, res: Response) => {
audit: createAudit(req.user),
});
const [createdConsultant] = inserted.ops;
emitEntityEvent(req, SocketEventTypes.EntityCreated, CollectionNames.CONSULTANTS, createdConsultant._id, createdConsultant);
return res.send(createdConsultant);
};
42 changes: 31 additions & 11 deletions backend/src/controllers/invoices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import {ObjectID, Db} from 'mongodb';
import {IInvoice, INVOICE_EXCEL_HEADERS} from '../models/invoices';
import {IAttachmentCollection} from '../models/attachments';
import {createPdf, createXml} from './utils';
import {CollectionNames, IAttachment, createAudit, updateAudit} from '../models/common';
import {CollectionNames, IAttachment, SocketEventTypes, createAudit, updateAudit} from '../models/common';
import {IProjectMonth} from '../models/projectsMonth';
import {ConfacRequest, Jwt} from '../models/technical';
import {saveAudit} from './utils/audit-logs';
import {emitEntityEvent} from './utils/entity-events';


const createInvoice = async (invoice: IInvoice, db: Db, pdfBuffer: Buffer, user: Jwt) => {
Expand Down Expand Up @@ -49,14 +50,16 @@ const moveProjectMonthAttachmentsToInvoice = async (invoice: IInvoice, projectMo
const projectMonth = await db.collection<IProjectMonth>(CollectionNames.PROJECTS_MONTH).findOne({_id: projectMonthId});
const updatedAttachmentDetails = projectMonth ? [...invoice.attachments, ...projectMonth?.attachments] : invoice.attachments;

const inserted = await db.collection<IInvoice>(CollectionNames.INVOICES)
const updateInvoiceResult = await db.collection<IInvoice>(CollectionNames.INVOICES)
.findOneAndUpdate({_id: new ObjectID(invoice._id)}, {$set: {attachments: updatedAttachmentDetails}}, {returnOriginal: false});
const updatedInvoice = inserted.value;
const updatedInvoice = updateInvoiceResult.value;

const updateProjectMonthResult = await db.collection<IProjectMonth>(CollectionNames.PROJECTS_MONTH).findOneAndUpdate({_id: projectMonthId}, {$set: {attachments: []}});
const updatedProjectMonth = updateProjectMonthResult.value;

await db.collection<IProjectMonth>(CollectionNames.PROJECTS_MONTH).findOneAndUpdate({_id: projectMonthId}, {$set: {attachments: []}});
await db.collection(CollectionNames.ATTACHMENTS_PROJECT_MONTH).findOneAndDelete({_id: projectMonthId});

return updatedInvoice;
return {updatedInvoice, updatedProjectMonth};
};


Expand Down Expand Up @@ -114,11 +117,15 @@ export const createInvoiceController = async (req: ConfacRequest, res: Response)

if (invoice.projectMonth) {
const projectMonthId = new ObjectID(invoice.projectMonth.projectMonthId);
const updatedInvoice = await moveProjectMonthAttachmentsToInvoice(createdInvoice, projectMonthId, req.db);

const {updatedInvoice, updatedProjectMonth} = await moveProjectMonthAttachmentsToInvoice(createdInvoice, projectMonthId, req.db);
emitEntityEvent(req, SocketEventTypes.EntityUpdated, CollectionNames.INVOICES, updatedInvoice!._id, updatedInvoice);
if (updatedProjectMonth) {
emitEntityEvent(req, SocketEventTypes.EntityUpdated, CollectionNames.PROJECTS_MONTH, updatedProjectMonth!._id, updatedProjectMonth);
}
return res.send(updatedInvoice);
}

emitEntityEvent(req, SocketEventTypes.EntityCreated, CollectionNames.INVOICES, createdInvoice._id, createdInvoice);
return res.send(createdInvoice);
};

Expand Down Expand Up @@ -172,15 +179,20 @@ export const updateInvoiceController = async (req: ConfacRequest, res: Response)
.findOneAndUpdate({_id: new ObjectID(invoice.projectMonth.projectMonthId)}, {$set: {verified: invoice.verified}});
}

const invoiceResponse = {_id, ...invoice};
const result: Array<any> = [{
type: 'invoice',
model: {_id, ...invoice},
model: invoiceResponse,
}];
emitEntityEvent(req, SocketEventTypes.EntityUpdated, CollectionNames.INVOICES, invoiceResponse._id, invoiceResponse);

if (projectMonth && projectMonth.ok && projectMonth.value) {
const projectMonthResponse = projectMonth.value;
result.push({
type: 'projectMonth',
model: projectMonth.value,
model: projectMonthResponse,
});
emitEntityEvent(req, SocketEventTypes.EntityUpdated, CollectionNames.PROJECTS_MONTH, projectMonthResponse._id, projectMonthResponse);
}

return res.send(result);
Expand All @@ -189,7 +201,7 @@ export const updateInvoiceController = async (req: ConfacRequest, res: Response)


/** Hard invoice delete: There is no coming back from this one */
export const deleteInvoiceController = async (req: Request, res: Response) => {
export const deleteInvoiceController = async (req: ConfacRequest, res: Response) => {
const { id: invoiceId }: { id: string; } = req.body;

const invoice = await req.db.collection<IInvoice>(CollectionNames.INVOICES).findOne({ _id: new ObjectID(invoiceId) });
Expand All @@ -213,12 +225,20 @@ export const deleteInvoiceController = async (req: Request, res: Response) => {

const projectMonthCollection = req.db.collection(CollectionNames.PROJECTS_MONTH);
const attachments = invoice.attachments.filter(a => a.type !== 'pdf');
await projectMonthCollection.findOneAndUpdate({ _id: new ObjectID(invoice.projectMonth.projectMonthId) }, { $set: { attachments } });

const projectMonthId = new ObjectID(invoice.projectMonth.projectMonthId);
const updateProjectMonthResult = await projectMonthCollection.findOneAndUpdate({ _id: projectMonthId }, { $set: { attachments } });
const updatedProjectMonth = updateProjectMonthResult.value;
if (updatedProjectMonth) {
emitEntityEvent(req, SocketEventTypes.EntityUpdated, CollectionNames.PROJECTS_MONTH, updatedProjectMonth._id, updatedProjectMonth);
}
}

await req.db.collection(CollectionNames.INVOICES).findOneAndDelete({ _id: new ObjectID(invoiceId) });
await req.db.collection(CollectionNames.ATTACHMENTS).findOneAndDelete({ _id: new ObjectID(invoiceId) });

emitEntityEvent(req, SocketEventTypes.EntityDeleted, CollectionNames.INVOICES, new ObjectID(invoiceId), null);

return res.send(invoiceId);
};

Expand Down
11 changes: 8 additions & 3 deletions backend/src/controllers/projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import {Request, Response} from 'express';
import moment from 'moment';
import {ObjectID} from 'mongodb';
import {IProject} from '../models/projects';
import {CollectionNames, createAudit, updateAudit} from '../models/common';
import {CollectionNames, createAudit, SocketEventTypes, updateAudit} from '../models/common';
import {ConfacRequest} from '../models/technical';
import {saveAudit} from './utils/audit-logs';
import {emitEntityEvent} from './utils/entity-events';

/** No longer in use: this is now done in the frontend */
export const findActiveProjectsForSelectedMonth = (selectedMonth: string, projects: IProject[]) => projects.filter(project => {
Expand Down Expand Up @@ -44,20 +45,24 @@ export const saveProject = async (req: ConfacRequest, res: Response) => {
const projectsColl = req.db.collection<IProject>(CollectionNames.PROJECTS);
const {value: originalProject} = await projectsColl.findOneAndUpdate({_id: new ObjectID(_id)}, {$set: project}, {returnOriginal: true});
await saveAudit(req, 'project', originalProject, project);
return res.send({_id, ...project});
const responseProject = {_id, ...project};
emitEntityEvent(req, SocketEventTypes.EntityUpdated, CollectionNames.PROJECTS, _id, responseProject);
return res.send(responseProject);
}

const inserted = await req.db.collection<Omit<IProject, '_id'>>(CollectionNames.PROJECTS).insertOne({
...project,
audit: createAudit(req.user),
});
const [createdProject] = inserted.ops;
emitEntityEvent(req, SocketEventTypes.EntityCreated, CollectionNames.PROJECTS, createdProject._id, createdProject);
return res.send(createdProject);
};


export const deleteProject = async (req: ConfacRequest, res: Response) => {
const id = req.body.id;
await req.db.collection(CollectionNames.PROJECTS).findOneAndDelete({ _id: new ObjectID(id) });
await req.db.collection(CollectionNames.PROJECTS).findOneAndDelete({_id: new ObjectID(id)});
emitEntityEvent(req, SocketEventTypes.EntityDeleted, CollectionNames.PROJECTS, id, null);
return res.send(id);
};
12 changes: 9 additions & 3 deletions backend/src/controllers/projectsMonth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import {Request, Response} from 'express';
import {ObjectID} from 'mongodb';
import moment from 'moment';
import {IProjectMonth, IProjectMonthOverview, TimesheetCheckAttachmentType} from '../models/projectsMonth';
import {CollectionNames, createAudit, updateAudit} from '../models/common';
import {CollectionNames, createAudit, SocketEventTypes, updateAudit} from '../models/common';
import {ConfacRequest} from '../models/technical';
import {saveAudit} from './utils/audit-logs';
import {emitEntityEvent} from './utils/entity-events';


export const getProjectsPerMonthController = async (req: Request, res: Response) => {
Expand Down Expand Up @@ -55,6 +56,8 @@ export const createProjectsMonthController = async (req: ConfacRequest, res: Res
return createdProjectMonth;
}));

emitEntityEvent(req, SocketEventTypes.EntityCreated, CollectionNames.PROJECTS_MONTH, null, createdProjectsMonth );

return res.send(createdProjectsMonth);
};

Expand All @@ -68,15 +71,17 @@ export const patchProjectsMonthController = async (req: ConfacRequest, res: Resp
const projMonthCollection = req.db.collection<IProjectMonth>(CollectionNames.PROJECTS_MONTH);
const {value: originalProjectMonth} = await projMonthCollection.findOneAndUpdate({_id: new ObjectID(_id)}, {$set: projectMonth}, {returnOriginal: true});
await saveAudit(req, 'projectMonth', originalProjectMonth, projectMonth);
return res.send({_id, ...projectMonth});
const projectMonthResponse = {_id, ...projectMonth};
emitEntityEvent(req, SocketEventTypes.EntityUpdated, CollectionNames.PROJECTS_MONTH, projectMonthResponse._id, projectMonthResponse);
return res.send(projectMonthResponse);
}

const inserted = await req.db.collection<IProjectMonth>(CollectionNames.PROJECTS_MONTH).insertOne({
...projectMonth,
audit: createAudit(req.user),
});
const [createdProjectMonth] = inserted.ops;

emitEntityEvent(req, SocketEventTypes.EntityCreated, CollectionNames.PROJECTS_MONTH, createdProjectMonth._id, createdProjectMonth);
return res.send(createdProjectMonth);
};

Expand All @@ -86,5 +91,6 @@ export const deleteProjectsMonthController = async (req: ConfacRequest, res: Res
const id = req.body.id;
await req.db.collection(CollectionNames.PROJECTS_MONTH).findOneAndDelete({ _id: new ObjectID(id) });
await req.db.collection(CollectionNames.ATTACHMENTS_PROJECT_MONTH).findOneAndDelete({ _id: new ObjectID(id) });
emitEntityEvent(req, SocketEventTypes.EntityDeleted, CollectionNames.PROJECTS_MONTH, id, null);
return res.send(id);
};
4 changes: 3 additions & 1 deletion backend/src/controllers/tests/1-clients.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import { IAudit } from '../../models/common';
import { ObjectID } from 'mongodb'
import { IClient } from '../../models/clients'
import { Jwt } from '../../models/technical'
import { saveClient } from '../clients'
import { saveClient } from '../clients';
import { SocketServerMock } from 'socket.io-mock-ts';

const fakeUser: Jwt = {
data: {
Expand Down Expand Up @@ -44,6 +45,7 @@ describe('clients controller :: saveClient creation', () => {
user: fakeUser,
body: {...fakeClient, name: 'Company X'},
db: fakeDb,
io: new SocketServerMock() as any,
} as ConfacRequest;

const res = {
Expand Down
2 changes: 2 additions & 0 deletions backend/src/controllers/tests/2-consultants.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { Jwt } from '../../models/technical'
import { saveConsultant } from '../consultants';
import { IConsultant } from '../../models/consultants';
import { MongoMemoryServer } from 'mongodb-memory-server';
import { SocketServerMock } from 'socket.io-mock-ts';

const fakeUser: Jwt = {
data: { _id: '_id', email: 'string', firstName: 'first', name: 'name', alias: 'alias', active: true },
Expand All @@ -29,6 +30,7 @@ const createFakeRequestAndResponse = (db: Db, consultant: Partial<IConsultant> |
user: fakeUser,
body: {...fakeConsultant, ...(consultant || {})},
db,
io: new SocketServerMock() as any,
} as ConfacRequest;

const res = {
Expand Down
4 changes: 3 additions & 1 deletion backend/src/controllers/tests/3-projectsMonth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import request from 'supertest';
import express from 'express';
import projectsRouter from '../../routes/projects';
import bodyParser from 'body-parser';
import { SocketServerMock } from 'socket.io-mock-ts';



Expand All @@ -20,7 +21,8 @@ const getFakeDb: jest.Mock<Db> = jest.fn();
const app = express();
app.use(bodyParser.json());
app.use((req: Request, res: Response, next: NextFunction) => {
req.db = getFakeDb()
req.db = getFakeDb();
req.io = new SocketServerMock() as any;
next();
});
app.use('/', projectsRouter);
Expand Down
4 changes: 3 additions & 1 deletion backend/src/controllers/tests/4-projectsMonth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import request from 'supertest';
import express from 'express';
import projectsRouter from '../../routes/projects';
import bodyParser from 'body-parser';
import { SocketServerMock } from 'socket.io-mock-ts';



Expand All @@ -19,7 +20,8 @@ const getFakeDb: jest.Mock<Db> = jest.fn();
const app = express();
app.use(bodyParser.json());
app.use((req: Request, res: Response, next: NextFunction) => {
req.db = getFakeDb()
req.db = getFakeDb();
req.io = new SocketServerMock() as any;
next();
});
app.use('/', projectsRouter);
Expand Down
Loading

0 comments on commit 5d385fb

Please sign in to comment.