@eclipse-emfcloud/modelserver-client
Version:
Typescript rest client to interact with an EMF.cloud modelserver
339 lines • 19.9 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
/********************************************************************************
* Copyright (c) 2022 STMicroelectronics and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* https://www.eclipse.org/legal/epl-2.0, or the MIT License which is
* available at https://opensource.org/licenses/MIT.
*
* SPDX-License-Identifier: EPL-2.0 OR MIT
*******************************************************************************/
const chai_1 = require("chai");
const fast_json_patch_1 = __importStar(require("fast-json-patch"));
const urijs_1 = __importDefault(require("urijs"));
const _1 = require(".");
const model_server_client_api_v2_1 = require("./model-server-client-api-v2");
describe('Integration tests for ModelServerClientV2', () => {
let client;
const baseUrl = new urijs_1.default({
protocol: 'http',
hostname: 'localhost',
port: '8081',
path: model_server_client_api_v2_1.ModelServerClientApiV2.API_ENDPOINT
});
const testUndoRedo = async (modeluri, originalModel, patchedModel) => {
// Expected: originalModel === undoModel === patchedUndoModel
// Expected: patchedModel === redoModel === patchedRedoModel
const undoPatch = await client.undo(modeluri);
const undoModel = await client.get(modeluri, _1.ModelServerObjectV2.is);
(0, chai_1.expect)(undoModel).to.deep.equal(originalModel);
const patchedUndoModel = undoPatch.patchModel(patchedModel, true);
(0, chai_1.expect)(patchedUndoModel).to.deep.equal(originalModel);
const redoPatch = await client.redo(modeluri);
const redoModel = await client.get(modeluri, _1.ModelServerObjectV2.is);
(0, chai_1.expect)(redoModel).to.deep.equal(patchedModel);
const patchedRedoModel = redoPatch.patchModel(patchedUndoModel, true);
(0, chai_1.expect)(patchedRedoModel).to.deep.equal(patchedModel);
// Restore initial state
await client.undo(modeluri);
};
beforeEach(() => {
client = new _1.ModelServerClientV2();
client.initialize(baseUrl, 'json-v2');
});
describe('test requests', () => {
it('edit with patch', async () => {
const modeluri = new urijs_1.default('SuperBrewer3000.coffee');
const newName = 'Super Brewer 6000';
const machine = await client.get(modeluri, _1.ModelServerObjectV2.is);
const patch = (0, _1.replace)(modeluri, machine, 'name', newName);
await client.edit(modeluri, patch);
const model = await client.get(modeluri);
(0, chai_1.expect)(model.name).to.be.equal(newName);
await testUndoRedo(modeluri, machine, model);
});
it('create with patch', async () => {
const modeluri = new urijs_1.default('SuperBrewer3000.coffee');
const originalModel = await client.get(modeluri, _1.ModelServerObjectV2.is);
const newWorkflowName = 'New Test Workflow';
const initialWorkflowsCount = originalModel.workflows.length;
const patch = (0, _1.create)(modeluri, originalModel, 'workflows', 'http://www.eclipsesource.com/modelserver/example/coffeemodel#//Workflow', { name: newWorkflowName });
await client.edit(modeluri, patch);
const patchedModel = await client.get(modeluri);
const workflows = patchedModel.workflows;
(0, chai_1.expect)(workflows.length).to.be.equal(initialWorkflowsCount + 1);
const newWorkflow = patchedModel.workflows[initialWorkflowsCount];
(0, chai_1.expect)(newWorkflow.name).to.be.equal(newWorkflowName);
(0, chai_1.expect)(newWorkflow).to.have.property('$id');
(0, chai_1.expect)(newWorkflow.$id).to.be.a('string');
await testUndoRedo(modeluri, originalModel, patchedModel);
});
it('add with patch', async () => {
const modeluri = new urijs_1.default('SuperBrewer3000.coffee');
const initialModel = await client.get(modeluri, _1.ModelServerObjectV2.is);
// Add a second workflow to the model; we'll use it to move a Task from a workflow to the other
const createWorkflow = (0, _1.create)(modeluri, initialModel, 'workflows', 'http://www.eclipsesource.com/modelserver/example/coffeemodel#//Workflow', { name: 'New Workflow' });
await client.edit(modeluri, createWorkflow);
const originalModel = await client.get(modeluri, _1.ModelServerObjectV2.is);
const sourceWF = originalModel.workflows[0];
const targetWF = originalModel.workflows[1];
const patch = (0, _1.add)(modeluri, targetWF, 'nodes', sourceWF.nodes[0]);
await client.edit(modeluri, patch);
const patchedModel = await client.get(modeluri);
const patchedSourceWF = patchedModel.workflows[0];
const patchedTargetWF = patchedModel.workflows[1];
(0, chai_1.expect)(patchedSourceWF.nodes).to.be.undefined;
(0, chai_1.expect)(patchedTargetWF.nodes).to.be.an('array').of.length(1);
(0, chai_1.expect)(patchedTargetWF.nodes[0].name).to.be.equal(sourceWF.nodes[0].name); // Node was moved
await testUndoRedo(modeluri, originalModel, patchedModel);
});
it('delete with patch - index based', async () => {
const modeluri = new urijs_1.default('SuperBrewer3000.coffee');
const originalModel = await client.get(modeluri, _1.ModelServerObjectV2.is);
const parentWorkflow = originalModel.workflows[0];
(0, chai_1.expect)(parentWorkflow.nodes).to.be.an('array').that.is.not.empty;
const patch = (0, _1.removeValueAt)(modeluri, parentWorkflow, 'nodes', 0);
await client.edit(modeluri, patch);
const patchedModel = await client.get(modeluri);
const patchedParentWorkflow = patchedModel.workflows[0];
(0, chai_1.expect)(patchedParentWorkflow.nodes).to.be.undefined;
await testUndoRedo(modeluri, originalModel, patchedModel);
});
it('delete with patch - object', async () => {
const modeluri = new urijs_1.default('SuperBrewer3000.coffee');
const originalModel = await client.get(modeluri, _1.ModelServerObjectV2.is);
const parentWorkflow = originalModel.workflows[0];
(0, chai_1.expect)(parentWorkflow.nodes).to.be.an('array').that.is.not.empty;
const valueToRemove = parentWorkflow.nodes[0];
const patch = (0, _1.removeObject)(modeluri, valueToRemove);
await client.edit(modeluri, patch);
const patchedModel = await client.get(modeluri);
const patchedParentWorkflow = patchedModel.workflows[0];
(0, chai_1.expect)(patchedParentWorkflow.nodes).to.be.undefined;
await testUndoRedo(modeluri, originalModel, patchedModel);
});
it('edit with command', async () => {
const modeluri = new urijs_1.default('SuperBrewer3000.coffee');
const newName = 'Super Brewer 6000';
const originalModel = await client.get(modeluri, _1.ModelServerObjectV2.is);
const owner = {
eClass: originalModel.$type,
$ref: urijs_1.default.build({ path: 'SuperBrewer3000.coffee', fragment: originalModel.$id })
};
const command = new _1.SetCommand(owner, 'name', [newName]);
await client.edit(modeluri, command);
const model = await client.get(modeluri);
(0, chai_1.expect)(model.name).to.be.equal(newName);
await testUndoRedo(modeluri, originalModel, model);
});
it('incremental patch update', async () => {
const modeluri = new urijs_1.default('SuperBrewer3000.coffee');
const newName = 'Super Brewer 6000';
const machine = await client.get(modeluri, _1.ModelServerObjectV2.is);
const patch = (0, _1.replace)(modeluri, machine, 'name', newName);
const updateResult = await client.edit(modeluri, patch);
(0, chai_1.expect)(updateResult.success).to.be.true;
(0, chai_1.expect)(updateResult.patchModel).to.not.be.undefined;
(0, chai_1.expect)(updateResult.patch).to.not.be.undefined;
// Patch a copy of the model (machine), to make sure the original model is
// unchanged. We'll need it later to check undo/redo behavior.
const patchedMachine = updateResult.patchModel(machine, true);
(0, chai_1.expect)(patchedMachine.name).to.be.equal(newName);
// Check that the incremental update is consistent with the server version of the model
const newMachine = await client.get(modeluri, _1.ModelServerObjectV2.is);
(0, chai_1.expect)(newMachine).to.deep.equal(patchedMachine);
await testUndoRedo(modeluri, machine, patchedMachine);
});
it('subscribe to changes', async () => {
const modeluri = new urijs_1.default('SuperBrewer3000.coffee');
const newName = 'Super Brewer 6000';
const machine = await client.get(modeluri, _1.ModelServerObjectV2.is);
const owner = {
eClass: machine.$type,
$ref: urijs_1.default.build({ path: 'SuperBrewer3000.coffee', fragment: machine.$id })
};
const command = new _1.SetCommand(owner, 'name', [newName]);
const listener = {};
const patchNotification = new Promise((resolve, _rej) => {
listener.onIncrementalUpdateV2 = resolve;
});
const subscription = new Promise((resolve, _rej) => {
listener.onSuccess = resolve;
});
client.subscribe(modeluri, new _1.NotificationSubscriptionListenerV2(listener));
// Make sure the subscription is initialized before editing the model,
// so that we don't miss the notification
await subscription;
await client.edit(modeluri, command);
const notification = await patchNotification;
const patch = notification.patch;
(0, chai_1.expect)(notification.modeluri.toString()).to.be.equal(modeluri.toString());
(0, chai_1.expect)(patch.length).to.be.equal(1);
const operation = patch[0];
(0, chai_1.expect)(_1.Operations.isReplace(operation, 'string')).to.be.equal(true);
(0, chai_1.expect)(operation.path).to.be.equal('/name');
if (_1.Operations.isReplace(operation, 'string')) {
(0, chai_1.expect)(operation.value).to.be.equal(newName);
}
await client.undo(modeluri);
client.unsubscribe(modeluri);
});
it('subscribe to incremental updates', async () => {
const modeluri = new urijs_1.default('SuperBrewer3000.coffee');
const newName = 'Super Brewer 6000';
const machine = await client.get(modeluri, _1.ModelServerObjectV2.is);
const owner = {
eClass: machine.$type,
$ref: urijs_1.default.build({ path: 'SuperBrewer3000.coffee', fragment: machine.$id })
};
const command = new _1.SetCommand(owner, 'name', [newName]);
const listener = {};
const patchNotification = new Promise((resolve, _rej) => {
listener.onIncrementalUpdateV2 = resolve;
});
const subscription = new Promise((resolve, _rej) => {
listener.onSuccess = resolve;
});
client.subscribe(modeluri, new _1.NotificationSubscriptionListenerV2(listener));
// Make sure the subscription is initialized before editing the model,
// so that we don't miss the notification
await subscription;
await client.edit(modeluri, command);
const notification = await patchNotification;
// Apply the incremental patch on the original model
const incrementalPatchedModel = notification.patchModel(machine, true);
// Retrieve the current model from the model server
const patchedModel = await client.get(modeluri, _1.ModelServerObjectV2.is);
// Check that the incrementally-patched model to be identical to the version
// from the model server.
(0, chai_1.expect)(incrementalPatchedModel).to.deep.equal(patchedModel);
await client.undo(modeluri);
client.unsubscribe(modeluri);
});
it('pure Json Patch changes', async () => {
const modeluri = new urijs_1.default('SuperBrewer3000.coffee');
const newName = 'Super Brewer 6000';
const machine = await client.get(modeluri, _1.ModelServerObjectV2.is);
const patchedMachine = (0, fast_json_patch_1.deepClone)(machine);
// Directly change the model
patchedMachine.name = newName;
patchedMachine.children[1].processor.clockSpeed = 6;
// Generate patch by diffing the original model and the patched one
const patch = fast_json_patch_1.default.compare(machine, patchedMachine);
await client.edit(modeluri, patch);
const model = await client.get(modeluri);
(0, chai_1.expect)(model.name).to.be.equal(newName);
(0, chai_1.expect)(model.children[1].processor.clockSpeed).to.be.equal(6);
await testUndoRedo(modeluri, machine, model);
});
it('clear list with "remove" patch operation', async () => {
const modeluri = new urijs_1.default('SuperBrewer3000.coffee');
const machine = await client.get(modeluri, _1.ModelServerObjectV2.is);
const initialWorkflowsSize = machine.workflows.length;
(0, chai_1.expect)(initialWorkflowsSize).to.not.be.equal(0);
const patch = [
{
op: 'remove',
path: '/workflows'
}
];
await client.edit(modeluri, patch);
const model = await client.get(modeluri);
const newWorkflows = model.workflows;
(0, chai_1.expect)(newWorkflows).to.be.undefined;
await testUndoRedo(modeluri, machine, model);
});
it('unset value with "remove" patch operation', async () => {
const modeluri = new urijs_1.default('SuperBrewer3000.coffee');
const machine = await client.get(modeluri, _1.ModelServerObjectV2.is);
const initialValue = machine.children[1].processor.thermalDesignPower;
(0, chai_1.expect)(initialValue).to.not.be.oneOf([undefined, 0]);
const patch = [
{
op: 'remove',
path: '/children/1/processor/thermalDesignPower'
}
];
await client.edit(modeluri, patch);
const model = await client.get(modeluri);
const newValue = model.children[1].processor.thermalDesignPower;
(0, chai_1.expect)(newValue).to.be.oneOf([undefined, 0]); // Should be === 0, but default values are not converted to Json at the moment; so we also expect 'undefined'
await testUndoRedo(modeluri, machine, model);
});
it('check all patch replies', async () => {
const modeluri = new urijs_1.default('SuperBrewer3000.coffee');
const newName = 'Super Brewer 6000';
const machine = await client.get(modeluri, _1.ModelServerObjectV2.is);
const patchedMachine = (0, fast_json_patch_1.deepClone)(machine);
// Directly change the model
patchedMachine.name = newName;
patchedMachine.children[1].processor.clockSpeed = 6;
// Generate patch by diffing the original model and the patched one
const patch = fast_json_patch_1.default.compare(machine, patchedMachine);
const result = await client.edit(modeluri, patch);
(0, chai_1.expect)(result.success).to.be.true;
(0, chai_1.expect)(result.patch).to.not.be.undefined;
(0, chai_1.expect)(result.allPatches).to.not.be.undefined;
(0, chai_1.expect)(result.allPatches).to.be.an('array').of.length(1);
// Patch the main resource
const updatedMachineMainPatch = result.patchModel(machine, true);
(0, chai_1.expect)(patchedMachine).to.deep.equal(updatedMachineMainPatch);
// Patch the first resource
const updatedMachineFirstPatch = result.patchModel(machine, true, new urijs_1.default(result.allPatches[0].modelUri));
(0, chai_1.expect)(patchedMachine).to.deep.equal(updatedMachineFirstPatch);
await testUndoRedo(modeluri, machine, updatedMachineFirstPatch);
});
it('test model patches', async () => {
const modeluri = new urijs_1.default('SuperBrewer3000.coffee');
const newName = 'Super Brewer 6000';
const machine = await client.get(modeluri, _1.ModelServerObjectV2.is);
const patchedMachine = (0, fast_json_patch_1.deepClone)(machine);
// Directly change the model
patchedMachine.name = newName;
patchedMachine.children[1].processor.clockSpeed = 6;
// Generate patches by diffing the original model and the patched one
const patch = fast_json_patch_1.default.compare(machine, patchedMachine);
const result = await client.edit(modeluri, patch);
(0, chai_1.expect)(result.success).to.be.true;
(0, chai_1.expect)(result.patch).to.not.be.undefined;
(0, chai_1.expect)(result.allPatches).to.not.be.undefined;
(0, chai_1.expect)(result.allPatches).to.be.an('array').of.length(1);
// Patch the main resource
const updatedMachineMainPatch = result.patchModel(machine, true);
(0, chai_1.expect)(patchedMachine).to.deep.equal(updatedMachineMainPatch);
// Patch the first resource
const updatedMachineFirstPatch = result.patchModel(machine, true, new urijs_1.default(result.allPatches[0].modelUri));
(0, chai_1.expect)(patchedMachine).to.deep.equal(updatedMachineFirstPatch);
await testUndoRedo(modeluri, machine, updatedMachineFirstPatch);
});
});
});
//# sourceMappingURL=model-server-client-v2-integration.spec.js.map