Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

179: Convert all Modelserver-Node APIs to use URI instead of String #55

Merged
merged 2 commits into from
Nov 4, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion examples/custom-command-example/src/example-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
Transaction
} from '@eclipse-emfcloud/modelserver-plugin-ext';
import { inject, injectable, named } from 'inversify';
import * as URI from 'urijs';

/**
* A simple example of a plug-in that provides custom commands.
Expand Down Expand Up @@ -52,7 +53,7 @@ class IncrementDurationCommandProvider implements CommandProvider {
return true; // The command type filter is all I need
}

getCommands(_modelUri: string, customCommand: ModelServerCommand): Transaction {
getCommands(_modelUri: URI, customCommand: ModelServerCommand): Transaction {
const [modelURI, elementID] = customCommand.owner.$ref.split('#');

return async executor => {
Expand Down
5 changes: 3 additions & 2 deletions examples/custom-validator-example/src/example-validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
ValidationProvider
} from '@eclipse-emfcloud/modelserver-plugin-ext';
import { inject, injectable, named } from 'inversify';
import * as URI from 'urijs';

/**
* A simple example of a plug-in that provides custom validation.
Expand Down Expand Up @@ -47,11 +48,11 @@ export class ExampleCustomValidationPlugin implements ModelServerPlugin {
class CoffeeMachineValidator implements ValidationProvider {
constructor(protected readonly modelServerClient: ModelServerClientApi, protected readonly logger: Logger) {}

canValidate(model: ModelServerObjectV2, modelURI: string): boolean {
canValidate(model: ModelServerObjectV2, modelURI: URI): boolean {
return true; // Nothing further to check than the pattern supplied at registration
}

validate(model: ModelServerObjectV2, modelURI: string): Diagnostic {
validate(model: ModelServerObjectV2, modelURI: URI): Diagnostic {
const probabilityTest = Math.random();
if (probabilityTest < 0.5) {
return Diagnostic.ok();
Expand Down
9 changes: 5 additions & 4 deletions examples/trigger-example/src/example-trigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
} from '@eclipse-emfcloud/modelserver-plugin-ext';
import { AddOperation, Operation, ReplaceOperation } from 'fast-json-patch';
import { inject, injectable, named } from 'inversify';
import * as URI from 'urijs';

/**
* A simple example of a plug-in that provides triggers for automatic update of model objects.
Expand All @@ -46,16 +47,16 @@ export class ExampleTriggerPlugin implements ModelServerPlugin {
class IncrementDurationTriggerProvider implements TriggerProvider {
constructor(protected readonly modelServerClient: ModelServerClientApi, protected readonly logger: Logger) {}

canTrigger(modelURI: string, patch: Operation[]): boolean {
canTrigger(modelURI: URI, patch: Operation[]): boolean {
return patch.some(isDurationChange);
}

async getTriggers(modelURI: string, modelDelta: Operation[]): Promise<Operation[]> {
async getTriggers(modelURI: URI, modelDelta: Operation[]): Promise<Operation[]> {
// In this case, because the trigger updates the same properties previously modified,
// we have all the information we need in the `modelDelta`. But in the general case,
// a trigger provider will need to find other related objects in the model and generate
// new changes from those, so that's how we do it here
const model = await this.modelServerClient.get(modelURI);
const model = await this.modelServerClient.get(modelURI.toString());
const result: Operation[] = [];

// Take only the last change to any given duration in case of multiple (such as
Expand All @@ -65,7 +66,7 @@ class IncrementDurationTriggerProvider implements TriggerProvider {
// Don't update if already a multiple of ten
if (element && op.value % 10 !== 0) {
this.logger.debug('Rounding up duration %d of object at %s', op.value, op.path);
result.push(replace(modelURI, element, 'duration', roundUp(op.value, 10)));
result.push(replace(modelURI.toString(), element, 'duration', roundUp(op.value, 10)));
}
});

Expand Down
33 changes: 13 additions & 20 deletions packages/modelserver-node/src/client/model-server-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ import * as WebSocket from 'ws';
import { CommandProviderRegistry } from '../command-provider-registry';
import { TriggerProviderRegistry } from '../trigger-provider-registry';
import { CompletablePromise } from './promise-utils';
import { validateModelURI } from './uri-utils';
import { WebSocketMessageAcceptor } from './web-socket-utils';

export const UpstreamConnectionConfig = Symbol('UpstreamConnectionConfig');
Expand Down Expand Up @@ -71,7 +70,7 @@ export interface InternalModelServerClientApi extends ModelServerClientApi {
* @returns a transactional context in which to execute a chained sequence of commands,
* or a rejected promise in the case that a transaction is already open on the given model
*/
openTransaction(modeluri: string): Promise<TransactionContext>;
openTransaction(modeluri: URI): Promise<TransactionContext>;
}

