mirror of
https://github.com/ansible/awx.git
synced 2026-01-13 11:00:03 -03:30
Merge pull request #9739 from nixocio/ui_issue_9723
Add templates screen to EE Add templates screen to EE. See: #9723 Reviewed-by: Jake McDermott <yo@jakemcdermott.me> Reviewed-by: Tiago Góes <tiago.goes2009@gmail.com>
This commit is contained in:
commit
85b3d7c515
@ -5,6 +5,16 @@ class ExecutionEnvironments extends Base {
|
||||
super(http);
|
||||
this.baseUrl = '/api/v2/execution_environments/';
|
||||
}
|
||||
|
||||
readUnifiedJobTemplates(id, params) {
|
||||
return this.http.get(`${this.baseUrl}${id}/unified_job_templates/`, {
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
readUnifiedJobTemplateOptions(id) {
|
||||
return this.http.options(`${this.baseUrl}${id}/unified_job_templates/`);
|
||||
}
|
||||
}
|
||||
|
||||
export default ExecutionEnvironments;
|
||||
|
||||
@ -36,10 +36,8 @@ class Organizations extends InstanceGroupsMixin(NotificationsMixin(Base)) {
|
||||
});
|
||||
}
|
||||
|
||||
readExecutionEnvironmentsOptions(id, params) {
|
||||
return this.http.options(`${this.baseUrl}${id}/execution_environments/`, {
|
||||
params,
|
||||
});
|
||||
readExecutionEnvironmentsOptions(id) {
|
||||
return this.http.options(`${this.baseUrl}${id}/execution_environments/`);
|
||||
}
|
||||
|
||||
createUser(id, data) {
|
||||
|
||||
@ -20,6 +20,7 @@ import ContentLoading from '../../components/ContentLoading';
|
||||
|
||||
import ExecutionEnvironmentDetails from './ExecutionEnvironmentDetails';
|
||||
import ExecutionEnvironmentEdit from './ExecutionEnvironmentEdit';
|
||||
import ExecutionEnvironmentTemplateList from './ExecutionEnvironmentTemplate';
|
||||
|
||||
function ExecutionEnvironment({ i18n, setBreadcrumb }) {
|
||||
const { id } = useParams();
|
||||
@ -64,6 +65,11 @@ function ExecutionEnvironment({ i18n, setBreadcrumb }) {
|
||||
link: `/execution_environments/${id}/details`,
|
||||
id: 0,
|
||||
},
|
||||
{
|
||||
name: i18n._(t`Templates`),
|
||||
link: `/execution_environments/${id}/templates`,
|
||||
id: 1,
|
||||
},
|
||||
];
|
||||
|
||||
if (!isLoading && contentError) {
|
||||
@ -114,6 +120,11 @@ function ExecutionEnvironment({ i18n, setBreadcrumb }) {
|
||||
executionEnvironment={executionEnvironment}
|
||||
/>
|
||||
</Route>
|
||||
<Route path="/execution_environments/:id/templates">
|
||||
<ExecutionEnvironmentTemplateList
|
||||
executionEnvironment={executionEnvironment}
|
||||
/>
|
||||
</Route>
|
||||
</>
|
||||
)}
|
||||
</Switch>
|
||||
|
||||
@ -0,0 +1,139 @@
|
||||
import React, { useEffect, useCallback } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { withI18n } from '@lingui/react';
|
||||
import { t } from '@lingui/macro';
|
||||
import { Card } from '@patternfly/react-core';
|
||||
|
||||
import { ExecutionEnvironmentsAPI } from '../../../api';
|
||||
import { getQSConfig, parseQueryString } from '../../../util/qs';
|
||||
import useRequest from '../../../util/useRequest';
|
||||
import DatalistToolbar from '../../../components/DataListToolbar';
|
||||
import PaginatedDataList from '../../../components/PaginatedDataList';
|
||||
|
||||
import ExecutionEnvironmentTemplateListItem from './ExecutionEnvironmentTemplateListItem';
|
||||
|
||||
const QS_CONFIG = getQSConfig(
|
||||
'execution_environments',
|
||||
{
|
||||
page: 1,
|
||||
page_size: 20,
|
||||
order_by: 'name',
|
||||
type: 'job_template,workflow_job_template',
|
||||
},
|
||||
['id', 'page', 'page_size']
|
||||
);
|
||||
|
||||
function ExecutionEnvironmentTemplateList({ i18n, executionEnvironment }) {
|
||||
const { id } = executionEnvironment;
|
||||
const location = useLocation();
|
||||
|
||||
const {
|
||||
error: contentError,
|
||||
isLoading,
|
||||
request: fetchTemplates,
|
||||
result: {
|
||||
templates,
|
||||
templatesCount,
|
||||
relatedSearchableKeys,
|
||||
searchableKeys,
|
||||
},
|
||||
} = useRequest(
|
||||
useCallback(async () => {
|
||||
const params = parseQueryString(QS_CONFIG, location.search);
|
||||
|
||||
const [response, responseActions] = await Promise.all([
|
||||
ExecutionEnvironmentsAPI.readUnifiedJobTemplates(id, params),
|
||||
ExecutionEnvironmentsAPI.readUnifiedJobTemplateOptions(id),
|
||||
]);
|
||||
|
||||
return {
|
||||
templates: response.data.results,
|
||||
templatesCount: response.data.count,
|
||||
actions: responseActions.data.actions,
|
||||
relatedSearchableKeys: (
|
||||
responseActions?.data?.related_search_fields || []
|
||||
).map(val => val.slice(0, -8)),
|
||||
searchableKeys: Object.keys(
|
||||
responseActions.data.actions?.GET || {}
|
||||
).filter(key => responseActions.data.actions?.GET[key].filterable),
|
||||
};
|
||||
}, [location, id]),
|
||||
{
|
||||
templates: [],
|
||||
templatesCount: 0,
|
||||
actions: {},
|
||||
relatedSearchableKeys: [],
|
||||
searchableKeys: [],
|
||||
}
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
fetchTemplates();
|
||||
}, [fetchTemplates]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card>
|
||||
<PaginatedDataList
|
||||
contentError={contentError}
|
||||
hasContentLoading={isLoading}
|
||||
items={templates}
|
||||
itemCount={templatesCount}
|
||||
pluralizedItemName={i18n._(t`Templates`)}
|
||||
qsConfig={QS_CONFIG}
|
||||
toolbarSearchableKeys={searchableKeys}
|
||||
toolbarRelatedSearchableKeys={relatedSearchableKeys}
|
||||
toolbarSearchColumns={[
|
||||
{
|
||||
name: i18n._(t`Name`),
|
||||
key: 'name__icontains',
|
||||
isDefault: true,
|
||||
},
|
||||
{
|
||||
name: i18n._(t`Type`),
|
||||
key: 'or__type',
|
||||
options: [
|
||||
[`job_template`, i18n._(t`Job Template`)],
|
||||
[`workflow_job_template`, i18n._(t`Workflow Template`)],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: i18n._(t`Created By (Username)`),
|
||||
key: 'created_by__username__icontains',
|
||||
},
|
||||
{
|
||||
name: i18n._(t`Modified By (Username)`),
|
||||
key: 'modified_by__username__icontains',
|
||||
},
|
||||
]}
|
||||
toolbarSortColumns={[
|
||||
{
|
||||
name: i18n._(t`Name`),
|
||||
key: 'name',
|
||||
},
|
||||
{
|
||||
name: i18n._(t`Created`),
|
||||
key: 'created',
|
||||
},
|
||||
{
|
||||
name: i18n._(t`Modified`),
|
||||
key: 'modified',
|
||||
},
|
||||
]}
|
||||
renderToolbar={props => (
|
||||
<DatalistToolbar {...props} qsConfig={QS_CONFIG} />
|
||||
)}
|
||||
renderItem={template => (
|
||||
<ExecutionEnvironmentTemplateListItem
|
||||
key={template.id}
|
||||
template={template}
|
||||
detailUrl={`/templates/${template.type}/${template.id}/details`}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Card>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default withI18n()(ExecutionEnvironmentTemplateList);
|
||||
@ -0,0 +1,116 @@
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
|
||||
import {
|
||||
mountWithContexts,
|
||||
waitForElement,
|
||||
} from '../../../../testUtils/enzymeHelpers';
|
||||
|
||||
import { ExecutionEnvironmentsAPI } from '../../../api';
|
||||
import ExecutionEnvironmentTemplateList from './ExecutionEnvironmentTemplateList';
|
||||
|
||||
jest.mock('../../../api/');
|
||||
|
||||
const templates = {
|
||||
data: {
|
||||
count: 3,
|
||||
results: [
|
||||
{
|
||||
id: 1,
|
||||
type: 'job_template',
|
||||
name: 'Foo',
|
||||
url: '/api/v2/job_templates/1/',
|
||||
related: {
|
||||
execution_environment: '/api/v2/execution_environments/1/',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
type: 'workflow_job_template',
|
||||
name: 'Bar',
|
||||
url: '/api/v2/workflow_job_templates/2/',
|
||||
related: {
|
||||
execution_environment: '/api/v2/execution_environments/1/',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
type: 'job_template',
|
||||
name: 'Fuzz',
|
||||
url: '/api/v2/job_templates/3/',
|
||||
related: {
|
||||
execution_environment: '/api/v2/execution_environments/1/',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const mockExecutionEnvironment = {
|
||||
id: 1,
|
||||
name: 'Default EE',
|
||||
};
|
||||
|
||||
const options = { data: { actions: { GET: {} } } };
|
||||
|
||||
describe('<ExecutionEnvironmentTemplateList/>', () => {
|
||||
let wrapper;
|
||||
|
||||
test('should mount successfully', async () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<ExecutionEnvironmentTemplateList
|
||||
executionEnvironment={mockExecutionEnvironment}
|
||||
/>
|
||||
);
|
||||
});
|
||||
await waitForElement(
|
||||
wrapper,
|
||||
'ExecutionEnvironmentTemplateList',
|
||||
el => el.length > 0
|
||||
);
|
||||
});
|
||||
|
||||
test('should have data fetched and render 3 rows', async () => {
|
||||
ExecutionEnvironmentsAPI.readUnifiedJobTemplates.mockResolvedValue(
|
||||
templates
|
||||
);
|
||||
|
||||
ExecutionEnvironmentsAPI.readUnifiedJobTemplateOptions.mockResolvedValue(
|
||||
options
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<ExecutionEnvironmentTemplateList
|
||||
executionEnvironment={mockExecutionEnvironment}
|
||||
/>
|
||||
);
|
||||
});
|
||||
await waitForElement(
|
||||
wrapper,
|
||||
'ExecutionEnvironmentTemplateList',
|
||||
el => el.length > 0
|
||||
);
|
||||
|
||||
expect(wrapper.find('ExecutionEnvironmentTemplateListItem').length).toBe(3);
|
||||
expect(ExecutionEnvironmentsAPI.readUnifiedJobTemplates).toBeCalled();
|
||||
expect(ExecutionEnvironmentsAPI.readUnifiedJobTemplateOptions).toBeCalled();
|
||||
});
|
||||
|
||||
test('should not render add button', async () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<ExecutionEnvironmentTemplateList
|
||||
executionEnvironment={mockExecutionEnvironment}
|
||||
/>
|
||||
);
|
||||
});
|
||||
waitForElement(
|
||||
wrapper,
|
||||
'ExecutionEnvironmentTemplateList',
|
||||
el => el.length > 0
|
||||
);
|
||||
expect(wrapper.find('ToolbarAddButton').length).toBe(0);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,43 @@
|
||||
import React from 'react';
|
||||
import { withI18n } from '@lingui/react';
|
||||
import { t } from '@lingui/macro';
|
||||
import { Link } from 'react-router-dom';
|
||||
import {
|
||||
DataListItem,
|
||||
DataListItemRow,
|
||||
DataListItemCells,
|
||||
} from '@patternfly/react-core';
|
||||
|
||||
import DataListCell from '../../../components/DataListCell';
|
||||
|
||||
function ExecutionEnvironmentTemplateListItem({ template, detailUrl, i18n }) {
|
||||
return (
|
||||
<DataListItem
|
||||
key={template.id}
|
||||
aria-labelledby={`check-action-${template.id}`}
|
||||
id={`${template.id}`}
|
||||
>
|
||||
<DataListItemRow>
|
||||
<DataListItemCells
|
||||
dataListCells={[
|
||||
<DataListCell key="name" aria-label={i18n._(t`Name`)}>
|
||||
<Link to={`${detailUrl}`}>
|
||||
<b>{template.name}</b>
|
||||
</Link>
|
||||
</DataListCell>,
|
||||
<DataListCell
|
||||
key="template-type"
|
||||
aria-label={i18n._(t`Template type`)}
|
||||
>
|
||||
{template.type === 'job_template'
|
||||
? i18n._(t`Job Template`)
|
||||
: i18n._(t`Workflow Job Template`)}
|
||||
</DataListCell>,
|
||||
]}
|
||||
/>
|
||||
</DataListItemRow>
|
||||
</DataListItem>
|
||||
);
|
||||
}
|
||||
|
||||
export default withI18n()(ExecutionEnvironmentTemplateListItem);
|
||||
@ -0,0 +1,48 @@
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
|
||||
import { mountWithContexts } from '../../../../testUtils/enzymeHelpers';
|
||||
|
||||
import ExecutionEnvironmentTemplateListItem from './ExecutionEnvironmentTemplateListItem';
|
||||
|
||||
describe('<ExecutionEnvironmentTemplateListItem/>', () => {
|
||||
let wrapper;
|
||||
const template = {
|
||||
id: 1,
|
||||
name: 'Foo',
|
||||
type: 'job_template',
|
||||
};
|
||||
|
||||
test('should mount successfully', async () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<ExecutionEnvironmentTemplateListItem
|
||||
template={template}
|
||||
detailUrl={`/templates/${template.type}/${template.id}/details`}
|
||||
/>
|
||||
);
|
||||
});
|
||||
expect(wrapper.find('ExecutionEnvironmentTemplateListItem').length).toBe(1);
|
||||
expect(wrapper.find('DataListCell[aria-label="Name"]').text()).toBe(
|
||||
template.name
|
||||
);
|
||||
expect(
|
||||
wrapper.find('DataListCell[aria-label="Template type"]').text()
|
||||
).toBe('Job Template');
|
||||
});
|
||||
|
||||
test('should distinguish template types', async () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<ExecutionEnvironmentTemplateListItem
|
||||
template={{ ...template, type: 'workflow_job_template' }}
|
||||
detailUrl={`/templates/${template.type}/${template.id}/details`}
|
||||
/>
|
||||
);
|
||||
});
|
||||
expect(wrapper.find('ExecutionEnvironmentTemplateListItem').length).toBe(1);
|
||||
expect(
|
||||
wrapper.find('DataListCell[aria-label="Template type"]').text()
|
||||
).toBe('Workflow Job Template');
|
||||
});
|
||||
});
|
||||
@ -0,0 +1 @@
|
||||
export { default } from './ExecutionEnvironmentTemplateList';
|
||||
@ -38,7 +38,7 @@ function OrganizationExecEnvList({ i18n, organization }) {
|
||||
|
||||
const [response, responseActions] = await Promise.all([
|
||||
OrganizationsAPI.readExecutionEnvironments(id, params),
|
||||
OrganizationsAPI.readExecutionEnvironmentsOptions(id, params),
|
||||
OrganizationsAPI.readExecutionEnvironmentsOptions(id),
|
||||
]);
|
||||
|
||||
return {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user