This is my code:
const aws = require('aws-sdk')
const ses = new aws.SES()
// const myEmail = process.env.EMAIL
// const myDomain = process.env.DOMAIN
const getParamsFromUrl = require('./getParamsFromUrl')
module.exports = (options) => {
const { myEmail, myDomain } = options
function generateResponse (code, payload) {
return {
statusCode: code,
headers: {
'Access-Control-Allow-Origin': myDomain,
'Access-Control-Allow-Headers': 'x-requested-with',
'Access-Control-Allow-Credentials': true
},
body: JSON.stringify(payload)
}
}
function generateError (code, err) {
console.log("hi"+ err)
return {
statusCode: code,
headers: {
'Access-Control-Allow-Origin': myDomain,
'Access-Control-Allow-Headers': 'x-requested-with',
'Access-Control-Allow-Credentials': true
},
body: JSON.stringify(err.message)
}
}
function generateEmailParams (body) {
const { email, name, content } = JSON.parse(body)
console.log(email, name, content)
if (!(email && name && content)) {
throw new Error('Missing parameters! Make sure to add parameters \'email\', \'name\', \'content\'.')
}
return {
Source: myEmail,
Destination: { ToAddresses: [myEmail] },
ReplyToAddresses: [email],
Message: {
Body: {
Text: {
Charset: 'UTF-8',
Data: `Message sent from email ${email} by ${name} \nContent: ${content}`
}
},
Subject: {
Charset: 'UTF-8',
Data: `You received a message from ${myDomain}!`
}
}
}
}
async function sendJSON (event) {
try {
const emailParams = generateEmailParams(event.body)
const data = await ses.sendEmail(emailParams).promise()
return generateResponse(200, data)
} catch (err) {
return generateError(500, err)
}
}
return sendJSON
}
/////handler.js
const options = {
myEmail: process.env.EMAIL,
myDomain: process.env.DOMAIN
}
const { sendJSON, sendFormEncoded } = require('./lambdaMailer')(options)
module.exports.sendJSON = sendJSON
I have defined values on node_env and email in secrets.json file. After running a curl link I am getting the error as:
curl --header "Content-Type: application/json" --request POST --data "{\"source\":\"zyx#gmail.com\",\"destination\":\"abc#gmail.com\",\"name\":\"xyz\",\"subject\":\"Hey!\",\"message\":\"Hey!\"}" https://lyt7frokj4.execute-api.us-east-1.amazonaws.com/dev/email/send
"Missing required key 'Source' in params"
I think it maybe the case-sensitivity issue. According to the docs, there should be Source, not source:
Source: 'STRING_VALUE', /* required */
Related
I have this cloud function that append a line to a Google Spreadsheet:
function addLine(req, res) {
res.set("Access-Control-Allow-Origin", "*");
if (req.method === "OPTIONS") {
res.set("Access-Control-Allow-Methods", "POST");
res.set("Access-Control-Allow-Headers", "Content-Type");
res.set("Access-Control-Max-Age", "3600");
return res.status(204).send("");
}
const isReqValid = validateReq(req);
if (!isReqValid) return res.send("Not valid request!"); // <--
const { body } = req;
const isBodyValid = validateData(body);
if (!isBodyValid) return res.send("Not valid payload!"); // <--
return appendData(body)
.then(() => res.send("Added line"))
.catch((err) => {
res.send("Generic error!");
});
}
function validateReq(req) {
if (req.method !== "POST") return false;
return true;
}
function validateData(data) {
// do something and return true or false
}
async function appendData(data) {
const client = await auth.getClient();
return sheets.spreadsheets.values.append(
{
spreadsheetId: ...,
auth: client,
range: "A1:B",
valueInputOption: "RAW",
resource: { values: [data] },
},
);
}
I use it in this way:
async collaborate(data: CollaborateDatum) {
await post('...cloudfunctions.net/addLine', data)
}
async function post(url, data) {
return fetch(url, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
}
How can I "read" the errors Not valid request! and Not valid payload!? Because if I tried to append a line with not valid data, I get status code 200 but in Chrome Dev Tools -> Network -> Response, the response is Not valid payload! but I don't know how to catch this error.
Thanks a lot!
You should be able to get any response text that's passed back like this:
let responseText = await (await post('...cloudfunctions.net/addLine', data)).text();
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 2 years ago.
I am trying to do something that I just can't wrap my head around, I have tried doing something with promises but still get stuck. I tried reading through this and still am stuck How do I return the response from an asynchronous call?
What I need to do is run the following code and get the body outside of the req, so I can check if it is succesful and send a response in a lambda with a 200 and a message from the body. I don't want to return the 200 status inside the function because I need to also check if a fetch request is succesful before sending the 200 status with a body back.
Hopefully someone can help with this
let statusTrue
const req = https.request(options, function(res) {
res.setEncoding("utf8")
res.on("data", function(body) {
console.log(`Body: ${body}`)
statusTrue = body.status
})
})
if (statusTrue) {
return {
statusCode: 200,
headers: {
"Access-Control-Allow-Origin": "*",
},
body: JSON.stringify({ email: true }),
}
} else {
return {
statusCode: 200,
headers: {
"Access-Control-Allow-Origin": "*",
},
body: JSON.stringify({ email: false }),
}
}
Update Heres my code with oswins answer, the full code for context. Right now the function fires after the handler finishes so I never am able to send a proper response back.
const sgMail = require("#sendgrid/mail")
require("dotenv").config()
const { SENDGRID_API_KEY, SENDGRID_TO_EMAIL } = process.env
const URL = require("url")
const https = require("https")
const fetch = require("node-fetch")
exports.handler = async (event, context) => {
try {
//console.log(JSON.parse(event.body))
//*** send off to google to verify captcha */
const body = JSON.parse(event.body)
let secret = "fdsf"
const fetchUrl = `https://www.google.com/recaptcha/api/siteverify?secret=${secret}&response=${body.captcha}&remoteip=${event.headers["client-ip"]}`
let isItTrue
await fetch(fetchUrl, {
method: "POST",
body: JSON.stringify({ message: "hello world" }),
headers: { "Content-Type": "application/json" },
})
.then(response => response.json())
.then(data => {
isItTrue = data.success
})
.catch(error => {
console.error("Error:", error)
})
//console.log(isItTrue)
//*** end */
//*** Running Form Sends Now if Captcha Valid */
if (isItTrue) {
//*** Zapier Send */
const webhook_url = URL.parse(
"https://hooks.zapier.com/hooks/catch/fds"
)
const options = {
hostname: webhook_url.hostname,
path: webhook_url.pathname,
method: "POST",
headers: { "Content-Type": "application/json" },
}
// Set up webhook request
const req = https.request(options, function(res) {
res.setEncoding("utf8")
res.on("data", function(body) {
console.log(`Body: ${body}`)
sendResponse(body.status)
})
})
// Handle webhook request error
req.on("error", function(e) {
const errorMessage = `[ERROR] Problem with request: ${e.message}`
console.log(errorMessage)
callback(e.message, {
statusCode: 400,
body: errorMessage,
})
})
// Send form data to webhook request and end request
req.end(JSON.stringify(body.data))
//*** End */
//console.log(zapierStatus)
const sendResponse = statusTrue => {
if (statusTrue === "success") {
return {
statusCode: 200,
headers: {
"Access-Control-Allow-Origin": "*",
},
body: JSON.stringify({ email: true }),
}
} else {
return {
statusCode: 200,
headers: {
"Access-Control-Allow-Origin": "*",
},
body: JSON.stringify({ email: false }),
}
}
}
} else {
return {
statusCode: 200,
headers: {
"Access-Control-Allow-Origin": "*",
},
body: JSON.stringify({ captcha: false }),
}
}
//*** end */
} catch (err) {
return { statusCode: 500, body: err.toString() }
}
}
Maybe wrapping statements outside the function into another function will help ;-)
const req = https.request(options, function (res) {
res.setEncoding("utf8")
res.on("data", function (body) {
console.log(`Body: ${body}`)
statusTrue = body.status
sendResponse(statusTrue)
})
})
function sendResponse(statusTrue) {
if (statusTrue) {
return {
statusCode: 200,
headers: {
"Access-Control-Allow-Origin": "*",
},
body: JSON.stringify({ email: true }),
}
} else {
return {
statusCode: 200,
headers: {
"Access-Control-Allow-Origin": "*",
},
body: JSON.stringify({ email: false }),
}
}
}
You can wrap your https.request call in a promise like this:
const makeRequest = function(options) {
return new Promise(function(resolve, reject) {
const req = https.request(options, function(res) {
res.setEncoding("utf8")
res.on("error", reject)
res.on("data", function(body) {
console.log(`Body: ${body}`)
resolve(body.status)
})
})
}
Then later you can do:
makeRequest({/*options*/})
.then(function(statusTrue) {
if (statusTrue) {
return {
statusCode: 200,
headers: {
"Access-Control-Allow-Origin": "*",
},
body: JSON.stringify({ email: true }),
}
} else {
return {
statusCode: 200,
headers: {
"Access-Control-Allow-Origin": "*",
},
body: JSON.stringify({ email: false }),
}
}
})
.then(function(ret) {
console.log(ret)
})
.catch(function(err) {
/* handle error */
})
I'm starting with Azure Functions. I have the following code:
module.exports = function (context, req)
{
context.log('JavaScript HTTP trigger function processed a request.');
context.log(context.req.body.videoId)
if (context.req.body.videoId =! null)
{
context.log('inicia a obtener comentarios')
const fetchComments = require('youtube-comments-task')
fetchComments(req.body.videoId)
.fork(e => context.log('ERROR', e), p => {
context.log('comments', p.comments)
})
context.res = { body: fetchComments.comments }
}
else {
context.res = {
status: 400,
body: "Please pass a videoId on the query string or in the request body"
};
}
context.done();
};
How can I return the JSON that fetchComments returns?
Move assigning context.res and call to context.done to promise callback. Set Content-Type to application/json in the headers. Based on your code, something like
if (context.req.body.videoId =! null) {
context.log('inicia a obtener comentarios')
const fetchComments = require('youtube-comments-task')
fetchComments(req.body.videoId)
.fork(e => context.log('ERROR', e), p => {
context.log('comments', p.comments);
context.res = {
headers: { 'Content-Type': 'application/json' },
body: p.comments
};
context.done();
});
}
else {
context.res = {
status: 400,
body: "Please pass a videoId on the query string or in the request body"
};
context.done();
}
I am trying the gmail apis. I've done the auth. Now I want to create a draft. But I am getting this error
{ error:
I20161220-15:53:43.486(4)? { errors: [Object],
I20161220-15:53:43.487(4)? code: 400,
I20161220-15:53:43.488(4)? message: 'Media type \'application/octet-stream\' is not supported. Valid media types: [message/rfc822]' } } }
Gmail api require base64 string with rfc822 standard. I am not sure of any good way to convert a string to rfc822. How do I do that?
I am using meteor for my app and here is my code.
import { Meteor } from 'meteor/meteor'
import { HTTP } from 'meteor/http'
Meteor.startup(() => {
// Meteor.call('createDraft')
Meteor.methods({
'createDraft': function () {
console.log(this.userId)
const user = Meteor.users.findOne(this.userId)
const email = user.services.google.email
console.log(email)
const token = user.services.google.accessToken
const dataObject = {
message: {
raw: CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse('dddd'))
},
headers: {
Authorization: `Bearer ${token}`
}
}
HTTP.post(`https://www.googleapis.com/upload/gmail/v1/users/${email}/drafts`, dataObject, (error, result) => {
if (error) {
console.log('err', error)
}
if (result) {
console.log('res', result)
}
})
}
})
})
Base64 encode the message and replace all + with -, replace all / with _, and remove the trailing = to make it URL-safe:
const rawMessage = btoa(
"From: sender#gmail.com\r\n" +
"To: receiver#gmail.com\r\n" +
"Subject: Subject Text\r\n\r\n" +
"The message text goes here"
).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')
const dataObject = {
message: {
raw: rawMessage
},
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
}
};
I just needed to send content type as message/rfc822. Here is the working code. Note that the raw message has something wrong in ts because the draft that is created has empty content. But the draft itself is created successfully.
import { Meteor } from 'meteor/meteor'
import { HTTP } from 'meteor/http'
Meteor.startup(() => {
// Meteor.call('createDraft')
Meteor.methods({
'createDraft': function () {
console.log(this.userId)
// CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse('dddd'))
const user = Meteor.users.findOne(this.userId)
const email = user.services.google.email
console.log(email)
const token = user.services.google.accessToken
const rawMessage = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(
'From: sender#gmail.com\r\n' +
'To: receiver#gmail.com\r\n' +
'Subject: Subject Text\r\n\r\n' +
'The message text goes here'
)).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')
const dataObject = {
message: {
raw: rawMessage
},
headers: {
'Content-Type': 'message/rfc822',
Authorization: `Bearer ${token}`
}
}
HTTP.post(`https://www.googleapis.com/upload/gmail/v1/users/${email}/drafts`, dataObject, (error, result) => {
if (error) {
console.log('err', error)
}
if (result) {
console.log('res', result)
}
})
}
})
})
I need my Wit.ai chat bot to respond to certain messages with images, and since I've refactored my code to match the latest messenger example in the node-wit SDK I can't figure out how to do so.
Previously this FB message function worked for me:
var newMessage = function (recipientId, msg, atts, cb) {
var opts = {
form: {
recipient: {
id: recipientId
},
}
}
if (atts) {
var message = {
attachment: {
"type": "image",
"payload": {
"url": msg
}
}
}
} else {
var message = {
text: msg
}
}
opts.form.message = message
newRequest(opts, function (err, resp, data) {
if (cb) {
cb(err || data.error && data.error.message, data)
}
})
}
Now I've updated to the node-wit SDK messenger example:
const fbMessage = (id, text) => {
const body = JSON.stringify({
recipient: { id },
message: { text },
});
const qs = 'access_token=' + encodeURIComponent(FB_PAGE_TOKEN);
return fetch('https://graph.facebook.com/me/messages?' + qs, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body,
})
.then(rsp => rsp.json())
.then(json => {
if (json.error && json.error.message) {
throw new Error(json.error.message);
}
return json;
});
};
Which I've modified like this to try and make image replies work:
const fbMessage = (id, text, atts) => {
if (atts) {
var body = {
attachment: {
"type": "image",
"payload": {
"url": { text }
}
},
};
} else {
var body = JSON.stringify({
recipient: { id },
message: { text },
});
}
const qs = 'access_token=' + encodeURIComponent(FB_PAGE_TOKEN);
return fetch('https://graph.facebook.com/me/messages?' + qs, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body,
})
.then(rsp => rsp.json())
.then(json => {
if (json.error && json.error.message) {
throw new Error(json.error.message);
}
return json;
});
};
Text messages are being sent as normal, but when I try to send an image attachment, my image url references are just being sent as strings.
The FB Messenger Send API reference is here
Any help would be greatly appreciated!
Got it working with this:
const fbMessage = (id, text) => {
var x = text.substring(0,4);
if (x == 'http') {
var body = JSON.stringify({
recipient: { id },
message: {
attachment: {
"type": "image",
"payload": {
"url": text
}
}
},
});
} else {
var body = JSON.stringify({
recipient: { id },
message: { text },
});
}
const qs = 'access_token=' + encodeURIComponent(FB_PAGE_TOKEN);
return fetch('https://graph.facebook.com/me/messages?' + qs, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body,
})
.then(rsp => rsp.json())
.then(json => {
if (json.error && json.error.message) {
throw new Error(json.error.message);
}
return json;
});
};
*NB - This obviously won't work if you plan on sending text replies that are just urls i.e. 'http://example.com'. To get around this you can put any symbol in front of the url address in your message like so: '> http://example.com' and links will work fine.