@truedat/dd 4.28.4 → 4.28.8

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.28.8] 2021-09-20
4
+
5
+ ### Added
6
+
7
+ - [TD-3970] `StructureFieldsDropdown` support for cast type and modifier
8
+
9
+ ## [4.28.6] 2021-09-20
10
+
11
+ ### Added
12
+
13
+ - [TD-3950] My grants table
14
+
3
15
  ## [4.28.4] 2021-09-15
4
16
 
5
17
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/dd",
3
- "version": "4.28.4",
3
+ "version": "4.28.8",
4
4
  "description": "Truedat Web Data Dictionary",
5
5
  "sideEffects": false,
6
6
  "jsnext:main": "src/index.js",
@@ -16,8 +16,8 @@
16
16
  "scripts": {
17
17
  "clean": "rimraf yarn-error.log",
18
18
  "debug": "node --inspect-brk node_modules/.bin/jest --runInBand",
19
- "test": "jest --coverage",
20
- "test:watch": "jest --watch",
19
+ "test": "TZ=UTC jest --coverage",
20
+ "test:watch": "TZ=UTC jest --watch",
21
21
  "eslint": "eslint src/**",
22
22
  "eslint:fix": "eslint --fix src/**"
23
23
  },
@@ -34,7 +34,7 @@
34
34
  "@testing-library/jest-dom": "^5.14.1",
35
35
  "@testing-library/react": "^12.0.0",
36
36
  "@testing-library/user-event": "^13.2.1",
37
- "@truedat/test": "4.28.4",
37
+ "@truedat/test": "4.28.8",
38
38
  "babel-jest": "^27.0.6",
39
39
  "babel-plugin-dynamic-import-node": "^2.3.3",
40
40
  "babel-plugin-lodash": "^3.3.4",
@@ -84,9 +84,9 @@
84
84
  ]
85
85
  },
86
86
  "dependencies": {
87
- "@truedat/auth": "4.28.4",
88
- "@truedat/core": "4.28.4",
89
- "@truedat/df": "4.28.4",
87
+ "@truedat/auth": "4.28.8",
88
+ "@truedat/core": "4.28.8",
89
+ "@truedat/df": "4.28.8",
90
90
  "lodash": "^4.17.21",
91
91
  "path-to-regexp": "^1.7.0",
92
92
  "prop-types": "^15.7.2",
@@ -107,5 +107,5 @@
107
107
  "react-dom": ">= 16.8.6 < 17",
108
108
  "semantic-ui-react": ">= 0.88.2 < 2.1"
109
109
  },
110
- "gitHead": "c00da6ddc499bca47a5a0e108c8ec94988293907"
110
+ "gitHead": "33a7f73dd4ffbd31d1df270c8453c3a876412990"
111
111
  }
package/src/api.js CHANGED
@@ -11,6 +11,8 @@ const API_GRANTS_SEARCH = "/api/grants/search";
11
11
  const API_GRAPH = "/api/graphs/:id";
12
12
  const API_GRAPHS = "/api/graphs";
13
13
  const API_GRAPH_CSV = "/api/graphs/csv";
14
+ const API_MY_GRANT_FILTERS_SEARCH = "/api/grant_filters/search/mine";
15
+ const API_MY_GRANTS_SEARCH = "/api/grants/search/mine";
14
16
  const API_NODE = "/api/nodes/:id";
15
17
  const API_NODES = "/api/nodes";
16
18
  const API_PROFILE_GROUP = "/api/profile_execution_groups/:id";
