Skip to content

Commit 857272e

Browse files
fix(compass-app-stores): fixes stale listeners getting triggered on globalAppRegistry events (#6019)
1 parent edfcdf6 commit 857272e

File tree

10 files changed

+570
-296
lines changed

10 files changed

+570
-296
lines changed

packages/compass-aggregations/src/modules/aggregation.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,7 @@ const fetchAggregationData = (
400400
logger: { log, mongoLogId },
401401
track,
402402
connectionInfoAccess,
403-
globalAppRegistry,
403+
connectionScopedAppRegistry,
404404
}
405405
) => {
406406
const {
@@ -452,7 +452,7 @@ const fetchAggregationData = (
452452
});
453453

454454
if (isMergeOrOut) {
455-
globalAppRegistry.emit(
455+
connectionScopedAppRegistry.emit(
456456
'agg-pipeline-out-executed',
457457
getDestinationNamespaceFromStage(
458458
namespace,

packages/compass-aggregations/src/modules/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,9 @@ export type PipelineBuilderExtraArgs = {
109109
instance: MongoDBInstance;
110110
dataService: DataService;
111111
connectionInfoAccess: ConnectionInfoAccess;
112-
connectionScopedAppRegistry: ConnectionScopedAppRegistry<'open-export'>;
112+
connectionScopedAppRegistry: ConnectionScopedAppRegistry<
113+
'open-export' | 'view-edited' | 'agg-pipeline-out-executed'
114+
>;
113115
};
114116

115117
export type PipelineBuilderThunkDispatch<A extends Action = AnyAction> =

packages/compass-aggregations/src/modules/pipeline-builder/stage-editor.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ export const runStage = (
389389
return async (
390390
dispatch,
391391
getState,
392-
{ pipelineBuilder, preferences, globalAppRegistry }
392+
{ pipelineBuilder, preferences, connectionScopedAppRegistry }
393393
) => {
394394
const {
395395
dataService: { dataService },
@@ -442,7 +442,7 @@ export const runStage = (
442442
id: idx,
443443
previewDocs: result.map((doc) => new HadronDocument(doc)),
444444
});
445-
globalAppRegistry.emit(
445+
connectionScopedAppRegistry.emit(
446446
'agg-pipeline-out-executed',
447447
getDestinationNamespaceFromStage(
448448
namespace,

packages/compass-aggregations/src/modules/pipeline-builder/text-editor-output-stage.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ export const runPipelineWithOutputStage = (): PipelineBuilderThunkAction<
131131
return async (
132132
dispatch,
133133
getState,
134-
{ pipelineBuilder, preferences, globalAppRegistry }
134+
{ pipelineBuilder, preferences, connectionScopedAppRegistry }
135135
) => {
136136
const {
137137
autoPreview,
@@ -173,7 +173,7 @@ export const runPipelineWithOutputStage = (): PipelineBuilderThunkAction<
173173
dispatch({
174174
type: OutputStageActionTypes.FetchSucceded,
175175
});
176-
globalAppRegistry.emit(
176+
connectionScopedAppRegistry.emit(
177177
'agg-pipeline-out-executed',
178178
getDestinationNamespaceFromStage(
179179
namespace,

packages/compass-aggregations/src/modules/update-view.spec.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,26 @@ import { ERROR_UPDATING_VIEW, updateView } from './update-view';
44
import { createNoopLogger } from '@mongodb-js/compass-logging/provider';
55
import { createNoopTrack } from '@mongodb-js/compass-telemetry/provider';
66
import AppRegistry from 'hadron-app-registry';
7-
import { TEST_CONNECTION_INFO } from '@mongodb-js/compass-connections/provider';
7+
import {
8+
type ConnectionInfoAccess,
9+
ConnectionScopedAppRegistryImpl,
10+
TEST_CONNECTION_INFO,
11+
} from '@mongodb-js/compass-connections/provider';
812

913
describe('update-view module', function () {
14+
const globalAppRegistry = new AppRegistry();
15+
const connectionInfoAccess: ConnectionInfoAccess = {
16+
getCurrentConnectionInfo() {
17+
return TEST_CONNECTION_INFO;
18+
},
19+
};
20+
const connectionScopedAppRegistry = new ConnectionScopedAppRegistryImpl(
21+
globalAppRegistry.emit.bind(globalAppRegistry),
22+
connectionInfoAccess
23+
);
1024
const thunkArg = {
11-
globalAppRegistry: new AppRegistry(),
25+
globalAppRegistry,
26+
connectionScopedAppRegistry,
1227
localAppRegistry: new AppRegistry(),
1328
pipelineBuilder: {
1429
getPipelineFromStages() {

packages/compass-aggregations/src/modules/update-view.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ export const updateView = (): PipelineBuilderThunkAction<Promise<void>> => {
8787
workspaces,
8888
logger: { debug },
8989
track,
90-
globalAppRegistry,
90+
connectionScopedAppRegistry,
9191
connectionInfoAccess,
9292
}
9393
) => {
@@ -123,7 +123,7 @@ export const updateView = (): PipelineBuilderThunkAction<Promise<void>> => {
123123
connectionInfo
124124
);
125125
debug('selecting namespace', viewNamespace);
126-
globalAppRegistry.emit('view-edited', viewNamespace);
126+
connectionScopedAppRegistry.emit('view-edited', viewNamespace);
127127
workspaces.openCollectionWorkspace(connectionInfo.id, viewNamespace);
128128
} catch (e: any) {
129129
debug('Unexpected error updating view', e);

packages/compass-app-stores/src/stores/instance-store.spec.ts

Lines changed: 92 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@ describe('InstanceStore [Store]', function () {
121121
let connectedInstance: MongoDBInstance;
122122
let initialInstanceRefreshedPromise: Promise<unknown>;
123123
beforeEach(function () {
124+
sinon
125+
.stub(connectionsManager, 'getDataServiceForConnection')
126+
.returns(dataService);
124127
connectionsManager.emit(
125128
ConnectionsManagerEvents.ConnectionAttemptSuccessful,
126129
connectedConnectionInfoId,
@@ -330,35 +333,35 @@ describe('InstanceStore [Store]', function () {
330333
});
331334
});
332335

333-
const createdEvents = ['agg-pipeline-out-executed'];
334-
335-
for (const evt of createdEvents) {
336-
context(`on '${evt}' event`, function () {
337-
it('should add collection to the databases collections', function () {
338-
globalAppRegistry.emit(evt, 'foo.qux');
339-
expect(
340-
connectedInstance.databases.get('foo')?.collections
341-
).to.have.lengthOf(3);
342-
expect(
343-
connectedInstance.databases
344-
.get('foo')
345-
?.collections.get('foo.qux', '_id')
346-
).to.exist;
336+
context(`on 'agg-pipeline-out-executed' event`, function () {
337+
it('should add collection to the databases collections', function () {
338+
globalAppRegistry.emit('agg-pipeline-out-executed', 'foo.qux', {
339+
connectionId: connectedConnectionInfoId,
347340
});
341+
expect(
342+
connectedInstance.databases.get('foo')?.collections
343+
).to.have.lengthOf(3);
344+
expect(
345+
connectedInstance.databases
346+
.get('foo')
347+
?.collections.get('foo.qux', '_id')
348+
).to.exist;
349+
});
348350

349-
it("should add new database and add collection to its collections if database doesn't exist yet", function () {
350-
globalAppRegistry.emit(evt, 'bar.qux');
351-
expect(connectedInstance.databases).to.have.lengthOf(2);
352-
expect(connectedInstance.databases.get('bar')).to.exist;
353-
expect(
354-
connectedInstance.databases.get('bar')?.collections
355-
).to.have.lengthOf(1);
356-
expect(
357-
connectedInstance.databases.get('bar')?.collections.get('bar.qux')
358-
).to.exist;
351+
it("should add new database and add collection to its collections if database doesn't exist yet", function () {
352+
globalAppRegistry.emit('agg-pipeline-out-executed', 'bar.qux', {
353+
connectionId: connectedConnectionInfoId,
359354
});
355+
expect(connectedInstance.databases).to.have.lengthOf(2);
356+
expect(connectedInstance.databases.get('bar')).to.exist;
357+
expect(
358+
connectedInstance.databases.get('bar')?.collections
359+
).to.have.lengthOf(1);
360+
expect(
361+
connectedInstance.databases.get('bar')?.collections.get('bar.qux')
362+
).to.exist;
360363
});
361-
}
364+
});
362365

363366
context(`on 'collection-created' event`, function () {
364367
it('should not respond when the connectionId does not matches the connectionId for the instance', function () {
@@ -469,4 +472,68 @@ describe('InstanceStore [Store]', function () {
469472
});
470473
});
471474
});
475+
476+
context('when disconnected', function () {
477+
it('should remove the instance from InstancesManager and should not perform any actions on the stale instance', async function () {
478+
// first connect
479+
connectionsManager.emit(
480+
ConnectionsManagerEvents.ConnectionAttemptSuccessful,
481+
connectedConnectionInfoId,
482+
dataService
483+
);
484+
485+
// setup a spy on old instance
486+
const oldInstance = instancesManager.getMongoDBInstanceForConnection(
487+
connectedConnectionInfoId
488+
);
489+
const oldFetchDatabasesSpy = sinon.spy(oldInstance, 'fetchDatabases');
490+
491+
// now disconnect
492+
connectionsManager.emit(
493+
ConnectionsManagerEvents.ConnectionDisconnected,
494+
connectedConnectionInfoId
495+
);
496+
497+
// there is no instance in store InstancesManager now
498+
expect(() =>
499+
instancesManager.getMongoDBInstanceForConnection(
500+
connectedConnectionInfoId
501+
)
502+
).to.throw;
503+
504+
// lets connect again and ensure that old instance does not receive events anymore
505+
const newDataService = createDataService();
506+
sinon
507+
.stub(connectionsManager, 'getDataServiceForConnection')
508+
.returns(dataService);
509+
connectionsManager.emit(
510+
ConnectionsManagerEvents.ConnectionAttemptSuccessful,
511+
connectedConnectionInfoId,
512+
newDataService
513+
);
514+
515+
// setup a spy on new instance
516+
const newInstance = instancesManager.getMongoDBInstanceForConnection(
517+
connectedConnectionInfoId
518+
);
519+
520+
let resolveFetchDatabasePromise: (() => void) | undefined;
521+
const fetchDatabasePromise = new Promise<void>((resolve) => {
522+
resolveFetchDatabasePromise = resolve;
523+
});
524+
const newFetchDatabasesSpy = sinon
525+
.stub(newInstance, 'fetchDatabases')
526+
.callsFake(() => {
527+
resolveFetchDatabasePromise?.();
528+
return Promise.resolve();
529+
});
530+
531+
globalAppRegistry.emit('refresh-databases', {
532+
connectionId: connectedConnectionInfoId,
533+
});
534+
await fetchDatabasePromise;
535+
expect(oldFetchDatabasesSpy).to.not.be.called;
536+
expect(newFetchDatabasesSpy).to.be.called;
537+
});
538+
});
472539
});

0 commit comments

Comments
 (0)