Skip to content

Commit

Permalink
feature: inc close + general cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
khpeet committed May 19, 2023
1 parent ab0fd9c commit 0907c44
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 23 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ This application aggregates incidents, anomalies, and issues across many account
- Holistic, filterable/exportable view of open incidents, issues, and anomalies across many accounts.
- Auto refresh configuration of open incidents, issues, and anomalies
- Persistent links to correlate with individual incidents and issues
- Acknowledge and close issues directly in list view
- Acknowledge and close issues/incidents directly in list view
- Analytics for total issue count, accumulated issue minutes, MTTR, and % of issues closed under 5 minutes.
- Configurable linked dashboard that can be used as a tool for operational/reliability reviews. [Template provided here - this can be added to all accounts](dashboards/ops_template.json)

Expand Down
19 changes: 13 additions & 6 deletions nerdlets/command-center-v2-nerdlet/open-issues.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,9 @@ export default class OpenIssues extends React.Component {
}

Promise.all(issueProms).then(issues => {
console.log(issues);
let entities = "";
for (let k=0; k < issues.length; k++) {
entities = "";
if (issues[k].issues.length > 0) {
for (let i=0; i < issues[k].issues.length; i++) {
issues[k].issues[i].accountName = issues[k].account;
Expand All @@ -143,11 +144,15 @@ export default class OpenIssues extends React.Component {
}
}

if (typeof issues[k].issues[i].relatedEntityName !== "undefined" && issues[k].issues[i].relatedEntityName !== null) {
entities = issues[k].issues[i].relatedEntityName.toString();
}

const oneExportableResult = {
Account: issues[k].account,
Title: issues[k].issues[i].name,
IncidentCount: issues[k].issues[i].incidentCount,
Entities: issues[k].issues[i].relatedEntityName.toString(),
Entities: entities,
Priority: issues[k].issues[i].priority,
Muted: issues[k].issues[i].mutingState,
'Opened At': moment.unix(activated).format(
Expand Down Expand Up @@ -246,8 +251,6 @@ export default class OpenIssues extends React.Component {

if (nextCursor == null) {
allIssues = allIssues.concat(issues);
console.log('all');
console.log(allIssues)
const oneAccount = {
account: acct.name,
id: acct.id,
Expand Down Expand Up @@ -708,14 +711,18 @@ export default class OpenIssues extends React.Component {
});

if (res.error) {
console.debug(`Failed to close issue: ${issue} within account: ${acctId}`);
console.debug(`Failed to close issue: ${issueToClose} within account: ${rowAccountId}`);
Toast.showToast({
title: 'Failed to close issue.',
type: Toast.TYPE.CRITICAL
});
} else {
await this.removeRow(issueToClose) //remove row from table
this.setState({ closeModalHidden: true });
Toast.showToast({
title: 'Issue closed!',
type: Toast.TYPE.Normal
});
}
}

Expand Down Expand Up @@ -825,7 +832,7 @@ export default class OpenIssues extends React.Component {
<Table.Cell>{row.accountName}</Table.Cell>
<Table.Cell>{row.name}</Table.Cell>
<Table.Cell>{row.incidentCount}</Table.Cell>
<Table.Cell><p style={{wordBreak: 'break-word'}}>{row.relatedEntityName.toString()}</p></Table.Cell>
<Table.Cell><p style={{wordBreak: 'break-word'}}>{typeof row.relatedEntityName == "undefined" ? "" : row.relatedEntityName.toString()}</p></Table.Cell>
<Table.Cell>{row.priority}</Table.Cell>
<Table.Cell>
{moment.unix(a).format('MM/DD/YY, h:mm a')}
Expand Down
106 changes: 94 additions & 12 deletions nerdlets/command-center-v2-nerdlet/open-violations.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
HeadingText,
Modal,
Icon,
NerdGraphMutation,
NerdGraphQuery,
Spinner,
TextField,
Expand Down Expand Up @@ -42,7 +43,9 @@ export default class OpenIncidents extends React.Component {
rowAccountId: null,
rowIncidentId: null,
ackModalHidden: true,
closeModalHidden: true,
incToAck: null,
incToClose: null,
currentTime: null
};
}
Expand Down Expand Up @@ -87,7 +90,7 @@ export default class OpenIncidents extends React.Component {
Promise.all(vioProms).then(violations => {
const allViolations = [];
for (const vSet of violations) {
const vArray = vSet.violations.map(v => v.deprecatedIncidentId);
const vArray = vSet.violations.map(v => `'${v.incidentId}'`).join(',');
allViolations.push(this.getViolationData(vSet, vArray.toString()));
}

Expand All @@ -110,7 +113,7 @@ export default class OpenIncidents extends React.Component {
let noteDisplay = null;
let noteLink = null;
for (let p = 0; p < links.length; p++) {
if (formattedTable[k].deprecatedIncidentId == Number(links[p].id)) {
if (formattedTable[k].incidentId == Number(links[p].id)) {
noteDisplay = links[p].document.displayText;
noteLink = links[p].document.linkText;
break;
Expand Down Expand Up @@ -233,7 +236,7 @@ export default class OpenIncidents extends React.Component {

switch (clickedCol) {
case 'ID':
translated = 'deprecatedIncidentId';
translated = 'incidentId';
break;
case 'Account':
translated = 'accountName';
Expand Down Expand Up @@ -310,7 +313,7 @@ export default class OpenIncidents extends React.Component {
this.setState({
linkModalHidden: false,
rowAccountId: row['account.id'],
rowIncidentId: row.deprecatedIncidentId
rowIncidentId: row.incidentId
});
}

Expand Down Expand Up @@ -343,7 +346,7 @@ export default class OpenIncidents extends React.Component {

updateLinkCell(d, l, iKey) {
const currentIndex = this.state.tableData.findIndex(
inc => inc.deprecatedIncidentId === iKey
inc => inc.incidentId === iKey
);
const tableCopy = [...this.state.tableData];
tableCopy[currentIndex].display = d;
Expand Down Expand Up @@ -425,7 +428,7 @@ export default class OpenIncidents extends React.Component {

linksFromNerdStore.forEach(lnk => {
const index = loadedData.findIndex(
v => v.deprecatedIncidentId == Number(lnk.id)
v => v.incidentId == Number(lnk.id)
);
if (index === -1) {
this.deleteLinkFromNerdStore(lnk.id);
Expand All @@ -445,7 +448,7 @@ export default class OpenIncidents extends React.Component {
openAckModal(row) {
this.setState({
ackModalHidden: false,
incToAck: row.deprecatedIncidentId
incToAck: row.incidentId
});
}

Expand Down Expand Up @@ -521,6 +524,64 @@ export default class OpenIncidents extends React.Component {
});
}

openCloseModal(row) {
this.setState({
closeModalHidden: false,
incToClose: row.incidentId,
rowAccountId: row['account.id']
});
}

_onCloseModal() {
this.setState({
closeModalHidden: true,
incToClose: null,
rowAccountId: null
});
}

removeRow(incidentToClose) {
let { filteredTableData } = this.state;

let filteredTableDataCopy = filteredTableData.filter(item => item.incidentId !== incidentToClose)

this.setState({
filteredTableData: filteredTableDataCopy
});
}

async closeIncident() {
const { incToClose, rowAccountId } = this.state;
let mutation = `
mutation {
aiIssuesCloseIncident(accountId: ${rowAccountId}, incidentId: "${incToClose}") {
error
incidentId
accountId
}
}
`;

const res = await NerdGraphMutation.mutate({
mutation: mutation
});

if (res.error || res.data.aiIssuesCloseIncident.error) {
console.debug(`Failed to close incident: ${incToClose} within account: ${rowAccountId}`);
Toast.showToast({
title: 'Failed to close incident.',
type: Toast.TYPE.CRITICAL
});
} else {
await this.removeRow(incToClose) //remove row from table
this.setState({ closeModalHidden: true });
Toast.showToast({
title: 'Incident closed!',
type: Toast.TYPE.Normal
});
}
}

renderTable() {
const {
searchText,
Expand Down Expand Up @@ -567,9 +628,6 @@ export default class OpenIncidents extends React.Component {
if (header == 'Ack') {
toolTipText = 'No GQL API available for incident acknowledgement';
}
if (header == 'Close') {
toolTipText = 'No GQL API available for incident closure';
}
if (header == 'Description') {
toolTipText = 'Custom Violation Description'
}
Expand All @@ -594,7 +652,7 @@ export default class OpenIncidents extends React.Component {
<Table.Row key={p}>
<Table.Cell>
<a href={row.incidentLink} target="_blank" rel="noreferrer">
{row.deprecatedIncidentId}
{row.incidentId}
</a>
</Table.Cell>
<Table.Cell><p>{row.accountName}</p></Table.Cell>
Expand Down Expand Up @@ -632,7 +690,7 @@ export default class OpenIncidents extends React.Component {
</Table.Cell>
<Table.Cell>
<Button
disabled
onClick={() => this.openCloseModal(row)}
type={Button.TYPE.PRIMARY}
iconType={
Button.ICON_TYPE.INTERFACE__OPERATIONS__ALERT__A_REMOVE
Expand Down Expand Up @@ -777,6 +835,30 @@ export default class OpenIncidents extends React.Component {
No
</Button>
</Modal>
<Modal
hidden={this.state.closeModalHidden}
onClose={() => this._onCloseModal()}
>
<HeadingText>
<strong>
Are you sure you want to close this incident?
</strong>
</HeadingText>
<Button
type={Button.TYPE.PRIMARY}
className="modalBtn"
onClick={() => this.closeIncident()}
>
Yes
</Button>
<Button
type={Button.TYPE.DESTRUCTIVE}
className="modalBtn"
onClick={() => this._onCloseModal()}
>
No
</Button>
</Modal>
</>
);
}
Expand Down
1 change: 0 additions & 1 deletion nerdlets/command-center-v2-nerdlet/splash.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ export default class Splash extends React.Component {

if (nextCursor == null) {
anAccountsIssues = anAccountsIssues.concat(issueCounts);
console.log(anAccountsIssues);

let criticalCount = anAccountsIssues.filter(issue => {
let priorityObj = issue.tags.find(k => k.key == 'priority');
Expand Down
4 changes: 2 additions & 2 deletions nerdlets/command-center-v2-nerdlet/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ module.exports = {
{
actor {
account(id: ${account}) {
nrql(query: "SELECT deprecatedIncidentId, priority FROM (SELECT uniqueCount(event) as 'total', latest(event) as 'state', latest(priority) as 'priority' FROM NrAiIncident where event in ('open','close') facet deprecatedIncidentId limit max) where total=1 and state='open' limit max ${time}") {
nrql(query: "SELECT incidentId, priority FROM (SELECT uniqueCount(event) as 'total', latest(event) as 'state', latest(priority) as 'priority' FROM NrAiIncident where event in ('open','close') and evaluationType != 'anomaly' facet incidentId limit max) where total=1 and state='open' limit max ${time}") {
results
}
}
Expand All @@ -80,7 +80,7 @@ module.exports = {
{
actor {
account(id: ${account}) {
nrql(query: "FROM NrAiIncident SELECT deprecatedIncidentId, account.id, title, targetName, policyName, conditionName, openTime, priority, muted, incidentLink, description where deprecatedIncidentId IN (${vios}) and event = 'open' LIMIT MAX ${time}") {
nrql(query: "FROM NrAiIncident SELECT incidentId, account.id, title, targetName, policyName, conditionName, openTime, priority, muted, incidentLink, description where incidentId IN (${vios}) and event = 'open' LIMIT MAX ${time}") {
results
}
}
Expand Down
2 changes: 1 addition & 1 deletion nr1.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"schemaType": "NERDPACK",
"id": "4d35196c-e69c-4fa9-b90c-43c6e3327e66",
"id": "c063e59f-9b12-41b0-8a56-bd315b879c9d",
"displayName": "Command Center V2",
"description": "Consolidated view of incidents, issues and analytics across many accounts"
}
Binary file modified screenshots/open_issues.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 0907c44

Please sign in to comment.