CRUD blueprint overriding in sails.js - node.js

According to this closed issue in sails:
https://github.com/balderdashy/sails/issues/835
CRUD Blueprint Overrides
"absolutely, this is coming in v0.10"
I'd like to modify the blueprints in my sailsjs service to allow named roots (consuming in ember).
Currently I'm having to customize every controller I create with actions that are largely duplicates of what is already in the blueprints.
I suspect that I can move this code out of my controllers now and into a blueprints override area, but I'm not clear on where to put that code.
Any examples or even just a pointer to the relevant code in sails the .10 repo would be greatly appreciated.

Update
In order to override blueprints in Sails 1.0 in the manner described below, you must first install the "custom blueprints" plugin for your project (npm install sails-hook-custom-blueprints).
To override blueprints in Sails v0.10, you create an api/blueprints folder and add your blueprint files (e.g. find.js, create.js, etc.) within. You can take a look at the code for the default actions in the Sails blueprints hook for a head start.
Adding custom blueprints is also supported, but they currently do not get bound to routes automatically. If you create a /blueprints/foo.js file, you can bind a route to it in your /config/routes.js file with (for example):
'GET /myRoute': {blueprint: 'foo'}

you can add actions with these names inside your controller to override default behaviour
to change destroy behavior
module.exports = {
destroy: function(req,res){
Goal.update({ id: req.param('id') }, { deleted: true })
.exec(function (err, goal) {
if (err) return res.json(err, 400);
return res.json(goal[0]);
});
}
}

It is possible to use the build in blueprints, but with policies running first. These policies might verify that the user is logged in, has the correct access, or similar. Really handy!
On each model, you have available callbacks both before and after data has been stored. Dig in: http://sailsjs.com/documentation/concepts/models-and-orm/lifecycle-callbacks
There is no default callback available for blueprints result. But don't give up. It is still possible to use the build in blueprints, and only modify the output. It might not be the most elegant solution, but it works well. Check out my “hack” here: Sails blueprints lifecycle

Related

Clean Architecture - How to handle usecase dependencies

I am refactoring one of my older applications around to using the concept of use cases "clean architecture".
I am little confused on how to leverage the common data & entity validations
for e.g. There are 2 use cases
Allow admins to import a new workflow template
Allow admins to create new workflow template
The above use cases are called from the controllers.
In both the above cases, there are some common database level validations like:
Is there already a workflow with same name ?
To handle these validations, Do I make this as separate use-case like "checkIfWorkflowWithSameNameExists()" ?
If I make a separate use case, then what options are better to call these common validations
Can one use case call another use case directly
export function importNewWorkflowTemplate(specs){
const { workflowRepository } = specs;
const exists = checkIfWorkflowWithSameNameExists()
if(exists){
//return error
}
return new (payLoad) => {
//logic
}
}
Should I be injecting the dependent use cases
export function importNewWorkflowTemplate(specs){
const { workflowRepository, checkIfWorkflowWithSameNameExists } = specs;
return new (payLoad) => {
//logic
}
}
Should the validation belong to outer layer like the controller?
What you describe - checkIfWorkflowWithSameNameExists() - is not a use case.
This is simply a method put on a domain service, such as a repository. This could be a repository method on your workflow repository such as hasWorkflowWithName(name). The repository represents a collection of aggregates and thus knows best if there is one with the same name already.
If there is more complex domain logic to check for an existing repository and then either perform some error handling or performing specific logic to create the logic you can also consider to encapsulate these steps inside a domain service method. In this case the workflow repository interface would be injected into the workflow domain service which would then again be injected into the use cases.
With that you could use the same domain service in both use cases and the use case are responsible to orchestrate the translation between the external commands and the domain service interface and the corresponding domain entities.

Feathers JS nested Routing or creating alternate services

