added optional currencies config

Signed-off-by: Vannarath Poeu <vannarath.poeu@gmail.com>
This commit is contained in:
Vannarath Poeu
2021-08-05 11:55:09 +08:00
parent edcb115530
commit 87ef530efe
6 changed files with 136 additions and 7 deletions
+25
View File
@@ -369,6 +369,31 @@ costInsights:
default: true
MSC:
name: Monthly Subscribers
currencies:
engineers:
label: 'Engineers 🛠'
unit: 'engineer'
usd:
label: 'US Dollars 💵'
kind: 'USD'
unit: 'dollar'
prefix: '$'
rate: 1
carbonOffsetTons:
label: 'Carbon Offset Tons ♻️⚖️s'
kind: 'CARBON_OFFSET_TONS'
unit: 'carbon offset ton'
rate: 3.5
beers:
label: 'Beers 🍺'
kind: 'BEERS'
unit: 'beer'
rate: 4.5
pintsIceCream:
label: 'Pints of Ice Cream 🍦'
kind: 'PINTS_OF_ICE_CREAM'
unit: 'ice cream pint'
rate: 5.5
homepage:
clocks:
- label: UTC
+30
View File
@@ -148,6 +148,36 @@ costInsights:
name: Metric C
```
### Currencies (Optional)
In the `Cost Overview` panel, users can choose from a dropdown of currencies to see costs in, such as Engineers or USD. Currencies must be defined as keys on the `currencies` field. A user-friendly label and unit are **required**. If not set, the `defaultCurrencies` in `currenc.ts` will be used.
A currency without `kind` is reserved to calculate cost for `engineers`. There should only be one currency without `kind`.
```yaml
## ./app-config.yaml
costInsights:
engineerCost: 200000
products:
productA:
name: Some Cloud Product
icon: storage
productB:
name: Some Other Cloud Product
icon: data
currencies:
metricA:
currencyA:
label: Currency A
unit: Unit A
currencyB:
label: Currency B
kind: CURRENCY_B
unit: Unit B
prefix: B
rate: 3.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.
+34
View File
@@ -48,5 +48,39 @@ interface Config {
default?: boolean;
};
};
currencies?: {
[id: string]: {
/**
* @visibility frontend
*/
label: string;
/**
* @visibility frontend
*/
unit: string;
/**
* @visibility frontend
*/
kind?: string;
/**
* @visibility frontend
*/
prefix?: string;
/**
* @visibility frontend
*/
rate?: number;
/**
* @visibility frontend
*/
default?: boolean;
};
};
};
}
+30 -2
View File
@@ -24,12 +24,12 @@ import React, {
import { Config as BackstageConfig } from '@backstage/config';
import { Currency, Icon, Metric, Product } from '../types';
import { getIcon } from '../utils/navigation';
import { validateMetrics } from '../utils/config';
import { validateCurrencies, validateMetrics } from '../utils/config';
import { defaultCurrencies } from '../utils/currency';
import { configApiRef, useApi } from '@backstage/core-plugin-api';
/*
* Config schema 2020-10-15
* Config schema 2021-08-05
*
* costInsights:
* engineerCost: 200000
@@ -46,6 +46,16 @@ import { configApiRef, useApi } from '@backstage/core-plugin-api';
* default: true
* metricB:
* name: Metric B
* currencies:
* currencyA:
* label: Currency A
* unit: Unit A
* currencyB:
* label: Currency B
* kind: CURRENCY_B
* unit: Unit B
* prefix: B
* rate: 3.5
*/
export type ConfigContextProps = {
@@ -96,6 +106,21 @@ export const ConfigProvider = ({ children }: PropsWithChildren<{}>) => {
return [];
}
function getCurrencies(): Currency[] {
const currencies = c.getOptionalConfig('costInsights.currencies');
if (currencies) {
return currencies.keys().map(key => ({
label: currencies.getString(`${key}.label`),
unit: currencies.getString(`${key}.unit`),
kind: currencies.getOptionalString(`${key}.kind`) || null,
prefix: currencies.getOptionalString(`${key}.prefix`),
rate: currencies.getOptionalNumber(`${key}.rate`),
}));
}
return defaultCurrencies;
}
function getIcons(): Icon[] {
const products = c.getConfig('costInsights.products');
const keys = products.keys();
@@ -115,8 +140,10 @@ export const ConfigProvider = ({ children }: PropsWithChildren<{}>) => {
const metrics = getMetrics();
const engineerCost = getEngineerCost();
const icons = getIcons();
const currencies = getCurrencies();
validateMetrics(metrics);
validateCurrencies(currencies);
setConfig(prevState => ({
...prevState,
@@ -124,6 +151,7 @@ export const ConfigProvider = ({ children }: PropsWithChildren<{}>) => {
products,
engineerCost,
icons,
currencies,
}));
setLoading(false);
@@ -21,8 +21,7 @@ import React, {
PropsWithChildren,
} from 'react';
import { Currency } from '../types';
import { findAlways } from '../utils/assert';
import { defaultCurrencies } from '../utils/currency';
import { useConfig } from './useConfig';
export type CurrencyContextProps = {
currency: Currency;
@@ -34,8 +33,12 @@ export const CurrencyContext = React.createContext<
>(undefined);
export const CurrencyProvider = ({ children }: PropsWithChildren<{}>) => {
const engineers = findAlways(defaultCurrencies, c => c.kind === null);
const [currency, setCurrency] = useState<Currency>(engineers);
const config = useConfig();
const currencies = config.currencies;
const engineers = currencies.find(currency => currency.kind === null);
const [currency, setCurrency] = useState<Currency>(
engineers || currencies[0],
);
return (
<CurrencyContext.Provider value={{ currency, setCurrency }}>
{children}
+10 -1
View File
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { Metric } from '../types';
import { Metric, Currency } from '../types';
export function validateMetrics(metrics: Metric[]) {
const defaults = metrics.filter(metric => metric.default);
@@ -24,3 +24,12 @@ export function validateMetrics(metrics: Metric[]) {
);
}
}
export function validateCurrencies(currencies: Currency[]) {
const withoutKinds = currencies.filter(currency => currency.kind === null);
if (withoutKinds.length > 1) {
throw new Error(
`Only one currency can be without kind. Found ${withoutKinds.length}`,
);
}
}