LoopBack Remote Method to return array of records - loopback

I used loopback to generate my api and AngularJS to communicate with it. I have a model called Sync that contains the following records:
Sync": {
"34": "{\"uuid\":\"287c6625-4a95-4e11-847e-ad13e98c75a2\",\"table\":\"Property\",\"action\":\"create\",\"timeChanged\":1466598611995,\"id\":34}",
"35": "{\"uuid\":\"287c6625-4a95-4e11-847e-ad13e98c75a2\",\"table\":\"Property\",\"action\":\"update\",\"timeChanged\":1466598625506,\"id\":35}",
"36": "{\"uuid\":\"176aa537-d000-496a-895c-315f608ce494\",\"table\":\"Property\",\"action\":\"update\",\"timeChanged\":1466598649119,\"id\":36}"
}
in my sync.js Model file I am trying to write the following method that accepts number (long - the timeChanged) and should return all of the records that are have equal or equal timeChanged field.
This is where I am at:
Sync.getRecodsAfterTimestamp = function(timestamp, cb){
var response = [];
Sync.find(
function(list) {
/* success */
// DELETE ALL OF THE User Propery ratings associated with this property
for(i = 0; i < list.length; i++){
if(list[i].timeChanged == timestamp){
response += list[i];
console.log("Sync with id: " + list[i].id);
}
}
cb(null, response);
},
function(errorResponse) { /* error */ });
}
Sync.remoteMethod (
'getRecodsAfterTimestamp',
{
http: {path: '/getRecodsAfterTimestamp', verb: 'get'},
accepts: {arg: 'timeChanged', type: 'number', http: { source: 'query' } },
returns: {arg: 'name', type: 'Array'}
}
);
When I try this method in the loopback explorer I see this "AssertionError"

Your problem must be due to incorrect arguments supplied to the Sync.find() method. (You have provided 2 functions for success and error scenarios). As per the Strongloop documentation, the persisted model's find function has 2 arguments viz. an optional filter object and a callback. The callback uses the node error-first style.
Please try changing your Sync.find() to something like below:
Sync.find(function(err, list) {
if (err){
//error callback
}
/* success */
// DELETE ALL OF THE User Propery ratings associated with this property
for(i = 0; i < list.length; i++){
if(list[i].timeChanged == timestamp){
response += list[i];
console.log("Sync with id: " + list[i].id);
}
}
cb(null, response);
});

Related

Send message and leave server on ready event if not whitelisted (Discord.JS + MongoDB)

I'm coding a whitelist system for my discord bot that, on ready event (and after a 3 seconds delay), checks if every server it is in has it's ID added to the whitelist database on MongoDB. If not, the bot sends an embed and leaves the server. I managed to get it working on the guildCreate event, but on ready event it performs the message and leave actions on every single server without filtering conditions, even though those are added to the list. I cannot figure out why. Also, I'm still new to JavaScript, so it could be just a minor mistake.
//VARIABLES
const { Client, MessageEmbed } = require("discord.js")
const config = require('../../Files/Configuration/config.json');
const DB = require("../../Schemas/WhitelistDB");
//READY EVENT
module.exports = {
name: "ready",
once: false,
async execute(client) {
//[ ... ] <--- OTHER UNNECESSARY CODE IN BETWEEN
setTimeout(function() { // <--- 3 SECONDS DELAY
client.guilds.cache.forEach(async (guild) => { // <--- CHECK EVERY SERVER
await DB.find({}).then(whitelistServers => { // <--- CHECK MONGODB ID LIST
if(!whitelistServers.includes(guild.id)) {
const channel = guild.channels.cache.filter(c => c.type === 'GUILD_TEXT').random(1)[0]; // <--- SEND MESSAGE TO RANDOM TEXT CHANNEL (It is sending to every server, when it should be sending only to the not whitelisted ones)
if(channel) {
const WhitelistEmbed = new MessageEmbed()
WhitelistEmbed.setColor(config.colors.RED)
WhitelistEmbed.setDescription(`${config.symbols.ERROR} ${config.messages.SERVER_NOT_WHITELISTED}`)
channel.send({embeds: [WhitelistEmbed]});
}
client.guilds.cache.get(guild.id).leave(); // <--- LEAVE SERVER (It is leaving every server, when it should be leaving only the not whitelisted ones)
} else { return }
});
});
}, 1000 * 3);
}
}
I found the solution myself!
Instead of finding the array of whitelisted ID's for each guild, find one at a time and instead of checking the content of the array, check if the array exists. This is the updated code:
//WHITELIST
setTimeout(function() {
client.guilds.cache.forEach(async (guild) => {
await DB.findOne({ GuildID: guild.id }).then(whitelistServers => {
if(!whitelistServers) {
const channel = guild.channels.cache.filter(c => c.type === 'GUILD_TEXT').random(1)[0];
if(channel) {
const WhitelistEmbed = new MessageEmbed()
WhitelistEmbed.setColor(config.colors.RED)
WhitelistEmbed.setDescription(`${config.symbols.ERROR} ${config.messages.SERVER_NOT_WHITELISTED}`)
channel.send({embeds: [WhitelistEmbed]});
}
client.guilds.cache.get(guild.id).leave();
} else { return }
});
});
}, 1000 * 3);