The project I'm working on uses the feathers JS framework server side. Many of the services have hooks (or middleware) that make other calls and attach data before sending back to the client. If I have a new feature that needs to query a database but for a only few specific things I'm thinking I don't want to use the already built out "find" method for this database query as that "find" method has many other unneeded hooks and calls to other databases to get data I do not need for this new query on my feature.
My two solutions so far:
I could use the standard "find" query and just write if statements in all hooks that check for a specific string parameter that can be passed in on client side so these hooks are deactivated on this specific call but that seems tedious especially if I find this need for several other different services that have already been built out.
I initialize a second service below my main service so if my main service is:
app.use('/comments', new JHService(options));
right underneath I write:
app.use('/comments/allParticipants', new JHService(options));
And then attach a whole new set of hooks for that service. Basically it's a whole new service with the only relation to the origin in that the first part of it's name is 'comments' Since I'm new to feathers I'm not sure if that is a performant or optimal solution.
Is there a better solution then those options? or is option 1 or option 2 the most correct way to solve my current issue?
You can always wrap the population hooks into a conditional hook:
const hooks = require('feathers-hooks-common');
app.service('myservice').after({
create: hooks.iff(hook => hook.params.populate !== false, populateEntries)
});
Now population will only run if params.populate is not false.

How to programmatically detach a model from a loopback application?

I am using loopback without the strongloop framework itself, meaning I have no access to any of the cli tools. I am able to succesfully create and launch a loopback server and define/load some models in this fashion:
var loopback = require('loopback');
var app = loopback();
var dataSource = app.dataSource
(
'db',
{
adapter : 'memory'
});
);
var UserModel = app.loopback.findModel('User');
UserModel.attachTo(dataSource);
app.model(UserModel);
/* ... other models loading / definitions */
// Expose API
app.use('/api', app.loopback.rest());
What I would like to achieve is to be able to detach a model from the loopback application at runtime, so it is not available from the rest API nor the loopback object anymore (without the need to restart the node script).
I know it is possible to remove a model definition made previously from the cli:
Destroy a model in loopback.io, but this is not valid in my case since what it does is to remove the json objects that are loaded at strongloop boot, which is not applicable here.
I would appreciate very much any help regarding this, I have found nothing in the strongloop API documentation.
Disclaimer: I am a core developer of LoopBack.
I am afraid there is no easy way for deleting models at runtime, we are tracking this request in issue #1590.
so it is not available from the rest API nor the loopback object anymore
Let's take a look at the REST API first. In order to remove your model from the REST API, you need to remove it from the list of "shared classes" maintained by strong-remoting and then clean the cached handler middleware.
delete app.remotes()._classes[modelName];
delete app.remotes()._typeRegistry._types[modelName];
delete app._handlers.rest;
When the next request comes in, LoopBack will create a new REST handler middleware and rebuild the routing table.
In essence, you need to undo the work done by this code.
In order to remove the model from LoopBack JavaScript APIs, you need to remove it from the list of models maintained by application's registry:
delete app.models[modelName];
delete app.models[classify(modelName)];
delete app.models[camelize(modelName)];
app.models.models.splice(app.models.indexOf(ModelCtor), 1);
(This is undoing the work done by this code).
Next, you need to remove it from loopback-datasource-juggler registries:
delete app.registry.modelBuilder.models[modelName];
Caveats:
I haven't run/tested this code, it may not work out of the box.
It does not handle the case where the removed model has relations with other models.
It does not notify loopback-component-explorer about the change in the API
Update
There's now a function called deleteModelByName that does exactly that.
https://apidocs.strongloop.com/loopback/#app-deletemodelbyname
https://github.com/strongloop/loopback/pull/3858/commits/0cd380c590be7a89d155e5792365d04f23c55851

Do I have to add loopback-component-passport's core models manually?

I installed loopback-component-passport so I can use it with strongloop's Loopback framework. I followed the docs,
but after this command: npm install loopback-component-passport, the following files/models are not created:
UserIdentity model
UserCredential model
ApplicationCredential model
These files are used by Loopback for third-party authentication. Do I have to add them manually? Or am I doing something wrong?
Yes, you have to add them manually. You can refer this example, loopback-example-passport on github.
The reason is the relationships of loopback-component-passport's core models i.e UserIdentity model, UserCredential and ApplicationCredential with Loopback's core model AccessToken and User doesn't come preconfigured which is required for authentication.
Also, you might notice that the developer extended the models but didn't added any model.js. This is so because there was no need to add any runtime logic. For starters, you might want to extend the core models as per the example and create relationship and acls only. Then you can proceed with custom logic.
I think loopback built in models stay tucked away in node_modules, so assuming you mean that these tables are missing from your datastore, you can do an autoupdate in order to get loopback to build any missing tables.
I add the following code to a file in the server/boot directory when I need to during development.
module.exports = function(app, done) {
app.datasources.mysql_db.autoupdate(function(err) {
if (err) throw err;
console.log("autoupdate");
done();
})
}
Where mysql_db is the name of your datastore.
This will update the datasource when the server boot but shouldn't drop existing data (saying that I have seen instances where foreign keys are lost, so care & db backup is still needed).

