How to intercept the creation of an entity in strapi? - javascript

I know that under the src/api folder are the entity folders each allowing 4 folders content-types, controllers, routes, and services. If I want to extend or change the behavior of an API then I intervene in the file under the controllers folder, possibly services as well. But this is only good for external calls.
For example if I want to do it when I use an external API I do the following under the foder /controllers => my-entity-name.js:
"use strict";
/**
* my-entity-name controller
*/
const { createCoreController } = require("#strapi/strapi").factories;
module.exports = createCoreController("api::my-entity-name.my-entity-name", ({ strapi }) => ({
async create(ctx) {
//Do my stuff here
//create manually the entity or super.create(ctx)
}
}))
I need to intercept the creation of an entity when the latter is done from the backoffice. Basically I need to invoke an external service before creating it. I noticed that it does not go through the same endpoint as the external API but it uses an internal one made like this: /content-manager/collection-types/api::my-entity.my-entity
What should I do to intercept it?

you have to do model lifecycle:
src/api/contenType/models/contentType/lifecycles.js
module.exports = {
async beforeCreate(event) {
…
}
}
If you need to stop creation due to error you can throw new ApplicationError(‘message’)
reference

Related

Where to initOracleClient in REST MVC Architecture

I have Express Routing set up with multiple routes, each using a different Oracle connection. I have to call initOracleClient prior to getConnection, however I get an error (Error: NJS-077: Oracle Client library has already been initialized) when I try to initOracleClient in both routes. I've tried moving the initOracleClient to different locations in the structure; both at the app level and route level. Where in a REST MVC structure do you initialize the client?
A REST MVC application typically has some supporting infrastructure. That is to say, MVC is not a complete blueprint on how to structure the entirety of your program's code - only a general rule of thumb of how to assign certain responsibilities.
The library you're using needs initialization, and apparently this code should execute only once. There are several ways to go about it:
Initialize the client once before starting up the express server, and then pass in the ready-to-use client for use by route handlers. This may be the easiest to use, but must necessarily delay the .listen() call - so the time until your application starts responding to HTTP may be longer.
Use a pattern known as the Singleton to allow route handlers to initialize the client, but only execute the initialization once under the hood. Depending on how exactly the library is initialized (does it return a Promise? does it use a callback?), this may require some careful design - for example, you may need to store and return a Promise instance, so multiple consumers will be calling .then() on the same Promise.
I implemented the Singleton pattern as suggested:
import oracledb from 'oracledb';
class PrivateOraInitSingleton {
constructor() {
try {
oracledb.initOracleClient({libDir: '/usr/local/lib/instantclient_19_8'});
} catch (err) {
console.error(err);
process.exit(1);
}
}
}
class OraInitSingleton {
constructor() {
throw new Error('Use OraInitSingleton.getInstance()');
}
static getInstance() {
if (!OraInitSingleton.instance) {
OraInitSingleton.instance = new PrivateOraInitSingleton();
}
return OraInitSingleton.instance;
}
}
export default OraInitSingleton;
Usage:
const object = OraInitSingleton.getInstance();
try {
connectionPromise = oracledb.getConnection({
user : process.env.DB_USER,
password : process.env.DB_PASSWORD,
connectString : process.env.CONNECT_STRING
});
} catch (err) {
console.error(err);
process.exit(1);
}

How to correctly make an entry point to my module, which is containing multiple classess?

