javascript object in lambda function - node.js

I am working on AWS Lambda and creating method by using node.js.
I need an object like this:
[
{
"TeamName" : "Sales",
"2020-01-01": "90",
"2020-01-02": "92",
"2020-01-03": "95",
"2020-01-04": "90",
"2020-01-05": "56",
"2020-01-06": "70",
"2020-01-07": "73"
},
]
but my current response is this:
[
{
"TeamName": "Billing",
"DateTime": "2020-06-13T00:00:00.000Z",
"Score": 9
},
{
"TeamName": "Billing",
"DateTime": "2020-06-13T00:00:00.000Z",
"Score": 9
},
{
"TeamName": "Billing",
"DateTime": "2020-06-11T00:00:00.000Z",
"Score": 5
},
]
Here is my Lambda method. I am not good at creating javascript object so please help me to make a response like this, Thanks.
exports.handler = (event, context, callback) => {
console.log('Events:',event);
let UserHierarchyGroupID = event['hierarchyGroupId'];
let team = [];
// allows for using callbacks as finish/error-handlers
context.callbackWaitsForEmptyEventLoop = false;
pool.getConnection(function(err, connection) {
if (err) throw err;
let sql = `SELECT date(Feedback.DateTime) as datetime,Feedback.Score,UserHierarchy.Layer5
FROM ctrData2.Feedback
LEFT OUTER JOIN ctrData2.CallDetail ON CallDetail.ContactId = Feedback.FeedbackID
LEFT OUTER JOIN ctrData2.UserTable ON UserTable.UserID = CallDetail.UserID
LEFT OUTER JOIN ctrData2.UserHierarchy ON UserTable.UserID = UserHierarchy.UserID
WHERE UserTable.UserHierarchyGroupID=?`;
let field = [UserHierarchyGroupID];
connection.query(sql,field, function (err, result, fields) {
if (err) throw err;
// console.log(result);
connection.release();
var date;
var score;
if(result.length>0){
result.forEach((item)=>{
team.push({
"TeamName": item.Layer5,
"DateTime": item.datetime,
"Score": item.Score
});
});
}else{
callback(null,{
status: 404,
Body: "Not found"
});
}
callback(null,team);
// FomratObjects(result,(formattedResponse)=>{
// // console.log(formattedResponse);
// callback(formattedResponse);
// });
});
});
};

Its doesn't look possible to create an object exactly like you mentioned but you can do this to assign value to every single date.
Hope it will be helpful.
function formatData(data){
var nObject = {};
data.forEach(d=>{
nObject[moment(d.datetime).format('MM-DD-YYYY')]=d.Score;
});
return nObject;
}

Related

Error when trying to purchase multiple items with one transaction