How to fix a Wait condition error?

I have a browser.wait() in an E2E test which is invoking a function defined in protractor.config.js file. When running the test, I get the following error:
Failed: Wait condition must be a promise-like object, function, or a Condition object
FYI - the function defined in protractor.config.js contains an If-condition which I need for two cases. When I remove the If-condition, the test runs fine. The function returns a: deferred.promise
What does this exactly means and how to fix it? Have been searching, but unfortunately can't anything related to it.
Function in protractor.config.js:
checkMail: function(user, subjectSent) {
const deferred = protractor.promise.defer();
var usermail;
var mailsubject;
var count = 0;
mailListener.on("mail", function(mail, seqno, attributes) {
var mailuid = attributes.uid;
var toMailbox = '[Gmail]/All Mail';
var i = ++count;
user = mail.to[0].address;
mailsubject = mail.subject;
if (i > 2) {
mailListener.stop();
return;
}
deferred.fulfill(mail);
});
if ((user === usermail) && (subjectSent === mailsubject)) {
return deferred.promise;
}
}
E2E it-function:
it("It should do xxxxxxx", (done) => {
browser.wait(browser.params.checkMail('user_email#yyyyy.com', 'Email subject'))
.then((email) => {
expect(email['subject']).toEqual("Email subject");
expect(email['headers'].to).toEqual( 'user_email#yyyyy.com' );
});
done();
});

Caching Loopback REST connector

