Extracting data from nested object with AWS ec2.describeInstances in node.js - javascript

I am relatively new to JavaScript.
I am trying to extract specific information from AWS about my EC2 instances using describeInstances. Specifically, I want to be able to provide a list of InstanceIds and extract from the resulting object the value of the Tags with Key: "Name". Here is the base code:
// Load the SDK for JavaScript
var AWS = require('aws-sdk');
// Load credentials and set region from JSON file
AWS.config.loadFromPath('./.aws/config.json');
// Create EC2 service object
var ec2 = new AWS.EC2({apiVersion: '2016-11-15'});
var params = {
DryRun: false,
InstanceIds: ['i-0be50217a4028a044', 'i-08b83c1c428e9a1d2']
};
ec2.describeInstances(params, function(err, data) {
if (err) {
console.log("Error", err.stack);
} else {
console.log("Success", JSON.stringify(data));
}
});
Upon running this code, a large, hairy, and nested object is returned. The JSON.stringify() version of this is shown here:
{
"Reservations": [{
"ReservationId": "r-04e32387e546387ba",
"OwnerId": "543800113692",
"Groups": [],
"Instances": [{
"InstanceId": "i-08b83c1c428e9a1d2",
"ImageId": "ami-8aa998ea",
"State": {
"Code": 16,
"Name": "running"
},
"PrivateDnsName": "ip-10-77-113-210.us-west-2.compute.internal",
"PublicDnsName": "ec2-35-165-200-222.us-west-2.compute.amazonaws.com",
"StateTransitionReason": "",
"KeyName": "Security1",
"AmiLaunchIndex": 0,
"ProductCodes": [],
"InstanceType": "t2.micro",
"LaunchTime": "2017-02-14T14:59:11.000Z",
"Placement": {
"AvailabilityZone": "us-west-2b",
"GroupName": "",
"Tenancy": "default"
},
"Monitoring": {
"State": "disabled"
},
"SubnetId": "subnet-228da755",
"VpcId": "vpc-af0f0dca",
"PrivateIpAddress": "10.77.113.210",
"PublicIpAddress": "35.165.200.222",
"Architecture": "x86_64",
"RootDeviceType": "ebs",
"RootDeviceName": "/dev/sda1",
"BlockDeviceMappings": [{
"DeviceName": "/dev/sda1",
"Ebs": {
"VolumeId": "vol-00e55d6bf114bfcaa0",
"Status": "attached",
"AttachTime": "2017-02-09T15:37:34.000Z",
"DeleteOnTermination": true
}
}],
"VirtualizationType": "hvm",
"ClientToken": "vOiiS1486654656072",
"Tags": [{
"Key": "Name",
"Value": "Fenris"
}],
"SecurityGroups": [{
"GroupName": "launch-wizard-2",
"GroupId": "sg-2312072c"
}],
"SourceDestCheck": true,
"Hypervisor": "xen",
"EbsOptimized": false
}]
}, {
"ReservationId": "r-0bbcb12e5c1162c23",
"OwnerId": "543800113692",
"Groups": [],
"Instances": [{
"InstanceId": "i-0be50217a40028a044",
"ImageId": "ami-8ba011ea",
"State": {
"Code": 80,
"Name": "stopped"
},
"PrivateDnsName": "ip-10-77-118-17.us-west-2.compute.internal",
"PublicDnsName": "",
"StateTransitionReason": "User initiated (2016-12-05 16:49:45 GMT)",
"KeyName": "Security3",
"AmiLaunchIndex": 0,
"ProductCodes": [],
"InstanceType": "t2.medium",
"LaunchTime": "2016-12-02T15:50:08.000Z",
"Placement": {
"AvailabilityZone": "us-west-2b",
"GroupName": "",
"Tenancy": "default"
},
"Monitoring": {
"State": "disabled"
},
"SubnetId": "subnet-228da700",
"VpcId": "vpc-af0f1ccb",
"PrivateIpAddress": "10.77.118.17",
"StateReason": {
"Code": "Client.UserInitiatedShutdown",
"Message": "Client.UserInitiatedShutdown: User initiated shutdown"
},
"Architecture": "x86_64",
"RootDeviceType": "ebs",
"RootDeviceName": "/dev/sda1",
"BlockDeviceMappings": [{
"DeviceName": "/dev/sda1",
"Ebs": {
"VolumeId": "vol-1c211ac8",
"Status": "attached",
"AttachTime": "2016-11-22T01:54:52.000Z",
"DeleteOnTermination": true
}
}],
"VirtualizationType": "hvm",
"ClientToken": "RQbhg1479762230132",
"Tags": [{
"Key": "Name",
"Value": "Heimdall"
}, {
"Key": "Type",
"Value": "Product Dev"
}],
"SecurityGroups": [{
"GroupName": "LinuxAPIdev",
"GroupId": "sg-5ea11777"
}],
"SourceDestCheck": true,
"Hypervisor": "xen",
"EbsOptimized": false
}]
}]
}
This is way more info than I need or want. I want to find a way to get only the values of Reservations.Instances.Tags.Value from the Reservations.Instances.Tags.Name key.
I thought that just writing it that way would work. But strangely, I can't seem to access the Reservations.Instances object at all:
// Load the SDK for JavaScript
var AWS = require('aws-sdk');
// Load credentials and set region from JSON file
AWS.config.loadFromPath('./.aws/config.json');
// Create EC2 service object
var ec2 = new AWS.EC2({apiVersion: '2016-11-15'});
var params = {
DryRun: false,
InstanceIds: ['i-0be5987a41191a044', 'i-08b83c3fc28e9a1d2']
};
// call EC2 to retrieve policy for selected bucket
ec2.describeInstances(params, function(err, data) {
if (err) {
console.log("Error", err.stack);
} else {
console.log("Success", JSON.stringify(data.Reservations.Instances));
}
});
This results in:
Success undefined
What am I doing wrong? How do I access the lower level of data within Instances? It is obviously there... it shows up in JSON.stringify(), but clearly I don't have the right protocol for extracting it.
(P.S. Because of AWS credentials, you won't be able to run my code without minor changes. You'll need to reference your own credentials, and InstanceIds from your own EC2 instances.)

