Skip to content

Commit 39403ff

Browse files
159: API V2: Properly define and support multi-resources patches/operations (#107)
159: API V2: Properly define and support multi-resources patches/operations - Add the possibility to specify Json Patches via a list of "Model Patch" objects, referencing a modelUri and a Patch. This allows edition of multi-resource models, with one Json Patch per resource. - Update parameter name for consistency with modified type Additional minor fixes: - Fix a minor warning in the integration tests - Support the optional "format" attribute in the client V2 implementation Contributed on behalf of STMicroelectronics. refs eclipse-emfcloud/emfcloud-modelserver#159 Signed-off-by: Camille Letavernier <cletavernier@eclipsesource.com>
1 parent a3d933f commit 39403ff

4 files changed

Lines changed: 58 additions & 18 deletions

File tree

packages/modelserver-client/src/model-server-client-api-v2.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,16 @@ export interface SubscriptionOptionsV2 extends SubscriptionOptions {
5353
paths?: string;
5454
}
5555

56+
/**
57+
* A union type that represents all Patches or Commands supported by the model server:
58+
* - Operation
59+
* - Operation[] (Json Patch)
60+
* - ModelPatch
61+
* - ModelPatch[]
62+
* - ModelServerCommand
63+
*/
64+
export type PatchOrCommand = Operation | Operation[] | ModelPatch | ModelPatch[] | ModelServerCommand;
65+
5666
/**
5767
* Basic client API to interact with a model server that conforms to the Modelserver API Version 2.
5868
*/
@@ -113,8 +123,7 @@ export interface ModelServerClientApiV2 {
113123

114124
ping(): Promise<boolean>;
115125

116-
edit(modeluri: string, patch: Operation | Operation[], format?: Format): Promise<ModelUpdateResult>;
117-
edit(modeluri: string, command: ModelServerCommand, format?: Format): Promise<ModelUpdateResult>;
126+
edit(modeluri: string, patchOrCommand: PatchOrCommand, format?: Format): Promise<ModelUpdateResult>;
118127

119128
undo(modeluri: string): Promise<ModelUpdateResult>;
120129

packages/modelserver-client/src/model-server-client-v2-integration.spec.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ describe('Integration tests for ModelServerClientV2', () => {
3232
let client: ModelServerClientV2;
3333
const baseUrl = `http://localhost:8081${ModelServerClientApiV2.API_ENDPOINT}`;
3434

35-
const testUndoRedo: (modeluri: string, originalModel: any, patchedModel: any) => void = async (
35+
const testUndoRedo: (modeluri: string, originalModel: any, patchedModel: any) => Promise<void> = async (
3636
modeluri,
3737
originalModel,
3838
patchedModel
@@ -387,5 +387,41 @@ describe('Integration tests for ModelServerClientV2', () => {
387387

388388
await testUndoRedo(modeluri, machine, updatedMachineFirstPatch);
389389
});
390+
391+
it('test model patches', async () => {
392+
const modeluri = 'SuperBrewer3000.coffee';
393+
const newName = 'Super Brewer 6000';
394+
const machine = await client.get(modeluri, ModelServerObjectV2.is);
395+
396+
const patchedMachine = deepClone(machine);
397+
398+
// Directly change the model
399+
patchedMachine.name = newName;
400+
patchedMachine.children[1].processor.clockSpeed = 6;
401+
402+
// Generate patch by diffing the original model and the patched one
403+
const patch = jsonpatch.compare(machine, patchedMachine);
404+
const modelPatch = {
405+
modelUri: modeluri,
406+
patch
407+
};
408+
409+
const result = await client.edit(modeluri, modelPatch);
410+
expect(result.success).to.be.true;
411+
412+
expect(result.patch).to.not.be.undefined;
413+
expect(result.allPatches).to.not.be.undefined;
414+
expect(result.allPatches).to.be.an('array').of.length(1);
415+
416+
// Patch the main resource
417+
const updatedMachineMainPatch = result.patchModel!(machine, true);
418+
expect(patchedMachine).to.deep.equal(updatedMachineMainPatch);
419+
420+
// Patch the first resource
421+
const updatedMachineFirstPatch = result.patchModel!(machine, true, result.allPatches![0].modelUri);
422+
expect(patchedMachine).to.deep.equal(updatedMachineFirstPatch);
423+
424+
await testUndoRedo(modeluri, machine, updatedMachineFirstPatch);
425+
});
390426
});
391427
});

packages/modelserver-client/src/model-server-client-v2.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@
99
* SPDX-License-Identifier: EPL-2.0 OR MIT
1010
*******************************************************************************/
1111
import axios, { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
12-
import { deepClone, Operation } from 'fast-json-patch';
12+
import { deepClone } from 'fast-json-patch';
1313
import WebSocket from 'isomorphic-ws';
1414

1515
import { ModelServerCommand } from './model/command-model';
1616
import { Diagnostic } from './model/diagnostic';
1717
import { ModelServerError, ServerConfiguration, SubscriptionOptions } from './model-server-client-api-v1';
18-
import { Format, FORMAT_JSON_V2, ModelServerClientApiV2, ModelUpdateResult } from './model-server-client-api-v2';
18+
import { Format, FORMAT_JSON_V2, ModelServerClientApiV2, ModelUpdateResult, PatchOrCommand } from './model-server-client-api-v2';
1919
import { MessageDataMapper, Model, ModelServerMessage } from './model-server-message';
2020
import { ModelServerPaths } from './model-server-paths';
2121
import { SubscriptionListener } from './subscription-listener';
@@ -212,17 +212,15 @@ export class ModelServerClientV2 implements ModelServerClientApiV2 {
212212
return this.process(this.restClient.get(ModelServerPaths.SERVER_PING), MessageDataMapper.isSuccess);
213213
}
214214

215-
edit(modeluri: string, patch: Operation): Promise<ModelUpdateResult>;
216-
edit(modeluri: string, patch: Operation[]): Promise<ModelUpdateResult>;
217-
edit(modeluri: string, command: ModelServerCommand): Promise<ModelUpdateResult>;
218-
edit(modeluri: string, patchOrCommand: Operation | Operation[] | ModelServerCommand): Promise<ModelUpdateResult> {
215+
edit(modeluri: string, patchOrCommand: PatchOrCommand, format = this.defaultFormat): Promise<ModelUpdateResult> {
219216
let patchMessage: any;
220217
if (patchOrCommand instanceof ModelServerCommand) {
221218
patchMessage = {
222219
type: 'modelserver.emfcommand',
223220
data: patchOrCommand
224221
};
225222
} else {
223+
// Operation[] and ModelPatch[] are treated in the same way; we don't need to distinguish both cases
226224
const fullPatch = Array.isArray(patchOrCommand) ? patchOrCommand : [patchOrCommand];
227225
patchMessage = {
228226
type: 'modelserver.patch',
@@ -238,8 +236,8 @@ export class ModelServerClientV2 implements ModelServerClientApiV2 {
238236
}
239237
}
240238
return this.process(
241-
this.restClient.patch(ModelServerPaths.MODEL_CRUD, encodeRequestBody(this.defaultFormat)(patchMessage), {
242-
params: { modeluri, format: this.defaultFormat }
239+
this.restClient.patch(ModelServerPaths.MODEL_CRUD, encodeRequestBody(format)(patchMessage), {
240+
params: { modeluri, format: format }
243241
}),
244242
MessageDataMapper.patchModel
245243
);

packages/modelserver-theia/src/node/theia-model-server-client-v2.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,17 @@
1111
import {
1212
AddCommand,
1313
CompoundCommand,
14+
Format,
1415
ModelServerClientV2,
1516
ModelServerCommand,
1617
ModelUpdateResult,
17-
Operations,
18+
PatchOrCommand,
1819
RemoveCommand,
1920
SetCommand,
2021
SubscriptionListener,
2122
SubscriptionOptionsV2
2223
} from '@eclipse-emfcloud/modelserver-client';
2324
import { decorate, inject, injectable, optional } from '@theia/core/shared/inversify';
24-
import { Operation } from 'fast-json-patch';
2525
import { CancellationToken } from 'vscode-jsonrpc';
2626

2727
import { ModelServerFrontendClient, TheiaModelServerClientV2 } from '../common';
@@ -88,14 +88,11 @@ export class TheiaBackendModelServerClientV2 extends ModelServerClientV2 impleme
8888
this.subscribe(modeluri, undefined, options);
8989
}
9090

91-
edit(modeluri: string, patchOrCommand: Operation | Operation[] | ModelServerCommand): Promise<ModelUpdateResult> {
91+
edit(modeluri: string, patchOrCommand: PatchOrCommand, format?: Format): Promise<ModelUpdateResult> {
9292
if (ModelServerCommand.is(patchOrCommand)) {
9393
return super.edit(modeluri, ensureCommandPrototype(patchOrCommand));
9494
}
95-
if (Operations.isOperation(patchOrCommand)) {
96-
return super.edit(modeluri, patchOrCommand);
97-
}
98-
return super.edit(modeluri, patchOrCommand);
95+
return super.edit(modeluri, patchOrCommand, format);
9996
}
10097
}
10198

0 commit comments

Comments
 (0)