Can I or Should I use a Global variable in Angularjs to store a logged in user?

I'm new to angular and developing my first 'real' application. I'm trying to build a calendar/scheduling app ( source code can all be seen on github ) and I want to be able to change the content if there is a user logged in (i.e. display details relevant to them) but here's the catch:
I don't want the app to be dependent on having a logged in user ( needs to be something that can be configured to work publicly, privately or both)
I don't want to implement the user/login within this app if it can be avoided ( I want to eventually include my app in another app where this might be implemented but isn't necessarily implemented using any particular security frameworks or limited to any)
I had an idea of creating some global variable user that could be referenced through out my application, or if I had to implement a system to do it all in this app that I could do so in in some abstract way so that different options could be injected in.
some of my ideas or understanding of what I should be doing may be completely wrong and ignorant of fundamentals but I genuinely do not know what approach I should take to do this.
In case it is relevant I currently don't have any back-end but eventually hope use MongoDB for storage and nodejs for services but I also want to try keep it open-ended to allow others to use different storage/backends such as sql and php
is there away to have a global uservariable/service that I could inject/populate from another (parent?) app?
If so what would be the best approach to do so?
If Not, why and what approach should I take and why?
Update
I Believe from comments online and some suggestion made to me that a service would be the best option BUT How would I go about injecting from a parent application into this applications service?
If your (single) page is rendered dynamically by the server and the server knows if you are logged-in or not, then you could do the following:
Dynamically render a script tag that produces:
<script>
window.user = { id: 1234, name: 'User A', isLoggedIn: true };
</script>
For non logged-in users:
<script>
window.user = { isLoggedIn: false };
</script>
For convinience, copy user to a value inside angular's IOC:
angular.module('myApp').value('user', window.user);
Then, you can use it in DI:
angular.module('myApp').factory('myService', function(user) {
return {
doSomething: function() {
if (user.isLoggedIn) {
...
} else {
...
}
}
};
});
Something tricky (which you should thing twice before doing [SEE COMMENTS]) is extending the $scope:
angular.module('myApp').config(function($provide) {
$provide.decorator('$controller', function($delegate, user) {
return function(constructor, locals) {
locals.$scope._user = user;
return $delegate(constructor, locals);
};
});
});
This piece of code decorates the $controller service (responsible for contructing controllers) and basically says that $scope objects prior to being passed to controllers, will be enhanced with the _user property.
Having it automatically $scoped means that you can directly use it any view, anywhere:
<div ng-if="_user.isLoggedIn">Content only for logged-in users</div>
This is something risky since you may end up running into naming conflicts with the original $scope API or properties that you add in your controllers.
It goes without saying that these stuff run solely in the client and they can be easily tampered. Your server-side code should always check the user and return the correct data subset or accept the right actions.
Yes you can do it in $rootScope. However, I believe it's better practice to put it inside a service. Services are singletons meaning they maintain the same state throughout the application and as such are prefect for storing things like a user object. Using a "user" service instead of $rootScope is just better organization in my opinion. Although technically you can achieve the same results, generally speaking you don't want to over-populate your $rootScope with functionality.
You can have a global user object inside the $rootScope and have it injected in all your controllers by simply putting it into the arguments of the controller, just as you do with $scope. Then you can implement functionalities in a simple check: if($rootScope.user). This allows you to model the user object in any way you want and where you want, acting as a global variable, inside of Angular's domain and good practices with DI.
Just to add on my comment and your edit. Here is what the code would look like if you wanted to be able to re-use your user service and insert it into other apps.
angular.module('user', []).service('userService', [function(){
//declare your user properties and methods
}])
angular.module('myApp', ['user'])
.controller('myCtrl', ['userService', '$scope', function(userService, scope){
// you can access userService from here
}])
Not sure if that's what you wanted but likewise you could have your "user" module have a dependency to another "parent" module and access that module's data the same way.

Resources