Add support for subscribing and unsubscribing to all object updates
All checks were successful
farmcontrol/farmcontrol-ws/pipeline/head This commit looks good
All checks were successful
farmcontrol/farmcontrol-ws/pipeline/head This commit looks good
- Implemented `subscribeToAllObjectUpdates` and `unsubscribeAllObjectUpdates` methods in the `UpdateManager` to handle bulk updates for object types. - Updated `SocketHost` and `SocketUser` classes to listen for new socket events related to all object updates. - Enhanced test coverage for the new subscription methods to ensure correct functionality and integration with the socket client.
This commit is contained in:
parent
7b84114588
commit
ffcc7cd0aa
@ -34,9 +34,11 @@ jest.unstable_mockModule('../../updates/updatemanager.js', () => ({
|
||||
subscribeToObjectNew: jest.fn(),
|
||||
subscribeToObjectDelete: jest.fn(),
|
||||
subscribeToObjectUpdate: jest.fn(),
|
||||
subscribeToAllObjectUpdates: jest.fn(),
|
||||
removeObjectNewListener: jest.fn(),
|
||||
removeObjectDeleteListener: jest.fn(),
|
||||
removeObjectUpdateListener: jest.fn(),
|
||||
removeAllObjectUpdatesListener: jest.fn(),
|
||||
removeAllListeners: jest.fn()
|
||||
}))
|
||||
}));
|
||||
|
||||
@ -46,10 +46,18 @@ export class SocketHost {
|
||||
'subscribeToObjectUpdates',
|
||||
this.handleSubscribeToObjectUpdatesEvent.bind(this)
|
||||
);
|
||||
this.socket.on(
|
||||
'subscribeToAllObjectUpdates',
|
||||
this.handleSubscribeToAllObjectUpdatesEvent.bind(this)
|
||||
);
|
||||
this.socket.on(
|
||||
'unsubscribeToObjectUpdates',
|
||||
this.handleUnsubscribeToObjectUpdatesEvent.bind(this)
|
||||
);
|
||||
this.socket.on(
|
||||
'unsubscribeAllObjectUpdates',
|
||||
this.handleUnsubscribeAllObjectUpdatesEvent.bind(this)
|
||||
);
|
||||
this.socket.on(
|
||||
'subscribeToObjectActions',
|
||||
this.handleSubscribeToObjectActions.bind(this)
|
||||
@ -217,6 +225,10 @@ export class SocketHost {
|
||||
);
|
||||
}
|
||||
|
||||
async handleSubscribeToAllObjectUpdatesEvent(data) {
|
||||
await this.updateManager.subscribeToAllObjectUpdates(data.objectType);
|
||||
}
|
||||
|
||||
async handleSubscribeToObjectActions(data) {
|
||||
await this.actionManager.subscribeToObjectActions(
|
||||
data._id,
|
||||
@ -247,6 +259,10 @@ export class SocketHost {
|
||||
);
|
||||
}
|
||||
|
||||
async handleUnsubscribeAllObjectUpdatesEvent(data) {
|
||||
await this.updateManager.removeAllObjectUpdatesListener(data.objectType);
|
||||
}
|
||||
|
||||
async setDevicesState(state, online, connectedAt) {
|
||||
logger.info('Setting devices state to', state, 'and online to', online);
|
||||
|
||||
|
||||
@ -60,6 +60,10 @@ export class SocketUser {
|
||||
'subscribeToObjectUpdate',
|
||||
this.handleSubscribeToObjectUpdateEvent.bind(this)
|
||||
);
|
||||
this.socket.on(
|
||||
'subscribeToAllObjectUpdates',
|
||||
this.handleSubscribeToAllObjectUpdatesEvent.bind(this)
|
||||
);
|
||||
this.socket.on(
|
||||
'subscribeToObjectEvent',
|
||||
this.handleSubscribeToObjectEventEvent.bind(this)
|
||||
@ -72,6 +76,10 @@ export class SocketUser {
|
||||
'unsubscribeObjectUpdate',
|
||||
this.handleUnsubscribeToObjectUpdateEvent.bind(this)
|
||||
);
|
||||
this.socket.on(
|
||||
'unsubscribeAllObjectUpdates',
|
||||
this.handleUnsubscribeAllObjectUpdatesEvent.bind(this)
|
||||
);
|
||||
this.socket.on(
|
||||
'unsubscribeObjectEvent',
|
||||
this.handleUnsubscribeObjectEventEvent.bind(this)
|
||||
@ -253,6 +261,15 @@ export class SocketUser {
|
||||
}
|
||||
}
|
||||
|
||||
async handleSubscribeToAllObjectUpdatesEvent(data, callback) {
|
||||
const result = await this.updateManager.subscribeToAllObjectUpdates(
|
||||
data.objectType
|
||||
);
|
||||
if (typeof callback === 'function') {
|
||||
callback(result);
|
||||
}
|
||||
}
|
||||
|
||||
async handleSubscribeToObjectEventEvent(data) {
|
||||
await this.eventManager.subscribeToObjectEvent(
|
||||
data._id,
|
||||
@ -279,6 +296,10 @@ export class SocketUser {
|
||||
);
|
||||
}
|
||||
|
||||
async handleUnsubscribeAllObjectUpdatesEvent(data) {
|
||||
await this.updateManager.removeAllObjectUpdatesListener(data.objectType);
|
||||
}
|
||||
|
||||
async handleUnsubscribeObjectEventEvent(data) {
|
||||
await this.eventManager.removeObjectEventsListener(
|
||||
data._id,
|
||||
|
||||
@ -198,6 +198,41 @@ describe('UpdateManager', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('subscribeToAllObjectUpdates', () => {
|
||||
it('should subscribe to all update events for an object type', async () => {
|
||||
await updateManager.subscribeToAllObjectUpdates('printer');
|
||||
|
||||
expect(natsServer.subscribe).toHaveBeenCalledWith(
|
||||
'printers.*.object',
|
||||
'test-socket-id',
|
||||
expect.any(Function)
|
||||
);
|
||||
|
||||
const natsCallback = natsServer.subscribe.mock.calls[0][2];
|
||||
const data = { status: 'idle' };
|
||||
natsCallback('printers.456.object', data);
|
||||
|
||||
expect(mockSocketClient.socket.emit).toHaveBeenCalledWith(
|
||||
'objectUpdate',
|
||||
{
|
||||
_id: '456',
|
||||
objectType: 'printer',
|
||||
object: data
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should skip all-object updates when a specific subscription exists', async () => {
|
||||
await updateManager.subscribeToObjectUpdate('123', 'printer');
|
||||
await updateManager.subscribeToAllObjectUpdates('printer');
|
||||
|
||||
const allUpdatesCallback = natsServer.subscribe.mock.calls[1][2];
|
||||
allUpdatesCallback('printers.123.object', { status: 'idle' });
|
||||
|
||||
expect(mockSocketClient.socket.emit).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('remove methods', () => {
|
||||
it('should remove new listener', async () => {
|
||||
await updateManager.removeObjectNewListener('printer');
|
||||
@ -222,6 +257,14 @@ describe('UpdateManager', () => {
|
||||
'test-socket-id'
|
||||
);
|
||||
});
|
||||
|
||||
it('should remove all object updates listener', async () => {
|
||||
await updateManager.removeAllObjectUpdatesListener('printer');
|
||||
expect(natsServer.removeSubscription).toHaveBeenCalledWith(
|
||||
'printers.*.object',
|
||||
'test-socket-id'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeAllListeners', () => {
|
||||
|
||||
@ -202,6 +202,11 @@ export class UpdateManager {
|
||||
constructor(socketClient) {
|
||||
this.socketClient = socketClient;
|
||||
this.subscriptions = new Set();
|
||||
this.objectUpdateSubscriptions = new Set();
|
||||
}
|
||||
|
||||
getObjectUpdateSubscriptionKey(objectType, id) {
|
||||
return `${objectType}:${id}`;
|
||||
}
|
||||
|
||||
matchesObjectTypeFilter(objectType, filter, value) {
|
||||
@ -307,6 +312,50 @@ export class UpdateManager {
|
||||
});
|
||||
});
|
||||
|
||||
this.objectUpdateSubscriptions.add(
|
||||
this.getObjectUpdateSubscriptionKey(objectType, id)
|
||||
);
|
||||
this.subscriptions.add(getSubscriptionKey(subject, owner));
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
extractIdFromUpdateSubject(subject) {
|
||||
const parts = subject.split('.');
|
||||
if (parts.length < 3 || parts[parts.length - 1] !== 'object') {
|
||||
return null;
|
||||
}
|
||||
return parts[parts.length - 2];
|
||||
}
|
||||
|
||||
async subscribeToAllObjectUpdates(objectType) {
|
||||
logger.debug('Subscribing to all object updates...', objectType);
|
||||
const subject = `${objectType}s.*.object`;
|
||||
const owner = this.socketClient.socketId;
|
||||
|
||||
await natsServer.subscribe(subject, owner, (key, value) => {
|
||||
const id = this.extractIdFromUpdateSubject(key);
|
||||
if (!id) {
|
||||
logger.warn('Unable to extract id from update subject:', key);
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
this.objectUpdateSubscriptions.has(
|
||||
this.getObjectUpdateSubscriptionKey(objectType, id)
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const expandedValue = expandObjectIds(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));
|
||||
return { success: true };
|
||||
}
|
||||
@ -333,6 +382,18 @@ export class UpdateManager {
|
||||
const subject = `${objectType}s.${id}.object`;
|
||||
const owner = this.socketClient.socketId;
|
||||
|
||||
await natsServer.removeSubscription(subject, owner);
|
||||
this.objectUpdateSubscriptions.delete(
|
||||
this.getObjectUpdateSubscriptionKey(objectType, id)
|
||||
);
|
||||
this.subscriptions.delete(getSubscriptionKey(subject, owner));
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
async removeAllObjectUpdatesListener(objectType) {
|
||||
const subject = `${objectType}s.*.object`;
|
||||
const owner = this.socketClient.socketId;
|
||||
|
||||
await natsServer.removeSubscription(subject, owner);
|
||||
this.subscriptions.delete(getSubscriptionKey(subject, owner));
|
||||
return { success: true };
|
||||
@ -351,6 +412,7 @@ export class UpdateManager {
|
||||
|
||||
await Promise.all(removePromises);
|
||||
this.subscriptions.clear();
|
||||
this.objectUpdateSubscriptions.clear();
|
||||
logger.debug(`Removed ${removePromises.length} update listener(s)`);
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user