refactor: create cost-insights-common isomorphic package

This moves most of the cost-insights API types into a separate
isomorphic @backstage/plugin-cost-insights-common package. This will
allow these types to be used by a a cost-insights backend or other
product specific modules.

Signed-off-by: Andrew Thauer <athauer@wealthsimple.com>
This commit is contained in:
Andrew Thauer
2022-06-28 19:42:52 -04:00
parent 06b6920a2c
commit 3e032a5de2
31 changed files with 416 additions and 226 deletions
+13 -180
View File
@@ -9,10 +9,21 @@ 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 } from '@backstage/plugin-cost-insights-common';
import { ContentRenderer } from 'recharts';
import { Cost } from '@backstage/plugin-cost-insights-common';
import { Currency } from '@backstage/plugin-cost-insights-common';
import { Dispatch } from 'react';
import { Duration } from '@backstage/plugin-cost-insights-common';
import { Entity } from '@backstage/plugin-cost-insights-common';
import { ForwardRefExoticComponent } from 'react';
import { Group } from '@backstage/plugin-cost-insights-common';
import { Maybe } from '@backstage/plugin-cost-insights-common';
import { Metric } from '@backstage/plugin-cost-insights-common';
import { MetricData } from '@backstage/plugin-cost-insights-common';
import { PaletteOptions } from '@material-ui/core/styles/createPalette';
import { Product } from '@backstage/plugin-cost-insights-common';
import { Project } from '@backstage/plugin-cost-insights-common';
import { PropsWithChildren } from 'react';
import { ReactNode } from 'react';
import { RechartsFunction } from 'recharts';
@@ -269,16 +280,6 @@ export type BarChartTooltipProps = {
actions?: ReactNode;
};
// 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;
}
// Warning: (ae-missing-release-tag) "ChangeThreshold" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@@ -289,32 +290,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;
trend: number;
dailyCost: number;
[key: string]: number;
};
// 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;
}
// Warning: (ae-missing-release-tag) "CostGrowth" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@@ -423,22 +398,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)
kind: string | null;
// (undocumented)
label: string;
// (undocumented)
prefix?: string;
// (undocumented)
rate?: number;
// (undocumented)
unit: string;
}
// Warning: (ae-missing-release-tag) "CurrencyType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@@ -465,52 +424,16 @@ export enum DataKey {
Previous = 'previous',
}
// 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;
};
// 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)
P30D = 'P30D',
// (undocumented)
P3M = 'P3M',
// (undocumented)
P7D = 'P7D',
// (undocumented)
P90D = 'P90D',
}
// Warning: (ae-missing-release-tag) "EngineerThreshold" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const EngineerThreshold = 0.5;
// 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>;
}
// Warning: (ae-missing-release-tag) "ExampleCostInsightsClient" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@@ -533,13 +456,6 @@ export class ExampleCostInsightsClient implements CostInsightsApi {
getUserGroups(userId: string): Promise<Group[]>;
}
// 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;
};
// Warning: (ae-missing-release-tag) "GrowthType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@@ -602,34 +518,6 @@ export type LegendItemProps = {
// @public (undocumented)
export type Loading = Record<string, boolean>;
// 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;
// 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;
};
// 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;
}
// 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,35 +536,6 @@ 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>;
// (undocumented)
metric: string | null;
// (undocumented)
project: Maybe<string>;
}
// 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;
}
// 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>;
// Warning: (ae-missing-release-tag) "ProductInsightsOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@@ -687,26 +546,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)
duration: Duration;
// (undocumented)
productType: string;
}
// 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;
}
// Warning: (ae-missing-release-tag) "ProjectGrowthAlert" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
@@ -763,14 +602,6 @@ export type TooltipItem = {
value: string;
};
// 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;
};
// Warning: (ae-missing-release-tag) "UnlabeledDataflowAlert" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
@@ -817,4 +648,6 @@ export interface UnlabeledDataflowData {
// (undocumented)
unlabeledCost: number;
}
export * from '@backstage/plugin-cost-insights-common';
```
+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.1",
"@backstage/theme": "^0.2.16-next.0",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
+6 -3
View File
@@ -13,10 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ForwardRefExoticComponent, RefAttributes } from 'react';
import { ChangeStatistic } from './ChangeStatistic';
import { Duration } from './Duration';
import { Maybe } from './Maybe';
import {
ChangeStatistic,
Duration,
Maybe,
} from '@backstage/plugin-cost-insights-common';
/**
* 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 {
@@ -1,22 +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.
*/
export type ChartData = {
date: number;
trend: number;
dailyCost: number;
[key: string]: number;
};
-27
View File
@@ -1,27 +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 { DateAggregation } from './DateAggregation';
import { ChangeStatistic } from './ChangeStatistic';
import { Trendline } from './Trendline';
export interface Cost {
id: string;
aggregation: DateAggregation[];
change?: ChangeStatistic;
trendline?: Trendline;
groupedCosts?: Record<string, Cost[]>;
}
@@ -14,14 +14,6 @@
* limitations under the License.
*/
export interface Currency {
kind: string | null;
label: string;
unit: string;
prefix?: string;
rate?: number;
}
export enum CurrencyType {
USD = 'USD',
CarbonOffsetTons = 'CARBON_OFFSET_TONS',
@@ -1,20 +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.
*/
export type DateAggregation = {
date: string; // YYYY-MM-DD
amount: number;
};
@@ -14,17 +14,4 @@
* limitations under the License.
*/
/**
* Time periods for cost comparison; slight abuse of ISO 8601 periods. We take P3M to mean
* '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.
*/
export enum Duration {
P7D = 'P7D',
P30D = 'P30D',
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: {}
},
]
}
},
]
}
}
*/
@@ -1,32 +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 { Maybe } from './Maybe';
import { Duration } from './Duration';
export interface PageFilters {
group: Maybe<string>;
project: Maybe<string>;
duration: Duration;
metric: string | null;
}
export type ProductFilters = Array<ProductPeriod>;
export interface ProductPeriod {
duration: Duration;
productType: string;
}
-19
View File
@@ -1,19 +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.
*/
export type Group = {
id: string;
};
-17
View File
@@ -1,17 +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.
*/
export type Maybe<T> = T | null;
-21
View File
@@ -1,21 +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.
*/
export type Metric = {
kind: string;
name: string;
default: boolean;
};
@@ -1,25 +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 { DateAggregation } from './DateAggregation';
import { ChangeStatistic } from './ChangeStatistic';
export interface MetricData {
id: string;
format: 'number' | 'currency';
aggregation: DateAggregation[];
change: ChangeStatistic;
}
@@ -1,20 +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.
*/
export interface Product {
kind: string;
name: string;
}
@@ -1,20 +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.
*/
export interface Project {
id: string;
name?: string;
}
@@ -1,19 +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.
*/
export type Trendline = {
slope: number;
intercept: number;
};
+6 -13
View File
@@ -14,22 +14,15 @@
* limitations under the License.
*/
// @deprecated - use types from `@backstage/plugin-cost-insights-common` instead.
export * from '@backstage/plugin-cost-insights-common';
// 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 './Duration';
export * from './Icon';
export * from './Filters';
export * from './Group';
export * from './Loading';
export * from './Maybe';
export * from './MetricData';
export * from './Metric';
export * from './Product';
export * from './Project';
export * from './Theme';
export * from './Trendline';