Moving Files and fix the Server Architecture

This commit is contained in:
Martin Karkowski 2020-11-12 17:07:05 +01:00
parent 2591e2cbfb
commit c8c721b1c9
23 changed files with 389 additions and 127 deletions

View File

@ -2,7 +2,7 @@
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2020-11-11 13:27:58
* @modify date 2020-11-11 15:41:24
* @modify date 2020-11-12 13:49:17
* @desc [description]
*/
@ -39,7 +39,7 @@ const main = async function () {
['-f', '--file'],
{
help: 'File containing containing the package definitions.',
defaultValue: './config/packages.json',
defaultValue: './config/settings.json',
type: 'string',
dest: 'file'
}

View File

@ -2,7 +2,7 @@
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2020-11-09 13:27:58
* @modify date 2020-11-10 16:24:52
* @modify date 2020-11-12 11:31:20
* @desc [description]
*/
@ -109,7 +109,7 @@ const main = async function () {
}
try {
await writeFile(join('dist', 'src', 'apidoc.json'), JSON.stringify(data.generalInformationModel, undefined, 4));
await writeFile(join('dist', 'lib', 'open-api', 'apidoc.json'), JSON.stringify(data.generalInformationModel, undefined, 4));
} catch(e){
opts.logger.error('Failed to generate ' + join('dist', 'src', 'apidoc.json'));
console.error(e);

View File

@ -2,28 +2,29 @@
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2020-11-11 13:27:58
* @modify date 2020-11-11 16:59:09
* @modify date 2020-11-12 14:10:27
* @desc [description]
*/
import { ArgumentParser } from 'argparse';
import { readFile } from 'fs/promises';
import "reflect-metadata";
import { promisify } from 'util';
import { AmqpLayer } from '../communication/amqpLayer';
import { EventLayer } from '../communication/eventLayer';
import { IoSocketClient } from '../communication/IoSocketClient';
import { IoSocketServer } from '../communication/IoSocketServer';
import { getLinkedDispatcher } from '../dispatcher/getLinkedDispatcher';
import { getPackageLoader } from '../loader/getPackageLoader';
import { loadPackages } from '../loader/loadPackages';
import { loadFunctions, loadPackages } from '../loader/loadPackages';
import { getNopeLogger } from '../logger/getLogger';
import { LoggerLevels } from '../logger/nopeLogger';
import { setGlobalLoggerLevel } from '../logger/setGlobalLoggerLevel';
import { INopeDispatcher } from '../types/nope/nopeDispatcher.interface';
// Define the Main Function.
// This function is used as cli tool.
const main = async function () {
export async function runNopeBackend() {
const parser = new ArgumentParser({
version: '1.0.0',
@ -59,13 +60,11 @@ const main = async function () {
opts = {} as any
}
parser.addArgument(
['-f', '--file'],
{
help: 'File containing containing the package definitions.',
defaultValue: './config/packages.json',
defaultValue: './config/settings.json',
type: 'string',
dest: 'file'
}
@ -82,7 +81,7 @@ const main = async function () {
}
);
parser.addArgument(
['-params', '--params'],
['-p', '--params'],
{
help: 'Paramas for the Channel, to connect to. The Following Defaults are used: \n' + JSON.stringify(layerDefaults, undefined, 4),
defaultValue: 'not-provided',
@ -90,8 +89,34 @@ const main = async function () {
dest: 'params'
}
);
parser.addArgument(
['-s', '--skipLoadingConfig'],
{
help: 'Flag to prevent loading the elements defined in the settings.json.',
action: 'append', nargs: '?',
dest: 'skipLoadingConfig'
}
);
parser.addArgument(
['-l', '--log'],
{
help: 'Specify the Logger Level. Defaults to "info". Valid values are: ' + LoggerLevels.join(', '),
defaultValue: 'info',
type: 'string',
dest: 'log'
}
);
const args = parser.parseArgs();
if (LoggerLevels.includes(args.log)){
setGlobalLoggerLevel(args.level);
}
args.skipLoadingConfig = Array.isArray(args.skipLoadingConfig);
// Define a Logger
const logger = getNopeLogger('starter');
@ -112,11 +137,26 @@ const main = async function () {
}
}
// If required load all Packages.
if (!args.skipLoadingConfig){
// Try to load the Modules.
try {
logger.info('loading Packages')
await loadFunctions(args.file)
} catch (e) {
logger.error('Unable to load the Packages defined in ' + args.file + ' See Output for detailled information');
console.error(e)
return;
}
}
// Try to create an dispatcher:
let dispatcher: INopeDispatcher;
try {
dispatcher = getLinkedDispatcher({
communicator: new validLayers[args.channel](... opts.params)
communicator: new validLayers[args.channel](... opts.params),
logger: getNopeLogger('dispatcher', 'silly')
})
} catch (e) {
logger.error('Unable to create the Dispatcher. Please check the Provided Parameters.');
@ -126,14 +166,25 @@ const main = async function () {
await dispatcher.ready.waitFor(value => value);
// Try to load the Modules.
try {
await loadPackages(getPackageLoader(dispatcher), args.file)
} catch (e) {
logger.error('Unable to load the Packages defined in ' + args.file + ' See Output for detailled information');
console.error(e)
return;
}
// If required load all Packages.
if (!args.skipLoadingConfig){
// Try to load the Modules.
try {
logger.info('loading Packages')
await loadPackages(getPackageLoader(dispatcher), args.file)
} catch (e) {
logger.error('Unable to load the Packages defined in ' + args.file + ' See Output for detailled information');
console.error(e)
return;
}
}
// Return the Dispatcher.
return dispatcher;
}
main().catch(e => console.error(e));
// If requested As Main => Perform the Operation.
if (require.main === module) {
runNopeBackend().catch(console.error);
}

View File

@ -0,0 +1,25 @@
/**
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2020-11-12 11:22:27
* @modify date 2020-11-12 11:22:36
* @desc [description]
*/
import "reflect-metadata";
import { getNopeLogger } from '../logger/getLogger';
import { startOpenApiBackend } from '../open-api/startOpenApiBackend';
import { runNopeBackend } from './runNopeBackend';
// Define the Main Function.
// This function is used as cli tool.
export async function runOpenApiServer() {
const dispatcher = await runNopeBackend();
const result = await startOpenApiBackend(dispatcher, { port: 3001, logger: getNopeLogger('open-api-server', 'debug') });
}
// If requested As Main => Perform the Operation.
if (require.main === module) {
runOpenApiServer().catch(console.error);
}

View File

@ -2,22 +2,24 @@
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2020-11-04 17:36:04
* @modify date 2020-11-06 08:46:24
* @modify date 2020-11-12 13:23:48
* @desc [description]
*/
import { EventEmitter } from 'events';
import { connect, Socket } from 'socket.io-client';
import { getCentralNopeLogger, getNopeLogger } from "../logger/getLogger";
import { Logger } from "winston";
import { getNopeLogger } from "../logger/getLogger";
import { NopeObservable } from '../observables/nopeObservable';
import { IAvailableInstanceGeneratorsMsg, IAvailableServicesMsg, IAvailableTopicsMsg, ICommunicationInterface, IExternalEventMsg, IRequestTaskMsg, IResponseTaskMsg, ITaskCancelationMsg } from "../types/nope/nopeCommunication.interface";
import { INopeObservable } from '../types/nope/nopeObservable.interface';
import { Logger } from "winston";
export class IoSocketClient implements ICommunicationInterface {
connected: INopeObservable<boolean>;
protected _emitter: typeof Socket;
protected _internalEmitter: EventEmitter
protected _logger: Logger;
constructor(public uri: string) {
@ -34,7 +36,8 @@ export class IoSocketClient implements ICommunicationInterface {
this._logger.info('connecting to: ' + uri);
this._emitter = connect(uri);
this._internalEmitter = new EventEmitter();
this._emitter.on('connect', (...args) => {
// Element is connected
_this._logger.info('connected');
@ -48,10 +51,12 @@ export class IoSocketClient implements ICommunicationInterface {
}
async onTaskCancelation(cb: (msg: ITaskCancelationMsg) => void) {
this._internalEmitter.on('cancel', cb);
this._emitter.on('cancel', cb);
}
async emitTaskCancelation(msg: ITaskCancelationMsg) {
this._internalEmitter.emit('cancel', msg);
this._emitter.emit('cancel', msg);
}
@ -64,70 +69,87 @@ export class IoSocketClient implements ICommunicationInterface {
}
async emitNewInstanceGeneratorsAvailable(generators: IAvailableInstanceGeneratorsMsg) {
this._internalEmitter.emit('generators', generators);
this._emitter.emit('generators', generators);
}
async onNewInstanceGeneratorsAvailable(cb: (generators: IAvailableInstanceGeneratorsMsg) => void) {
this._emitter.on('generators', cb)
this._internalEmitter.on('generators', cb);
this._emitter.on('generators', cb);
}
async emitRpcRequest(name: string, request: IRequestTaskMsg) {
this._internalEmitter.emit(name, request);
this._emitter.emit(name, request);
}
async emitRpcResult(name: string, result: IResponseTaskMsg) {
this._internalEmitter.emit(name, result);
this._emitter.emit(name, result);
}
async onRpcResult(name: string, cb: (result: IResponseTaskMsg) => void) {
this._internalEmitter.on(name, cb);
this._emitter.on(name, cb);
}
async offRpcResponse(name: string, cb: (result: IResponseTaskMsg) => void) {
this._internalEmitter.off(name, cb);
this._emitter.off(name, cb);
}
async onRpcRequest(name: string, cb: (data: IRequestTaskMsg) => void) {
this._internalEmitter.on(name, cb);
this._emitter.on(name, cb);
}
async offRpcRequest(name: string, cb: (data: IRequestTaskMsg) => void) {
this._internalEmitter.off(name, cb);
this._emitter.off(name, cb);
}
async emitNewServicesAvailable(services: IAvailableServicesMsg) {
this._internalEmitter.emit('services', services)
this._emitter.emit('services', services)
}
async onNewServicesAvailable(cb: (services: IAvailableServicesMsg) => void) {
this._internalEmitter.on('services', cb);
this._emitter.on('services', cb);
}
async onBonjour(cb: (dispatcher: string) => void) {
this._internalEmitter.on('bonjour', cb);
this._emitter.on('bonjour', cb);
}
async emitBonjour(dispatcher: string) {
this._internalEmitter.emit('bonjour', dispatcher);
this._emitter.emit('bonjour', dispatcher);
}
async emitNewTopicsAvailable(topics: IAvailableTopicsMsg) {
this._internalEmitter.emit('topics', topics);
this._emitter.emit('topics', topics)
}
async onNewTopicsAvailable(cb: (topics: IAvailableTopicsMsg) => void) {
this._emitter.on('topics', cb)
this._emitter.on('topics', cb);
this._emitter.on('topics', cb);
}
async onEvent(event: string, cb: (data: IExternalEventMsg) => void) {
this._emitter.on('event_' + event, cb);
this._emitter.on('event_' + event, cb);
}
async emitEvent(event: string, data: IExternalEventMsg) {
this._emitter.emit('event_' + event, data)
this._internalEmitter.emit('event_' + event, data);
this._emitter.emit('event_' + event, data);
}
async offEvent(event: string, cb: (data: IExternalEventMsg) => void) {
this._internalEmitter.off('event_' + event, cb);
this._emitter.off('event_' + event, cb);
}
}

View File

@ -2,10 +2,11 @@
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2020-11-06 08:52:42
* @modify date 2020-11-11 16:29:07
* @modify date 2020-11-12 13:26:43
* @desc [description]
*/
import { EventEmitter } from "events";
import { Server } from "http";
import * as io from 'socket.io';
import { Logger } from "winston";
@ -24,6 +25,7 @@ export class IoSocketSeverEventEmitter {
protected _sockets: Set<io.Socket>;
protected _funcs: Map<string, (...args: any[]) => void>;
protected _logger: Logger;
protected _internalEmitter: EventEmitter;
/**
* Creates an instance of IoSocketServer.
@ -38,6 +40,8 @@ export class IoSocketSeverEventEmitter {
this._socket = (io as any)();
}
this._internalEmitter = new EventEmitter();
this.connected = new NopeObservable();
this.connected.setContent(false);
@ -99,6 +103,7 @@ export class IoSocketSeverEventEmitter {
// Store the Function
this._funcs.set(event, cb);
this._internalEmitter.on(event, cb);
}
emit(event: string, data: any): void {
@ -106,6 +111,7 @@ export class IoSocketSeverEventEmitter {
this._logger.debug('sending data on: ' + event);
}
this._socket.emit(event, data);
this._internalEmitter.emit(event,data);
}
}

View File

@ -2,7 +2,7 @@
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2020-11-06 08:52:55
* @modify date 2020-11-06 09:21:34
* @modify date 2020-11-12 13:55:43
* @desc [description]
*/
@ -34,12 +34,13 @@ export type callable<T> = {
* @param options The Options.
*/
export function exportFunctionToDispatcher<T>(func: T, options: IExportFunctionToDispatcherParameters) {
container.instance.set(options.id, {
callback: async (...args) => await ((func as any)(...args)),
options,
uri: options.id || (func as any).name
});
// Only add the element if it doesnt exists.
if (!container.instance.has(options.id)){
container.instance.set(options.id, {
callback: async (...args) => await ((func as any)(...args)),
options,
uri: options.id || (func as any).name
});
}
return func;
}

View File

@ -2,7 +2,7 @@
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2020-08-25 23:27:28
* @modify date 2020-11-11 16:38:47
* @modify date 2020-11-12 14:11:17
* @desc [description]
*/
@ -16,18 +16,21 @@ import { getDispatcher } from "./getDispatcher";
* @param options
*/
export function getLinkedDispatcher(options: INopeRpcDispatcherOptions) {
const container = getSingleton('nopeBackendDispatcher.container', () => {
return new Map<string, {
uri: string,
callback: (...args) => Promise<any>,
options: IExportFunctionToDispatcherParameters
}>()
});
// Create the Dispatcher Instance.
const dispatcher = getDispatcher(options);
// Define a Container, which contains all functions.
const container = getSingleton('nopeBackendDispatcher.container', () => {
return new Map<string, {
uri: string,
callback: (...args) => Promise<any>,
options: IExportFunctionToDispatcherParameters
}>()
});
// If the Dispatcher has been connected, register all functions.
dispatcher.ready.waitFor(value => value).then(() => {
dispatcher.ready.waitFor(value => value === true).then(() => {
if (dispatcher.ready.getContent()){
// Iterate over the Functions
for (const [uri, settings] of container.instance.entries()) {
@ -36,7 +39,7 @@ export function getLinkedDispatcher(options: INopeRpcDispatcherOptions) {
});
}
} else {
console.log('Failed')
// Failed to Setup the Container.
}
});

View File

@ -2,7 +2,7 @@
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2020-10-12 18:52:00
* @modify date 2020-11-09 09:43:40
* @modify date 2020-11-12 14:27:58
* @desc [description]
*/
@ -141,8 +141,8 @@ export class nopeDispatcher implements INopeDispatcher {
provideInstanceGeneratorForExternalDispatchers<I extends INopeModule>(identifier: string, cb: IGenerateRemoteInstanceForOtherDispatcherCallback<I>) {
const _this = this;
if (this._logger?.isDebugEnabled()) {
this._logger.debug('Adding instance generator for "' + identifier + '" to external Generators. Other Elements can now create instances of this type.');
if (this._logger?.isSillyEnabled()) {
this._logger.silly('Adding instance generator for "' + identifier + '" to external Generators. Other Elements can now create instances of this type.');
}
const _cb = this.registerFunction(async (data: IInstanceCreationMsg) => {
@ -219,8 +219,8 @@ export class nopeDispatcher implements INopeDispatcher {
unprovideInstanceGeneratorForExternalDispatchers(identifier: string) {
if (this._externalGenerators.has(identifier)) {
if (this._logger?.isDebugEnabled()) {
this._logger.debug('Removing instance generator for "' + identifier + '" from external Generators. Other Elements cant create instances of this type anymore.');
if (this._logger?.isSillyEnabled()) {
this._logger.silly('Removing instance generator for "' + identifier + '" from external Generators. Other Elements cant create instances of this type anymore.');
}
this.unregisterFunction(this._externalGenerators.get(identifier));
@ -230,8 +230,8 @@ export class nopeDispatcher implements INopeDispatcher {
registerInternalInstanceGenerator<I extends INopeModule>(identifier: string, cb: IGenerateRemoteInstanceCallback<I>) {
if (this._logger?.isDebugEnabled()) {
this._logger.debug('Adding instance generator for "' + identifier + '" as internal Generator. This Generator wont be used externally.');
if (this._logger?.isSillyEnabled()) {
this._logger.silly('Adding instance generator for "' + identifier + '" as internal Generator. This Generator wont be used externally.');
}
this._internalGenerators.set(identifier, cb);
@ -239,8 +239,8 @@ export class nopeDispatcher implements INopeDispatcher {
unregisterInternalInstanceGenerator(identifier: string) {
if (this._logger?.isDebugEnabled()) {
this._logger.debug('Rmoving instance generator for "' + identifier + '" from internal Generator. The sytem cant create elements of this type any more.');
if (this._logger?.isSillyEnabled()) {
this._logger.silly('Rmoving instance generator for "' + identifier + '" from internal Generator. The sytem cant create elements of this type any more.');
}
this._internalGenerators.delete(identifier);
@ -267,13 +267,13 @@ export class nopeDispatcher implements INopeDispatcher {
throw Error('Please Provide at least a "type" and "identifier" in the paremeters');
}
if (this._logger?.isDebugEnabled()) {
this._logger.debug('Requesting an Instance of type: "' + _defDescription.type + '" with the identifier: "' + _defDescription.identifier + '"');
if (this._logger?.isSillyEnabled()) {
this._logger.silly('Requesting an Instance of type: "' + _defDescription.type + '" with the identifier: "' + _defDescription.identifier + '"');
}
if (this._instances.has(_description.identifier)) {
if (this._logger?.isDebugEnabled()) {
this._logger.debug('Already created instance with the identifiert: "' + _defDescription.identifier + '" => returning this instance');
if (this._logger?.isSillyEnabled()) {
this._logger.silly('Already created instance with the identifiert: "' + _defDescription.identifier + '" => returning this instance');
}
// Add the Dispatcher to the Element:
@ -296,16 +296,16 @@ export class nopeDispatcher implements INopeDispatcher {
if (this._internalGenerators.has(_type)) {
if (this._logger?.isDebugEnabled()) {
this._logger.debug('No instance with the identifiert: "' + _defDescription.identifier + '" found, but an internal generator is available. Using the internal one for creating the instance and requesting the "real" instance externally');
if (this._logger?.isSillyEnabled()) {
this._logger.silly('No instance with the identifiert: "' + _defDescription.identifier + '" found, but an internal generator is available. Using the internal one for creating the instance and requesting the "real" instance externally');
}
const result = await this.performCall<IInstanceDescriptionMsg>('generateInstance_' + _description.type, [_description], {
paramsHasNoCallback: true,
});
if (this._logger?.isDebugEnabled()) {
this._logger.debug('Received a description for the instance');
if (this._logger?.isSillyEnabled()) {
this._logger.silly('Received a description for the instance');
}
// Create the Instance
@ -470,12 +470,16 @@ export class nopeDispatcher implements INopeDispatcher {
if (this._logger?.isErrorEnabled()) {
// If there is a Logger:
this._logger.error('Dispatcher "' + this.id + '" failed with request: "' + data.taskId + '"');
this._logger.error(error);
console.error(error);
this._logger.error(error);
}
// An Error occourd => Forward the Error.
const result: IResponseTaskMsg = {
error,
error: {
error,
msg: error.toString()
},
taskId: data.taskId,
type: 'response'
}
@ -508,6 +512,12 @@ export class nopeDispatcher implements INopeDispatcher {
// Based on the Result of the Remote =>
if (task && data.error) {
if (this._logger?.isErrorEnabled()){
this._logger.error('Failed with task ' + data.taskId);
this._logger.error('Reason: ' + data.error.msg);
console.log(data.error.error);
}
task.reject(data.error);
// Clearout the Timer
@ -592,6 +602,10 @@ export class nopeDispatcher implements INopeDispatcher {
if (data.dispatcher !== _this.id) {
_this._mappingOfRemoteDispatchersAndServices.set(data.dispatcher, data);
_this._updateExternalServices();
if (this._logger?.isSillyEnabled()) {
// If there is a Logger:
this._logger.silly('received new services');
}
}
} catch (e) {
}
@ -603,6 +617,10 @@ export class nopeDispatcher implements INopeDispatcher {
if (data.dispatcher !== _this.id) {
_this._mappingOfRemoteDispatchersAndTopics.set(data.dispatcher, data);
_this._updateExternalEvents();
if (this._logger?.isSillyEnabled()) {
// If there is a Logger:
this._logger.silly('received new events');
}
}
} catch (e) {
}
@ -612,6 +630,10 @@ export class nopeDispatcher implements INopeDispatcher {
try {
_this._mappingOfRemoteDispatchersAndGenerators.set(data.dispatcher, data);
_this._updateExternalGenerators();
if (this._logger?.isSillyEnabled()) {
// If there is a Logger:
this._logger.silly('received new generators');
}
} catch (e) {
}
})
@ -638,6 +660,11 @@ export class nopeDispatcher implements INopeDispatcher {
_this._updateExternalServices();
_this._updateExternalGenerators();
_this._updateExternalEvents();
if (this._logger?.isDebugEnabled()) {
// If there is a Logger:
this._logger.debug('a dispatcher went offline');
}
});
this._communicator.onTaskCancelation((event) => {
@ -765,7 +792,7 @@ export class nopeDispatcher implements INopeDispatcher {
for (const dispatcherInfo of this._mappingOfRemoteDispatchersAndServices.values()) {
dispatcherInfo.services.map(service => _this._externalProvidedServices.add(service));
}
// Create a Comparing loop.
// The Loop checks if the element doesnt exists in the known services
// before the update.
@ -914,9 +941,9 @@ export class nopeDispatcher implements INopeDispatcher {
// Register Functions.
this._communicator.onRpcRequest(_req, cb);
if (this._logger?.isDebugEnabled()) {
if (this._logger?.isSillyEnabled()) {
// If there is a Logger:
this._logger.debug('Dispatcher "' + this.id + '" listening on: "' + _req + '"');
this._logger.silly('Dispatcher "' + this.id + '" listening on: "' + _req + '"');
}
}
}
@ -1014,9 +1041,9 @@ export class nopeDispatcher implements INopeDispatcher {
// Publish the Available Services.
this._sendAvailableServices();
if (this._logger?.isDebugEnabled()) {
if (this._logger?.isSillyEnabled()) {
// If there is a Logger:
this._logger.debug('Dispatcher "' + this.id + '" registered: "' + _id + '"');
this._logger.silly('Dispatcher "' + this.id + '" registered: "' + _id + '"');
}
}
@ -1042,9 +1069,9 @@ export class nopeDispatcher implements INopeDispatcher {
// Publish the Available Services.
this._sendAvailableServices();
if (this._logger?.isDebugEnabled()) {
if (this._logger?.isSillyEnabled()) {
// If there is a Logger:
this._logger.debug('Dispatcher "' + this.id + '" unregistered: "' + _id + '"');
this._logger.silly('Dispatcher "' + this.id + '" unregistered: "' + _id + '"');
}
}
@ -1061,9 +1088,9 @@ export class nopeDispatcher implements INopeDispatcher {
// Publish the Available Services.
this._sendAvailableProperties();
if (this._logger?.isDebugEnabled()) {
if (this._logger?.isSillyEnabled()) {
// If there is a Logger:
this._logger.debug('Dispatcher "' + this.id + '" unregistered: "' + _id + '"');
this._logger.silly('Dispatcher "' + this.id + '" unregistered: "' + _id + '"');
}
}
@ -1490,13 +1517,13 @@ export class nopeDispatcher implements INopeDispatcher {
}
if (_this._logger?.isDebugEnabled()) {
_this._logger.debug('Clearing Callbacks from ' + _taskId);
if (_this._logger?.isSillyEnabled()) {
_this._logger.silly('Clearing Callbacks from ' + _taskId);
}
}
if (_this._logger?.isDebugEnabled()) {
_this._logger.debug('Dispatcher "' + this.id + '" requesting externally Function "' + serviceName + '" with task: "' + _taskId + '"');
if (_this._logger?.isSillyEnabled()) {
_this._logger.silly('Dispatcher "' + this.id + '" requesting externally Function "' + serviceName + '" with task: "' + _taskId + '"');
}
// Define a Callback-Function, which will expect the Task.
@ -1592,15 +1619,15 @@ export class nopeDispatcher implements INopeDispatcher {
if (_this._subscriptionMode === 'individual') {
await _this._communicator.emitRpcRequest(_this._getServiceName(taskRequest.functionId, 'request'), taskRequest);
if (_this._logger && _this._logger.isDebugEnabled()) {
_this._logger.debug('Dispatcher "' + this.id + '" putting task "' + _taskId + '" on: "' + _this._getServiceName(taskRequest.functionId, 'request') + '"');
if (_this._logger && _this._logger.isSillyEnabled()) {
_this._logger.silly('Dispatcher "' + this.id + '" putting task "' + _taskId + '" on: "' + _this._getServiceName(taskRequest.functionId, 'request') + '"');
}
} else {
await _this._communicator.emitRpcRequest('request', taskRequest);
if (_this._logger && _this._logger.isDebugEnabled()) {
_this._logger.debug('Dispatcher "' + this.id + '" putting task "' + _taskId + '" on: "request"');
if (_this._logger && _this._logger.isSillyEnabled()) {
_this._logger.silly('Dispatcher "' + this.id + '" putting task "' + _taskId + '" on: "request"');
}
}

View File

@ -2,7 +2,7 @@
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2020-11-11 14:19:10
* @modify date 2020-11-11 16:12:18
* @modify date 2020-11-12 13:53:17
* @desc [description]
*/
@ -15,7 +15,7 @@ import { IInstanceCreationMsg } from "../types/nope/nopeCommunication.interface"
import { IPackageDescription } from "../types/nope/nopePackage.interface";
import { INopePackageLoader } from "../types/nope/nopePackageLoader.interface";
export interface IConfigFile {
export interface IPackageConfig {
nameOfPackage: string;
defaultInstances: {
options: Partial<IInstanceCreationMsg>;
@ -31,6 +31,14 @@ export interface IConfigFile {
path: string;
}
export interface IConfigFile {
functions: {
path: string,
functions: []
}[],
packages: IPackageConfig[]
}
/**
* List the available Packages
*
@ -65,6 +73,33 @@ export async function listPackages(dir: string = './modules') {
return ret;
}
export async function listFunctions(dir: string = './modules') {
// Define the Return Array.
const ret = new Array<{
content: any,
path: string,
}>();
// Scan for the Package-Files
// And iterate over them.
for (const fileName of await listFiles(dir, '.functions.js')) {
// Now Try to load a Package, to test whether is is an assembly.
try {
ret.push({
content: (await import(resolve(fileName))).DESCRIPTION,
path: fileName
});
} catch (e) {
getNopeLogger('helper-list-functions').error('Failed Loading the functions in file: ' + fileName);
getNopeLogger('helper-list-functions').error(e);
}
}
return ret;
}
/**
* Helper Function to write a default configuration.
*
@ -77,8 +112,7 @@ export async function writeDefaultConfig(
filename: string = join(resolve(process.cwd()), 'config', 'assembly.json')
) {
// Determine all Packages
const packages = await listPackages(dir);
const valuesToStore: IConfigFile[] = packages.map(item => {
const packages: IPackageConfig[] = (await listPackages(dir)).map(item => {
return {
nameOfPackage: item.package.nameOfPackage,
defaultInstances: item.package.defaultInstances,
@ -87,7 +121,18 @@ export async function writeDefaultConfig(
}
});
await createFile(filename, JSON.stringify(valuesToStore, undefined, 4));
const functions = (await listFunctions(dir)).map(item => {
return {
path: item.path,
functions: Object.getOwnPropertyNames(item.content || {})
}
});
await createFile(filename, JSON.stringify({
functions,
packages
}, undefined, 4));
}
/**
@ -98,7 +143,10 @@ export async function writeDefaultConfig(
* @param {string} filename
*/
export async function loadPackages(loader: INopePackageLoader,filename: string = join(resolve(process.cwd()), 'config', 'assembly.json')) {
let data: IConfigFile[] = [];
let data: IConfigFile = {
functions: [],
packages: []
};
try {
/** Load the File and Parse it. */
@ -126,7 +174,7 @@ export async function loadPackages(loader: INopePackageLoader,filename: string =
// Scan for the Package-Files
// And iterate over them.
for (const item of data) {
for (const item of data.packages) {
// Now Try to load a Package, to test whether is is an assembly.
try {
const loadedPackage = (await import(resolve(item.path))).DESCRIPTION as IPackageDescription<any>;
@ -149,4 +197,49 @@ export async function loadPackages(loader: INopePackageLoader,filename: string =
// Generate the instances.
await loader.generateInstances()
}
export async function loadFunctions(filename: string = join(resolve(process.cwd()), 'config', 'assembly.json')){
let data: IConfigFile = {
functions: [],
packages: []
};
try {
/** Load the File and Parse it. */
data = JSON.parse(
await readFile(filename,{encoding: 'utf8'})
);
} catch (e) {
// Generate the Default File
await writeDefaultConfig(filename);
// Show an Hint
getNopeLogger('helper-load-packages').warn('No configuration was present. Created a new config file in ' + filename);
// Readin the newly created Data.
data = JSON.parse(await readFile(
filename, {
encoding:'utf8'
})
);
}
// Define the Return Array.
const functionPackages = new Array<any>();
// Scan for the Package-Files
// And iterate over them.
for (const item of data.functions) {
// Now Try to load a Package, to test whether is is an assembly.
try {
const loadedFunction = (await import(resolve(item.path))).DESCRIPTION as IPackageDescription<any>;
functionPackages.push(loadedFunction);
} catch (e) {
getNopeLogger('helper-load-packages').error('Failed Loading function-file at ' + item.path);
}
}
return functionPackages;
}

View File

@ -2,7 +2,7 @@
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2018-07-01 09:10:35
* @modify date 2020-11-07 01:15:42
* @modify date 2020-11-12 13:17:43
* @desc [description]
*/
@ -444,6 +444,8 @@ export class NopePackageLoader implements INopePackageLoader {
throw Error('Already loaded a Package with the name "' + element.nameOfPackage +'" !');
}
this._logger.info('loading package ' + element.nameOfPackage);
// Store the Package
this.packages[element.nameOfPackage] = element;

View File

@ -5,6 +5,7 @@ import { SPLITCHAR } from "../helpers/objectMethods";
* Contains the Valid Types of the Loger.
*/
export type LoggerLevel = 'error' | 'warn' | 'info' | 'http' | 'verbose' | 'debug' | 'silly';
export const LoggerLevels = ['error' , 'warn' , 'info' , 'http' , 'verbose' , 'debug' , 'silly']
const order: {[K in LoggerLevel]: number} = {
error: 0,

View File

@ -32,16 +32,16 @@ export function runMiddleware(app) {
new_req[i] = options[i];
}
} else {
new_req = createReq(path, options);
new_req = _createReq(path, options);
}
new_res = createRes(callback);
new_res = _createRes(callback);
app(new_req, new_res);
};
/* end - APP.runMiddleware*/
};
function createReq(path, options) {
function _createReq(path, options) {
if (!options) options = {};
var req = _.extend(
{
@ -58,7 +58,7 @@ function createReq(path, options) {
// req.connection=_req.connection
return req;
}
function createRes(callback) {
function _createRes(callback) {
var res: any = {
_removedHeader: {},
};

View File

@ -1,3 +1,11 @@
/**
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2020-11-12 11:21:41
* @modify date 2020-11-12 11:21:43
* @desc [description]
*/
import * as bodyParser from "body-parser";
import * as cors from 'cors';
import * as express from "express";
@ -7,8 +15,7 @@ import { assignIn } from 'lodash';
import { join } from "path";
import "reflect-metadata";
import { Logger } from 'winston';
import { IoSocketServer } from "../lib/communication/IoSocketServer";
import { getLinkedDispatcher } from "../lib/dispatcher/getLinkedDispatcher";
import { INopeDispatcher } from "../types/nope/nopeDispatcher.interface";
import { getBackendAccesors } from './getBackendAccessors';
/**
@ -35,9 +42,12 @@ function _genApiDoc(title: string, version: string, basePath: string, definition
/**
* Function to start a Open-API-Server
* @param _dispatcher The Dispatcher, which should be used.
* @param options Options for the Server
*/
export async function startBackend(options: {
export async function startOpenApiBackend(
_dispatcher: INopeDispatcher,
options: {
port?: number,
backendPort?: number,
basePath?: string,
@ -77,11 +87,7 @@ export async function startBackend(options: {
routesGlob: '**/*.{ts,js}',
routesIndexFileRegExp: /(?:index)?\.[tj]s$/,
dependencies: {
_dispatcher: getLinkedDispatcher(
{
communicator: new IoSocketServer(opts.backendPort, 'generic', 'generic')
}
)
_dispatcher
},
});

View File

@ -76,6 +76,9 @@ export async function parseModuleToOpenAPI(description: IParsableDescription, op
// 1. Specify the Mode (No Params = GET, else POST)
(method as any).mode = method.schema.inputs.length > 0 ? 'POST' : 'GET';
(method as any).required = method.schema.inputs.filter(param => !param.optional).map(param => param.name);
(method as any).hasInput = method.schema.inputs.length > 0;
// Now adapt the Schema of the Method.
// Iterate over the Inputs, add a Description if not provided
// And determine whehter the Parameters are required or not.
@ -83,8 +86,6 @@ export async function parseModuleToOpenAPI(description: IParsableDescription, op
// Provide a Description. (If not provided)
param.description = param.description || 'Not provided';
// Open API uses "required" instead of optional => invert
param.optional = !param.optional;
if (options.sharedDefinitions){
param.schema.definitions = undefined;
@ -111,6 +112,8 @@ export async function parseModuleToOpenAPI(description: IParsableDescription, op
(method as any).resultDescription = method.schema.outputs.description || 'Not Provided';
// And add a Schema for the Return type.
(method as any).parsedOutput = JSON.stringify(method.schema.outputs);
(method as any).parsedInput = JSON.stringify(_unifySchema(method.schema.inputs, options));
(method as any).hasInput = method.schema.inputs.length > 0;
(method as any).tag = description.name;
// Determine the Filename.
@ -134,7 +137,7 @@ export async function parseModuleToOpenAPI(description: IParsableDescription, op
for (const imp of imports){
const relativDir = relative(fileDir, imp.dir);
(method as any)[imp.name] = replaceAll(join(relativDir, imp.fileName), '\\', '/');
}
}
// Write down the Schema:
await createFile(
@ -192,8 +195,6 @@ export async function parseFunctionToOpenAPI(description: IFunctionOptions, opti
// Provide a Description. (If not provided)
param.description = param.description || 'Not provided';
// Open API uses "required" instead of optional => invert
param.optional = !param.optional;
if (options.sharedDefinitions){
param.schema.definitions = undefined;
@ -221,10 +222,14 @@ export async function parseFunctionToOpenAPI(description: IFunctionOptions, opti
// And add a Schema for the Return type.
(_description as any).parsedOutput = JSON.stringify(_description.schema.outputs);
(_description as any).tag = 'generic-services';
(_description as any).parsedInput = JSON.stringify(_unifySchema(_description.schema.inputs, options));
(_description as any).hasInput = _description.schema.inputs.length > 0;
(_description as any).required = _description.schema.inputs.filter(param => !param.optional).map(param => param.name);
(_description as any).name = _description.id;
// Determine the Filename.
const fileDir = join(options.outputDir, 'generic-services');
const fileName = join(fileDir, _description.id+'.ts');
const fileName = join(fileDir, _description.id + '.ts');
// Determine the Import Pathes.
const imports = [

View File

@ -34,16 +34,15 @@ export default function (_dispatcher: INopeDispatcher) {
{{#if methodDescription}}summary: '{{methodDescription}}',{{/if}}
{{#if operationId}}operationId: '{{operationId}}',{{/if}}
parameters: [
{{#each schema.inputs}}
{{#if hasInput}}
{
name: '{{name}}',
in: "body",
description: '{{description}}',
required: {{optional}},
schema: {{{parsedSchema}}}
}{{#unless @last}},{{/unless}}
{{/each}}
required: true,
schema: {{{parsedInput}}}
}
{{/if}}
],
responses: {
200: {

View File

@ -43,7 +43,17 @@ export default function (_dispatcher: INopeDispatcher) {
schema: {{{parsedSchema}}}
}{{#unless @last}},{{/unless}}
{{/each}}
{{/each}}
schema: {
type: 'object',
properties: {
{{#each schema.inputs}}
{{name}}: {{{parsedSchema}}}{{#unless @last}},{{/unless}}
{{/each}}
},
required: [{{#each required}}element{{#unless @last}},{{/unless}}{{/each}}]
}
],
responses: {
200: {

View File

@ -2,7 +2,7 @@
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2020-10-12 18:31:54
* @modify date 2020-11-06 23:17:07
* @modify date 2020-11-12 14:26:21
* @desc [description]
*/
@ -439,7 +439,10 @@ export type IResponseTaskMsg = {
*
* @type {*}
*/
error?: any
error?: {
error: any,
msg: string
}
}
export type IAvailableServicesMsg = {

View File

@ -2,11 +2,12 @@
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2020-11-10 16:53:43
* @modify date 2020-11-11 16:57:30
* @modify date 2020-11-12 14:29:57
* @desc [description]
*/
import { IPackageDescription } from "../../../lib/types/nope/nopePackage.interface";
import { startTask } from "./xetics.functions";
import { XeticsInterfaceClient } from "./xetics.module";
const TYPES = {
@ -22,7 +23,7 @@ export const DESCRIPTION: IPackageDescription<typeof TYPES> = {
identifier: "xetics-einlegen",
params: [
'Einlegen',
0
1
],
type: XeticsInterfaceClient.prototype.constructor.name.toString()
},
@ -33,7 +34,7 @@ export const DESCRIPTION: IPackageDescription<typeof TYPES> = {
identifier: "xetics-schrauben",
params: [
'Verschrauben',
0
1
],
type: XeticsInterfaceClient.prototype.constructor.name.toString()
},
@ -43,7 +44,7 @@ export const DESCRIPTION: IPackageDescription<typeof TYPES> = {
identifier: "xetics-qualitaetskontrolle",
params: [
'Qualitaetskontrolle',
0
1
],
type: XeticsInterfaceClient.prototype.constructor.name.toString()
},
@ -53,7 +54,7 @@ export const DESCRIPTION: IPackageDescription<typeof TYPES> = {
identifier: "xetics-verpacken",
params: [
'Verpacken',
0
1
],
type: XeticsInterfaceClient.prototype.constructor.name.toString()
},
@ -64,7 +65,7 @@ export const DESCRIPTION: IPackageDescription<typeof TYPES> = {
providedClasses: [
{
description: {
name: XeticsInterfaceClient.constructor.name,
name: XeticsInterfaceClient.prototype.constructor.name.toString(),
selector: TYPES.xeticsClient,
type: XeticsInterfaceClient
},
@ -73,7 +74,14 @@ export const DESCRIPTION: IPackageDescription<typeof TYPES> = {
}
}
],
providedFunctions: [],
providedFunctions: [
{
function: startTask,
options: {
id: 'startTask'
}
}
],
requiredPackages: [],
types: TYPES
}

View File

@ -12,5 +12,5 @@
},
"tempDir": "./temp/",
"configDir": "./config",
"modules": "./modules"
"modules": "./dist/modules"
}