@eclipse-emfcloud/modelserver-node
Advanced tools
Comparing version 0.2.0-next.ce04993.18 to 0.2.0-next.d9b3b27.30
@@ -32,2 +32,3 @@ "use strict"; | ||
const trigger_provider_registry_1 = require("../trigger-provider-registry"); | ||
const promise_utils_1 = require("./promise-utils"); | ||
const web_socket_utils_1 = require("./web-socket-utils"); | ||
@@ -233,2 +234,3 @@ exports.UpstreamConnectionConfig = Symbol('UpstreamConnectionConfig'); | ||
this.rollback = this.rollback.bind(this); | ||
this.uuid = promise_utils_1.CompletablePromise.newPromise(); | ||
} | ||
@@ -242,3 +244,3 @@ /** | ||
open(closeCallback) { | ||
const result = new Promise((resolve, reject) => { | ||
const result = new Promise((resolveTransaction, reject) => { | ||
this.closeCallback = closeCallback; | ||
@@ -254,3 +256,8 @@ const wsURI = new URL(this.transactionURI); | ||
this.socket = socket; | ||
resolve(this); | ||
web_socket_utils_1.WebSocketMessageAcceptor.promise(this.socket, msg => typeof msg.data === 'string', modelserver_client_1.MessageDataMapper.asString) | ||
.then(uuid_ => { | ||
this.uuid.resolve(uuid_); | ||
resolveTransaction(this); | ||
}) | ||
.catch(this.uuid.reject); | ||
}; | ||
@@ -268,2 +275,10 @@ socket.onmessage = event => { | ||
} | ||
/** | ||
* Obtain the UUID of this transaction, when it has been received from the upstream server. | ||
* | ||
* @returns the UUID, when it is available | ||
*/ | ||
getUUID() { | ||
return this.uuid; | ||
} | ||
// Doc inherited from `Executor` interface | ||
@@ -286,3 +301,3 @@ async applyPatch(patch) { | ||
// Doc inherited from `Executor` interface | ||
async execute(command) { | ||
async execute(modelUri, command) { | ||
if (!this.socket) { | ||
@@ -298,3 +313,3 @@ return Promise.reject('Socket is closed.'); | ||
this.logger.debug(`Getting commands provided recursively for ${command.type}`); | ||
const provided = await this.commandProviderRegistry.getCommands(command); | ||
const provided = await this.commandProviderRegistry.getCommands(modelUri, command); | ||
if (typeof provided === 'function') { | ||
@@ -325,13 +340,16 @@ // It's a transaction function. We already have a transaction context (this one) | ||
} | ||
sendExecuteMessage(body) { | ||
async sendExecuteMessage(body) { | ||
if (!this.socket) { | ||
return Promise.reject('Socket is closed.'); | ||
} | ||
// Make sure that we have first processed the transaction opening exchange | ||
await this.getUUID(); | ||
const { aggregatedUpdateResult: current } = this.peekNestedContext(); | ||
const result = web_socket_utils_1.WebSocketMessageAcceptor.promise(this.socket, message => { | ||
const matched = message.type === 'success' && modelserver_client_1.Operations.isPatch(message.data.patch); | ||
if (matched && current) { | ||
this.mergeModelUpdateResult(current, message.data); | ||
const result = web_socket_utils_1.WebSocketMessageAcceptor.promise(this.socket, message => true, // Need to accept and merge failed updates as well as successful | ||
modelserver_client_1.MessageDataMapper.patchModel).then(modelUpdateResult => { | ||
// If we are currently tracking a merged update result, incorporate this new result | ||
if (current) { | ||
this.mergeModelUpdateResult(current, modelUpdateResult); | ||
} | ||
return matched; | ||
return modelUpdateResult; | ||
}); | ||
@@ -385,3 +403,7 @@ this.socket.send(JSON.stringify(this.message('execute', body))); | ||
if ((src === null || src === void 0 ? void 0 : src.patch) && src.patch.length) { | ||
dst.success = dst.success && src.success; | ||
dst.patch = ((_a = dst.patch) !== null && _a !== void 0 ? _a : []).concat(src.patch); | ||
if (dst.success && src.patchModel) { | ||
dst.patchModel = src.patchModel; | ||
} | ||
} | ||
@@ -388,0 +410,0 @@ } |
@@ -12,3 +12,3 @@ /// <reference types="node" /> | ||
*******************************************************************************/ | ||
import { AnyObject, ModelServerMessage, TypeGuard } from '@eclipse-emfcloud/modelserver-client'; | ||
import { AnyObject, MessageDataMapper, ModelServerMessage, TypeGuard } from '@eclipse-emfcloud/modelserver-client'; | ||
import { Request } from 'express'; | ||
@@ -87,3 +87,14 @@ import { Logger } from 'winston'; | ||
*/ | ||
static promise<U>(socket: WebSocket, predicate: (object: ModelServerMessage<U>) => boolean): Promise<U>; | ||
static promise<D>(socket: WebSocket, predicate: (object: ModelServerMessage<D>) => boolean): Promise<D>; | ||
/** | ||
* Convenience for creating a promise that will be resolved when a message of the expected | ||
* type is received, with transformation of the message by a mapper function. | ||
* If the socket is closed before that, then the promise is rejected. | ||
* | ||
* @param socket the socket on which to wait for incoming messages | ||
* @param predicate a test whether a message payload is of the expected type | ||
* @param mapper a message data mapper to generate the promised result | ||
* @returns a promise of the expected mapped message type | ||
*/ | ||
static promise<D = unknown, U = unknown>(socket: WebSocket, predicate: (object: ModelServerMessage<D>) => boolean, mapper: MessageDataMapper<U>): Promise<U>; | ||
} | ||
@@ -90,0 +101,0 @@ /** |
@@ -106,13 +106,8 @@ "use strict"; | ||
} | ||
/** | ||
* Convenience for creating a promise that will be resolved when a message of the expected | ||
* type is received. If the socket is closed before that, then the promise is rejected. | ||
* | ||
* @param socket the socket on which to wait for incoming messages | ||
* @param predicate a test whether a message payload is of the expected type | ||
* @returns a promise of the expected message type | ||
*/ | ||
static promise(socket, predicate) { | ||
static promise(socket, predicate, mapper) { | ||
if (!mapper) { | ||
mapper = modelserver_client_1.IdentityMapper; | ||
} | ||
return new Promise((resolve, reject) => { | ||
const parser = message => (predicate(message) ? message.data : undefined); | ||
const parser = message => (predicate(message) ? mapper(message) : undefined); | ||
WebSocketMessageAcceptor.accept(socket, parser, resolve, () => reject('Socket closed')); | ||
@@ -119,0 +114,0 @@ }); |
@@ -44,2 +44,3 @@ /******************************************************************************** | ||
* | ||
* @param modelUri the URI of the model being edited. | ||
* @param customCommand get the commands provided for a given custom command | ||
@@ -49,4 +50,4 @@ * @returns the provided command, command transaction, or the original `customCommand` standing in for itself | ||
*/ | ||
getCommands(customCommand: ModelServerCommand): Promise<ModelServerCommand | Operation[] | Transaction>; | ||
getCommands(modelUri: string, customCommand: ModelServerCommand): Promise<ModelServerCommand | Operation[] | Transaction>; | ||
} | ||
//# sourceMappingURL=command-provider-registry.d.ts.map |
@@ -83,2 +83,3 @@ "use strict"; | ||
* | ||
* @param modelUri the URI of the model being edited. | ||
* @param customCommand get the commands provided for a given custom command | ||
@@ -88,3 +89,3 @@ * @returns the provided command, command transaction, or the original `customCommand` standing in for itself | ||
*/ | ||
async getCommands(customCommand) { | ||
async getCommands(modelUri, customCommand) { | ||
let result; | ||
@@ -94,3 +95,3 @@ const provider = this.getProvider(customCommand); | ||
this.logger.debug(`Invoking provider for custom ${customCommand.type} command`); | ||
result = await provider.getCommands(customCommand); | ||
result = await provider.getCommands(modelUri, customCommand); | ||
if (!result) { | ||
@@ -97,0 +98,0 @@ this.logger.warn(`No commands provided. Custom ${customCommand.type} command will be unhandled.`); |
@@ -34,2 +34,5 @@ "use strict"; | ||
let BasicModelServerPluginContext = BasicModelServerPluginContext_1 = class BasicModelServerPluginContext { | ||
constructor() { | ||
this.plugins = []; | ||
} | ||
async initializePlugins() { | ||
@@ -97,2 +100,3 @@ const initializer = this.initializePlugin.bind(this); | ||
__decorate([ | ||
(0, inversify_1.optional)(), | ||
(0, inversify_1.multiInject)(modelserver_plugin_ext_1.ModelServerPlugin), | ||
@@ -99,0 +103,0 @@ __metadata("design:type", Array) |
@@ -101,3 +101,3 @@ "use strict"; | ||
this.logger.debug(`Getting commands provided for ${command.type}`); | ||
const provided = await this.commandProviderRegistry.getCommands(command); | ||
const provided = await this.commandProviderRegistry.getCommands(modelURI, command); | ||
this.forwardEdit(modelURI, provided, res); | ||
@@ -148,3 +148,3 @@ } | ||
// Command case | ||
await executor.execute(providedEdit); | ||
await executor.execute(modelURI, providedEdit); | ||
} | ||
@@ -151,0 +151,0 @@ else { |
@@ -68,8 +68,28 @@ "use strict"; | ||
exports.SubscriptionRoutes = SubscriptionRoutes; | ||
/** | ||
* Parse the subscription request query parameters coming from the downstream client to | ||
* extract details for local use and filter what is passed along to the upstream | ||
* subscription. This includes | ||
* | ||
* - enforcing required parameters, e.g. `modeluri` | ||
* - applying type coercion and defaults to known parameters, e.g. `livevalidation` | ||
* - filtering out query parameters that cannot be parsed from simple strings | ||
* | ||
* @param query the subscription request query parameters from downstream that we forward to the upstream server | ||
* @returns the parsed and filtered subscription query parameters | ||
*/ | ||
function parseQuery(query) { | ||
return { | ||
return Object.keys(query).reduce((acc, item) => { | ||
const param = parseQueryParam(query, item); | ||
if (param && !acc[item]) { | ||
acc[item] = param; | ||
} | ||
return acc; | ||
}, { | ||
// Special-case these because the modeluri is required and the | ||
// other two are known parameters and have typed default values | ||
modeluri: parseQueryParam(query, 'modeluri'), | ||
livevalidation: parseQueryParam(query, 'livevalidation', false), | ||
timeout: parseQueryParam(query, 'timeout', 'number') | ||
}; | ||
}); | ||
} | ||
@@ -76,0 +96,0 @@ function parseQueryParam(query, name, typeOrDefaultValue, defaultValue) { |
@@ -0,1 +1,2 @@ | ||
/// <reference types="node" /> | ||
/******************************************************************************** | ||
@@ -15,2 +16,3 @@ * Copyright (c) 2022 STMicroelectronics. | ||
import { WebsocketRequestHandler } from 'express-ws'; | ||
import * as http from 'http'; | ||
import { InternalModelServerClientApi } from './client/model-server-client'; | ||
@@ -28,2 +30,3 @@ import { InternalModelServerPluginContext } from './plugin-context'; | ||
protected initialize(): void; | ||
protected server: http.Server; | ||
/** | ||
@@ -38,2 +41,8 @@ * Serve the Model Server application on the given TCP `port`. | ||
/** | ||
* Stop the server. | ||
* | ||
* @returns a promise that resolves when the server is stopped | ||
*/ | ||
stop(): Promise<void>; | ||
/** | ||
* Create a request handler that forwards requests to the given _Upstream Model Server_. | ||
@@ -40,0 +49,0 @@ * |
@@ -80,3 +80,3 @@ "use strict"; | ||
const resultHandler = () => { | ||
app.listen(port, () => this.logger.info(`Model Server (node.js) listening on port ${port}.`)); | ||
this.server = app.listen(port, () => this.logger.info(`Model Server (node.js) listening on port ${port}.`)); | ||
return true; | ||
@@ -90,2 +90,21 @@ }; | ||
/** | ||
* Stop the server. | ||
* | ||
* @returns a promise that resolves when the server is stopped | ||
*/ | ||
async stop() { | ||
return new Promise((resolve, reject) => { | ||
var _a; | ||
(_a = this.server) === null || _a === void 0 ? void 0 : _a.close((err) => { | ||
if (err) { | ||
this.logger.warn('Failed to stop server: %s', err.message); | ||
reject(err); | ||
} | ||
else { | ||
resolve(); | ||
} | ||
}); | ||
}); | ||
} | ||
/** | ||
* Create a request handler that forwards requests to the given _Upstream Model Server_. | ||
@@ -159,4 +178,4 @@ * | ||
upstream.on('close', (0, web_socket_utils_1.handleClose)('upstream', this.logger, downstream)); | ||
downstream.on('message', msg => upstream.send(msg)); | ||
upstream.on('message', msg => downstream.send(msg)); | ||
downstream.on('message', rawDataHelper(upstream)); | ||
upstream.on('message', rawDataHelper(downstream)); | ||
} | ||
@@ -202,2 +221,4 @@ catch (error) { | ||
exports.ModelServer = ModelServer; | ||
// Relay text data as text | ||
const rawDataHelper = (sock) => (data, isBinary) => isBinary ? sock.send(data) : sock.send(data.toString()); | ||
//# sourceMappingURL=server.js.map |
{ | ||
"name": "@eclipse-emfcloud/modelserver-node", | ||
"version": "0.2.0-next.ce04993.18+ce04993", | ||
"version": "0.2.0-next.d9b3b27.30+d9b3b27", | ||
"description": "Business Logic layer façade for the Model Server.", | ||
@@ -28,3 +28,3 @@ "license": "(EPL-2.0 OR MIT)", | ||
"dependencies": { | ||
"@eclipse-emfcloud/modelserver-plugin-ext": "0.2.0-next.ce04993.18+ce04993", | ||
"@eclipse-emfcloud/modelserver-plugin-ext": "0.2.0-next.d9b3b27.30+d9b3b27", | ||
"axios": "^0.24.0", | ||
@@ -62,3 +62,3 @@ "express": "^4.17.1", | ||
}, | ||
"gitHead": "ce04993de916e356ac692284e7bb9ff5ca0f9218" | ||
"gitHead": "d9b3b27cb095d547d0a474c187cc836abb75fbc0" | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 1 instance in 1 package
270145
87
3602