I am trying to send mail using gmail api in my meteor application, which returning below error,
Error in calendar insert: Error: failed [400] { "error": { "errors": [ { "domain": "global", "reason": "invalidArgument", "message": "'raw' RFC822 payload message string or uploading message via /upload/* URL required" } ], "code": 400, "message": "'raw' RFC822 payload message string or uploading message via /upload/* URL required" } }
I have tried the below,
"sendGmail": function(str) {
this.unblock();
var url = "https://www.googleapis.com/gmail/v1/users/me/messages/send";
var encodedMail = new Buffer(str).toString("base64").replace(/\+/g, '-').replace(/\//g, '_');
try {
Meteor.http.post(url, {
'headers' : {
'Authorization': "Bearer " + Meteor.user().services.google.accessToken,
'Content-Type': 'application/json'
},
'body': JSON.stringify({
"raw": encodedMail
})
});
} catch(e){
console.log("Error in calendar insert: " + e);
} finally {
return true;
}
}
passing the below string value as argument:
var str = "Content-Type: text/plain; charset=\"UTF-8\"\n" +
"MIME-Version: 1.0\n" +
"Content-Transfer-Encoding: 7bit\n" +
"to: arunmail2u#gmail.com\n" +
"from: arunsugan08#gmail.com\n" +
"subject: Meteor test mail\n\n" +
"Hi, this is test mail from meteor application";
Meteor.call('sendGmail', str);
A body string is given as content, not body. Check the documentation.
content - Takes a plain String and sets it on the HTTP request body.
Related
I am trying to send an email using POST request with just Node standard modules in NodeJS v8.10 via GMail API.
The sample code is given below.
I am getting an error:
{
"error": {
"errors": [
{
"domain": "global",
"reason": "invalidArgument",
"message": "Recipient address required"
}
],
"code": 400,
"message": "Recipient address required"
}
}
It says recipient address required but according to what I think, (I may be wrong), my base64url conversion is proper since I checked it in Google API Explorer but my problem is in passing the data, I am doing it properly as told in the guide i.e. inside body with 'raw' key but it still does not work and this is where my problem must be. Maybe I am missing something, maybe I do not know the proper structure.
Yes, there are multiple posts regarding this but none of them provided solution.
I referred the guide on https://developers.google.com/gmail/api/v1/reference/users/messages/send
but the given example is of with the use of client library.
I tried everything, passing the 'raw' with base64url encoded data into write function of the request, passing it as a data parameter in options, passing it through body parameters in options, everything I can think of.
Am I missing something? Where am I going wrong?
I am a newbie in nodejs so please explain and if possible, an example structure of solution would be most welcome.
Base64url produced is working fine, I guess. I copied the string produced by conversion and tried it at https://developers.google.com/gmail/api/v1/reference/users/messages/send?apix=true
It works fine and sends me the mail but it does not work on my code.
var email = (
"Content-Type: text/plain; charset=\"UTF-8\"\n" +
"Content-length: 5000\n" +
"MIME-Version: 1.0\n" +
"Content-Transfer-Encoding: message/rfc2822\n" +
"to: something#something.com\n" +
"from: \"Some Name\" <something#gmail.com>\n" +
"subject: Hello world\n\n" +
"The actual message text goes here"
);
async function sendMail(token,resp) {
return new Promise((resolve,reject) => {
var base64EncodedEmail = Buffer.from(email).toString('base64');
var base64urlEncodedEmail = base64EncodedEmail.replace(/\+/g, '-').replace(/\//g, '_');
var params = {
userId: 'me',
resource: {
'raw': base64urlEncodedEmail
}
};
var body2 = {
"raw": base64urlEncodedEmail,
}
var options = {
hostname: 'www.googleapis.com',
path:'/upload/gmail/v1/users/me/messages/send',
headers: {
'Authorization':'Bearer '+token,
'Content-Type':'message/rfc822',
},
body: {
"raw": base64urlEncodedEmail,
'resource': {
'raw': base64urlEncodedEmail,
}
},
data: JSON.stringify({
'raw': base64urlEncodedEmail,
'resource': {
'raw': base64urlEncodedEmail,
}
}),
message: {
'raw': base64urlEncodedEmail,
},
payload: {
"raw": base64urlEncodedEmail, //this is me trying everything I can think of
},
// body: raw,
// }
userId: 'me',
// resource: {
// 'raw': base64urlEncodedEmail
// },
method: 'POST',
};
var id='';
console.log(base64urlEncodedEmail);
const req = https.request(options, (res) => {
var body = '';
res.on('data', (d) => {
body += d;
});
res.on('end', () => {
var parsed = body;
console.log(parsed);
})
});
req.on('error', (e) => {
console.error(e);
});
req.write(JSON.stringify(body2));
req.end();
});
};
Thank you for your time and answers.
I found the solution.
It says everywhere to convert the rfc822 formatted string to Base64url to send and attach it to 'raw' property in the POST body but I don't know what has changed and you don't need to do that anymore.
First things first, the Content-Type in header should be
'Content-Type':'message/rfc822'
Now, since we are specifying the content-type as message/rfc822, we don't need to convert the data we want to send into base64url format anymore, I guess (Not sure of the reason because I have a very little knowledge about this.)
Only passing "To: something#any.com" as body works.
Here is the complete code of how to get it done for someone who is struggling for the same problem.
function makeBody(to, from, subject, message) {
let str = [
"to: ", to, "\n",
"from: ", from, "\n",
"subject: ", subject, "\n\n",
message,
].join('');
return str;
}
async function getIdAsync(token,resp) {
return new Promise((resolve,reject) => {
let raw = makeBody("something#gmail.com", "something#gmail.com", "Subject Here", "blah blah blah");
var options = {
hostname: 'www.googleapis.com',
path:'/upload/gmail/v1/users/me/messages/send',
headers: {
'Authorization':'Bearer '+token,
'Content-Type':'message/rfc822'
},
method: 'POST',
};
const req = https.request(options, (res) => {
var body = '';
res.on('data', (d) => {
body += d;
});
res.on('end', () => {
var parsed = body;
console.log(parsed);
})
});
req.on('error', (e) => {
console.error(e);
});
req.write(raw);
req.end();
});
};
Happy Coding :)
I have followed gmail api for sending email. I am getting error as:
"message": "400 - \"{\n \\"error\\": {\n \\"errors\\": [\n {\n \\"domain\\": \\"global\\",\n \\"reason\\": \\"invalidArgument\\",\n \\"message\\": \\"'raw' RFC822 payload message string or uploading message via /upload/* URL required\\"\n }\n ],\n \\"code\\": 400,\n \\"message\\": \\"'raw' RFC822 payload message string or uploading message via /upload/* URL required\\"\n }\n}\n\""
Here is the piece of code I have written for sending mail using gmail api with node.js. Help me out to resolve the issue.
router.post('/composeMail', async (req, res, next) => {
function makeBody(to, from, subject, message) {
let str = ["Content-Type: text/plain; charset=\"UTF-8\"\n",
"Content-length: 5000\n",
"Content-Transfer-Encoding: message/rfc822\n",
"to: ", to,"\n",
"from: ", from,"\n",
"subject: ", subject,"\n\n",
message
].join('');
console.log("String: ", str);
// let encodedMail = new Buffer(str).toString("base64").replace(/\+/g, '-').replace(/\//g, '_');
let encodedMail = btoa(str).replace(/\+/g, '-').replace(/\//g, '_');
return encodedMail;
}
let raw = makeBody("dinesh.kumar#gmail.com", "dinesh.kumar#gmail.com", "Test mail", "Everything is fine");
let obj = {};
obj.raw = raw;
let body = JSON.stringify(obj);
let option = {
url: "https://www.googleapis.com/gmail/v1/users/userId/messages/send",
method: 'POST',
headers: {
'Authorization': `Bearer ${req.query.access_token}`
},
qs: {
userId: 'me'
},
body: body
};
await request(option).then(body => {
return res.apiOk(body);
}).catch(err => {
return res.apiError(err);
})
});
You want to send an email using Gmail API by the request module.
If my understanding is correct, how about this modification? I think that there are several answers. So please think of this as one of them.
Modification points:
Please use https://www.googleapis.com/upload/gmail/v1/users/userId/messages/send as the endpoint.
Use the value as a string.
Add 'Content-Type': 'message/rfc822' to the headers.
Modified script:
Please modify makeBody() as follows.
function makeBody(to, from, subject, message) {
let str = [
"to: ", to, "\n",
"from: ", from, "\n",
"subject: ", subject, "\n\n",
message,
].join('');
return str;
}
Please modify option as follows.
let raw = makeBody("dinesh.kumar#gmail.com", "dinesh.kumar#gmail.com", "Test mail", "Everything is fine");
const userId = 'me'; // Please modify this for your situation.
let option = {
url: "https://www.googleapis.com/upload/gmail/v1/users/" + userId + "/messages/send",
method: 'POST',
headers: {
'Authorization': `Bearer ${req.query.access_token}`,
'Content-Type': 'message/rfc822',
},
body: raw,
};
Note:
This modified script supposes that Gmail API is enabled at API console and the required scope for sending emails is included in the scopes of access token.
Reference:
Users.messages: send
In my environment, I could confirm that this modified script worked fine. But if this was not what you want, I'm sorry.
In order to access the google maps tracks API, i have access token sent by Google’s OAuth 2.0 Authorization Server. Now i need to send this access token with every http request i made to google map tracks API. Where should i put this access token in http request ?. I tried to put in headers as 'X-Access-Token' : 'myaccesstoken', but am getting response from api as below.
body: '{\n "error": {\n "errors": [\n {\n "domain": "global",\n "reason": "required",\n "message": "Login Required",\n "locationType": "header",\n "location": "Authorization"\n }\n ],\n "code": 401,\n "message": "Login Required"\n }\n}\n' }
I referred google map tracks API official documentation https://developers.google.com/maps/documentation/tracks/auth, but unable to solve this error.
below is the code am using to authenticate with Google’s OAuth 2.0 Authorization Server, and code to send request to google map tracks API.
var jwt = new googleapis.auth.JWT(client_email, keyFile, null, scopes, null);
jwt.authorize(function(jwtErr, tokens){
if(jwtErr){
return;
}
else{
headers = {
'content-type' : 'application/json',
'X-Access-Token' : tokens.access_token
};
options = {
url : 'https://www.googleapis.com/tracks/v1/geofences/list',
headers : headers,
method : 'POST',
};
request(options, function(error, response, body){
if(error){
return;
}
console.log(response);
});
}
});
Found the solution, access token should be put in
header : { 'Authorization' : 'myaccesstoken' },
so after changes, code will look like below.
var jwt = new googleapis.auth.JWT(client_email, keyFile, null, scopes, null);
jwt.authorize(function(jwtErr, tokens){
if(jwtErr){
return;
}
else{
headers = {
'content-type' : 'application/json;charset=utf-8',
'content-length': Buffer.byteLength(data),
**'Authorization' : tokens.access_token**
};
options = {
host: 'www.googleapis.com',
path: '/tracks/v1/entities/create',
headers : headers,
method : 'POST',
};
var post_req = https.request(options, function(res){
res.setEncoding('utf8');
res.on('data', function (chunk) {
console.log('Response: ' + chunk);
});
});
post_req.on('error', function(error_msg){
console.log(error_msg);
});
post_req.write(data);
post_req.end();
}
});
I do have checked validity of my access_token at https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=mytoken
which is replying as follows,
{
"issued_to": "myservice_account_emailid",
"audience": "myservice_account_emailid",
"scope": "https://www.googleapis.com/auth/tracks",
"expires_in": 1300,
"access_type": "offline"
}
which means still my token is valid, but if i make request to google tracks api from my application, using the same access_token, am getting response as below ,
Response: {
"error": {
"errors": [
{
"domain": "global",
"reason": "authError",
"message": "Invalid Credentials",
"locationType": "header",
"location": "Authorization"
}
],
"code": 401,
"message": "Invalid Credentials"
}
}
Even i created new project in google developer console, and created new service account , and used the new credentials in application, but still getting same response. what could be the reason?
Found my self.
My error prone code is as below ,
var jwt = new googleapis.auth.JWT(client_email, keyFile, null, scopes, null);
jwt.authorize(function(jwtErr, tokens){
if(jwtErr){
return;
}
else{
headers = {
'content-type' : 'application/json;charset=utf-8',
'content-length': Buffer.byteLength(data),
'Authorization' : tokens.access_token
};
options = {
host: 'www.googleapis.com',
path: '/tracks/v1/entities/create',
headers : headers,
method : 'POST',
};
var post_req = https.request(options, function(res){
res.setEncoding('utf8');
res.on('data', function (chunk) {
console.log('Response: ' + chunk);
});
});
post_req.on('error', function(error_msg){
console.log(error_msg);
});
post_req.write(data);
post_req.end();
}
});
The problem is in header object, 'Authorization' field is set to only access.token value, along with that we must specify token_type value also.
so header must be like below.
headers = {
'content-type' : 'application/json;charset=utf-8',
'content-length': Buffer.byteLength(data),
'Authorization' : tokens.token_type+' '+tokens.access_token
};
and now it solves 401 Invalid Credentials error.
I want to send an email through Google API without the unnecessary OAUTH2 parameters. I only have the access_token and the refresh_token of that user.
How can I send an email through Gmail API through a basic POST request in NodeJS, with Request npm plugin?
abraham is correct, but I just thought I'd give you an example.
var request = require('request');
server.listen(3000, function () {
console.log('%s listening at %s', server.name, server.url);
// Base64-encode the mail and make it URL-safe
// (replace all "+" with "-" and all "/" with "_")
var encodedMail = new Buffer(
"Content-Type: text/plain; charset=\"UTF-8\"\n" +
"MIME-Version: 1.0\n" +
"Content-Transfer-Encoding: 7bit\n" +
"to: reciever#gmail.com\n" +
"from: sender#gmail.com\n" +
"subject: Subject Text\n\n" +
"The actual message text goes here"
).toString("base64").replace(/\+/g, '-').replace(/\//g, '_');
request({
method: "POST",
uri: "https://www.googleapis.com/gmail/v1/users/me/messages/send",
headers: {
"Authorization": "Bearer 'access_token'",
"Content-Type": "application/json"
},
body: JSON.stringify({
"raw": encodedMail
})
},
function(err, response, body) {
if(err){
console.log(err); // Failure
} else {
console.log(body); // Success!
}
});
});
Don't forget to change the reciever and sender email address for the example to work.
There are two methods for attaching OAuth2 access_tokens to a Google API request.
Using the access_token query parameter like this: ?access_token=oauth2-token
Using the HTTP Authorization header like this: Authorization: Bearer oauth2-token
The second one is prefered for POST requests so the raw HTTP request for sending an email would look something like this.
POST /gmail/v1/users/me/messages/send HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer oauth2Token
{"raw":"encodedMessage"}