I have gotten the PayPal API to work with one item and I'm now trying to get it to work with a whole "shopping-cart". I have encountered an error that I don't know how to solve. I suspect it might have to do something with the payment-jsons total value that represents the total cost of the whole transaction. However I don't know what to do about it.
Here is the error:
Error: Response Status : 400
at IncomingMessage.<anonymous> (E:\Users\willi\Documents\Node\Store\node_modules\paypal-rest-sdk\lib\client.js:130:23)
at IncomingMessage.emit (events.js:327:22)
at endReadableNT (internal/streams/readable.js:1327:12)
at processTicksAndRejections (internal/process/task_queues.js:80:21) {
response: {
name: 'MALFORMED_REQUEST',
message: 'Incoming JSON request does not map to API request',
information_link: 'https://developer.paypal.com/webapps/developer/docs/api/#MALFORMED_REQUEST',
debug_id: '9e8898a463ee3',
httpStatusCode: 400
},
httpStatusCode: 400
}
And here is the code in question
const pay = (req, res) => {
async function f() {
items = [];
req_items = req.body.body
let itemsProcessed = 0
req_items.forEach(item => {
console.log(item.id)
const param = item.id
Item.find({ _id: param })
.then((result) => {
const item_body = {
"name": result[0].title,
"sku": "001",
"price": parseFloat(result[0].price),
"currency": "EUR",
"quantity": item.amount
}
items.push(item_body)
itemsProcessed = itemsProcessed + 1
})
.catch((err) => {
console.log(err)
})
})
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("done!"), 1000)
});
let result = await promise; // wait until the promise resolves (*)
console.log(items)
const create_payment_json = {
"intent": "sale",
"payer": {
"payment_method": "paypal"
},
"redirect_urls": {
"return_url": "http://localhost:3000/success",
"cancel_url": "http://localhost:3000/cancel"
},
"transactions": [{
"item_list": {
"items": [items]
},
"amount": {
"currency": "EUR",
"total": parseFloat(req.body.subtotal) // 25
},
"description": "Purcahsed from the Store"
}]
};
// console.log(req.body)
// console.log(create_payment_json.transactions[0])
paypal.payment.create(create_payment_json, function (error, payment) {
if (error) {
throw error;
} else {
for(let i = 0;i < payment.links.length;i++){
if(payment.links[i].rel === 'approval_url'){
res.redirect(payment.links[i].href);
}
}
}
});
}
f();
}
API deprecation notice
You are integrating the deprecated v1/payments PayPal API. You shouldn't be doing so for a new integration; the current API is v2/checkout/orders, documented here.
Typically you'll want to create two routes on your own server, 'Create Order' and 'Capture Order', which return their own JSON when called. Then you can pair those two routes with the following approval flow: https://developer.paypal.com/demo/checkout/#/pattern/server
But as for your problem, debugging an issue like this is much simpler if you simply log your request JSON to see what the problem with it is.
If you do so, you will see that the "items" array you are sending has an array inside of an array of only one item (the other array). That array shouldn't be there.
This seems the culprit:
"items": [items]
Here you decided to make an array, which was useful when "items" was a single item (no array). But when items is already an array, you shouldn't be putting the array into a new array -- the resulting JSON won't map to an API request, and PayPal will return an error.
What you should do is get rid of those brackets and ensure that at this point in the code execution, "items" is already an array (if it wasn't before).

Unable to write item(s) to DynamoDB table utilizing DocumentClient - Nodejs

I'm absolutely brand new to DynamoDb and I'm trying to simply write an object from a NodeJS Lambda. Based on what I've read and researched I should probably be using DocumentClient from the aws-sdk. I also found the following question here regarding issues with DocumentClient, but it doesn't seem to address my specific issue....which I can't really find/pinpoint unfortunately. I've set up a debugger to help with SAM local development, but it appears to be only providing some of the errors.
The code's implementation is shown here.
var params = {
TableName: "March-Madness-Teams",
Item: {
"Id": {"S": randstring.generate(9)},
"School":{"S": team_name},
"Seed": {"S": seed},
"ESPN_Id": {"S": espn_id}
}
}
console.log(JSON.stringify(params))
dynamodb.put(params, (error,data) => {
if (error) {
console.log("Error ", error)
} else {
console.log("Success! ", data)
}
})
Basically I'm scrubbing a website utilizing cheerio library and cherry picking values from the DOM and saving them into the json object shown below.
{
"TableName": "March-Madness-Teams",
"Item": {
"Id": {
"S": "ED311Oi3N"
},
"School": {
"S": "BAYLOR"
},
"Seed": {
"S": "1"
},
"ESPN_Id": {
"S": "239"
}
}
}
When I attempt to push this json object to Dynamo, I get errors says
Error MultipleValidationErrors: There were 2 validation errors:
* MissingRequiredParameter: Missing required key 'TableName' in params
* MissingRequiredParameter: Missing required key 'Item' in params
The above error is all good in well....I assume it didn't like the fact that I had wrapped those to keys in strings, so I removed the quotes and sent the following
{
TableName: "March-Madness-Teams",
Item: {
"Id": {
"S": "ED311Oi3N"
},
"School": {
"S": "BAYLOR"
},
"Seed": {
"S": "1"
},
"ESPN_Id": {
"S": "239"
}
}
}
However, when I do that...I kind of get nothing.
Here is a larger code snippet.
return new Promise((resolve,reject) => {
axios.get('http://www.espn.com/mens-college-basketball/bracketology')
.then(html => {
const dynamodb = new aws.DynamoDB.DocumentClient()
let $ = cheerio.load(html.data)
$('.region').each(async function(index, element){
var preregion = $(element).children('h3,b').text()
var region = preregion.substr(0, preregion.indexOf('(') - 1)
$(element).find('a').each(async function(index2, element2){
var seed = $(element2).siblings('span.rank').text()
if (seed.length > 2){
seed = $(element2).siblings('span.rank').text().substring(0, 2)
}
var espn_id = $(element2).attr('href').split('/').slice(-2)[0]
var team_name = $(element2).text()
var params = {
TableName: "March-Madness-Teams",
Item: {
"Id": randstring.generate(9),
"School":team_name,
"Seed": seed,
"ESPN_Id": espn_id
}
}
console.log(JSON.stringify(params))
// dynamodb.put(params)
// .then(function(data) {
// console.log(`Success`, data)
// })
})
})
})
})
Can you try without the type?
Instead of
"School":{"S": team_name},
for example, use
"School": team_name,
From your code, I can see the mis promise on the dynamodb request. Try to change your lines :
dynamodb.put(params).then(function(data) {
console.log(`Success`, data)
})
to be :
dynamodb.put(params).promise().then(function(data) {
console.log(`Success`, data)
})
you can combine with await too :
await dynamodb.put(params).promise().then(function(data) {
console.log(`Success`, data)
})
exports.lambdaHandler = async (event, context) => {
const html = await axios.get('http://www.espn.com/mens-college-basketball/bracketology')
let $ = cheerio.load(html.data)
const schools = buildCompleteSchoolObject(html, $)
try {
await writeSchoolsToDynamo(schools)
return { statusCode: 200 }
} catch (error) {
return { statusCode: 400, message: error.message }
}
}
const writeSchoolsToDynamo = async (schools) => {
const promises = schools.map(async school => {
await dynamodb.put(school).promise()
})
await Promise.all(promises)
}
const buildCompleteSchoolObject = (html, $) => {
const schools = []
$('.region').each(loopThroughSubRegions(schools, $))
return schools
}
const loopThroughSubRegions = (schools, $) => {
return (index, element) => {
var preregion = $(element).children('h3,b').text()
var region = preregion.substr(0, preregion.indexOf('(') - 1)
$(element).find('a').each(populateSchoolObjects(schools, $))
}
}
const populateSchoolObjects = (schools, $) => {
return (index, element) => {
var seed = $(element).siblings('span.rank').text()
if (seed.length > 2) {
seed = $(element).siblings('span.rank').text().substring(0, 2)
}
var espn_id = $(element).attr('href').split('/').slice(-2)[0]
var team_name = $(element).text()
schools.push({
TableName: "March-Madness-Teams",
Item: {
"Id": randstring.generate(9),
"School": team_name,
"Seed": seed,
"ESPN_Id": espn_id
}
})
}
}
I know this is drastically different from what I started with but I did some more digging and kind of kind of worked to this...I'm not sure if this is the best way, but I seemed to get it to work...Let me know if something should change!
Oh I understand what you want.
Maybe you can see the code above works, but there is one concept you have to improve here about async - await and promise especially on lambda function.
I have some notes here from your code above, maybe can be your consideration to improve your lambda :
Using await for every promise in lambda is not the best approach because we know the lambda time limitation. But sometimes we can do that for other case.
Maybe you can change the dynamodb.put method to be dynamodb.batchWriteItem :
The BatchWriteItem operation puts or deletes multiple items in one or more tables.
Or If you have to use dynamodb.put instead, try to get improve the code to be like so :
const writeSchoolsToDynamo = async (schools) => {
const promises = schools.map(school => {
dynamodb.put(school).promise()
})
return Promise.all(promises)
}

NodeJS: How to convert 'aggregation'(cursor) object to CSV and return a CSV response from a request using Express?

I want to convert the result from aggregate to a csv. In the code (or pseudocode) below the results of the aggregate are stored in variable cursor
I want to return a csv as a response ( res.send(csv) ). Do I have to use res.set('Content-Type', 'content-type: text/csv') ?
The code below is a mix of NodeJs and pseudocode. Function covert_to_csv indicates my ignorance about the subject.
const express = require('express')
const app = express();
app.get('/', (req, res) => {
let agg=[ /* a query in MongoDB */]
MongoClient.connect(URL,(err, client) => {
res.set('Content-Type', 'content-type: text/csv');
let collection = client.db('db').collection('col')
let cursor = collection.aggregate(agg)
let csv_file = covert_to_csv(cursor)
csv_file.toArray((error, result) => {
res.send(result);
});
});
app.listen(port, () => console.log(`listening on port ${port}!`));
Edit:
In my original code i am getting a response in json.I run the following code:
let agg=[ /* a query in MongoDB */]
let cursor = collection.aggregate(agg)
cursor.toArray((error, result) => {
if(error) {
return res.status(500).send(error);
res.send(result);
I am getting this response from Postman in JSON :
[
{
"Source": "entso-e",
"Dataset": "ActualTotalLoad",
"AreaName": "Germany",
"AreaTypeCode": "CTY",
"MapCode": "DE",
"ResolutionCode": "PT15M",
"Year": "2018",
"Month": "1",
"Day": "2",
"DateTimeUTC": "2018-01-02 00:00:00.0000000",
"ActualTotalLoadValue": "41412.38",
"UpdateTimeUTC": "2018-01-02 13:16:19.0000000"
},
{
"Source": "entso-e",
"Dataset": "ActualTotalLoad",
"AreaName": "Germany",
"AreaTypeCode": "CTY",
"MapCode": "DE",
"ResolutionCode": "PT15M",
"Year": "2018",
"Month": "1",
"Day": "2",
"DateTimeUTC": "2018-01-02 00:45:00.0000000",
"ActualTotalLoadValue": "40785.17",
"UpdateTimeUTC": "2018-01-02 13:16:19.0000000"
},
..... (more documents)
]
You can use a module like json2csv to do the job for you. Your example will look something like that
const { Parser } = require('json2csv');
app.get('/', function(req, res) {
let agg = [/* mongo aggregation */]
res.setHeader('Content-Type', 'text/csv');
res.setHeader('Content-Disposition', 'attachment; filename=\"' + 'download-' + Date.now() + '.csv\"');
let cursor = collection.aggregate(agg)
const fields = ['Source', 'Dataset', 'Areaname'];//all your field names
const json2csvParser = new Parser({ fields });
cursor.toArray((error, result) => {
res.send(json2csvParser.parse(result))
})
})
Try referring to https://www.npmjs.com/package/fast-csv and set keys as header option to true

First alexa skill

I am trying to develop my first Alexa skill using Node.js, and every time I try to test it I get "There was a problem with the requested skill's response".
I am trying create a random restaurant generator. Pretty simple its an array of restaurants, a random index is selected, and Alexa says the restaurant. I don't know where I went wrong I have uploaded my .json and .js files if anyone can help i'd really appreciate it.
index.js:
const Alexa = require('alexa-sdk');
const APP_ID = 'amzn1.ask.skill.9350e65b-fb41-48ce-9930-98b5156eb63c';
const handlers = {
'LaunchRequest': function () {
this.emit('randomRestaurantGeneratorIntent');
},
'randomRestaurantGeneratorIntent': function () {
var randomResturant;
var foodArray = ['IHOP', 'Dennys', 'burger king'];
randomResturant = foodArray[Math.floor(Math.random() * foodArray.length)];
this.response.speak(randomResturant);
this.emit(':responseReady');
},
'AMAZON.HelpIntent': function () {
const say = 'You can say what did I learn, or, you can say exit... How can I help you?';
this.response.speak(say).listen(say);
this.emit(':responseReady');
},
'AMAZON.CancelIntent': function () {
this.response.speak('Bye!');
this.emit(':responseReady');
},
'AMAZON.StopIntent': function () {
this.response.speak('Bye!');
this.emit(':responseReady');
}
};
exports.handler = function (event, context, callback) {
const alexa = Alexa.handler(event, context, callback);
alexa.APP_ID = APP_ID;
alexa.registerHandlers(handlers);
alexa.execute();
};
randomResturantGeneratorIntent.JSON:
{
"interactionModel": {
"languageModel": {
"invocationName": "random restaurant generator",
"intents": [
{
"name": "AMAZON.CancelIntent",
"samples": []
},
{
"name": "AMAZON.HelpIntent",
"samples": []
},
{
"name": "AMAZON.StopIntent",
"samples": []
},
{
"name": "AMAZON.NavigateHomeIntent",
"samples": []
},
{
"name": "randomRestaurantGeneratorIntent",
"slots": [],
"samples": [
"Launch Random Restaurant Generator "
]
}
],
"types": []
}
}
}
Thank you
Try this way to render responses.
var speechOutput = 'Your response here';
var reprompt = "How can I help?";
this.response.speak(speechOutput);
this.response.listen(reprompt);
this.emit(":responseReady");
Try this function in inline editor for your first skill. and try to test with open random restaurant generator,
/**
* Called when the user launches the skill without specifying what they want.
*/
function onLaunch(launchRequest, session, callback) {
console.log(`onLaunch requestId=${launchRequest.requestId}, sessionId=${session.sessionId}`);
// Dispatch to your skill's launch.
getWelcomeResponse(callback);
}
function buildResponse(sessionAttributes, speechletResponse) {
return {
version: '1.0',
sessionAttributes,
response: speechletResponse,
};
}
function getWelcomeResponse(callback) {
// If we wanted to initialize the session to have some attributes we could add those here.
const sessionAttributes = {};
const cardTitle = 'Welcome';
const speechOutput = 'Welcome to Your First Alexa Skill';
// If the user either does not reply to the welcome message or says something that is not
// understood, they will be prompted again with this text.
const repromptText = 'Please tell me What do you want to know?';
const shouldEndSession = false;
callback(sessionAttributes,
buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
}
function buildSpeechletResponse(title, output, repromptText, shouldEndSession) {
return {
outputSpeech: {
type: 'PlainText',
text: output,
},
//For testing purpose only
// card: {
// type: 'Simple',
// title: `SessionSpeechlet - ${title}`,
// content: `SessionSpeechlet - ${output}`,
// },
reprompt: {
outputSpeech: {
type: 'PlainText',
text: repromptText,
},
},
shouldEndSession,
};
}
exports.handler = (event, context, callback) => {
try {
console.log(`event.session.application.applicationId=${event.session.application.applicationId}`);
if (event.request.type === 'LaunchRequest') {
onLaunch(event.request,
event.session,
(sessionAttributes, speechletResponse) => {
callback(null, buildResponse(sessionAttributes, speechletResponse));
});
}
}
catch (err) {
callback(err);
}
};
I’ve been using lambda for two years and it’s terrible to debug and deploy for me until I started to use aws cloud9.
I suggest that you use aws cloud9 which is a cloud IDE for writing, running and debugging code. You could run the lambda function as local environment.
Check their website to get more information. It’s time consuming, but totally worth it, especially if you want to develop an Alexa skill.
Most of the times you get that error for 2 things:
You don't have the trigger "Alexa Skill Kit" in your lambda function. If you don't have it, you can add one in the designer tab of the configuration of the lambda function.
You don't have the neccesary modules in your lambda function. You can add them locally with "npm install ask-sdk-core" and then upload the folder.
Use this way:
var speechOutput = 'Your response here';
var reprompt = "How can I help?";
this.response.speak(speechOutput);
this.response.listen(reprompt);
this.emit(":responseReady");

Export mongodb collection data to csv file in node js

I have created a mongodb collection in mongolab and want to print all documents in that collecton. I have mongolab and the url of mongolab to get connected with the collection is -
mongodb://user:1234#xyz.mongolab.com:41248/new
The sample document structure is -
{
"_id": "9759572745-Sing",
"details": {
"Gender": "M",
"PreTrainingStatus": "Fresher",
"Religion": "Hindu",
"attendanceInPercentage": "",
"batchHolders": {
"AssessmentDate": "Thu Jul 16 2015",
"CourseFee": "7500",
"isEditable": false
},
"batchID": "282726",
"eid": "",
"whereDidYouHearAboutStar": "---Select---",
"skillInstructorOrTrainerName": "282726",
"specificGovtInstitutetieups": "---Select---",
"isSelected": false,
"isEditable": false
},
"addedOnMs": 1439455766000,
"submittedOnMs": 1439454813000,
"latitude": "27.409566879272",
"longitude": "77.69295501709",
"locationName": "Uttar Pradesh 281006,null"
}
I want to Print it and all nested properties to be displayed in a column.
But I am not able to do so, Please help.
Thank you (In Advance),
Dia
Implementing json2csv library for exporting data to csv file on nodejs
Example
const json2csv = require('json2csv').parse;
//For unique file name
const dateTime = new Date().toISOString().slice(-24).replace(/\D/g,
'').slice(0, 14);
const filePath = path.join(__dirname, "../../../", "public", "exports", "csv-" + dateTime + ".csv");
let csv;
const student = await req.db.collection('Student').find({}).toArray();
// Logging student
// [{id:1,name:"John",country:"USA"},{id:1,name:"Ronny",country:"Germany"}]
const fields = ['id','name','country'];
try {
csv = json2csv(booking_info, {fields});
} catch (err) {
return res.status(500).json({err});
}
fs.writeFile(filePath, csv, function (err) {
if (err) {
return res.json(err).status(500);
}
else {
setTimeout(function () {
fs.unlink(filePath, function (err) { // delete this file after 30 seconds
if (err) {
console.error(err);
}
console.log('File has been Deleted');
});
}, 30000);
res.download(filePath);
}
})
You can use https://www.npmjs.com/package/json2csv
Set nested option value to true
And Also Specify fields that you want from JSON. For nested document you can specify like this details.Gender

Resources