How to handle validation scripts distributed in diferents files in NodeJS? - node.js

I have several modules that export a function that return true or false when the payload is valid or not. I also have a config file in JSON in which I specify the name of the validator script to use depending on the payload type:
[
{
"boardVersion": "1",
"availableInterfaces": [
{ "name": "digital", "validator": "digitalV1" },
{ "name": "analog", "validator": "analogV1" },
]
}
]
And for example inside digitalv1.js I have something like:
import validator from 'validator';
module.exports = (value) => {
validator.isInt(value, {min: 0, max: 3});
};
And finally, a controller that gets the payload from ExpressJS and depending on the endpoint called it decides what validator to use. The thing now is how can I load the validator in the controller.
There are 2 approaches that come to my mind:
In the controller, I require every validator and push them in a key-value array (or object), in which the key is the name of the validator and the value the validator itself.
Instead of defining a validator name in the JSON file I could just put the file path in the file system and just require the file when I need it.
I there a better/cleaner way to approach this? Feel free to suggest even a different architecture. The idea though is to keep validators separated for code cleanness sake.

You can use option 1, but with a "barrel module", which imports the validators, and attaches them onto an array or object. Then, any module that needs the validators can just import the main module, and use the provided lookup function, index, or key to get the validator they need.
Be careful of circular dependencies: if you have them, you can use module.exports.x = and Node will handle them correctly (Note: I'm not sure if this is necessary with ES6 modules, which handle circular dependencies a little differently; just something to be on guard for).
Since the knowledge to create this "barrel module" already exists in your JSON, you could watch your JSON file in your build process, and automatically generate this barrel module if any of the file associations change, throw an error if a specified module is not found at a certain file path, etc.

Related

Put data on ServersideProps

Is it possible to get data from server side's config file on client by getServersideProps() in Next.js? How to put data in props? Or how to get it on client in other way?
I have tried publicRuntimeConfig in next.config.js, but it is undefined and I don't know why...
It's hard to tell exactly what's going on but I have one idea from experience: You need to make sure you're calling nextJSApp.prepare() before any modules using next/config are included.
As an example,
// SomeComponent.tsx
import { getConfig } from 'next/config'
const config = getConfig()
export interface X { ... }
// server.ts
import { X } from './SomeComponent'
app.prepare().then(...)
This fails because module are loaded first and the config hasn't been initialized until app.prepare has been completed.
The solution for this is to either use TypeScript's import(...) syntax if you just need a type or use node's require for dynamic resolution during runtime.

Instantiate a class knowing the file path at runtime

How can I instantiate a class (with, say, a known empty constructor), for example:
at api/EmptyClass1.ts, I have:
export default class EmptyClass1 {
}
and, at api/EmptyClass2.ts, I have:
export default class EmptyClass2 {
}
I want this function:
function(filepath:string):any{
return Object.fromFile(filepath); //this line is mock code
}
to return a new instance of either EmptyClass1 or EmptyClass2, if the parameter filepath:string is "api/EmptyClass1.ts" or "api/EmptyClass2.ts", respectively.
The files defining the classes may not be known at the time the function is written may include any number of files. Consequently, using the import statement for each class, then using a switch, or if-then statements is not an acceptable solution.
The .ts files defining the classes are transcoded to javascript and reside in the application .build folder as .js files.
I am using typescript on node.js (recent versions).
Use require instead, and your problem will be solved. If the file may not exist, you can use optional-require if you want to have a fallback without using try/catch.
function fromFile(filepath:string):any{
// return Object.fromFile(filepath); //this line is mock code
return require(filepath);
}
Or just call require directly instead of wrapping it in another function.
Also check:
nodejs require inside TypeScript file

Take JSON response and transform it to pass around application

In my NestJS app, I'm making a REST request to a remote API I do not have control over. The REST API has a response containing JSON, a large object, most of which I do not need. Let's assume hypothetically that we have a JSON object that looks like the following:
{
"foo": [
1,
2,
3
],
"bar": {
"nested": {
"some_key": "some_val"
}
}
}
What if in this case, after I make a request to this API, I want to pass around only a subset of the above. Something like a NestedDto that would look like the following:
import { IsNotEmpty, IsString } from 'class-validator'
export class NestedDto {
#IsNotEmpty()
#IsString()
someKey: string
}
What is the best way for me to take the data that's being returned from the REST API and transform it into the above-using tools that NestJS offers? I want to be able to take responses from remote APIs and pass said data around inside of my NestJS app using my interface specifications.
All right, if you're just using Axios with no special configuration, you could do what you are already doping in Express by just mapping the response down to what you want. If you want to get a little fancy with it you could always implement some sort of class decorated with class-transformer decorators and use a plainToClass method mixed with the #Transform() decorator.
By the way, by default, NestJS provides an HttpModule that is a wrapper around Axios, but its responses are as RxJS Observables. If you decide to go with the observable route, you can use the observable operator map to do the mapping for you (you'll still have to provide mapping implementation though, like the plainToClass I mentioned above), but that is completely up to you.

define keystone_user from openstack/puppet-keystone via hiera?

I am using https://github.com/openstack/puppet-keystone to set up an OpenStack management/controller node. I need to add the 'glance' user to keystone. I want to try and do as much as I can in my hiera data so my manifest will be simple.
Here is my manifest:
class kilo2_keystone {
include controller_ceph
include keystone
include keystone::config
include keystone::user
# keystone_user { 'glance':
# ensure => present,
# }
}
The commented out section works, but I want to be able to do include keystone::user and supply the parameters in my hiera data like so:
keystone::user:
"%{hiera('glance_admin_user')}":
ensure: present
But when I run puppet agent -t on my node I get this error:
Could not find class ::keystone::user
The commented-out code declares a resource of type keystone_user, not a class. Presumably its type, keystone_user, is provided by the puppet-keystone module. The include() family of functions are for declaring classes, not resources, so they are inapplicable to keystone_user.
There is more than one way you could proceed. If you don't anticipate wanting to anything more complicated than declaring one or more keystone_users present, then I'd recommend giving your class a parameter for the user name(s), to which you can assign a value via Hiera:
class kilo2_keystone($usernames = []) {
include controller_ceph
include keystone
include keystone::config
keystone_user { $usernames:
ensure => present,
}
}
On the other hand, if you want to be able to declare multiple users, each with its own set of attributes, then the create_resources() function is probably the path of least resistance. You still want to parameterize your class so that it gets the data from Hiera via automated data binding, but now you want the data to be structured differently, as described in the create_resources() docs: as a hash mapping resource titles (usernames, in your case) to inner hashes of resource parameters to corresponding values.
For example, your class might look like this:
class kilo2_keystone($userdata = {}) {
include controller_ceph
include keystone
include keystone::config
create_resources('keystone_user', $userdata)
}
The corresponding data for this class might look like this:
kilo2_keystone::userdata:
glance:
ensure: present
enabled: true
another_user:
ensure: absent
Note also that you are placing your kilo2_keystone class in the top scope. You really ought to put it in a module and assign it to that module's namespace. The latter would look like this:
class mymodule::kilo2_keystone($userdata = {}) {
# ...
}

how to pass a shared variable to downstream modules?

I have a node toplevel myapp variable that contains some key application state - loggers, db handlers and some other data. The modules downstream in directory hierarchy need access to these data. How can I set up a key/value system in node to do that?
A highly upticked and accepted answer in Express: How to pass app-instance to routes from a different file? suggests using, in a lower level module
//in routes/index.js
var app = require("../app");
But this injects a hard-coded knowledge of the directory structure and file names which should be a bigger no-no jimho. Is there some other method, like something native in JavaScript? Nor do I relish the idea of declaring variables without var.
What is the node way of making a value available to objects created in lower scopes? (I am very much new to node and all-things-node aren't yet obvious to me)
Thanks a lot.
Since using node global (docs here) seems to be the solution that OP used, thought I'd add it as an official answer to collect my valuable points.
I strongly suggest that you namespace your variables, so something like
global.myApp.logger = { info here }
global.myApp.db = {
url: 'mongodb://localhost:27017/test',
connectOptions : {}
}
If you are in app.js and just want to allow access to it
global.myApp = this;
As always, use globals with care...
This is not really related to node but rather general software architecture decisions.
When you have a client and a server module/packages/classes (call them whichever way you like) one way is to define routines on the server module that takes as arguments whichever state data your client keeps on the 'global' scope, completes its tasks and reports back to the client with results.
This way, it is perfectly decoupled and you have a strict control of what data goes where.
Hope this helps :)
One way to do this is in an anonymous function - i.e. instead of returning an object with module.exports, return a function that returns an appropriate value.
So, let's say we want to pass var1 down to our two modules, ./module1.js and ./module2.js. This is how the module code would look:
module.exports = function(var1) {
return {
doSomething: function() { return var1; }
};
}
Then, we can call it like so:
var downstream = require('./module1')('This is var1');
Giving you exactly what you want.
I just created an empty module and installed it under node_modules as appglobals.js
// index.js
module.exports = {};
// package.json too is barebones
{ "name": "appGlobals" }
And then strut it around as without fearing refactoring in future:
var g = require("appglobals");
g.foo = "bar";
I wish it came built in as setter/getter, but the flexibility has to be admired.
(Now I only need to figure out how to package it for production)

Resources