Skip to content

Commit

Permalink
Add feature to Add/Edit Execution Environments (#8165)
Browse files Browse the repository at this point in the history
* Add feature to Add/Edit Execution Environments

Add feature to Add/Edit Execution Environments.

Also, add key for `ExecutionEnvironmentsList`.

See: #7887

* Update registry credential label
  • Loading branch information
nixocio authored and jbradberry committed Sep 21, 2020
1 parent f3b3172 commit af6f441
Show file tree
Hide file tree
Showing 11 changed files with 570 additions and 29 deletions.
2 changes: 1 addition & 1 deletion awx/ui_next/src/routeConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ function getRouteConfig(i18n) {
screen: Applications,
},
{
title: i18n._(t`Execution environments`),
title: i18n._(t`Execution Environments`),
path: '/execution_environments',
screen: ExecutionEnvironments,
},
Expand Down
133 changes: 116 additions & 17 deletions awx/ui_next/src/screens/ExecutionEnvironment/ExecutionEnvironment.jsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,124 @@
import React from 'react';
import { Route, Redirect, Switch } from 'react-router-dom';
import React, { useEffect, useCallback } from 'react';
import {
Link,
Redirect,
Route,
Switch,
useLocation,
useParams,
} from 'react-router-dom';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { Card, PageSection } from '@patternfly/react-core';
import { CaretLeftIcon } from '@patternfly/react-icons';

import useRequest from '../../util/useRequest';
import { ExecutionEnvironmentsAPI } from '../../api';
import RoutedTabs from '../../components/RoutedTabs';
import ContentError from '../../components/ContentError';
import ContentLoading from '../../components/ContentLoading';

import ExecutionEnvironmentDetails from './ExecutionEnvironmentDetails';
import ExecutionEnvironmentEdit from './ExecutionEnvironmentEdit';

function ExecutionEnvironment() {
function ExecutionEnvironment({ i18n, setBreadcrumb }) {
const { id } = useParams();
const { pathname } = useLocation();

const {
isLoading,
error: contentError,
request: fetchExecutionEnvironments,
result: executionEnvironment,
} = useRequest(
useCallback(async () => {
const { data } = await ExecutionEnvironmentsAPI.readDetail(id);
return data;
}, [id]),
null
);

useEffect(() => {
fetchExecutionEnvironments();
}, [fetchExecutionEnvironments, pathname]);

useEffect(() => {
if (executionEnvironment) {
setBreadcrumb(executionEnvironment);
}
}, [executionEnvironment, setBreadcrumb]);

const tabsArray = [
{
name: (
<>
<CaretLeftIcon />
{i18n._(t`Back to execution environments`)}
</>
),
link: '/execution_environments',
id: 99,
},
{
name: i18n._(t`Details`),
link: `/execution_environments/${id}/details`,
id: 0,
},
];

if (!isLoading && contentError) {
return (
<PageSection>
<Card>
<ContentError error={contentError}>
{contentError.response?.status === 404 && (
<span>
{i18n._(t`Execution environment not found.`)}{' '}
<Link to="/execution_environments">
{i18n._(t`View all execution environments`)}
</Link>
</span>
)}
</ContentError>
</Card>
</PageSection>
);
}

let cardHeader = <RoutedTabs tabsArray={tabsArray} />;
if (pathname.endsWith('edit')) {
cardHeader = null;
}

return (
<Switch>
<Redirect
from="/execution_environments/:id"
to="/execution_environments/:id/details"
exact
/>
<Route path="/execution_environments/:id/edit">
<ExecutionEnvironmentEdit />
</Route>
<Route path="/execution_environments/:id/details">
<ExecutionEnvironmentDetails />
</Route>
</Switch>
<PageSection>
<Card>
{cardHeader}
{isLoading && <ContentLoading />}
{!isLoading && executionEnvironment && (
<Switch>
<Redirect
from="/execution_environments/:id"
to="/execution_environments/:id/details"
exact
/>
{executionEnvironment && (
<>
<Route path="/execution_environments/:id/edit">
<ExecutionEnvironmentEdit
executionEnvironment={executionEnvironment}
/>
</Route>
<Route path="/execution_environments/:id/details">
<ExecutionEnvironmentDetails />
</Route>
</>
)}
</Switch>
)}
</Card>
</PageSection>
);
}

export default ExecutionEnvironment;
export default withI18n()(ExecutionEnvironment);
Original file line number Diff line number Diff line change
@@ -1,11 +1,40 @@
import React from 'react';
import React, { useState } from 'react';
import { Card, PageSection } from '@patternfly/react-core';
import { useHistory } from 'react-router-dom';

import ExecutionEnvironmentForm from '../shared/ExecutionEnvironmentForm';
import { CardBody } from '../../../components/Card';
import { ExecutionEnvironmentsAPI } from '../../../api';

function ExecutionEnvironmentAdd() {
const history = useHistory();
const [submitError, setSubmitError] = useState(null);

const handleSubmit = async values => {
try {
const { data: response } = await ExecutionEnvironmentsAPI.create({
...values,
credential: values?.credential?.id,
});
history.push(`/execution_environments/${response.id}/details`);
} catch (error) {
setSubmitError(error);
}
};

const handleCancel = () => {
history.push(`/execution_environments`);
};
return (
<PageSection>
<Card>
<div>Add Execution Environments</div>
<CardBody>
<ExecutionEnvironmentForm
onSubmit={handleSubmit}
submitError={submitError}
onCancel={handleCancel}
/>
</CardBody>
</Card>
</PageSection>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import React from 'react';
import { act } from 'react-dom/test-utils';
import { createMemoryHistory } from 'history';

import { mountWithContexts } from '../../../../testUtils/enzymeHelpers';
import { ExecutionEnvironmentsAPI } from '../../../api';
import ExecutionEnvironmentAdd from './ExecutionEnvironmentAdd';

jest.mock('../../../api');

const executionEnvironmentData = {
credential: 4,
description: 'A simple EE',
image: 'https://registry.com/image/container',
};

ExecutionEnvironmentsAPI.create.mockResolvedValue({
data: {
id: 42,
},
});

describe('<ExecutionEnvironmentAdd/>', () => {
let wrapper;
let history;

beforeEach(async () => {
history = createMemoryHistory({
initialEntries: ['/execution_environments'],
});
await act(async () => {
wrapper = mountWithContexts(<ExecutionEnvironmentAdd />, {
context: { router: { history } },
});
});
});

afterEach(() => {
jest.clearAllMocks();
wrapper.unmount();
});

test('handleSubmit should call the api and redirect to details page', async () => {
await act(async () => {
wrapper.find('ExecutionEnvironmentForm').prop('onSubmit')({
executionEnvironmentData,
});
});
wrapper.update();
expect(ExecutionEnvironmentsAPI.create).toHaveBeenCalledWith({
executionEnvironmentData,
});
expect(history.location.pathname).toBe(
'/execution_environments/42/details'
);
});

test('handleCancel should return the user back to the execution environments list', async () => {
wrapper.find('Button[aria-label="Cancel"]').simulate('click');
expect(history.location.pathname).toEqual('/execution_environments');
});

test('failed form submission should show an error message', async () => {
const error = {
response: {
data: { detail: 'An error occurred' },
},
};
ExecutionEnvironmentsAPI.create.mockImplementationOnce(() =>
Promise.reject(error)
);
await act(async () => {
wrapper.find('ExecutionEnvironmentForm').invoke('onSubmit')(
executionEnvironmentData
);
});
wrapper.update();
expect(wrapper.find('FormSubmitError').length).toBe(1);
});
});
Original file line number Diff line number Diff line change
@@ -1,13 +1,39 @@
import React from 'react';
import { Card, PageSection } from '@patternfly/react-core';
import React, { useState } from 'react';
import { useHistory } from 'react-router-dom';

function ExecutionEnvironmentEdit() {
import { CardBody } from '../../../components/Card';
import { ExecutionEnvironmentsAPI } from '../../../api';
import ExecutionEnvironmentForm from '../shared/ExecutionEnvironmentForm';

function ExecutionEnvironmentEdit({ executionEnvironment }) {
const history = useHistory();
const [submitError, setSubmitError] = useState(null);
const detailsUrl = `/execution_environments/${executionEnvironment.id}/details`;

const handleSubmit = async values => {
try {
await ExecutionEnvironmentsAPI.update(executionEnvironment.id, {
...values,
credential: values.credential ? values.credential.id : null,
});
history.push(detailsUrl);
} catch (error) {
setSubmitError(error);
}
};

const handleCancel = () => {
history.push(detailsUrl);
};
return (
<PageSection>
<Card>
<div>Edit Execution environments</div>
</Card>
</PageSection>
<CardBody>
<ExecutionEnvironmentForm
executionEnvironment={executionEnvironment}
onSubmit={handleSubmit}
submitError={submitError}
onCancel={handleCancel}
/>
</CardBody>
);
}

Expand Down
Loading

0 comments on commit af6f441

Please sign in to comment.