@truedat/dd 5.15.2 → 5.16.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -5
- package/src/api.js +6 -0
- package/src/components/GrantRequestApprovalResults.js +40 -0
- package/src/components/GrantRequestBulkActions.js +136 -0
- package/src/components/GrantRequestBulkApprovalForm.js +69 -0
- package/src/components/GrantRequestBulkApprovalPopup.js +43 -0
- package/src/components/GrantRequestBulkRoleSelector.js +41 -0
- package/src/components/GrantRequestRow.js +62 -0
- package/src/components/GrantRequestSearchFilters.js +22 -0
- package/src/components/GrantRequestsFiltersLoader.js +33 -0
- package/src/components/GrantRequestsLabelResults.js +52 -0
- package/src/components/GrantRequestsSearch.js +35 -0
- package/src/components/GrantRequestsSearchLoader.js +47 -0
- package/src/components/GrantRequestsSearchResults.js +156 -0
- package/src/components/GrantRequestsSelectedFilters.js +54 -0
- package/src/components/GrantRequestsTable.js +131 -0
- package/src/components/GrantRoutes.js +34 -16
- package/src/components/Grants.js +1 -0
- package/src/components/__tests__/GrantRequestApprovalResults.spec.js +30 -0
- package/src/components/__tests__/GrantRequestBulkActions.spec.js +31 -0
- package/src/components/__tests__/GrantRequestBulkApprovalForm.spec.js +54 -0
- package/src/components/__tests__/GrantRequestBulkApprovalPopup.spec.js +17 -0
- package/src/components/__tests__/GrantRequestBulkRoleSelector.spec.js +48 -0
- package/src/components/__tests__/GrantRequestFiltersLoader.spec.js +20 -0
- package/src/components/__tests__/GrantRequestRow.spec.js +95 -0
- package/src/components/__tests__/GrantRequestSearchFilters.spec.js +19 -0
- package/src/components/__tests__/GrantRequestsLabelResults.spec.js +45 -0
- package/src/components/__tests__/GrantRequestsSearch.spec.js +23 -0
- package/src/components/__tests__/GrantRequestsSearchLoader.spec.js +15 -0
- package/src/components/__tests__/GrantRequestsSearchResults.spec.js +59 -0
- package/src/components/__tests__/GrantRequestsTable.spec.js +35 -0
- package/src/components/__tests__/__snapshots__/GrantRequestApprovalResults.spec.js.snap +69 -0
- package/src/components/__tests__/__snapshots__/GrantRequestBulkActions.spec.js.snap +51 -0
- package/src/components/__tests__/__snapshots__/GrantRequestBulkApprovalForm.spec.js.snap +41 -0
- package/src/components/__tests__/__snapshots__/GrantRequestBulkApprovalPopup.spec.js.snap +11 -0
- package/src/components/__tests__/__snapshots__/GrantRequestBulkRoleSelector.spec.js.snap +56 -0
- package/src/components/__tests__/__snapshots__/GrantRequestRow.spec.js.snap +55 -0
- package/src/components/__tests__/__snapshots__/GrantRequestSearchFilters.spec.js.snap +47 -0
- package/src/components/__tests__/__snapshots__/GrantRequestsLabelResults.spec.js.snap +36 -0
- package/src/components/__tests__/__snapshots__/GrantRequestsSearch.spec.js.snap +50 -0
- package/src/components/__tests__/__snapshots__/GrantRequestsSearchLoader.spec.js.snap +3 -0
- package/src/components/__tests__/__snapshots__/GrantRequestsSearchResults.spec.js.snap +248 -0
- package/src/components/__tests__/__snapshots__/GrantRequestsTable.spec.js.snap +19 -0
- package/src/components/__tests__/__snapshots__/GrantRoutes.spec.js.snap +0 -4
- package/src/components/index.js +8 -0
- package/src/hooks/useGrantRequest.js +9 -0
- package/src/reducers/__tests__/grantRequestCount.spec.js +38 -0
- package/src/reducers/__tests__/grantRequestPermissions.spec.js +66 -0
- package/src/reducers/__tests__/grantRequestsActiveFilters.spec.js +90 -0
- package/src/reducers/__tests__/grantRequestsFiltersLoading.spec.js +34 -0
- package/src/reducers/__tests__/grantRequestsSearch.spec.js +38 -0
- package/src/reducers/__tests__/grantRequestsSearchLoading.spec.js +36 -0
- package/src/reducers/__tests__/grantRequestsSearchQuery.spec.js +96 -0
- package/src/reducers/__tests__/grantRequestsSelectedFilter.spec.js +72 -0
- package/src/reducers/grantRequestCount.js +23 -0
- package/src/reducers/grantRequestPermissions.js +31 -0
- package/src/reducers/grantRequestSearchQuery.js +55 -0
- package/src/reducers/grantRequestsActiveFilters.js +56 -0
- package/src/reducers/grantRequestsFilters.js +23 -0
- package/src/reducers/grantRequestsFiltersLoading.js +14 -0
- package/src/reducers/grantRequestsSearch.js +19 -0
- package/src/reducers/grantRequestsSearchLoading.js +14 -0
- package/src/reducers/grantRequestsSelectedFilter.js +34 -0
- package/src/reducers/graphLoading.js +2 -0
- package/src/reducers/index.js +22 -0
- package/src/reducers/structureRedirect.js +2 -3
- package/src/routines.js +13 -0
- package/src/sagas/__tests__/createGraph.spec.js +4 -4
- package/src/sagas/__tests__/fetchGrantRequestsFilters.spec.js +91 -0
- package/src/sagas/__tests__/fetchGrantRequestsSearch.spec.js +92 -0
- package/src/sagas/createGraph.js +7 -71
- package/src/sagas/fetchGrantRequestsFilters.js +32 -0
- package/src/sagas/fetchGrantRequestsSearch.js +30 -0
- package/src/sagas/fetchGraph.js +26 -4
- package/src/sagas/graph/polling.js +70 -0
- package/src/sagas/index.js +6 -0
- package/src/selectors/__tests__/getGrantRequestsAvailableFilters.spec.js +15 -0
- package/src/selectors/__tests__/getGrantRequestsFilterTypes.spec.js +19 -0
- package/src/selectors/__tests__/getGrantRequestsSearchColumns.spec.js +30 -0
- package/src/selectors/__tests__/getGrantRequestsSearchQuery.spec.js +24 -0
- package/src/selectors/__tests__/getGrantRequestsSelectedFilterActiveValues.spec.js +15 -0
- package/src/selectors/__tests__/getGrantRequestsSelectedFilterValues.spec.js +15 -0
- package/src/selectors/__tests__/getGrantRequestsSelectedFilters.spec.js +13 -0
- package/src/selectors/getGrantRequestsAvailableFilters.js +17 -0
- package/src/selectors/getGrantRequestsFilterTypes.js +7 -0
- package/src/selectors/getGrantRequestsSearchColumns.js +119 -0
- package/src/selectors/getGrantRequestsSearchQuery.js +19 -0
- package/src/selectors/getGrantRequestsSelectedFilterActiveValues.js +8 -0
- package/src/selectors/getGrantRequestsSelectedFilterValues.js +12 -0
- package/src/selectors/getGrantRequestsSelectedFilters.js +8 -0
- package/src/selectors/index.js +10 -0
- package/src/components/GrantRequestApprovals.js +0 -41
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { testSaga } from "redux-saga-test-plan";
|
|
2
|
+
import { apiJsonPost, JSON_OPTS } from "@truedat/core/services/api";
|
|
3
|
+
import {
|
|
4
|
+
fetchGrantRequestsFiltersRequestSaga,
|
|
5
|
+
fetchGrantRequestsFiltersSaga,
|
|
6
|
+
} from "../fetchGrantRequestsFilters";
|
|
7
|
+
import { fetchGrantRequestsFilters } from "../../routines";
|
|
8
|
+
import { API_GRANT_REQUESTS_FILTERS_SEARCH } from "../../api";
|
|
9
|
+
|
|
10
|
+
const filters = {
|
|
11
|
+
filters: {
|
|
12
|
+
current_status: ["pending"],
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
const payload = {
|
|
16
|
+
filters: filters,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
describe("sagas: fetchGrantRequestsFiltersRequestSaga", () => {
|
|
20
|
+
it("should invoke fetchGrantRequestsFiltersSaga on fetchGrantRequestsFilters.TRIGGER", () => {
|
|
21
|
+
expect(() => {
|
|
22
|
+
testSaga(fetchGrantRequestsFiltersRequestSaga)
|
|
23
|
+
.next()
|
|
24
|
+
.takeLatest(
|
|
25
|
+
fetchGrantRequestsFilters.TRIGGER,
|
|
26
|
+
fetchGrantRequestsFiltersSaga
|
|
27
|
+
)
|
|
28
|
+
.finish()
|
|
29
|
+
.isDone();
|
|
30
|
+
}).not.toThrow();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("should throw exception if an unhandled action is received", () => {
|
|
34
|
+
expect(() => {
|
|
35
|
+
testSaga(fetchGrantRequestsFiltersRequestSaga)
|
|
36
|
+
.next()
|
|
37
|
+
.takeLatest("FOO", fetchGrantRequestsFiltersRequestSaga);
|
|
38
|
+
}).toThrow();
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe("sagas: fetchGrantRequestsFiltersSaga", () => {
|
|
43
|
+
const data = {
|
|
44
|
+
data: { foo: {}, var: {} },
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
it("should put a success action when a response is returned", () => {
|
|
48
|
+
expect(() => {
|
|
49
|
+
testSaga(fetchGrantRequestsFiltersSaga, { payload })
|
|
50
|
+
.next()
|
|
51
|
+
.put(fetchGrantRequestsFilters.request())
|
|
52
|
+
.next()
|
|
53
|
+
.call(
|
|
54
|
+
apiJsonPost,
|
|
55
|
+
API_GRANT_REQUESTS_FILTERS_SEARCH,
|
|
56
|
+
payload,
|
|
57
|
+
JSON_OPTS
|
|
58
|
+
)
|
|
59
|
+
.next({ data })
|
|
60
|
+
.put(fetchGrantRequestsFilters.success({ data }))
|
|
61
|
+
.next()
|
|
62
|
+
.put(fetchGrantRequestsFilters.fulfill())
|
|
63
|
+
.next()
|
|
64
|
+
.isDone();
|
|
65
|
+
}).not.toThrow();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("should put a failure action when the call returns an error", () => {
|
|
69
|
+
const message = "Request failed";
|
|
70
|
+
const error = { message };
|
|
71
|
+
|
|
72
|
+
expect(() => {
|
|
73
|
+
testSaga(fetchGrantRequestsFiltersSaga, { payload })
|
|
74
|
+
.next()
|
|
75
|
+
.put(fetchGrantRequestsFilters.request())
|
|
76
|
+
.next()
|
|
77
|
+
.call(
|
|
78
|
+
apiJsonPost,
|
|
79
|
+
API_GRANT_REQUESTS_FILTERS_SEARCH,
|
|
80
|
+
payload,
|
|
81
|
+
JSON_OPTS
|
|
82
|
+
)
|
|
83
|
+
.throw(error)
|
|
84
|
+
.put(fetchGrantRequestsFilters.failure(message))
|
|
85
|
+
.next()
|
|
86
|
+
.put(fetchGrantRequestsFilters.fulfill())
|
|
87
|
+
.next()
|
|
88
|
+
.isDone();
|
|
89
|
+
}).not.toThrow();
|
|
90
|
+
});
|
|
91
|
+
});
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { testSaga } from "redux-saga-test-plan";
|
|
2
|
+
import { apiJsonPost, JSON_OPTS } from "@truedat/core/services/api";
|
|
3
|
+
import {
|
|
4
|
+
fetchGrantRequestsSearchRequestSaga,
|
|
5
|
+
fetchGrantRequestsSearchSaga,
|
|
6
|
+
} from "../fetchGrantRequestsSearch";
|
|
7
|
+
import { fetchGrantRequestsSearch } from "../../routines";
|
|
8
|
+
import { API_GRANT_REQUESTS_SEARCH } from "../../api";
|
|
9
|
+
|
|
10
|
+
const body = {
|
|
11
|
+
must: {
|
|
12
|
+
current_status: ["pending"],
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
const payload = body;
|
|
16
|
+
|
|
17
|
+
describe("sagas: fetchGrantRequestsSearchRequestSaga", () => {
|
|
18
|
+
it("should invoke fetchGrantRequestsSearchSaga on fetchGrantRequestsSearch.TRIGGER", () => {
|
|
19
|
+
expect(() => {
|
|
20
|
+
testSaga(fetchGrantRequestsSearchRequestSaga)
|
|
21
|
+
.next()
|
|
22
|
+
.debounce(
|
|
23
|
+
200,
|
|
24
|
+
fetchGrantRequestsSearch.TRIGGER,
|
|
25
|
+
fetchGrantRequestsSearchSaga
|
|
26
|
+
)
|
|
27
|
+
.finish()
|
|
28
|
+
.isDone();
|
|
29
|
+
}).not.toThrow();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("should throw exception if an unhandled action is received", () => {
|
|
33
|
+
expect(() => {
|
|
34
|
+
testSaga(fetchGrantRequestsSearchRequestSaga)
|
|
35
|
+
.next()
|
|
36
|
+
.takeLatest("FOO", fetchGrantRequestsSearchRequestSaga);
|
|
37
|
+
}).toThrow();
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe("sagas: fetchGrantRequestsSearchSaga", () => {
|
|
42
|
+
const data = {
|
|
43
|
+
_permissions: ["Role1", "Role2"],
|
|
44
|
+
data: [
|
|
45
|
+
{
|
|
46
|
+
id: 385,
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
id: 385,
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const headers = {
|
|
55
|
+
"x-total-count": "2",
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
it("should put a success action when a response is returned", () => {
|
|
59
|
+
expect(() => {
|
|
60
|
+
testSaga(fetchGrantRequestsSearchSaga, { payload })
|
|
61
|
+
.next()
|
|
62
|
+
.put(fetchGrantRequestsSearch.request(body))
|
|
63
|
+
.next()
|
|
64
|
+
.call(apiJsonPost, API_GRANT_REQUESTS_SEARCH, body, JSON_OPTS)
|
|
65
|
+
.next({ data, headers })
|
|
66
|
+
.put(fetchGrantRequestsSearch.success({ data, headers }))
|
|
67
|
+
.next()
|
|
68
|
+
.put(fetchGrantRequestsSearch.fulfill())
|
|
69
|
+
.next()
|
|
70
|
+
.isDone();
|
|
71
|
+
}).not.toThrow();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("should put a failure action when the call returns an error", () => {
|
|
75
|
+
const message = "Request failed";
|
|
76
|
+
const error = { message };
|
|
77
|
+
|
|
78
|
+
expect(() => {
|
|
79
|
+
testSaga(fetchGrantRequestsSearchSaga, { payload })
|
|
80
|
+
.next()
|
|
81
|
+
.put(fetchGrantRequestsSearch.request(body))
|
|
82
|
+
.next()
|
|
83
|
+
.call(apiJsonPost, API_GRANT_REQUESTS_SEARCH, body, JSON_OPTS)
|
|
84
|
+
.throw(error)
|
|
85
|
+
.put(fetchGrantRequestsSearch.failure(message))
|
|
86
|
+
.next()
|
|
87
|
+
.put(fetchGrantRequestsSearch.fulfill())
|
|
88
|
+
.next()
|
|
89
|
+
.isDone();
|
|
90
|
+
}).not.toThrow();
|
|
91
|
+
});
|
|
92
|
+
});
|
package/src/sagas/createGraph.js
CHANGED
|
@@ -1,55 +1,43 @@
|
|
|
1
|
-
/* eslint-disable fp/no-mutation */
|
|
2
|
-
/* eslint-disable fp/no-let */
|
|
3
1
|
import _ from "lodash/fp";
|
|
4
2
|
import {
|
|
5
3
|
all,
|
|
6
4
|
call,
|
|
7
5
|
cancel,
|
|
8
|
-
cancelled,
|
|
9
6
|
fork,
|
|
10
7
|
put,
|
|
11
8
|
select,
|
|
12
9
|
take,
|
|
13
10
|
takeLatest,
|
|
14
|
-
delay,
|
|
15
11
|
} from "redux-saga/effects";
|
|
16
|
-
import {
|
|
17
|
-
import { compile } from "path-to-regexp";
|
|
12
|
+
import { apiJsonPost, JSON_OPTS } from "@truedat/core/services/api";
|
|
18
13
|
import { excludeNode, cancelPoll, createGraph, fetchGraph } from "../routines";
|
|
19
|
-
import { API_GRAPHS
|
|
14
|
+
import { API_GRAPHS } from "../api";
|
|
15
|
+
import { polling } from "./graph/polling";
|
|
20
16
|
|
|
21
17
|
export const selectLineageQuery = _.prop("lineageQuery");
|
|
22
18
|
|
|
23
19
|
export const POLLING_CANCELLED = "Polling cancelled!";
|
|
24
20
|
|
|
25
|
-
function* exponentialBackoff(base, power) {
|
|
26
|
-
while (true) {
|
|
27
|
-
yield Math.pow(base, power);
|
|
28
|
-
// eslint-disable-next-line fp/no-mutation, no-param-reassign
|
|
29
|
-
power++;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
21
|
export function* createGraphSaga() {
|
|
34
|
-
let graphAcceptedOrCreated;
|
|
35
22
|
try {
|
|
36
23
|
const body = yield select(selectLineageQuery);
|
|
37
24
|
yield put(createGraph.request(body));
|
|
38
25
|
|
|
39
26
|
/* graph is either new (accepted) or "created" (actually previously stored
|
|
40
27
|
from an earlier accepted one) */
|
|
41
|
-
graphAcceptedOrCreated = yield call(
|
|
28
|
+
const graphAcceptedOrCreated = yield call(
|
|
42
29
|
apiJsonPost,
|
|
43
30
|
API_GRAPHS,
|
|
44
31
|
body,
|
|
45
32
|
JSON_OPTS
|
|
46
33
|
);
|
|
47
34
|
|
|
48
|
-
if (graphAcceptedOrCreated.status
|
|
35
|
+
if (graphAcceptedOrCreated.status === 202) {
|
|
49
36
|
yield put(fetchGraph.request(graphAcceptedOrCreated));
|
|
50
37
|
// starts the task in the background
|
|
51
38
|
const pollingTask = yield fork(
|
|
52
39
|
polling,
|
|
40
|
+
createGraph,
|
|
53
41
|
graphAcceptedOrCreated,
|
|
54
42
|
body.isRedirected
|
|
55
43
|
);
|
|
@@ -59,7 +47,7 @@ export function* createGraphSaga() {
|
|
|
59
47
|
// user clicked stop. cancel the background task
|
|
60
48
|
// this will cause the forked bgSync task to jump into its finally block
|
|
61
49
|
yield cancel(pollingTask);
|
|
62
|
-
} else if (graphAcceptedOrCreated.status
|
|
50
|
+
} else if (graphAcceptedOrCreated.status === 201) {
|
|
63
51
|
yield put(
|
|
64
52
|
createGraph.success({
|
|
65
53
|
...graphAcceptedOrCreated,
|
|
@@ -82,58 +70,6 @@ export function* createGraphSaga() {
|
|
|
82
70
|
}
|
|
83
71
|
}
|
|
84
72
|
|
|
85
|
-
export function* polling(graphAccepted, isRedirected) {
|
|
86
|
-
const backoff = exponentialBackoff(1.2, 0);
|
|
87
|
-
let backoffTime,
|
|
88
|
-
retry = 0,
|
|
89
|
-
graphResp,
|
|
90
|
-
isCancelled = false;
|
|
91
|
-
|
|
92
|
-
do {
|
|
93
|
-
backoffTime = backoff.next().value * 1000;
|
|
94
|
-
yield delay(backoffTime);
|
|
95
|
-
|
|
96
|
-
try {
|
|
97
|
-
graphResp = yield call(
|
|
98
|
-
apiJson,
|
|
99
|
-
compile(API_GRAPH_HASH)({
|
|
100
|
-
hash: graphAccepted.data.graph_hash,
|
|
101
|
-
}),
|
|
102
|
-
JSON_OPTS
|
|
103
|
-
);
|
|
104
|
-
} catch (error) {
|
|
105
|
-
if (error.response) {
|
|
106
|
-
const { status, data } = error.response;
|
|
107
|
-
yield put(createGraph.failure({ status, data }));
|
|
108
|
-
} else {
|
|
109
|
-
yield put(createGraph.failure(error.message));
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
graphResp?.status == 202 &&
|
|
114
|
-
(yield put(
|
|
115
|
-
fetchGraph.failure({
|
|
116
|
-
data: {
|
|
117
|
-
graph_hash: graphAccepted.data.graph_hash,
|
|
118
|
-
retry: ++retry,
|
|
119
|
-
},
|
|
120
|
-
})
|
|
121
|
-
));
|
|
122
|
-
|
|
123
|
-
isCancelled = yield cancelled();
|
|
124
|
-
isCancelled && (yield put(createGraph.failure(POLLING_CANCELLED)));
|
|
125
|
-
} while (!isCancelled && graphResp?.status == 202);
|
|
126
|
-
|
|
127
|
-
graphResp?.status == 200 && !isCancelled
|
|
128
|
-
? yield put(
|
|
129
|
-
createGraph.success({
|
|
130
|
-
...graphResp,
|
|
131
|
-
data: { ...graphResp.data, isRedirected },
|
|
132
|
-
})
|
|
133
|
-
)
|
|
134
|
-
: yield put(createGraph.failure({}));
|
|
135
|
-
}
|
|
136
|
-
|
|
137
73
|
export function* createGraphRequestSaga() {
|
|
138
74
|
yield all([
|
|
139
75
|
takeLatest(createGraph.TRIGGER, createGraphSaga),
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import { call, put, takeLatest } from "redux-saga/effects";
|
|
3
|
+
import { apiJsonPost, JSON_OPTS } from "@truedat/core/services/api";
|
|
4
|
+
import { fetchGrantRequestsFilters } from "../routines";
|
|
5
|
+
import { API_GRANT_REQUESTS_FILTERS_SEARCH } from "../api";
|
|
6
|
+
|
|
7
|
+
export function* fetchGrantRequestsFiltersSaga(payload) {
|
|
8
|
+
try {
|
|
9
|
+
yield put(fetchGrantRequestsFilters.request());
|
|
10
|
+
const url = API_GRANT_REQUESTS_FILTERS_SEARCH;
|
|
11
|
+
const filters = _.propOr({}, "payload.filters")(payload);
|
|
12
|
+
const body = { filters };
|
|
13
|
+
const { data } = yield call(apiJsonPost, url, body, JSON_OPTS);
|
|
14
|
+
yield put(fetchGrantRequestsFilters.success({ data }));
|
|
15
|
+
} catch (error) {
|
|
16
|
+
if (error.response) {
|
|
17
|
+
const { status, data } = error.response;
|
|
18
|
+
yield put(fetchGrantRequestsFilters.failure({ status, data }));
|
|
19
|
+
} else {
|
|
20
|
+
yield put(fetchGrantRequestsFilters.failure(error.message));
|
|
21
|
+
}
|
|
22
|
+
} finally {
|
|
23
|
+
yield put(fetchGrantRequestsFilters.fulfill());
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function* fetchGrantRequestsFiltersRequestSaga() {
|
|
28
|
+
yield takeLatest(
|
|
29
|
+
fetchGrantRequestsFilters.TRIGGER,
|
|
30
|
+
fetchGrantRequestsFiltersSaga
|
|
31
|
+
);
|
|
32
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { call, put, debounce } from "redux-saga/effects";
|
|
2
|
+
import { apiJsonPost, JSON_OPTS } from "@truedat/core/services/api";
|
|
3
|
+
import { fetchGrantRequestsSearch } from "../routines";
|
|
4
|
+
import { API_GRANT_REQUESTS_SEARCH } from "../api";
|
|
5
|
+
|
|
6
|
+
export function* fetchGrantRequestsSearchSaga({ payload: body }) {
|
|
7
|
+
try {
|
|
8
|
+
const url = API_GRANT_REQUESTS_SEARCH;
|
|
9
|
+
yield put(fetchGrantRequestsSearch.request(body));
|
|
10
|
+
const { data, headers } = yield call(apiJsonPost, url, body, JSON_OPTS);
|
|
11
|
+
yield put(fetchGrantRequestsSearch.success({ data, headers }));
|
|
12
|
+
} catch (error) {
|
|
13
|
+
if (error.response) {
|
|
14
|
+
const { status, data } = error.response;
|
|
15
|
+
yield put(fetchGrantRequestsSearch.failure({ status, data }));
|
|
16
|
+
} else {
|
|
17
|
+
yield put(fetchGrantRequestsSearch.failure(error.message));
|
|
18
|
+
}
|
|
19
|
+
} finally {
|
|
20
|
+
yield put(fetchGrantRequestsSearch.fulfill());
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function* fetchGrantRequestsSearchRequestSaga() {
|
|
25
|
+
yield debounce(
|
|
26
|
+
200,
|
|
27
|
+
fetchGrantRequestsSearch.TRIGGER,
|
|
28
|
+
fetchGrantRequestsSearchSaga
|
|
29
|
+
);
|
|
30
|
+
}
|
package/src/sagas/fetchGraph.js
CHANGED
|
@@ -1,18 +1,40 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
1
2
|
import { compile } from "path-to-regexp";
|
|
2
|
-
import { call, put, takeLatest } from "redux-saga/effects";
|
|
3
|
+
import { call, cancel, fork, put, take, takeLatest } from "redux-saga/effects";
|
|
3
4
|
import { apiJson, JSON_OPTS } from "@truedat/core/services/api";
|
|
4
|
-
import { fetchGraph } from "../routines";
|
|
5
5
|
import { API_GRAPH } from "../api";
|
|
6
|
+
import { cancelPoll, fetchGraph } from "../routines";
|
|
7
|
+
import { polling } from "./graph/polling";
|
|
6
8
|
|
|
7
9
|
const toApiPath = compile(API_GRAPH);
|
|
8
10
|
|
|
11
|
+
export const selectLineageQuery = _.prop("lineageQuery");
|
|
12
|
+
|
|
9
13
|
export function* fetchGraphSaga({ payload }) {
|
|
10
14
|
try {
|
|
11
15
|
const url = toApiPath(payload);
|
|
12
16
|
yield put(fetchGraph.request(payload));
|
|
13
|
-
const { data } = yield call(apiJson, url, JSON_OPTS);
|
|
14
17
|
|
|
15
|
-
|
|
18
|
+
/* graph is either new (202 accepted) or 200 OK*/
|
|
19
|
+
const graphAcceptedOrFetched = yield call(apiJson, url, JSON_OPTS);
|
|
20
|
+
|
|
21
|
+
if (graphAcceptedOrFetched.status === 202) {
|
|
22
|
+
yield put(fetchGraph.request(graphAcceptedOrFetched));
|
|
23
|
+
// starts the task in the background
|
|
24
|
+
const pollingTask = yield fork(
|
|
25
|
+
polling,
|
|
26
|
+
fetchGraph,
|
|
27
|
+
graphAcceptedOrFetched
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
// wait for the user stop action
|
|
31
|
+
yield take(cancelPoll.TRIGGER);
|
|
32
|
+
// user clicked stop. cancel the background task
|
|
33
|
+
// this will cause the forked bgSync task to jump into its finally block
|
|
34
|
+
yield cancel(pollingTask);
|
|
35
|
+
} else if (graphAcceptedOrFetched.status === 200) {
|
|
36
|
+
yield put(yield put(fetchGraph.success(graphAcceptedOrFetched.data)));
|
|
37
|
+
}
|
|
16
38
|
} catch (error) {
|
|
17
39
|
if (error.response) {
|
|
18
40
|
const { status, data } = error.response;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/* eslint-disable fp/no-mutation */
|
|
2
|
+
/* eslint-disable fp/no-let */
|
|
3
|
+
|
|
4
|
+
import { call, cancelled, delay, put } from "redux-saga/effects";
|
|
5
|
+
import { apiJson, JSON_OPTS } from "@truedat/core/services/api";
|
|
6
|
+
import { compile } from "path-to-regexp";
|
|
7
|
+
import { fetchGraph } from "../../routines";
|
|
8
|
+
import { API_GRAPH_HASH } from "../../api";
|
|
9
|
+
|
|
10
|
+
export const POLLING_CANCELLED = "Polling cancelled!";
|
|
11
|
+
|
|
12
|
+
function* exponentialBackoff(base, power) {
|
|
13
|
+
while (true) {
|
|
14
|
+
yield Math.pow(base, power);
|
|
15
|
+
// eslint-disable-next-line fp/no-mutation, no-param-reassign
|
|
16
|
+
power++;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function* polling(routine, graphAccepted, isRedirected) {
|
|
21
|
+
const backoff = exponentialBackoff(1.2, 0);
|
|
22
|
+
let backoffTime,
|
|
23
|
+
retry = 0,
|
|
24
|
+
graphResp,
|
|
25
|
+
isCancelled = false;
|
|
26
|
+
|
|
27
|
+
do {
|
|
28
|
+
backoffTime = backoff.next().value * 1000;
|
|
29
|
+
yield delay(backoffTime);
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
graphResp = yield call(
|
|
33
|
+
apiJson,
|
|
34
|
+
compile(API_GRAPH_HASH)({
|
|
35
|
+
hash: graphAccepted.data.graph_hash,
|
|
36
|
+
}),
|
|
37
|
+
JSON_OPTS
|
|
38
|
+
);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
if (error.response) {
|
|
41
|
+
const { status, data } = error.response;
|
|
42
|
+
yield put(routine.failure({ status, data }));
|
|
43
|
+
} else {
|
|
44
|
+
yield put(routine.failure(error.message));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
graphResp?.status == 202 &&
|
|
49
|
+
(yield put(
|
|
50
|
+
fetchGraph.failure({
|
|
51
|
+
data: {
|
|
52
|
+
graph_hash: graphAccepted.data.graph_hash,
|
|
53
|
+
retry: ++retry,
|
|
54
|
+
},
|
|
55
|
+
})
|
|
56
|
+
));
|
|
57
|
+
|
|
58
|
+
isCancelled = yield cancelled();
|
|
59
|
+
isCancelled && (yield put(routine.failure(POLLING_CANCELLED)));
|
|
60
|
+
} while (!isCancelled && graphResp?.status == 202);
|
|
61
|
+
|
|
62
|
+
graphResp?.status == 200 && !isCancelled
|
|
63
|
+
? yield put(
|
|
64
|
+
routine.success({
|
|
65
|
+
...graphResp,
|
|
66
|
+
data: { ...graphResp.data, isRedirected: isRedirected || false },
|
|
67
|
+
})
|
|
68
|
+
)
|
|
69
|
+
: yield put(routine.failure({}));
|
|
70
|
+
}
|
package/src/sagas/index.js
CHANGED
|
@@ -23,6 +23,8 @@ import { downloadGrantsRequestSaga } from "./downloadGrants";
|
|
|
23
23
|
import { downloadReferenceDatasetRequestSaga } from "./downloadReferenceDataset";
|
|
24
24
|
import { downloadStructuresRequestSaga } from "./downloadStructures";
|
|
25
25
|
import { fetchGrantFiltersRequestSaga } from "./fetchGrantFilters";
|
|
26
|
+
import { fetchGrantRequestsFiltersRequestSaga } from "./fetchGrantRequestsFilters";
|
|
27
|
+
import { fetchGrantRequestsSearchRequestSaga } from "./fetchGrantRequestsSearch";
|
|
26
28
|
import { fetchGrantRequestRequestSaga } from "./fetchGrantRequest";
|
|
27
29
|
import { fetchGrantRequestsRequestSaga } from "./fetchGrantRequests";
|
|
28
30
|
import { fetchGrantsRequestSaga } from "./fetchGrants";
|
|
@@ -85,6 +87,8 @@ export {
|
|
|
85
87
|
downloadStructuresRequestSaga,
|
|
86
88
|
fetchGrantFiltersRequestSaga,
|
|
87
89
|
fetchGrantRequestRequestSaga,
|
|
90
|
+
fetchGrantRequestsFiltersRequestSaga,
|
|
91
|
+
fetchGrantRequestsSearchRequestSaga,
|
|
88
92
|
fetchGrantRequestsRequestSaga,
|
|
89
93
|
fetchGrantsRequestSaga,
|
|
90
94
|
fetchGraphRequestSaga,
|
|
@@ -148,6 +152,8 @@ export default [
|
|
|
148
152
|
fetchGrantFiltersRequestSaga(),
|
|
149
153
|
fetchGrantRequestRequestSaga(),
|
|
150
154
|
fetchGrantRequestsRequestSaga(),
|
|
155
|
+
fetchGrantRequestsFiltersRequestSaga(),
|
|
156
|
+
fetchGrantRequestsSearchRequestSaga(),
|
|
151
157
|
fetchGrantsRequestSaga(),
|
|
152
158
|
fetchGraphRequestSaga(),
|
|
153
159
|
fetchLineageEventsRequestSaga(),
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { getGrantRequestsAvailableFilters } from "..";
|
|
2
|
+
|
|
3
|
+
const foo = { values: ["foo1", "foo2"] };
|
|
4
|
+
const bar = { values: ["bar1", "bar2"] };
|
|
5
|
+
const baz = { values: ["baz1", "baz2"] };
|
|
6
|
+
|
|
7
|
+
describe("selectors: getGrantRequestsAvailableFilters", () => {
|
|
8
|
+
const grantRequestsFilters = { foo, bar, baz };
|
|
9
|
+
const grantRequestsActiveFilters = { baz: [] };
|
|
10
|
+
const state = { grantRequestsFilters, grantRequestsActiveFilters };
|
|
11
|
+
|
|
12
|
+
it("should return the keys of the grant request filters which are not currently active", () => {
|
|
13
|
+
expect(getGrantRequestsAvailableFilters(state)).toEqual(["foo", "bar"]);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { getGrantRequestsFilterTypes } from "..";
|
|
2
|
+
|
|
3
|
+
const foo = { type: "fooType", values: ["foo1", "foo2"] };
|
|
4
|
+
const bar = { type: "barType", values: ["bar1", "bar2"] };
|
|
5
|
+
const bay = { values: ["bay1"] };
|
|
6
|
+
|
|
7
|
+
describe("selectors: getGrantRequestsFilterTypes", () => {
|
|
8
|
+
const state = { grantRequestsFilters: { foo, bar, bay } };
|
|
9
|
+
|
|
10
|
+
const expected = {
|
|
11
|
+
foo: "fooType",
|
|
12
|
+
bar: "barType",
|
|
13
|
+
bay: undefined,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
it("should return a map with filter types", () => {
|
|
17
|
+
expect(getGrantRequestsFilterTypes(state)).toEqual(expected);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import {
|
|
3
|
+
defaultGrantRequestsSearchTableColumns,
|
|
4
|
+
getGrantRequestsSearchColumns,
|
|
5
|
+
} from "..";
|
|
6
|
+
|
|
7
|
+
describe("selectors: getGrantRequestsSearchColumns", () => {
|
|
8
|
+
const state = {};
|
|
9
|
+
|
|
10
|
+
it("get default grant requests columns", () => {
|
|
11
|
+
const columns = getGrantRequestsSearchColumns(state);
|
|
12
|
+
expect(_.map("name")(columns)).toEqual(
|
|
13
|
+
_.flow(_.map("name"))(defaultGrantRequestsSearchTableColumns)
|
|
14
|
+
);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("get specified columns in reducer", () => {
|
|
18
|
+
const grantRequestsSearchColumns = [
|
|
19
|
+
{
|
|
20
|
+
name: "foo",
|
|
21
|
+
fieldDecorator: _.identity,
|
|
22
|
+
},
|
|
23
|
+
];
|
|
24
|
+
const columns = getGrantRequestsSearchColumns({
|
|
25
|
+
...state,
|
|
26
|
+
grantRequestsSearchColumns,
|
|
27
|
+
});
|
|
28
|
+
expect(columns).toStrictEqual(grantRequestsSearchColumns);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { getGrantRequestsSearchQuery } from "..";
|
|
2
|
+
|
|
3
|
+
const grantRequestSearchQuery = { query: "foo" };
|
|
4
|
+
const grantRequestsActiveFilters = { foo: "bar", bar: "baz" };
|
|
5
|
+
const grantRequestsDefaultFilters = { status: "some status" };
|
|
6
|
+
|
|
7
|
+
describe("selectors: getGrantRequestsSearchQuery", () => {
|
|
8
|
+
const state = {
|
|
9
|
+
grantRequestsDefaultFilters,
|
|
10
|
+
grantRequestsActiveFilters,
|
|
11
|
+
grantRequestSearchQuery,
|
|
12
|
+
};
|
|
13
|
+
it("get query merged with defaults", () => {
|
|
14
|
+
const res = getGrantRequestsSearchQuery({
|
|
15
|
+
grantRequestSearchQuery,
|
|
16
|
+
grantRequestsDefaultFilters,
|
|
17
|
+
grantRequestsActiveFilters,
|
|
18
|
+
});
|
|
19
|
+
expect(res).toEqual({
|
|
20
|
+
query: "foo",
|
|
21
|
+
filters: { foo: "bar", bar: "baz", status: "some status" },
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { getGrantRequestsSelectedFilterActiveValues } from "..";
|
|
2
|
+
|
|
3
|
+
const foo = ["foo1", "foo2"];
|
|
4
|
+
const bar = ["bar1", "bar2"];
|
|
5
|
+
const baz = ["baz1", "baz2"];
|
|
6
|
+
|
|
7
|
+
describe("selectors: getGrantRequestsSelectedFilterActiveValues", () => {
|
|
8
|
+
const grantRequestsActiveFilters = { foo: ["foo1"] };
|
|
9
|
+
const grantRequestsSelectedFilter = "foo";
|
|
10
|
+
const state = { grantRequestsActiveFilters, grantRequestsSelectedFilter };
|
|
11
|
+
|
|
12
|
+
it("should return the active values of the currently selected grant filters", () => {
|
|
13
|
+
expect(getGrantRequestsSelectedFilterActiveValues(state)).toEqual(["foo1"]);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { getGrantRequestsSelectedFilterValues } from "../../../../dd/src/selectors";
|
|
2
|
+
|
|
3
|
+
const foo = { values: ["foo1", "foo2"] };
|
|
4
|
+
const bar = { values: ["bar1", "bar2"] };
|
|
5
|
+
const baz = { values: ["baz1", "baz2"] };
|
|
6
|
+
|
|
7
|
+
describe("selectors: getGrantRequestsSelectedFilterValues", () => {
|
|
8
|
+
const grantRequestsSelectedFilter = "foo";
|
|
9
|
+
const grantRequestsFilters = { foo, bar, baz };
|
|
10
|
+
const state = { grantRequestsSelectedFilter, grantRequestsFilters };
|
|
11
|
+
|
|
12
|
+
it("should return the values of the currently selected Grant Request filters", () => {
|
|
13
|
+
expect(getGrantRequestsSelectedFilterValues(state)).toEqual(foo.values);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { getGrantRequestsSelectedFilters } from "..";
|
|
2
|
+
|
|
3
|
+
describe("selectors: getGrantRequestsSelectedFilters", () => {
|
|
4
|
+
const grantRequestsActiveFilters = {
|
|
5
|
+
baz: ["baz1", "baz2"],
|
|
6
|
+
must_not_approved_by: ["approver 1"],
|
|
7
|
+
};
|
|
8
|
+
const state = { grantRequestsActiveFilters };
|
|
9
|
+
|
|
10
|
+
it("should return the keys of the structure filters which are currently active", () => {
|
|
11
|
+
expect(getGrantRequestsSelectedFilters(state)).toEqual(["baz"]);
|
|
12
|
+
});
|
|
13
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import { createSelector } from "reselect";
|
|
3
|
+
import { getGrantRequestsSelectedFilters } from "./getGrantRequestsSelectedFilters";
|
|
4
|
+
|
|
5
|
+
const getGrantRequestsFilters = _.propOr({}, "grantRequestsFilters");
|
|
6
|
+
|
|
7
|
+
export const getGrantRequestsAvailableFilters = createSelector(
|
|
8
|
+
getGrantRequestsFilters,
|
|
9
|
+
getGrantRequestsSelectedFilters,
|
|
10
|
+
(grantRequestsFilters, grantRequestsSelectedFilters) => {
|
|
11
|
+
return _.flow(
|
|
12
|
+
_.omitBy(({ values }) => _.size(values) < 2),
|
|
13
|
+
_.keys,
|
|
14
|
+
_.without(grantRequestsSelectedFilters)
|
|
15
|
+
)(grantRequestsFilters);
|
|
16
|
+
}
|
|
17
|
+
);
|