-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Fleet] Show Count of Agent Policies on Integration Details #86916
Changes from all commits
8f209ef
1796a5d
1e7ce9f
58b0d39
874998c
9f2bb2a
ffb866d
c0f04d6
a3240ab
d4e9dd9
6da3349
89f49eb
b67afc1
c30a3c1
87daa10
3eefff1
13604f0
3b4d481
9bcd2d8
4c3d739
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,7 @@ import { | |
GetFleetStatusResponse, | ||
GetInfoResponse, | ||
GetPackagePoliciesResponse, | ||
GetStatsResponse, | ||
} from '../../../../../../../common/types/rest_spec'; | ||
import { DetailViewPanelName, KibanaAssetType } from '../../../../../../../common/types/models'; | ||
import { | ||
|
@@ -29,7 +30,7 @@ describe('when on integration detail', () => { | |
const detailPageUrlPath = pagePathGetters.integration_details({ pkgkey }); | ||
let testRenderer: TestRenderer; | ||
let renderResult: ReturnType<typeof testRenderer.render>; | ||
let mockedApi: MockedApi; | ||
let mockedApi: MockedApi<EpmPackageDetailsResponseProvidersMock>; | ||
const render = () => | ||
(renderResult = testRenderer.render( | ||
<Route path={PAGE_ROUTING_PATHS.integration_details}> | ||
|
@@ -48,6 +49,39 @@ describe('when on integration detail', () => { | |
window.location.hash = '#/'; | ||
}); | ||
|
||
describe('and the package is installed', () => { | ||
beforeEach(() => render()); | ||
|
||
it('should display agent policy usage count', async () => { | ||
await mockedApi.waitForApi(); | ||
expect(renderResult.queryByTestId('agentPolicyCount')).not.toBeNull(); | ||
}); | ||
|
||
it('should show the Policies tab', async () => { | ||
await mockedApi.waitForApi(); | ||
expect(renderResult.queryByTestId('tab-policies')).not.toBeNull(); | ||
}); | ||
}); | ||
|
||
describe('and the package is not installed', () => { | ||
beforeEach(() => { | ||
const unInstalledPackage = mockedApi.responseProvider.epmGetInfo(); | ||
unInstalledPackage.response.status = 'not_installed'; | ||
mockedApi.responseProvider.epmGetInfo.mockReturnValue(unInstalledPackage); | ||
render(); | ||
}); | ||
|
||
it('should NOT display agent policy usage count', async () => { | ||
await mockedApi.waitForApi(); | ||
expect(renderResult.queryByTestId('agentPolicyCount')).toBeNull(); | ||
}); | ||
|
||
it('should NOT the Policies tab', async () => { | ||
await mockedApi.waitForApi(); | ||
expect(renderResult.queryByTestId('tab-policies')).toBeNull(); | ||
}); | ||
}); | ||
|
||
describe('and a custom UI extension is NOT registered', () => { | ||
beforeEach(() => render()); | ||
|
||
|
@@ -190,12 +224,27 @@ describe('when on integration detail', () => { | |
}); | ||
}); | ||
|
||
interface MockedApi { | ||
interface MockedApi< | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Some of this here is refactoring which will facilitate breaking this out to a separate set of test utilities so that this approach can be used with other test areas of Fleet. |
||
R extends Record<string, jest.MockedFunction<any>> = Record<string, jest.MockedFunction<any>> | ||
> { | ||
/** Will return a promise that resolves when triggered APIs are complete */ | ||
waitForApi: () => Promise<void>; | ||
/** A object containing the list of API response provider functions that are used by the mocked API */ | ||
responseProvider: R; | ||
} | ||
|
||
const mockApiCalls = (http: MockedFleetStartServices['http']): MockedApi => { | ||
interface EpmPackageDetailsResponseProvidersMock { | ||
epmGetInfo: jest.MockedFunction<() => GetInfoResponse>; | ||
epmGetFile: jest.MockedFunction<() => string>; | ||
epmGetStats: jest.MockedFunction<() => GetStatsResponse>; | ||
fleetSetup: jest.MockedFunction<() => GetFleetStatusResponse>; | ||
packagePolicyList: jest.MockedFunction<() => GetPackagePoliciesResponse>; | ||
agentPolicyList: jest.MockedFunction<() => GetAgentPoliciesResponse>; | ||
} | ||
|
||
const mockApiCalls = ( | ||
http: MockedFleetStartServices['http'] | ||
): MockedApi<EpmPackageDetailsResponseProvidersMock> => { | ||
let inflightApiCalls = 0; | ||
const apiDoneListeners: Array<() => void> = []; | ||
const markApiCallAsHandled = async () => { | ||
|
@@ -663,31 +712,62 @@ On Windows, the module was tested with Nginx installed from the Chocolatey repos | |
perPage: 100, | ||
}; | ||
|
||
const epmGetStatsResponse: GetStatsResponse = { | ||
response: { | ||
agent_policy_count: 2, | ||
}, | ||
}; | ||
|
||
const mockedApiInterface: MockedApi<EpmPackageDetailsResponseProvidersMock> = { | ||
waitForApi() { | ||
return new Promise((resolve) => { | ||
if (inflightApiCalls > 0) { | ||
apiDoneListeners.push(resolve); | ||
} else { | ||
resolve(); | ||
} | ||
}); | ||
}, | ||
responseProvider: { | ||
epmGetInfo: jest.fn().mockReturnValue(epmPackageResponse), | ||
epmGetFile: jest.fn().mockReturnValue(packageReadMe), | ||
epmGetStats: jest.fn().mockReturnValue(epmGetStatsResponse), | ||
fleetSetup: jest.fn().mockReturnValue(agentsSetupResponse), | ||
packagePolicyList: jest.fn().mockReturnValue(packagePoliciesResponse), | ||
agentPolicyList: jest.fn().mockReturnValue(agentPoliciesResponse), | ||
}, | ||
}; | ||
|
||
http.get.mockImplementation(async (path) => { | ||
if (typeof path === 'string') { | ||
if (path === epmRouteService.getInfoPath(`nginx-0.3.7`)) { | ||
markApiCallAsHandled(); | ||
return epmPackageResponse; | ||
return mockedApiInterface.responseProvider.epmGetInfo(); | ||
} | ||
|
||
if (path === epmRouteService.getFilePath('/package/nginx/0.3.7/docs/README.md')) { | ||
markApiCallAsHandled(); | ||
return packageReadMe; | ||
return mockedApiInterface.responseProvider.epmGetFile(); | ||
} | ||
|
||
if (path === fleetSetupRouteService.getFleetSetupPath()) { | ||
markApiCallAsHandled(); | ||
return agentsSetupResponse; | ||
return mockedApiInterface.responseProvider.fleetSetup(); | ||
} | ||
|
||
if (path === packagePolicyRouteService.getListPath()) { | ||
markApiCallAsHandled(); | ||
return packagePoliciesResponse; | ||
return mockedApiInterface.responseProvider.packagePolicyList(); | ||
} | ||
|
||
if (path === agentPolicyRouteService.getListPath()) { | ||
markApiCallAsHandled(); | ||
return agentPoliciesResponse; | ||
return mockedApiInterface.responseProvider.agentPolicyList(); | ||
} | ||
|
||
if (path === epmRouteService.getStatsPath('nginx')) { | ||
markApiCallAsHandled(); | ||
return mockedApiInterface.responseProvider.epmGetStats(); | ||
} | ||
|
||
const err = new Error(`API [GET ${path}] is not MOCKED!`); | ||
|
@@ -697,15 +777,5 @@ On Windows, the module was tested with Nginx installed from the Chocolatey repos | |
} | ||
}); | ||
|
||
return { | ||
waitForApi() { | ||
return new Promise((resolve) => { | ||
if (inflightApiCalls > 0) { | ||
apiDoneListeners.push(resolve); | ||
} else { | ||
resolve(); | ||
} | ||
}); | ||
}, | ||
}; | ||
return mockedApiInterface; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
import React, { memo } from 'react'; | ||
import { useGetPackageStats } from '../../../../hooks'; | ||
|
||
/** | ||
* Displays a count of Agent Policies that are using the given integration | ||
*/ | ||
export const IntegrationAgentPolicyCount = memo<{ packageName: string }>(({ packageName }) => { | ||
const { data } = useGetPackageStats(packageName); | ||
|
||
return <>{data?.response.agent_policy_count ?? 0}</>; | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to be called summary elsewhere. Perhaps we should make the var names consistent, to mentally correlate them a bit better.
If the route already exists and is
/stats
, I can see giving the route's var name the same as the route itself. But then it's switched to called summary on the layer right outside that. Consider naming the pattern var name summary as well. Reduces the mental context switch to just here, next to the route, which is a little more concrete to reason about.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have renamed everything else
*Stats*
in order keep consistent with this decision here 😄 . Thanks for highlighting that - "me culpa" 😞