How do I pass parameters through "use" method in CompoundJS? - node.js

I am using CompoundJS (an MVC for Express / NodeJS). To share code between controllers the Doc says that on controller1.js I can share a method by using publish method:
/***controller1.js***/
function sharedFunction () {...}
publish('sharedFunction', sharedFunction); //Sharing...
And in the controller2.js I can access it by loading it and trying the use method:
/***controller2.js***/
load('controller1'); // _controller siffix must be omitted
use('sharedFunction')
PROBLEM
This works great, however, I have a sharedFunctionthat has params:
/***controller1.js***/
function sharedFunction (param1, param2) {...}
publish('sharedFunction', sharedFunction); //Sharing...
I have been reading the Docs, however I can't find how, or the syntax, to add this params on my usemethod on controller1.js. Where do I send these params?:
/***controller2.js***/
load('controller1'); // _controller siffix must be omitted
use('sharedFunction(params here?)', {params here?}) //where do I send the params?
Thank you very much!

Here's what I've done:
var sharedFunction = use('sharedFunction');
sharedFunction(param1, param2);

Related

Validation Error: Using global entity manager instance methods for context specific actions is disallowed

Using MikroORM and getting this error:
ValidationError: Using global EntityManager instance methods for context specific actions is disallowed.
If you need to work with the global instance's identity map, use `allowGlobalContext` configuration option or `fork()` instead
The code that it corresponds to is below:
import { MikroORM } from "#mikro-orm/core";
import { __prod__ } from "./constants";
import { Post } from "./entities/Post";
import mikroConfig from "./mikro-orm.config";
const main = async () => {
const orm = await MikroORM.init(mikroConfig);
const post = orm.em.create(Post, {
title: "my first post",
});
await orm.em.persistAndFlush(post);
await orm.em.nativeInsert(Post, { title: "my first post 2" });
};
main().catch((error) => {
console.error(error);
});
I am unsure where I need to use the .fork() method
Don't disable validations without understanding them!
I can't believe what I see in the replies here. For anybody coming here, please don't disable the validation (either via MIKRO_ORM_ALLOW_GLOBAL_CONTEXT env var or via allowGlobalContext configuration). Disabling the validation is fine only under very specific circumstances, mainly in unit tests.
In case you don't know me, I am the one behind MikroORM, as well as the one who added this validation - for a very good reason, so please don't just disable that, it means you have a problem to solve, not that you should add one line to your configuration to shut it up.
This validation was added to MikroORM v5 (so not typeorm, please dont confuse those two), and it means exactly what it says - you are trying to work with the global context, while you should be working with request specific one. Consult the docs for why you need request context here: https://mikro-orm.io/docs/identity-map#why-is-request-context-needed. In general using single (global) context will result in instable API response and basically a one huge memory leak.
So now we should understand why the validation is there and why we should not disable it. Next how to get around it properly.
As others mentined (and as the validation error message mentioned too), we can create fork and use that instead:
const fork = orm.em.fork();
const res = await fork.find(...);
But that would be quite tedious, in real world apps, we usually have middlewares we can use to do this for us automatically. That is where the RequestContext helper comes into play. It uses the AsyncLocalStorage under the hood and is natively supported in the ORM.
Following text is mostly an extraction of the MikroORM docs.
How does RequestContext helper work?
Internally all EntityManager methods that work with the Identity Map (e.g. em.find() or em.getReference()) first call em.getContext() to access the contextual fork. This method will first check if we are running inside RequestContext handler and prefer the EntityManager fork from it.
// we call em.find() on the global EM instance
const res = await orm.em.find(Book, {});
// but under the hood this resolves to
const res = await orm.em.getContext().find(Book, {});
// which then resolves to
const res = await RequestContext.getEntityManager().find(Book, {});
The RequestContext.getEntityManager() method then checks AsyncLocalStorage static instance we use for creating new EM forks in the RequestContext.create() method.
The AsyncLocalStorage class from Node.js core is the magician here. It allows us to track the context throughout the async calls. It allows us to decouple the EntityManager fork creation (usually in a middleware as shown in previous section) from its usage through the global EntityManager instance.
Using RequestContext helper via middleware
If we use dependency injection container like inversify or the one in nestjs framework, it can be hard to achieve this, because we usually want to access our repositories via DI container, but it will always provide we with the same instance, rather than new one for each request.
To solve this, we can use RequestContext helper, that will use node's AsyncLocalStorage in the background to isolate the request context. MikroORM will always use request specific (forked) entity manager if available, so all we need to do is to create new request context preferably as a middleware:
app.use((req, res, next) => {
RequestContext.create(orm.em, next);
});
We should register this middleware as the last one just before request handlers and before any of our custom middleware that is using the ORM. There might be issues when we register it before request processing middleware like queryParser or bodyParser, so definitely register the context after them.
Later on we can then access the request scoped EntityManager via RequestContext.getEntityManager(). This method is used under the hood automatically, so we should not need it.
RequestContext.getEntityManager() will return undefined if the context was not started yet.
Simple usage without the helper
Now your example code from the OP is very basic, for that forking seems like the easiest thing to do, as its very bare bones, you dont have any web server there, so no middlewares:
const orm = await MikroORM.init(mikroConfig);
const emFork = orm.em.fork(); // <-- create the fork
const post = emFork.create(Post, { // <-- use the fork instead of global `orm.em`
title: "my first post",
});
await emFork.persistAndFlush(post); // <-- use the fork instead of global
await orm.em.nativeInsert(Post, { title: "my first post 2" }); // <-- this line could work with the global EM too, why? because `nativeInsert` is not touching the identity map = the context
But we can use the RequestContext here too, to demonstrate how it works:
const orm = await MikroORM.init(mikroConfig);
// run things in the `RequestContext` handler
await RequestContext.createAsync(orm.em, async () => {
// inside this handler the `orm.em` will actually use the contextual fork, created via `RequestContext.createAsync()`
const post = orm.em.create(Post, {
title: "my first post",
});
await orm.em.persistAndFlush(post);
await orm.em.nativeInsert(Post, { title: "my first post 2" });
});
The #UseRequestContext() decorator
Middlewares are executed only for regular HTTP request handlers, what if we need
a request scoped method outside that? One example of that is queue handlers or
scheduled tasks (e.g. CRON jobs).
We can use the #UseRequestContext() decorator. It requires us to first inject the
MikroORM instance to current context, it will be then used to create the context
for us. Under the hood, the decorator will register new request context for our
method and execute it inside the context.
This decorator will wrap the underlying method in RequestContext.createAsync() call. Every call to such method will create new context (new EntityManager fork) which will be used inside.
#UseRequestContext() should be used only on the top level methods. It should not be nested - a method decorated with it should not call another method that is also decorated with it.
#Injectable()
export class MyService {
constructor(private readonly orm: MikroORM) { }
#UseRequestContext()
async doSomething() {
// this will be executed in a separate context
}
}
Alternatively we can provide a callback that will return the MikroORM instance.
import { DI } from '..';
export class MyService {
#UseRequestContext(() => DI.orm)
async doSomething() {
// this will be executed in a separate context
}
}
Note that this is not a universal workaround, you should not blindly put the decorator everywhere - its actually the opposite, it should be used only for a very specific use case like CRON jobs, in other contexts where you can use middlewares this is not needed at all.
I faced a similar issue today when I upgraded the mikrorm setup from v4 to v5. After doing some RnD, I found the following changes helped me solve the mentioned error.
In the config object which is passed to the MikroORM.init call, pass the following property
allowGlobalContext: true
Don't directly use em to create database entry. Instead use the following code
const post = orm.em.fork({}).create(Post, {
title: "my first post",
});
The above changes should help you fix the error.
I am also very new to MikroORM. so, I am not sure why this error appears. But my uneducated guess is, they are restricting access to any changes to the global EntityManager em instance.
After doing some digging I found this solution:
yarn install dotenv
create a .env file in the root of the project
In your .env file paste the following:
MIKRO_ORM_ALLOW_GLOBAL_CONTEXT = true
Problem solved!