Loopback has the concept of non-database connectors, including a REST connector.
What is the right way of caching get requests to such data source?
Interesting thought... I think you'd have to do this yourself by creating a new custom remote method and check a local hash of values:
// in /common/models/myModel.js
var cache = {};
MyModel.lookup = function loopkup(someParam, next) {
if (cache[someParam]) {
// first see if the value is already in the cache
return next(null, cache[someParam]);
} else {
// otherwise do the REST remote method call...
MyModel.restLoopkup(someParam, function lookupCallback(err, data) {
if (err) { return next(err); }
cache[someParam] = data; // ...and then set the new cache value.
next(null, data);
});
};
MyModel.remoteMethod(
'lookup',
{
accepts: { arg: 'param', type: 'object', http: { source: 'query' } },
returns: { arg: 'results', type: 'object' },
http: { verb: 'get', path: '/lookup' }
}
);
This code would set up an endpoint at .../api/MyModels/lookup?param=foobar for the calling code to hit. Note that you would probably want to also set an expiration time for the data and properly manage the "cache". You could also use something like a redis store for the values instead of in-memory like I've done above.
Good luck!

Posting custom stories to Facebook with batch request

I have a web application that allows users to post custom stories to their Facebook timeline with the list of fitness exercises they have performed.
The first version is looping through the exercises and calling FP.api for each exercise and it works fine.
Now I would like to make a single call to FB.api with a batch request to speed up the posting and that's where I'm having trouble.
Here is the code with the loop that works fine (exids is an array of numbers):
function postToFB(exids)
{
fbi = 0;
fblength = exids.length;
for (var i = 0; i < fblength; i++)
{
FB.api(
'me/' + vitNamespace + ':perform',
'post',
{
exercise: "http://www.vitalclub.net/scripts/getExforFB.php?exid=" + exids[i],
'fb:explicitly_shared': true
},
function(response) {
...
});
}
}
and here is the code with the batch request that returns an error:
function postToFB(exids)
{
var batcharr = [];
for (var i = 0; i < exids.length; i++)
batcharr.push({ method: 'post', relative_url: 'me/' + vitNamespace + ':perform', body: "exercice=http://www.vitalclub.net/scripts/getExforFB.php%3Fexid%3D" + exids[i] + "&fb:explicitly_shared=true" });
FB.api(
'/',
'post',
{ batch: batcharr, include_headers: false },
function(response) {
...
});
}
The error I get (for each exercise) is the following: The action you're trying to publish is invalid because it does not specify any reference objects. At least one of the following properties must be specified: exercise.
I presume this has to do with the way the body in the batch request is formatted but I cannot find the right way to format it. I have tried using encodeURIComponent on the URL representing the exercise but the error is the same.
Anybody has an idea of what the problem is?
Thanks,
Jean
OK, my bad. It was really a stupid error. I had written the parameter "exercise" in French instead of English (so "exercice" instead of "exercise") and that's where the problem was.
I'm also now using $.param to format the parameters so I now have:
var batcharr = [];
var opts;
for (var i = 0; i < exids.length; i++)
{
opts = { exercise: "http://www.vitalclub.net/scripts/getExforFB.php?exid=" + exids[i], 'fb:explicitly_shared': true };
batcharr.push({ method: 'post', relative_url: 'me/' + vitNamespace + ':perform', body: $.param(opts) });
}
before calling FB.api and it works like a charm!

Facebook api multiple request

I am creating a facebook application generator. And I need to check if the user has currently added the application on this facebook page or not.
In order to do that, i first request facebook api to give a list of his pages. Then i loop through all the pages. And request for apps on each of these pages.
Lastly i compare the appId with the one user just created and displays the display the warning accordingly.
The problem is , when i loop through each of the pageId and request FbApi for subpages, the request response is delayed and the for each loop completes its cycle before the results are fetched from facebook.
Here is my code, which is quite complex... Any ideas to fix the issue is highly appreciated.
FB.login(function (response) {
FB.api('/me/accounts', function (apiresponse) {
var totalPages = apiresponse.data.length;
var pageIndex = 0;
$.each(apiresponse.data, function (pageNumber, pageData) {
var pageAccessToken = pageData.access_token;
var tabPageName = pageData.name;
var tabPageId = pageData.id;
FB.api("/" + tabPageId + "/tabs", function (response) {
var foundApp = false
$.each(response.data, function (index, value) {
var exsistingAppId = (value.id).split("app_").pop();
if (exsistingAppId == fbAppId) {
foundApp = true;
}
});
if (foundApp === true) {
var data = {
PageId: tabPageId,
Url: window.location.href.split("/").pop()
}
$.ajax({
type: "POST",
url: '/facebook/Match',
contentType: "application/json",
data: JSON.stringify(data),
success: function (data) {
if (data == "True") {
$("#addToFacebookModal ul").append("<li><span class='pageTab'><a class='pageTabName' target='_blank' href='https://facebook.com/profile.php?id=" +tabPageId + "'>" +tabPageName + "</a></span><a class='deleteAppFromPageTab' data-id='" +tabPageId + "' data-accessToken='" +pageAccessToken + "'>[x]</a></li>");
alreadyAdded.push(true);
} else {
alreadyAdded.push(false);
}
pageIndex++;
if (pageIndex == totalPages) {
console.log("Total Pages = " + totalPages + ", Looped through = " + alreadyAdded.length);
if (alreadyAdded.indexOf(true) >= 0) {
$("#addToFacebookModal").modal();
} else {
addToFacebook();
}
}
}
});
}
else {
pageIndex++;
}
});
});
});
}, { scope: 'manage_pages' });
Here is pseudocode of what i am doing
var myVariable
-Fb.Api Callback function returns response array
-Loop through the response array
-Get new Response array based on the previous response in that array
-Loop through each item of the new response array and compare it with myVariable.
The problem is that responses are delayed while the loop finishes up before the responses arrive. As i result i cant compare the nested Item with myVariable.
If by "facebook page" you mean a business page / organization page (not a profile), you can get the same information more easily by checking the endpoint '/{{page_id}}/tabs/{{app_id}}.
Replace page_id with the ID of the page you want to check and app_id similarly with your app ID. I don't have working code at the moment, but something like this:
FB.api(
'/' + checkPageID + '/tabs/{{app_id}}',
function (response) {
// Do console.log(response) to figure out how to see if installed or not
}
)
YOu can use fields expansion:
https://developers.facebook.com/docs/graph-api/using-graph-api/v2.3#fieldexpansion
FB.api('/me/accounts', {fields: 'name, address{city}'},function (response)
{
//do something here.
}