improves isLoading state and removes unnecessary RBAC

This commit is contained in:
Alex Corey
2020-04-30 17:25:14 -04:00
parent 77fd2d677a
commit 1ea924aa13
6 changed files with 32 additions and 145 deletions

View File

@@ -6,15 +6,10 @@ class InventorySources extends LaunchUpdateMixin(Base) {
super(http); super(http);
this.baseUrl = '/api/v2/inventory_sources/'; this.baseUrl = '/api/v2/inventory_sources/';
this.allowSyncStart = this.allowSyncStart.bind(this); this.createSyncStart = this.createSyncStart.bind(this);
this.startSyncSource = this.startSyncSource.bind(this);
} }
allowSyncStart(sourceId) { createSyncStart(sourceId, extraVars) {
return this.http.get(`${this.baseUrl}${sourceId}/update/`);
}
startSyncSource(sourceId, extraVars) {
return this.http.post(`${this.baseUrl}${sourceId}/update/`, { return this.http.post(`${this.baseUrl}${sourceId}/update/`, {
extra_vars: extraVars, extra_vars: extraVars,
}); });

View File

@@ -5,15 +5,10 @@ class InventoryUpdates extends LaunchUpdateMixin(Base) {
constructor(http) { constructor(http) {
super(http); super(http);
this.baseUrl = '/api/v2/inventory_updates/'; this.baseUrl = '/api/v2/inventory_updates/';
this.allowSyncCancel = this.allowSyncCancel.bind(this); this.createSyncCancel = this.createSyncCancel.bind(this);
this.cancelSyncSource = this.cancelSyncSource.bind(this);
} }
allowSyncCancel(sourceId) { createSyncCancel(sourceId) {
return this.http.get(`${this.baseUrl}${sourceId}/cancel/`);
}
cancelSyncSource(sourceId) {
return this.http.post(`${this.baseUrl}${sourceId}/cancel/`); return this.http.post(`${this.baseUrl}${sourceId}/cancel/`);
} }
} }

View File

@@ -25,10 +25,7 @@ function InventorySourceListItem({
detailUrl, detailUrl,
label, label,
}) { }) {
const [isCancelSyncLoading, setIsCancelSyncLoading] = useState(false); const [isSyncLoading, setIsSyncLoading] = useState(false);
const [isStartSyncLoading, setIsStartSyncLoading] = useState(false);
const isDisabled = isCancelSyncLoading || isStartSyncLoading;
const generateLastJobTooltip = job => { const generateLastJobTooltip = job => {
return ( return (
@@ -53,7 +50,7 @@ function InventorySourceListItem({
<DataListItem aria-labelledby={`check-action-${source.id}`}> <DataListItem aria-labelledby={`check-action-${source.id}`}>
<DataListItemRow> <DataListItemRow>
<DataListCheck <DataListCheck
isDisabled={isDisabled} isDisabled={isSyncLoading}
id={`select-source-${source.id}`} id={`select-source-${source.id}`}
checked={isSelected} checked={isSelected}
onChange={onSelect} onChange={onSelect}
@@ -99,12 +96,9 @@ function InventorySourceListItem({
> >
{source.summary_fields.user_capabilities.start && ( {source.summary_fields.user_capabilities.start && (
<InventorySourceSyncButton <InventorySourceSyncButton
onCancelSyncLoading={isLoading => onSyncLoading={isLoading => {
setIsCancelSyncLoading(isLoading) setIsSyncLoading(isLoading);
} }}
onStartSyncLoading={isLoading =>
setIsStartSyncLoading(isLoading)
}
source={source} source={source}
/> />
)} )}
@@ -113,7 +107,7 @@ function InventorySourceListItem({
aria-label={i18n._(t`Edit Source`)} aria-label={i18n._(t`Edit Source`)}
variant="plain" variant="plain"
component={Link} component={Link}
isDisabled={isDisabled} isDisabled={isSyncLoading}
to={`${detailUrl}/edit`} to={`${detailUrl}/edit`}
> >
<PencilAltIcon /> <PencilAltIcon />

View File

@@ -106,6 +106,7 @@ describe('<InventorySourceListItem />', () => {
); );
expect(wrapper.find('StatusIcon').length).toBe(0); expect(wrapper.find('StatusIcon').length).toBe(0);
}); });
test('should not render sync buttons', async () => { test('should not render sync buttons', async () => {
const onSelect = jest.fn(); const onSelect = jest.fn();
wrapper = mountWithContexts( wrapper = mountWithContexts(

View File

@@ -9,12 +9,7 @@ import AlertModal from '@components/AlertModal/AlertModal';
import ErrorDetail from '@components/ErrorDetail/ErrorDetail'; import ErrorDetail from '@components/ErrorDetail/ErrorDetail';
import { InventoryUpdatesAPI, InventorySourcesAPI } from '@api'; import { InventoryUpdatesAPI, InventorySourcesAPI } from '@api';
function InventorySourceSyncButton({ function InventorySourceSyncButton({ onSyncLoading, source, i18n }) {
onCancelSyncLoading,
onStartSyncLoading,
source,
i18n,
}) {
const [updateStatus, setUpdateStatus] = useState(source.status); const [updateStatus, setUpdateStatus] = useState(source.status);
const { const {
@@ -23,25 +18,14 @@ function InventorySourceSyncButton({
request: startSyncProcess, request: startSyncProcess,
} = useRequest( } = useRequest(
useCallback(async () => { useCallback(async () => {
let syncStatus;
const { const {
data: { can_update }, data: { status },
} = await InventorySourcesAPI.allowSyncStart(source.id); } = await InventorySourcesAPI.createSyncStart(source.id);
if (can_update) {
syncStatus = await InventorySourcesAPI.startSyncSource(source.id);
} else {
throw new Error(
i18n._(
t`You do not have permission to start this inventory source sync`
)
);
}
setUpdateStatus(syncStatus.data.status); setUpdateStatus(status);
return syncStatus.data.status; return status;
}, [source.id, i18n]), }, [source.id]),
{} {}
); );
@@ -58,29 +42,15 @@ function InventorySourceSyncButton({
}, },
}, },
} = await InventorySourcesAPI.readDetail(source.id); } = await InventorySourcesAPI.readDetail(source.id);
const {
data: { can_cancel }, await InventoryUpdatesAPI.createSyncCancel(id);
} = await InventoryUpdatesAPI.allowSyncCancel(id); setUpdateStatus(null);
if (can_cancel) { }, [source.id])
await InventoryUpdatesAPI.cancelSyncSource(id);
setUpdateStatus(null);
} else {
throw new Error(
i18n._(
t`You do not have permission to cancel this inventory source sync`
)
);
}
}, [source.id, i18n])
); );
useEffect(() => onStartSyncLoading(startSyncLoading), [ useEffect(() => onSyncLoading(startSyncLoading || cancelSyncLoading), [
onStartSyncLoading, onSyncLoading,
startSyncLoading, startSyncLoading,
]);
useEffect(() => onCancelSyncLoading(cancelSyncLoading), [
onCancelSyncLoading,
cancelSyncLoading, cancelSyncLoading,
]); ]);
@@ -131,8 +101,7 @@ function InventorySourceSyncButton({
} }
InventorySourceSyncButton.propTypes = { InventorySourceSyncButton.propTypes = {
onCancelSyncLoading: PropTypes.func.isRequired, onSyncLoading: PropTypes.func.isRequired,
onStartSyncLoading: PropTypes.func.isRequired,
source: PropTypes.shape({}), source: PropTypes.shape({}),
}; };

View File

@@ -8,8 +8,7 @@ jest.mock('@api/models/InventoryUpdates');
jest.mock('@api/models/InventorySources'); jest.mock('@api/models/InventorySources');
const source = { id: 1, name: 'Foo', source: 'Source Bar' }; const source = { id: 1, name: 'Foo', source: 'Source Bar' };
const onCancelSyncLoading = jest.fn(); const onSyncLoading = jest.fn();
const onStartSyncLoading = jest.fn();
describe('<InventorySourceSyncButton />', () => { describe('<InventorySourceSyncButton />', () => {
let wrapper; let wrapper;
@@ -17,8 +16,7 @@ describe('<InventorySourceSyncButton />', () => {
wrapper = mountWithContexts( wrapper = mountWithContexts(
<InventorySourceSyncButton <InventorySourceSyncButton
source={source} source={source}
onCancelSyncLoading={onCancelSyncLoading} onSyncLoading={onSyncLoading}
onStartSyncLoading={onStartSyncLoading}
/> />
); );
}); });
@@ -41,26 +39,21 @@ describe('<InventorySourceSyncButton />', () => {
wrapper = mountWithContexts( wrapper = mountWithContexts(
<InventorySourceSyncButton <InventorySourceSyncButton
source={{ status: 'pending', ...source }} source={{ status: 'pending', ...source }}
onCancelSyncLoading={onCancelSyncLoading} onSyncLoading={onSyncLoading}
onStartSyncLoading={onStartSyncLoading}
/> />
); );
expect(wrapper.find('MinusCircleIcon').length).toBe(1); expect(wrapper.find('MinusCircleIcon').length).toBe(1);
}); });
test('should start sync properly', async () => { test('should start sync properly', async () => {
InventorySourcesAPI.allowSyncStart.mockResolvedValue({ InventorySourcesAPI.createSyncStart.mockResolvedValue({
data: { can_update: true },
});
InventorySourcesAPI.startSyncSource.mockResolvedValue({
data: { status: 'pending' }, data: { status: 'pending' },
}); });
await act(async () => await act(async () =>
wrapper.find('Button[aria-label="Start sync source"]').simulate('click') wrapper.find('Button[aria-label="Start sync source"]').simulate('click')
); );
expect(InventorySourcesAPI.allowSyncStart).toBeCalledWith(1); expect(InventorySourcesAPI.createSyncStart).toBeCalledWith(1);
expect(InventorySourcesAPI.startSyncSource).toBeCalledWith(1);
wrapper.update(); wrapper.update();
expect(wrapper.find('Button[aria-label="Cancel sync source"]').length).toBe( expect(wrapper.find('Button[aria-label="Cancel sync source"]').length).toBe(
1 1
@@ -70,18 +63,14 @@ describe('<InventorySourceSyncButton />', () => {
InventorySourcesAPI.readDetail.mockResolvedValue({ InventorySourcesAPI.readDetail.mockResolvedValue({
data: { summary_fields: { current_update: { id: 120 } } }, data: { summary_fields: { current_update: { id: 120 } } },
}); });
InventoryUpdatesAPI.allowSyncCancel.mockResolvedValue({ InventoryUpdatesAPI.createSyncCancel.mockResolvedValue({
data: { can_cancel: true },
});
InventoryUpdatesAPI.cancelSyncSource.mockResolvedValue({
data: { status: '' }, data: { status: '' },
}); });
wrapper = mountWithContexts( wrapper = mountWithContexts(
<InventorySourceSyncButton <InventorySourceSyncButton
source={{ status: 'pending', ...source }} source={{ status: 'pending', ...source }}
onCancelSyncLoading={onCancelSyncLoading} onSyncLoading={onSyncLoading}
onStartSyncLoading={onStartSyncLoading}
/> />
); );
expect(wrapper.find('Button[aria-label="Cancel sync source"]').length).toBe( expect(wrapper.find('Button[aria-label="Cancel sync source"]').length).toBe(
@@ -93,8 +82,7 @@ describe('<InventorySourceSyncButton />', () => {
); );
expect(InventorySourcesAPI.readDetail).toBeCalledWith(1); expect(InventorySourcesAPI.readDetail).toBeCalledWith(1);
expect(InventoryUpdatesAPI.allowSyncCancel).toBeCalledWith(120); expect(InventoryUpdatesAPI.createSyncCancel).toBeCalledWith(120);
expect(InventoryUpdatesAPI.cancelSyncSource).toBeCalledWith(120);
wrapper.update(); wrapper.update();
@@ -102,59 +90,4 @@ describe('<InventorySourceSyncButton />', () => {
1 1
); );
}); });
test('Should prevent user from starting sync', async () => {
InventorySourcesAPI.allowSyncStart.mockResolvedValue({
data: { can_update: false },
});
InventorySourcesAPI.startSyncSource.mockResolvedValue({
data: { status: 'pending' },
});
await act(async () =>
wrapper.find('Button[aria-label="Start sync source"]').simulate('click')
);
expect(InventorySourcesAPI.allowSyncStart).toBeCalledWith(1);
expect(InventorySourcesAPI.startSyncSource).not.toBeCalledWith();
wrapper.update();
expect(wrapper.find('AlertModal').length).toBe(1);
expect(wrapper.find('Button[aria-label="Start sync source"]').length).toBe(
1
);
});
test('should prevent user from canceling sync', async () => {
InventorySourcesAPI.readDetail.mockResolvedValue({
data: { summary_fields: { current_update: { id: 120 } } },
});
InventoryUpdatesAPI.allowSyncCancel.mockResolvedValue({
data: { can_cancel: false },
});
InventoryUpdatesAPI.cancelSyncSource.mockResolvedValue({
data: { status: '' },
});
wrapper = mountWithContexts(
<InventorySourceSyncButton
source={{ status: 'pending', ...source }}
onCancelSyncLoading={onCancelSyncLoading}
onStartSyncLoading={onStartSyncLoading}
/>
);
expect(wrapper.find('Button[aria-label="Cancel sync source"]').length).toBe(
1
);
await act(async () =>
wrapper.find('Button[aria-label="Cancel sync source"]').simulate('click')
);
expect(InventorySourcesAPI.readDetail).toBeCalledWith(1);
expect(InventoryUpdatesAPI.allowSyncCancel).toBeCalledWith(120);
expect(InventoryUpdatesAPI.cancelSyncSource).not.toBeCalledWith(120);
wrapper.update();
expect(wrapper.find('AlertModal').length).toBe(1);
expect(wrapper.find('Button[aria-label="Cancel sync source"]').length).toBe(
1
);
});
}); });