Merge pull request #4623 from backstage/cost-insights-override-default-alert-properties
Cost insights override default alert properties
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-cost-insights': patch
|
||||
---
|
||||
|
||||
Default alert properties can be overridden using accessors
|
||||
@@ -94,6 +94,7 @@ Zalando
|
||||
Zhou
|
||||
Zolotusky
|
||||
abc
|
||||
accessors
|
||||
adamdmharvey
|
||||
andrewthauer
|
||||
api
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 React from 'react';
|
||||
import pluralize from 'pluralize';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { ProjectGrowthAlert } from './ProjectGrowthAlert';
|
||||
import { ProjectGrowthData } from '../types';
|
||||
import {
|
||||
MockCurrencyProvider,
|
||||
MockConfigProvider,
|
||||
MockBillingDateProvider,
|
||||
} from '../utils/tests';
|
||||
|
||||
const mockData: ProjectGrowthData = {
|
||||
project: 'test-project',
|
||||
periodStart: '2021-01-01',
|
||||
periodEnd: '2021-02-01',
|
||||
aggregation: [0, 0],
|
||||
change: {
|
||||
ratio: 0,
|
||||
amount: 0,
|
||||
},
|
||||
products: [
|
||||
{
|
||||
id: 'product-a',
|
||||
aggregation: [0, 0],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// suppress recharts componentDidUpdate deprecation warnings
|
||||
jest.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
|
||||
async function renderInContext(children: JSX.Element) {
|
||||
return renderInTestApp(
|
||||
<MockConfigProvider>
|
||||
<MockBillingDateProvider>
|
||||
<MockCurrencyProvider>{children}</MockCurrencyProvider>
|
||||
</MockBillingDateProvider>
|
||||
</MockConfigProvider>,
|
||||
);
|
||||
}
|
||||
|
||||
class CustomProjectGrowthAlert extends ProjectGrowthAlert {
|
||||
get url() {
|
||||
return 'path/to/resource';
|
||||
}
|
||||
get title() {
|
||||
return `Investigate cost growth in ${pluralize(
|
||||
'project',
|
||||
this.data.products.length,
|
||||
true,
|
||||
)}`;
|
||||
}
|
||||
}
|
||||
|
||||
describe('ProjectGrowthAlert', () => {
|
||||
describe('constructor', () => {
|
||||
it('should create a project growth alert', async () => {
|
||||
const alert = new ProjectGrowthAlert(mockData);
|
||||
const { getByText, queryByText } = await renderInContext(alert.element);
|
||||
|
||||
expect(alert.url).toBe('/cost-insights/investigating-growth');
|
||||
expect(alert.title).toBe(
|
||||
'Investigate cost growth in project test-project',
|
||||
);
|
||||
expect(alert.subtitle).toBe(
|
||||
'Cost growth outpacing business growth is unsustainable long-term.',
|
||||
);
|
||||
expect(getByText('1 product')).toBeInTheDocument();
|
||||
expect(queryByText('sorted by cost')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('a subclass can inherit and override defaults using accessors', async () => {
|
||||
const alert = new CustomProjectGrowthAlert(mockData);
|
||||
const { getByText, queryByText } = await renderInContext(alert.element);
|
||||
|
||||
expect(alert.url).toBe('path/to/resource');
|
||||
expect(alert.title).toBe('Investigate cost growth in 1 project');
|
||||
expect(alert.subtitle).toBe(
|
||||
'Cost growth outpacing business growth is unsustainable long-term.',
|
||||
);
|
||||
expect(getByText('1 product')).toBeInTheDocument();
|
||||
expect(queryByText('sorted by cost')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -27,18 +27,22 @@ import { Alert, ProjectGrowthData } from '../types';
|
||||
export class ProjectGrowthAlert implements Alert {
|
||||
data: ProjectGrowthData;
|
||||
|
||||
url = '/cost-insights/investigating-growth';
|
||||
subtitle =
|
||||
'Cost growth outpacing business growth is unsustainable long-term.';
|
||||
|
||||
constructor(data: ProjectGrowthData) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
get url() {
|
||||
return '/cost-insights/investigating-growth';
|
||||
}
|
||||
|
||||
get title() {
|
||||
return `Investigate cost growth in project ${this.data.project}`;
|
||||
}
|
||||
|
||||
get subtitle() {
|
||||
return 'Cost growth outpacing business growth is unsustainable long-term.';
|
||||
}
|
||||
|
||||
get element() {
|
||||
return <ProjectGrowthAlertCard alert={this.data} />;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 React from 'react';
|
||||
import pluralize from 'pluralize';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { UnlabeledDataflowAlert } from './UnlabeledDataflowAlert';
|
||||
import { UnlabeledDataflowData } from '../types';
|
||||
import {
|
||||
MockCurrencyProvider,
|
||||
MockConfigProvider,
|
||||
MockBillingDateProvider,
|
||||
} from '../utils/tests';
|
||||
|
||||
const mockData: UnlabeledDataflowData = {
|
||||
periodStart: '2021-02-01',
|
||||
periodEnd: '2021-03-31',
|
||||
unlabeledCost: 0,
|
||||
labeledCost: 0,
|
||||
projects: [
|
||||
{
|
||||
id: 'project-a',
|
||||
labeledCost: 0,
|
||||
unlabeledCost: 0,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// suppress recharts componentDidUpdate deprecation warnings
|
||||
jest.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
|
||||
async function renderInContext(children: JSX.Element) {
|
||||
return renderInTestApp(
|
||||
<MockConfigProvider>
|
||||
<MockBillingDateProvider>
|
||||
<MockCurrencyProvider>{children}</MockCurrencyProvider>
|
||||
</MockBillingDateProvider>
|
||||
</MockConfigProvider>,
|
||||
);
|
||||
}
|
||||
|
||||
class CustomUnlabeledDataflowAlert extends UnlabeledDataflowAlert {
|
||||
get url() {
|
||||
return 'path/to/resource';
|
||||
}
|
||||
get title() {
|
||||
return `Add labels to ${pluralize(
|
||||
'workflow',
|
||||
this.data.projects.length,
|
||||
true,
|
||||
)}`;
|
||||
}
|
||||
}
|
||||
|
||||
describe('UnlabeledDataflowAlert', () => {
|
||||
describe('constructor', () => {
|
||||
it('should create an unlabeled dataflow alert', async () => {
|
||||
const alert = new UnlabeledDataflowAlert(mockData);
|
||||
const { getByText } = await renderInContext(alert.element);
|
||||
|
||||
expect(alert.url).toBe('/cost-insights/labeling-jobs');
|
||||
expect(alert.title).toBe('Add labels to workflows');
|
||||
expect(alert.subtitle).toBe(
|
||||
'Labels show in billing data, enabling cost insights for each workflow.',
|
||||
);
|
||||
expect(
|
||||
getByText(
|
||||
'Showing costs from 1 project with unlabeled Dataflow jobs in the last 30 days.',
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('a subclass can inherit and override defaults using accessors', async () => {
|
||||
const alert = new CustomUnlabeledDataflowAlert(mockData);
|
||||
const { getByText } = await renderInContext(alert.element);
|
||||
|
||||
expect(alert.url).toBe('path/to/resource');
|
||||
expect(alert.title).toBe('Add labels to 1 workflow');
|
||||
expect(alert.subtitle).toBe(
|
||||
'Labels show in billing data, enabling cost insights for each workflow.',
|
||||
);
|
||||
expect(
|
||||
getByText(
|
||||
'Showing costs from 1 project with unlabeled Dataflow jobs in the last 30 days.',
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -28,15 +28,22 @@ export class UnlabeledDataflowAlert implements Alert {
|
||||
data: UnlabeledDataflowData;
|
||||
status?: AlertStatus;
|
||||
|
||||
url = '/cost-insights/labeling-jobs';
|
||||
title = 'Add labels to workflows';
|
||||
subtitle =
|
||||
'Labels show in billing data, enabling cost insights for each workflow.';
|
||||
|
||||
constructor(data: UnlabeledDataflowData) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
get url() {
|
||||
return '/cost-insights/labeling-jobs';
|
||||
}
|
||||
|
||||
get title() {
|
||||
return 'Add labels to workflows';
|
||||
}
|
||||
|
||||
get subtitle() {
|
||||
return 'Labels show in billing data, enabling cost insights for each workflow.';
|
||||
}
|
||||
|
||||
get element() {
|
||||
return <UnlabeledDataflowAlertCard alert={this.data} />;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user