Merge pull request #12954 from kielosz/make-products-optional
Make products optional in Cloud Insights
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-cost-insights': patch
|
||||
---
|
||||
|
||||
Make `products` field optional in the config
|
||||
@@ -120,14 +120,24 @@ To expose the plugin to your users, you can integrate the `cost-insights` route
|
||||
|
||||
## Configuration
|
||||
|
||||
Cost Insights has only two required configuration fields: a map of cloud `products` for showing cost breakdowns and `engineerCost` - the average yearly cost of an engineer including benefits. Products must be defined as keys on the `products` field.
|
||||
Cost Insights has only one required configuration field: `engineerCost` - the average yearly cost of an engineer including benefits.
|
||||
|
||||
### Basic
|
||||
|
||||
```yaml
|
||||
## ./app-config.yaml
|
||||
costInsights:
|
||||
engineerCost: 200000
|
||||
```
|
||||
|
||||
### Products (Optional)
|
||||
|
||||
For showing cost breakdowns you can define a map of cloud products. They must be defined as keys on the `products` field. A user-friendly name is **required**.
|
||||
|
||||
You can optionally supply a product `icon` to display in Cost Insights navigation. See the [type file](https://github.com/backstage/backstage/blob/master/plugins/cost-insights/src/types/Icon.ts) for supported types and Material UI icon [mappings](https://github.com/backstage/backstage/blob/master/plugins/cost-insights/src/utils/navigation.tsx).
|
||||
|
||||
**Note:** Product keys should be unique and camelCased. Backstage does not support underscores in configuration keys.
|
||||
|
||||
### Basic
|
||||
|
||||
```yaml
|
||||
## ./app-config.yaml
|
||||
costInsights:
|
||||
|
||||
Vendored
+1
-1
@@ -21,7 +21,7 @@ export interface Config {
|
||||
*/
|
||||
engineerCost: number;
|
||||
|
||||
products: {
|
||||
products?: {
|
||||
[kind: string]: {
|
||||
/**
|
||||
* @visibility frontend
|
||||
|
||||
@@ -84,6 +84,7 @@ export const CostInsightsPage = () => {
|
||||
const accepted = useMemo(() => alerts.filter(isAlertAccepted), [alerts]);
|
||||
const dismissed = useMemo(() => alerts.filter(isAlertDismissed), [alerts]);
|
||||
|
||||
const isProductsDisplayed = !!config.products?.length;
|
||||
const isActionItemsDisplayed = !!active.length;
|
||||
const isAlertInsightsDisplayed = !!alerts.length;
|
||||
|
||||
@@ -247,14 +248,16 @@ export const CostInsightsPage = () => {
|
||||
return (
|
||||
<CostInsightsLayout groups={groups}>
|
||||
<Grid container wrap="nowrap">
|
||||
<Grid item>
|
||||
<Box position="sticky" top={20}>
|
||||
<CostInsightsNavigation
|
||||
products={products}
|
||||
alerts={active.length}
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
{(isProductsDisplayed || isActionItemsDisplayed) && (
|
||||
<Grid item>
|
||||
<Box position="sticky" top={20}>
|
||||
<CostInsightsNavigation
|
||||
products={products}
|
||||
alerts={active.length}
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
)}
|
||||
<Grid item xs>
|
||||
<Box
|
||||
display="flex"
|
||||
@@ -316,17 +319,19 @@ export const CostInsightsPage = () => {
|
||||
</Box>
|
||||
</Grid>
|
||||
</Collapse>
|
||||
{!isAlertInsightsDisplayed && <Divider />}
|
||||
<Grid item xs>
|
||||
<Box px={3} py={6}>
|
||||
<ProductInsights
|
||||
group={pageFilters.group}
|
||||
project={pageFilters.project}
|
||||
products={config.products}
|
||||
onLoaded={setProducts}
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
{!isAlertInsightsDisplayed && isProductsDisplayed && <Divider />}
|
||||
{isProductsDisplayed && (
|
||||
<Grid item xs>
|
||||
<Box px={3} py={6}>
|
||||
<ProductInsights
|
||||
group={pageFilters.group}
|
||||
project={pageFilters.project}
|
||||
products={config.products}
|
||||
onLoaded={setProducts}
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</Container>
|
||||
</Grid>
|
||||
|
||||
@@ -85,12 +85,15 @@ export const ConfigProvider = ({ children }: PropsWithChildren<{}>) => {
|
||||
|
||||
useEffect(() => {
|
||||
function getProducts(): Product[] {
|
||||
const products = c.getConfig('costInsights.products');
|
||||
return products.keys().map(key => ({
|
||||
kind: key,
|
||||
name: products.getString(`${key}.name`),
|
||||
aggregation: [0, 0],
|
||||
}));
|
||||
const products = c.getOptionalConfig('costInsights.products');
|
||||
if (products) {
|
||||
return products.keys().map(key => ({
|
||||
kind: key,
|
||||
name: products.getString(`${key}.name`),
|
||||
aggregation: [0, 0],
|
||||
}));
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function getMetrics(): Metric[] {
|
||||
@@ -122,13 +125,14 @@ export const ConfigProvider = ({ children }: PropsWithChildren<{}>) => {
|
||||
}
|
||||
|
||||
function getIcons(): Icon[] {
|
||||
const products = c.getConfig('costInsights.products');
|
||||
const keys = products.keys();
|
||||
|
||||
return keys.map(k => ({
|
||||
kind: k,
|
||||
component: getIcon(products.getOptionalString(`${k}.icon`)),
|
||||
}));
|
||||
const products = c.getOptionalConfig('costInsights.products');
|
||||
if (products) {
|
||||
return products.keys().map(k => ({
|
||||
kind: k,
|
||||
component: getIcon(products.getOptionalString(`${k}.icon`)),
|
||||
}));
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function getEngineerCost(): number {
|
||||
|
||||
@@ -21,6 +21,7 @@ import React, {
|
||||
SetStateAction,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useReducer,
|
||||
useState,
|
||||
} from 'react';
|
||||
@@ -32,6 +33,7 @@ import {
|
||||
INITIAL_LOADING_ACTIONS,
|
||||
} from '../utils/loading';
|
||||
import { useBackdropStyles as useStyles } from '../utils/styles';
|
||||
import { useConfig } from './useConfig';
|
||||
|
||||
export type LoadingContextProps = {
|
||||
state: Loading;
|
||||
@@ -54,7 +56,16 @@ function reducer(prevState: Loading, action: Partial<Loading>): Loading {
|
||||
|
||||
export const LoadingProvider = ({ children }: PropsWithChildren<{}>) => {
|
||||
const classes = useStyles();
|
||||
const actions = INITIAL_LOADING_ACTIONS;
|
||||
const { products } = useConfig();
|
||||
const actions = useMemo(
|
||||
() =>
|
||||
INITIAL_LOADING_ACTIONS.filter(
|
||||
action =>
|
||||
products.length ||
|
||||
action !== DefaultLoadingAction.CostInsightsProducts,
|
||||
),
|
||||
[products],
|
||||
);
|
||||
const [state, dispatch] = useReducer(reducer, getDefaultState(actions));
|
||||
const [isBackdropVisible, setBackdropVisible] = useState(false);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user