Skip to content

Commit e9bb72d

Browse files
committed
Various minor improvements
Contributed on behalf of STMicroelectronics Co-authored-by: Christian W. Damus <[email protected]> Co-authored-by: Stefan Dirix <[email protected]> Co-authored-by: Gabriel GASNOT <[email protected]> Co-authored-by: Camille Letavernier <[email protected]> Co-authored-by: FlorentPastorSTM <[email protected]> Co-authored-by: Anthony Fusco <[email protected]> * feat: add hook for modelhub fully initialized * feat: add an abstraction layer for logger * feat: multi client MAB * fix: properly handle ModelHub notifications in multi-window setup * fix: allow no command in CompoundCommand * fix: Quash unhandled exceptions in command execution Catch exceptions thrown by command execution in the exclusive lock for command execution sequencing and log them tersely at debug level so that they don't appear in the normal course but are still available for diagnosis in detailed debugging. * chore: migrate to Theia 1.58.4 specify inversify resolution instead of dependency * chore: bump hub initialization to 2min
1 parent 2a6fe83 commit e9bb72d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+5242
-3894
lines changed

docs/.prettierrc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"overrides": [
3+
{
4+
"files": "*.md",
5+
"options": {
6+
"tabWidth": 2,
7+
"singleQuote": true,
8+
"printWidth": 160
9+
}
10+
}
11+
]
12+
}