How to save the chat log locally and access in Bot framework NodeJS v4?

A logger is implemented as a middle-ware. Need to access the output of the logger through another dialog.
Look at the Botbuilder-Samples repo, the 17.multilingual-conversations sample. It demonstrates how you can interact with the dialog by receiving and sending activities based around the current context and inputs.
First, assign your middleware to the adapter in the index.js file:
const { LoggerMiddleware } = require('./logger-middleware');
adapter.use(new LoggerMiddleware (parameter_1, parameter_2));
Like the translator-middleware.js file, you will want to pass any necessary parameters thru the constructor of your middleware file:
constructor(parameter_1, parameter_2) {
this.parameter_1 = parameter_1;
this.parameter_2 = parameter_2;
}
After which, you create the onTurn method (and any associated methods), passing in the context and utilizing the class constructor parameters you need. Here you can create new dialogs that make use of the passed in logged data.
async onTurn(turnContext, next) {
let loggerText = this.parameter_1;
[...do stuff with <loggerText> data...]
await next();
}
In many respects, the middleware looks and functions like your main bot.js file. It is simply called at a different point in the process.
Hope of help.

How to stub SOAP client request with sinon in Node JS?

I am using strong-soap module to get data from SOAP request.
var soap = require('strong-soap').soap;
soap.createClient(url, options, function (err, client) {
var method = client.GetInfoSOAP;
method(requestQuery, function (err, info) {
// bla bla
}
}
I am getting the required data. Now
I want to write unit test case to mock the SOAP request using sinon stub, but didn't get any success. Any help would be appreciated.
What you want is controlling the soap object's createClient. You can do that using techniques that fall into one of two categories:
Dependency injection - expose a setter through which you can inject a fake module you control yourself for testing
Using link seams - hook into the import/require mechanism and override what the module is getting.
The Sinon project has a nice page on using link seams through proxyquire, and I have also detailed how to do DI on the issue tracker.
To achieve the first, all you need is to do something like this in the module:
module.exports._injectSoap = (fake) => soap = fake;
Then in your test code:
const fakeSoap = { createClient : sinon.stub().returns(/* ? */) }
myModule._injectSoap(fakeSoap);
...
assert(fakeSoap.createClient.calledOnce);
assert(...)
Hi i have solved my problem with the following code :
sinon.stub(soap, 'createClient').yields(null, {
GetInfoSOAP: function (request, cb) {
return cb(null, myDesiredData);
}
});

Sails JS new response in WebStorm

I'm newbie in Sails JS..
I'm trying to create a new custom response, I didn't find any auto-generator so I created it manually.
//someName.js
module.exports = function someName(data, options) {
console.log('test');
};
I'm trying to access this response from controller:
//someController.js
module.exports = {
someController: function(req, res) {
res.someName();
}
}
The problem is that WebStorm isn't recognize this response..
Unresolved function or method someName.
But when executing the app-it's working..(WebStorm recognize the default responses which came with the 'sails new someApp').
Thanks for your help!
The code in the Sails.js library that loads custom responses probably does something like this:
files = getFilesInApiResponsesDirectory()
files.forEach(function(file) {
name = extractName(file);
res[name] = require(file); // <-- too dynamic to infer at this point
})
There is no way that WebStorm code analyzer could infer the relationship between res and your custom response function without actually running Sails.js code or receiving a (less dynamic, more explicit) hint.
Anyway, the message you got does not necessarily represent a critical error, it's a "code inspection" you can disable or suppress.

Backbone.js/express.js parameters for model.save()

I'm using Backbone.js on the client and node.js on the backend, and I'm having a bit of trouble doing a 'limited' model save, as explained here : http://backbonejs.org/#Model-save
As in the example, if I do
book.save({author: "Teddy"});
how do I access the parameters of the save using express.js, i.e. the fact that I only want to save to the 'author' field? I've tried the following
req.body -> gives ALL parameters of the model being saved, I want only the 'author' field
req.query -> empty
Any help much appreciated!
As stated in Model.save documentation:
save model.save([attributes], [options])
[...] The attributes hash (as in set) should contain the attributes you'd like to change —
keys that aren't mentioned won't be altered — but, a complete
representation of the resource will be sent to the server.
You can however override the save method and provide a data attribute via the options which will be sent to the server instead of the full model representation. For example, this will only save the attributes passed to the method :
var M = Backbone.Model.extend({
save: function (attrs, options) {
options || (options = {});
options.contentType = 'application/json';
options.data = JSON.stringify(attrs);
Backbone.Model.prototype.save.call(this, attrs, options);
}
});
And a Fiddle http://jsfiddle.net/dLFgD/
As #mikebridge noted in the comments, this behavior can now be obtained by passing an attrs option. So either use
book.save(null, {
attrs: {author: "Teddy"}
});
or keep the override
var M = Backbone.Model.extend({
url: '/echo/json/',
save: function(attrs, options) {
options || (options = {});
options.attrs = attrs;
Backbone.Model.prototype.save.call(this, attrs, options);
}
});
http://jsfiddle.net/nikoshr/dLFgD/7/
You could also send a PATCH request if you're using a Backbone version that supports it (>=0.9.9) and your server understands that verb, as explained in #pkyeck's answer
with the current version of Backbone (1.1.0) you could also do a PATCH which only sends the changed attributes to the server.
If instead, you'd only like the changed attributes to be sent to the server, call model.save(attrs, {patch: true}). You'll get an HTTP PATCH request to the server with just the passed-in attributes.
taken from here: http://backbonejs.org/#Model-save
in your case this would be:
book.save("author", "Teddy", {patch: true});
The best way I have found to access POST parameters is to use the bodyParser middleware. This is referenced at this question: How to retrieve POST query parameters?
/* On startup, register the bodyParser middleware */
app.use(express.bodyParser());
/* Then, during route execution, each parameter can be accessed with req.param */
app.post('/author/:id', function(req, res) {
var author = req.param('author', null); // Second parameter is default.
...
});

Resources