Stackable functions - node.js

I am looking for a library agnostic way to "stack" functions. The paradigm's I am used to is "middleware", where something happens within a function errors can be thrown, and a context (or req) global is used to attach new properties or change existing ones. These ideas are found in libraries like express, or type-graphql.
I am looking for some agnostic way to chain middleware, not dependent on these type of libraries.
Here's an example of the kinds of functions I have.
I am struggling with some kind of clean way to author functions. The global approach is not complimentary to proper typing using typescript, and isn't very functional.
Where the more functional approach lacks this kind of "chainablity", where I can simply have an array of functions like below.
// logs the start of middleware
context.utility.log(debug, ids.onLoad),
// fetches user by email submitted
context.potentialUser.fetchByEmail(SignupOnSubmitArgs),
// throws error if the user is found
context.potentialUser.errorContextPropPresent,
// checks if passowrd and reenterPassword match
context.potentialUser.signupPassword(SignupOnSubmitArgs),
// creates the user
context.user.create(SignupOnSubmitArgs, ''),
// thows error if create failed in some way
context.user.errorContextPropAbsent,
// adds user id to session
context.utility.login,
// redirects user to dashboard
context.utility.redirect(Pages2.dashboardManage)
Is there any tools / libraries out there that will allow be to author clear and clean chain-able functions, and glue them together in a stackable way?

Returning this is usually the way for being able to chain methods. I made you an example showing both sync and async functions:
class ChainedOperations {
constructor(private value: number){}
public add(n: number): this {
this.value += n;
return this;
}
public subtract(n: number): this {
this.value -= n;
return this;
}
public async send(): Promise<this> {
console.log(`Sending ${this.value} somewhere`);
return this;
}
}
async function somewhereElse(): Promise<void> {
const firstChain = await new ChainedOperations(1).add(1).subtract(1).send();
await firstChain.add(1).subtract(2).send()
}
somewhereElse().catch(e => { throw new Error(e) });
For better dealing with async functions you can use pipe pattern where you chain but also wait for the final result and pass it to the next guy:
abstract class Pipable {
public pipe(...functions: Function[]) {
return (input: any) => functions.reduce((chain, func: any) => chain.then(func.bind(this)), Promise.resolve(input));
}
}
class AClass extends Pipable {
constructor(private value: number){
super();
}
public add(n: number): number {
this.value += n;
return this.value;
}
public subtract(n: number): number {
this.value -= n;
return this.value;
}
public async send(): Promise<number> {
console.log(`Sending ${this.value} somewhere`);
return this.value;
}
}
async function Something(){
const myClass = new AClass(2);
const composition = await myClass.pipe(myClass.add, myClass.subtract, myClass.send)(2);
}
Something();
Some people don't like to start from beginning but work their way backwards from the last function. If you want that just replace .reduce with .reduceRight. If you like fancy names, starting from last is called Composing as opposed to piping.

Related

Use global nest module in decorator

I have a global logger module in nest, that logs to a cloud logging service. I am trying to create a class method decorator that adds logging functionality. But I am struggling how to inject the service of a global nest module inside a decorator, since all dependency injection mechanisms I found in the docs depend are class or class property based injection.
export function logDecorator() {
// I would like to inject a LoggerService that is a provider of a global logger module
let logger = ???
return (target: any, propertyKey: string, propertyDescriptor: PropertyDescriptor) => {
//get original method
const originalMethod = propertyDescriptor.value;
//redefine descriptor value within own function block
propertyDescriptor.value = function(...args: any[]) {
logger.log(`${propertyKey} method called with args.`);
//attach original method implementation
const result = originalMethod.apply(this, args);
//log result of method
logger.log(`${propertyKey} method return value`);
};
};
}
UPDATE: Per reqest a simple example
Basic example would be to log calls to a service method using my custom logger (which in my case logs to a cloud service):
class MyService {
#logDecorator()
someMethod(name: string) {
// calls to this method as well as method return values would be logged to CloudWatch
return `Hello ${name}`
}
}
Another extended use case would be to catch some errors, then log them. I have a lot of this kind of logic that get reused across all my services.
Okay, found a solution. In case anyone else stumbles upon this. First please keep in mind how decorators work – they are class constructor based, not instance based.
In my case I wanted to have my logger service injected in the class instance. So the solution is to tell Nest in the decorator to inject the LoggerService into the instance of the class that contains the decorated method.
import { Inject } from '#nestjs/common';
import { LoggerService } from '../../logger/logger.service';
export function logErrorDecorator(bubble = true) {
const injectLogger = Inject(LoggerService);
return (target: any, propertyKey: string, propertyDescriptor: PropertyDescriptor) => {
injectLogger(target, 'logger'); // this is the same as using constructor(private readonly logger: LoggerService) in a class
//get original method
const originalMethod = propertyDescriptor.value;
//redefine descriptor value within own function block
propertyDescriptor.value = async function(...args: any[]) {
try {
return await originalMethod.apply(this, args);
} catch (error) {
const logger: LoggerService = this.logger;
logger.setContext(target.constructor.name);
logger.error(error.message, error.stack);
// rethrow error, so it can bubble up
if (bubble) {
throw error;
}
}
};
};
}
This gives the possibility to catch errors in a method, log them within the service context, and either re-throw them (so your controllers can handle user resp) or not. In my case I also had to implement some transaction-related logic here.
export class FoobarService implements OnModuleInit {
onModuleInit() {
this.test();
}
#logErrorDecorator()
test() {
throw new Error('Oh my');
}
}

