Merge pull request #2734 from spotify/timbonicus/example-data
Better data for ExampleCostInsightsClient
This commit is contained in:
@@ -27,8 +27,8 @@
|
||||
"@backstage/plugin-sentry": "^0.1.1-alpha.24",
|
||||
"@backstage/plugin-tech-radar": "^0.1.1-alpha.24",
|
||||
"@backstage/plugin-techdocs": "^0.1.1-alpha.24",
|
||||
"@backstage/plugin-welcome": "^0.1.1-alpha.24",
|
||||
"@backstage/plugin-user-settings": "^0.1.1-alpha.24",
|
||||
"@backstage/plugin-welcome": "^0.1.1-alpha.24",
|
||||
"@backstage/test-utils": "^0.1.1-alpha.24",
|
||||
"@backstage/theme": "^0.1.1-alpha.24",
|
||||
"@material-ui/core": "^4.11.0",
|
||||
@@ -36,6 +36,7 @@
|
||||
"@octokit/rest": "^18.0.0",
|
||||
"@roadiehq/backstage-plugin-github-pull-requests": "^0.4.3",
|
||||
"@roadiehq/backstage-plugin-travis-ci": "^0.2.3",
|
||||
"dayjs": "^1.9.1",
|
||||
"history": "^5.0.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^16.12.0",
|
||||
@@ -44,6 +45,7 @@
|
||||
"react-router": "6.0.0-beta.0",
|
||||
"react-router-dom": "6.0.0-beta.0",
|
||||
"react-use": "^15.3.3",
|
||||
"regression": "^2.0.1",
|
||||
"zen-observable": "^0.8.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -55,6 +57,7 @@
|
||||
"@types/jquery": "^3.3.34",
|
||||
"@types/node": "^12.0.0",
|
||||
"@types/react-dom": "^16.9.8",
|
||||
"@types/regression": "^2.0.0",
|
||||
"@types/zen-observable": "^0.8.0",
|
||||
"cross-env": "^7.0.0",
|
||||
"cypress": "^4.2.0",
|
||||
|
||||
@@ -15,21 +15,84 @@
|
||||
*/
|
||||
/* eslint-disable no-restricted-imports */
|
||||
|
||||
import dayjs from 'dayjs';
|
||||
import regression, { DataPoint } from 'regression';
|
||||
import {
|
||||
Alert,
|
||||
ChangeStatistic,
|
||||
Cost,
|
||||
CostInsightsApi,
|
||||
DateAggregation,
|
||||
Duration,
|
||||
exclusiveEndDateOf,
|
||||
Group,
|
||||
inclusiveStartDateOf,
|
||||
Maybe,
|
||||
ProductCost,
|
||||
Project,
|
||||
ProjectGrowthAlert,
|
||||
ProjectGrowthData,
|
||||
Trendline,
|
||||
UnlabeledDataflowAlert,
|
||||
UnlabeledDataflowData,
|
||||
Maybe,
|
||||
} from '@backstage/plugin-cost-insights';
|
||||
|
||||
function durationOf(intervals: string): Duration {
|
||||
const match = intervals.match(/\/(?<duration>P\d+[DM])\//);
|
||||
const { duration } = match!.groups!;
|
||||
return duration as Duration;
|
||||
}
|
||||
|
||||
function aggregationFor(
|
||||
duration: Duration,
|
||||
baseline: number,
|
||||
): DateAggregation[] {
|
||||
const days = dayjs(exclusiveEndDateOf(duration)).diff(
|
||||
inclusiveStartDateOf(duration),
|
||||
'day',
|
||||
);
|
||||
|
||||
return [...Array(days).keys()].reduce(
|
||||
(values: DateAggregation[], i: number): DateAggregation[] => {
|
||||
const last = values.length ? values[values.length - 1].amount : baseline;
|
||||
values.push({
|
||||
date: dayjs(inclusiveStartDateOf(duration))
|
||||
.add(i, 'day')
|
||||
.format('YYYY-MM-DD'),
|
||||
amount: last + (baseline / 20) * (Math.random() * 2 - 1),
|
||||
});
|
||||
return values;
|
||||
},
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
function trendlineOf(aggregation: DateAggregation[]): Trendline {
|
||||
const data: ReadonlyArray<DataPoint> = aggregation.map(a => [
|
||||
Date.parse(a.date) / 1000,
|
||||
a.amount,
|
||||
]);
|
||||
const result = regression.linear(data, { precision: 5 });
|
||||
return {
|
||||
slope: result.equation[0],
|
||||
intercept: result.equation[1],
|
||||
};
|
||||
}
|
||||
|
||||
function changeOf(aggregation: DateAggregation[]): ChangeStatistic {
|
||||
const half = Math.ceil(aggregation.length / 2);
|
||||
const before = aggregation
|
||||
.slice(0, half)
|
||||
.reduce((sum, a) => sum + a.amount, 0);
|
||||
const after = aggregation
|
||||
.slice(half, aggregation.length)
|
||||
.reduce((sum, a) => sum + a.amount, 0);
|
||||
return {
|
||||
ratio: (after - before) / before,
|
||||
amount: after - before,
|
||||
};
|
||||
}
|
||||
|
||||
export class ExampleCostInsightsClient implements CostInsightsApi {
|
||||
private request(_: any, res: any): Promise<any> {
|
||||
return new Promise(resolve => setTimeout(resolve, 0, res));
|
||||
@@ -58,30 +121,17 @@ export class ExampleCostInsightsClient implements CostInsightsApi {
|
||||
metric: string | null,
|
||||
intervals: string,
|
||||
): Promise<Cost> {
|
||||
const aggregation = aggregationFor(
|
||||
durationOf(intervals),
|
||||
metric ? 0.3 : 8_000,
|
||||
);
|
||||
const groupDailyCost: Cost = await this.request(
|
||||
{ group, metric, intervals },
|
||||
{
|
||||
id: metric, // costs with null ids will appear as "All Projects" in Cost Overview panel
|
||||
aggregation: [
|
||||
{ date: '2020-08-01', amount: 75_000 / (metric ? 200_000 : 1) },
|
||||
{ date: '2020-08-02', amount: 120_000 / (metric ? 200_000 : 1) },
|
||||
{ date: '2020-08-03', amount: 110_000 / (metric ? 200_000 : 1) },
|
||||
{ date: '2020-08-04', amount: 90_000 / (metric ? 200_000 : 1) },
|
||||
{ date: '2020-08-05', amount: 80_000 / (metric ? 200_000 : 1) },
|
||||
{ date: '2020-08-06', amount: 85_000 / (metric ? 200_000 : 1) },
|
||||
{ date: '2020-08-07', amount: 82_500 / (metric ? 200_000 : 1) },
|
||||
{ date: '2020-08-08', amount: 100_000 / (metric ? 200_000 : 1) },
|
||||
{ date: '2020-08-09', amount: 130_000 / (metric ? 200_000 : 1) },
|
||||
{ date: '2020-08-10', amount: 140_000 / (metric ? 200_000 : 1) },
|
||||
],
|
||||
change: {
|
||||
ratio: 0.86,
|
||||
amount: 65_000,
|
||||
},
|
||||
trendline: {
|
||||
slope: 0,
|
||||
intercept: 90_000,
|
||||
},
|
||||
aggregation: aggregation,
|
||||
change: changeOf(aggregation),
|
||||
trendline: trendlineOf(aggregation),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -93,30 +143,17 @@ export class ExampleCostInsightsClient implements CostInsightsApi {
|
||||
metric: string | null,
|
||||
intervals: string,
|
||||
): Promise<Cost> {
|
||||
const aggregation = aggregationFor(
|
||||
durationOf(intervals),
|
||||
metric ? 0.1 : 1_500,
|
||||
);
|
||||
const projectDailyCost: Cost = await this.request(
|
||||
{ project, metric, intervals },
|
||||
{
|
||||
id: 'project-a',
|
||||
aggregation: [
|
||||
{ date: '2020-08-01', amount: 1000 },
|
||||
{ date: '2020-08-02', amount: 2000 },
|
||||
{ date: '2020-08-03', amount: 3000 },
|
||||
{ date: '2020-08-04', amount: 4000 },
|
||||
{ date: '2020-08-05', amount: 5000 },
|
||||
{ date: '2020-08-06', amount: 6000 },
|
||||
{ date: '2020-08-07', amount: 7000 },
|
||||
{ date: '2020-08-08', amount: 8000 },
|
||||
{ date: '2020-08-09', amount: 9000 },
|
||||
{ date: '2020-08-10', amount: 10_000 },
|
||||
],
|
||||
change: {
|
||||
ratio: 0.5,
|
||||
amount: 10000,
|
||||
},
|
||||
trendline: {
|
||||
slope: 0,
|
||||
intercept: 0,
|
||||
},
|
||||
aggregation: aggregation,
|
||||
change: changeOf(aggregation),
|
||||
trendline: trendlineOf(aggregation),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -168,7 +205,7 @@ export class ExampleCostInsightsClient implements CostInsightsApi {
|
||||
entities: [
|
||||
{
|
||||
id: null, // entities with null ids will be appear as "Unlabeled" in product panels
|
||||
aggregation: [45_000, 50_000],
|
||||
aggregation: [15_000, 30_000],
|
||||
},
|
||||
{
|
||||
id: 'entity-a',
|
||||
|
||||
@@ -61,7 +61,7 @@ const CostOverviewCard = ({
|
||||
</CostOverviewHeader>
|
||||
<Divider />
|
||||
<Box marginY={1} display="flex" flexDirection="column">
|
||||
<CostOverviewChartLegend change={change} title={name} />
|
||||
<CostOverviewChartLegend change={change} title={`${name} Trend`} />
|
||||
<CostOverviewChart
|
||||
responsive
|
||||
metric={metric}
|
||||
|
||||
@@ -5457,6 +5457,11 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/regression@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/@types/regression/-/regression-2.0.0.tgz#0677ea78d7bdb37039c02ebbccf062042f756ae3"
|
||||
integrity sha512-Ch2FD53M1HpFLL6zSTc/sfuyqQcIPy+/PV3xFT6QYtk9EOiMI29XOYmLNxBb1Y0lfMOR/NNa86J1gRc/1jGLyw==
|
||||
|
||||
"@types/relateurl@*":
|
||||
version "0.2.28"
|
||||
resolved "https://registry.npmjs.org/@types/relateurl/-/relateurl-0.2.28.tgz#6bda7db8653fa62643f5ee69e9f69c11a392e3a6"
|
||||
@@ -9529,6 +9534,11 @@ dateformat@^3.0.0:
|
||||
resolved "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
|
||||
integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==
|
||||
|
||||
dayjs@^1.9.1:
|
||||
version "1.9.1"
|
||||
resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.9.1.tgz#201a755f7db5103ed6de63ba93a984141c754541"
|
||||
integrity sha512-01NCTBg8cuMJG1OQc6PR7T66+AFYiPwgDvdJmvJBn29NGzIG+DIFxPLNjHzwz3cpFIvG+NcwIjP9hSaPVoOaDg==
|
||||
|
||||
de-indent@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
|
||||
@@ -19877,6 +19887,11 @@ regjsparser@^0.6.4:
|
||||
dependencies:
|
||||
jsesc "~0.5.0"
|
||||
|
||||
regression@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npmjs.org/regression/-/regression-2.0.1.tgz#8d29c3e8224a10850c35e337e85a8b2fac3b0c87"
|
||||
integrity sha1-jSnD6CJKEIUMNeM36FqLL6w7DIc=
|
||||
|
||||
relateurl@^0.2.7:
|
||||
version "0.2.7"
|
||||
resolved "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
|
||||
|
||||
Reference in New Issue
Block a user