Reservations, Instances and Tags All are arrays that''s why :
Reservations[0].Instances[0].Tags[0].Value
// value of 1ˢᵗ tag of 1ˢᵗ instance of 1ˢᵗ reservation
and Not
Reservations.Instances.Tags.Value

Within your describeInstances() method, you can simply use
// for InstanceId : "i-08b83c1c428e9a1d2"
data.Reservations[0].Instances[0].Tags[0].Value;
// and for InstanceId : "i-0be50217a40028a044"
data.Reservations[1].Instances[0].Tags[0].Value;
You can anyway create a function, if required, to return the tags value if the object structure is known.

Related

Is there a way to search for keywords across multiple collections in MongoDB?

MongoDB Atlas Cluster version: 5.0.12
MERN Stack Application
Simple Search App that returns Specific ads when keywords are typed in an input.
Front end is handled by React-Redux.
And I am using Axios to request my server for data.
Server is using Mongo's aggregate pipeline function to search for text using $search,
and then a few different operators to fetch data from another collection.
There are two collections, the main one has a foreign key that references the second one.
Here is a sample json of both the collections
ads: {
_id: 1,
companyId: 1,
primaryText: "",
description: "",
CTA: "Sign Up",
imageUrl: "",
heading: "",
}
companies: {
_id: 1,
name: "Levi's",
url: "",
}
This is the search index that I have been using to look for keywords in the collection.
{
"mappings": {
"dynamic": true,
"fields": {
"company": {
"fields": {
"name": [
{
"dynamic": true,
"type": "document"
},
{
"type": "string"
}
]
},
"type": "document"
},
"description": [
{
"dynamic": true,
"type": "document"
},
{
"type": "string"
}
],
"heading": [
{
"dynamic": true,
"type": "document"
},
{
"type": "string"
}
],
"primaryText": [
{
"dynamic": true,
"type": "document"
},
{
"type": "string"
}
]
}
}
}
Mongo doesn't let me query $search anywhere in the pipeline except as the first operation.
So the order that works is this
$seach --> $lookup --> $project --> $unwind
This works but the only problem is that when I try to search for keyword that is present in the companies collection, like name: "Levi's", it doesn't respond with the corresponding ad.
So, In short I am trying to find a way to apply $search on a collection that has the gone through a $lookup.
Thank you, and I appreciate you spending time reading this.

How do I select an adjacent value from a JSON array using map()?