Can I overload method in module.export in node.js?

I have an app.js with this code:
var addnote = (title,body) => { /* enter code here */ }
module.exports = {addnote};
Can I add another addnotes function with different parameters to that file?
Function overloading in JavaScript does not exist like in other programming languages such as C# and Java.
What you should be looking to do is pass an object as a parameter that has properties attached and filter them out there..
You could call different functions from your little 'mapping function' just implement the logic there if it isn't big (to keep the code clear).
function foo(parameters){
var title = parameters.title;
var body = parameters.body;
if(parameters.extraProperty){
// oh we have extraProperty passed in too, run a different function?
bar(title, body, parameters.extraProperty); // ??
}
}
foo({title: 'Title', body: 'Body', extraProperty: 'This is extra...'});
If this is your own custom module, you can use the concept of function overriding, where each child class can have its own way to handle something and also have a default way to do things.
class Parent {
constructor(name) {
this.name = name;
}
greet() {
console.log(`Hello ${this.name}`);
}
}
class Child1 extends Parent {
constructor(name) {
super(name);
}
greet() {
console.log(`Hey there ${this.name}. This is Child 1`);
}
}
class Child2 extends Parent {
constructor(name) {
super(name);
}
greet() {
console.log(`Hi there ${this.name}. This is Child 2`);
}
}
const o1 = new Child1('Foo')
const o2 = new Child2('Foo')
o1.greet();
o2.greet();
But if you are trying to override a function in an external module(You do not have access to that code, like a library), my suggestion is to create a wrapper and add functionality there.

Instantiate object after promisified import

I am struggling to instantiate object from dynamically imported classes. Basically I have some plugins which kinda look like this:
export interface IPlugin {
compile(logEvent: LogEventInfo): string;
}
export class DatePlugin implements IPlugin {
public compile(logEvent: LogEventInfo): string {
const date: Date = new Date();
return `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`;
}
}
In another file I want to dynamically crawl a folder, load all source files and instantiate them. I saw that import(...).then() can return a loaded object however in my case it returns the class and my object creation starts looking very ugly:
public async loadPlugins(): Promise<void> {
// ...
await Promise.all(pluginFiles.map(async (pluginFile: string): Promise<void> => {
const pluginFilePath: string = path.join(pluginsFolder, pluginFile);
import(pluginFilePath).then((plugin: any): void => {
const obj: IPlugin = (new plugin[Object.keys(plugin)[0]]() as IPlugin;
// ...
});
}));
}
Isn't there any better way to instantiate all those classes when loading them?
import() promises aren't chained, this is a mistake similar to this case that may result in problems with error handling and race conditions.
map shares a common potential problem with this case. It's used only to provide promises to wait for them, but not actual values. Since the purpose of async function call is to get class instance, it's reasonable to map pluginFile input to obj output value if it's supposed to be stored then - or compile result if it isn't:
public async loadPlugins(): Promise<...> {
const plugins = await Promise.all(pluginFiles.map(async (pluginFile: string): Promise<IPlugin> => {
const pluginFilePath: string = path.join(pluginsFolder, pluginFile);
const pluginExports = await import(pluginFilePath);
// preferably pluginExports.default export to not rely on keys order
const Plugin: { new(): IPlugin } = Object.values(pluginExports)[0];
return new Plugin();
}));
...
}
The only real benefit that import provides here is that it's future-proof, it can seamlessly be used natively in Node.js with third-party ES modules (.mjs) files. Since TypeScript is used any way and uses require for ES module imports under the hood, it may be reasonable to discard asynchronous routine and use require synchronously instead of import for dynamic imports:
public loadPlugins(): <...> {
const plugins = pluginFiles.map((pluginFile: string): IPlugin => {
const pluginFilePath: string = path.join(pluginsFolder, pluginFile);
const pluginExports = require(pluginFilePath);
// preferably pluginExports.default export to not rely on keys order
const Plugin: { new(): IPlugin } = Object.values(pluginExports)[0];
return new Plugin();
}));
...
}

With TypeScript: unable to refer to 'this' (class) from inside a function

