How to inspect Node.JS AWS Lambda data? - javascript

I am new to both Lambda and Node.JS. I originally wanted to write the function in Python, but boss says he'd like it in Node. I am writing an AWS Lambda function to turn off specified EC2 instances at the end of the day. I am having trouble inspecting if describeInstances is grabbing the correct data.
Right now the code shows return String(instances); but I've tried numerous different things such as return instances.response.data; which gives an error about trying to stringify the data or something.
var AWS = require('aws-sdk');
var ec2 = new AWS.EC2();
AWS.config.update({region: 'us-west-2'});
exports.handler = async (event) => {
var params = {
Filters: [
{
Name: "tag:Parking",
Values: [
"true"
]
}
]
};
var instances = ec2.describeInstances(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else return data; // successful response
});
//return Object.getOwnPropertyNames(instances);
//return instances.response.httpResponse;
return String(instances);
};
I just want to be able to view a list of the returned EC2 instances to see if I have the correct instances before turning them off.

describeInstances returns an AWS.Request object not the actual results of the operation, which are returned in the callback handler you passed to the describeInstances function.
You can do it like this using async/await syntax
const data = await ec2.describeInstances(params).promise();
return data

You should be able to view the logs in the AWS Console. The easiest way is to go to the Lambda console, select the function in question, click "Monitoring" near the top left, and then "View logs in CloudWatch" just below that to the right.

Related

Js-IPFS Error: cid.toBaseEncodedString() is not a function

I'm currently working on the backend of a website that would work similar to YouTube but only use IPFS for storage, meaning if you want to "upload" videos to the site it would already have to be on the IPFS network. The actual function is more of an Index than anything else but it was something that I wanted to tackle.
The section I'm working on is intended to verify the integrity of the CID hashes by making sure that there are still providers on the network for that specific content. If there aren't any then the CID and any information associated will get removed from my database but I'm currently getting an issue when trying using the ipfs.dhs.findProvs function.
Here is part of my code:
const ipfs = await IPFS.create({
libp2p: { config: { dht: { enabled: true } } },
});
for (var i of integrityData) {
let cid = new CID(i.CID);
console.log(cid);
let providers = ipfs.dht.findProvs(cid, { numProviders: 2 });
for await (const provider of providers) {
console.log(provider);
}
}
Error Log:
C:\Users\...\node_modules\libp2p-kad-dht\src\providers.js:202
this._log('getProviders %s', cid.toBaseEncodedString())
^
TypeError: cid.toBaseEncodedString is not a function
To further explain my code, the for loops is iterating the JSON content received the Database after querying for all the CIDs in it. i.CID does return the correct CID as a string which I then create a CID object from and pass to the function here ipfs.dht.findProvs(cid, { numProviders: 2 });. The nested for loop is there to iterate through the object that is received but I haven't made it to that stage as I keep getting the same error.

Cloud HTTPS Functions: returning a Promise which is inside a Promisee