/**
Expand Down Expand Up @@ -174,22 +173,16 @@ export class InternalModelServerClient implements InternalModelServerClientApi {
return this.delegate.initialize(this._baseURL.toString(), DEFAULT_FORMAT);
}

async openTransaction(modelUri: string): Promise<TransactionContext> {
let uri: URI;
try {
uri = validateModelURI(modelUri);
} catch (error) {
this.logger.error(error);
return Promise.reject();
}
if (this.transactions.has(uri.toString())) {
async openTransaction(modelUri: URI): Promise<TransactionContext> {
const uriKey = modelUri.normalize().toString();
if (this.transactions.has(uriKey)) {
// Open a nested transaction
return this.transactions.get(uri.toString()).openTransaction();
return this.transactions.get(uriKey).openTransaction();
}

const clientID = uuid();

return axios.post(this.makeURL('transaction', { modeluri: uri.toString() }), { data: clientID }).then(response => {
return axios.post(this.makeURL('transaction', { modeluri: modelUri.toString() }), { data: clientID }).then(response => {
const { uri: transactionURI } = (response.data as CreateTransactionResponseBody).data;
const result = new DefaultTransactionContext(
transactionURI,
Expand All @@ -198,8 +191,8 @@ export class InternalModelServerClient implements InternalModelServerClientApi {
this.triggerProviderRegistry,
this.logger
);
this.transactions.set(uri.toString(), result);
return result.open(tc => this.closeTransaction(uri, tc));
this.transactions.set(uriKey, result);
return result.open(tc => this.closeTransaction(modelUri, tc));
});
}

Expand Down Expand Up @@ -370,7 +363,7 @@ class DefaultTransactionContext implements TransactionContext {

constructor(
protected readonly transactionURI: string,
protected readonly modelURI: string,
protected readonly modelURI: URI,
protected readonly commandProviderRegistry: CommandProviderRegistry,
protected readonly triggerProviderRegistry: TriggerProviderRegistry,
protected readonly logger: Logger
Expand All @@ -385,7 +378,7 @@ class DefaultTransactionContext implements TransactionContext {
}

// Doc inherited from `EditTransaction` interface
getModelURI(): string {
getModelURI(): URI {
return this.modelURI;
}

Expand Down Expand Up @@ -490,7 +483,7 @@ class DefaultTransactionContext implements TransactionContext {
}

// Doc inherited from `Executor` interface
async execute(modelUri: string, command: ModelServerCommand): Promise<ModelUpdateResult> {
async execute(modelUri: URI, command: ModelServerCommand): Promise<ModelUpdateResult> {
if (!this.isOpen()) {
return Promise.reject('Socket is closed.');
}
Expand All @@ -499,7 +492,7 @@ class DefaultTransactionContext implements TransactionContext {
}

/** Internal implementation of the {@link TransactionContext.execute} method. */
async doExecute(modelUri: string, command: ModelServerCommand): Promise<ModelUpdateResult> {
async doExecute(modelUri: URI, command: ModelServerCommand): Promise<ModelUpdateResult> {
// Hook in command-provider plug-ins
if (!this.commandProviderRegistry.hasProvider(command.type)) {
// Not handled. Send along to the Model Server
Expand Down Expand Up @@ -688,7 +681,7 @@ class DefaultTransactionContext implements TransactionContext {
protected message(type: MessageBody['type'], data: unknown = {}): MessageBody {
return {
type,
modelUri: this.modelURI,
modelUri: this.modelURI.toString(),
data
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { ModelServerCommand, replace } from '@eclipse-emfcloud/modelserver-clien
import { CommandProvider, Logger, Transaction } from '@eclipse-emfcloud/modelserver-plugin-ext';
import { expect } from 'chai';
import { Container } from 'inversify';
import * as URI from 'urijs';

import { CommandProviderRegistry } from './command-provider-registry';

Expand Down Expand Up @@ -52,7 +53,7 @@ class TestCommandProvider implements CommandProvider {
return true;
}

getCommands(_modelUri: string, customCommand: ModelServerCommand): Transaction {
getCommands(_modelUri: URI, customCommand: ModelServerCommand): Transaction {
if (!customCommand.owner) {
console.error('custom command owner was unexpectedly undefined');
return async () => false;
Expand Down
2 changes: 1 addition & 1 deletion packages/modelserver-node/src/command-provider-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export class CommandProviderRegistry {
* @returns the provided command, command transaction, or the original `customCommand` standing in for itself
* if no provider can handle the custom command
*/
async getCommands(modelUri: string, customCommand: ModelServerCommand): Promise<ModelServerCommand | Operation[] | Transaction> {
async getCommands(modelUri: URI, customCommand: ModelServerCommand): Promise<ModelServerCommand | Operation[] | Transaction> {
ndoschek marked this conversation as resolved.
Show resolved Hide resolved
let result: ModelServerCommand | Operation[] | Transaction | undefined;
const provider = this.getProvider(customCommand);
if (provider) {
Expand Down
6 changes: 3 additions & 3 deletions packages/modelserver-node/src/routes/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export class ModelsRoutes implements RouteProvider {
? this.modelServerClient.create(modeluri.toString(), model, format)
: this.modelServerClient.update(modeluri.toString(), model, format);

delegated.then(this.performModelValidation(modeluri.toString())).then(relay(res)).catch(handleError(res));
delegated.then(this.performModelValidation(modeluri)).then(relay(res)).catch(handleError(res));
};
}

Expand Down Expand Up @@ -146,7 +146,7 @@ export class ModelsRoutes implements RouteProvider {
patchOrCommand: Operation | Operation[] | ModelServerCommand,
res: Response<any, Record<string, any>>
): void {
this.editService.edit(modelURI.toString(), patchOrCommand).then(relay(res)).catch(handleError(res));
this.editService.edit(modelURI, patchOrCommand).then(relay(res)).catch(handleError(res));
}

/**
Expand All @@ -155,7 +155,7 @@ export class ModelsRoutes implements RouteProvider {
* @param modelURI the model created or replaced
* @returns the created or replacing model
*/
protected performModelValidation(modelURI: string): (delegatedResult: AnyObject) => Promise<AnyObject> {
protected performModelValidation(modelURI: URI): (delegatedResult: AnyObject) => Promise<AnyObject> {
const validator = this.validationManager;

return async (delegatedResult: AnyObject) => validator.performLiveValidation(modelURI).then(() => delegatedResult);
Expand Down
8 changes: 6 additions & 2 deletions packages/modelserver-node/src/routes/undo-redo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { ModelUpdateResult } from '@eclipse-emfcloud/modelserver-client/lib';
import { Logger, RouteProvider, RouterFactory } from '@eclipse-emfcloud/modelserver-plugin-ext';
import { Request, RequestHandler, Response } from 'express';
import { inject, injectable, named } from 'inversify';
import * as URI from 'urijs';

import { InternalModelServerClientApi } from '../client/model-server-client';
import { ValidationManager } from '../services/validation-manager';
Expand Down Expand Up @@ -69,11 +70,14 @@ export class UndoRedoRoutes implements RouteProvider {
? this.modelServerClient.undo(modelURI)
: this.modelServerClient.redo(modelURI);

delegated.then(this.performLiveValidation(modelURI)).then(relay(res)).catch(handleError(res));
delegated
.then(this.performLiveValidation(new URI(modelURI)))
.then(relay(res))
.catch(handleError(res));
};
}

protected performLiveValidation(modelURI: string): (delegatedResult: ModelUpdateResult) => Promise<ModelUpdateResult> {
protected performLiveValidation(modelURI: URI): (delegatedResult: ModelUpdateResult) => Promise<ModelUpdateResult> {
const validator = this.validationManager;

return async (delegatedResult: ModelUpdateResult) => validator.performLiveValidation(modelURI).then(() => delegatedResult);
Expand Down
3 changes: 2 additions & 1 deletion packages/modelserver-node/src/routes/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import { Logger, RouteProvider, RouterFactory } from '@eclipse-emfcloud/modelserver-plugin-ext';
import { Request, RequestHandler, Response } from 'express';
import { inject, injectable, named } from 'inversify';
import URI = require('urijs');
ndoschek marked this conversation as resolved.
Show resolved Hide resolved

import { InternalModelServerClientApi } from '../client/model-server-client';
import { ValidationManager } from '../services/validation-manager';
Expand Down Expand Up @@ -67,7 +68,7 @@ export class ValidationRoutes implements RouteProvider {
return;
}

this.validationManager.validate(modelURI).then(relay(res)).catch(handleError(res));
this.validationManager.validate(new URI(modelURI)).then(relay(res)).catch(handleError(res));
};
}
}
9 changes: 5 additions & 4 deletions packages/modelserver-node/src/server-integration-test.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { Operation } from 'fast-json-patch';
import { Container } from 'inversify';
import * as sinon from 'sinon';
import { assert } from 'sinon';
import * as URI from 'urijs';
import * as WebSocket from 'ws';

import { InternalModelServerClientApi, TransactionContext } from './client/model-server-client';
Expand Down Expand Up @@ -262,7 +263,7 @@ describe('Server Integration Tests', () => {
});

beforeEach(async () => {
transaction = await client.openTransaction('SuperBrewer3000.coffee');
transaction = await client.openTransaction(new URI('SuperBrewer3000.coffee'));
});

afterEach(async () => {
Expand All @@ -281,7 +282,7 @@ describe('Server Integration Tests', () => {

it('transaction already open', async () => {
try {
const duplicate = await client.openTransaction('SuperBrewer3000.coffee');
const duplicate = await client.openTransaction(new URI('SuperBrewer3000.coffee'));
await duplicate.rollback('Should not have been opened.');
expect.fail('Should not have been able to open duplicate transaction.');
} catch (error) {
Expand Down Expand Up @@ -371,7 +372,7 @@ describe('Server Integration Tests', () => {
it('Transaction rolled back by failure', async () => {
const patch: Operation = { op: 'replace', path: '/workflows/0/name', value: 'New Name' };

const transaction1 = await client.openTransaction('SuperBrewer3000.coffee');
const transaction1 = await client.openTransaction(new URI('SuperBrewer3000.coffee'));

try {
await transaction1.applyPatch(patch);
Expand All @@ -386,7 +387,7 @@ describe('Server Integration Tests', () => {
expect(transaction1.isOpen()).to.be.false;

// Should be able to open a new transaction
const transaction2 = await client.openTransaction('SuperBrewer3000.coffee');
const transaction2 = await client.openTransaction(new URI('SuperBrewer3000.coffee'));
expect(transaction2.isOpen()).to.be.true;

try {
Expand Down
17 changes: 9 additions & 8 deletions packages/modelserver-node/src/services/edit-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { AddCommand, ModelServerCommand, ModelUpdateResult, RemoveCommand, SetCo
import { Executor, Logger, Transaction } from '@eclipse-emfcloud/modelserver-plugin-ext';
import { Operation } from 'fast-json-patch';
import { inject, injectable, named } from 'inversify';
import * as URI from 'urijs';

import { InternalModelServerClientApi, isModelServerCommand, TransactionContext } from '../client/model-server-client';
import { CommandProviderRegistry } from '../command-provider-registry';
Expand Down Expand Up @@ -39,7 +40,7 @@ export class EditService {
@inject(ValidationManager)
protected readonly validationManager: ValidationManager;

async edit(modelURI: string, patchOrCommand: Operation | Operation[] | ModelServerCommand): Promise<ModelUpdateResult> {
async edit(modelURI: URI, patchOrCommand: Operation | Operation[] | ModelServerCommand): Promise<ModelUpdateResult> {
if (isModelServerCommand(patchOrCommand)) {
// Case of executing a command
const command = patchOrCommand;
Expand All @@ -51,15 +52,15 @@ export class EditService {
return this.forwardEdit(modelURI, patch);
}

protected async handleCommand(modelURI: string, command: ModelServerCommand): Promise<ModelUpdateResult> {
protected async handleCommand(modelURI: URI, command: ModelServerCommand): Promise<ModelUpdateResult> {
this.logger.debug(`Getting commands provided for ${command.type}`);

const provided = await this.commandProviderRegistry.getCommands(modelURI, command);
return this.forwardEdit(modelURI, provided);
}

protected forwardEdit(
modelURI: string,
modelURI: URI,
providedEdit: ModelServerCommand | Operation | Operation[] | Transaction
): Promise<ModelUpdateResult> {
if (this.triggerProviderRegistry.hasProviders()) {
Expand All @@ -69,7 +70,7 @@ export class EditService {
}

private async forwardEditSimple(
modelURI: string,
modelURI: URI,
providedEdit: ModelServerCommand | Operation | Operation[] | Transaction
): Promise<ModelUpdateResult> {
if (typeof providedEdit === 'function') {
Expand All @@ -86,18 +87,18 @@ export class EditService {

if (isModelServerCommand(providedEdit)) {
// Command case
result = this.modelServerClient.edit(modelURI, providedEdit);
result = this.modelServerClient.edit(modelURI.toString(), providedEdit);
} else {
// JSON Patch case
result = this.modelServerClient.edit(modelURI, providedEdit);
result = this.modelServerClient.edit(modelURI.toString(), providedEdit);
}

return result.then(this.performPatchValidation(modelURI));
}
}

private async forwardEditWithTriggers(
modelURI: string,
modelURI: URI,
providedEdit: ModelServerCommand | Operation | Operation[] | Transaction
): Promise<ModelUpdateResult> {
let result = true;
Expand Down Expand Up @@ -135,7 +136,7 @@ export class EditService {
* @param modelURI the model patched
* @returns a function that performs live validation on a model update result if it was successful
*/
protected performPatchValidation(modelURI: string): (validate: ModelUpdateResult) => Promise<ModelUpdateResult> {
protected performPatchValidation(modelURI: URI): (validate: ModelUpdateResult) => Promise<ModelUpdateResult> {
const validator = this.validationManager;

return async (validate: ModelUpdateResult) => {
Expand Down
Loading