@@ -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 {
|
||||
|
||||
@@ -35,7 +35,6 @@ export enum DefaultLoadingAction {
|
||||
export const INITIAL_LOADING_ACTIONS = [
|
||||
DefaultLoadingAction.UserGroups,
|
||||
DefaultLoadingAction.CostInsightsInitial,
|
||||
DefaultLoadingAction.CostInsightsProducts,
|
||||
];
|
||||
|
||||
export const getDefaultState = (loadingActions: string[]): Loading => {
|
||||
|
||||
Reference in New Issue
Block a user