katello 3.15.0.rc1.3 → 3.15.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of katello might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/app/controllers/katello/api/v2/api_controller.rb +5 -0
- data/app/controllers/katello/api/v2/host_packages_controller.rb +1 -0
- data/app/controllers/katello/api/v2/hosts_bulk_actions_controller.rb +1 -1
- data/app/lib/actions/katello/content_view/destroy.rb +1 -0
- data/app/lib/actions/katello/host/erratum/applicable_errata_install.rb +1 -1
- data/app/lib/actions/pulp3/content_view/delete_repository_references.rb +26 -0
- data/app/lib/actions/pulp3/orchestration/repository/delete.rb +8 -1
- data/app/lib/actions/pulp3/repository/delete_version.rb +20 -0
- data/app/lib/katello/concerns/base_template_scope_extensions.rb +23 -1
- data/app/lib/katello/errors.rb +2 -0
- data/app/models/katello/repository.rb +44 -9
- data/app/models/setting/content.rb +8 -2
- data/app/services/katello/pulp3/migration.rb +19 -4
- data/app/services/katello/pulp3/repository.rb +4 -0
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/activation-keys/details/activation-key-details.controller.js +13 -0
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/activation-keys/details/activation-key-subscriptions.controller.js +4 -2
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/activation-keys/details/views/activation-key-subscriptions.html +11 -8
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/common/views/katello-agent-notice.html +8 -0
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/bulk/views/content-hosts-bulk-errata-modal.html +2 -0
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/bulk/views/content-hosts-bulk-packages-modal.html +2 -0
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/content/views/content-host-errata.html +2 -0
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/content/views/content-host-packages-actions.html +1 -0
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/content/views/content-host-packages-applicable.html +2 -1
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/content/views/content-host-packages-installed.html +4 -2
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/details/content-host-details.controller.js +10 -1
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/details/views/content-host-info.html +6 -3
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/details/views/content-host-subscriptions.html +4 -2
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/views/register-client.html +9 -1
- data/lib/katello/tasks/pulp3_content_switchover.rake +2 -1
- data/lib/katello/version.rb +1 -1
- data/webpack/__mocks__/foremanReact/redux/API.js +6 -0
- data/webpack/__mocks__/foremanReact/redux/middlewares/IntervalMiddleware/IntervalSelectors.js +6 -0
- data/webpack/__mocks__/foremanReact/redux/middlewares/IntervalMiddleware/index.js +11 -0
- data/webpack/scenes/Subscriptions/Manifest/ManifestHistoryReducer.js +0 -12
- data/webpack/scenes/Subscriptions/Manifest/index.js +0 -1
- data/webpack/scenes/Subscriptions/SubscriptionActions.js +49 -10
- data/webpack/scenes/Subscriptions/SubscriptionConstants.js +10 -5
- data/webpack/scenes/Subscriptions/SubscriptionReducer.js +37 -43
- data/webpack/scenes/Subscriptions/SubscriptionsPage.js +55 -131
- data/webpack/scenes/Subscriptions/SubscriptionsSelectors.js +2 -5
- data/webpack/scenes/Subscriptions/UpstreamSubscriptions/UpstreamSubscriptionsPage.js +1 -1
- data/webpack/scenes/Subscriptions/__tests__/SubscriptionsActions.test.js +75 -8
- data/webpack/scenes/Subscriptions/__tests__/SubscriptionsPage.test.js +35 -8
- data/webpack/scenes/Subscriptions/__tests__/SubscriptionsReducer.test.js +58 -46
- data/webpack/scenes/Subscriptions/__tests__/SubscriptionsSelectors.test.js +3 -5
- data/webpack/scenes/Subscriptions/__tests__/__snapshots__/SubscriptionsActions.test.js.snap +85 -9
- data/webpack/scenes/Subscriptions/__tests__/__snapshots__/SubscriptionsPage.test.js.snap +0 -2
- data/webpack/scenes/Subscriptions/__tests__/__snapshots__/SubscriptionsReducer.test.js.snap +111 -124
- data/webpack/scenes/Subscriptions/__tests__/__snapshots__/SubscriptionsSelectors.test.js.snap +2 -12
- data/webpack/scenes/Subscriptions/__tests__/subscriptions.fixtures.js +2 -8
- data/webpack/scenes/Subscriptions/components/SubscriptionsTable/SubscriptionsTable.js +1 -16
- data/webpack/scenes/Subscriptions/components/SubscriptionsTable/__tests__/__snapshots__/SubscriptionsTable.test.js.snap +1 -6
- data/webpack/scenes/Subscriptions/components/SubscriptionsTable/components/Dialogs/UpdateDialog.js +0 -25
- data/webpack/scenes/Subscriptions/index.js +2 -5
- data/webpack/scenes/Tasks/TaskActions.js +34 -104
- data/webpack/scenes/Tasks/TaskSelectors.js +7 -0
- data/webpack/scenes/Tasks/__tests__/TaskActions.test.js +17 -154
- data/webpack/scenes/Tasks/__tests__/TaskSelectors.test.js +21 -0
- data/webpack/scenes/Tasks/__tests__/__snapshots__/TaskActions.test.js.snap +57 -0
- data/webpack/scenes/Tasks/__tests__/__snapshots__/TaskSelectors.test.js.snap +9 -0
- data/webpack/scenes/Tasks/__tests__/task.fixtures.js +0 -82
- data/webpack/scenes/Tasks/helpers.js +13 -6
- metadata +12 -3
- data/webpack/scenes/Tasks/TaskConstants.js +0 -9
data/webpack/scenes/Subscriptions/__tests__/__snapshots__/SubscriptionsSelectors.test.js.snap
CHANGED
@@ -6,24 +6,14 @@ exports[`Subscriptions selectors should select delete-modal-opened 1`] = `false`
|
|
6
6
|
|
7
7
|
exports[`Subscriptions selectors should select search-query 1`] = `"some-query"`;
|
8
8
|
|
9
|
-
exports[`Subscriptions selectors should select subscriptions
|
10
|
-
Array [
|
11
|
-
"task1",
|
12
|
-
"task2",
|
13
|
-
]
|
14
|
-
`;
|
15
|
-
|
16
|
-
exports[`Subscriptions selectors should select task-modal-opened 1`] = `false`;
|
9
|
+
exports[`Subscriptions selectors should select subscriptions task 1`] = `Object {}`;
|
17
10
|
|
18
11
|
exports[`Subscriptions selectors should select the subscriptions state 1`] = `
|
19
12
|
Object {
|
20
13
|
"deleteButtonDisabled": true,
|
21
14
|
"deleteModalOpened": false,
|
22
15
|
"searchQuery": "some-query",
|
16
|
+
"task": Object {},
|
23
17
|
"taskModalOpened": false,
|
24
|
-
"tasks": Array [
|
25
|
-
"task1",
|
26
|
-
"task2",
|
27
|
-
],
|
28
18
|
}
|
29
19
|
`;
|
@@ -11,7 +11,7 @@ export const initialState = Immutable({
|
|
11
11
|
itemCount: 0,
|
12
12
|
quantitiesLoading: false,
|
13
13
|
availableQuantities: null,
|
14
|
-
|
14
|
+
task: undefined,
|
15
15
|
tableColumns: [],
|
16
16
|
selectedTableColumns: [],
|
17
17
|
});
|
@@ -248,7 +248,6 @@ export const groupedSubscriptions = Immutable({
|
|
248
248
|
itemCount: 81,
|
249
249
|
quantitiesLoading: false,
|
250
250
|
availableQuantities: null,
|
251
|
-
tasks: [],
|
252
251
|
tableColumns: [],
|
253
252
|
selectedTableColumns: [],
|
254
253
|
});
|
@@ -318,7 +317,6 @@ export const successState = Immutable({
|
|
318
317
|
itemCount: 81,
|
319
318
|
quantitiesLoading: false,
|
320
319
|
availableQuantities: null,
|
321
|
-
tasks: [],
|
322
320
|
tableColumns: [],
|
323
321
|
selectedTableColumns: [],
|
324
322
|
});
|
@@ -335,7 +333,6 @@ export const permissionDeniedState = Immutable({
|
|
335
333
|
itemCount: 0,
|
336
334
|
quantitiesLoading: false,
|
337
335
|
availableQuantities: null,
|
338
|
-
tasks: [],
|
339
336
|
tableColumns: [],
|
340
337
|
selectedTableColumns: [],
|
341
338
|
});
|
@@ -366,7 +363,6 @@ export const errorState = Immutable({
|
|
366
363
|
results: [],
|
367
364
|
quantitiesLoading: false,
|
368
365
|
availableQuantities: null,
|
369
|
-
tasks: [],
|
370
366
|
tableColumns: [],
|
371
367
|
selectedTableColumns: [],
|
372
368
|
});
|
@@ -436,18 +432,16 @@ export const poolsUpdate = [{
|
|
436
432
|
export const updateQuantitySuccessActions = [
|
437
433
|
{
|
438
434
|
type: 'UPDATE_QUANTITY_REQUEST',
|
439
|
-
quantities: poolsUpdate,
|
440
435
|
},
|
441
436
|
{
|
442
|
-
response: requestSuccessResponse,
|
443
437
|
type: 'UPDATE_QUANTITY_SUCCESS',
|
438
|
+
response: requestSuccessResponse,
|
444
439
|
},
|
445
440
|
];
|
446
441
|
|
447
442
|
export const updateQuantityFailureActions = [
|
448
443
|
{
|
449
444
|
type: 'UPDATE_QUANTITY_REQUEST',
|
450
|
-
quantities: poolsUpdate,
|
451
445
|
},
|
452
446
|
failureAction('UPDATE_QUANTITY_FAILURE'),
|
453
447
|
toastErrorAction(),
|
@@ -128,16 +128,12 @@ class SubscriptionsTable extends Component {
|
|
128
128
|
getUpdateDialogProps = () => {
|
129
129
|
const { showUpdateConfirmDialog: show, updatedQuantity } = this.state;
|
130
130
|
const {
|
131
|
-
updateQuantity,
|
131
|
+
updateQuantity,
|
132
132
|
} = this.props;
|
133
133
|
return {
|
134
|
-
bulkSearch,
|
135
|
-
organization,
|
136
134
|
show,
|
137
|
-
task,
|
138
135
|
updatedQuantity,
|
139
136
|
updateQuantity,
|
140
|
-
confirmEdit: this.confirmEdit,
|
141
137
|
enableEditing: this.enableEditing,
|
142
138
|
showUpdateConfirm: this.showUpdateConfirm,
|
143
139
|
};
|
@@ -198,7 +194,6 @@ class SubscriptionsTable extends Component {
|
|
198
194
|
|
199
195
|
groupedSubscriptions[groupId].open = !open;
|
200
196
|
|
201
|
-
|
202
197
|
const rows = buildTableRows(
|
203
198
|
groupedSubscriptions,
|
204
199
|
subscriptions.availableQuantities,
|
@@ -287,21 +282,11 @@ SubscriptionsTable.propTypes = {
|
|
287
282
|
onDeleteSubscriptions: PropTypes.func.isRequired,
|
288
283
|
onSubscriptionDeleteModalClose: PropTypes.func.isRequired,
|
289
284
|
toggleDeleteButton: PropTypes.func.isRequired,
|
290
|
-
task: PropTypes.shape({}),
|
291
|
-
bulkSearch: PropTypes.func,
|
292
|
-
organization: PropTypes.shape({
|
293
|
-
owner_details: PropTypes.shape({
|
294
|
-
displayName: PropTypes.string,
|
295
|
-
}),
|
296
|
-
}),
|
297
285
|
selectedRows: PropTypes.instanceOf(Array).isRequired,
|
298
286
|
onSelectedRowsChange: PropTypes.func.isRequired,
|
299
287
|
};
|
300
288
|
|
301
289
|
SubscriptionsTable.defaultProps = {
|
302
|
-
task: { humanized: {} },
|
303
|
-
bulkSearch: undefined,
|
304
|
-
organization: undefined,
|
305
290
|
canManageSubscriptionAllocations: false,
|
306
291
|
};
|
307
292
|
|
@@ -23,7 +23,7 @@ exports[`subscriptions table should render a loading state 1`] = `
|
|
23
23
|
"results": Array [],
|
24
24
|
"selectedTableColumns": Array [],
|
25
25
|
"tableColumns": Array [],
|
26
|
-
"
|
26
|
+
"task": undefined,
|
27
27
|
}
|
28
28
|
}
|
29
29
|
tableColumns={
|
@@ -35,11 +35,6 @@ exports[`subscriptions table should render a loading state 1`] = `
|
|
35
35
|
"end_date",
|
36
36
|
]
|
37
37
|
}
|
38
|
-
task={
|
39
|
-
Object {
|
40
|
-
"humanized": Object {},
|
41
|
-
}
|
42
|
-
}
|
43
38
|
toggleDeleteButton={[Function]}
|
44
39
|
updateQuantity={[Function]}
|
45
40
|
>
|
data/webpack/scenes/Subscriptions/components/SubscriptionsTable/components/Dialogs/UpdateDialog.js
CHANGED
@@ -3,17 +3,12 @@ import PropTypes from 'prop-types';
|
|
3
3
|
import { sprintf, translate as __ } from 'foremanReact/common/I18n';
|
4
4
|
import { MessageDialog } from 'patternfly-react';
|
5
5
|
import { buildPools } from '../../SubscriptionsTableHelpers';
|
6
|
-
import { renderTaskStartedToast } from '../../../../../Tasks/helpers';
|
7
|
-
import { BLOCKING_FOREMAN_TASK_TYPES } from '../../../../SubscriptionConstants';
|
8
6
|
|
9
7
|
const UpdateDialog = ({
|
10
8
|
show,
|
11
9
|
updatedQuantity,
|
12
10
|
updateQuantity,
|
13
11
|
showUpdateConfirm,
|
14
|
-
bulkSearch,
|
15
|
-
organization,
|
16
|
-
task,
|
17
12
|
enableEditing,
|
18
13
|
}) => {
|
19
14
|
const quantityLength = Object.keys(updatedQuantity).length;
|
@@ -21,12 +16,6 @@ const UpdateDialog = ({
|
|
21
16
|
showUpdateConfirm(false);
|
22
17
|
if (quantityLength > 0) {
|
23
18
|
await updateQuantity(buildPools(updatedQuantity));
|
24
|
-
await bulkSearch({
|
25
|
-
action: `organization '${organization.owner_details.displayName}'`,
|
26
|
-
result: 'pending',
|
27
|
-
label: BLOCKING_FOREMAN_TASK_TYPES.join(' or '),
|
28
|
-
});
|
29
|
-
renderTaskStartedToast(task);
|
30
19
|
}
|
31
20
|
enableEditing(false);
|
32
21
|
};
|
@@ -60,21 +49,7 @@ UpdateDialog.propTypes = {
|
|
60
49
|
updateQuantity: PropTypes.func.isRequired,
|
61
50
|
updatedQuantity: PropTypes.shape(PropTypes.Object).isRequired,
|
62
51
|
showUpdateConfirm: PropTypes.func.isRequired,
|
63
|
-
bulkSearch: PropTypes.func,
|
64
|
-
organization: PropTypes.shape({
|
65
|
-
owner_details: PropTypes.shape({
|
66
|
-
displayName: PropTypes.string,
|
67
|
-
}),
|
68
|
-
}),
|
69
|
-
task: PropTypes.shape({}),
|
70
52
|
enableEditing: PropTypes.func.isRequired,
|
71
53
|
};
|
72
54
|
|
73
|
-
UpdateDialog.defaultProps = {
|
74
|
-
task: { humanized: {} },
|
75
|
-
bulkSearch: undefined,
|
76
|
-
organization: undefined,
|
77
|
-
};
|
78
|
-
|
79
|
-
|
80
55
|
export default UpdateDialog;
|
@@ -11,9 +11,8 @@ import {
|
|
11
11
|
selectSubscriptionsState,
|
12
12
|
selectSearchQuery,
|
13
13
|
selectDeleteModalOpened,
|
14
|
-
selectTaskModalOpened,
|
15
14
|
selectDeleteButtonDisabled,
|
16
|
-
|
15
|
+
selectSubscriptionsTask,
|
17
16
|
selectActivePermissions,
|
18
17
|
selectTableSettings,
|
19
18
|
} from './SubscriptionsSelectors';
|
@@ -33,13 +32,11 @@ const mapStateToProps = (state) => {
|
|
33
32
|
subscriptionTableSettings,
|
34
33
|
activePermissions: selectActivePermissions(state),
|
35
34
|
simpleContentAccess: selectSimpleContentAccessEnabled(state),
|
36
|
-
|
35
|
+
task: selectSubscriptionsTask(state),
|
37
36
|
searchQuery: selectSearchQuery(state),
|
38
37
|
deleteModalOpened: selectDeleteModalOpened(state),
|
39
|
-
taskModalOpened: selectTaskModalOpened(state),
|
40
38
|
deleteButtonDisabled: selectDeleteButtonDisabled(state),
|
41
39
|
organization: state.katello.organization,
|
42
|
-
taskDetails: state.katello.manifestHistory.taskDetails,
|
43
40
|
};
|
44
41
|
};
|
45
42
|
|
@@ -1,111 +1,41 @@
|
|
1
|
+
import { addToast } from 'foremanReact/redux/actions/toasts';
|
1
2
|
import { propsToSnakeCase } from 'foremanReact/common/helpers';
|
3
|
+
import { get } from 'foremanReact/redux/API';
|
4
|
+
import { stopInterval, withInterval } from 'foremanReact/redux/middlewares/IntervalMiddleware';
|
5
|
+
import { foremanTasksApi } from '../../services/api';
|
6
|
+
import { bulkSearchKey, pollTaskKey, taskFinishedToast } from './helpers';
|
7
|
+
|
8
|
+
export const toastTaskFinished = task => async dispatch =>
|
9
|
+
dispatch(addToast(taskFinishedToast(task)));
|
10
|
+
|
11
|
+
const taskBulkSearchParams = params => ({
|
12
|
+
search: Object.entries(propsToSnakeCase(params))
|
13
|
+
.map((item) => {
|
14
|
+
if (item[0] === 'action') {
|
15
|
+
return `${item[0]}~${item[1]}`;
|
16
|
+
}
|
17
|
+
return `${item[0]}=${item[1]}`;
|
18
|
+
})
|
19
|
+
.join(' and '),
|
20
|
+
});
|
2
21
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
TASK_BULK_SEARCH_REQUEST,
|
9
|
-
TASK_BULK_SEARCH_SUCCESS,
|
10
|
-
TASK_BULK_SEARCH_FAILURE,
|
11
|
-
RESET_TASKS,
|
12
|
-
} from './TaskConstants';
|
13
|
-
|
14
|
-
export const bulkSearch = (extendedParams = {}) => async (dispatch) => {
|
15
|
-
const params = {
|
16
|
-
search: Object.entries(propsToSnakeCase(extendedParams))
|
17
|
-
.map((item) => {
|
18
|
-
if (item[0] === 'action') {
|
19
|
-
return `${item[0]}~${item[1]}`;
|
20
|
-
}
|
21
|
-
return `${item[0]}=${item[1]}`;
|
22
|
-
})
|
23
|
-
.join(' and '),
|
24
|
-
};
|
25
|
-
|
26
|
-
dispatch({ type: TASK_BULK_SEARCH_REQUEST });
|
27
|
-
|
28
|
-
try {
|
29
|
-
const { data } = await api.get('/tasks', {}, params);
|
30
|
-
return dispatch({
|
31
|
-
type: TASK_BULK_SEARCH_SUCCESS,
|
32
|
-
response: data,
|
33
|
-
});
|
34
|
-
} catch (error) {
|
35
|
-
return dispatch({
|
36
|
-
type: TASK_BULK_SEARCH_FAILURE,
|
37
|
-
result: error,
|
38
|
-
});
|
39
|
-
}
|
40
|
-
};
|
41
|
-
|
42
|
-
export const resetTasks = () => (dispatch) => {
|
43
|
-
dispatch({
|
44
|
-
type: RESET_TASKS,
|
45
|
-
});
|
46
|
-
};
|
47
|
-
|
48
|
-
export const loadTask = (taskId, extendedParams = {}) => async (dispatch) => {
|
49
|
-
dispatch({ type: GET_TASK_REQUEST });
|
50
|
-
|
51
|
-
const params = {
|
52
|
-
...propsToSnakeCase(extendedParams),
|
53
|
-
};
|
54
|
-
|
55
|
-
try {
|
56
|
-
const { data } = await api.get(`/tasks/${taskId}`, {}, params);
|
57
|
-
return dispatch({
|
58
|
-
type: GET_TASK_SUCCESS,
|
59
|
-
response: data,
|
60
|
-
});
|
61
|
-
} catch (error) {
|
62
|
-
return dispatch({
|
63
|
-
type: GET_TASK_FAILURE,
|
64
|
-
result: error,
|
65
|
-
});
|
66
|
-
}
|
67
|
-
};
|
22
|
+
const getTasks = (key, params = {}) => get({
|
23
|
+
key,
|
24
|
+
url: `${foremanTasksApi.baseApiPath}/tasks`,
|
25
|
+
params: taskBulkSearchParams(params),
|
26
|
+
});
|
68
27
|
|
69
|
-
const
|
70
|
-
|
71
|
-
&& action.result.response
|
72
|
-
&& action.result.response.status === 401);
|
28
|
+
export const startPollingTasks = (key, taskSearchParams = {}) =>
|
29
|
+
withInterval(getTasks(bulkSearchKey(key), taskSearchParams));
|
73
30
|
|
31
|
+
export const stopPollingTasks = key => stopInterval(bulkSearchKey(key));
|
74
32
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
if (!isUnauthorized(action)) {
|
80
|
-
if (id === orgId) {
|
81
|
-
setTimeout(() => dispatch(pollBulkSearch(extendedParams, interval, orgId)), interval);
|
82
|
-
}
|
83
|
-
}
|
84
|
-
};
|
85
|
-
const { id, loading } = getState().katello.organization;
|
86
|
-
if (id === orgId && !loading) {
|
87
|
-
const dispatchedAction = await dispatch(await bulkSearch(extendedParams));
|
88
|
-
triggerPolling(dispatchedAction);
|
89
|
-
return dispatchedAction;
|
90
|
-
}
|
33
|
+
const getTask = (key, task) => get({
|
34
|
+
key,
|
35
|
+
url: `${foremanTasksApi.baseApiPath}/tasks/${task.id}`,
|
36
|
+
});
|
91
37
|
|
92
|
-
|
93
|
-
|
38
|
+
export const startPollingTask = (key, task) =>
|
39
|
+
withInterval(getTask(pollTaskKey(key), task));
|
94
40
|
|
95
|
-
export const
|
96
|
-
(dispatch, getState) => new Promise((resolve, reject) => {
|
97
|
-
const pollUntilDone = (action) => {
|
98
|
-
const { id, loading } = getState().katello.organization;
|
99
|
-
|
100
|
-
if (isUnauthorized(action) || id !== orgId || loading) {
|
101
|
-
reject(action.result);
|
102
|
-
} else if (action.response && action.response.pending) {
|
103
|
-
// eslint-disable-next-line promise/prefer-await-to-then
|
104
|
-
setTimeout(() => dispatch(loadTask(taskId, extendedParams)).then(pollUntilDone), interval);
|
105
|
-
} else {
|
106
|
-
resolve(action.response);
|
107
|
-
}
|
108
|
-
};
|
109
|
-
// eslint-disable-next-line promise/prefer-await-to-then
|
110
|
-
return dispatch(loadTask(taskId, extendedParams)).then(pollUntilDone);
|
111
|
-
});
|
41
|
+
export const stopPollingTask = key => stopInterval(pollTaskKey(key));
|
@@ -0,0 +1,7 @@
|
|
1
|
+
import { selectDoesIntervalExist } from 'foremanReact/redux/middlewares/IntervalMiddleware/IntervalSelectors';
|
2
|
+
import { bulkSearchKey, pollTaskKey } from './helpers';
|
3
|
+
|
4
|
+
export const selectIsPollingTask = (state, key) => selectDoesIntervalExist(state, pollTaskKey(key));
|
5
|
+
|
6
|
+
export const selectIsPollingTasks = (state, key) =>
|
7
|
+
selectDoesIntervalExist(state, bulkSearchKey(key));
|
@@ -1,155 +1,18 @@
|
|
1
|
-
import
|
2
|
-
import
|
3
|
-
import thunk from 'redux-thunk';
|
4
|
-
import Immutable from 'seamless-immutable';
|
5
|
-
import configureMockStore from 'redux-mock-store';
|
6
|
-
import {
|
7
|
-
bulkSearchSuccessResponse,
|
8
|
-
bulkSearchSuccessActions,
|
9
|
-
buildBulkSearchFailureActions,
|
10
|
-
getTaskSuccessResponse,
|
11
|
-
getTaskSuccessActions,
|
12
|
-
buildTaskFailureActions,
|
13
|
-
getTaskPendingResponse,
|
14
|
-
getTaskPendingActions,
|
15
|
-
} from './task.fixtures';
|
16
|
-
|
17
|
-
import { bulkSearch, loadTask, pollTaskUntilDone, pollBulkSearch } from '../TaskActions';
|
18
|
-
|
19
|
-
const mockStore = configureMockStore([thunk]);
|
20
|
-
const store = mockStore({
|
21
|
-
tasks: Immutable({}),
|
22
|
-
katello: { organization: { id: 1, loading: false } },
|
23
|
-
});
|
24
|
-
const mockApi = new MockAdapter(axios);
|
25
|
-
|
26
|
-
beforeEach(() => {
|
27
|
-
store.clearActions();
|
28
|
-
mockApi.reset();
|
29
|
-
});
|
30
|
-
|
31
|
-
let originalTimeout;
|
32
|
-
let setTimeoutSpy;
|
33
|
-
|
34
|
-
beforeEach(() => {
|
35
|
-
setTimeoutSpy = jest.spyOn(window, 'setTimeout');
|
36
|
-
|
37
|
-
originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
|
38
|
-
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
|
39
|
-
});
|
40
|
-
|
41
|
-
afterEach(() => {
|
42
|
-
setTimeoutSpy.mockReset();
|
43
|
-
setTimeoutSpy.mockRestore();
|
44
|
-
|
45
|
-
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
|
46
|
-
});
|
47
|
-
|
48
|
-
describe('task actions', () => {
|
49
|
-
describe('creates TASK_BULK_SEARCH_REQUEST', () => {
|
50
|
-
const url = '/foreman_tasks/api/tasks';
|
51
|
-
|
52
|
-
it('and then fails with 422', async () => {
|
53
|
-
mockApi.onGet(url).reply(422);
|
54
|
-
|
55
|
-
await store.dispatch(bulkSearch());
|
56
|
-
expect(store.getActions()).toEqual(buildBulkSearchFailureActions());
|
57
|
-
});
|
58
|
-
|
59
|
-
it('and ends with success', async () => {
|
60
|
-
mockApi.onGet(url).reply(200, bulkSearchSuccessResponse);
|
61
|
-
|
62
|
-
await store.dispatch(bulkSearch());
|
63
|
-
expect(store.getActions()).toEqual(bulkSearchSuccessActions);
|
64
|
-
});
|
65
|
-
});
|
66
|
-
|
67
|
-
describe('creates GET_TASK_REQUEST', () => {
|
68
|
-
const taskId = 'eb1b6271-8a69-4d98-84fc-bea06ddcc166';
|
69
|
-
const url = `/foreman_tasks/api/tasks/${taskId}`;
|
70
|
-
|
71
|
-
it('and then fails with 422', async () => {
|
72
|
-
mockApi.onGet(url).reply(422);
|
73
|
-
|
74
|
-
await store.dispatch(loadTask(taskId));
|
75
|
-
expect(store.getActions()).toEqual(buildTaskFailureActions());
|
76
|
-
});
|
1
|
+
import { testActionSnapshotWithFixtures } from 'react-redux-test-utils';
|
2
|
+
import { getTaskSuccessResponse } from './task.fixtures';
|
77
3
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
await store.dispatch(pollTaskUntilDone(taskId, {}, 1, 1));
|
94
|
-
expect(store.getActions()).toEqual(getTaskSuccessActions);
|
95
|
-
expect(setTimeoutSpy).toHaveBeenCalledTimes(0);
|
96
|
-
});
|
97
|
-
|
98
|
-
it('polls until a task is done', async () => {
|
99
|
-
mockApi
|
100
|
-
.onGet(url)
|
101
|
-
.replyOnce(200, getTaskPendingResponse)
|
102
|
-
.onGet(url)
|
103
|
-
.replyOnce(200, getTaskPendingResponse)
|
104
|
-
.onGet(url)
|
105
|
-
.replyOnce(200, getTaskSuccessResponse);
|
106
|
-
|
107
|
-
const expectedActions = getTaskPendingActions
|
108
|
-
.concat(getTaskPendingActions)
|
109
|
-
.concat(getTaskSuccessActions);
|
110
|
-
|
111
|
-
await store.dispatch(pollTaskUntilDone(taskId, {}, 1, 1));
|
112
|
-
expect(store.getActions()).toEqual(expectedActions);
|
113
|
-
expect(setTimeoutSpy).toHaveBeenCalledTimes(2);
|
114
|
-
});
|
115
|
-
|
116
|
-
it('stops polling on unauthorized response', async () => {
|
117
|
-
mockApi
|
118
|
-
.onGet(url).replyOnce(200, getTaskPendingResponse)
|
119
|
-
.onGet(url).replyOnce(401);
|
120
|
-
|
121
|
-
const expectedActions = getTaskPendingActions
|
122
|
-
.concat(buildTaskFailureActions(401));
|
123
|
-
|
124
|
-
try {
|
125
|
-
await store.dispatch(pollTaskUntilDone(taskId, {}, 1, 1));
|
126
|
-
} catch (e) {
|
127
|
-
expect(store.getActions()).toEqual(expectedActions);
|
128
|
-
expect(setTimeoutSpy).toHaveBeenCalledTimes(1);
|
129
|
-
}
|
130
|
-
});
|
131
|
-
});
|
132
|
-
|
133
|
-
describe('pollBulkSearch', () => {
|
134
|
-
const url = '/foreman_tasks/api/tasks';
|
135
|
-
|
136
|
-
it('polls', async () => {
|
137
|
-
mockApi
|
138
|
-
.onGet(url)
|
139
|
-
.replyOnce(200, bulkSearchSuccessResponse);
|
140
|
-
|
141
|
-
await store.dispatch(pollBulkSearch({}, 1, 1));
|
142
|
-
expect(setTimeoutSpy).toHaveBeenCalledTimes(1);
|
143
|
-
expect(store.getActions()).toEqual(bulkSearchSuccessActions);
|
144
|
-
});
|
145
|
-
|
146
|
-
it('stops polling on unauthorized response', async () => {
|
147
|
-
mockApi
|
148
|
-
.onGet(url).replyOnce(401);
|
149
|
-
|
150
|
-
await store.dispatch(pollBulkSearch({}, 1, 1));
|
151
|
-
expect(store.getActions()).toEqual(buildBulkSearchFailureActions(401));
|
152
|
-
expect(setTimeoutSpy).toHaveBeenCalledTimes(0);
|
153
|
-
});
|
154
|
-
});
|
155
|
-
});
|
4
|
+
import {
|
5
|
+
startPollingTask,
|
6
|
+
stopPollingTask,
|
7
|
+
startPollingTasks,
|
8
|
+
stopPollingTasks,
|
9
|
+
toastTaskFinished,
|
10
|
+
} from '../TaskActions';
|
11
|
+
|
12
|
+
describe('task actions', () => testActionSnapshotWithFixtures({
|
13
|
+
'can search tasks': () => startPollingTasks('TEST'),
|
14
|
+
'can stop searching tasks': () => stopPollingTasks('TEST'),
|
15
|
+
'can poll a task': () => startPollingTask('TEST', { id: '12345' }),
|
16
|
+
'can stop polling a task': () => stopPollingTask('TEST'),
|
17
|
+
'can toast a finished task': () => toastTaskFinished(getTaskSuccessResponse),
|
18
|
+
}));
|