mirror of
https://github.com/ansible/awx.git
synced 2024-10-31 06:51:10 +03:00
Hook up delete on jobs list. Add more comprehensive error handling on delete in organization and template lists.
This commit is contained in:
parent
1d05c21af4
commit
5549dac17d
@ -1,49 +1,64 @@
|
|||||||
|
import AdHocCommands from './models/AdHocCommands';
|
||||||
import Config from './models/Config';
|
import Config from './models/Config';
|
||||||
import InstanceGroups from './models/InstanceGroups';
|
import InstanceGroups from './models/InstanceGroups';
|
||||||
import Inventories from './models/Inventories';
|
import Inventories from './models/Inventories';
|
||||||
|
import InventoryUpdates from './models/InventoryUpdates';
|
||||||
import JobTemplates from './models/JobTemplates';
|
import JobTemplates from './models/JobTemplates';
|
||||||
import Jobs from './models/Jobs';
|
import Jobs from './models/Jobs';
|
||||||
import Labels from './models/Labels';
|
import Labels from './models/Labels';
|
||||||
import Me from './models/Me';
|
import Me from './models/Me';
|
||||||
import Organizations from './models/Organizations';
|
import Organizations from './models/Organizations';
|
||||||
import Projects from './models/Projects';
|
import Projects from './models/Projects';
|
||||||
|
import ProjectUpdates from './models/ProjectUpdates';
|
||||||
import Root from './models/Root';
|
import Root from './models/Root';
|
||||||
|
import SystemJobs from './models/SystemJobs';
|
||||||
import Teams from './models/Teams';
|
import Teams from './models/Teams';
|
||||||
import UnifiedJobTemplates from './models/UnifiedJobTemplates';
|
import UnifiedJobTemplates from './models/UnifiedJobTemplates';
|
||||||
import UnifiedJobs from './models/UnifiedJobs';
|
import UnifiedJobs from './models/UnifiedJobs';
|
||||||
import Users from './models/Users';
|
import Users from './models/Users';
|
||||||
|
import WorkflowJobs from './models/WorkflowJobs';
|
||||||
import WorkflowJobTemplates from './models/WorkflowJobTemplates';
|
import WorkflowJobTemplates from './models/WorkflowJobTemplates';
|
||||||
|
|
||||||
|
const AdHocCommandsAPI = new AdHocCommands();
|
||||||
const ConfigAPI = new Config();
|
const ConfigAPI = new Config();
|
||||||
const InstanceGroupsAPI = new InstanceGroups();
|
const InstanceGroupsAPI = new InstanceGroups();
|
||||||
const InventoriesAPI = new Inventories();
|
const InventoriesAPI = new Inventories();
|
||||||
|
const InventoryUpdatesAPI = new InventoryUpdates();
|
||||||
const JobTemplatesAPI = new JobTemplates();
|
const JobTemplatesAPI = new JobTemplates();
|
||||||
const JobsAPI = new Jobs();
|
const JobsAPI = new Jobs();
|
||||||
const LabelsAPI = new Labels();
|
const LabelsAPI = new Labels();
|
||||||
const MeAPI = new Me();
|
const MeAPI = new Me();
|
||||||
const OrganizationsAPI = new Organizations();
|
const OrganizationsAPI = new Organizations();
|
||||||
const ProjectsAPI = new Projects();
|
const ProjectsAPI = new Projects();
|
||||||
|
const ProjectUpdatesAPI = new ProjectUpdates();
|
||||||
const RootAPI = new Root();
|
const RootAPI = new Root();
|
||||||
|
const SystemJobsAPI = new SystemJobs();
|
||||||
const TeamsAPI = new Teams();
|
const TeamsAPI = new Teams();
|
||||||
const UnifiedJobTemplatesAPI = new UnifiedJobTemplates();
|
const UnifiedJobTemplatesAPI = new UnifiedJobTemplates();
|
||||||
const UnifiedJobsAPI = new UnifiedJobs();
|
const UnifiedJobsAPI = new UnifiedJobs();
|
||||||
const UsersAPI = new Users();
|
const UsersAPI = new Users();
|
||||||
|
const WorkflowJobsAPI = new WorkflowJobs();
|
||||||
const WorkflowJobTemplatesAPI = new WorkflowJobTemplates();
|
const WorkflowJobTemplatesAPI = new WorkflowJobTemplates();
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
AdHocCommandsAPI,
|
||||||
ConfigAPI,
|
ConfigAPI,
|
||||||
InstanceGroupsAPI,
|
InstanceGroupsAPI,
|
||||||
InventoriesAPI,
|
InventoriesAPI,
|
||||||
|
InventoryUpdatesAPI,
|
||||||
JobTemplatesAPI,
|
JobTemplatesAPI,
|
||||||
JobsAPI,
|
JobsAPI,
|
||||||
LabelsAPI,
|
LabelsAPI,
|
||||||
MeAPI,
|
MeAPI,
|
||||||
OrganizationsAPI,
|
OrganizationsAPI,
|
||||||
ProjectsAPI,
|
ProjectsAPI,
|
||||||
|
ProjectUpdatesAPI,
|
||||||
RootAPI,
|
RootAPI,
|
||||||
|
SystemJobsAPI,
|
||||||
TeamsAPI,
|
TeamsAPI,
|
||||||
UnifiedJobTemplatesAPI,
|
UnifiedJobTemplatesAPI,
|
||||||
UnifiedJobsAPI,
|
UnifiedJobsAPI,
|
||||||
UsersAPI,
|
UsersAPI,
|
||||||
|
WorkflowJobsAPI,
|
||||||
WorkflowJobTemplatesAPI,
|
WorkflowJobTemplatesAPI,
|
||||||
};
|
};
|
||||||
|
10
awx/ui_next/src/api/models/AdHocCommands.js
Normal file
10
awx/ui_next/src/api/models/AdHocCommands.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import Base from '../Base';
|
||||||
|
|
||||||
|
class AdHocCommands extends Base {
|
||||||
|
constructor(http) {
|
||||||
|
super(http);
|
||||||
|
this.baseUrl = '/api/v2/ad_hoc_commands/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AdHocCommands;
|
10
awx/ui_next/src/api/models/InventoryUpdates.js
Normal file
10
awx/ui_next/src/api/models/InventoryUpdates.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import Base from '../Base';
|
||||||
|
|
||||||
|
class InventoryUpdates extends Base {
|
||||||
|
constructor(http) {
|
||||||
|
super(http);
|
||||||
|
this.baseUrl = '/api/v2/inventory_updates/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InventoryUpdates;
|
10
awx/ui_next/src/api/models/ProjectUpdates.js
Normal file
10
awx/ui_next/src/api/models/ProjectUpdates.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import Base from '../Base';
|
||||||
|
|
||||||
|
class ProjectUpdates extends Base {
|
||||||
|
constructor(http) {
|
||||||
|
super(http);
|
||||||
|
this.baseUrl = '/api/v2/project_updates/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ProjectUpdates;
|
10
awx/ui_next/src/api/models/SystemJobs.js
Normal file
10
awx/ui_next/src/api/models/SystemJobs.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import Base from '../Base';
|
||||||
|
|
||||||
|
class SystemJobs extends Base {
|
||||||
|
constructor(http) {
|
||||||
|
super(http);
|
||||||
|
this.baseUrl = '/api/v2/system_jobs/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SystemJobs;
|
10
awx/ui_next/src/api/models/WorkflowJobs.js
Normal file
10
awx/ui_next/src/api/models/WorkflowJobs.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import Base from '../Base';
|
||||||
|
|
||||||
|
class WorkflowJobs extends Base {
|
||||||
|
constructor(http) {
|
||||||
|
super(http);
|
||||||
|
this.baseUrl = '/api/v2/workflow_jobs/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default WorkflowJobs;
|
@ -4,9 +4,18 @@ import { withI18n } from '@lingui/react';
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { Card, PageSection, PageSectionVariants } from '@patternfly/react-core';
|
import { Card, PageSection, PageSectionVariants } from '@patternfly/react-core';
|
||||||
|
|
||||||
import { UnifiedJobsAPI } from '@api';
|
import {
|
||||||
|
AdHocCommandsAPI,
|
||||||
|
InventoryUpdatesAPI,
|
||||||
|
JobsAPI,
|
||||||
|
ProjectUpdatesAPI,
|
||||||
|
SystemJobsAPI,
|
||||||
|
UnifiedJobsAPI,
|
||||||
|
WorkflowJobsAPI,
|
||||||
|
} from '@api';
|
||||||
import AlertModal from '@components/AlertModal';
|
import AlertModal from '@components/AlertModal';
|
||||||
import DatalistToolbar from '@components/DataListToolbar';
|
import DatalistToolbar from '@components/DataListToolbar';
|
||||||
|
import ErrorDetail from '@components/ErrorDetail';
|
||||||
import PaginatedDataList, {
|
import PaginatedDataList, {
|
||||||
ToolbarDeleteButton,
|
ToolbarDeleteButton,
|
||||||
} from '@components/PaginatedDataList';
|
} from '@components/PaginatedDataList';
|
||||||
@ -27,8 +36,8 @@ class JobList extends Component {
|
|||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
hasContentLoading: true,
|
hasContentLoading: true,
|
||||||
|
deletionError: null,
|
||||||
contentError: null,
|
contentError: null,
|
||||||
deletionError: false,
|
|
||||||
selected: [],
|
selected: [],
|
||||||
jobs: [],
|
jobs: [],
|
||||||
itemCount: 0,
|
itemCount: 0,
|
||||||
@ -36,7 +45,7 @@ class JobList extends Component {
|
|||||||
this.loadJobs = this.loadJobs.bind(this);
|
this.loadJobs = this.loadJobs.bind(this);
|
||||||
this.handleSelectAll = this.handleSelectAll.bind(this);
|
this.handleSelectAll = this.handleSelectAll.bind(this);
|
||||||
this.handleSelect = this.handleSelect.bind(this);
|
this.handleSelect = this.handleSelect.bind(this);
|
||||||
this.handleDelete = this.handleDelete.bind(this);
|
this.handleJobDelete = this.handleJobDelete.bind(this);
|
||||||
this.handleDeleteErrorClose = this.handleDeleteErrorClose.bind(this);
|
this.handleDeleteErrorClose = this.handleDeleteErrorClose.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,7 +61,7 @@ class JobList extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleDeleteErrorClose() {
|
handleDeleteErrorClose() {
|
||||||
this.setState({ deletionError: false });
|
this.setState({ deletionError: null });
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSelectAll(isSelected) {
|
handleSelectAll(isSelected) {
|
||||||
@ -70,13 +79,41 @@ class JobList extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleDelete() {
|
async handleJobDelete() {
|
||||||
const { selected } = this.state;
|
const { selected, itemCount } = this.state;
|
||||||
this.setState({ hasContentLoading: true, deletionError: false });
|
this.setState({ hasContentLoading: true });
|
||||||
try {
|
try {
|
||||||
await Promise.all(selected.map(({ id }) => UnifiedJobsAPI.destroy(id)));
|
await Promise.all(
|
||||||
|
selected.map(({ type, id }) => {
|
||||||
|
let deletePromise;
|
||||||
|
switch (type) {
|
||||||
|
case 'job':
|
||||||
|
deletePromise = JobsAPI.destroy(id);
|
||||||
|
break;
|
||||||
|
case 'ad_hoc_command':
|
||||||
|
deletePromise = AdHocCommandsAPI.destroy(id);
|
||||||
|
break;
|
||||||
|
case 'system_job':
|
||||||
|
deletePromise = SystemJobsAPI.destroy(id);
|
||||||
|
break;
|
||||||
|
case 'project_update':
|
||||||
|
deletePromise = ProjectUpdatesAPI.destroy(id);
|
||||||
|
break;
|
||||||
|
case 'inventory_update':
|
||||||
|
deletePromise = InventoryUpdatesAPI.destroy(id);
|
||||||
|
break;
|
||||||
|
case 'workflow_job':
|
||||||
|
deletePromise = WorkflowJobsAPI.destroy(id);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return deletePromise;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
this.setState({ itemCount: itemCount - selected.length });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.setState({ deletionError: true });
|
this.setState({ deletionError: err });
|
||||||
} finally {
|
} finally {
|
||||||
await this.loadJobs();
|
await this.loadJobs();
|
||||||
}
|
}
|
||||||
@ -150,7 +187,7 @@ class JobList extends Component {
|
|||||||
additionalControls={[
|
additionalControls={[
|
||||||
<ToolbarDeleteButton
|
<ToolbarDeleteButton
|
||||||
key="delete"
|
key="delete"
|
||||||
onDelete={this.handleDelete}
|
onDelete={this.handleJobDelete}
|
||||||
itemsToDelete={selected}
|
itemsToDelete={selected}
|
||||||
itemName={itemName}
|
itemName={itemName}
|
||||||
/>,
|
/>,
|
||||||
@ -176,6 +213,7 @@ class JobList extends Component {
|
|||||||
onClose={this.handleDeleteErrorClose}
|
onClose={this.handleDeleteErrorClose}
|
||||||
>
|
>
|
||||||
{i18n._(t`Failed to delete one or more jobs.`)}
|
{i18n._(t`Failed to delete one or more jobs.`)}
|
||||||
|
<ErrorDetail error={deletionError} />
|
||||||
</AlertModal>
|
</AlertModal>
|
||||||
</PageSection>
|
</PageSection>
|
||||||
);
|
);
|
||||||
|
@ -1,7 +1,15 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
|
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
|
||||||
|
|
||||||
import { UnifiedJobsAPI } from '@api';
|
import {
|
||||||
|
AdHocCommandsAPI,
|
||||||
|
InventoryUpdatesAPI,
|
||||||
|
JobsAPI,
|
||||||
|
ProjectUpdatesAPI,
|
||||||
|
SystemJobsAPI,
|
||||||
|
UnifiedJobsAPI,
|
||||||
|
WorkflowJobsAPI,
|
||||||
|
} from '@api';
|
||||||
import JobList from './JobList';
|
import JobList from './JobList';
|
||||||
|
|
||||||
jest.mock('@api');
|
jest.mock('@api');
|
||||||
@ -11,7 +19,7 @@ const mockResults = [
|
|||||||
id: 1,
|
id: 1,
|
||||||
url: '/api/v2/project_updates/1',
|
url: '/api/v2/project_updates/1',
|
||||||
name: 'job 1',
|
name: 'job 1',
|
||||||
type: 'project update',
|
type: 'project_update',
|
||||||
summary_fields: {
|
summary_fields: {
|
||||||
user_capabilities: {
|
user_capabilities: {
|
||||||
delete: true,
|
delete: true,
|
||||||
@ -31,9 +39,42 @@ const mockResults = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
url: '/api/v2/jobs/3',
|
url: '/api/v2/inventory_updates/3',
|
||||||
name: 'job 3',
|
name: 'job 3',
|
||||||
type: 'job',
|
type: 'inventory_update',
|
||||||
|
summary_fields: {
|
||||||
|
user_capabilities: {
|
||||||
|
delete: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
url: '/api/v2/workflow_jobs/4',
|
||||||
|
name: 'job 4',
|
||||||
|
type: 'workflow_job',
|
||||||
|
summary_fields: {
|
||||||
|
user_capabilities: {
|
||||||
|
delete: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
url: '/api/v2/system_jobs/5',
|
||||||
|
name: 'job 5',
|
||||||
|
type: 'system_job',
|
||||||
|
summary_fields: {
|
||||||
|
user_capabilities: {
|
||||||
|
delete: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 6,
|
||||||
|
url: '/api/v2/ad_hoc_commands/6',
|
||||||
|
name: 'job 6',
|
||||||
|
type: 'ad_hoc_command',
|
||||||
summary_fields: {
|
summary_fields: {
|
||||||
user_capabilities: {
|
user_capabilities: {
|
||||||
delete: true,
|
delete: true,
|
||||||
@ -52,7 +93,7 @@ describe('<JobList />', () => {
|
|||||||
await waitForElement(
|
await waitForElement(
|
||||||
wrapper,
|
wrapper,
|
||||||
'JobList',
|
'JobList',
|
||||||
el => el.state('jobs').length === 3
|
el => el.state('jobs').length === 6
|
||||||
);
|
);
|
||||||
|
|
||||||
done();
|
done();
|
||||||
@ -61,7 +102,7 @@ describe('<JobList />', () => {
|
|||||||
test('select makes expected state updates', async done => {
|
test('select makes expected state updates', async done => {
|
||||||
const [mockItem] = mockResults;
|
const [mockItem] = mockResults;
|
||||||
const wrapper = mountWithContexts(<JobList />);
|
const wrapper = mountWithContexts(<JobList />);
|
||||||
await waitForElement(wrapper, 'JobListItem', el => el.length === 3);
|
await waitForElement(wrapper, 'JobListItem', el => el.length === 6);
|
||||||
|
|
||||||
wrapper
|
wrapper
|
||||||
.find('JobListItem')
|
.find('JobListItem')
|
||||||
@ -79,20 +120,59 @@ describe('<JobList />', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('select-all-delete makes expected state updates and api calls', async done => {
|
test('select-all-delete makes expected state updates and api calls', async done => {
|
||||||
|
AdHocCommandsAPI.destroy = jest.fn();
|
||||||
|
InventoryUpdatesAPI.destroy = jest.fn();
|
||||||
|
JobsAPI.destroy = jest.fn();
|
||||||
|
ProjectUpdatesAPI.destroy = jest.fn();
|
||||||
|
SystemJobsAPI.destroy = jest.fn();
|
||||||
|
WorkflowJobsAPI.destroy = jest.fn();
|
||||||
const wrapper = mountWithContexts(<JobList />);
|
const wrapper = mountWithContexts(<JobList />);
|
||||||
await waitForElement(wrapper, 'JobListItem', el => el.length === 3);
|
await waitForElement(wrapper, 'JobListItem', el => el.length === 6);
|
||||||
|
|
||||||
wrapper.find('DataListToolbar').prop('onSelectAll')(true);
|
wrapper.find('DataListToolbar').prop('onSelectAll')(true);
|
||||||
expect(wrapper.find('JobList').state('selected').length).toEqual(3);
|
expect(wrapper.find('JobList').state('selected').length).toEqual(6);
|
||||||
|
|
||||||
wrapper.find('DataListToolbar').prop('onSelectAll')(false);
|
wrapper.find('DataListToolbar').prop('onSelectAll')(false);
|
||||||
expect(wrapper.find('JobList').state('selected').length).toEqual(0);
|
expect(wrapper.find('JobList').state('selected').length).toEqual(0);
|
||||||
|
|
||||||
wrapper.find('DataListToolbar').prop('onSelectAll')(true);
|
wrapper.find('DataListToolbar').prop('onSelectAll')(true);
|
||||||
expect(wrapper.find('JobList').state('selected').length).toEqual(3);
|
expect(wrapper.find('JobList').state('selected').length).toEqual(6);
|
||||||
|
|
||||||
wrapper.find('ToolbarDeleteButton').prop('onDelete')();
|
wrapper.find('ToolbarDeleteButton').prop('onDelete')();
|
||||||
expect(UnifiedJobsAPI.destroy).toHaveBeenCalledTimes(3);
|
expect(AdHocCommandsAPI.destroy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(InventoryUpdatesAPI.destroy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(JobsAPI.destroy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(ProjectUpdatesAPI.destroy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(SystemJobsAPI.destroy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(WorkflowJobsAPI.destroy).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('error is shown when job not successfully deleted from api', async done => {
|
||||||
|
JobsAPI.destroy.mockRejectedValue(
|
||||||
|
new Error({
|
||||||
|
response: {
|
||||||
|
config: {
|
||||||
|
method: 'delete',
|
||||||
|
url: '/api/v2/jobs/2',
|
||||||
|
},
|
||||||
|
data: 'An error occurred',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
const wrapper = mountWithContexts(<JobList />);
|
||||||
|
wrapper.find('JobList').setState({
|
||||||
|
jobs: mockResults,
|
||||||
|
itemCount: 6,
|
||||||
|
selected: mockResults.slice(1, 2),
|
||||||
|
});
|
||||||
|
wrapper.find('ToolbarDeleteButton').prop('onDelete')();
|
||||||
|
await waitForElement(
|
||||||
|
wrapper,
|
||||||
|
'Modal',
|
||||||
|
el => el.props().isOpen === true && el.props().title === 'Error!'
|
||||||
|
);
|
||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -7,6 +7,7 @@ import { Card, PageSection, PageSectionVariants } from '@patternfly/react-core';
|
|||||||
import { OrganizationsAPI } from '@api';
|
import { OrganizationsAPI } from '@api';
|
||||||
import AlertModal from '@components/AlertModal';
|
import AlertModal from '@components/AlertModal';
|
||||||
import DataListToolbar from '@components/DataListToolbar';
|
import DataListToolbar from '@components/DataListToolbar';
|
||||||
|
import ErrorDetail from '@components/ErrorDetail';
|
||||||
import PaginatedDataList, {
|
import PaginatedDataList, {
|
||||||
ToolbarAddButton,
|
ToolbarAddButton,
|
||||||
ToolbarDeleteButton,
|
ToolbarDeleteButton,
|
||||||
@ -28,7 +29,7 @@ class OrganizationsList extends Component {
|
|||||||
this.state = {
|
this.state = {
|
||||||
hasContentLoading: true,
|
hasContentLoading: true,
|
||||||
contentError: null,
|
contentError: null,
|
||||||
hasDeletionError: false,
|
deletionError: null,
|
||||||
organizations: [],
|
organizations: [],
|
||||||
selected: [],
|
selected: [],
|
||||||
itemCount: 0,
|
itemCount: 0,
|
||||||
@ -71,17 +72,17 @@ class OrganizationsList extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleDeleteErrorClose() {
|
handleDeleteErrorClose() {
|
||||||
this.setState({ hasDeletionError: false });
|
this.setState({ deletionError: null });
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleOrgDelete() {
|
async handleOrgDelete() {
|
||||||
const { selected } = this.state;
|
const { selected } = this.state;
|
||||||
|
|
||||||
this.setState({ hasContentLoading: true, hasDeletionError: false });
|
this.setState({ hasContentLoading: true });
|
||||||
try {
|
try {
|
||||||
await Promise.all(selected.map(org => OrganizationsAPI.destroy(org.id)));
|
await Promise.all(selected.map(org => OrganizationsAPI.destroy(org.id)));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.setState({ hasDeletionError: true });
|
this.setState({ deletionError: err });
|
||||||
} finally {
|
} finally {
|
||||||
await this.loadOrganizations();
|
await this.loadOrganizations();
|
||||||
}
|
}
|
||||||
@ -134,7 +135,7 @@ class OrganizationsList extends Component {
|
|||||||
itemCount,
|
itemCount,
|
||||||
contentError,
|
contentError,
|
||||||
hasContentLoading,
|
hasContentLoading,
|
||||||
hasDeletionError,
|
deletionError,
|
||||||
selected,
|
selected,
|
||||||
organizations,
|
organizations,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
@ -212,12 +213,13 @@ class OrganizationsList extends Component {
|
|||||||
</Card>
|
</Card>
|
||||||
</PageSection>
|
</PageSection>
|
||||||
<AlertModal
|
<AlertModal
|
||||||
isOpen={hasDeletionError}
|
isOpen={deletionError}
|
||||||
variant="danger"
|
variant="danger"
|
||||||
title={i18n._(t`Error!`)}
|
title={i18n._(t`Error!`)}
|
||||||
onClose={this.handleDeleteErrorClose}
|
onClose={this.handleDeleteErrorClose}
|
||||||
>
|
>
|
||||||
{i18n._(t`Failed to delete one or more organizations.`)}
|
{i18n._(t`Failed to delete one or more organizations.`)}
|
||||||
|
<ErrorDetail error={deletionError} />
|
||||||
</AlertModal>
|
</AlertModal>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
|
@ -15,6 +15,7 @@ import {
|
|||||||
import { JobTemplatesAPI, WorkflowJobTemplatesAPI } from '@api';
|
import { JobTemplatesAPI, WorkflowJobTemplatesAPI } from '@api';
|
||||||
import AlertModal from '@components/AlertModal';
|
import AlertModal from '@components/AlertModal';
|
||||||
import DatalistToolbar from '@components/DataListToolbar';
|
import DatalistToolbar from '@components/DataListToolbar';
|
||||||
|
import ErrorDetail from '@components/ErrorDetail';
|
||||||
import PaginatedDataList, {
|
import PaginatedDataList, {
|
||||||
ToolbarDeleteButton,
|
ToolbarDeleteButton,
|
||||||
ToolbarAddButton,
|
ToolbarAddButton,
|
||||||
@ -39,7 +40,7 @@ class TemplatesList extends Component {
|
|||||||
this.state = {
|
this.state = {
|
||||||
hasContentLoading: true,
|
hasContentLoading: true,
|
||||||
contentError: null,
|
contentError: null,
|
||||||
hasDeletionError: false,
|
deletionError: null,
|
||||||
selected: [],
|
selected: [],
|
||||||
templates: [],
|
templates: [],
|
||||||
itemCount: 0,
|
itemCount: 0,
|
||||||
@ -66,7 +67,7 @@ class TemplatesList extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleDeleteErrorClose() {
|
handleDeleteErrorClose() {
|
||||||
this.setState({ hasDeletionError: false });
|
this.setState({ deletionError: null });
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSelectAll(isSelected) {
|
handleSelectAll(isSelected) {
|
||||||
@ -92,7 +93,7 @@ class TemplatesList extends Component {
|
|||||||
async handleTemplateDelete() {
|
async handleTemplateDelete() {
|
||||||
const { selected, itemCount } = this.state;
|
const { selected, itemCount } = this.state;
|
||||||
|
|
||||||
this.setState({ hasContentLoading: true, hasDeletionError: false });
|
this.setState({ hasContentLoading: true });
|
||||||
try {
|
try {
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
selected.map(({ type, id }) => {
|
selected.map(({ type, id }) => {
|
||||||
@ -107,7 +108,7 @@ class TemplatesList extends Component {
|
|||||||
);
|
);
|
||||||
this.setState({ itemCount: itemCount - selected.length });
|
this.setState({ itemCount: itemCount - selected.length });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.setState({ hasDeletionError: true });
|
this.setState({ deletionError: err });
|
||||||
} finally {
|
} finally {
|
||||||
await this.loadTemplates();
|
await this.loadTemplates();
|
||||||
}
|
}
|
||||||
@ -159,7 +160,7 @@ class TemplatesList extends Component {
|
|||||||
const {
|
const {
|
||||||
contentError,
|
contentError,
|
||||||
hasContentLoading,
|
hasContentLoading,
|
||||||
hasDeletionError,
|
deletionError,
|
||||||
templates,
|
templates,
|
||||||
itemCount,
|
itemCount,
|
||||||
selected,
|
selected,
|
||||||
@ -287,12 +288,13 @@ class TemplatesList extends Component {
|
|||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
<AlertModal
|
<AlertModal
|
||||||
isOpen={hasDeletionError}
|
isOpen={deletionError}
|
||||||
variant="danger"
|
variant="danger"
|
||||||
title={i18n._(t`Error!`)}
|
title={i18n._(t`Error!`)}
|
||||||
onClose={this.handleDeleteErrorClose}
|
onClose={this.handleDeleteErrorClose}
|
||||||
>
|
>
|
||||||
{i18n._(t`Failed to delete one or more template.`)}
|
{i18n._(t`Failed to delete one or more template.`)}
|
||||||
|
<ErrorDetail error={deletionError} />
|
||||||
</AlertModal>
|
</AlertModal>
|
||||||
</PageSection>
|
</PageSection>
|
||||||
);
|
);
|
||||||
|
@ -199,7 +199,7 @@ describe('<TemplatesList />', () => {
|
|||||||
expect(WorkflowJobTemplatesAPI.destroy).toHaveBeenCalledTimes(1);
|
expect(WorkflowJobTemplatesAPI.destroy).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('error is shown when template not successfully deleted from api', async () => {
|
test('error is shown when template not successfully deleted from api', async done => {
|
||||||
JobTemplatesAPI.destroy.mockRejectedValue(
|
JobTemplatesAPI.destroy.mockRejectedValue(
|
||||||
new Error({
|
new Error({
|
||||||
response: {
|
response: {
|
||||||
@ -225,5 +225,7 @@ describe('<TemplatesList />', () => {
|
|||||||
'Modal',
|
'Modal',
|
||||||
el => el.props().isOpen === true && el.props().title === 'Error!'
|
el => el.props().isOpen === true && el.props().title === 'Error!'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user