-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(backstage-plugin): Refactor data service
Reuse request logic in the data service. Rename data service from GroupDataService to DoraDataService. Reorganise tests to describe them better. Rename test to describe what is being tested. Addresses #71
- Loading branch information
1 parent
feff7e4
commit bb6f4f4
Showing
8 changed files
with
242 additions
and
252 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
8 changes: 4 additions & 4 deletions
8
backstage-plugin/plugins/open-dora/src/hooks/MetricBenchmarkHook.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,18 @@ | ||
import { useApi } from '@backstage/core-plugin-api'; | ||
import { useEffect, useState } from 'react'; | ||
import { dfBenchmarkKey } from '../models/DfBenchmarkData'; | ||
import { groupDataServiceApiRef } from '../services/GroupDataService'; | ||
import { doraDataServiceApiRef } from '../services/DoraDataService'; | ||
|
||
export const useMetricBenchmark = (type: string) => { | ||
const groupDataService = useApi(groupDataServiceApiRef); | ||
const doraDataService = useApi(doraDataServiceApiRef); | ||
const [benchmark, setDfBenchmark] = useState<dfBenchmarkKey | undefined>(); | ||
const [error, setError] = useState<Error | undefined>(); | ||
|
||
useEffect(() => { | ||
groupDataService.retrieveBenchmarkData({ type: type }).then(response => { | ||
doraDataService.retrieveBenchmarkData({ type: type }).then(response => { | ||
setDfBenchmark(response.key); | ||
}, setError); | ||
}, [groupDataService, type]); | ||
}, [doraDataService, type]); | ||
|
||
return { error: error, benchmark: benchmark }; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
201 changes: 201 additions & 0 deletions
201
backstage-plugin/plugins/open-dora/src/services/DoraDataService.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
import { MockConfigApi } from '@backstage/test-utils'; | ||
import { rest } from 'msw'; | ||
import { baseUrl, benchmarkUrl, metricUrl } from '../../testing/mswHandlers'; | ||
import { server } from '../setupTests'; | ||
import { DoraDataService } from './DoraDataService'; | ||
|
||
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'); | ||
const aggregation = params.get('aggregation'); | ||
const project = params.get('project'); | ||
const team = params.get('team'); | ||
|
||
return res( | ||
ctx.json({ | ||
aggregation: aggregation || 'weekly', | ||
dataPoints: [ | ||
{ | ||
key: `${project}_${team}_${aggregation}_${type}_first_key`, | ||
value: 2.3, | ||
}, | ||
], | ||
}), | ||
); | ||
}), | ||
); | ||
const mockConfig = new MockConfigApi({ | ||
'open-dora': { apiBaseUrl: baseUrl }, | ||
}); | ||
|
||
return new DoraDataService({ configApi: mockConfig }); | ||
} | ||
|
||
describe('DoraDataService', () => { | ||
describe('retriveMetricDataPoints', () => { | ||
it('should return data from the server', async () => { | ||
const service = createService(); | ||
|
||
expect( | ||
await service.retrieveMetricDataPoints({ type: 'df_count' }), | ||
).toEqual({ | ||
aggregation: 'weekly', | ||
dataPoints: [{ key: 'null_null_null_df_count_first_key', value: 2.3 }], | ||
}); | ||
}); | ||
|
||
it('should use provided details in the query parameters', async () => { | ||
const service = createService(); | ||
|
||
expect( | ||
await service.retrieveMetricDataPoints({ | ||
type: 'df_count', | ||
aggregation: 'monthly', | ||
project: 'project1', | ||
team: 'team1', | ||
}), | ||
).toEqual({ | ||
aggregation: 'monthly', | ||
dataPoints: [ | ||
{ key: 'project1_team1_monthly_df_count_first_key', value: 2.3 }, | ||
], | ||
}); | ||
}); | ||
|
||
it('should throw an error if the response does not contain metric data', async () => { | ||
const service = createService(); | ||
|
||
server.use( | ||
rest.get(metricUrl, (_, res, ctx) => { | ||
return res(ctx.json({ other: 'data' })); | ||
}), | ||
); | ||
await expect( | ||
service.retrieveMetricDataPoints({ | ||
type: 'df_count', | ||
}), | ||
).rejects.toEqual(new Error('Unexpected response')); | ||
}); | ||
|
||
it('should throw an error when the server is unreachable', async () => { | ||
const service = createService(); | ||
|
||
server.use( | ||
rest.get(metricUrl, (_, res) => { | ||
return res.networkError('Host unreachable'); | ||
}), | ||
); | ||
await expect( | ||
service.retrieveMetricDataPoints({ | ||
type: 'df_count', | ||
}), | ||
).rejects.toEqual(new Error('Failed to fetch')); | ||
}); | ||
|
||
it('should throw an error when the server returns a non-ok status', async () => { | ||
const service = createService(); | ||
|
||
server.use( | ||
rest.get(metricUrl, (_, res, ctx) => { | ||
return res(ctx.status(401)); | ||
}), | ||
); | ||
await expect( | ||
service.retrieveMetricDataPoints({ | ||
type: 'df_count', | ||
}), | ||
).rejects.toEqual(new Error('Unauthorized')); | ||
|
||
server.use( | ||
rest.get(metricUrl, (_, res, ctx) => { | ||
return res(ctx.status(500)); | ||
}), | ||
); | ||
await expect( | ||
service.retrieveMetricDataPoints({ | ||
type: 'df_count', | ||
}), | ||
).rejects.toEqual(new Error('Internal Server Error')); | ||
}); | ||
}); | ||
|
||
describe('retrieveBenchmarkData', () => { | ||
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 send the params in the request', 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')); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.