I'm learning TypeScript and have the following class:
class DetailDriver {
public get driver() {
return super.getEntity();
}
public activate(): breeze.Promise {
var id = this.driver.id(); // this refers to (class) DetailDriver
return promise
.then(getCertificate)
.fail(somethingWrong);
function getCertificate() {
var id = this.driver.id(); // this refers to any
return ...
}
}
}
As you can see on the above code, the first call to this refers to my class DetailDriver. That's good. The second call to this (inside getCertificate) refers to any. That's not what I need. I need to refer to my class DetailDriver.
How to proceed?
Thanks.
Well,
According to section 4.9.2 of the TypeScript Language Specification you should use fat arrow syntax to preserve the scoping for this.
return promise
.then(() => return.this.id;)
.fail(somethingWrong);
Then the this keyword is properly determined to be a Driver.
For reference, you could also just do:
class SomeClass {
public someMethod() {
// Do something
}
public anotherMethod() {
var that = this; // Reference the class instance
function someFunction () {
that.someMethod();
}
}
}
You could refactor to something like this:
class DetailDriver {
public get driver() {
return super.getEntity();
}
public activate(): breeze.Promise {
var id = this.driver.id(); // this refers to (class) DetailDriver
return promise
.then(this.getCertificate.bind(this)) // <- important part
.fail(somethingWrong);
}
// new method function here
private getCertificate() {
var id = this.driver.id(); // this refers to any
return ...
}
}
Using the function keyword anywhere in your class will make any reference to this keyword refer to that function rather than the outer class. Generally, you want to avoid defining functions inside of classes, unless you use the "fat arrow" syntax. That would look like this:
class DetailDriver {
public get driver() {
return super.getEntity();
}
public activate(): breeze.Promise {
var id = this.driver.id(); // this refers to (class) DetailDriver
return promise
.then(() => { // <- important part
var id = this.driver.id(); // this refers to any
return ...
})
.fail(somethingWrong);
}
}

Pattern / architecture / anonymous methods

I am relatively new to C#, maybe you could help me with this.
I got a couple of methods callServiceXY(param1, param2, ...) that call a certain service. For many reasons these service calls can go wrong (and I don't really care for the reason in the end). So basically I need to always wrap them with something like this - to have them execute again if something goes wrong:
var i = 3;
while(i>0)
try{
call...()
} catch{
i--;
}
i=0;
}
I'd rather write this code only once. Could I somehow have a method like tryXtimes(int x, callService()) that allows me to execute an undefined or anonymous method? (I have Javascript in mind where this is possible...)?
Yes this is possible. C# 3.5 added support for Action and Func<T> types. An Action won't return any value, a Func will always return a value.
You have several different versions that also accept a number of parameters. The following Console Applications describes how you could do this:
using System;
namespace Stackoverflow
{
class Service
{
public int MyMethod() { return 42; }
public void MyMethod(string param1, bool param2) { }
public int MyMethod(object paramY) { return 42; }
}
class Program
{
static void ExecuteWithRetry(Action action)
{
try
{
action();
}
catch
{
action();
}
}
static T ExecuteWithRetry<T>(Func<T> function)
{
try
{
return function();
}
catch
{
return function();
}
}
static void Main(string[] args)
{
Service s = new Service();
ExecuteWithRetry(() => s.MyMethod("a", true));
int a = ExecuteWithRetry(() => s.MyMethod(1));
int b = ExecuteWithRetry(() => s.MyMethod(true));
}
}
}
As you can see, there are two overloads for ExecuteWithRetry. One returning void, one returning a type. You can call ExecuteWithRetry by passing an Action or a Func.
--> Edit: Awesome! Just a little extra code to complete the example:
With anonymous function/method:
ExecuteWithRetry(() =>
{
logger.Debug("test");
});
And with more parameters (action, int)
Method header:
public static void ExecuteWithRetryX(Action a, int x)
Method call:
ExecuteWithRetryX(() => { logger.Debug("test"); }, 2);
I would use the strategy/factory pattern(s) for this. This answer https://stackoverflow.com/a/13641801/626442 gives and example of the use of the strategy/factory pattern with links. The question at the above link will give you another type of example where this pattern can be adopted.
There are great examples of these design patterns here and the following are detailed intros to the Strategy pattern and the Factory pattern. The former of the last two links also shows you how to combine the two to do something like what you require.
I hope this helps.
Try following
void CallServiceXY(params object []objects)
{
Console.WriteLine("a");
throw new Exception("");
}
void Retry(int maxRetryCount, Action<object[]> action, params object[] obj)
{
int retryCount = 1;
while ( retryCount <= maxRetryCount)
{
try
{
action(obj);
return;
}
catch
{
retryCount++;
}
}
}
void Main()
{
Retry(2,CallServiceXY);
Retry(2,CallServiceXY,"");
Retry(2,CallServiceXY,"","");
}
Demo here
Trick is Action<object[]> that accepts object array and return void and params keyword in Retry method.
To return non void value, Change Action<object[]> to Func<T, object[]>.

Resources