mirror of
https://github.com/ansible/awx.git
synced 2026-05-07 17:37:37 -02:30
Merge pull request #6239 from marshmalien/check-host-inventory-id
Check for top-level inventory and host inventory match Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
This commit is contained in:
@@ -8,6 +8,7 @@ class Inventories extends InstanceGroupsMixin(Base) {
|
|||||||
|
|
||||||
this.readAccessList = this.readAccessList.bind(this);
|
this.readAccessList = this.readAccessList.bind(this);
|
||||||
this.readHosts = this.readHosts.bind(this);
|
this.readHosts = this.readHosts.bind(this);
|
||||||
|
this.readHostDetail = this.readHostDetail.bind(this);
|
||||||
this.readGroups = this.readGroups.bind(this);
|
this.readGroups = this.readGroups.bind(this);
|
||||||
this.readGroupsOptions = this.readGroupsOptions.bind(this);
|
this.readGroupsOptions = this.readGroupsOptions.bind(this);
|
||||||
this.promoteGroup = this.promoteGroup.bind(this);
|
this.promoteGroup = this.promoteGroup.bind(this);
|
||||||
@@ -27,6 +28,22 @@ class Inventories extends InstanceGroupsMixin(Base) {
|
|||||||
return this.http.get(`${this.baseUrl}${id}/hosts/`, { params });
|
return this.http.get(`${this.baseUrl}${id}/hosts/`, { params });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async readHostDetail(inventoryId, hostId) {
|
||||||
|
const {
|
||||||
|
data: { results },
|
||||||
|
} = await this.http.get(
|
||||||
|
`${this.baseUrl}${inventoryId}/hosts/?id=${hostId}`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (Array.isArray(results) && results.length) {
|
||||||
|
return results[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(
|
||||||
|
`How did you get here? Host not found for Inventory ID: ${inventoryId}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
readGroups(id, params) {
|
readGroups(id, params) {
|
||||||
return this.http.get(`${this.baseUrl}${id}/groups/`, { params });
|
return this.http.get(`${this.baseUrl}${id}/groups/`, { params });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
} from 'react-router-dom';
|
} from 'react-router-dom';
|
||||||
import useRequest from '@util/useRequest';
|
import useRequest from '@util/useRequest';
|
||||||
|
|
||||||
import { HostsAPI } from '@api';
|
import { InventoriesAPI } from '@api';
|
||||||
import { Card, CardActions } from '@patternfly/react-core';
|
import { Card, CardActions } from '@patternfly/react-core';
|
||||||
import { CaretLeftIcon } from '@patternfly/react-icons';
|
import { CaretLeftIcon } from '@patternfly/react-icons';
|
||||||
import { TabbedCardHeader } from '@components/Card';
|
import { TabbedCardHeader } from '@components/Card';
|
||||||
@@ -35,12 +35,14 @@ function InventoryHost({ i18n, setBreadcrumb, inventory }) {
|
|||||||
request: fetchHost,
|
request: fetchHost,
|
||||||
} = useRequest(
|
} = useRequest(
|
||||||
useCallback(async () => {
|
useCallback(async () => {
|
||||||
const { data } = await HostsAPI.readDetail(match.params.hostId);
|
const response = await InventoriesAPI.readHostDetail(
|
||||||
|
inventory.id,
|
||||||
|
match.params.hostId
|
||||||
|
);
|
||||||
return {
|
return {
|
||||||
host: data,
|
host: response,
|
||||||
};
|
};
|
||||||
}, [match.params.hostId]), // eslint-disable-line react-hooks/exhaustive-deps
|
}, [inventory.id, match.params.hostId]),
|
||||||
{
|
{
|
||||||
host: null,
|
host: null,
|
||||||
}
|
}
|
||||||
@@ -48,7 +50,7 @@ function InventoryHost({ i18n, setBreadcrumb, inventory }) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchHost();
|
fetchHost();
|
||||||
}, [fetchHost]);
|
}, [fetchHost, location.pathname]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (inventory && host) {
|
if (inventory && host) {
|
||||||
@@ -89,24 +91,7 @@ function InventoryHost({ i18n, setBreadcrumb, inventory }) {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
let cardHeader = (
|
if (contentError) {
|
||||||
<TabbedCardHeader>
|
|
||||||
<RoutedTabs tabsArray={tabsArray} />
|
|
||||||
<CardActions>
|
|
||||||
<CardCloseButton linkTo={hostListUrl} />
|
|
||||||
</CardActions>
|
|
||||||
</TabbedCardHeader>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (location.pathname.endsWith('edit')) {
|
|
||||||
cardHeader = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isLoading) {
|
|
||||||
return <ContentLoading />;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isLoading && contentError) {
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<ContentError error={contentError}>
|
<ContentError error={contentError}>
|
||||||
@@ -125,48 +110,51 @@ function InventoryHost({ i18n, setBreadcrumb, inventory }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{cardHeader}
|
{['edit'].some(name => location.pathname.includes(name)) ? null : (
|
||||||
<Switch>
|
<TabbedCardHeader>
|
||||||
<Redirect
|
<RoutedTabs tabsArray={tabsArray} />
|
||||||
from="/inventories/inventory/:id/hosts/:hostId"
|
<CardActions>
|
||||||
to="/inventories/inventory/:id/hosts/:hostId/details"
|
<CardCloseButton linkTo={hostListUrl} />
|
||||||
exact
|
</CardActions>
|
||||||
/>
|
</TabbedCardHeader>
|
||||||
{host &&
|
)}
|
||||||
inventory && [
|
|
||||||
<Route
|
{isLoading && <ContentLoading />}
|
||||||
key="details"
|
|
||||||
path="/inventories/inventory/:id/hosts/:hostId/details"
|
{!isLoading && host && (
|
||||||
>
|
<Switch>
|
||||||
<InventoryHostDetail host={host} />
|
<Redirect
|
||||||
</Route>,
|
from="/inventories/inventory/:id/hosts/:hostId"
|
||||||
<Route
|
to="/inventories/inventory/:id/hosts/:hostId/details"
|
||||||
key="edit"
|
exact
|
||||||
path="/inventories/inventory/:id/hosts/:hostId/edit"
|
/>
|
||||||
>
|
<Route
|
||||||
<InventoryHostEdit host={host} inventory={inventory} />
|
key="details"
|
||||||
</Route>,
|
path="/inventories/inventory/:id/hosts/:hostId/details"
|
||||||
<Route
|
>
|
||||||
key="completed-jobs"
|
<InventoryHostDetail host={host} />
|
||||||
path="/inventories/inventory/:id/hosts/:hostId/completed_jobs"
|
</Route>
|
||||||
>
|
<Route
|
||||||
<JobList defaultParams={{ job__hosts: host.id }} />
|
key="edit"
|
||||||
</Route>,
|
path="/inventories/inventory/:id/hosts/:hostId/edit"
|
||||||
]}
|
>
|
||||||
<Route
|
<InventoryHostEdit host={host} inventory={inventory} />
|
||||||
key="not-found"
|
</Route>
|
||||||
path="*"
|
<Route
|
||||||
render={() =>
|
key="completed-jobs"
|
||||||
!isLoading && (
|
path="/inventories/inventory/:id/hosts/:hostId/completed_jobs"
|
||||||
<ContentError isNotFound>
|
>
|
||||||
<Link to={`${match.url}/details`}>
|
<JobList defaultParams={{ job__hosts: host.id }} />
|
||||||
{i18n._(`View Inventory Host Details`)}
|
</Route>
|
||||||
</Link>
|
<Route key="not-found" path="*">
|
||||||
</ContentError>
|
<ContentError isNotFound>
|
||||||
)
|
<Link to={`${match.url}/details`}>
|
||||||
}
|
{i18n._(`View Inventory Host Details`)}
|
||||||
/>
|
</Link>
|
||||||
</Switch>
|
</ContentError>
|
||||||
|
</Route>
|
||||||
|
</Switch>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { act } from 'react-dom/test-utils';
|
import { act } from 'react-dom/test-utils';
|
||||||
import { createMemoryHistory } from 'history';
|
import { createMemoryHistory } from 'history';
|
||||||
import { HostsAPI } from '@api';
|
import { InventoriesAPI } from '@api';
|
||||||
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
|
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
|
||||||
import mockHost from '../shared/data.host.json';
|
import mockHost from '../shared/data.host.json';
|
||||||
import InventoryHost from './InventoryHost';
|
import InventoryHost from './InventoryHost';
|
||||||
@@ -15,7 +15,7 @@ jest.mock('react-router-dom', () => ({
|
|||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
HostsAPI.readDetail.mockResolvedValue({
|
InventoriesAPI.readHostDetail.mockResolvedValue({
|
||||||
data: { ...mockHost },
|
data: { ...mockHost },
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ describe('<InventoryHost />', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should show content error when api throws error on initial render', async () => {
|
test('should show content error when api throws error on initial render', async () => {
|
||||||
HostsAPI.readDetail.mockRejectedValueOnce(new Error());
|
InventoriesAPI.readHostDetail.mockRejectedValueOnce(new Error());
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper = mountWithContexts(
|
wrapper = mountWithContexts(
|
||||||
<InventoryHost inventory={mockInventory} setBreadcrumb={() => {}} />
|
<InventoryHost inventory={mockInventory} setBreadcrumb={() => {}} />
|
||||||
@@ -76,4 +76,13 @@ describe('<InventoryHost />', () => {
|
|||||||
});
|
});
|
||||||
await waitForElement(wrapper, 'ContentError', el => el.length === 1);
|
await waitForElement(wrapper, 'ContentError', el => el.length === 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should show content error when inventory id does not match host inventory', async () => {
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<InventoryHost inventory={{ id: 99 }} setBreadcrumb={() => {}} />
|
||||||
|
);
|
||||||
|
});
|
||||||
|
await waitForElement(wrapper, 'ContentError', el => el.length === 1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user