loopback remote method return variable other than request data - node.js

I have a generic SendMail route which I want to create multiple remote methods to handle multiple request templates. Any ideas on how to return a Email_Type from the remote method back to the base route. I know I could add a default with a code in it, but would like a more elegant solution.
Mail.genericSendMail = function genericEmail(response, callback) {
console.log(response);
let templateId=0;
//PROBLEM: HOW TO KNOW WHICH REMOTE WAS USED
switch (response.emailType) {
case "Template-1":
templateId= 1234;
break;
case "Template-2":
tempalteId = 456;
break;
default:
templateId = 789l
} //switch
console.log(templateId);
};
//Want multiple routes like this to support various templates
Mail.remoteMethod("genericEmail", {
http: {
path: "/emailTemplate1",
verb: "POST"
},
accepts [
{arg: "request", type:"object",http: {source:"body"},
default: {firstName:"", lastName:"",emailAddress:""}
}],
returns: RESTResponseStatic.loopbackAdapterCommonRestResponseDefinition()
});
//Want multiple routes like this to support various templates
Mail.remoteMethod("genericEmail", {
http: {
path: "/emailTemplate2",
verb: "POST"
},
accepts [
{arg: "request", type:"object",http: {source:"body"},
default: {emailAddress:"", promoCode:""}
}],
returns: RESTResponseStatic.loopbackAdapterCommonRestResponseDefinition()
});