@@ -48,6 +50,8 @@ export {
48
50
  API_GRAPH,
49
51
  API_GRAPHS,
50
52
  API_GRAPH_CSV,
53
+ API_MY_GRANTS_SEARCH,
54
+ API_MY_GRANT_FILTERS_SEARCH,
51
55
  API_NODE,
52
56
  API_NODES,
53
57
  API_PROFILE_GROUP,
@@ -9,6 +9,8 @@ import {
9
9
  STRUCTURE_TYPES,
10
10
  SYSTEMS,
11
11
  GRANTS_REQUESTS_CHECKOUT,
12
+ GRANTS,
13
+ MY_GRANTS,
12
14
  } from "@truedat/core/routes";
13
15
  import ProfileExecution from "./ProfileExecution";
14
16
  import ProfileExecutionBreadcrumbs from "./ProfileExecutionBreadcrumbs";
@@ -24,6 +26,9 @@ import StructureTypesRoutes from "./StructureTypesRoutes";
24
26
  import SystemsRoutes from "./SystemsRoutes";
25
27
  import SystemsLoader from "./SystemsLoader";
26
28
  import StructureGrantCartCheckout from "./StructureGrantCartCheckout";
29
+ import GrantFiltersLoader from "./GrantFiltersLoader";
30
+ import GrantsLoader from "./GrantsLoader";
31
+ import Grants from "./Grants";
27
32
 
28
33
  const TemplatesLoader = React.lazy(() =>
29
34
  import("@truedat/df/templates/components/TemplatesLoader")
@@ -69,6 +74,28 @@ export const DictionaryRoutes = () => {
69
74
  path={GRANTS_REQUESTS_CHECKOUT}
70
75
  render={() => <StructureGrantCartCheckout />}
71
76
  />
77
+ <Route
78
+ path={GRANTS}
79
+ render={() => (
80
+ <>
81
+ <GrantsLoader />
82
+ <GrantFiltersLoader />
83
+ <Grants />
84
+ </>
85
+ )}
86
+ />
87
+ <Route
88
+ path={MY_GRANTS}
89
+ render={() => {
90
+ return (
91
+ <>
92
+ <GrantsLoader onlyMyGrants />
93
+ <GrantFiltersLoader onlyMyGrants />
94
+ <Grants onlyMyGrants />
95
+ </>
96
+ );
97
+ }}
98
+ />
72
99
  <Route path={STRUCTURE_TAGS} render={() => <StructureTagsRoutes />} />
73
100
  <Route path={STRUCTURE_TYPES} render={() => <StructureTypesRoutes />} />
74
101
  <Route
@@ -27,6 +27,7 @@ export const EditDomainForm = ({ title, updateStructure, structure }) => {
27
27
  <Container style={{ width: "400px" }}>
28
28
  <Header as="h2" content={title} />
29
29
  <DomainDropdownSelector
30
+ clearable={false}
30
31
  onChange={handleDomainSelected}
31
32
  name="domain"
32
33
  value={domainId || structure?.domain_id}
@@ -4,13 +4,18 @@ import { FiltersLoader } from "@truedat/core/components";
4
4
  import { makeActiveFiltersSelector } from "@truedat/core/selectors";
5
5
  import { clearGrantFilters, fetchGrantFilters } from "../routines";
6
6
 
7
- const mapDispatchToProps = (dispatch) =>
8
- bindActionCreators(
9
- { clearFilters: clearGrantFilters, fetchFilters: fetchGrantFilters },
7
+ const mapDispatchToProps = (dispatch, props) => {
8
+ const { onlyMyGrants } = props;
9
+ const fetchMyGrantsFilters = (payload) => {
10
+ return fetchGrantFilters({ payload, onlyMyGrants });
11
+ };
12
+ return bindActionCreators(
13
+ { clearFilters: clearGrantFilters, fetchFilters: fetchMyGrantsFilters },
10
14
  dispatch
11
15
  );
16
+ };
12
17
 
13
- const makeMapStateToProps = () => {
18
+ const makeMapStateToProps = ({}) => {
14
19
  const activeFiltersSelector = makeActiveFiltersSelector("grantActiveFilters");
15
20
  const mapStateToProps = (state, props) => {
16
21
  return {
@@ -13,14 +13,18 @@ import GrantsLabelResults from "./GrantsLabelResults";
13
13
  import GrantSelectedFilters from "./GrantSelectedFilters";
14
14
  import GrantsPagination from "./GrantsPagination";
15
15
 
16
- export const Grants = ({ grantsLoading }) => (
16
+ export const Grants = ({ grantsLoading, onlyMyGrants }) => (
17
17
  <Segment>
18
18
  <Header as="h2">
19
19
  <Icon circular name="cogs" />
20
20
  <Header.Content>
21
- <FormattedMessage id="grants.header" />
21
+ <FormattedMessage
22
+ id={onlyMyGrants ? "grants.mine.header" : "grants.header"}
23
+ />
22
24
  <Header.Subheader>
23
- <FormattedMessage id="grants.subheader" />
25
+ <FormattedMessage
26
+ id={onlyMyGrants ? "grants.mine.subheader" : "grants.subheader"}
27
+ />
24
28
  </Header.Subheader>
25
29
  </Header.Content>
26
30
  </Header>
@@ -34,7 +38,7 @@ export const Grants = ({ grantsLoading }) => (
34
38
  </Dimmer>
35
39
  </Dimmer.Dimmable>
36
40
  <GrantsLabelResults />
37
- <GrantsTable />
41
+ <GrantsTable onlyMyGrants={onlyMyGrants} />
38
42
  <GrantsPagination />
39
43
  </Segment>
40
44
  </Segment>
@@ -42,6 +46,7 @@ export const Grants = ({ grantsLoading }) => (
42
46
 
43
47
  Grants.propTypes = {
44
48
  grantsLoading: PropTypes.bool,
49
+ onlyMyGrants: PropTypes.bool,
45
50
  };
46
51
 
47
52
  const mapStateToProps = ({ grantsLoading }) => ({ grantsLoading });
@@ -4,15 +4,20 @@ import { connect } from "react-redux";
4
4
  import { makeSearchQuerySelector } from "@truedat/core/selectors";
5
5
  import { clearGrants, fetchGrants } from "../routines";
6
6
 
7
- export const GrantsLoader = ({ clearGrants, fetchGrants, payload }) => {
7
+ export const GrantsLoader = ({
8
+ clearGrants,
9
+ fetchGrants,
10
+ payload,
11
+ onlyMyGrants = false,
12
+ }) => {
8
13
  useEffect(() => {
9
14
  return () => {
10
15
  clearGrants();
11
16
  };
12
17
  }, [clearGrants]);
13
18
  useEffect(() => {
14
- fetchGrants(payload);
15
- }, [payload, fetchGrants]);
19
+ fetchGrants({ payload, onlyMyGrants });
20
+ }, [payload, fetchGrants, onlyMyGrants]);
16
21
  return null;
17
22
  };
18
23
 
@@ -20,6 +25,7 @@ GrantsLoader.propTypes = {
20
25
  clearGrants: PropTypes.func,
21
26
  fetchGrants: PropTypes.func,
22
27
  payload: PropTypes.object,
28
+ onlyMyGrants: PropTypes.bool,
23
29
  };
24
30
 
25
31
  const makeMapStateToProps = () => {
@@ -102,8 +102,8 @@ GrantsTable.propTypes = {
102
102
  grantsSorting: PropTypes.array,
103
103
  };
104
104
 
105
- const mapStateToProps = (state) => ({
106
- columns: getGrantsTableColumns(state),
105
+ const mapStateToProps = (state, { onlyMyGrants }) => ({
106
+ columns: getGrantsTableColumns({ ...state, onlyMyGrants }),
107
107
  grants: state.grants,
108
108
  grantsLoading: state.grantsLoading,
109
109
  grantsSorting: _.path("grantQuery.sort")(state),
@@ -6,52 +6,6 @@ import { OptionGroup } from "@truedat/core/components";
6
6
  import { useIntl } from "react-intl";
7
7
  import { getStructuresFields } from "../selectors/getStructuresFields";
8
8
 
9
- const withFilters = (structureFields, filters) =>
10
- filters
11
- ? _.filter((f) =>
12
- _.every.convert({ cap: false })((v, k) => v.includes(_.prop(k)(f)))(
13
- filters
14
- )
15
- )(structureFields)
16
- : structureFields;
17
-
18
- const getOptions = (structureFields, formatMessage) =>
19
- _.flow(
20
- _.sortBy(_.flow(_.prop("name"), _.toLower)),
21
- _.groupBy("label"),
22
- _.reduce.convert({ cap: false })(
23
- (acc, v, k) =>
24
- _.isNil(k) || k === "undefined"
25
- ? [...acc, ...v]
26
- : [...acc, { label: k, options: v }],
27
- []
28
- ),
29
- _.map.convert({ cap: false })((field) =>
30
- toFieldOption(field, formatMessage)
31
- )
32
- )(structureFields);
33
-
34
- const toFieldOption = (field, formatMessage) =>
35
- _.has("options")(field)
36
- ? {
37
- icon: "table",
38
- label: field.label,
39
- options: _.map((field) => formatOption(field, formatMessage))(
40
- field.options
41
- ),
42
- }
43
- : formatOption(field, formatMessage);
44
-
45
- const formatOption = (field, formatMessage) => ({
46
- key: field.data_structure_id,
47
- text: field.name,
48
- value: field.data_structure_id,
49
- icon: formatMessage({
50
- id: `field_type.icon.${field.field_type}`,
51
- defaultMessage: "hashtag",
52
- }),
53
- });
54
-
55
9
  export const StructureFieldsDropdown = ({
56
10
  label,
57
11
  inline,
@@ -59,13 +13,77 @@ export const StructureFieldsDropdown = ({
59
13
  structureFields,
60
14
  filters,
61
15
  value,
16
+ typeCastModifiers,
62
17
  }) => {
63
18
  const { formatMessage } = useIntl();
64
- const onClick = ({ value }) => {
19
+ const onClick = ({ value, modifier }) => {
65
20
  const field = _.find(_.propEq("data_structure_id", value))(structureFields);
66
- onSelectField(field);
21
+ onSelectField(field, modifier);
67
22
  };
68
23
 
24
+ const formatOption = (field) => {
25
+ const modifier = _.find({sourceType: field.field_type})(typeCastModifiers)
26
+ if(modifier){
27
+ return({
28
+ key: field.data_structure_id,
29
+ text: `${field.name} (${formatMessage({
30
+ id: `filtersGrid.field.modifier.${modifier.name}`
31
+ })})`,
32
+ modifier,
33
+ value: field.data_structure_id,
34
+ icon: formatMessage({
35
+ id: `field_type.icon.${field.field_type}`,
36
+ defaultMessage: "hashtag",
37
+ }),
38
+ })
39
+ }
40
+
41
+ return({
42
+ key: field.data_structure_id,
43
+ text: field.name,
44
+ value: field.data_structure_id,
45
+ icon: formatMessage({
46
+ id: `field_type.icon.${field.field_type}`,
47
+ defaultMessage: "hashtag",
48
+ }),
49
+ })
50
+
51
+ };
52
+ const toFieldOption = (field) =>
53
+ _.has("options")(field)
54
+ ? {
55
+ icon: "table",
56
+ label: field.label,
57
+ options: _.map(formatOption)(field.options),
58
+ }
59
+ : formatOption(field);
60
+
61
+ const getOptions = _.flow(
62
+ _.sortBy(_.flow(_.prop("name"), _.toLower)),
63
+ _.groupBy("label"),
64
+ _.reduce.convert({ cap: false })(
65
+ (acc, v, k) =>
66
+ _.isNil(k) || k === "undefined"
67
+ ? [...acc, ...v]
68
+ : [...acc, { label: k, options: v }],
69
+ []
70
+ ),
71
+ _.map(toFieldOption),
72
+ )
73
+
74
+ const castSourceTypes = _.flow(
75
+ _.map(({sourceType})=>sourceType),
76
+ _.uniq
77
+ )(typeCastModifiers)
78
+ const options = _.flow(
79
+ _.filter((f) =>
80
+ _.every.convert({ cap: false })((v, k) => v.includes(_.prop(k)(f)))(
81
+ filters
82
+ ) || castSourceTypes.includes(f.field_type)
83
+ ),
84
+ getOptions
85
+ )(structureFields);
86
+
69
87
  return (
70
88
  <OptionGroup
71
89
  inline={inline}
@@ -75,7 +93,7 @@ export const StructureFieldsDropdown = ({
75
93
  placeholder={formatMessage({
76
94
  id: "structureFields.dropdown.placeholder",
77
95
  })}
78
- options={getOptions(withFilters(structureFields, filters), formatMessage)}
96
+ options={options}
79
97
  />
80
98
  );
81
99
  };
@@ -87,6 +105,7 @@ StructureFieldsDropdown.propTypes = {
87
105
  structureFields: PropTypes.array,
88
106
  onSelectField: PropTypes.func,
89
107
  filters: PropTypes.object,
108
+ typeCastModifiers: PropTypes.array,
90
109
  };
91
110
 
92
111
  const mapStateToProps = (state, props) => ({
@@ -52,7 +52,7 @@ describe("<ValueConditionStructure />", () => {
52
52
  data_structure_id: 2,
53
53
  field_type: "string",
54
54
  label: "bar",
55
- });
55
+ }, undefined);
56
56
  });
57
57
  });
58
58
  });
@@ -17,6 +17,14 @@ exports[`<DictionaryRoutes /> matches the latest snapshot 1`] = `
17
17
  path="/grants_requests/checkout"
18
18
  render={[Function]}
19
19
  />
20
+ <Route
21
+ path="/grants"
22
+ render={[Function]}
23
+ />
24
+ <Route
25
+ path="/my_grants"
26
+ render={[Function]}
27
+ />
20
28
  <Route
21
29
  path="/structure_tags"
22
30
  render={[Function]}
@@ -7,7 +7,10 @@ exports[`<GrantsLoader /> matches the latest snapshot 1`] = `
7
7
  [MockFunction] {
8
8
  "calls": Array [
9
9
  Array [
10
- Object {},
10
+ Object {
11
+ "onlyMyGrants": false,
12
+ "payload": Object {},
13
+ },
11
14
  ],
12
15
  ],
13
16
  "results": Array [
@@ -25,7 +25,7 @@ exports[`<GrantsTable /> matches the latest snapshot 1`] = `
25
25
  <TableHeaderCell
26
26
  as="th"
27
27
  className=""
28
- content="data_structure_version"
28
+ content="grant.header.data_structure_version"
29
29
  key="1"
30
30
  onClick={[Function]}
31
31
  sorted="ascending"
@@ -1,7 +1,6 @@
1
1
  import DictionaryRoutes from "./DictionaryRoutes";
2
2
  import FilteredNav from "./FilteredNav";
3
3
  import GrantSelectedFilters from "./GrantSelectedFilters";
4
- import GrantsRoutes from "./GrantsRoutes";
5
4
  import GrantsTable from "./GrantsTable";
6
5
  import LineageDefs from "./LineageDefs";
7
6
  import LineageDownloadButton from "./LineageDownloadButton";
@@ -57,7 +56,6 @@ import ValueConditionStructure from "./ValueConditionStructure";
57
56
  export {
58
57
  DictionaryRoutes,
59
58
  FilteredNav,
60
- GrantsRoutes,
61
59
  GrantSelectedFilters,
62
60
  GrantsTable,
63
61
  LineageDefs,
@@ -34,15 +34,17 @@ export default {
34
34
  "grant.cell.end_date.null": "No expiration set",
35
35
  "grant.cell.end_date.same_user": "It's you",
36
36
  "grant.header.user": "Account",
37
- "grant.header.data_structure": "Structure",
37
+ "grant.header.data_structure_version": "Structure",
38
38
  "grant.header.end_date": "Expires",
39
39
  "grant.header.start_date": "Access granted",
40
40
  "grant.updated_at": "Last changed",
41
41
  "grants.header": "Grants",
42
+ "grants.mine.header": "My grants",
43
+ "grants.mine.subheader": "Structures granted to me",
42
44
  "grants.retrieved.results": "{count} grants found",
43
- "grants.search.placeholder": "Enter a search...",
45
+ "grants.search.placeholder": "Search grants...",
44
46
  "grants.search.results.empty": "No grants found",
45
- "grants.subheader": "Grants",
47
+ "grants.subheader": "Granted access to structures",
46
48
  "filters.with_content.raw.true": "Extra Info",
47
49
  "graph.actions.download.popup": "Select a graph",
48
50
  "graph.actions.download.tooltip": "Download to csv",
@@ -34,16 +34,18 @@ export default {
34
34
  "grant.cell.direct_access": "Acceso Directo",
35
35
  "grant.cell.end_date.null": "Sin fecha de expiración",
36
36
  "grant.cell.end_date.same_user": "Eres tú",
37
- "grant.header.data_structure": "Estructura",
37
+ "grant.header.data_structure_version": "Estructura",
38
38
  "grant.header.end_date": "Expira",
39
39
  "grant.header.start_date": "Acceso desde",
40
40
  "grant.header.user": "Cuenta",
41
41
  "grant.updated_at": "Actualizada",
42
- "grants.header": "Grants",
42
+ "grants.header": "Accesos",
43
+ "grants.mine.header": "Mis accesos",
44
+ "grants.mine.subheader": "Estructuras a las que tengo acceso",
43
45
  "grants.retrieved.results": "{count} grants encontrados",
44
- "grants.search.placeholder": "Introduce una busqueda...",
46
+ "grants.search.placeholder": "Buscar accesos...",
45
47
  "grants.search.results.empty": "Ningún grant encontrado",
46
- "grants.subheader": "Grants",
48
+ "grants.subheader": "Accesos a estructuras",
47
49
  "filters.with_content.raw.true": "Contiene información extra",
48
50
  "graph.actions.download.popup": "Seleccione un grafo",
49
51
  "graph.actions.download.tooltip": "Descargar en csv",
@@ -5,6 +5,7 @@ import {
5
5
  removeGrantFilter,
6
6
  resetGrantFilters,
7
7
  toggleGrantFilterValue,
8
+ clearGrantFilters,
8
9
  } from "../routines";
9
10
 
10
11
  export const initialState = {};
@@ -26,6 +27,7 @@ export const grantActiveFilters = (state = initialState, { type, payload }) => {
26
27
  const { filter } = payload;
27
28
  return _.omit(filter)(state);
28
29
  }
30
+ case clearGrantFilters.TRIGGER:
29
31
  case resetGrantFilters.TRIGGER: {
30
32
  return initialState;
31
33
  }
@@ -0,0 +1,97 @@
1
+ import { debounce } from "redux-saga/effects";
2
+ import { testSaga } from "redux-saga-test-plan";
3
+ import { apiJsonPost, JSON_OPTS } from "@truedat/core/services/api";
4
+ import {
5
+ fetchGrantFiltersRequestSaga,
6
+ fetchGrantFiltersSaga,
7
+ } from "../fetchGrantFilters";
8
+ import { fetchGrantFilters } from "../../routines";
9
+ import {
10
+ API_GRANT_FILTERS_SEARCH,
11
+ API_MY_GRANT_FILTERS_SEARCH,
12
+ } from "../../api";
13
+
14
+ describe("sagas: fetchGrantFiltersRequestSaga", () => {
15
+ it("should invoke fetchGrantFiltersSaga on trigger", () => {
16
+ expect(() => {
17
+ testSaga(fetchGrantFiltersRequestSaga)
18
+ .next()
19
+ .takeLatest(fetchGrantFilters.TRIGGER, fetchGrantFiltersSaga)
20
+ .finish()
21
+ .isDone();
22
+ }).not.toThrow();
23
+ });
24
+
25
+ it("should throw exception if an unhandled action is received", () => {
26
+ expect(() => {
27
+ testSaga(fetchGrantFiltersRequestSaga)
28
+ .next()
29
+ .takeLatest("FOO", fetchGrantFiltersSaga);
30
+ }).toThrow();
31
+ });
32
+ });
33
+
34
+ describe("sagas: fetchGrantFiltersSaga", () => {
35
+ const grantFilters = { filters: ["filtro1", "filtro2"] };
36
+ const data = {
37
+ collection: [
38
+ { id: 1, user_id: 1, data_structure_id: 1, description: "desc1" },
39
+ { id: 2, user_id: 1, data_structure_id: 1, description: "desc2" },
40
+ ],
41
+ };
42
+ const headers = {
43
+ "x-total-count": "123",
44
+ };
45
+
46
+ it("should put a success action when a response is returned", () => {
47
+ expect(() => {
48
+ testSaga(fetchGrantFiltersSaga, { payload: { payload: grantFilters } })
49
+ .next()
50
+ .put(fetchGrantFilters.request())
51
+ .next()
52
+ .call(apiJsonPost, API_GRANT_FILTERS_SEARCH, grantFilters, JSON_OPTS)
53
+ .next({ data })
54
+ .put(fetchGrantFilters.success({ data }))
55
+ .next()
56
+ .put(fetchGrantFilters.fulfill())
57
+ .next()
58
+ .isDone();
59
+ }).not.toThrow();
60
+ });
61
+
62
+ it("should call API_MY_GRANTS_URL when onlyMyGrants is true", () => {
63
+ expect(() => {
64
+ testSaga(fetchGrantFiltersSaga, {
65
+ payload: { payload: grantFilters, onlyMyGrants: true },
66
+ })
67
+ .next()
68
+ .put(fetchGrantFilters.request())
69
+ .next()
70
+ .call(
71
+ apiJsonPost,
72
+ API_MY_GRANT_FILTERS_SEARCH,
73
+ grantFilters,
74
+ JSON_OPTS
75
+ );
76
+ }).not.toThrow();
77
+ });
78
+
79
+ it("should put a failure action when the call returns an error", () => {
80
+ const message = "Request failed";
81
+ const error = { message };
82
+
83
+ expect(() => {
84
+ testSaga(fetchGrantFiltersSaga, { payload: { payload: grantFilters } })
85
+ .next()
86
+ .put(fetchGrantFilters.request())
87
+ .next()
88
+ .call(apiJsonPost, API_GRANT_FILTERS_SEARCH, grantFilters, JSON_OPTS)
89
+ .throw(error)
90
+ .put(fetchGrantFilters.failure(message))
91
+ .next()
92
+ .put(fetchGrantFilters.fulfill())
93
+ .next()
94
+ .isDone();
95
+ }).not.toThrow();
96
+ });
97
+ });
@@ -3,7 +3,7 @@ import { testSaga } from "redux-saga-test-plan";
3
3
  import { apiJsonPost, JSON_OPTS } from "@truedat/core/services/api";
4
4
  import { fetchGrantsRequestSaga, fetchGrantsSaga } from "../fetchGrants";
5
5
  import { fetchGrants } from "../../routines";
6
- import { API_GRANTS_SEARCH } from "../../api";
6
+ import { API_GRANTS_SEARCH, API_MY_GRANTS_SEARCH } from "../../api";
7
7
 
8
8
  describe("sagas: fetchGrantsRequestSaga", () => {
9
9
  it("should invoke fetchGrantsSaga on trigger", () => {
@@ -39,7 +39,7 @@ describe("sagas: fetchGrantsSaga", () => {
39
39
 
40
40
  it("should put a success action when a response is returned", () => {
41
41
  expect(() => {
42
- testSaga(fetchGrantsSaga, { payload: grantQuery })
42
+ testSaga(fetchGrantsSaga, { payload: { payload: grantQuery } })
43
43
  .next()
44
44
  .put(fetchGrants.request(grantQuery))
45
45
  .next()
@@ -53,12 +53,24 @@ describe("sagas: fetchGrantsSaga", () => {
53
53
  }).not.toThrow();
54
54
  });
55
55
 
56
+ it("should call API_MY_GRANTS_URL when onlyMyGrants is true", () => {
57
+ expect(() => {
58
+ testSaga(fetchGrantsSaga, {
59
+ payload: { payload: grantQuery, onlyMyGrants: true },
60
+ })
61
+ .next()
62
+ .put(fetchGrants.request(grantQuery))
63
+ .next()
64
+ .call(apiJsonPost, API_MY_GRANTS_SEARCH, grantQuery, JSON_OPTS);
65
+ }).not.toThrow();
66
+ });
67
+
56
68
  it("should put a failure action when the call returns an error", () => {
57
69
  const message = "Request failed";
58
70
  const error = { message };
59
71
 
60
72
  expect(() => {
61
- testSaga(fetchGrantsSaga, { payload: grantQuery })
73
+ testSaga(fetchGrantsSaga, { payload: { payload: grantQuery } })
62
74
  .next()
63
75
  .put(fetchGrants.request(grantQuery))
64
76
  .next()
@@ -2,13 +2,15 @@ import _ from "lodash/fp";
2
2
  import { call, put, takeLatest } from "redux-saga/effects";
3
3
  import { apiJsonPost, JSON_OPTS } from "@truedat/core/services/api";
4
4
  import { fetchGrantFilters } from "../routines";
5
- import { API_GRANT_FILTERS_SEARCH } from "../api";
5
+ import { API_GRANT_FILTERS_SEARCH, API_MY_GRANT_FILTERS_SEARCH } from "../api";
6
6
 
7
- export function* fetchGrantFiltersSaga(payload) {
7
+ export function* fetchGrantFiltersSaga({ payload: { payload, onlyMyGrants } }) {
8
8
  try {
9
9
  yield put(fetchGrantFilters.request());
10
- const url = API_GRANT_FILTERS_SEARCH;
11
- const filters = _.propOr({}, "payload.filters")(payload);
10
+ const url = onlyMyGrants
11
+ ? API_MY_GRANT_FILTERS_SEARCH
12
+ : API_GRANT_FILTERS_SEARCH;
13
+ const filters = _.propOr({}, "filters")(payload);
12
14
  const body = { filters };
13
15
  const { data } = yield call(apiJsonPost, url, body, JSON_OPTS);
14
16
  yield put(fetchGrantFilters.success({ data }));
@@ -1,11 +1,11 @@
1
1
  import { call, put, debounce } from "redux-saga/effects";
2
2
  import { apiJsonPost, JSON_OPTS } from "@truedat/core/services/api";
3
3
  import { fetchGrants } from "../routines";
4
- import { API_GRANTS_SEARCH } from "../api";
4
+ import { API_GRANTS_SEARCH, API_MY_GRANTS_SEARCH } from "../api";
5
5
 
6
- export function* fetchGrantsSaga({ payload: body }) {
6
+ export function* fetchGrantsSaga({ payload: { payload: body, onlyMyGrants } }) {
7
7
  try {
8
- const url = API_GRANTS_SEARCH;
8
+ const url = onlyMyGrants ? API_MY_GRANTS_SEARCH : API_GRANTS_SEARCH;
9
9
  yield put(fetchGrants.request(body));
10
10
  const { data, headers } = yield call(apiJsonPost, url, body, JSON_OPTS);
11
11
  yield put(fetchGrants.success({ data, headers }));
@@ -226,14 +226,16 @@ export const defaultGrantsTableColumns = [
226
226
  ];
227
227
 
228
228
  export const getGrantsTableColumns = createSelector(
229
- [_.prop("grantsColumns"), _.prop("authentication")],
230
- (grantsColumns, authentication) =>
231
- _.flow(
229
+ [_.prop("grantsColumns"), _.prop("authentication"), _.prop("onlyMyGrants")],
230
+ (grantsColumns, authentication, onlyMyGrants) => {
231
+ return _.flow(
232
232
  _.defaultTo(defaultGrantsTableColumns),
233
+ _.reject((c) => (onlyMyGrants ? c.name === "user" : false)),
233
234
  _.map((c) => {
234
235
  return c.name === "user"
235
236
  ? { ...c, fieldDecorator: c.fieldDecorator(authentication) }
236
237
  : c;
237
238
  })
238
- )(grantsColumns)
239
+ )(grantsColumns);
240
+ }
239
241
  );
@@ -1,33 +0,0 @@
1
- import React from "react";
2
- import { Route } from "react-router-dom";
3
- import { Unauthorized } from "@truedat/core/components";
4
- import { useAuthorized } from "@truedat/core/hooks";
5
- import { GRANTS } from "@truedat/core/routes";
6
- import GrantsTable from "./GrantsTable";
7
- import GrantFiltersLoader from "./GrantFiltersLoader";
8
- import GrantsLoader from "./GrantsLoader";
9
- import StructureGrants from "./StructureGrants";
10
- import GrantFilters from "./GrantFilters";
11
- import Grants from "./Grants";
12
-
13
- export const GrantsRoutes = () => {
14
- const authorized = true; //useAuthorized("grants");
15
- return (
16
- <Route
17
- path={GRANTS}
18
- render={() =>
19
- authorized ? (
20
- <>
21
- <GrantsLoader />
22
- <GrantFiltersLoader />
23
- <Grants />
24
- </>
25
- ) : (
26
- <Unauthorized />
27
- )
28
- }
29
- />
30
- );
31
- };
32
-
33
- export default GrantsRoutes;
@@ -1,19 +0,0 @@
1
- import React from "react";
2
- import { shallow } from "enzyme";
3
- import { intl } from "@truedat/test/intl-stub";
4
- import { GrantsRoutes } from "../GrantsRoutes";
5
-
6
- // workaround for enzyme issue with React.useContext
7
- // see https://github.com/airbnb/enzyme/issues/2176#issuecomment-532361526
8
- jest.spyOn(React, "useContext").mockImplementation(() => intl);
9
-
10
- jest.mock("@truedat/core/hooks", () => ({
11
- useAuthorized: jest.fn(() => true),
12
- }));
13
-
14
- describe("<GrantsRoutes />", () => {
15
- it("matches the latest snapshot", () => {
16
- const wrapper = shallow(<GrantsRoutes />);
17
- expect(wrapper).toMatchSnapshot();
18
- });
19
- });
@@ -1,8 +0,0 @@
1
- // Jest Snapshot v1, https://goo.gl/fbAQLP
2
-
3
- exports[`<GrantsRoutes /> matches the latest snapshot 1`] = `
4
- <Route
5
- path="/grants"
6
- render={[Function]}
7
- />
8
- `;