adding new files in 1.4.6

This commit is contained in:
Martin Karkowski 2022-11-06 11:44:49 +01:00
parent 97a6031d32
commit bdd257545b
9 changed files with 621 additions and 29 deletions

View File

@ -26,7 +26,7 @@ export type callable<T> = {
export function exportAsNopeService<T>(
func: T,
options: IexportAsNopeServiceParameters
) {
): T & { options: IexportAsNopeServiceParameters } {
// Only add the element if it doesnt exists.
if (!CONTAINER.services.has(options.id)) {
CONTAINER.services.set(options.id, {
@ -37,5 +37,8 @@ export function exportAsNopeService<T>(
uri: options.id || (func as any).name,
});
}
return func;
(func as any).options = options;
return func as any;
}

View File

@ -73,11 +73,24 @@ export async function createPath(path: string) {
return path;
}
export async function deletePath(_dir_path: string): Promise<void> {
if (!(await exists(_dir_path))) {
/**
* Deletes the complete Path recursevly.
* > *WARNING: * Deletes Everything in the Folder.
*
* Example:
* `deletePath('C:\\Test');`
*
* This deletes all Files and Subfolders in the given Path.
* Furthermore the Folder Test itself is removed.
*
* @export
* @param {string} dir_path
*/
export async function deletePath(dir_path: string): Promise<void> {
if (!(await exists(dir_path))) {
throw new URIError("path doesnt exits.");
}
const _totalPath = _dir_path;
const _totalPath = dir_path;
/** Sort the Pathes according to their length. For instance:
* _pathes = ['C:\\Test\\Sub', 'C:\\Test\\Sub\\SubSub']
*
@ -104,7 +117,7 @@ export async function deletePath(_dir_path: string): Promise<void> {
await rmdir(_path);
}
await rmdir(_dir_path);
await rmdir(dir_path);
}
/**

28
lib/helpers/hash.ts Normal file
View File

@ -0,0 +1,28 @@
/**
* @author M.Karkowski
* @email M.Karkowski@zema.de
*/
import { stringifyWithFunctions } from "./jsonMethods";
/**
* Function to generate a Hash
* @param obj the Object, that should be hashed
*/
export function generateHash(obj: any) {
// Convert the object to String
const str = typeof obj === "string" ? obj : stringifyWithFunctions(obj);
// Define Vars.
let hash = 0,
i,
chr;
if (str.length === 0) return hash.toString();
for (i = 0; i < str.length; i++) {
chr = str.charCodeAt(i);
hash = (hash << 5) - hash + chr;
hash |= 0; // Convert to 32bit integer
}
return hash.toString();
}

View File

@ -8,34 +8,41 @@ import * as async from "./async";
import * as descriptors from "./descriptors";
import * as functions from "./functionMethods";
import * as subject from "./getSubject";
import * as hashs from "./hash";
import * as ids from "./idMethods";
import * as json from "./jsonMethods";
import * as schema from "./jsonSchemaMethods";
import * as lazy from "./lazyMethods";
import * as limit from "./limit";
import * as lists from "./lists";
import * as objects from "./objectMethods";
import * as pathes from "./pathMatchingMethods";
import * as runtime from "./runtimeMethods";
import * as sets from "./setMethods";
import * as singletons from "./singletonMethod";
import * as strings from "./stringMethods";
import * as taskQueues from "./taskQueue";
export * from "./arrayMethods";
export * from "./async";
export * from "./descriptors";
export * from "./functionMethods";
export * from "./getSubject";
export * from "./hash";
export * from "./idMethods";
export * from "./jsonMethods";
export * from "./jsonSchemaMethods";
export * from "./lazyMethods";
export * from "./limit";
export * from "./lists";
export * from "./objectMethods";
export * from "./pathMatchingMethods";
export * from "./runtimeMethods";
export * from "./setMethods";
export * from "./singletonMethod";
export * from "./stringMethods";
export * from "./taskQueue";
export {
async,
arrays,
@ -53,4 +60,7 @@ export {
descriptors,
functions,
limit,
hashs,
taskQueues,
lists,
};

259
lib/helpers/lists.ts Normal file
View File

@ -0,0 +1,259 @@
import { dynamicSort, extractListElement } from "./arrayMethods";
/**
* A Priority List. All Items are sorted by a Priority Number.
*
* @export
* @class PriorityList
*/
export class PriorityList<T> {
private _priority_list = new Array<{ priority: number; data: T }>();
private _list = new Array<T>();
private _updated = false;
/**
* Function to returns a sorted List containing only the Value
*
* @returns {Array<T>} Sorted List containing the Values.
* @memberof PriorityList
*/
public list(): Array<T> {
return extractListElement(this._priority_list, "data");
}
protected _sort(): void {
// Sort the List based on the element priority
this._priority_list.sort(dynamicSort("priority", true));
// Adapt the _list element :
this._list = extractListElement(this._priority_list, "data");
this._updated = true;
}
/**
* Adds Data to the Priority List
* @param _priority lower => lower priority
* @param _data data which are stored
*/
public push(_priority: number, _data: T): void {
// Add the Element with the given priority to the list
this._updated = false;
this._priority_list.push({ priority: _priority, data: _data });
}
/**
* Returns the Element with the lowest priority
*
* @param {boolean} [remove=true] Flag to remove the item. Defaults to true. Otherwise it remains in the list.
* @return {(T | null)}
* @memberof PriorityList
*/
public highest(remove = true): T | null {
if (!this._updated) {
this._sort();
}
const _ret = this._priority_list[remove ? "splice" : "slice"](0, 1)[0];
return _ret ? _ret.data : null;
}
/**
* Returns the Element with the highest priority
* @param {boolean} [remove=true] Flag to remove the item. Defaults to true. Otherwise it remains in the list.
* @return {(T | null)}
* @memberof PriorityList
*/
public lowest(remove = true): T | null {
if (!this._updated) {
this._sort();
}
let _ret: { priority: number; data: T } | undefined = undefined;
if (remove) {
_ret = this._priority_list.pop();
} else {
_ret = this._priority_list[this._list.length - 1];
}
return _ret ? _ret.data : null;
}
/**
* Returns the Length of the Priority list
*
* @readonly
* @type {number}
* @memberof PriorityList
*/
public get length(): number {
return this._priority_list.length;
}
}
/**
* Limited List. This list at max contains a specific amount of elements.
* After the max number of elements has been added, the first element added
* will be removed.
*/
export class LimitedList<T> {
/**
* Element containing the list
*
* @private
* @type {Array<T>}
* @memberof LimitedList
*/
private _list: Array<T>;
/**
* Internal Pointer, showing the actual item.
*
* @private
* @type {number}
* @memberof LimitedList
*/
private _pointer: number;
constructor(public maxLength: number) {
this._pointer = -1;
this._list = new Array<T>();
}
/**
* Adds Data to the Stack. The Pointer is getting adapted.
*
* @param {T} data
* @returns
* @memberof LimitedList
*/
push(data: T) {
// Check if the Maximum length is achieved
if (this._list.length >= this.maxLength) {
// Remove the First Element
this._list = this._list.slice(1, this._pointer + 1);
}
// Store the Content
const ret = this._list.push(data);
// Adapt the Pointer
this._pointer = this._list.length - 1;
return ret;
}
/**
* Contains the Length of the list.
*
* @readonly
* @memberof LimitedList
*/
public get length() {
return this._list.length;
}
/**
* Gets the current pointer.
*
* @readonly
* @memberof LimitedList
*/
public get currentPointer() {
return this._pointer;
}
last(): T | null {
if (this._list.length > 0) {
this._pointer = this._list.length - 1;
return this._list[this._pointer];
}
// No data available.
return null;
}
/**
* Returns the Pointer to the first item.
* @returns
*/
first(): T | null {
this._pointer = this._list.length - 1;
if (this._pointer >= 0 && this._pointer < this._list.length) {
return this._list[this._pointer];
}
// No data available.
return null;
}
/**
* Returns the last item. Adapts the pointer and the
* current item is the last item.
* example:
* l = limited.last()
* c = limited.current()
*
* l == c -> True
* @returns The last element.
*/
previous(): T | null {
// Check if the Pointer is in the defined Range
if (this._pointer - 1 >= 0 && this._pointer - 1 < this._list.length) {
return this._list[--this._pointer];
}
// No data available.
return null;
}
/**
* Returns the current item, the pointer is showing at.
* @returns
*/
current(): T | null {
// Check if the Pointer is in the defined Range
if (this._pointer >= 0 && this._pointer < this._list.length) {
return this._list[this._pointer];
}
/** No data available any more */
return null;
}
next(): T | null {
/** Check if the Pointer is in the defined Range */
if (this._pointer + 1 >= 0 && this._pointer + 1 < this._list.length) {
return this._list[++this._pointer];
}
/** No data available any more */
return null;
}
/**
* Pops the last element. If there is no element undefined is returned.
* @returns The last element.
*/
pop(current = false): T {
if (current) {
const ret = this._list.splice(this._pointer, 1)[0];
return ret;
}
const ret = this._list.pop();
// Adapt the Pointer
this._pointer = this._list.length - 1;
return ret;
}
/**
* Helper to iterate over all items.
* @param callbackFn
* @param thisArg
*/
public forEach(
callbackFn: (item: T, index: number, array: Array<T>) => void,
thisArg?: any
) {
this._list.forEach(callbackFn, thisArg);
}
}

View File

@ -0,0 +1,132 @@
/**
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @desc [description]
*/
import { sleep } from "./async";
import { assert, expect } from "chai";
import { describe, it } from "mocha";
import { PriorityTaskQueue } from "./taskQueue";
describe("PriorityTaskQueue", function () {
// Describe the required Test:
describe("Async Functions", function () {
it("no parallel execution - no priority", async function () {
let called: string[] = [];
async function delayed(ret: string) {
await sleep(25);
called.push(ret);
return ret;
}
const queue = new PriorityTaskQueue();
queue.maxParallel = 1;
queue.usePriority = false;
const promises = [
queue.execute(delayed, ["first"], 5),
queue.execute(delayed, ["second"], 10),
];
const start = Date.now();
await Promise.all(promises);
const diff = Date.now() - start;
assert(diff > 40, "Functions should be called after each other");
assert(called[0] == "first", "First should be the first entry");
assert(called[1] == "second", "First should be the first entry");
});
it("parallel execution - no priority", async function () {
let called: string[] = [];
async function delayed(ret: string) {
await sleep(25);
called.push(ret);
return ret;
}
const queue = new PriorityTaskQueue();
queue.maxParallel = 10;
queue.usePriority = false;
const promises = [
queue.execute(delayed, ["first"], 5),
queue.execute(delayed, ["second"], 10),
];
const start = Date.now();
await Promise.all(promises);
const diff = Date.now() - start;
assert(diff < 40, "Functions should be called parallel");
assert(called[0] == "first", "First should be the first entry");
assert(called[1] == "second", "First should be the first entry");
});
it("no parallel execution - with priority", async function () {
let called: string[] = [];
async function delayed(ret: string) {
await sleep(25);
called.push(ret);
return ret;
}
const queue = new PriorityTaskQueue();
queue.maxParallel = 1;
queue.usePriority = true;
const promises = [
queue.execute(delayed, ["first"], 5),
queue.execute(delayed, ["second"], 10),
queue.execute(delayed, ["third"], 15),
];
const start = Date.now();
await Promise.all(promises);
const diff = Date.now() - start;
assert(diff > 40, "Functions should be called after each other");
assert(called[1] == "third", "second should be the third entry");
assert(called[2] == "second", "third should be the second entry");
});
it("parallel execution - with priority", async function () {
let called: string[] = [];
async function delayed(ret: string) {
await sleep(25);
called.push(ret);
return ret;
}
const queue = new PriorityTaskQueue();
queue.maxParallel = 10;
queue.usePriority = true;
const promises = [
queue.execute(delayed, ["first"], 5),
queue.execute(delayed, ["second"], 10),
queue.execute(delayed, ["third"], 15),
];
const start = Date.now();
await Promise.all(promises);
const diff = Date.now() - start;
assert(diff < 40, "Functions should be called parallel");
assert(called[0] == "first", "First should be the first entry");
assert(called[1] == "second", "second should be the second entry");
assert(called[2] == "third", "third should be the third entry");
});
});
});

155
lib/helpers/taskQueue.ts Normal file
View File

@ -0,0 +1,155 @@
import { NopePromise } from "../promise";
import { isAsyncFunction } from "./async";
import { PriorityList } from "./lists";
/**
* A Task-Queue. This could be used to make parallel
* Request run sequentially. For Instance during
* Saving and Reading Vars to achive a consistent set
* of Data.
*
* Usage:
* // Create a Queue
* const _queue = new PriorityTaskQueue();
* // Create a Function
* const _func = (_input: string, _cb) => {
* console.log("Hallo ", _input)
* _cb(null, null);
* };
*
* await _queue.execute(_func, 'Welt');
*
* @export
* @class TaskQeue
*/
export class PriorityTaskQueue {
protected _queue = new PriorityList<{
func: (...args) => void;
cancel: () => void;
args: any;
resolve: (data) => void;
reject: (err) => void;
}>();
protected _runningTasks = 0;
protected _counter = 0;
public maxParallel = 1;
public usePriority = true;
/**
* Executes the given Task. If now Task is running it is executed immediatelly,
* otherwise it is pushed in the queue and call if the other tasks are call.
*
* @param {any} _func The Function which should be called.
* @param {any} _param The Data which should be used for the call.
* @param {any} _callback The Callback, which should be called after
* @memberof TaskQeue
*/
public execute<T>(
func: (...args) => T | Promise<T>,
args: any[],
priority: number = 0,
cancel: () => void = () => {}
): NopePromise<T> {
let resolve, reject;
const promise = new NopePromise<T>((res, rej) => {
resolve = res;
reject = rej;
});
promise.cancel = cancel;
// Check whether the Execution is activ:
if (
this._runningTasks < this.maxParallel &&
this._queue.length < this.maxParallel
) {
this._runningTasks++;
this._execute({
func,
args,
cancel,
resolve,
reject,
});
} else {
// Extend the Queue.
this._queue.push(this.usePriority ? priority : this._counter++, {
func,
args,
cancel,
resolve,
reject,
});
}
return promise;
}
protected _execute(data: {
func: (...args) => any | Promise<any>;
args: any[];
cancel: () => void;
resolve: (data) => void;
reject: (err) => void;
}) {
// Verify whether there is an CancelHandler, if yes.
// Register at the Cancel-Handler. Thereby the next
// function is call if the currently running Task is
// aborted.
if (data.cancel) {
data.args.push(() => {
data.cancel();
data.reject(Error("Canceled"));
this._finish();
});
}
if (isAsyncFunction(data.func)) {
(data.func as (...args: any[]) => Promise<any>)(...data.args)
.then((res) => {
data.resolve(res);
this._finish();
})
.catch((err) => {
data.reject(err);
this._finish();
});
} else {
try {
const res = (data.func as (...args: any[]) => any)(...data.args);
data.resolve(res);
} catch (err) {
data.reject(err);
}
this._finish();
}
}
/**
* Internal Function to Finish all Tasks.
*
* @protected
* @memberof PriorityTaskQueue
*/
protected _finish() {
// Remove one Element.
this._runningTasks--;
if (this._runningTasks < 0) {
this._runningTasks = 0;
}
// Remove the First Task
const task = this._queue.highest();
// Call the Function with the adapted Callback, if there is a Task Left open.
if (task) {
this._execute(task);
}
}
public get length(): number {
return this._queue.length;
}
}

View File

@ -183,19 +183,7 @@ export interface IUiDefinition {
*/
ui?: IClassDescription["ui"];
/**
* Name of the Package
*/
package: string;
/**
* Path of the defintio file.
*/
path?: string;
/**
* Class identifier
*/
class: string;
/**
* The Methods of the class
* Definition of the UI.
*/
methods: {
[index: string]: IServiceOptions["ui"];

View File

@ -46,13 +46,6 @@ export async function writeUiFile(
// Iterate over the classes.
for (const cls of item.package.providedClasses) {
const itemToAdd: IUiDefinition["classes"][0] = {
// The Class Name
class: cls.description.name,
// The Package Name
package: item.package.nameOfPackage,
// The Path of he File.
path: item.path,
// The defined UI defintions.
ui: cls.ui,
// Define the Methods elements
methods: {},
@ -74,7 +67,12 @@ export async function writeUiFile(
) {
// If an ui definition exists, we want
// to export it and store it in our file.
uiFile.classes[itemToAdd.class] = itemToAdd;
const ui = itemToAdd.ui || {};
uiFile.classes[cls.description.name] = {
...ui,
methods: itemToAdd.methods,
};
}
}
@ -302,7 +300,10 @@ export async function uploadUi(args: Partial<UploadArgs>) {
return result;
}
async function getContentOfNewestFile() {
async function getContentOfNewestFile(): Promise<{
functions: any;
classes: any;
}> {
// Get all Possible Files
const _files = await getFiles((item, scope) => {
return item.identifier === "ui-definition";
@ -330,7 +331,10 @@ export async function uploadUi(args: Partial<UploadArgs>) {
logger.error(e);
}
}
return {};
return {
functions: {},
classes: {},
};
}
logger.info(