update api calls to utilized network context

This commit is contained in:
John Mitchell 2019-04-08 12:34:02 -04:00
parent 722ae932ab
commit fa232a94bd
No known key found for this signature in database
GPG Key ID: FE6A9B5BD4EB5C94
14 changed files with 163 additions and 126 deletions

View File

@ -15,6 +15,8 @@ import {
import { I18n } from '@lingui/react';
import { Trans, t } from '@lingui/macro';
import { withNetwork } from '../../contexts/Network';
import CheckboxListItem from '../ListItem';
import DataListToolbar from '../DataListToolbar';
import SelectedList from '../SelectedList';
@ -67,7 +69,7 @@ class Lookup extends React.Component {
}
async getData () {
const { getItems } = this.props;
const { getItems, handleHttpError } = this.props;
const { page, page_size, sortedColumnKey, sortOrder } = this.state;
this.setState({ error: false });
@ -92,7 +94,7 @@ class Lookup extends React.Component {
this.setState(stateToUpdate);
} catch (err) {
this.setState({ error: true });
handleHttpError(err) || this.setState({ error: true });
}
}
@ -273,4 +275,4 @@ Lookup.defaultProps = {
name: null,
};
export default Lookup;
export default withNetwork(Lookup);

View File

@ -8,6 +8,8 @@ import { CubesIcon } from '@patternfly/react-icons';
import { I18n, i18nMark } from '@lingui/react';
import { Trans, t } from '@lingui/macro';
import { withNetwork } from '../../contexts/Network';
import DataListToolbar from '../DataListToolbar';
import NotificationListItem from './NotificationListItem';
import Pagination from '../Pagination';
@ -117,58 +119,71 @@ class Notifications extends Component {
}
async createError (id, isCurrentlyOn) {
const { onCreateError, match } = this.props;
const { onCreateError, match, handleHttpError } = this.props;
const postParams = { id };
let errorHandled;
if (isCurrentlyOn) {
postParams.disassociate = true;
}
try {
await onCreateError(match.params.id, postParams);
} catch (err) {
this.setState({ error: true });
errorHandled = handleHttpError(err);
if (!errorHandled) {
this.setState({ error: true });
}
} finally {
if (isCurrentlyOn) {
// Remove it from state
this.setState((prevState) => ({
errorTemplateIds: prevState.errorTemplateIds.filter((templateId) => templateId !== id)
}));
} else {
// Add it to state
this.setState(prevState => ({
errorTemplateIds: [...prevState.errorTemplateIds, id]
}));
if (!errorHandled) {
if (isCurrentlyOn) {
// Remove it from state
this.setState((prevState) => ({
errorTemplateIds: prevState.errorTemplateIds.filter((templateId) => templateId !== id)
}));
} else {
// Add it to state
this.setState(prevState => ({
errorTemplateIds: [...prevState.errorTemplateIds, id]
}));
}
}
}
}
async createSuccess (id, isCurrentlyOn) {
const { onCreateSuccess, match } = this.props;
const { onCreateSuccess, match, handleHttpError } = this.props;
const postParams = { id };
let errorHandled;
if (isCurrentlyOn) {
postParams.disassociate = true;
}
try {
await onCreateSuccess(match.params.id, postParams);
} catch (err) {
this.setState({ error: true });
errorHandled = handleHttpError(err);
if (!errorHandled) {
this.setState({ error: true });
}
} finally {
if (isCurrentlyOn) {
// Remove it from state
this.setState((prevState) => ({
successTemplateIds: prevState.successTemplateIds.filter((templateId) => templateId !== id)
}));
} else {
// Add it to state
this.setState(prevState => ({
successTemplateIds: [...prevState.successTemplateIds, id]
}));
if (!errorHandled) {
if (isCurrentlyOn) {
// Remove it from state
this.setState((prevState) => ({
successTemplateIds: prevState.successTemplateIds
.filter((templateId) => templateId !== id)
}));
} else {
// Add it to state
this.setState(prevState => ({
successTemplateIds: [...prevState.successTemplateIds, id]
}));
}
}
}
}
async readNotifications (queryParams) {
const { noInitialResults } = this.state;
const { onReadNotifications, onReadSuccess, onReadError, match } = this.props;
const { onReadNotifications, onReadSuccess, onReadError, match, handleHttpError } = this.props;
const { page, page_size, order_by } = queryParams;
let sortOrder = 'ascending';
@ -233,12 +248,11 @@ class Notifications extends Component {
}
this.setState({
successTemplateIds,
errorTemplateIds
errorTemplateIds,
loading: false
});
} catch (err) {
this.setState({ error: true });
} finally {
this.setState({ loading: false });
handleHttpError(err) || this.setState({ error: true, loading: false });
}
}
@ -329,4 +343,4 @@ Notifications.propTypes = {
onCreateSuccess: PropTypes.func.isRequired,
};
export default Notifications;
export default withNetwork(Notifications);

View File

@ -1,11 +1,15 @@
import React, { Component, Fragment } from 'react';
import { Route, Switch } from 'react-router-dom';
import { Route, withRouter, Switch } from 'react-router-dom';
import { i18nMark } from '@lingui/react';
import { NetworkProvider } from '../../contexts/Network';
import { withRootDialog } from '../../contexts/RootDialog';
import Breadcrumbs from '../../components/Breadcrumbs/Breadcrumbs';
import OrganizationsList from './screens/OrganizationsList';
import OrganizationAdd from './screens/OrganizationAdd';
import Organization from './screens/Organization/Organization';
import Breadcrumbs from '../../components/Breadcrumbs/Breadcrumbs';
class Organizations extends Component {
state = {
@ -13,7 +17,7 @@ class Organizations extends Component {
'/organizations': i18nMark('Organizations'),
'/organizations/add': i18nMark('Create New Organization')
}
}
};
setBreadcrumbConfig = (organization) => {
if (!organization) {
@ -35,7 +39,7 @@ class Organizations extends Component {
}
render () {
const { match, api, history, location } = this.props;
const { match, history, location, setRootDialogMessage } = this.props;
const { breadcrumbConfig } = this.state;
return (
@ -47,28 +51,34 @@ class Organizations extends Component {
<Route
path={`${match.path}/add`}
render={() => (
<OrganizationAdd
api={api}
/>
<OrganizationAdd />
)}
/>
<Route
path={`${match.path}/:id`}
render={() => (
<Organization
api={api}
history={history}
location={location}
setBreadcrumb={this.setBreadcrumbConfig}
/>
render={({ match: newRouteMatch }) => (
<NetworkProvider
handle404={() => {
history.replace('/organizations');
setRootDialogMessage({
title: '404',
bodyText: `Cannot find organization with ID ${newRouteMatch.params.id}.`,
variant: 'warning'
});
}}
>
<Organization
history={history}
location={location}
setBreadcrumb={this.setBreadcrumbConfig}
/>
</NetworkProvider>
)}
/>
<Route
path={`${match.path}`}
render={() => (
<OrganizationsList
api={api}
/>
<OrganizationsList />
)}
/>
</Switch>
@ -77,4 +87,4 @@ class Organizations extends Component {
}
}
export default Organizations;
export default withRootDialog(withRouter(Organizations));

View File

@ -7,6 +7,8 @@ import { t } from '@lingui/macro';
import Lookup from '../../../components/Lookup';
import { withNetwork } from '../../../contexts/Network';
const INSTANCE_GROUPS_LOOKUP_COLUMNS = [
{ name: i18nMark('Name'), key: 'name', isSortable: true },
{ name: i18nMark('Modified'), key: 'modified', isSortable: false, isNumeric: true },
@ -69,9 +71,6 @@ class InstanceGroupsLookup extends React.Component {
}
InstanceGroupsLookup.propTypes = {
api: PropTypes.shape({
getInstanceGroups: PropTypes.func,
}).isRequired,
value: PropTypes.arrayOf(PropTypes.object).isRequired,
tooltip: PropTypes.string,
onChange: PropTypes.func.isRequired,
@ -81,4 +80,4 @@ InstanceGroupsLookup.defaultProps = {
tooltip: '',
};
export default InstanceGroupsLookup;
export default withNetwork(InstanceGroupsLookup);

View File

@ -13,6 +13,8 @@ import {
Link
} from 'react-router-dom';
import { withNetwork } from '../../../contexts/Network';
import AlertModal from '../../../components/AlertModal';
import Pagination from '../../../components/Pagination';
import DataListToolbar from '../../../components/DataListToolbar';
@ -238,20 +240,16 @@ class OrganizationAccessList extends React.Component {
}
async removeAccessRole (roleId, resourceId, type) {
const { removeRole } = this.props;
const { removeRole, handleHttpError } = this.props;
const url = `/api/v2/${type}/${resourceId}/roles/`;
try {
await removeRole(url, roleId);
const queryParams = this.getQueryParams();
await this.fetchOrgAccessList(queryParams);
this.setState({ showWarning: false });
} catch (error) {
this.setState({ error });
handleHttpError(error) || this.setState({ error });
}
const queryParams = this.getQueryParams();
try {
this.fetchOrgAccessList(queryParams);
} catch (error) {
this.setState({ error });
}
this.setState({ showWarning: false });
}
handleWarning (roleName, roleId, resourceName, resourceId, type) {
@ -432,4 +430,4 @@ OrganizationAccessList.propTypes = {
removeRole: PropTypes.func.isRequired,
};
export default OrganizationAccessList;
export default withNetwork(OrganizationAccessList);

View File

@ -9,7 +9,8 @@ import {
FormGroup,
} from '@patternfly/react-core';
import { ConfigContext } from '../../../context';
import { Config } from '../../../contexts/Config';
import { withNetwork } from '../../../contexts/Network';
import FormRow from '../../../components/FormRow';
import FormField from '../../../components/FormField';
import FormActionGroup from '../../../components/FormActionGroup';
@ -81,7 +82,7 @@ class OrganizationForm extends Component {
}
render () {
const { api, organization, handleCancel } = this.props;
const { organization, handleCancel } = this.props;
const { instanceGroups, formIsValid, error } = this.state;
const defaultVenv = '/venv/ansible/';
@ -112,7 +113,7 @@ class OrganizationForm extends Component {
type="text"
label={i18n._(t`Description`)}
/>
<ConfigContext.Consumer>
<Config>
{({ custom_virtualenvs }) => (
custom_virtualenvs && custom_virtualenvs.length > 1 && (
<Field
@ -133,10 +134,9 @@ class OrganizationForm extends Component {
/>
)
)}
</ConfigContext.Consumer>
</Config>
</FormRow>
<InstanceGroupsLookup
api={api}
value={instanceGroups}
onChange={this.handleInstanceGroupsChange}
tooltip={i18n._(t`Select the Instance Groups for this Organization to run on.`)}
@ -157,7 +157,6 @@ class OrganizationForm extends Component {
}
OrganizationForm.propTypes = {
api: PropTypes.shape().isRequired,
organization: PropTypes.shape(),
handleSubmit: PropTypes.func.isRequired,
handleCancel: PropTypes.func.isRequired,
@ -175,4 +174,4 @@ OrganizationForm.contextTypes = {
custom_virtualenvs: PropTypes.arrayOf(PropTypes.string)
};
export default withRouter(OrganizationForm);
export default withNetwork(withRouter(OrganizationForm));

View File

@ -13,13 +13,16 @@ import {
PageSection
} from '@patternfly/react-core';
import { withNetwork } from '../../../../contexts/Network';
import Tabs from '../../../../components/Tabs/Tabs';
import Tab from '../../../../components/Tabs/Tab';
import OrganizationAccess from './OrganizationAccess';
import OrganizationDetail from './OrganizationDetail';
import OrganizationEdit from './OrganizationEdit';
import OrganizationNotifications from './OrganizationNotifications';
import OrganizationTeams from './OrganizationTeams';
import Tabs from '../../../../components/Tabs/Tabs';
import Tab from '../../../../components/Tabs/Tab';
class Organization extends Component {
constructor (props) {
@ -47,18 +50,18 @@ class Organization extends Component {
async fetchOrganization () {
const {
api,
match,
setBreadcrumb
setBreadcrumb,
api,
handleHttpError
} = this.props;
try {
const { data } = await api.getOrganizationDetails(+match.params.id);
this.setState({ organization: data });
const { data } = await api.getOrganizationDetails(parseInt(match.params.id, 10));
setBreadcrumb(data);
this.setState({ organization: data, loading: false });
} catch (error) {
this.setState({ error: true });
} finally {
this.setState({ loading: false });
handleHttpError(error) || this.setState({ error: true, loading: false });
}
}
@ -66,7 +69,6 @@ class Organization extends Component {
const {
location,
match,
api,
history
} = this.props;
@ -125,7 +127,6 @@ class Organization extends Component {
path="/organizations/:id/edit"
render={() => (
<OrganizationEdit
api={api}
match={match}
organization={organization}
/>
@ -137,7 +138,6 @@ class Organization extends Component {
path="/organizations/:id/details"
render={() => (
<OrganizationDetail
api={api}
match={match}
organization={organization}
/>
@ -148,7 +148,6 @@ class Organization extends Component {
path="/organizations/:id/access"
render={() => (
<OrganizationAccess
api={api}
match={match}
location={location}
history={history}
@ -160,7 +159,9 @@ class Organization extends Component {
render={() => (
<OrganizationTeams
id={Number(match.params.id)}
api={api}
match={match}
location={location}
history={history}
/>
)}
/>
@ -168,7 +169,6 @@ class Organization extends Component {
path="/organizations/:id/notifications"
render={() => (
<OrganizationNotifications
api={api}
match={match}
location={location}
history={history}
@ -184,4 +184,4 @@ class Organization extends Component {
}
}
export default withRouter(Organization);
export default withNetwork(withRouter(Organization));

View File

@ -1,4 +1,7 @@
import React from 'react';
import { withNetwork } from '../../../../contexts/Network';
import OrganizationAccessList from '../../components/OrganizationAccessList';
class OrganizationAccess extends React.Component {
@ -38,4 +41,4 @@ class OrganizationAccess extends React.Component {
}
}
export default OrganizationAccess;
export default withNetwork(OrganizationAccess);

View File

@ -2,6 +2,7 @@ import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { I18n } from '@lingui/react';
import { Trans, t } from '@lingui/macro';
import {
CardBody,
Button,
@ -9,6 +10,9 @@ import {
TextContent,
TextVariants,
} from '@patternfly/react-core';
import { withNetwork } from '../../../../contexts/Network';
import BasicChip from '../../../../components/BasicChip/BasicChip';
const detailWrapperStyle = {
@ -68,6 +72,7 @@ class OrganizationDetail extends Component {
async loadInstanceGroups () {
const {
api,
handleHttpError,
match
} = this.props;
try {
@ -78,7 +83,7 @@ class OrganizationDetail extends Component {
instanceGroups: [...data.results]
});
} catch (err) {
this.setState({ error: true });
handleHttpError(err) || this.setState({ error: true });
}
}
@ -173,4 +178,4 @@ class OrganizationDetail extends Component {
}
}
export default OrganizationDetail;
export default withNetwork(OrganizationDetail);

View File

@ -2,8 +2,8 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { withRouter } from 'react-router-dom';
import { CardBody } from '@patternfly/react-core';
import OrganizationForm from '../../components/OrganizationForm';
import { withNetwork } from '../../../../contexts/Network';
class OrganizationEdit extends Component {
constructor (props) {
@ -20,14 +20,13 @@ class OrganizationEdit extends Component {
}
async handleSubmit (values, groupsToAssociate, groupsToDisassociate) {
const { api, organization } = this.props;
const { api, organization, handleHttpError } = this.props;
try {
await api.updateOrganizationDetails(organization.id, values);
await this.submitInstanceGroups(groupsToAssociate, groupsToDisassociate);
} catch (err) {
this.setState({ error: err });
} finally {
this.handleSuccess();
} catch (err) {
handleHttpError(err) || this.setState({ error: err });
}
}
@ -42,7 +41,7 @@ class OrganizationEdit extends Component {
}
async submitInstanceGroups (groupsToAssociate, groupsToDisassociate) {
const { api, organization } = this.props;
const { api, organization, handleHttpError } = this.props;
const url = organization.related.instance_groups;
try {
@ -53,18 +52,17 @@ class OrganizationEdit extends Component {
await api.disassociate(url, id);
}));
} catch (err) {
this.setState({ error: err });
handleHttpError(err) || this.setState({ error: err });
}
}
render () {
const { api, organization } = this.props;
const { organization } = this.props;
const { error } = this.state;
return (
<CardBody>
<OrganizationForm
api={api}
organization={organization}
handleSubmit={this.handleSubmit}
handleCancel={this.handleCancel}
@ -76,7 +74,6 @@ class OrganizationEdit extends Component {
}
OrganizationEdit.propTypes = {
api: PropTypes.shape().isRequired,
organization: PropTypes.shape().isRequired,
};
@ -85,4 +82,4 @@ OrganizationEdit.contextTypes = {
};
export { OrganizationEdit as _OrganizationEdit };
export default withRouter(OrganizationEdit);
export default withNetwork(withRouter(OrganizationEdit));

View File

@ -1,5 +1,7 @@
import React, { Component } from 'react';
import { withNetwork } from '../../../../contexts/Network';
import NotificationsList from '../../../../components/NotificationsList/Notifications.list';
class OrganizationNotifications extends Component {
@ -60,4 +62,4 @@ class OrganizationNotifications extends Component {
}
}
export default OrganizationNotifications;
export default withNetwork(OrganizationNotifications);

View File

@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import { withRouter } from 'react-router-dom';
import OrganizationTeamsList from '../../components/OrganizationTeamsList';
import { parseQueryString } from '../../../../qs';
import { withNetwork } from '../../../../contexts/Network';
const DEFAULT_QUERY_PARAMS = {
page: 1,
@ -47,7 +48,7 @@ class OrganizationTeams extends React.Component {
}
async readOrganizationTeamsList () {
const { api, id } = this.props;
const { api, handleHttpError, id } = this.props;
const params = this.getQueryParams();
this.setState({ isLoading: true });
try {
@ -61,7 +62,7 @@ class OrganizationTeams extends React.Component {
isInitialized: true,
});
} catch (error) {
this.setState({
handleHttpError(error) && this.setState({
error,
isLoading: false,
isInitialized: true,
@ -94,9 +95,8 @@ class OrganizationTeams extends React.Component {
}
OrganizationTeams.propTypes = {
id: PropTypes.number.isRequired,
api: PropTypes.shape().isRequired,
id: PropTypes.number.isRequired
};
export { OrganizationTeams as _OrganizationTeams };
export default withRouter(OrganizationTeams);
export default withNetwork(withRouter(OrganizationTeams));

View File

@ -13,6 +13,8 @@ import {
} from '@patternfly/react-core';
import { TimesIcon } from '@patternfly/react-icons';
import { withNetwork } from '../../../contexts/Network';
import OrganizationForm from '../components/OrganizationForm';
class OrganizationAdd extends React.Component {
@ -29,7 +31,7 @@ class OrganizationAdd extends React.Component {
}
async handleSubmit (values, groupsToAssociate) {
const { api } = this.props;
const { api, handleHttpError } = this.props;
try {
const { data: response } = await api.createOrganization(values);
const instanceGroupsUrl = response.related.instance_groups;
@ -37,10 +39,9 @@ class OrganizationAdd extends React.Component {
await Promise.all(groupsToAssociate.map(async id => {
await api.associateInstanceGroup(instanceGroupsUrl, id);
}));
} catch (err) {
this.setState({ error: err });
} finally {
this.handleSuccess(response.id);
} catch (err) {
handleHttpError(err) || this.setState({ error: err });
}
} catch (err) {
this.setState({ error: err });
@ -58,7 +59,6 @@ class OrganizationAdd extends React.Component {
}
render () {
const { api } = this.props;
const { error } = this.state;
return (
@ -82,7 +82,6 @@ class OrganizationAdd extends React.Component {
</CardHeader>
<CardBody>
<OrganizationForm
api={api}
handleSubmit={this.handleSubmit}
handleCancel={this.handleCancel}
/>
@ -105,4 +104,4 @@ OrganizationAdd.contextTypes = {
};
export { OrganizationAdd as _OrganizationAdd };
export default withRouter(OrganizationAdd);
export default withNetwork(withRouter(OrganizationAdd));

View File

@ -21,6 +21,9 @@ import {
} from '@patternfly/react-core';
import { CubesIcon } from '@patternfly/react-icons';
import { withNetwork } from '../../../contexts/Network';
import DataListToolbar from '../../../components/DataListToolbar';
import OrganizationListItem from '../components/OrganizationListItem';
import Pagination from '../../../components/Pagination';
@ -170,14 +173,21 @@ class OrganizationsList extends Component {
}
async handleOrgDelete (event) {
const { orgsToDelete } = this.state;
const { orgsToDelete, handleHttpError } = this.state;
const { api } = this.props;
let errorHandled;
orgsToDelete.forEach(async (org) => {
await api.destroyOrganization(org.id);
try {
await Promise.all(orgsToDelete.map(async (org) => api.destroyOrganization(org.id)));
this.handleClearOrgsToDelete();
} catch (err) {
errorHandled = handleHttpError(err);
} finally {
if (!errorHandled) {
const queryParams = this.getQueryParams();
this.fetchOrganizations(queryParams);
});
}
}
event.preventDefault();
}
@ -192,7 +202,7 @@ class OrganizationsList extends Component {
}
async fetchOrganizations (queryParams) {
const { api } = this.props;
const { api, handleHttpError } = this.props;
const { page, page_size, order_by } = queryParams;
let sortOrder = 'ascending';
@ -220,6 +230,7 @@ class OrganizationsList extends Component {
sortedColumnKey,
results,
selected: [],
loading: false
};
// This is in place to track whether or not the initial request
@ -233,9 +244,7 @@ class OrganizationsList extends Component {
this.setState(stateToUpdate);
this.updateUrl(queryParams);
} catch (err) {
this.setState({ error: true });
} finally {
this.setState({ loading: false });
handleHttpError(err) || this.setState({ error: true, loading: false });
}
}
@ -271,8 +280,8 @@ class OrganizationsList extends Component {
isOpen={isModalOpen}
onClose={this.handleClearOrgsToDelete}
actions={[
<Button variant="danger" aria-label="confirm-delete" onClick={this.handleOrgDelete}>Delete</Button>,
<Button variant="secondary" aria-label="cancel-delete" onClick={this.handleClearOrgsToDelete}>Cancel</Button>
<Button variant="danger" key="delete" aria-label="confirm-delete" onClick={this.handleOrgDelete}>Delete</Button>,
<Button variant="secondary" key="cancel" aria-label="cancel-delete" onClick={this.handleClearOrgsToDelete}>Cancel</Button>
]}
>
{warningMsg}
@ -350,4 +359,4 @@ class OrganizationsList extends Component {
}
}
export default withRouter(OrganizationsList);
export default withNetwork(withRouter(OrganizationsList));