I'm currently working on an HTTPS Cloud Function using Firebase, consisting in deleting the post my Android user requested.
General idea
The workflow is (the whole code is available at the end of this SO Question): 1) Firebase checks the user identity (admin.auth().verifyIdToken) ; 2) Firestore gets data from the post that must be deleted (deleteDbEntry.get().then()) ; 3) Cloud Storage prepares itself to delete the file found in the gotten data (.file(filePath).delete()) ; 4) Firestore prepares a batch to delete the post (batch.delete(deleteDbEntry);) and to update the likes/unlikes using the gotten data (batch.update(updateUserLikes,) ; 5) executes the promise of the deletion of the file and of the batch (return Promise.all([deleteFile, batch_commit])).
Expected behavior
I would want to check the user identity. If it's successful, to get the requested post to delete's data using Firebase. If it's successful, I would want to execute the Firestore batch plus the Cloud Storage file deletion in the same promise (that's why I use Promise.all([deleteFile, batch_commit]).then()). If the identity check fails, or if the data get fails, or if the batch fails, I would want to tell the Android app. If all successes, idem.
As all of these operations are in a Cloud HTTPS Function, I must return a promise. This promise, I think, would correspond to all that operations if they are successful, or to an error if at least one is not (?).
Actual behavior
For the moment, I just return the promise of the Firebase user identity check.
My problem & My question
I can't go from the actual behavior to the expected behavior because:
I think it's not very clear in my mind whether I should return the promise corresponding to "all these operations are successful, or at least one is not" in this Cloud HTTPS Function
As these operations are nested (except the Firestorage file deletion + Firestore post deletion which are present in a batch), I can't return something like Promise.all().
My question
Could you please tell me if I'm right (point 1.) and, if not: what should I do? If yes: how could I do it, because of point 2.?
Whole Firebase Cloud HTTPS Function code
Note: I've removed my input data controls to make my code more lisible.
exports.deletePost = functions.https.onCall((data, context) => {
return admin.auth().verifyIdToken(idToken)
.then(function(decodedToken) {
const uid = decodedToken.uid;
const type_of_post = data.type_of_post;
const the_post = data.the_post;
const deleteDbEntry = admin_firestore.collection('list_of_' + type_of_post).doc(the_post);
const promise = deleteDbEntry.get().then(function(doc) {
const filePath = type_of_post + '/' + uid + '/' + data.stored_image_name;
const deleteFile = storage.bucket('android-f.appspot.com').file(filePath).delete();
const batch = admin.firestore().batch();
batch.delete(deleteDbEntry);
if(doc.data().number_of_likes > 0) {
const updateUserLikes = admin_firestore.collection("users").doc(uid);
batch.update(updateUserLikes, "likes", FieldValue.increment(-doc.data().number_of_likes));
}
const batch_commit = batch.commit();
return Promise.all([deleteFile, batch_commit]).then(function() {
return 1;
}).catch(function(error) {
console.log(error);
throw new functions.https.HttpsError('unknown', 'Unable to delete the post. (2)');
});
}).catch(function(error) {
console.log(error);
throw new functions.https.HttpsError('unknown', 'Unable to delete the post. (1)');
});
return promise;
}).catch(function(error) {
console.log(error);
throw new functions.https.HttpsError('unknown', 'An error occurred while verifying the token.');
});
});
You should note that you are actually defining a Callable Cloud Function and not an HTTPS one, since you do:
exports.deletePost = functions.https.onCall((data, context) => {..});
One of the advantages of a Callable Cloud Function over an HTTPS one is that it "automatically deserializes the request body and validates auth tokens".
So you can simply get the user uid with context.auth.uid;.
Now, regarding the way of "orchestrating" the different calls, IMHO you should just chain the different Promises returned by the asynchronous Firebase methods (the ones of Firestore and the one of Cloud Storage), as follows:
exports.deletePost = functions.https.onCall((data, context) => {
//....
const uid = context.auth.uid;
let number_of_likes;
const type_of_post = data.type_of_post;
const the_post = data.the_post;
const deleteDbEntry = admin_firestore.collection('list_of_' + type_of_post).doc(the_post);
return deleteDbEntry.get()
.then(doc => {
number_of_likes = doc.data().number_of_likes;
const filePath = type_of_post + '/' + uid + '/' + data.stored_image_name;
return storage.bucket('android-f.appspot.com').file(filePath).delete();
})
.then(() => {
const batch = admin.firestore().batch();
batch.delete(deleteDbEntry);
if (number_of_likes > 0) {
const updateUserLikes = admin_firestore.collection("users").doc(uid);
batch.update(updateUserLikes, "likes", FieldValue.increment(-doc.data().number_of_likes));
}
return batch.commit();
}).catch(function (error) {
console.log(error);
throw new functions.https.HttpsError('....', '.....');
});
});
I don't think using Promise.all() will bring any interest, in your case, because, as explained here, "if any of the passed-in promises reject, Promise.all asynchronously rejects with the value of the promise that rejected, whether or not the other promises have resolved".
At the time of writing, there is no way to group all of these asynchronous calls to different Firebase services into one atomic operation.
Even if the batched write at the end is atomic, it could happen that the file in Cloud Storage is correctly deleted but that the batched write to Firestore is not executed, for example because there is a problem with the Firestore service.
Also, note that you only need one exception handler at the end of the Promise chain. If you want to differentiate the cause of the exception, in such a way that you send a different error message to the front-end you could use the approach presented in this article.
The article shows how to define different custom Error classes (derived from the standard built-in Error object) which are used to check the kind of error in the exception handler.

Problems with fetch and XMLHttpRequest

The first thing is that I'm a noob and I know it... Maybe you think this is a repeated question but I read a lot of posts about the same question but no one helped.
I'm trying to develop a web application about working orders, clients, providers, etc... I have a Rest API with the proper routes connected to a database (mysql), and the logic with the CRUM methods. I'm still working on the server part and until some days ago everything went fine, since I tested this first with Postman and then with some simple tests and everything is working well.
The thing is I'm trying to develop the logic (the real one) of my application to access to some individual elements of the json object (the API returns an array, but that's not the problem). I need to check and generate the serial number of the working orders with this format 'number/year'. I tried with both fetch() and XMLHttpRequest to access the data and nothing works.... I can't access to the elements of the array because I always have something wrong.
If I try this inside if my tests using fetch() it works but if I try this inside my numeroOT() method I can't, I don't know what else to do so I need help please... I'm going crazy with this thing!!
This is the code that works IN MY TEST:
describe('TEST logica.js', function () {
it('Genero el NÂș de Orden', async () => {
var numeroDeOT = laLogica.numeroOT(); //this is the method of my logic I'm testing and it's suposed to do the thing
//This is the the only code which I could acceed to the json. But this has to go inside the numeroOT() method but only works in the test
//-------------------------------------------
var response = await fetch('http://localhost:3000/ots');
var orden = await response.json();
var laOrden = orden[orden.length-1]; //the last element/json object of the array
var elNumero = laOrden.numero_ot; //the element I want to check and generate
console.log(elNumero);
//The code to check this element and generate the new one goes down here but that's clear
//---------------------------------------------
console.log(numeroDeOT);
expect(numeroDeOT).to.be.a('String'); //this is to check what my method numeroOT() returns. Usually 'undefined'
}) //it
}); //describe
I realized that the two ways I was trying in my code there weren't possible to use them with node (since I'm using node), so I tried this code into my method and it works perfectly and I finally can access to my array of JSON objects!
var options = {
host : 'localhost',
port : 3000,
path : '/ots', // the rest of the url with parameters if needed
method : 'GET' // do GET
};
var request = http.request(options, function (res) {
console.log("request received");
var data = '';
res.on('data', function (chunk) {
data += chunk;
});
res.on('end', function () {
console.log(data);
});
});
request.on('error', function (e) {
console.log(e.message);
});
request.end();