I've started to develop a desktop app with node and electron. It has a package, which is implementing connection with some API. It is structured as one base class, and some derrived classes in this way:
ApiBase
ApiAuth extends ApiBase
ApiAuth.login()
ApiAuth.logout()
etc...
ApiTasks extends ApiBase
ApiTasks.getTaskList()
etc...
etc...
And now, i want to make nice and convinient way to use these classes in my app. So i need to create some entry point, which will provide an access to my API implementation. But, i do not have much expirience to make it right.
I thought about something like this:
index.js:
const ApiAuth = require('./amazing-time-api-auth');
const ApiTasks = require('./amazing-time-api-tasks');
apiAuth = new ApiAuth('www.sample-host.com');
apiTasks = new ApiTasks('www.sample-host.com');
module.exports = {
login: apiAuth.login,
logout: apiAuth.logout,
getTaskList: apiTasks.getTaskList,
etc...
}
somwhere at the app:
const api = require("./lib/someApi");
// need to get task list for some reason
api.getTaskList(param1, param2)
But there are some problems with this approach i managed:
it is a problem to pass host param to the constructors in index.js dynamicly
i am not sure if creating this instances everytime requiring index.js is a rigth thing
So i want to know about some approches i can use here, because i do now even know where to start research. Thank you.
I think that you identified some of the most crucial decisions with this:
it is a problem to pass host param to the constructors in index.js dynamicly
IMO Configuration and the interface are important considerations. Even though it can be refactored after the fact an easy to configure and consume interface will help reduce adoption of your library. As you pointed out the configuration is static right now and very brittle. Ie a change to the URL will cascade to all clients and require all clients to update.
A first intuitive alternative may be to allow dynamic configuration of the current structure:
apiAuth = new ApiAuth(process.env.API_AUTH_URL || 'www.sample-host.com');
apiTasks = new ApiTasks(process.env.API_TASKS_URL || 'www.sample-host.com');
While this allows client to dynamically configure the URL, the configuration is "implicit". IMO this is unintuitive and difficult to document. Also it's not explicit and requires a client to look in the code to see the environmental variables and instantiation flow.
I would favor exposing these classes to the client directly. I would consider this approach "explicit" as it forces the client to explicitly configure/instantiate your components. I think it's like providing your clients with primitives and allowing them to compose, build, and configure them in whatever way they want:
const ApiAuth = require('./amazing-time-api-auth');
const ApiTasks = require('./amazing-time-api-tasks');
module.exports = {
auth: ApiAuth,
tasks: ApiTasks
}
This automatically namespaces the api behind its functions (auth|tasks) AND requires that the client instantiatae the classes before using:
const api = require("./lib/someApi");
const auth = new api.auth(process.env.SOMETHING, 'some-url');
This pulls the configuration further out in the architecture. It forces the client to decide how it wants to get the URL and explicitly instantiate the library. What if one of your clients doesn't use login/logout? This may be more flexible in that case.
i am not sure if creating this instances everytime requiring index.js is a rigth thing
If instantiation should remain hidden, another alternative would be to provide a builder function in order to encapsulate it:
const ApiAuth = require('./amazing-time-api-auth');
const ApiTasks = require('./amazing-time-api-tasks');
apiAuth = new ApiAuth('www.sample-host.com');
apiTasks = new ApiTasks('www.sample-host.com');
module.exports = {
auth: {
build: (url) => {
return new ApiAuth(url);
}
},
tasks: {
build: (url) => {
return new ApiTasks(url);
}
}
}
This should still hide each class but still allows the client to decide how it configures each class:
const api = require("./lib/someApi");
const auth = api.auth.build('my-url');
auth.login();

How do I access an object from the main process from a render process [Electron]

I'm trying to create a tool for editing files containing a object that is related to my companies business logic. I'm using electron to do so.
I've created a javascript class which represents the object, handles its internals, and provides buisness functions on it:
class Annotation {
constructor() {
this._variables = []
this._resourceGenerators = []
}
get variables() {
return this._variables
}
get resourceGenerators() {
return this._resourceGenerators
}
save(path) {
...
}
static load(path) {
...
}
};
module.exports = Annotation;
I create the object in my main process, and I have an event handler which gives render processes access to it:
const {ipcMain} = require('electron')
const Annotation = require('./annotation.js');
... Do electron window stuff here ...
var annotation = new Annotation()
ipcMain.on('getAnnotation', (event, path) => {
event.returnValue = annotation
})
I've just found out that sending an object through ipcMain.sendSync uses JSON.stringify to pass the annotation, meaning it looses the getters/functions on it.
I'm fairly new to web/electron development; what is the proper way of handling this? Previously I had handlers in main for dealing with most of the functions that the render processes needed, but main started to become very bloated, so I'm trying to refactor it somewhat.
TL; DR: RECONSTRUCT OBJECT ON RECEIVER SIDE.
Description: Electron's main architectural design is based on multi-process, separating main (node.js) and each renderer (chromium) processes and allow to communicate between processes via IPC mechanism. And due to several reason (efficiency, performance, security, etcs) Electron's OOTO IPC only allows serializable POJO to be sent / received. Once receiver have those data, you may need reconstruct desired object from there.
If your intention around access is to share references like true singleton, that's not available.
The first thing I would suggest is that in most cases, you don't need to transfer anything to the main process. The main process is mostly for creating windows and accessing Electron API's which are restricted to the main process. Everything else should and can be done from the renderer including access to all node modules. You can write files, access databases, etc all from the renderer.
Read this article about the differences between the main and renderer processes and what you should be using each for.

