Refactor UpdateManager and enhance object update handling
All checks were successful
farmcontrol/farmcontrol-ws/pipeline/head This commit looks good

- Updated the `distributeUpdate` function to include the object ID in the published message, improving traceability.
- Refactored the `UpdateManager` class to streamline object update emissions, consolidating logic into the new `emitObjectUpdate` method for better maintainability.
- Enhanced test cases to ensure proper handling of object updates with the new structure, verifying that emitted events include the correct object ID and data.
- Modified the `objectViewSchema` to remove the `_reference` field and added a new `viewMode` field for improved schema clarity.
This commit is contained in:
Tom Butcher 2026-09-03 22:25:32 +01:00
parent c94dc058ef
commit 9447a3206e
4 changed files with 31 additions and 30 deletions

View File

@ -1,9 +1,7 @@
import mongoose from 'mongoose'; import mongoose from 'mongoose';
import { generateId } from '../../utils.js';
const { Schema } = mongoose; const { Schema } = mongoose;
const objectViewSchema = new mongoose.Schema({ const objectViewSchema = new mongoose.Schema({
_reference: { type: String, default: () => generateId()() },
user: { user: {
type: Schema.Types.ObjectId, type: Schema.Types.ObjectId,
ref: 'user', ref: 'user',
@ -35,6 +33,10 @@ const objectViewSchema = new mongoose.Schema({
type: Schema.Types.Mixed, type: Schema.Types.Mixed,
default: () => ({}), default: () => ({}),
}, },
viewMode: {
type: Schema.Types.Mixed,
default: null,
},
createdAt: { createdAt: {
type: Date, type: Date,
required: true, required: true,

View File

@ -555,7 +555,7 @@ async function getAuditLogs(idOrIds) {
} }
async function distributeUpdate(value, id, type) { async function distributeUpdate(value, id, type) {
await natsServer.publish(`${type}s.${id}.object`, value); await natsServer.publish(`${type}s.${id}.object`, { ...value, _id: id });
} }
async function distributeStats(value, type) { async function distributeStats(value, type) {

View File

@ -184,7 +184,7 @@ describe('UpdateManager', () => {
); );
const natsCallback = natsServer.subscribe.mock.calls[0][2]; const natsCallback = natsServer.subscribe.mock.calls[0][2];
const data = { status: 'idle' }; const data = { _id: '123', status: 'idle' };
natsCallback('printers.123.object', data); natsCallback('printers.123.object', data);
expect(mockSocketClient.socket.emit).toHaveBeenCalledWith( expect(mockSocketClient.socket.emit).toHaveBeenCalledWith(
@ -192,7 +192,7 @@ describe('UpdateManager', () => {
{ {
_id: '123', _id: '123',
objectType: 'printer', objectType: 'printer',
object: data object: { status: 'idle' }
} }
); );
}); });
@ -209,7 +209,7 @@ describe('UpdateManager', () => {
); );
const natsCallback = natsServer.subscribe.mock.calls[0][2]; const natsCallback = natsServer.subscribe.mock.calls[0][2];
const data = { status: 'idle' }; const data = { _id: '456', status: 'idle' };
natsCallback('printers.456.object', data); natsCallback('printers.456.object', data);
expect(mockSocketClient.socket.emit).toHaveBeenCalledWith( expect(mockSocketClient.socket.emit).toHaveBeenCalledWith(
@ -217,7 +217,7 @@ describe('UpdateManager', () => {
{ {
_id: '456', _id: '456',
objectType: 'printer', objectType: 'printer',
object: data object: { status: 'idle' }
} }
); );
}); });
@ -227,7 +227,10 @@ describe('UpdateManager', () => {
await updateManager.subscribeToAllObjectUpdates('printer'); await updateManager.subscribeToAllObjectUpdates('printer');
const allUpdatesCallback = natsServer.subscribe.mock.calls[1][2]; const allUpdatesCallback = natsServer.subscribe.mock.calls[1][2];
allUpdatesCallback('printers.123.object', { status: 'idle' }); allUpdatesCallback('printers.123.object', {
_id: '123',
status: 'idle'
});
expect(mockSocketClient.socket.emit).toHaveBeenCalledTimes(0); expect(mockSocketClient.socket.emit).toHaveBeenCalledTimes(0);
}); });

View File

@ -303,13 +303,7 @@ export class UpdateManager {
const owner = this.socketClient.socketId; const owner = this.socketClient.socketId;
await natsServer.subscribe(subject, owner, (key, value) => { await natsServer.subscribe(subject, owner, (key, value) => {
const expandedValue = expandObjectIds(value); this.emitObjectUpdate(objectType, value);
logger.trace('Object update event:', id);
this.socketClient.socket.emit('objectUpdate', {
_id: id,
objectType: objectType,
object: { ...expandedValue }
});
}); });
this.objectUpdateSubscriptions.add( this.objectUpdateSubscriptions.add(
@ -319,12 +313,20 @@ export class UpdateManager {
return { success: true }; return { success: true };
} }
extractIdFromUpdateSubject(subject) { emitObjectUpdate(objectType, value) {
const parts = subject.split('.'); const expandedValue = expandObjectIds(value);
if (parts.length < 3 || parts[parts.length - 1] !== 'object') { if (!expandedValue || expandedValue._id == null) {
return null; logger.warn('Object update missing _id:', objectType, value);
return;
} }
return parts[parts.length - 2];
const { _id, ...object } = { ...expandedValue };
logger.trace('Object update event:', _id, objectType);
this.socketClient.socket.emit('objectUpdate', {
_id,
objectType,
object
});
} }
async subscribeToAllObjectUpdates(objectType) { async subscribeToAllObjectUpdates(objectType) {
@ -333,9 +335,9 @@ export class UpdateManager {
const owner = this.socketClient.socketId; const owner = this.socketClient.socketId;
await natsServer.subscribe(subject, owner, (key, value) => { await natsServer.subscribe(subject, owner, (key, value) => {
const id = this.extractIdFromUpdateSubject(key); const id = value?._id;
if (!id) { if (id == null) {
logger.warn('Unable to extract id from update subject:', key); logger.warn('Object update missing _id:', objectType, value);
return; return;
} }
@ -347,13 +349,7 @@ export class UpdateManager {
return; return;
} }
const expandedValue = expandObjectIds(value); this.emitObjectUpdate(objectType, value);
logger.trace('All object update event:', id, objectType);
this.socketClient.socket.emit('objectUpdate', {
_id: id,
objectType: objectType,
object: { ...expandedValue }
});
}); });
this.subscriptions.add(getSubscriptionKey(subject, owner)); this.subscriptions.add(getSubscriptionKey(subject, owner));