Call getUsers() function from MongoDB driver

I am currently building an api application that checks the status and gets information of various types of dbs(i.e. Mongo, MySQL) using Sailsjs such as users, depending on a user input. Here is a snippet of the code I am working on. The local host is just the test database I am connecting to, but in the future it will be supplied by the user.
var mp = require('mongodb-promise');
var MongoClient = require('mongodb');
mp.MongoClient.connect("mongodb://#localhost:27017/test")
.then(function(db){
db.getUsers().then(function(users){
res.ok(users);
})
})
.fail(function(err) {
console.log(err);
})
I am attempting to use promises for the async issue. The problem I am having is that it doesn't work. It tells me that that Object[object object] has no method 'getUsers'. I have searched and can't seem to find a solution that works.
If I change the function to the below, I get the some data back.
mp.MongoClient.connect("mongodb://#localhost:27017/IMS")
.then(function(db){
db.stats().then(function(stats){
return res.ok(stats);
})
})
.fail(function(err) {
console.log(err);
dbObject.vipUp = false;
})
I am not sure what the issue is or how to solve it.
What you are doing here is using the node native driver methods to connect and inspect the database. There is in fact "no such method" as .getUsers() here in this API or in fact in any other API.
The .getUsers() function is just a "shell helper" that is basically implemented like this:
function (args) {
var cmdObj = {usersInfo: 1};
Object.extend(cmdObj, args);
var res = this.runCommand(cmdObj);
if (!res.ok) {
var authSchemaIncompatibleCode = 69;
if (res.code == authSchemaIncompatibleCode ||
(res.code == null && res.errmsg == "no such cmd: usersInfo")) {
// Working with 2.4 schema user data
return this.system.users.find({}).toArray();
}
throw Error(res.errmsg);
}
return res.users;
}
So what you should be able to see here is that this normally wraps a "command" form, or otherwise falls back for compatibility with MongoDB 2.4 to querying the system.users collection on the current database.
Therefore, instead of calling a method that does not exist, you then need to use the .command() method instead:
mp.MongoClient.connect("mongodb://#localhost:27017/test")
.then(function(db){
db.command({ "usersInfo": 1}).then(function(users){
res.ok(users);
})
})
.fail(function(err) {
console.log(err);
})
Or in the case of connecting to a MongoDB 2.4 instance, then fetch from the .collection():
mp.MongoClient.connect("mongodb://#localhost:27017/test")
.then(function(db){
db.collection('system.users').find().toArray().then(function(users){
res.ok(users);
})
})
.fail(function(err) {
console.log(err);
})
At any rate, you really should be establishing the database connection elsewhere in your application ( or re-using the underlying driver connection from another store ), and then calling methods on the connection already establihed. This is always preferable to creating a connection on the request of the information you want to retrieve.
Also, recent versions of the node native driver support promises right out of the box. So there may be no need to configure in anything else, depending on how you intend to use it.

meteor js create mongodb database hook to store data from API at fixed interval

tldr - What is the best pattern create a 'proprietary database' with data from an API? In this case, using Meteor JS and collections in mongo db.
Steps
1. Ping API
2. Insert Data into Mongo at some interval
In lib/collections.js
Prices = new Mongo.Collection("prices");
Basic stock api call, in server.js:
Meteor.methods({
getPrice: function () {
var result = Meteor.http.call("GET", "http://api.fakestockprices.com/ticker/GOOG.json");
return result.data;
}
});
Assume the JSON is returned clean and tidy, and I want to store the entire object (how you manipulate what is returned is not important, storing the return value is)
We could manipulate the data in the Meteor.method function above but should we? In Angular services are used to call API, but its recommended to modularize and keep the API call in its own function. Lets borrow that, and Meteor.call the above getPrice.
Assume this also done in server.js (please correct).
Meteor.call("getPrice", function(error, result) {
if (error)
console.log(error)
var price = result;
Meteor.setInterval(function() {
Prices.insert(price);
}, 1800000); // 30min
});
Once in the db, a pub/sub could be established, which I'll omit and link to this overview.
You may want to take a look at the synced-cron package.
With a cron job it's pretty easy, just call your method:
// server.js
SyncedCron.start();
SyncedCron.add({
name: "get Price",
schedule: function(parser){
return parser.text('every 30 minutes');
},
job: function(){
return Meteor.call("getPrice");
}
});
Then in getPrice you can do var result = HTTP.call(/* etc */); and Prices.insert(result);. You would want some additional checks of course, as you have pointed out.

Categories