feat: make engineer threshold configurable

Signed-off-by: Chris Langhout <clanghout@bol.com>
This commit is contained in:
Chris Langhout
2022-11-23 13:10:55 +01:00
parent f9ca3c28d6
commit 9de358c361
9 changed files with 44 additions and 11 deletions
+1
View File
@@ -373,6 +373,7 @@ auth:
development: {}
costInsights:
engineerCost: 200000
engineerThreshold: 0.5
products:
computeEngine:
name: Compute Engine
+13
View File
@@ -222,6 +222,19 @@ costInsights:
rate: 3.5
```
### costChangeNegligibleThreshold (Optional; default 0.5)
This threshold determines whether to show 'Negligible', or a percentage with a fraction of 'engineers' for cost savings or cost excess on top of the charts.
A threshold of 0.5 means that `Negligible` is shown when the difference in costs is lower than that fraction of engineers in that time frame,
and show `XX% or ~N engineers` when it's above the threshold.
```yaml
## ./app-config.yaml
costInsights:
engineerCost: 200000
engineerThreshold: 0.5
```
## Alerts
The CostInsightsApi `getAlerts` method may return any type of alert or recommendation (called collectively "Action Items" in Cost Insights) that implements the [Alert type](https://github.com/backstage/backstage/blob/master/plugins/cost-insights/src/types/Alert.ts). This allows you to deliver any alerts or recommendations specific to your infrastructure or company migrations.
+1
View File
@@ -237,6 +237,7 @@ export type ConfigContextProps = {
products: Product[];
icons: Icon[];
engineerCost: number;
engineerThreshold: number;
currencies: Currency[];
};
@@ -19,7 +19,6 @@ import classnames from 'classnames';
import {
CurrencyType,
Duration,
EngineerThreshold,
GrowthType,
} from '../../types';
import { ChangeStatistic } from '@backstage/plugin-cost-insights-common';
@@ -42,7 +41,7 @@ export const CostGrowth = (props: CostGrowthProps) => {
const { change, duration } = props;
const styles = useStyles();
const { engineerCost } = useConfig();
const { engineerCost, engineerThreshold } = useConfig();
const [currency] = useCurrency();
// Only display costs in absolute values
@@ -55,7 +54,7 @@ export const CostGrowth = (props: CostGrowthProps) => {
// If a ratio cannot be calculated, don't format.
const growth = notEmpty(change.ratio)
? growthOf({ ratio: change.ratio, amount: engineers })
? growthOf({ ratio: change.ratio, amount: engineers }, engineerThreshold)
: null;
// Determine if growth is significant enough to highlight
const classes = classnames({
@@ -63,7 +62,7 @@ export const CostGrowth = (props: CostGrowthProps) => {
[styles.savings]: growth === GrowthType.Savings,
});
if (engineers < EngineerThreshold) {
if (engineers < engineerThreshold) {
return <span className={classes}>Negligible</span>;
}
@@ -23,6 +23,7 @@ import { growthOf } from '../../utils/change';
import { GrowthType } from '../../types';
import { useCostGrowthStyles as useStyles } from '../../utils/styles';
import { ChangeStatistic, Maybe } from '@backstage/plugin-cost-insights-common';
import { useConfig } from '../../hooks';
/** @public */
export type CostGrowthIndicatorProps = TypographyProps & {
@@ -35,10 +36,11 @@ export type CostGrowthIndicatorProps = TypographyProps & {
/** @public */
export const CostGrowthIndicator = (props: CostGrowthIndicatorProps) => {
const { engineerThreshold } = useConfig();
const { change, formatter, className, ...extraProps } = props;
const classes = useStyles();
const growth = growthOf(change);
const growth = growthOf(change, engineerThreshold);
const classNames = classnames(classes.indicator, className, {
[classes.excess]: growth === GrowthType.Excess,
@@ -70,6 +70,7 @@ export type ConfigContextProps = {
products: Product[];
icons: Icon[];
engineerCost: number;
engineerThreshold: number;
currencies: Currency[];
};
@@ -83,6 +84,7 @@ const defaultState: ConfigContextProps = {
products: [],
icons: [],
engineerCost: 0,
engineerThreshold: 0.5,
currencies: defaultCurrencies,
};
@@ -183,11 +185,19 @@ export const ConfigProvider = ({ children }: PropsWithChildren<{}>) => {
return c.getNumber('costInsights.engineerCost');
}
function getEngineerThreshold(): number {
return (
c.getOptionalNumber('costInsights.engineerThreshold') ??
defaultState.engineerThreshold
);
}
function getConfig() {
const baseCurrency = getBaseCurrency();
const products = getProducts();
const metrics = getMetrics();
const engineerCost = getEngineerCost();
const engineerThreshold = getEngineerThreshold();
const icons = getIcons();
const currencies = getCurrencies();
@@ -200,6 +210,7 @@ export const ConfigProvider = ({ children }: PropsWithChildren<{}>) => {
metrics,
products,
engineerCost,
engineerThreshold,
icons,
currencies,
}));
@@ -95,6 +95,7 @@ export const MockConfigProvider = (props: MockConfigProviderProps) => {
products: [],
icons: [],
engineerCost: 0,
engineerThreshold: 0.5,
currencies: [],
};
@@ -63,7 +63,7 @@ describe.each`
expected: GrowthType;
}) => {
it(`should display ${GrowthMap[expected]}`, () => {
expect(growthOf({ ratio, amount })).toBe(expected);
expect(growthOf({ ratio, amount }, EngineerThreshold)).toBe(expected);
});
},
);
+10 -5
View File
@@ -18,7 +18,6 @@ import {
Cost,
ChangeStatistic,
ChangeThreshold,
EngineerThreshold,
GrowthType,
MetricData,
Duration,
@@ -29,8 +28,11 @@ import { inclusiveStartDateOf } from './duration';
import { notEmpty } from './assert';
// Used for displaying status colors
export function growthOf(change: ChangeStatistic): GrowthType {
const exceedsEngineerThreshold = Math.abs(change.amount) >= EngineerThreshold;
export function growthOf(
change: ChangeStatistic,
engineerThreshold: number,
): GrowthType {
const exceedsEngineerThreshold = Math.abs(change.amount) >= engineerThreshold;
if (notEmpty(change.ratio)) {
if (exceedsEngineerThreshold && change.ratio >= ChangeThreshold.upper) {
@@ -41,9 +43,12 @@ export function growthOf(change: ChangeStatistic): GrowthType {
return GrowthType.Savings;
}
} else {
if (exceedsEngineerThreshold && change.amount > 0) return GrowthType.Excess;
if (exceedsEngineerThreshold && change.amount < 0)
if (exceedsEngineerThreshold && change.amount > 0) {
return GrowthType.Excess;
}
if (exceedsEngineerThreshold && change.amount < 0) {
return GrowthType.Savings;
}
}
return GrowthType.Negligible;