Load an installable hook before native hooks

I am using sails-hook-sequelize to load sequelize as the ORM in my sails app. However, my controllers and policies setup (i.e. just creating their methods) is dependent on the models. I need to run the sails-hook-sequelize installable hook before I run controllers and policies hooks (currently it is running it after and the controllers/policies are failing to load). How can I do this? T
Thanks in advance.
Edit: Here is some code to illustrate what I am trying to accomplish:
UserController.js
let Endpoint = require('../classes/Endpoint');
let endpoint = new Endpoint(User);
Object.assign(endpoint, {
find
});
module.exports = endpoint;
function find(req, res, next) {
User.findAll(
{
where: req.query,
include: [
{
model: Privilege,
include: [
{
model: Account,
where: {
accountPkey: {
$in: AuthorizationService.accountsForPrivileges(req.tokenData.privileges, ['ADMINISTRATOR', 'OFFICE MANAGER'])
}
}
}
]
}
]
})
.then(users => res.ok(users))
.catch(err => res.serverError(err));
}
basically, I have a default Endpoint class that I instantiate and then add methods to. You can see that the Endpoint class takes a model argument. However, when this hook runs, the models don't exist yet because they are defined by a third party hook (using sequelize).
There's currently no way to run a third-party hook before the core hooks in Sails.
Often times when I see questions like this, it's from someone who's trying to create a Wordpress-like platform, and they're making the assumption that for every new "entity type" that an end-user creates (i.e. blog, article, comment) they need a new model. An alternative is to create an Entity model, with a contents attribute that is an association to a Content or EntityAttribute model which is more or less just a key/value store. Then you can use wildcard routes to have the EntityController actions load the correct type of entity.

expressjs conditional view render path

I'm writing an app using expressjs. I have my views in, commonly, /views folder. They cover 90% of my customers' needs, but sometimes I have to override one or another of those view to add custom-tailored features. I really wonder I can build a folder structure like:
*{ ...other expressjs files and folders...}*
/views
view1.jade
view2.jade
view2.jade
/customerA
view2.jade
/customerB
view3.jade
What I'd like is to override the behaviour of expressjs' response.render() function to apply the following algorithm:
1. a customer requests a view
2. if /{customer_folder}/{view_name}.jade exists, than
render /{customer_folder}/{view_name}.jade
else
render /views/{view_name}.jade
Thus, for customerA, response.render('view1') will refer to /views/view1.jade while response.render('view2') will refer to /customerA/view2.jade
(those who use appcelerator's titanium may sound it familiar)
I'd like an elegant way to implement this behavior without the hassle of modify expressjs' core functionality, and thus possibly get treated at upgrading my framework. I guess it's a common problem but I can't find any article on the web.
I would create a custom View class:
var express = require('express');
var app = express();
var View = app.get('view');
var MyView = function(name, options) {
View.call(this, name, options);
};
MyView.prototype = Object.create(View.prototype);
MyView.prototype.lookup = function(path) {
// `path` contains the template name to look up, so here you can perform
// your customer-specific lookups and change `path` so that it points to
// the correct file for the customer...
...
// when done, just call the original lookup method.
return View.prototype.lookup.call(this, path);
};
app.set('view', MyView);
You can hook http.ServerResponse.render.
Here's some code from the top of my head, to be used as middleware:
var backup = res.render
res.render = function() {
//Do your thing with the arguments array, maybe use environment variables
backup.apply(res, arguments) //Function.prototype.apply calls a function in context of argument 1, with argument 2 being the argument array for the actual call
}

Categories