Loopback Remote Method and Firebase Admin NodeJS - angularjs

I am currently trying to create a remote method in loopback that will query a firebase database using the Firebase Admin SDK in NodeJS.
It works, but the issue I am having is that I am unable to make it realtime. It keeps crashing with an error pointing to the callback function being called more than once.
Here is a snippet of the code for my remote method:
'use strict';
module.exports = function(Scusers) {
var admin = require("firebase-admin");
Scusers.listItems = function(cb) {
// Get a database reference
var db = admin.database();
var ref = db.ref("users");
// Attach an asynchronous callback to read the data at our posts reference
var items = [];
// return list of users ordered by key and push each object into an array
ref.orderByKey().on("value", function(snapshot) {
snapshot.forEach(function(data) {
items.push(data.val());
});
// return array
cb(null, items);
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
};
}
If I change this line:
ref.orderByKey().on
for:
ref.orderByKey().once
It works, but on my front-end which is coded in AngularJS, it won't see the changes unless I manually call a refresh.
What should be the best approach to this? Sorry if it is unclear or my approach is wrong, I am so new at this. Thanks!

Related

Admin on Rest Mongoose compatible queries

I am using Admin on Rest to create a dashboard for my rest calls.
The call is going through and I looked at the call in my console.
GET /api/v2/admin/user?_end=10&_order=DESC&_sort=id&_start=0 200 61.102 ms - 2846
But it isn't returning anything, which I assume is because Mongo doesn't take in queries with _end, _order, _start, as well as it uses _id instead of id.
What would be the best workaround for this? As I can't modify the call going in.
The logic for the endpoint is below. I am also (not pictured) trying to manually create the sorting options but I feel like that isn't efficient.
// routes/admin/user.js
var express = require('express'),
router = express.Router();
var User = require(__models + 'user');
router.route('/')
.get(function(req, res, next){
var query = req.query || {};
User.find(query).then(users => {
return res.json(users);
}).catch(err => next(err));
});
module.exports = router;
You need to write your own REST Client for creating queries in formats your REST API understands.
https://marmelab.com/admin-on-rest/RestClients.html#writing-your-own-rest-client
I created a util function to do this for me.
module.exports.getJsonFromUrl = function(query) {
var result = {};
query.split("&").forEach(function(part) {
var item = part.split("=");
result[item[0]] = decodeURIComponent(item[1]);
});
return result;
};
And I called it from controller like so
var query = routeUtil.getJsonFromUrl(req._parsedUrl.query);

How to post some data and get results using MEAN stack and ngResource

I am working on a application and I am using MEAN stack as technology. In AngularJS, I am using ngResource to CRUD operations. Can any one suggest how to send username and password to server and get response back to check if the credentials are valid. I need help in ngResource and mongoose code. Thanks.
Check out the mean.js boilerplate:
https://github.com/meanjs/mean
You'll see how they do it pretty quickly:
moduleName.client.controller.js will make an http call, using the injected http. Here is an example of the call being made from /modules/users/client/controllers/authentication.client.controller.js (with some edits to the code to make it easier to see what you're looking for):
AuthenticationController.$inject = ['$scope', '$state', '$http', 'Authentication'];
function AuthenticationController($scope, $state, $http, Authentication, ) {
...
vm.authentication = Authentication;
$http.post('/api/auth/signup', vm.credentials).success(function (response) {
// If successful we assign the response to the global user model
vm.authentication.user = response;
}).error(function (response) {
vm.error = response.message;
});
}
Now, this call is posted to '/api/auth/signup'. The file that handles this route is located in /modules/users/server/routes/auth.server.routes.js:
modules.exports = function(app) {
var users = require('../controllers/users.server.controller');
...
app.route('/api/auth/signup').post(users.signup);
}
As you can see, the route (the url) matches the one you called from the client controller. As that $http call from the controller was a $http.post(), the route entry must match. You can see that it does above.
The parameter users.signup passed above refers to a function in yet another file: /modules/users/server/controllers/users/users.authentication.server.controller.js. This is your main controller for the authentication part of the users module. Now, within this file we can see the signup function is exported:
/* note: there are global variables here, see below */
exports.signup = function (req, res) {
// For security measurement we remove the roles from the req.body object
delete req.body.roles;
// Init user and add missing fields
var user = new User(req.body);
user.provider = 'local';
user.displayName = user.firstName + ' ' + user.lastName;
// Then save the user
user.save(function (err) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
// Remove sensitive data before login
user.password = undefined;
user.salt = undefined;
req.login(user, function (err) {
if (err) {
res.status(400).send(err);
} else {
res.json(user);
}
});
}
});
};
Now, a lot is going on here, but we can break it down.
The req variable is the post request as passed by $http. The res variable is the response the client expects to receive back.
Notice how vm.credentials was passed in $http.post('/api/auth/signup/', vm.credentials)? This is bound to req.body and may be accessed by your server controller from that.
So in this example, the req.body is the required data to create a new user on the server. This is done using mongoose, which has a schema called User. Access this by declaring globals at the top of your controller:
var mongoose = require('mongoose'),
User = mongoose.model('User');
You can see that a new user is instantiated above. It is saved via the mongoose call .save().
Lastly, your server functions should response to the client's request using the res variable passed to the function. See how once the user is created successfully, the function calls
res.jsonp(user);
This is success() to the client, which accepts the response and binds it to a local variable vm.authentication.user
Hope this helps!

Angular-permission define permissions retrieved via API

I am trying to use angular-permission to implement permission-based authentication but I don't know where to define those permissions which are retrieved from my back-end via API which requires token-based access.
First, let me give a bit background about how my app looks like. On my back-end, my system portal, I define permissions to allow different APIs to be called. Permissions won't change all the time. Only when I add new features(APIs), new permissions will be added. For example.
permission1: api1,api2,api3
permission2:api4,api5,api6
permission3:api7,api8,api9
On the front-end, customers login the front-end web portal and create customized roles themselves which group some permissions together, for example:
admin: permission1,permission2,permission3
auditor:permission 3
The angular-permission doc says (https://github.com/Narzerus/angular-permission/blob/development/docs/1-manging-permissions.md#multiple-permissions) I can use PermissionStore.defineManyPermissions to define permissions which are retrieved from API after user login. That's all clear.
So I have two modules. One is the Authentication module which handles user login. The other one is the Permission module which handles the permission validation. On the Permission module .run() phase, I define the permissions like this:
var getPermissions = function () {
var deferred = $q.defer();
system.permissions.get(
function () {
return deferred.resolve(system.permissions._permissions);
},
function (error) {
console.log("error if can't load permissions");
console.log(error);
}
);
return deferred.promise;
};
var loadPermissions = function () {
var promise = getPermissions();
promise.then(function (permissions) {
var arrayPermissions = formatPermissionArray(permissions);
//var arrayPermissions=['viewSeed','viewAuthentication'];
PermissionStore.defineManyPermissions(arrayPermissions, checkPermission);
console.log("from permission run service");
console.log(arrayPermissions);
}, function (reason) {
console.log('Failed: ' + reason);
}, function (update) {
console.log('Got notification: ' + update);
});
};
loadPermissions();
var formatPermissionArray = function (sourceData) {
var formatedPermissionArray = [];
for (var i = 0; i < sourceData.length; i++) {
formatedPermissionArray.push(sourceData[i].permissionId);
};
return formatedPermissionArray;
};
But during the bootstrap of the app, this module already loaded and the arrayPermissions will be empty since user hasn't logged in yet.
I tried to use oclazyload to load the Permission module from the login controller of the Authentication module, that actually works but if user refresh/reload their page, the Permission module won't be loaded anymore.
I am new to web development and also new to AngularJs. Just a few months experience. I don't know if I am doing it in a complete wrong way.
My questions are:
The API for retrieving a permission list should require authentication? Since I will need to put those authentication on the UI-router routes. Anyone can see it anyway. If I should not protect that API, then my problem is solved.
If I should keep my api protected, how should I address the issues I described above and that is where to define the permissions for angular-permission and how to use API to retrieve the permissions.
I hope I have managed to describe my issues clearly. Any help or guidance are greatly appreciated.
Regards,
Lola
I'm using angular-permission with angular-satellizer. PermRoleStore or PermPermissionStore needs to be in run block. You can add data to JSON WEB TOKEN add use it at the run block like I did.
$auth.getPayload()This function returns payload from JWT in localStorage. And in that payload it has data with role key which I saved in backend. I hope this helps your issue.
.run(function (PermRoleStore, $auth, Yollar) {
PermRoleStore
.defineRole('ADMIN', function () {
if($auth.getPayload()) {
if ($auth.getPayload().data.role === 'ADMIN') {
return true;
}
else {
return false;
}
}
else {
return false;
}
});
PermRoleStore
.defineRole('MODERATOR', function () {
if($auth.getPayload()) {
if ($auth.getPayload().data.role === 'MODERATOR') {
return true;
}
else {
return false;
}
}
else {
return false;
}
});
})

AngularJS Web Api HttpHandler Image Downloader

My AngularJS application is interacting with ASP.NET Web API to full fill the request which is working fine. it has an use case that allows user to download user specific secure PDF document. I have implemented this functionality as below
AngularJS:
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
$scope.DownloadHandler = function (id, downloadURL) {
FileStreamManager.getPdf(id, downloadURL)
.then(function (result) {
// success
window.open(downloadURL + id, '_self', '');
},
function (result) {
$scope.errors = result.data;
});
};
Note : downloadURL is the Controller call like \ImageRepo\Get
Web Api Controller I have this implementation:
HttpResponseMessage response = new HttpResponseMessage();
// DB call to to build the URL
string fileName = "myLocation\Image\doc.pdf";
if (!fileProvider.Exists(fileName))
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
FileStream fileStream = fileProvider.Open(fileName);
response.Content = new StreamContent(fileStream);
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentDisposition.FileName = fileName;
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
response.Content.Headers.ContentLength = fileProvider.GetLength(fileName);
return response;
Which is working fine. Due to the security issue, i was asked to implement this by using HttpHanlder which is pretty new to me. I have some question on the same.
Should my AngularJS ng-click calls to my .ashx handler directly instead of a Controller URL? Or Should this call route through Controller? How?
I have DB calls to build the image URL and update some the status. Can this be done in Handler itself?
How would i make sure my documents are secured while downloading?
Please help.

Jaydata toLiveArray() error "Pure Class" w/ IndexedDB provider

Using Jaydata, I can populate my simple local database from an odata service. The database has an Organization table.
I am also using AngularJs. The following code gets the organizations from the local database and binds them to $scope.Organizations which is referenced on my OrganziationIndex view. I've tested this so far in Chrome, Safari on iOS7, and the Android browser built in to ICS, and it works as expected in each of those browsers.
var localDB = new InspecTechDB({
name: 'local',
databaseName: 'InspecTech'
});
app.controller('OrganizationIndex', function ($scope, $data) {
$scope.organizations = [];
//wait until the localDB is ready, then get the Organizations
$.when(localDB.onReady())
.then(function () {
$scope.inspectechdb = localDB;
$scope.organizations = localDB.Organizations.toLiveArray();
$scope.message = "Organizations fetched from local store. Click one of them!";
});
});
However, in IE11, I get an error message on the console that states simply "Pure Class" on this line and the organizations are not listed on the view:
scope.organizations = localDB.Organizations.toLiveArray();
A Google search reveals literally no results: Google Search
I've verified with console logging that the table is populated from the odata service. What I have found is that if I change the code as shown below (changed from local database to odata service), that the error goes away:
var remoteDB = new InspecTechDB({
name: 'oData',
oDataServiceHost: '/odata'
});
app.controller('OrganizationIndex', function ($scope, $data) {
$scope.organizations = [];
//wait until the localDB is ready, then get the Organizations
$.when(remoteDB.onReady())
.then(function () {
$scope.inspectechdb = remoteDB;
$scope.organizations = remoteDB.Organizations.toLiveArray();
$scope.message = "Organizations fetched from local store. Click one of them!";
});
});
I do intend for the application to be run offline so I need to be able to get the organizations from the local database and not the odata service.
Can someone point in the right direction for what I need to do to make this work in IE?
Thanks,
Mike
UPDATE 1:
I'm observing the same error in Firefox.
UPDATE 2:
I'm observing the same error in Chrome if I change the provider like so:
var localDB = new InspecTechDB({
name: 'indexedDb',
databaseName: 'InspecTech'
});
So the problem is not specific to IE, more like specific to the IndexedDB provider.
UDPATE 3:
Here's some workaround code. I'd still like to know about the problem with .toLiveArray().
var localDB = new InspecTechDB({
name: 'local',
databaseName: 'InspecTech'
});
app.controller('OrganizationIndex', function ($scope, $data) {
$scope.organizations = [];
//wait until the localDB is ready, then get the Organizations
//$.when(localDB.onReady())
//.then(function () {
// $scope.inspectechdb = localDB;
// $scope.organizations = localDB.Organizations.toLiveArray()
// .then(function () {
// $scope.message = "Organizations fetched from local store. Click one of them!";
// });
//});
//this code replaces the above code since I can't make toLiveArray work w/ indexedDB provider
$.when(localDB.onReady())
.then(function () {
var organizations = localDB.Organizations.toArray();
return $.when(organizations)
.then(function (orgs) {
$scope.$apply(function () {
orgs.forEach(function (organization) {
$scope.organizations.push(organization);
});
});
});
});
});

Resources