Merge branch 'backstage:master' into master

This commit is contained in:
matteosilv
2022-07-01 19:12:29 +02:00
committed by GitHub
38 changed files with 610 additions and 289 deletions
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/plugin-cost-insights': patch
---
Move cost-insights data specific API types (non react) into an @backstage/plugin-cost-insights-common
isomorphic package. This allows these types to be shared in any backend packages or other cost-insight
modules.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-cost-insights-common': minor
---
Introduces a new isomorphic @backstage/plugin-cost-insight-common package to contain shared types across all other cost insights packages and modules.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-search-react': patch
---
Fix search pagination to reset page cursor also when a term is cleared.
@@ -0,0 +1 @@
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
@@ -0,0 +1 @@
# @backstage/plugin-cost-insights-common
+8
View File
@@ -0,0 +1,8 @@
# Cost Insights Common
Shared isomorphic code for the cost-insights plugin.
## Links
- [Frontend part of the plugin](https://github.com/backstage/backstage/tree/master/plugins/cost-insights)
- [The Backstage homepage](https://backstage.io)
@@ -0,0 +1,92 @@
## API Report File for "@backstage/plugin-cost-insights-common"
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
// @public (undocumented)
export interface ChangeStatistic {
amount: number;
ratio?: number;
}
// @public (undocumented)
export interface Cost {
// (undocumented)
aggregation: DateAggregation[];
// (undocumented)
change?: ChangeStatistic;
// (undocumented)
groupedCosts?: Record<string, Cost[]>;
// (undocumented)
id: string;
// (undocumented)
trendline?: Trendline;
}
// @public (undocumented)
export type DateAggregation = {
date: string;
amount: number;
};
// @public
export interface Entity {
// (undocumented)
aggregation: [number, number];
// (undocumented)
change: ChangeStatistic;
// (undocumented)
entities: Record<string, Entity[]>;
// (undocumented)
id: Maybe<string>;
}
// @public (undocumented)
export type Group = {
id: string;
};
// @public (undocumented)
export type Maybe<T> = T | null;
// @public (undocumented)
export type Metric = {
kind: string;
name: string;
default: boolean;
};
// @public (undocumented)
export interface MetricData {
// (undocumented)
aggregation: DateAggregation[];
// (undocumented)
change: ChangeStatistic;
// (undocumented)
format: 'number' | 'currency';
// (undocumented)
id: string;
}
// @public (undocumented)
export interface Product {
// (undocumented)
kind: string;
// (undocumented)
name: string;
}
// @public (undocumented)
export interface Project {
// (undocumented)
id: string;
// (undocumented)
name?: string;
}
// @public (undocumented)
export type Trendline = {
slope: number;
intercept: number;
};
```
+42
View File
@@ -0,0 +1,42 @@
{
"name": "@backstage/plugin-cost-insights-common",
"description": "Common functionalities for the cost-insights plugin",
"version": "0.0.0",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"private": false,
"publishConfig": {
"access": "public",
"main": "dist/index.cjs.js",
"module": "dist/index.esm.js",
"types": "dist/index.d.ts"
},
"backstage": {
"role": "common-library"
},
"homepage": "https://backstage.io",
"repository": {
"type": "git",
"url": "https://github.com/backstage/backstage",
"directory": "plugins/cost-insights-common"
},
"keywords": [
"backstage"
],
"scripts": {
"build": "backstage-cli package build",
"lint": "backstage-cli package lint",
"test": "backstage-cli package test",
"prepack": "backstage-cli package prepack",
"postpack": "backstage-cli package postpack",
"clean": "backstage-cli package clean"
},
"dependencies": {},
"devDependencies": {
"@backstage/cli": "^0.18.0-next.1"
},
"files": [
"dist"
]
}
+23
View File
@@ -0,0 +1,23 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Provides shared objects useful for interacting with the cost-insights plugin.
*
* @packageDocumentation
*/
export * from './types';
@@ -0,0 +1,17 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export {};
@@ -0,0 +1,32 @@
/*
* Copyright 2020 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @public
*/
export interface ChangeStatistic {
/**
* The ratio of change from one duration to another, expressed as: (newSum - oldSum) / oldSum
* If a ratio cannot be calculated - such as when a new or old sum is zero,
* the ratio can be omitted and where applicable, ∞ or -∞ will display based on amount.
*/
ratio?: number;
/**
* The actual USD change between time periods (can be negative if costs decreased)
*/
amount: number;
}
@@ -18,6 +18,9 @@ import { DateAggregation } from './DateAggregation';
import { ChangeStatistic } from './ChangeStatistic';
import { Trendline } from './Trendline';
/**
* @public
*/
export interface Cost {
id: string;
aggregation: DateAggregation[];
@@ -14,7 +14,15 @@
* limitations under the License.
*/
/**
* @public
*/
export type DateAggregation = {
date: string; // YYYY-MM-DD
/**
* The date aggregation as string.
* @example YYYY-MM-DD
*/
date: string;
amount: number;
};
@@ -0,0 +1,124 @@
/*
* Copyright 2020 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ChangeStatistic } from './ChangeStatistic';
import { Maybe } from './Maybe';
/**
*
* An entity is a tree-like structure that represents any unique
* product or service that generates cost over a fixed period of time.
* An entity could be atomic or composite. An atomic entity is indivisible
* and cannot be broken into sub-entities.
*
* A composite entity is divided into sub-entities that account for portions
* of the total cost **over the same time period**. The root entity is
* expected to only have _one_ Record consisting of the sub-entities to display
* in the product panel (keyed by the entity type, such as "service" for
* compute entities).
*
* The root sub-entities may have multiple breakdowns - for example, a
* breakdown of an entity cost by SKU vs deployment environment. The sum
* aggregated cost of each keyed breakdown should equal the sub-entity's cost.
*
* Entities with null ids are considered "unlabeled" - costs without attribution.
* If an entity is a composite, it may only have one (1) null child but may have any number of
* null grandchildren.
*
* @public
*
* @example
* Here's an example composite entity:
* ```
* const compositeEntity = {
* id: 'product',
* aggregation: [0, 200],
* change: {
* ratio: 2000,
* amount: 200
* },
* entities: {
* service: [
* {
* id: 'service-a',
* aggregation: [0, 100],
* change: {
* ratio: 100,
* amount: 100
* },
* entities: {}
* },
* {
* id: 'service-b',
* aggregation: [0, 100],
* change: {
* ratio: 100,
* amount: 100
* },
* entities: {
* SKU: [
* {
* id: 'service-b-sku-a',
* aggregation: [0, 25],
* change: {
* ratio: 25,
* amount: 25
* },
* entities: {}
* },
* {
* id: null, // Unlabeled cost for service-b
* aggregation: [0, 75],
* change: {
* ratio: 75,
* amount: 75
* },
* entities: {}
* },
* ],
* deployment: [
* {
* id: 'service-b-env-a',
* aggregation: [0, 50],
* change: {
* ratio: 50,
* amount: 50
* },
* entities: {}
* },
* {
* id: 'service-b-env-b',
* aggregation: [0, 50],
* change: {
* ratio: 50,
* amount: 50
* },
* entities: {}
* },
* ]
* }
* },
* ]
* }
* }
* ```
*/
export interface Entity {
id: Maybe<string>;
aggregation: [number, number];
entities: Record<string, Entity[]>;
change: ChangeStatistic;
}
@@ -14,6 +14,9 @@
* limitations under the License.
*/
/**
* @public
*/
export type Group = {
id: string;
};
@@ -14,4 +14,7 @@
* limitations under the License.
*/
/**
* @public
*/
export type Maybe<T> = T | null;
@@ -14,6 +14,9 @@
* limitations under the License.
*/
/**
* @public
*/
export type Metric = {
kind: string;
name: string;
@@ -17,6 +17,9 @@
import { DateAggregation } from './DateAggregation';
import { ChangeStatistic } from './ChangeStatistic';
/**
* @public
*/
export interface MetricData {
id: string;
format: 'number' | 'currency';
@@ -14,6 +14,9 @@
* limitations under the License.
*/
/**
* @public
*/
export interface Product {
kind: string;
name: string;
@@ -14,6 +14,9 @@
* limitations under the License.
*/
/**
* @public
*/
export interface Project {
id: string;
name?: string;
@@ -13,6 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @public
*/
export type Trendline = {
slope: number;
intercept: number;
@@ -0,0 +1,27 @@
/*
* Copyright 2020 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './ChangeStatistic';
export * from './Cost';
export * from './DateAggregation';
export * from './Entity';
export * from './Group';
export * from './Maybe';
export * from './MetricData';
export * from './Metric';
export * from './Product';
export * from './Project';
export * from './Trendline';
+45 -99
View File
@@ -9,9 +9,12 @@ import { ApiRef } from '@backstage/core-plugin-api';
import { BackstagePalette } from '@backstage/theme';
import { BackstagePlugin } from '@backstage/core-plugin-api';
import { BackstageTheme } from '@backstage/theme';
import { ChangeStatistic as ChangeStatistic_2 } from '@backstage/plugin-cost-insights-common';
import * as common from '@backstage/plugin-cost-insights-common';
import { ContentRenderer } from 'recharts';
import { Dispatch } from 'react';
import { ForwardRefExoticComponent } from 'react';
import { Maybe as Maybe_2 } from '@backstage/plugin-cost-insights-common';
import { PaletteOptions } from '@material-ui/core/styles/createPalette';
import { PropsWithChildren } from 'react';
import { ReactNode } from 'react';
@@ -32,9 +35,9 @@ export type Alert = {
status?: AlertStatus;
url?: string;
buttonText?: string;
SnoozeForm?: Maybe<AlertForm>;
AcceptForm?: Maybe<AlertForm>;
DismissForm?: Maybe<AlertForm>;
SnoozeForm?: Maybe_2<AlertForm>;
AcceptForm?: Maybe_2<AlertForm>;
DismissForm?: Maybe_2<AlertForm>;
onSnoozed?(options: AlertOptions): Promise<Alert[]>;
onAccepted?(options: AlertOptions): Promise<Alert[]>;
onDismissed?(options: AlertOptions): Promise<Alert[]>;
@@ -55,9 +58,9 @@ export interface AlertCost {
// @public (undocumented)
export interface AlertDismissFormData {
// (undocumented)
feedback: Maybe<string>;
feedback: Maybe_2<string>;
// (undocumented)
other: Maybe<string>;
other: Maybe_2<string>;
// (undocumented)
reason: AlertDismissReason;
}
@@ -269,15 +272,11 @@ export type BarChartTooltipProps = {
actions?: ReactNode;
};
// Warning: (tsdoc-characters-after-block-tag) The token "@backstage" looks like a TSDoc tag but contains an invalid character "/"; if it is not a tag, use a backslash to escape the "@"
// Warning: (ae-missing-release-tag) "ChangeStatistic" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export interface ChangeStatistic {
// (undocumented)
amount: number;
// (undocumented)
ratio?: number;
}
// @public @deprecated (undocumented)
export type ChangeStatistic = common.ChangeStatistic;
// Warning: (ae-missing-release-tag) "ChangeThreshold" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -289,8 +288,6 @@ export enum ChangeThreshold {
upper = 0.05,
}
// Warning: (ae-missing-release-tag) "ChartData" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type ChartData = {
date: number;
@@ -299,21 +296,11 @@ export type ChartData = {
[key: string]: number;
};
// Warning: (tsdoc-characters-after-block-tag) The token "@backstage" looks like a TSDoc tag but contains an invalid character "/"; if it is not a tag, use a backslash to escape the "@"
// Warning: (ae-missing-release-tag) "Cost" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export interface Cost {
// (undocumented)
aggregation: DateAggregation[];
// (undocumented)
change?: ChangeStatistic;
// (undocumented)
groupedCosts?: Record<string, Cost[]>;
// (undocumented)
id: string;
// (undocumented)
trendline?: Trendline;
}
// @public @deprecated (undocumented)
export type Cost = common.Cost;
// Warning: (ae-missing-release-tag) "CostGrowth" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -423,8 +410,6 @@ export interface CostInsightsThemeOptions extends PaletteOptions {
palette: CostInsightsPaletteOptions;
}
// Warning: (ae-missing-release-tag) "Currency" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export interface Currency {
// (undocumented)
@@ -465,21 +450,17 @@ export enum DataKey {
Previous = 'previous',
}
// Warning: (tsdoc-characters-after-block-tag) The token "@backstage" looks like a TSDoc tag but contains an invalid character "/"; if it is not a tag, use a backslash to escape the "@"
// Warning: (ae-missing-release-tag) "DateAggregation" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type DateAggregation = {
date: string;
amount: number;
};
// @public @deprecated (undocumented)
export type DateAggregation = common.DateAggregation;
// Warning: (ae-missing-release-tag) "DEFAULT_DATE_FORMAT" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const DEFAULT_DATE_FORMAT = 'yyyy-LL-dd';
// Warning: (ae-missing-release-tag) "Duration" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
export enum Duration {
// (undocumented)
@@ -497,19 +478,11 @@ export enum Duration {
// @public (undocumented)
export const EngineerThreshold = 0.5;
// Warning: (tsdoc-characters-after-block-tag) The token "@backstage" looks like a TSDoc tag but contains an invalid character "/"; if it is not a tag, use a backslash to escape the "@"
// Warning: (ae-missing-release-tag) "Entity" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export interface Entity {
// (undocumented)
aggregation: [number, number];
// (undocumented)
change: ChangeStatistic;
// (undocumented)
entities: Record<string, Entity[]>;
// (undocumented)
id: Maybe<string>;
}
// @public @deprecated (undocumented)
export type Entity = common.Entity;
// Warning: (ae-missing-release-tag) "ExampleCostInsightsClient" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -533,12 +506,11 @@ export class ExampleCostInsightsClient implements CostInsightsApi {
getUserGroups(userId: string): Promise<Group[]>;
}
// Warning: (tsdoc-characters-after-block-tag) The token "@backstage" looks like a TSDoc tag but contains an invalid character "/"; if it is not a tag, use a backslash to escape the "@"
// Warning: (ae-missing-release-tag) "Group" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type Group = {
id: string;
};
// @public @deprecated (undocumented)
export type Group = common.Group;
// Warning: (ae-missing-release-tag) "GrowthType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -602,33 +574,23 @@ export type LegendItemProps = {
// @public (undocumented)
export type Loading = Record<string, boolean>;
// Warning: (tsdoc-characters-after-block-tag) The token "@backstage" looks like a TSDoc tag but contains an invalid character "/"; if it is not a tag, use a backslash to escape the "@"
// Warning: (ae-missing-release-tag) "Maybe" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type Maybe<T> = T | null;
// @public @deprecated (undocumented)
export type Maybe<T> = common.Maybe<T>;
// Warning: (tsdoc-characters-after-block-tag) The token "@backstage" looks like a TSDoc tag but contains an invalid character "/"; if it is not a tag, use a backslash to escape the "@"
// Warning: (ae-missing-release-tag) "Metric" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type Metric = {
kind: string;
name: string;
default: boolean;
};
// @public @deprecated (undocumented)
export type Metric = common.Metric;
// Warning: (tsdoc-characters-after-block-tag) The token "@backstage" looks like a TSDoc tag but contains an invalid character "/"; if it is not a tag, use a backslash to escape the "@"
// Warning: (ae-missing-release-tag) "MetricData" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export interface MetricData {
// (undocumented)
aggregation: DateAggregation[];
// (undocumented)
change: ChangeStatistic;
// (undocumented)
format: 'number' | 'currency';
// (undocumented)
id: string;
}
// @public @deprecated (undocumented)
export type MetricData = common.MetricData;
// Warning: (ae-forgotten-export) The symbol "MockConfigProviderProps" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "MockConfigProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
@@ -648,32 +610,24 @@ export const MockCurrencyProvider: ({
...context
}: MockCurrencyProviderProps) => JSX.Element;
// Warning: (ae-missing-release-tag) "PageFilters" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export interface PageFilters {
// (undocumented)
duration: Duration;
// (undocumented)
group: Maybe<string>;
group: Maybe_2<string>;
// (undocumented)
metric: string | null;
// (undocumented)
project: Maybe<string>;
project: Maybe_2<string>;
}
// Warning: (tsdoc-characters-after-block-tag) The token "@backstage" looks like a TSDoc tag but contains an invalid character "/"; if it is not a tag, use a backslash to escape the "@"
// Warning: (ae-missing-release-tag) "Product" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export interface Product {
// (undocumented)
kind: string;
// (undocumented)
name: string;
}
// @public @deprecated (undocumented)
export type Product = common.Product;
// Warning: (ae-missing-release-tag) "ProductFilters" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type ProductFilters = Array<ProductPeriod>;
@@ -687,8 +641,6 @@ export type ProductInsightsOptions = {
project: Maybe<string>;
};
// Warning: (ae-missing-release-tag) "ProductPeriod" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export interface ProductPeriod {
// (undocumented)
@@ -697,15 +649,11 @@ export interface ProductPeriod {
productType: string;
}
// Warning: (tsdoc-characters-after-block-tag) The token "@backstage" looks like a TSDoc tag but contains an invalid character "/"; if it is not a tag, use a backslash to escape the "@"
// Warning: (ae-missing-release-tag) "Project" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export interface Project {
// (undocumented)
id: string;
// (undocumented)
name?: string;
}
// @public @deprecated (undocumented)
export type Project = common.Project;
// Warning: (ae-missing-release-tag) "ProjectGrowthAlert" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -731,7 +679,7 @@ export interface ProjectGrowthData {
// (undocumented)
aggregation: [number, number];
// (undocumented)
change: ChangeStatistic;
change: ChangeStatistic_2;
// (undocumented)
periodEnd: string;
// (undocumented)
@@ -749,7 +697,7 @@ export interface ResourceData {
// (undocumented)
current: number;
// (undocumented)
name: Maybe<string>;
name: Maybe_2<string>;
// (undocumented)
previous: number;
}
@@ -763,13 +711,11 @@ export type TooltipItem = {
value: string;
};
// Warning: (tsdoc-characters-after-block-tag) The token "@backstage" looks like a TSDoc tag but contains an invalid character "/"; if it is not a tag, use a backslash to escape the "@"
// Warning: (ae-missing-release-tag) "Trendline" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type Trendline = {
slope: number;
intercept: number;
};
// @public @deprecated (undocumented)
export type Trendline = common.Trendline;
// Warning: (ae-missing-release-tag) "UnlabeledDataflowAlert" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -77,7 +77,7 @@ We can use the data provided by the Cost Explorer API to implement CostInsightsA
### 1. [getGroupDailyCost](https://github.com/backstage/backstage/blob/master/plugins/cost-insights/src/api/CostInsightsApi.ts#L93)
The `getGroupDailyCost` method is expected to return daily cost aggregations for a given group and interval time frame as a [`Cost`](https://github.com/backstage/backstage/blob/master/plugins/cost-insights/src/types/Cost.ts).
The `getGroupDailyCost` method is expected to return daily cost aggregations for a given group and interval time frame as a [`Cost`](https://github.com/backstage/backstage/blob/master/plugins/cost-insights-common/src/types/Cost.ts).
#### Total Daily cost
@@ -208,13 +208,13 @@ Sample `groupedCosts` based on the response:
### 2. [getProjectDailyCost](https://github.com/backstage/backstage/blob/master/plugins/cost-insights/src/api/CostInsightsApi.ts#L111)
The `getProjectDailyCost` method is expected to return daily cost aggregations for a given billing entity and interval time frame as a [`Cost`](https://github.com/backstage/backstage/blob/master/plugins/cost-insights/src/types/Cost.ts).
The `getProjectDailyCost` method is expected to return daily cost aggregations for a given billing entity and interval time frame as a [`Cost`](https://github.com/backstage/backstage/blob/master/plugins/cost-insights-common/src/types/Cost.ts).
This should be similar to the `getGroupDailyCost` method implementation, but with an updated `LINKED_ACCOUNT` filter to get narrower cost data for a lower-level linked account.
### 3. [getProductInsights](https://github.com/backstage/backstage/blob/master/plugins/cost-insights/src/api/CostInsightsApi.ts#L111)
The `getProductInsights` method is expected to return cost aggregations for a particular cloud product and interval time frame as an [`Entity`](https://github.com/backstage/backstage/blob/master/plugins/cost-insights/src/types/Entity.ts).
The `getProductInsights` method is expected to return cost aggregations for a particular cloud product and interval time frame as an [`Entity`](https://github.com/backstage/backstage/blob/master/plugins/cost-insights-common/src/types/Entity.ts).
#### Cloud product cost by resource
+1
View File
@@ -38,6 +38,7 @@
"@backstage/config": "^1.0.1",
"@backstage/core-components": "^0.9.6-next.1",
"@backstage/core-plugin-api": "^1.0.3",
"@backstage/plugin-cost-insights-common": "^0.0.0",
"@backstage/theme": "^0.2.16-next.0",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
+2 -2
View File
@@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ForwardRefExoticComponent, RefAttributes } from 'react';
import { ChangeStatistic } from './ChangeStatistic';
import { ChangeStatistic, Maybe } from '@backstage/plugin-cost-insights-common';
import { Duration } from './Duration';
import { Maybe } from './Maybe';
/**
* Generic alert type with required fields for display. The `element` field will be rendered in
@@ -14,15 +14,6 @@
* limitations under the License.
*/
export interface ChangeStatistic {
// The ratio of change from one duration to another, expressed as: (newSum - oldSum) / oldSum
// If a ratio cannot be calculated - such as when a new or old sum is zero,
// the ratio can be omitted and where applicable, ∞ or -∞ will display based on amount.
ratio?: number;
// The actual USD change between time periods (can be negative if costs decreased)
amount: number;
}
export const EngineerThreshold = 0.5;
export enum ChangeThreshold {
@@ -14,6 +14,9 @@
* limitations under the License.
*/
/**
* @public
*/
export type ChartData = {
date: number;
trend: number;
+3 -7
View File
@@ -14,6 +14,9 @@
* limitations under the License.
*/
/**
* @public
*/
export interface Currency {
kind: string | null;
label: string;
@@ -21,10 +24,3 @@ export interface Currency {
prefix?: string;
rate?: number;
}
export enum CurrencyType {
USD = 'USD',
CarbonOffsetTons = 'CARBON_OFFSET_TONS',
Beers = 'BEERS',
IceCream = 'PINTS_OF_ICE_CREAM',
}
@@ -0,0 +1,22 @@
/*
* Copyright 2020 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export enum CurrencyType {
USD = 'USD',
CarbonOffsetTons = 'CARBON_OFFSET_TONS',
Beers = 'BEERS',
IceCream = 'PINTS_OF_ICE_CREAM',
}
@@ -0,0 +1,17 @@
/*
* Copyright 2020 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export const DEFAULT_DATE_FORMAT = 'yyyy-LL-dd';
+2 -2
View File
@@ -19,6 +19,8 @@
* 'last completed quarter', and P30D/P90D to be '[month|quarter] relative to today'. So if
* it's September 15, P3M represents costs for Q2 and P30D represents August 16 -
* September 15.
*
* @public
*/
export enum Duration {
P7D = 'P7D',
@@ -26,5 +28,3 @@ export enum Duration {
P90D = 'P90D',
P3M = 'P3M',
}
export const DEFAULT_DATE_FORMAT = 'yyyy-LL-dd';
-118
View File
@@ -1,118 +0,0 @@
/*
* Copyright 2020 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ChangeStatistic } from './ChangeStatistic';
import { Maybe } from './Maybe';
export interface Entity {
id: Maybe<string>;
aggregation: [number, number];
entities: Record<string, Entity[]>;
change: ChangeStatistic;
}
/*
An entity is a tree-like structure that represents any unique
product or service that generates cost over a fixed period of time.
An entity could be atomic or composite. An atomic entity is indivisible
and cannot be broken into sub-entities.
A composite entity is divided into sub-entities that account for portions
of the total cost **over the same time period**. The root entity is
expected to only have _one_ Record consisting of the sub-entities to display
in the product panel (keyed by the entity type, such as "service" for
compute entities).
The root sub-entities may have multiple breakdowns - for example, a
breakdown of an entity cost by SKU vs deployment environment. The sum
aggregated cost of each keyed breakdown should equal the sub-entity's cost.
Entities with null ids are considered "unlabeled" - costs without attribution.
If an entity is a composite, it may only have one (1) null child but may have any number of
null grandchildren.
{
id: 'product',
aggregation: [0, 200],
change: {
ratio: 2000,
amount: 200
},
entities: {
service: [
{
id: 'service-a',
aggregation: [0, 100],
change: {
ratio: 100,
amount: 100
},
entities: {}
},
{
id: 'service-b',
aggregation: [0, 100],
change: {
ratio: 100,
amount: 100
},
entities: {
SKU: [
{
id: 'service-b-sku-a',
aggregation: [0, 25],
change: {
ratio: 25,
amount: 25
},
entities: {}
},
{
id: null, // Unlabeled cost for service-b
aggregation: [0, 75],
change: {
ratio: 75,
amount: 75
},
entities: {}
},
],
deployment: [
{
id: 'service-b-env-a',
aggregation: [0, 50],
change: {
ratio: 50,
amount: 50
},
entities: {}
},
{
id: 'service-b-env-b',
aggregation: [0, 50],
change: {
ratio: 50,
amount: 50
},
entities: {}
},
]
}
},
]
}
}
*/
+10 -1
View File
@@ -14,9 +14,12 @@
* limitations under the License.
*/
import { Maybe } from './Maybe';
import { Maybe } from '@backstage/plugin-cost-insights-common';
import { Duration } from './Duration';
/**
* @public
*/
export interface PageFilters {
group: Maybe<string>;
project: Maybe<string>;
@@ -24,8 +27,14 @@ export interface PageFilters {
metric: string | null;
}
/**
* @public
*/
export type ProductFilters = Array<ProductPeriod>;
/**
* @public
*/
export interface ProductPeriod {
duration: Duration;
productType: string;
+30 -12
View File
@@ -14,22 +14,40 @@
* limitations under the License.
*/
import * as common from '@backstage/plugin-cost-insights-common';
/** * @deprecated use the same type from @backstage/plugin-cost-insights-common instead */
export type ChangeStatistic = common.ChangeStatistic;
/** * @deprecated use the same type from @backstage/plugin-cost-insights-common instead */
export type Cost = common.Cost;
/** * @deprecated use the same type from @backstage/plugin-cost-insights-common instead */
export type DateAggregation = common.DateAggregation;
/** * @deprecated use the same type from @backstage/plugin-cost-insights-common instead */
export type Entity = common.Entity;
/** * @deprecated use the same type from @backstage/plugin-cost-insights-common instead */
export type Group = common.Group;
/** * @deprecated use the same type from @backstage/plugin-cost-insights-common instead */
export type Maybe<T> = common.Maybe<T>;
/** * @deprecated use the same type from @backstage/plugin-cost-insights-common instead */
export type Metric = common.Metric;
/** * @deprecated use the same type from @backstage/plugin-cost-insights-common instead */
export type MetricData = common.MetricData;
/** * @deprecated use the same type from @backstage/plugin-cost-insights-common instead */
export type Product = common.Product;
/** * @deprecated use the same type from @backstage/plugin-cost-insights-common instead */
export type Project = common.Project;
/** * @deprecated use the same type from @backstage/plugin-cost-insights-common instead */
export type Trendline = common.Trendline;
// TODO: Split some of these up into `@backstage/plugin-cost-insights-react` for presentation types
// and `@backstage/plugin-cost-insights-common` for data transfer object types.
export * from './Alert';
export * from './ChangeStatistic';
export * from './ChartData';
export * from './Cost';
export * from './DateAggregation';
export * from './Duration';
export * from './Currency';
export * from './Entity';
export * from './Icon';
export * from './CurrencyType';
export * from './DateFormat';
export * from './Duration';
export * from './Filters';
export * from './Group';
export * from './Icon';
export * from './Loading';
export * from './Maybe';
export * from './MetricData';
export * from './Metric';
export * from './Product';
export * from './Project';
export * from './Theme';
export * from './Trendline';
@@ -108,40 +108,58 @@ describe('SearchContext', () => {
expect(result.current).toEqual(expect.objectContaining(initialState));
});
it('Resets cursor when term is set (and different from previous)', async () => {
const { result, waitForNextUpdate } = renderHook(() => useSearch(), {
wrapper,
initialProps: {
initialState: {
...initialState,
pageCursor: 'SOMEPAGE',
describe('Resets cursor', () => {
it('When term is cleared', async () => {
const { result, waitForNextUpdate } = renderHook(() => useSearch(), {
wrapper,
initialProps: {
initialState: {
...initialState,
term: 'first term',
pageCursor: 'SOMEPAGE',
},
},
},
});
await waitForNextUpdate();
expect(result.current.term).toEqual('first term');
expect(result.current.pageCursor).toEqual('SOMEPAGE');
act(() => {
result.current.setTerm('');
});
await waitForNextUpdate();
expect(result.current.pageCursor).toBeUndefined();
});
await waitForNextUpdate();
it('When term is set (and different from previous)', async () => {
const { result, waitForNextUpdate } = renderHook(() => useSearch(), {
wrapper,
initialProps: {
initialState: {
...initialState,
term: 'first term',
pageCursor: 'SOMEPAGE',
},
},
});
expect(result.current.pageCursor).toEqual('SOMEPAGE');
await waitForNextUpdate();
act(() => {
result.current.setTerm('first term');
expect(result.current.term).toEqual('first term');
expect(result.current.pageCursor).toEqual('SOMEPAGE');
act(() => {
result.current.setTerm('second term');
});
await waitForNextUpdate();
expect(result.current.pageCursor).toBeUndefined();
});
act(() => {
result.current.setPageCursor('OTHERPAGE');
});
await waitForNextUpdate();
expect(result.current.pageCursor).toEqual('OTHERPAGE');
act(() => {
result.current.setTerm('second term');
});
await waitForNextUpdate();
expect(result.current.pageCursor).toEqual(undefined);
});
describe('Performs search (and sets results)', () => {
@@ -150,10 +150,11 @@ export const SearchContextProvider = (props: SearchContextProviderProps) => {
useEffect(() => {
// Any time a term is reset, we want to start from page 0.
if (term && prevTerm && term !== prevTerm) {
// Only reset the term if it has been modified by the user at least once, the initial state must not reset the term.
if (prevTerm !== undefined && term !== prevTerm) {
setPageCursor(undefined);
}
}, [term, prevTerm, initialState.pageCursor]);
}, [term, prevTerm, setPageCursor]);
const value: SearchContextValue = {
result,
+5 -5
View File
@@ -5263,9 +5263,9 @@
"@react-hookz/deep-equal" "^1.0.1"
"@react-hookz/web@^14.0.0":
version "14.6.0"
resolved "https://registry.npmjs.org/@react-hookz/web/-/web-14.6.0.tgz#8b3824841ee679b23516aa9b316e819e55590a1c"
integrity sha512-4ZQU4wsNd4QsMe4LkF9t1y4NS2cCp1N/JfG9TYw1dsWLOJhZ0w+4ebp7cydN5BLeceIaILcfQ7fBYUvrBJN6EA==
version "14.7.0"
resolved "https://registry.npmjs.org/@react-hookz/web/-/web-14.7.0.tgz#3154b7cf38bf464e7b60417eae7f3348ce19e6f6"
integrity sha512-9AN0W3QH3Aj5/LXHKy2XiysjgvaNRLQwEVyCaU3AOMW9c1XlL5hSYc67ExubmzQRw2kAYx2Jpgl8wKCIINxsxQ==
dependencies:
"@react-hookz/deep-equal" "^1.0.2"
@@ -6787,7 +6787,7 @@
resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c"
integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==
"@types/react-dom@*", "@types/react-dom@<18.0.0":
"@types/react-dom@*", "@types/react-dom@<18.0.0", "@types/react-dom@^17":
version "17.0.17"
resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.17.tgz#2e3743277a793a96a99f1bf87614598289da68a1"
integrity sha512-VjnqEmqGnasQKV0CWLevqMTXBYG9GbwuE6x3VetERLh0cq2LTptFE73MrQi2S7GkKXCf2GgwItB/melLnxfnsg==
@@ -26313,7 +26313,7 @@ ws@^7.3.1, ws@^7.4.6:
resolved "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz#9e0ac77ee50af70d58326ecff7e85eb3fa375e67"
integrity sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==
ws@^8.3.0:
ws@^8.0.0, ws@^8.3.0:
version "8.8.0"
resolved "https://registry.npmjs.org/ws/-/ws-8.8.0.tgz#8e71c75e2f6348dbf8d78005107297056cb77769"
integrity sha512-JDAgSYQ1ksuwqfChJusw1LSJ8BizJ2e/vVu5Lxjq3YvNJNlROv1ui4i+c/kUUrPheBvQl4c5UbERhTwKa6QBJQ==