examples/theia/electron-app/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
},
2727
"devDependencies": {
2828
"@theia/cli": "*",
29-
"electron": "^23.3.13",
29+
"electron": "30.1.2",
3030
"rimraf": "^6.0.1"
3131
},
3232
"scripts": {

examples/theia/model-hub-integration/src/browser/model-form-widget.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ export abstract class ModelFormWidget<M extends NonNullable<object>>
5353
@inject(ILogger)
5454
protected readonly logger: ILogger;
5555

56+
protected readonly onContentChangeEmitter = new Emitter<void>();
57+
58+
get onContentChanged() {
59+
return this.onContentChangeEmitter.event;
60+
}
61+
5662
private _dirty = false;
5763
private dirtyChangedEmitter = new Emitter<void>();
5864
readonly onDirtyChanged = this.dirtyChangedEmitter.event;

ext/model-service-theia/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
},
2828
"engines": {
2929
"yarn": ">=1.7.0 <2.x.x",
30-
"node": ">=18.0.0"
30+
"node": ">=20.14.0"
3131
},
3232
"files": [
3333
"lib",
@@ -45,7 +45,7 @@
4545
"@types/chai": "^4.2.22",
4646
"@types/chai-like": "^1.1.1",
4747
"@types/mocha": "^9.0.0",
48-
"@types/node": "^16.10.3",
48+
"@types/node": "^20.14.0",
4949
"@types/sinon": "^10.0.13",
5050
"@types/sinon-chai": "^3.2.9",
5151
"@typescript-eslint/eslint-plugin": "^6.7.5",

ext/model-service-theia/src/browser/__tests__/frontend-model-hub-provider.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ const MODEL1 = { name: 'Model 1' };
4848
describe('FrontendModelHubProvider', () => {
4949
const appContext = 'test-app';
5050

51-
let fake: FakeModelHubProtocol;
51+
let fake: FakeModelHubProtocol<string>;
5252
let subscriber: FrontendModelHubSubscriber;
5353
let modelHubProvider: FrontendModelHubProvider<string>;
5454
let bombFrontendModelHubImpl: () => void;
@@ -62,7 +62,7 @@ describe('FrontendModelHubProvider', () => {
6262
FrontendModelHubProvider
6363
);
6464

65-
fake = container.get(FakeModelHubProtocol);
65+
fake = container.get<FakeModelHubProtocol<string>>(FakeModelHubProtocol);
6666
fake.setModel(MODEL1_ID, MODEL1);
6767
connectClient(fake, subscriber);
6868

ext/model-service-theia/src/browser/__tests__/frontend-model-hub-subscriber.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ describe('FrontendModelHubSubscriber', () => {
5454
const appContext = 'test-app';
5555

5656
let sandbox: sinon.SinonSandbox;
57-
let fake: FakeModelHubProtocol;
57+
let fake: FakeModelHubProtocol<string>;
5858
let subscriber: FrontendModelHubSubscriber;
5959
let tracker: ModelHubTracker;
6060

@@ -78,7 +78,7 @@ describe('FrontendModelHubSubscriber', () => {
7878
);
7979
tracker = container.get<ModelHubTracker>(ModelHubTracker);
8080

81-
fake = container.get(FakeModelHubProtocol);
81+
fake = container.get<FakeModelHubProtocol<string>>(FakeModelHubProtocol);
8282
fake.setModel(MODEL1_ID, MODEL1);
8383
connectClient(fake, subscriber);
8484

ext/model-service-theia/src/browser/__tests__/frontend-model-hub.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ describe('FrontendModelHub', () => {
5959

6060
let sandbox: sinon.SinonSandbox;
6161
let modelHub: FrontendModelHub;
62-
let fake: FakeModelHubProtocol;
62+
let fake: FakeModelHubProtocol<string>;
6363
let provider: FrontendModelHubProvider;
6464

6565
beforeEach(async () => {
@@ -73,7 +73,7 @@ describe('FrontendModelHub', () => {
7373
FrontendModelHubSubscriber
7474
);
7575

76-
fake = container.get(FakeModelHubProtocol);
76+
fake = container.get<FakeModelHubProtocol<string>>(FakeModelHubProtocol);
7777
fake.setModel(MODEL1_ID, MODEL1);
7878
connectClient(fake, subscriber);
7979
});

ext/model-service-theia/src/browser/frontend-model-hub-provider.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ export function bindFrontendModelHubProvider(bind: interfaces.Bind): void {
5050
sub.onModelHubCreated = (createdContext) => {
5151
if (createdContext === context) {
5252
try {
53-
resolve(child.get(FrontendModelHubImpl));
53+
resolve(
54+
child.get<FrontendModelHubImpl<string>>(FrontendModelHubImpl)
55+
);
5456
} catch (error) {
5557
reject(error);
5658
}

ext/model-service-theia/src/browser/frontend-model-hub.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import {
1717
ModelHub,
1818
ModelHubSubscription,
19+
ModelServiceSubscription,
1920
} from '@eclipse-emfcloud/model-service';
2021
import { Diagnostic } from '@eclipse-emfcloud/model-validation';
2122
import { ILogger } from '@theia/core';
@@ -80,11 +81,16 @@ export type FrontendModelHub<K = string> = {
8081
| 'getModelService'
8182
| 'liveValidation'
8283
| 'getModelAccessorBus'
84+
| 'subscribe'
8385
>]: MakeAsync<ModelHub<K, string>[key]>;
8486
} & {
8587
readonly context: string;
8688
readonly isDisposed: boolean;
8789
getModelAccessorBus: () => FrontendModelAccessorBus;
90+
subscribe<M extends object = object>(): Promise<ModelHubSubscription<K, M>>;
91+
subscribe<M extends object = object>(
92+
...modelIds: K[]
93+
): Promise<ModelServiceSubscription<K, M>>;
8894
};
8995

9096
export const FrontendModelHubContext = Symbol('FrontendModelHubContext');

ext/model-service-theia/src/node/__tests__/model-accessor-bus-server.spec.ts

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import {
4242
RpcConnectionFactory,
4343
bindFakeRpcConnectionFactory,
4444
} from './fake-json-rpc';
45+
import { Emitter, Event } from '@theia/core';
4546

4647
type FakeModel = Record<string, unknown>;
4748
const FAKE_MODEL_ID = 'test.fake-model';
@@ -58,26 +59,44 @@ function createTestContainer(contrib: ModelServiceContribution): Container {
5859
type FullModelAccessorBusServer = ModelAccessorBusServer<string> & {
5960
getModelAccessorBus: (context: string) => Promise<ModelAccessorBus>;
6061
};
62+
6163
describe('Model Accessor Bus Server', () => {
6264
const appContext = 'test-app';
6365

6466
let sandbox: sinon.SinonSandbox;
6567
let modelAccessorBusServer: ModelAccessorBusServer;
68+
let modelAccessorBusServer2: ModelAccessorBusServer;
69+
6670
let fakeContribution: FakeContribution;
6771
let clientProxy: {
68-
[key in keyof ModelAccessorBusClient]: sinon.SinonSpy<
69-
Parameters<ModelAccessorBusClient[key]>
70-
>;
72+
onAccessorChanged: sinon.SinonSpy;
73+
closeSubscription: sinon.SinonSpy;
74+
onDidOpenConnection: Event<void>;
75+
onDidCloseConnection: Event<void>;
76+
closeConnection: () => void;
77+
};
78+
let clientProxy2: {
79+
onAccessorChanged: sinon.SinonSpy;
80+
closeSubscription: sinon.SinonSpy;
81+
onDidOpenConnection: Event<void>;
82+
onDidCloseConnection: Event<void>;
83+
closeConnection: () => void;
7184
};
7285

7386
beforeEach(async () => {
7487
sandbox = sinon.createSandbox();
7588
fakeContribution = new FakeContribution();
7689
const container = createTestContainer(fakeContribution);
7790
const factory = container.get(RpcConnectionFactory);
91+
const closeConnectionEmitter = new Emitter<void>();
7892
clientProxy = {
7993
onAccessorChanged: sandbox.stub(),
8094
closeSubscription: sandbox.stub(),
95+
onDidOpenConnection: new Emitter<void>().event,
96+
onDidCloseConnection: closeConnectionEmitter.event,
97+
closeConnection(): void {
98+
closeConnectionEmitter.fire();
99+
},
81100
};
82101
modelAccessorBusServer = await factory.getServer<ModelAccessorBusServer>(
83102
ModelAccessorBusProtocolServicePath,
@@ -275,6 +294,7 @@ describe('Model Accessor Bus Server', () => {
275294
expect(clientProxy.onAccessorChanged.calledOnceWith(1)).to.be.true;
276295
});
277296
});
297+
278298
describe('getModelAccessorBus', async () => {
279299
it('expect to be different on different contexts', async () => {
280300
const spiedAccessorBusAccess = sandbox.spy(
@@ -307,6 +327,45 @@ describe('Model Accessor Bus Server', () => {
307327
expect(bus_0 === bus_1).to.be.true;
308328
});
309329
});
330+
331+
describe('multiple clients', () => {
332+
beforeEach(async () => {
333+
const closeConnectionEmitter = new Emitter<void>();
334+
clientProxy2 = {
335+
onAccessorChanged: sandbox.stub(),
336+
closeSubscription: sandbox.stub(),
337+
onDidOpenConnection: new Emitter<void>().event,
338+
onDidCloseConnection: closeConnectionEmitter.event,
339+
closeConnection(): void {
340+
closeConnectionEmitter.fire();
341+
},
342+
};
343+
const container = createTestContainer(fakeContribution);
344+
const factory = container.get(RpcConnectionFactory);
345+
modelAccessorBusServer2 = await factory.getServer<ModelAccessorBusServer>(
346+
ModelAccessorBusProtocolServicePath,
347+
clientProxy2
348+
);
349+
});
350+
351+
it('supports multiple clients', async () => {
352+
expect(modelAccessorBusServer2).to.exist;
353+
expect(modelAccessorBusServer2).not.to.be.equal(modelAccessorBusServer); // Different instances
354+
});
355+
356+
it('can close client', async () => {
357+
const token = await modelAccessorBusServer.subscribe(appContext, 'test');
358+
clientProxy.closeConnection();
359+
// closeSubscription should be called before the client is removed. A real
360+
// client would not receive the event (since the connection is closed), but our
361+
// mock client doesn't actually have a connection to close.
362+
sinon.assert.calledWithExactly(clientProxy.closeSubscription, token.id);
363+
expect(modelAccessorBusServer.getClient()).to.be.undefined;
364+
365+
// Other server+client still exists
366+
expect(modelAccessorBusServer2.getClient()).to.exist;
367+
});
368+
});
310369
});
311370

312371
export class MockProvider extends HubAwareProvider {

0 commit comments

Comments
 (0)