Skip to content

Commit

Permalink
feat: separate views from config and componentise
Browse files Browse the repository at this point in the history
  • Loading branch information
matewilk committed Mar 18, 2024
1 parent f81ce84 commit d6d7f76
Show file tree
Hide file tree
Showing 9 changed files with 561 additions and 272 deletions.
387 changes: 130 additions & 257 deletions nerdlets/nr1-attribute-explorer/attribute-explorer.js

Large diffs are not rendered by default.

90 changes: 90 additions & 0 deletions nerdlets/nr1-attribute-explorer/components/AttributeChart.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// AttributeChart.js
import React from "react";
import {
PlatformStateContext,
NrqlQuery,
GridItem,
Tile,
HeadingText,
BarChart,
Spinner,
Stack,
StackItem,
SectionMessage,
} from "nr1";

const AttributeChartQuery = ({
accountId,
query,
pollInterval,
timeRange,
onClickBar,
}) => {
return (
<NrqlQuery
accountIds={[accountId]}
query={query}
pollInterval={pollInterval}
timeRange={timeRange}
>
{({ loading, error, data }) => {
if (loading) {
return <Spinner type={Spinner.TYPE.DOT} />;
}

if (error) {
return (
<Stack>
<StackItem>
<SectionMessage
type={SectionMessage.TYPE.WARNING}
title="Chart failed to display."
description={error.message}
/>
</StackItem>
</Stack>
);
}

if (data != null && data.length > 0) {
return <BarChart data={data} fullWidth onClickBar={onClickBar} />;
} else {
return <div className="myNoData">No values.</div>;
}
}}
</NrqlQuery>
);
};

const AttributeChart = ({
accountId,
attribute,
query,
onClickBar,
pollInterval = 60000, // 60 seconds,
}) => {
return (
<GridItem columnSpan={2}>
<Tile type={Tile.TYPE.PLAIN} sizeType={Tile.SIZE_TYPE.SMALL}>
<div className="myHeader">
<HeadingText type={HeadingText.TYPE.HEADING_5}>
{attribute}
</HeadingText>
</div>
<PlatformStateContext.Consumer>
{(platformState) => (
<AttributeChartQuery
accountId={accountId}
query={query}
pollInterval={pollInterval}
platformState={platformState}
onClickBar={onClickBar}
/>
)}
</PlatformStateContext.Consumer>
</Tile>
</GridItem>
);
};

export { AttributeChart };
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React from "react";
import { Grid } from "nr1";
import { AttributeChart } from "./AttributeChart";

const AttributeChartsContainer = ({
accountId,
guid,
eventTypes,
currentEvent,
attribWhere,
duration,
currentMetric,
attributes,
setAttribute,
}) => {
// Construct the NRQL query
const values = [
`percentile(${duration[currentEvent]}, 50)`,
`percentile(${duration[currentEvent]}, 75)`,
`percentile(${duration[currentEvent]}, 90)`,
`percentile(${duration[currentEvent]}, 99)`,
`average(${duration[currentEvent]})`,
"count(*)",
];
const attributeString = "ATTRIBUTE";
const chartQuery = `SELECT ${values[currentMetric]} FROM ${eventTypes[currentEvent]} WHERE entityGuid = '${guid}'${attribWhere} FACET ${attributeString}`;
return (
<Grid>
{/* Loop through all the attributes and build an array of charts */}
{attributes.map((attribute) => (
<AttributeChart
key={attribute}
accountId={accountId}
attribute={attribute}
query={chartQuery.replace("ATTRIBUTE", attribute)}
// Uncomment to set the refresh rate to 30 seconds (or any other value you want to use) - default is 60 seconds.
// pollInterval={30000}
pollInterval={60000}
onClickBar={(evt) => setAttribute(attribute, evt.metadata.name)}
/>
))}
</Grid>
);
};

export { AttributeChartsContainer };
15 changes: 15 additions & 0 deletions nerdlets/nr1-attribute-explorer/components/ChartTile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from "react";
import { Tile, HeadingText, GridItem } from "nr1";

const ChartTile = ({ title, chart, columnSpan }) => {
return (
<GridItem columnSpan={columnSpan}>
<Tile type={Tile.TYPE.PLAIN} sizeType={Tile.SIZE_TYPE.SMALL}>
<HeadingText className="mySpaceBelow myHeader">{title}</HeadingText>
{chart}
</Tile>
</GridItem>
);
};

export { ChartTile };
16 changes: 16 additions & 0 deletions nerdlets/nr1-attribute-explorer/components/CustomDropdown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from "react";
import { Dropdown, DropdownItem } from "nr1";

const CustomDropdown = ({ title, options, onSelect, iconType }) => {
return (
<Dropdown title={title} iconType={iconType} className="mySpaceRight">
{options.map((option, index) => (
<DropdownItem key={index} onClick={() => onSelect(index)}>
{option}
</DropdownItem>
))}
</Dropdown>
);
};

export { CustomDropdown };
35 changes: 35 additions & 0 deletions nerdlets/nr1-attribute-explorer/components/HeaderChart.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React from "react";
import {
PlatformStateContext,
NrqlQuery,
LineChart,
HistogramChart,
} from "nr1";

