Skip to content

Commit

Permalink
feat: save configured views for fast retrieval
Browse files Browse the repository at this point in the history
  • Loading branch information
ljacobsson committed Jul 9, 2021
1 parent ec31f51 commit 45705e7
Show file tree
Hide file tree
Showing 13 changed files with 421 additions and 245 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,12 @@ You can use `awscii` together with the [watch](https://linuxize.com/post/linux-w
`watch -n60 --color awscii lambda --name my-function-name --graph-types Errors,Invocations --profile default`

![Demo](https://raw.githubusercontent.com/mhlabs/awscii-cli/main/images/demo2.gif)

## Saving commands for future retrieval
All commands take a `--save [view name]` option. This will save the input from the UI in a configuration file so it can be displayed faster next time you want to view it.

To load a saved view or views, use `awscii load [--name [view name(s)]]`. You can display more than one saved view at a time by comma separating the view names.

This lets you compare and visually correlate metrics from different stacks, functions or tables.

![Demo](https://raw.githubusercontent.com/mhlabs/awscii-cli/main/images/demo3.gif)
Binary file added images/demo3.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require("./src/commands/lambda");
require("./src/commands/dynamodb");
require("./src/commands/apigateway");
require("./src/commands/stack");
require("./src/commands/load-view");

program.version(package.version, "-v, --version", "output the current version");
program.parse(process.argv);
22 changes: 22 additions & 0 deletions src/cache-util.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const fs = require("fs");
const os = require("os");
const path = require("path");
const inputUtil = require("./input-util");

const settingsPath = path.join(os.homedir(), ".awscii-cli");

Expand All @@ -26,7 +27,28 @@ function get(filename) {
return JSON.parse(file.toString());
}

async function saveCommand(cmd, commandName, resourceNames) {
const commands = get("views.json") || {};
let replace = true;
const saveAs = cmd.save;
delete cmd.save;
delete cmd.nocache;
if (commands[saveAs]) {
replace = await inputUtil.prompt("View already exists. Replace?");
}
if (resourceNames) {
cmd.name = resourceNames[0];
cmd.names = resourceNames;
}
if (replace) {
commands[saveAs] = { commandName, cmd };
save("views.json", commands);
console.log("Saved command as " + saveAs);
}
}

module.exports = {
saveCommand,
save,
get,
};
3 changes: 2 additions & 1 deletion src/cloudformation-util.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const AWS = require("aws-sdk");
const inputUtil = require("./input-util");
const demoUtil = require("./demo-util");

async function selectResources(stackName, ...types) {
const cfn = new AWS.CloudFormation();
Expand All @@ -20,7 +21,7 @@ async function selectResources(stackName, ...types) {
return await inputUtil.checkbox(
"Select resource",
resourceList.sort().map((p) => {
return { name: inputUtil.obfuscateName(p, types[0].split("::")[2]), value: p };
return { name: demoUtil.obfuscateName(p, types[0].split("::")[2]), value: p };
})
);
}
Expand Down
110 changes: 64 additions & 46 deletions src/commands/apigateway/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ program
.option("--stage [staeName]", "Name of the API stage. Defaults to UI prompt")
.option("--graph-types [graphName]", "Type(s) of the graph. Comma separated")
.option("-t --timespan [timespan]", "The timespan in minutes.", 60)
.option("--save [view name]", "Saves the view for future retrieval")
.option(
"-r --region [region]",
"AWS region. Defaults to environment variable AWS_REGION"
Expand All @@ -28,56 +29,73 @@ program
"AWS profile. Defaults to environment variable AWS_PROFILE",
"default"
)
.description("Browses and visualises API Gateway V1 metrics as ASCII diagrams")
.description(
"Browses and visualises API Gateway V1 metrics as ASCII diagrams"
)
.action(async (cmd) => {
authHelper.authenticate(cmd);
const apiGateway = new AWS.APIGateway();

let nextMarker = null;
let resourceList = [];
if (!cmd.id) {
spinner.start();
do {
const apis = await apiGateway
.getRestApis({ position: nextMarker })
.promise();
await handle(cmd);
});
async function handle(cmd) {
authHelper.authenticate(cmd);
const apiGateway = new AWS.APIGateway();

resourceList.push(
...apis.items.map((p) => {
return { name: p.name, value: p };
})
);
nextMarker = apis.position;
} while (nextMarker);
spinner.stop();
let nextMarker = null;
let resourceList = [];
if (!cmd.id) {
spinner.start();
do {
const apis = await apiGateway
.getRestApis({ position: nextMarker })
.promise();

resourceList = resourceList.sort((a, b) =>
a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1
resourceList.push(
...apis.items.map((p) => {
return { name: p.name, value: p };
})
);
}
nextMarker = apis.position;
} while (nextMarker);
spinner.stop();

resourceList = resourceList.sort((a, b) =>
a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1
);
}

if (cmd.id) {
const api = await apiGateway.getRestApi({restApiId: cmd.id}).promise();
cmd.name = api.name;
}
if (cmd.id) {
const api = await apiGateway.getRestApi({ restApiId: cmd.id }).promise();
cmd.name = api.name;
}

const resource =
cmd.name ? {name:cmd.name, id: cmd.id} : undefined ||
const resource = cmd.name
? { name: cmd.name, id: cmd.id }
: undefined ||
(await inputUtil.autocomplete("Select Rest API", resourceList));
const stages = await apiGateway
.getStages({ restApiId: resource.id })
.promise();
const stage =
cmd.stage ||
(await inputUtil.autocomplete(
"Select stage",
stages.item.map((p) => p.stageName)
));
await metricsUtil.render(
cmd,
{ name: resource.name, stage: stage },
graphTypeMapping,
`https://${AWS.config.region}.console.aws.amazon.com/apigateway/home?region=${AWS.config.region}#/apis/${resource.id}/dashboard`,
`awscii apigateway --id ${resource.id} --stage ${stage} --graph-types #graphtypes# --profile ${cmd.profile}`
);
});
const stages = await apiGateway
.getStages({ restApiId: resource.id })
.promise();
const stage =
cmd.stage ||
(await inputUtil.autocomplete(
"Select stage",
stages.item.map((p) => p.stageName)
));
const renderResult = await metricsUtil.render(
cmd,
{ name: resource.name, stage: stage },
graphTypeMapping,
`https://${AWS.config.region}.console.aws.amazon.com/apigateway/home?region=${AWS.config.region}#/apis/${resource.id}/dashboard`,
`awscii apigateway --id ${resource.id} --stage ${stage} --graph-types #graphtypes# --profile ${cmd.profile}`
);

if (cmd.save) {
cmd.graphTypes = renderResult.graphTypes;
cmd.stage = stage;
cmd.id = resource.id;
await cacheUtil.saveCommand(cmd, "apigateway");
}
}

module.exports = {
handle,
};
59 changes: 36 additions & 23 deletions src/commands/dynamodb/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const authHelper = require("../../auth-helper");
const AWS = require("aws-sdk");
const inputUtil = require("../../input-util");
const metricsUtil = require("../../metrics-util");
const cacheUtil = require("../../cache-util");
const graphTypeMapping = require("./graph-type-mapping");
require("@mhlabs/aws-sdk-sso");
const { Spinner } = require("cli-spinner");
Expand All @@ -25,33 +26,45 @@ program
"AWS profile. Defaults to environment variable AWS_PROFILE",
"default"
)
.option("--save [view name]", "Saves the view for future retrieval")
.description("Browses and visualises DynamoDB metrics as ASCII diagrams")
.action(async (cmd) => {
authHelper.authenticate(cmd);
const dynamodb = new AWS.DynamoDB();
await handle(cmd);
});

let nextMarker = null;
let tableList = [];
if (!cmd.tableName) {
spinner.start();
const tables = await dynamodb.listTables().promise();
tableList.push(...tables.TableNames);
spinner.stop();
async function handle(cmd) {
authHelper.authenticate(cmd);
const dynamodb = new AWS.DynamoDB();

tableList = tableList.sort((a, b) =>
a.toLowerCase() > b.toLowerCase() ? 1 : -1
);
}
const resourceName =
cmd.name ||
(await inputUtil.autocomplete("Select table", tableList));
let nextMarker = null;
let tableList = [];
if (!cmd.tableName) {
spinner.start();
const tables = await dynamodb.listTables().promise();
tableList.push(...tables.TableNames);
spinner.stop();

await metricsUtil.render(
cmd,
resourceName,
graphTypeMapping,
`https://${AWS.config.region}.console.aws.amazon.com/dynamodb/home?region=${AWS.config.region}#tables:selected=${resourceName};tab=metrics`,
`awscii dynamodb --name ${resourceName} --graph-types #graphtypes# --profile ${cmd.profile}`
tableList = tableList.sort((a, b) =>
a.toLowerCase() > b.toLowerCase() ? 1 : -1
);
});
}
const resourceName =
cmd.name || (await inputUtil.autocomplete("Select table", tableList));

const renderResult = await metricsUtil.render(
cmd,
resourceName,
graphTypeMapping,
`https://${AWS.config.region}.console.aws.amazon.com/dynamodb/home?region=${AWS.config.region}#tables:selected=${resourceName};tab=metrics`,
`awscii dynamodb --name ${resourceName} --graph-types #graphtypes# --profile ${cmd.profile}`
);

if (cmd.save) {
cmd.graphTypes = renderResult.graphTypes;
await cacheUtil.saveCommand(cmd, "dynamodb", [resourceName]);
}
}

module.exports = {
handle
}
Loading

0 comments on commit 45705e7

Please sign in to comment.