I have some JSON as shown below...
var JSONobj = {
"headline":{
"localized":{
"en_US":"Doctor"
},
"preferredLocale":{
"country":"US",
"language":"en"
}
},
"identities":[
{
"access_token":"AQVUTBfbOs5JLsdfsdfH_W1aZ2N0PrbL0LhD5Y5-g",
"provider":"linkedin",
"user_id":"v57678565vf",
"connection":"linkedin",
"isSocial":true
},
{
"access_token":"AQVUTBsdfsdfsdfsdfwePrbL0LhD5Y5-g",
"provider":"facebook",
"user_id":"hshs8722",
"connection":"facebook",
"isSocial":true
}
],
"name":"John Bob"
};
Using JavaScript I need to go through each item in the "identities" array, find the item with a "connection" value of "facebook", and return the associated "access_token" value.
Note: This example has two items in the "identities" array, but in production there will a dynamic number of items. Sometimes more, sometimes less.
I have been trying to do it using map() as shown below, but I can't figure it out.
var access_token = JSONobj.identities.map(i=>i.connection);
console.log(access_token);
You can use Array.find to find the first object in identities that has a connection of "facebook", then extract the access_token from that object:
var JSONobj = {
"headline": {
"localized": {
"en_US": "Doctor"
},
"preferredLocale": {
"country": "US",
"language": "en"
}
},
"identities": [{
"access_token": "AQVUTBfbOs5JLsdfsdfH_W1aZ2N0PrbL0LhD5Y5-g",
"provider": "linkedin",
"user_id": "v57678565vf",
"connection": "linkedin",
"isSocial": true
},
{
"access_token": "AQVUTBsdfsdfsdfsdfwePrbL0LhD5Y5-g",
"provider": "facebook",
"user_id": "hshs8722",
"connection": "facebook",
"isSocial": true
}
],
"name": "John Bob"
};
var access_token = JSONobj.identities.find(o => o.connection == 'facebook').access_token;
console.log(access_token);
Note (as pointed out by #secan) that if it's possible that there might not be an identity with a connection of "facebook", it is safer to use:
(JSONobj.identities.find(i=>i.connection === 'facebook')||{}).access_token;
as this will return undefined rather than raising an error.
Another alternative in that situation (as pointed out by #pilchard) is to use optional chaining (although this requires a fairly recent browser for support):
JSONobj.identities.find(i=>i.connection === 'Facebook')?.access_token;
create a generala function wich accept identities array and required connection name,
you can use Array.find to go through connection array items
function getAccessToken(identitiesArr, connection) {
let identity = identitiesArr.find(e => e.connection == connection);
if (identity)
return identity.access_token;
return '';
}
var JSONobj = {
"headline": {
"localized": {
"en_US": "Doctor"
},
"preferredLocale": {
"country": "US",
"language": "en"
}
},
"identities": [{
"access_token": "AQVUTBfbOs5JLsdfsdfH_W1aZ2N0PrbL0LhD5Y5-g",
"provider": "linkedin",
"user_id": "v57678565vf",
"connection": "linkedin",
"isSocial": true
},
{
"access_token": "AQVUTBsdfsdfsdfsdfwePrbL0LhD5Y5-g",
"provider": "facebook",
"user_id": "hshs8722",
"connection": "facebook",
"isSocial": true
}
],
"name": "John Bob"
};
let token = getAccessToken(JSONobj.identities, "facebook");
console.log(token);

Mapping JSON response to using JS

I have a JSON response coming in and I need to map it into an Object and return only the specific fields using js.
Here I have created an example response:
{
"Header": {
"SenderId": "IDMAN",
},
"Shipments": {
"Shipment": [
{
"ShipmentNumber": "KIOKLIOS32",
"Product": {
"value": "1234",
"description": "example desc"
},
"Services": {
"Service": [
{
"value": "0234",
"amount": null,
"unit": null,
}
]
},
...
}
There will be multiple Service's and I need the fields shipments.shipment.product.value and shipments.shipment.services.service.value (all of them from the list).
One of my ideas was to first try create a const:
if (response.status === 200) {
let data = await response.text();
const shipmentData = JSON.parse(data);
const destination = {
"Header": {
"SenderId": _.get(shipmentData, 'Header.SenderId'),
}
};
res.status(200)
.send(destination);
}
This way I can get the senderId but I cannot access the array.
In which way do I have to create my object to map all of this?

Creating Facebook Website Audiences Returning JSON Error

I'm trying to write a node.js script to create a set of Facebook audiences based on URL structure, but I'm getting the below error and I can't seem to identify what is wrong with the JSON I'm sending:
Error I'm getting back: FacebookRequestError: Invalid rule JSON format: Invalid rule JSON format.
It seems that the 'rule' property of the 'params' object is somehow invalid, but I can't identify what is wrong with it. I even tried copying their example from the docs and it gave the same error. I also pasted the JSON into the API explorer and the editor there indicated valid JSON, but the API response was the same.
api explorer screenshot
After reading this similar question, I tried a bunch of variations using single vs double-quotes, JSON.stringifying the whole thing, part of it, none of it, etc... I'm hoping fresh eyes might catch it.
My Code:
"use strict";
const bizSdk = require("facebook-nodejs-business-sdk");
const AdAccount = bizSdk.AdAccount;
const CustomAudience = bizSdk.CustomAudience;
const access_token = "REDACTED";
const app_secret = "REDACTED";
const app_id = "REDACTED";
const id = "act_REDACTED";
const pixelID = "REDACTED";
const api = bizSdk.FacebookAdsApi.init(access_token);
const showDebugingInfo = true; // Setting this to true shows more debugging info.
if (showDebugingInfo) {
api.setDebug(true);
}
const logApiCallResult = (apiCallName, data) => {
console.log(apiCallName);
if (showDebugingInfo) {
console.log("Data:" + JSON.stringify(data));
}
};
let fields, params;
fields = [
];
params = {
"name": "Website - Viewed Product - Corrugated Containers - 180 days",
"rule": {
"inclusions": {
"operator": "or",
"rules": {
"inclusions": {
"operator": "or",
"rules": [
{
"event_sources": [
{
"id": pixelID,
"type": "pixel"
}
],
"retention_seconds": 8400,
"filter": {
"operator": "and",
"filters": [
{
"field": "url",
"operator": "i_contains",
"value": "/products/corrugated-containers"
}
]
}
}
]
}
}
}
},
"retention_days": "180",
"prefill": "1"
};
const customaudiences = (new AdAccount(id)).createCustomAudience(
fields,
params
);
logApiCallResult("customaudiences api call complete.", customaudiences);
It looks like I had accidentally nested a rules object within a rules object somehow! I fixed that and it created the audience without throwing an error...now I can't see the audience definition in Facebook's interface to check that it's correct, but that's a completely different topic.
I changed...
params = {
"name": "Website - Viewed Product - Corrugated Containers - 180 days",
"rule": {
"inclusions": {
"operator": "or",
"rules": {
"inclusions": {
"operator": "or",
"rules": [
{
"event_sources": [
{
"id": pixelID,
"type": "pixel"
}
],
"retention_seconds": 8400,
"filter": {
"operator": "and",
"filters": [
{
"field": "url",
"operator": "i_contains",
"value": "/products/corrugated-containers"
}
]
}
}
]
}
}
}
},
"retention_days": "180",
"prefill": "1"
};
to
params = {
"name": "Website - Viewed Product - Corrugated Containers - 180 days",
"rule": {
'inclusions': {
'operator': 'or',
'rules': [{
'event_sources': [{
'id': pixelID,
'type': 'pixel'
}],
'retention_seconds': retention_seconds,
'filter': {
'operator': 'and',
'filters': [{
'field': 'url',
'operator': 'i_contains',
'value': '/products/corrugated-containers'
}]
}
}]
}
},
"retention_days": retention_days,
"prefill": "1"
};

Passing a parameter between multiple intents triggering Dialogflow's Fulfillment

I have a Dialogflow agent that I've setup with a web hook that is pulling info from a Firebase database. I am trying to have different intents kick off different queries on the DB. From the Welcome Intent the agent asks for a name that it will matching the DB (e.g. Hi it's nice to meet you? What is the name you looking for?). From there the user is gives a name which is a response that triggers another intent called "name." This intent has a parameter called "agent.parameters.defaultName" which is passed into the fulfillment and used to query the DB. Below is the webhook kicks off the following script:
});
process.env.DEBUG = 'dialogflow:debug'; // enables lib debugging statements
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request,
response) => {
const agent = new WebhookClient({ request, response });
console.log('Dialogflow Request headers: ' +
JSON.stringify(request.headers));
console.log('Dialogflow Request body: ' + JSON.stringify(request.body));
//function to request bio info on the db
function handleData(agent) {
const congressName = agent.parameters.congressName;
return admin.database().ref().once("value").then((snapshot) => {
var nameInfo = snapshot.child('Name/' + congressName + '/profile').val();
agent.add(nameInfo + "\n \n Tell me the first and last name of the next
person you'd like to learn about. Or you can say Twitter to get this
individual's Twitter info.");
});
}
//Function to return the name's tweet info
function handleTweet(agent) {
const congressName = agent.parameters.congressName;
return admin.database().ref().once("value").then((snapshot) => {
var nameTweet = snapshot.child('Name/' + congressName + '/twitter_handle').val();
agent.add(nameTweet);
});
}
// Run the proper function handler based on the matched Dialogflow intent
name
let intentMap = new Map();
intentMap.set('name', handleData);
intentMap.set('name - custom', handleTweet);
agent.handleRequest(intentMap);
});
This returns the bio and prompts the user to say another name or say twitter to pull the twitter info.
If the user says Twitter I'd like for another intent to be triggered. This Intent would also have a fulfillment that would call the DB, but this time it'd pull the Twitter info. Where I'm having an issue is I can't get this intent to trigger the fulfillment. I'm wondering if my parameter is in the right section or if I need to figure out how to pass it to the Twitter intent. I'm not sure where I'm off.
Below are the intents:
//Name intent
{
"id": "0c7bd173-e7fe-4bb4-9b87-7b94624ceb4e",
"name": "name",
"auto": true,
"contexts": [],
"responses": [{
"resetContexts": false,
"action": "congressName",
"affectedContexts": [{
"name": "Name",
"parameters": {},
"lifespan": 5
}],
"parameters": [{
"id": "a79559d6-d3db-4b37-b681-174fce8bc58c",
"required": true,
"dataType": "#sys.any",
"name": "congressName",
"value": "$congressName",
"prompts": [{
"lang": "en",
"value": "What is the proper first and last name of the person you
are looking for info on?"
}],
"isList": false
}],
"messages": [{
"type": 0,
"lang": "en",
"speech": []
}],
"defaultResponsePlatforms": {},
"speech": []
}],
"priority": 500000,
"webhookUsed": true,
"webhookForSlotFilling": false,
"lastUpdate": 1535995990,
"fallbackIntent": false,
"events": []
}
[{
"id": "a14768b0-c64d-4a63-9ccb-d9452b74ed21",
"data": [{
"text": "tammy duckworth",
"alias": "congressName",
"meta": "#sys.any",
"userDefined": false
}],
"isTemplate": false,
"count": 0,
"updated": 1535223341
},
{
"id": "520acfc8-102b-4e14-9342-54678e9f6940",
"data": [{
"text": "tom cotton",
"alias": "congressName",
"meta": "#sys.any",
"userDefined": false
}],
"isTemplate": false,
"count": 0,
"updated": 1535223341
}
]
//Twitter intent
"id": "78330811-d692-4c70-adb2-3130b608d46f",
"name": "twitter",
"auto": true,
"contexts": [],
"responses": [{
"resetContexts": false,
"action": "",
"affectedContexts": [],
"parameters": [{
"id": "7acd4cb2-9cd7-4c2a-b5aa-2981ee25acf4",
"dataType": "#congressName",
"name": "congressName",
"value": "$congressName",
"isList": false
}],
"messages": [{
"type": 0,
"lang": "en",
"speech": []
}],
"defaultResponsePlatforms": {},
"speech": []
}],
"priority": 500000,
"webhookUsed": true,
"webhookForSlotFilling": false,
"lastUpdate": 1535996186,
"fallbackIntent": false,
"events": []
}
[{
"id": "4f099a33-74c6-4832-acd1-815aca6605f2",
"data": [{
"text": "Susan Collins",
"alias": "congressName",
"meta": "#congressName",
"userDefined": false
},
{
"text": " Twitter",
"userDefined": false
}
],
"isTemplate": false,
"count": 0,
"updated": 1535996186
},
{
"id": "f775749a-56fd-410d-a167-174e6eb03ddf",
"data": [{
"text": "twitter ",
"userDefined": false
},
{
"text": "#congress",
"alias": "congressName",
"meta": "#congressName",
"userDefined": true
}
],
"isTemplate": false,
"count": 0,
"updated": 1535238371
}
]

Categories