Skip to content

Commit

Permalink
Merge branch 'main' into chore/#71-refactor-error-reducer
Browse files Browse the repository at this point in the history
  • Loading branch information
kylejwatson committed Nov 27, 2023
2 parents fd8081e + 607984b commit 73c6239
Show file tree
Hide file tree
Showing 12 changed files with 271 additions and 9 deletions.
2 changes: 2 additions & 0 deletions backstage-plugin/plugins/open-dora/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.57",
"@mui/x-charts": "^6.0.0-alpha.11",
"i18next": "^23.7.6",
"react-i18next": "^13.5.0",
"react-use": "^17.2.4"
},
"peerDependencies": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,19 @@ import {
ResponseErrorPanel,
SupportButton,
} from '@backstage/core-components';
import { useApi } from '@backstage/core-plugin-api';
import { getEntityRelations, useEntity } from '@backstage/plugin-catalog-react';
import { Grid } from '@material-ui/core';
import React from 'react';
import React, { useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import '../../i18n';
import { dfBenchmarkKey } from '../../models/DfBenchmarkData';
import { groupDataServiceApiRef } from '../../services/GroupDataService';
import { MetricContext } from '../../services/MetricContext';
import { useMetricData } from '../../services/MetricDataHook';
import { BarChartComponent } from '../BarChartComponent/BarChartComponent';
import { DropdownComponent } from '../DropdownComponent/DropdownComponent';
import { HighlightTextBoxComponent } from '../HighlightTextBoxComponent/HighlightTextBoxComponent';
import './DashboardComponent.css';

export interface DashboardComponentProps {
Expand Down Expand Up @@ -49,8 +55,31 @@ export const DashboardComponent = ({
entityName,
entityGroup,
}: DashboardComponentProps) => {
// Overview
const [dfOverview, setDfOverview] = React.useState<dfBenchmarkKey | null>(
null,
);

const [t] = useTranslation();
const [selectedTimeUnit, setSelectedTimeUnit] = React.useState('weekly');

const groupDataService = useApi(groupDataServiceApiRef);

useEffect(() => {
groupDataService.retrieveBenchmarkData({ type: 'df' }).then(
response => {
setDfOverview(response.key);
},
(error: Error) => {
console.error(error);
// dispatch({
// type: 'dfBenchmarkError',
// error: error,
// });
},
);
}, [entityGroup, entityName, selectedTimeUnit, groupDataService]);

return (
<MetricContext.Provider
value={{
Expand Down Expand Up @@ -81,11 +110,36 @@ export const DashboardComponent = ({
</Grid>
</div>
</Grid>

<ChartGridItem type="df_count" label="Deployment Frequency" />
<Grid item xs={12} className="gridBorder">
<div className="gridBoxText">
<Grid container>
<Grid item xs={3}>
<HighlightTextBoxComponent
title=""
text=""
highlight={
dfOverview
? t(
`deployment_frequency.overall_labels.${dfOverview}`,
)
: t('custom_errors.data_unavailable')
}
// to do: think of text colouring for different scenarios
textColour="positiveHighlight"
/>
</Grid>
</Grid>
</div>
</Grid>
<ChartGridItem
type="df_count"
label={t('deployment_frequency.labels.deployment_frequency')}
/>
<ChartGridItem
type="df_average"
label="Deployment Frequency Average"
label={t(
'deployment_frequency.labels.deployment_frequency_average',
)}
/>
</Grid>
<Grid item />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import { fireEvent, render, screen } from '@testing-library/react';
import { DropdownComponent } from './DropdownComponent';
import '../../i18n';

describe('DropdownComponent', () => {
it('should create show a dropdown with the aggregation choices', async () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Box, MenuItem, TextField } from '@material-ui/core';
import React from 'react';

import { useTranslation } from 'react-i18next';

interface DropdownComponentProps {
onSelect: (selection: string) => void;
selection: string;
Expand All @@ -10,6 +12,7 @@ export const DropdownComponent = ({
onSelect,
selection,
}: DropdownComponentProps) => {
const [t] = useTranslation();
return (
<Box sx={{ display: 'flex', m: 3, flexDirection: 'column' }}>
<TextField
Expand All @@ -18,16 +21,16 @@ export const DropdownComponent = ({
value={selection}
onChange={e => onSelect(e.target.value)}
select
label="Time Unit"
label={t('dropdown_time_units.time_unit')}
>
<MenuItem key={1} value="weekly">
Weekly
{t('dropdown_time_units.weekly')}
</MenuItem>
<MenuItem key={2} value="monthly">
Monthly
{t('dropdown_time_units.monthly')}
</MenuItem>
<MenuItem key={3} value="quarterly">
Quarterly
{t('dropdown_time_units.quarterly')}
</MenuItem>
</TextField>
</Box>
Expand Down
20 changes: 20 additions & 0 deletions backstage-plugin/plugins/open-dora/src/i18n.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/* istanbul ignore file */
import i18next from 'i18next';
import { initReactI18next } from 'react-i18next';

// Import all translation files
import translationEnglish from './locales/en/translation.json';

// Using translation
const resources = {
en: {
translation: translationEnglish,
},
};

i18next.use(initReactI18next).init({
resources,
lng: 'en', // default language
});

export default i18next;
25 changes: 25 additions & 0 deletions backstage-plugin/plugins/open-dora/src/locales/en/translation.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"deployment_frequency": {
"labels": {
"deployment_frequency": "Deployment Frequency",
"deployment_frequency_average": "Deployment Frequency Average"
},
"overall_labels": {
"lt-6month": "Fewer than once per six months",
"on-demand": "On-demand",
"week-month": "Between once per week and once per month",
"month-6month": "Between once per month and once every 6 months"
}
},

"dropdown_time_units": {
"time_unit": "Time Unit",
"weekly": "Weekly",
"monthly": "Monthly",
"quarterly": "Quarterly"
},

"custom_errors": {
"data_unavailable": "Data unavailable"
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export interface ChartErrors {
countError: Error | null;
averageError: Error | null;
dfBenchmarkError: Error | null;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export interface dfBenchmarkData {
key: dfBenchmarkKey;
}
export type dfBenchmarkKey =
| 'on-demand'
| 'week-month'
| 'month-6month'
| 'lt-6month';
Original file line number Diff line number Diff line change
@@ -1,11 +1,28 @@
import { MockConfigApi } from '@backstage/test-utils';
import { rest } from 'msw';
import { baseUrl, metricUrl } from '../../testing/mswHandlers';
import { baseUrl, benchmarkUrl, metricUrl } from '../../testing/mswHandlers';
import { server } from '../setupTests';
import { GroupDataService } from './GroupDataService';

function createService() {
server.use(
rest.get(benchmarkUrl, (req, res, ctx) => {
const params = req.url.searchParams;
const type = params.get('type');

switch (type) {
case 'df': {
return res(
ctx.json({
key: 'lt-6month',
}),
);
}
default: {
return res(ctx.status(400));
}
}
}),
rest.get(metricUrl, (req, res, ctx) => {
const params = req.url.searchParams;
const type = params.get('type');
Expand Down Expand Up @@ -119,3 +136,64 @@ describe('GroupDataService', () => {
).rejects.toEqual(new Error('Internal Server Error'));
});
});

describe('BenchmarkService', () => {
it('should return deployment frequency overall data from the server', async () => {
const service = createService();

expect(await service.retrieveBenchmarkData({ type: 'df' })).toEqual({
key: 'lt-6month',
});
});

it('should throw an error if the response does not contain metric data', async () => {
const service = createService();

server.use(
rest.get(benchmarkUrl, (_, res, ctx) => {
return res(ctx.json({ other: 'data' }));
}),
);
await expect(
service.retrieveBenchmarkData({
type: 'df',
}),
).rejects.toEqual(new Error('Unexpected response'));
});

it('should return 404 for invalid types', async () => {
const service = createService();

await expect(
service.retrieveBenchmarkData({
type: 'invalid_type',
}),
).rejects.toEqual(new Error('Bad Request'));
});

it('should throw an error when the server returns a non-ok status', async () => {
const service = createService();

server.use(
rest.get(benchmarkUrl, (_, res, ctx) => {
return res(ctx.status(401));
}),
);
await expect(
service.retrieveBenchmarkData({
type: 'df',
}),
).rejects.toEqual(new Error('Unauthorized'));

server.use(
rest.get(benchmarkUrl, (_, res, ctx) => {
return res(ctx.status(500));
}),
);
await expect(
service.retrieveBenchmarkData({
type: 'df',
}),
).rejects.toEqual(new Error('Internal Server Error'));
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ConfigApi, createApiRef } from '@backstage/core-plugin-api';
import { MetricData } from '../models/MetricData';
import { dfBenchmarkData } from '../models/DfBenchmarkData';

export const groupDataServiceApiRef = createApiRef<GroupDataService>({
id: 'plugin.open-dora.group-data',
Expand Down Expand Up @@ -41,4 +42,30 @@ export class GroupDataService {

return response as MetricData;
}

async retrieveBenchmarkData(params: { type: string }) {
const baseUrl = this.options.configApi.getString('open-dora.apiBaseUrl');

const url = new URL(baseUrl);
url.pathname = 'dora/api/benchmark';
for (const [key, value] of Object.entries(params)) {
if (value) {
url.searchParams.append(key, value);
}
}
const data = await fetch(url.toString(), {
method: 'GET',
});

if (!data.ok) {
throw new Error(data.statusText);
}

const response = await data.json();
if (response.key === undefined) {
throw new Error('Unexpected response');
}

return response as dfBenchmarkData;
}
}
9 changes: 9 additions & 0 deletions backstage-plugin/plugins/open-dora/testing/mswHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { rest } from 'msw';

export const baseUrl = 'http://localhost:10666';
export const metricUrl = `${baseUrl}/dora/api/metric`;
export const benchmarkUrl = `${baseUrl}/dora/api/benchmark`;

export const handlers = [
rest.get(metricUrl, (_, res, ctx) => {
Expand All @@ -12,4 +13,12 @@ export const handlers = [
}),
);
}),

rest.get(benchmarkUrl, (_, res, ctx) => {
return res(
ctx.json({
key: 'on-demand',
}),
);
}),
];
Loading

0 comments on commit 73c6239

Please sign in to comment.