There are a couple of different ways to do this. Since it happens to be a POST request, I usually go with attaching data to the body using a before remote hook.
Let's say you have a model method for logging in users.
Say we have a multi realm platform, so we need to know what platform we are logging in. If you don't use realms or don't know what they are, don't worry. This just shows you how to populate the data to the model method.
User.login = function(data, cb) {
if (data.realm == 'platform1) {
return logUserIntoPlatform1(data, cb);
}
return logUserIntoDefaultPlatform(data, cb);
}
Now let's say you don't want the client/frontend to send the realm and you don't want to do the lookup for realm in the model. We can add a beforeRemote hook like so:
User.beforeRemote('login', function (context, user, next) {
context.args.data.realm = lookUpRealmSync(context); // 1
next();
});
This will be called before the login method. Note the next() call: this is how you could do error detection before actually hitting the model method. Something like next({ status: 422, message: 'Parameter missing: password }); would return an error and not execute the User.login method.
You may have to look carefully at your context object (i.e. the line marked with 1 may not work exactly as I've shown for you).
If you want to read more about this stuff, I LoopBack's docs are pretty good. It seems they've been updated since I've last used them so I can't link you to the more useful pages. I found the remote method documentation here though.
Edit: I took a closer look at your question. You should be able to retrieve the path from the context object and pass data accordingly. I'm not going to try to code that since I don't know where it would actually be within the object.

Related

why am I getting favicon.ico when i am using findOne method for express params routes?

when i am using list.save() method a object other than customList name which is favicon.ico is also
saving as record in following cod, Why am i gatting favicon.ico as object.
app.get('/:listRoute',function (req,res) {
const customList=(req.params.listRoute);
List.findOne({name:customList }, function (err,result) {
if (!err) {
if (!result) {
const list=new List({
name: customList,
items: defaultItems
})
list.save();
} else {
console.log(result);
res.render('list', {
listTitle: result.name,
latestItems: result.items})
}
}
});
})
When you visit a website (any URL on that website), a browser will typically also send a request to that same domain for /favicon.ico so see if the web site offers an icon to be a visual representation of the site.
Since you are using a wildcarded top level route:
app.get('/:listRoute', ...)
That will get hit by the request for /favicon.ico. Some other urls you also may need to watch out for being requested are: /robots.txt, /humans.txt, /sitemap.xml, /ads.txt.
There are a number of ways to work around this:
Your wildcard route can first check req.url or req.params.listRoute to see if it's something it should ignore.
You can place other top level routes that you want to keep out of your wildcard route in a position before this route so they don't end up in this one.
Don't use a top level wildcard route. Instead, use something like /list/:listRoute so it won't automatically match any top level http request. Your use of a top level wildcarded route interferes with other future uses of your site and can create backwards compatibility going forward when you want to add other top level routes to your site. Imagine if sometime in the future, you want to add /contact or /login or /logout. Those all conflict with /:listRoute.
Try to add a callback function to the list.save();
Let me know if this works. The reason is maybe because of sync issues. eg: time taken by mongoDB to update the first document & save > the time taken by the 'Get' method to redirect to itself. Therefore by adding this callback it kinda make sure the code gets saved first and err checked before the redirect.
eg:
list.save(function(err){
if(!err) {
console.log("list is successfully saved"); //log is optional
res.redirect("/" + listRoute);
}
});
When fetching route data using params with express,the entered data easily we can log.But if not adding top-level route and just trying to get the required route eg:
app.get("/:requireddata",function(req,res){
const data = req.params.requireddata;
});
in this case, when loading main page the favicon.ico will generate as a result.
So for getting an exact result, that's when only loading requireddata route we can get the result by using higher level route.
In case there is no higher-level route add just an additional route before requireddata as shown below:
app.get("/add/:requireddata",function(){});
Here /add/ is an additional route for avoiding favicon.ico
For me this worked, so if this information is useful just go head.
Hey there I also came across this exact issue.
So here is my solution to that.
Just enclose everything in a if block and there you go. DONE !!!!
app.get("/:name", function (req, res) {
if (req.params.name != "favicon.ico") {
const name = _.capitalize(req.params.name);
List.findOne({ name: name }, (err, foundList) => {
if (!err) {
//new list with default items created
if (!foundList) {
const list = new List({
name: name,
items: defaultItems,
});
list.save();
res.redirect("/" + name);
} else {
res.render("list", {
listTitle: foundList.name,
newListItem: foundList.items,
});
}
}
});
}
});
P.s.:- It will throw some error from mongo but that'll not affect the overall working.
Hope this helps.

Very simple Node.js POST form inquiries

I have a simple node.js app using express static and nodemailer with a POST form that emails the filled fields to myself. My problems are very simple, I'm just quite new to Node so I can't find a way to do them.
My first problem is that I can't find a way to put all the form data into the email's text. Below, I am trying to store my form data in a JSON and call it in the email text, but it doesn't work. It has only correctly worked for me when I only used one variable (ex. just req.body.name) for the text. How can I format my data together in the email?
My second problem is that I can find a way to handle the app after the email is sent. I want it to redirect to a success page, as shown in the marked line, but it does not work. Instead, the page goes to /reqform and displays an error message saying success.html doesn't exist (it is in the same public folder as my html file). I believe the problem lies in my sendFile usage, but I'm not sure.
app.post('/reqform', urlencodedParser, function (req, res) {
response = {
name: req.body.name,
email: req.body.email,
phone: req.body.phone
};
var mailContent = {
from: 'myemail#gmail.com',
to: 'myemail#gmail.com',
subject: 'Service Request From req.body.name',
text: response //*** Problem #1
};
transporter.sendMail(mailClient, function (error, info) {
if (error) {
console.log(error);
} else {
res.sendFile("success.html"); //** Problem #2
}
});
})
Any help is greatly appreciated. Thanks!
The default string value for a JavaScript Object is just "[object Object]". If you want anything else, you'll have to be specific with how you want it represented.
For example, JSON is a text/string format that represents values like Objects. It's separate from the Object itself, so you'll need to convert to use it:
var mailContent = {
// ...
text: JSON.stringify(response)
}
Or, provide your own formatting:
var mailContent = {
// ...
text: `Name: ${response.name}\nEmail: ${response.email}\nPhone: ${response.phone}`
}
It might be better to use res.redirect() in this case, allowing the client/browser to request success.html via your application's static() middleware:
res.redirect('/success.html');
res.sendFile() doesn't collaborate with any static() middleware to know about your public folder. It expects you to provide a full path either directly or with its root option:
res.sendFile(path.join(__dirname, 'public/success.html'));

What is the correct way to test Webhooks?

I have a ReSTFul API in Meteor. I use hooks from Mandrill, Stripe, and other libraries to update a collection.
Router.route('/mandrill/message_rejected', { where: 'server' })
.post(function () {
var request = EJSON.parse(this.request.body.mandrill_events);
var rejects = _.map(_.where(request, {
event: 'reject'
}, {
return object.msg.email;
});
Meteor.users.update({
emails: {
$elemMatch: {
"address": {
$in: rejects
}
}
}
}, {
$set: { status: 'rejected' }
});
this.response.end();
});
My question is; how can I automate tests for this? The request is supposed to come from Mandrill. Is there some way to test webhook messages in a consistent way?
I use Mocha (although you can use other testing frameworks as Jasmine).
I combine the tests with the superagent library that allows you to perform HTTP requests.
The next part does the trick: set up a log and store the received JSON from Mandril or other hooks that you receive and build a library (or fixture) of incoming responses.
Then you can build up the different cases that you need, for example:
Removing an expected field
Sending duplicates
And so on
Making this method consistent requires that you spend time thinking about what hooks you expect to receive, reading the documentation to evaluate if a case you are thinking on is not possible and so on.
I'll recommend you to keep the log of received hooks to improve your tests over the time.

Google+ insert moment with nodejs client

Has anyone been able to get the google-api-nodejs-client to successfully insert a moment?
Whatever I try, I get a generic 400 "Invalid value" error but am unable to narrow down the invalid value because the API Explorer doesn't work either.
Would it be because of the missing data-requestvisibleactions parameter? I'm using passport.js's require('passport-google-oauth').OAuth2Strategy for handling oauth access, and that part is working fine, but I have no idea how to incorporate requestvisibleactions into the oauth request flow since this is definitely not originating from a clientside form.
Here's a snippet of what I'm trying to do (using the latest version of googleapis, v1.0.2):
var google = require('googleapis')
var auth = new google.auth.OAuth2()
auth.setCredentials({
'access_token': user.token
})
google.plus('v1').moments.insert({
collection: 'vault',
userId: 'me',
debug: true,
resource: {
type: "http://schemas.google.com/AddActivity",
target: {
type: "http://schema.org/CreativeWork",
url: "...omitted...",
image: "...omitted...",
description: "test",
name: "test"
}
},
auth: auth
}, function (err, response) {
if (err) {
console.error(err)
res.send(err.code, err)
} else {
console.log(response)
res.send(200)
}
})
ref 1 (out-of-date w.r.t. an older version of googleapis)
ref 2 (client-side, where the use of data-requestvisibleactions is more obvious)
As you speculated, you need the request_visible_actions parameter as part of the URL calling the oauth endpoint.
It looks like the current version of passport-google-oauth doesn't support this parameter. Judging by several of the open issues and pull requests, it isn't clear that the author will respond to requests to add it either. You have two possible options:
Switch to using the OAuth support that is included in google-api-nodejs-client
Patch the passport-google-oauth code. (And possibly submit a pull request in the hopes it will be useful to someone else.)
I don't use passport.js or the passport module in question, so I can't test this, but based on the github repository, I think you can insert the following in lib/passport-google-oauth/oauth2.js after line 136 and before the return statement:
if (options.requestVisibleActions) {
// Space separated list of allowed app actions
// as documented at:
// https://developers.google.com/+/web/app-activities/#writing_an_app_activity_using_the_google_apis_client_libraries
// https://developers.google.com/+/api/moment-types/
params['request_visible_actions'] = options.requestVisibleActions;
}

Reasonable design of using socket.io for RPC

I am building a web app that uses socket.io to trigger remote procedure calls that passes sessions specific data back go the client. As my app is getting bigger and getting more users, I wanted to check to see if my design is reasonable.
The server that receives websocket communications and triggers RPCs looks something like this:
s.on('run', function(input) {
client.invoke(input.method, input.params, s.id, function(error, res, more) {
s.emit('output', {
method: input.method,
error: error,
response: res,
more: more,
id: s.id
});
});
});
However, this means that the client has to first emit the method invocation, and then listen to all method returns and pluck out its correct return value:
socket.on('output', function(res) {
if (res.id === socket.sessionid) {
if (!res.error) {
if(res.method === 'myMethod') {
var myResponse = res.response;
// Do more stuff with my response
});
}
}
});
It is starting to seem like a messy design as I add more and more functions... is there a better way to do this?
The traditional AJAX way of attaching a callback to each function would be a lot nicer, but I want to take advantage of the benefits of websockets (e.g. less overhead for rapid communications).

Resources