I have authorised a small node app with the gmail API. I am successfully getting responses. I can, for eg, see my email signature using users.settings.sendAs.list()
However When I try and make a change to the settings, again in this case the signature, the response I get back is empty. So the update is working in so far as i can wipe a signature I added manually in gmail, but I cannot add anything new.
Here's the script
var google = require('googleapis');
var key = require('./Sig-Updater.json');
var gmail = google.gmail('v1');
var jwtClient = new google.auth.JWT(
key.client_email,
null,
key.private_key,
['https://www.googleapis.com/auth/gmail.settings.basic', 'https://www.googleapis.com/auth/gmail.settings.sharing', 'https://www.googleapis.com/auth/gmail.modify', 'https://mail.google.com', 'https://www.googleapis.com/auth/gmail.compose',
'https://www.googleapis.com/auth/gmail.readonly' ],
'some_email#somewhere.com'
);
jwtClient.authorize(function (err, tokens) {
if (err) {
console.log('Auth failed because: ' + err);
return;
}
console.log(tokens)
gmail.users.settings.sendAs.update({
userId: 'me',
auth: jwtClient,
sendAsEmail: 'some_email#somewhere.com',
signature: '<div dir="ltr">Hello there</div>'
}, function (err, resp) {
if(err){
console.log(err);
} else {
console.log(resp);
}
});
});
Sorry for any noob business - new to this API and indeed node.js
Looks like my Noob disclaimer was warranted in the end,
I had omitted the 'resource' object from the request.
Should look more like
gmail.users.settings.sendAs.update({
userId: 'me',
auth: jwtClient,
sendAsEmail: 'some_email#somewhere.com',
fields: 'signature',
resource: {
signature: '<div dir="ltr">Hello there</div>'
}
}, function (err, resp) {
if(err){
console.log(err);
} else {
console.log(resp);
}
});
Hope that helps someone in the future!
Related
I am attempting to send a text message when a user requests to reset their password. I would like to wait for the message to be sent to alert the user if it was successful or not. I am currently attempting to do it as follows:
async function sendResetPasswordTextMessage(req, res) {
let result = {};
let phoneNumber = req.body.phoneNumber;
if (phoneNumber === undefined) {
return sendInvalidParametersMessage(res);
}
phoneNumber = phoneNumber.toString();
const userProfile = await models.UserProfile.findOne({
where: {
phoneNumber: phoneNumber
}
});
************************** RELEVANT CODE TO ISSUE *************************
if (userProfile) {
const message = "Your username is:\n" + userProfile.username;
const sent = await AWSSNSClient.sendMessage(message, phoneNumber);
if (!sent) {
result.error = setTitleAndMessage("Error", "An error occurred");
} else {
result.success = setTitleAndMessage("Success", "Message sent");
}
}
return res.send(result);
***************************************************************************
}
In my other class AWSSNSClient, I have the following sendMessage function:
function sendMessage(message, phoneNumber) {
const params = {
Message: message,
MessageStructure: "string",
PhoneNumber: "+1" + phoneNumber
};
let sent = false;
sns.publish(params, function(err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
}
else {
sent = true;
}
});
return sent;
}
I cannot figure out how to make sendMessage wait for sns.publish to return before it returns itself. I have tried making it an async method and adding await on sns.publish, but the function still returns before sent gets set to true.
I know that the messages are sending without error because I am receiving them and no console logs are printed.
Stumbled on this one via Google trying to figure this out myself today - short answer that I am now using:
You can now do this with Async/Await — and Call the AWS service (SNS for example) with a .promise() extension to tell aws-sdk to use the promise-ified version of that service function (SNS) instead of the call back based version.
The only caveat here is the containing function must ALSO be async to utilize the await syntax.
For example:
let snsResult = await sns.publish({
Message: snsPayload,
MessageStructure: 'json',
TargetArn: endPointArn
}, async function (err, data) {
if (err) {
console.log("SNS Push Failed:");
console.log(err.stack);
return;
}
console.log('SNS push suceeded: ' + data);
return data;
}).promise();
The important part is the .promise() on the end there. Full docs on using aws-sdk in an async / promise based manner can be found here: https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/using-promises.html
In order to run another aws-sdk task you would similarly add await and the .promise() extension to that function (assuming that is available).
For anyone who runs into this thread and is actually looking to simply push multiple aws-sdk promises to an array and wait for that WHOLE array to finish (without regard to which promise executes first) I ended up with something like this:
let snsPromises = [] // declare array to hold promises
let snsResult = await sns.publish({
Message: snsPayload,
MessageStructure: 'json',
TargetArn: endPointArn
}, async function (err, data) {
if (err) {
console.log("Search Push Failed:");
console.log(err.stack);
return;
}
console.log('Search push suceeded: ' + data);
return data;
}).promise();
snsPromises.push(snsResult)
await Promise.all(snsPromises)
Hope that helps someone that randomly stumbles on this via google like I did!
stackdave will that actually wait?
Necevil "Search push suceeded will get logged twice" because you're mixing calling operations by passing a callback and using promises. You should only use one method of getting the result
let snsResult = await sns.publish({
Message: snsPayload,
MessageStructure: 'json',
TargetArn: endPointArn}).promise()
will do the trick
You can simply use callbacks for that. Modify your sendMessge like this
function sendMessage(message, phoneNumber, cb) {
const params = {
Message: message,
MessageStructure: "string",
PhoneNumber: "+1" + phoneNumber
};
sns.publish(params, cb);
}
then on your main file you can supply callback like this
if (userProfile) {
const message = "Your username is:\n" + userProfile.username;
AWSSNSClient.sendMessage(message, phoneNumber, (err, data) => {
if (err) {
result.error = setTitleAndMessage("Error", "An error occurred");
}
else {
result.success = setTitleAndMessage("Success", "Message sent");
}
res.send(result);
});
}
Here the right updated API, August 2018, Necevil answer send the sms twice.
// using config.env
AWS.config.region = 'eu-west-1';
AWS.config.update({
accessKeyId: process.env.AMAZON_SMS_ID,
secretAccessKey: process.env.AMAZON_SMS_TOKEN,
});
// parameters
let params = {
Message: contentSMS, // here your sms
PhoneNumber: mobile, // here the cellphone
};
const snsResult = await sns.publish(params, async (err, data) => {
if (err) {
console.log("ERROR", err.stack);
}
console.log('SNS ok: ' , JSON.stringify (data));
});
If you're having issues with duplicate SNS messages being sent, I fixed this issue by utilizing examples from AWS:
// Load the AWS SDK for Node.js
var AWS = require('aws-sdk');
// Set region
AWS.config.update({region: 'REGION'});
// Create publish parameters
var params = {
Message: 'MESSAGE_TEXT', /* required */
TopicArn: 'TOPIC_ARN'
};
// Create promise and SNS service object
var publishTextPromise = new AWS.SNS({apiVersion: '2010-03-31'}).publish(params).promise();
// Handle promise's fulfilled/rejected states
publishTextPromise.then(
function(data) {
console.log("Message ${params.Message} send sent to the topic ${params.TopicArn}");
console.log("MessageID is " + data.MessageId);
}).catch(
function(err) {
console.error(err, err.stack);
});
By utilizing a traditional .then() I was able to squash the duplicate message bug mentioned in comments above.
You can create a async function what use the promise method
async function sendMessage(message, phoneNumber){
const params = {
Message: message,
PhoneNumber: phoneNumber
};
return new Promise((resolve, reject) => {
SNS.publish(params, (err, data) => {
if (err) {
console.log("Search Push Failed:");
console.log(err.stack);
return reject(err);
} else {
console.log('Search push suceeded:' + phoneNumber);
return resolve(data);
}
})
});
}
and then you can call
var s= await sendMessage(message,phoneNumber);
i can't get the Credentials for my CognitoIdentity. When the User is successfully authenticated, he needs to get a Identity to access other AWS Services. In my case thats AWS IoT. But for somehow, i can't get any credentials.
This is the Error Message:
Error retrieving credentials: NotAuthorizedException: Access to
Identity 'eu-central-1:XXXXXXXXXX' is
forbidden.
My Code is almost exactly like the Tutorial on Github:
var cognitoUser = new AWSCognito.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function (result) {
console.log("Logged in");
console.log('access token + ' + result.getAccessToken().getJwtToken());
// window.location.href = "index.html";
AWS.config.region = AWSConfiguration.region;
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: AWSConfiguration.IdPoolId,
Logins : {
'cognito-idp.eu-central-1.amazonaws.com/eu-central-1_XXXX' : result.getIdToken().getJwtToken()
}
});
var cognitoIdentity = new AWS.CognitoIdentity();
AWS.config.credentials.get(function(err, data) {
if (!err) {
console.log('retrieved identity: ' + AWS.config.credentials.identityId);
var params = {
IdentityId: AWS.config.credentials.identityId
};
cognitoIdentity.getCredentialsForIdentity(params, function(err, data) {
if (!err) {
thingShadows.updateWebSocketCredentials(data.credentials.AccessKeyId,
data.credentials.SecretKey,
data.credentials.SessionToken);
}
else {
console.log('error retrieving credentials: ' + err);
}
});
}
else {
console.log('error retrieving identity:' + err);
}
});
}
});
Please note that i skipped not related code.
authenticated users have full access to all AWS services i'm using.
I don't think you need to call cognitoIdentity.getCredentialsForIdentity(). Your IAM keys should be put into the AWS.config.credentials object when you call AWS.config.credentials.get(). You can access them directly in the callback you provide when you call it.
In other words, when you're logging out the retrieved identity: to the console, the credentials object should already have your secret key, access key id, and session token in it.
All of this (give or take a curly brace):
var params = {
IdentityId: AWS.config.credentials.identityId
};
cognitoIdentity.getCredentialsForIdentity(params, function(err, data) {
if (!err) {
thingShadows.updateWebSocketCredentials(data.credentials.AccessKeyId,
data.credentials.SecretKey,
data.credentials.SessionToken);
}
else {
console.log('error retrieving credentials: ' + err);
}
});
Can probably be replaced with something like this:
thingShadows.updateWebSocketCredentials(AWS.config.credentials.accessKeyId,
AWS.config.credentials.secretKey,
AWS.config.credentials.sessionToken);
If you pass in a Logins map with the user pool id and access token in it, the getCredentialsForIdentity() call might succeed; I didn't test it. I haven't yet run into a use case where I needed to use this particular API, and I suspect you don't need it either.
Source: I work on a 100% javascript application that uses both authenticated and unauthenticated Cognito identities. We don't call getCredentialsForIdentity() anywhere, and trying to insert it produced the same error you're getting.
I am a beginner and am currently making a User Management system in NodeJS, I had previously done it with MongoDB, Express. Right now im making it all again with Express, Sequelize and Postgresql to better understand some concepts.
What im stuck at is the reset page where I previously used Async.waterfall to get the email id and send email using SendGrid, but now I want to know how can I convert it using Promises..? It is a bit confusing to understand how to use them with concurrent callbacks.
Here is the previous code using the async.waterfall :
app.post('/forgotpassword', function(req, res, next) {
async.waterfall([
function(done) {
crypto.randomBytes(20, function(err, buf) {
var token = buf.toString('hex');
done(err, token);
});
},
//2
function(token, done) {
User.findOne({ 'local.email': req.body.email }, function(err, user) {
if (!user) {
req.flash('forgotMessage', 'No account with that email address exists.');
return res.redirect('/forgotpassword');
}
user.local.resetPasswordToken = token;
user.local.resetPasswordExpires = Date.now() + 3600000; // 1 hour
user.save(function(err) {
done(err, token, user);
});
});
},
//3
function(token, user, done) {
var nodemailer = require('nodemailer');
var sgTransport = require('nodemailer-sendgrid-transport');
var options = {
auth: {
api_key: ''
}
};
var mailer= nodemailer.createTransport(sgTransport(options));
var mailOptions = {
to: user.local.email,
from: 'passwordreset#demo.com',
subject: 'Node.js Password Reset',
text: 'You are receiving this because you (or someone else) have requested the reset of the password for your account.\n\n' +
'Please click on the following link, or paste this into your browser to complete the process:\n\n' +
'http://' + req.headers.host + '/reset/' + token + '\n\n' +
'If you did not request this, please ignore this email and your password will remain unchanged.\n'
};
mailer.sendMail(mailOptions, function(err) {
req.flash('forgotMessage', 'An e-mail has been sent to ' + user.local.email + ' with further instructions.');
done(err, 'done');
});
}
],
//2 out of Async
function(err) {
if (err) return next(err);
res.redirect('/forgotpassword');
});
});
From async.waterfall documentation
Runs an array of functions in series, each passing their results to
the next in the array. However, if any of the functions pass an error
to the callback, the next function is not executed and the main
callback is immediately called with the error.
So its exactly the same job as Promise.then do, just chain your promises.
crypto.randomBytes(20)
.then( function (buf) {
var token = buf.toString('hex');
return token;
})
.then( function(token) {
return Model.User.findOne({where: {'email' : req.body.email}});
})
.then(function (user) {
if(!user){
// throw no user found error
}
return Model.User.create();
})
.catch( function(err) {
// error handling
// catch no user found error and show flash message
});
You have to have single catch in the end of promises chain, and then should not to be inside of another .then function. I can suggest to read this article - We have a problem with promises.
I'm working on my private project using Evernote API and node.js(express.js)
I successfully got an requestToken and accessToken using jsOAuth Module and I got the note guid from noteMetaData too.
however, when I use
noteStore.getNote
function, It continuously fails.
with this errormessage :
Error in NodeBinaryHttpTransport.flush: Binary protocol does not support synchronous calls
however I already inserted my callback function.
I checked API Docs but not specific doc for javascript. Arguments are different. I checked what kind of arguments does noteStore.getNote wants by logging Function.length, but it was 0.
here is my code.
I use express and router is require('express').Router();
gb is global object that contains my developer token and secret.
router.get('/users',function (request,response){
var parsedUrl = url.parse(request.url);
console.log('search is:'+parsedUrl.search)
client.getAccessToken(
gb.oauthToken,
gb.oauthSecret,
getOauthVerifier(parsedUrl.search),
function(error, oauthAccessToken, oauthAccessTokenSecret, results) {
if(error) {
console.log("error\n\n\n");
console.log(error);
}
else {
console.log('successfully get an access token.');
var accessedClient = new Evernote.Client({
token: oauthAccessToken,
sandbox:true
})
var noteStore = accessedClient.getNoteStore("https://sandbox.evernote.com/edam/note/");
noteStore.listNotebooks(function(err, notebook){
var filter = new Evernote.NoteFilter();
filter.notebookGuid = notebook[0].guid;
console.log('filter is..',filter);
var resultSpec = new Evernote.NotesMetadataResultSpec();
resultSpec.includeTitle = true;
resultSpec.includeContentLength = true;
resultSpec.includeCreated = true;
resultSpec.includeAttributes = true;
noteStore.findNotesMetadata(filter, 0, 100, resultSpec, function(err, notesMeta) {
if (err) {
console.error('err',err);
}
else {
console.log("Found "+notesMeta.notes.length+" notes in your default notebook . . .")
for (var i in notesMeta.notes) {
var noteGuid = notesMeta.notes[i]['guid'];
var note = noteStore.getNote(
noteGuid,{
withContent: true
},
function(err, results){
if(err) return console.error("Error")
console.log(results);
response.end();
})
}
}
});
})
}
);
})
It was a stupid question. I found a solution.
All I Needed to do was sticking to the parameter guided in the API docs.
noteGuid,{
var note = noteStore.getNote( true, true, true,true
},
function(err, results){
if(err)
return console.error("Error")
console.log(results);
response.end();
})
Background:
I must create or update an document based on post request that I have zero control over. I'm calling the function updateOrCreate()
Question:
How can I properly find a document by an field called nuid without using _id in mongo/mongoose
example payload:
curl -H "Content-Type: application/json" -X POST -d '{"participant":{"nuid":"98ASDF988SDF89SDF89989SDF9898"}}' http://localhost:9000/api/things
thing.controller:
exports.updateOrCreate = function(req, res) {
//Thing.findByNuid() will not work but it will explain what i'm trying to accomplish
/**
Thing.findByNuid(req.body.participant.nuid, function (err, thing) {
if (err) { return handleError(res, err); }
if(!thing) {
Thing.create(req.body.participant, function(err, thing) {
if(err) { return handleError(res, err); }
});
}
var updated = _.merge(thing, req.body.participant);
updated.save(function (err) {
if (err) { return handleError(res, err); }
});
});
**/
//this block will fetch all the things that have nuids but that seems really heavy and awful practice
Thing.find({'nuid':req.body.participant.nuid}, function(err, thing){
console.log(thing);
});
// This block is here to communicate this will create a new thing as expected.
Thing.create(req.body.participant, function(err, thing) {
if(err) { return handleError(res, err); }
});
}
Schema
var ThingSchema = new Schema({
nuid: String
});
UPDATE:
var query = {"nuid": req.body.participant.nuid};
var update = {nuid: 'heyyy'};
Thing.findOneAndUpdate(
query,
update,
{upsert: true},
function(err, thing){
console.log(thing, "thing");
console.log(err, "err");
}
);
I would use findOneAndUpdate first and then based on the result do an insert. findOneAndUpdate use mongoDB findAndModify command.
You should also look at new & upsert options of it which would create a document if not found.