const HeaderChart = ({ accountId, query, chartType, pollInterval = 60000 }) => {
return (
<PlatformStateContext.Consumer>
{(platformState) => (
<NrqlQuery
accountIds={[accountId]}
query={query}
pollInterval={pollInterval}
timeRange={platformState.timeRange}
>
{({ data }) => {
switch (chartType) {
case "line":
return <LineChart data={data} fullWidth />;
case "histogram":
return <HistogramChart data={data} fullWidth />;
default:
return null;
}
}}
</NrqlQuery>
)}
</PlatformStateContext.Consumer>
);
};

export { HeaderChart };
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import React, { useEffect, useState } from "react";
import { Grid, ChartGroup } from "nr1";
import { HeaderChart } from "./HeaderChart";
import { ChartTile } from "./ChartTile";

const HeaderChartsContainer = ({
accountId,
guid,
eventTypes,
currentEvent,
attribWhere,
duration,
histogramCeiling,
}) => {
const [responseQuery, setResponseQuery] = useState("");
const [countQuery, setCountQuery] = useState("");
const [histogramQuery, setHistogramQuery] = useState("");

useEffect(() => {
const errorNRQL = [
", filter(count(*), WHERE error) as 'Errors'", // Transactions
"", // TransactionError (no where clause here, as all are errors!)
", filter(count(*), WHERE error.message IS NOT NULL OR error.class IS NOT NULL) as Errors", // Spans (APM)
", filter(count(*), WHERE otel.status_code = 'ERROR') as Errors", // Spans (OTEL)
];

// The response time query for the first chart.
const newResponseQuery = `SELECT percentile(${duration[currentEvent]}, 50, 75, 90, 99) as Duration, average(${duration[currentEvent]}) as 'Avg duration' FROM ${eventTypes[currentEvent]} WHERE entityGuid = '${guid}'${attribWhere} TIMESERIES AUTO`;
// The count and error query for the second chart.
const newCountQuery = `SELECT count(*) as 'Count'${errorNRQL[currentEvent]} FROM ${eventTypes[currentEvent]} WHERE entityGuid = '${guid}'${attribWhere} TIMESERIES AUTO`;
// The histogram query for the third chart.
const newHistogramQuery = `SELECT histogram(${duration[currentEvent]}, ${histogramCeiling}, 20) as Duration FROM ${eventTypes[currentEvent]} WHERE entityGuid = '${guid}'${attribWhere}`;

setResponseQuery(newResponseQuery);
setCountQuery(newCountQuery);
setHistogramQuery(newHistogramQuery);
}, [
accountId,
guid,
eventTypes,
currentEvent,
attribWhere,
duration,
histogramCeiling,
]);

return (
<Grid>
<ChartGroup>
<ChartTile
title="Performance over time"
chart={
<HeaderChart
accountId={accountId}
query={responseQuery}
chartType="line"
/>
}
columnSpan={5}
/>
<ChartTile
title="Counts over time"
chart={
<HeaderChart
accountId={accountId}
query={countQuery}
chartType="line"
/>
}
columnSpan={4}
/>
<ChartTile
title="Response time histogram"
chart={
<HeaderChart
accountId={accountId}
query={histogramQuery}
chartType="histogram"
/>
}
columnSpan={3}
/>
</ChartGroup>
</Grid>
);
};

export { HeaderChartsContainer };
33 changes: 18 additions & 15 deletions nerdlets/nr1-attribute-explorer/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import React from 'react';
import { NerdletStateContext, EntityByGuidQuery, Spinner, StackItem, SectionMessage } from 'nr1';
import AttributeExplorer from './attribute-explorer';
import React from "react";
import {
NerdletStateContext,
EntityByGuidQuery,
Spinner,
StackItem,
SectionMessage,
} from "nr1";
import AttributeExplorer from "./attribute-explorer";

/*
Main parent class, checks for an entity guid and calls the main class (AttributeExplorer).
Expand All @@ -13,11 +19,10 @@ export default class Nr1AttributeExplorerNerdlet extends React.Component {

// Method to get and return the entity guid from the nerdlet state.
getEntityGuid(nerd) {
if (nerd.hasOwnProperty('entityGuid')) {
if (nerd.hasOwnProperty("entityGuid")) {
console.log(nerd);
return nerd.entityGuid;
}
else {
} else {
return null;
}
}
Expand All @@ -27,7 +32,7 @@ export default class Nr1AttributeExplorerNerdlet extends React.Component {
render() {
return (
<NerdletStateContext.Consumer>
{(nerdletState) =>
{(nerdletState) => (
<EntityByGuidQuery entityGuid={this.getEntityGuid(nerdletState)}>
{({ loading, error, data }) => {
if (loading) {
Expand All @@ -38,20 +43,18 @@ export default class Nr1AttributeExplorerNerdlet extends React.Component {
return (
<StackItem>
<SectionMessage
type={SectionMessage.TYPE.CRITICAL}
title="Error in lauching the app."
type={SectionMessage.TYPE.CRITICAL}
title="Error in lauching the app."
description={error.message}
/>
</StackItem>
</StackItem>
);
}

if (data.entities.length === 0) {
return (
<StackItem>
<SectionMessage
description="No service found, please launch the app from within an APM or OTEL service."
/>
<SectionMessage description="No service found, please launch the app from within an APM or OTEL service." />
</StackItem>
);
}
Expand All @@ -67,8 +70,8 @@ export default class Nr1AttributeExplorerNerdlet extends React.Component {
);
}}
</EntityByGuidQuery>
}
)}
</NerdletStateContext.Consumer>
);
}
}
}
Loading

0 comments on commit d6d7f76

Please sign in to comment.