@truedat/dd 6.3.1 → 6.3.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/dd",
3
- "version": "6.3.1",
3
+ "version": "6.3.2",
4
4
  "description": "Truedat Web Data Dictionary",
5
5
  "sideEffects": false,
6
6
  "jsnext:main": "src/index.js",
@@ -34,7 +34,7 @@
34
34
  "@testing-library/jest-dom": "^5.16.5",
35
35
  "@testing-library/react": "^12.0.0",
36
36
  "@testing-library/user-event": "^13.2.1",
37
- "@truedat/test": "6.2.3",
37
+ "@truedat/test": "6.3.2",
38
38
  "babel-jest": "^28.1.0",
39
39
  "babel-plugin-dynamic-import-node": "^2.3.3",
40
40
  "babel-plugin-lodash": "^3.3.4",
@@ -88,9 +88,9 @@
88
88
  },
89
89
  "dependencies": {
90
90
  "@apollo/client": "^3.7.1",
91
- "@truedat/auth": "6.3.1",
92
- "@truedat/core": "6.3.1",
93
- "@truedat/df": "6.3.1",
91
+ "@truedat/auth": "6.3.2",
92
+ "@truedat/core": "6.3.2",
93
+ "@truedat/df": "6.3.2",
94
94
  "lodash": "^4.17.21",
95
95
  "moment": "^2.29.4",
96
96
  "path-to-regexp": "^1.7.0",
@@ -115,5 +115,5 @@
115
115
  "react-dom": ">= 16.8.6 < 17",
116
116
  "semantic-ui-react": ">= 2.0.3 < 2.2"
117
117
  },
118
- "gitHead": "0c8183f86f4fe56211b82d2f2427c64f983f0b81"
118
+ "gitHead": "b9ccce69ebb729f62d28972e31b2865d74e9a0d6"
119
119
  }
@@ -270,6 +270,7 @@ export const DATA_STRUCTURE_VERSION_QUERY = gql`
270
270
  id
271
271
  inserted_at
272
272
  latest_note
273
+ roles
273
274
  source {
274
275
  external_id
275
276
  id
@@ -277,17 +278,6 @@ export const DATA_STRUCTURE_VERSION_QUERY = gql`
277
278
  source_id
278
279
  system_id
279
280
  updated_at
280
- id
281
- alias
282
- confidential
283
- domain_ids
284
- domains {
285
- id
286
- name
287
- }
288
- external_id
289
- inserted_at
290
- updated_at
291
281
  }
292
282
  parents {
293
283
  alias
@@ -0,0 +1,24 @@
1
+ import React from "react";
2
+ import PropTypes from "prop-types";
3
+
4
+ import { useHistory } from "react-router-dom";
5
+ import AddResourceMember from "@truedat/core/components/AddResourceMember";
6
+ import { linkTo } from "@truedat/core/routes";
7
+ import StructureCrumbs from "./StructureCrumbs";
8
+
9
+ const AddStructureMember = ({ id }) => {
10
+ const history = useHistory();
11
+ const onSuccess = () => history.push(linkTo.STRUCTURE_MEMBERS({ id }));
12
+ return (
13
+ <>
14
+ <StructureCrumbs />
15
+ <AddResourceMember type="structure" id={`${id}`} onSuccess={onSuccess} />
16
+ </>
17
+ );
18
+ };
19
+
20
+ AddStructureMember.propTypes = {
21
+ id: PropTypes.number,
22
+ };
23
+
24
+ export default AddStructureMember;
@@ -0,0 +1,6 @@
1
+ import React from "react";
2
+ import ResourceMembers from "@truedat/core/components/ResourceMembers";
3
+
4
+ export const StructureRoles = () => <ResourceMembers type="structure" />;
5
+
6
+ export default StructureRoles;
@@ -22,6 +22,7 @@ import StructureParentRelations from "./StructureParentRelations";
22
22
  import StructureVersions from "./StructureVersions";
23
23
  import StructureMetadata from "./StructureMetadata";
24
24
  import StructureNotesLoader from "./StructureNotesLoader";
25
+ import StructureRoles from "./StructureRoles";
25
26
  import StructureStructureLinks from "./StructureStructureLinks";
26
27
  import StructureStructureForm from "./StructureStructureForm";
27
28
 
@@ -79,6 +80,7 @@ export const StructureTabPane = ({ activeTab, tabVisibility }) => {
79
80
  {activeTab === "children" && <StructureChildrenRelations />}
80
81
  {activeTab === "versions" && <StructureVersions />}
81
82
  {activeTab === "events" && <StructureEvents />}
83
+ {activeTab === "roles" && <StructureRoles />}
82
84
  {activeTab === "rules" && tabVisibility.rules && (
83
85
  <RuleImplementationsTable withoutColumns={["status"]} />
84
86
  )}
@@ -13,6 +13,7 @@ import {
13
13
  STRUCTURE_NOTES,
14
14
  STRUCTURE_PARENTS,
15
15
  STRUCTURE_PROFILE,
16
+ STRUCTURE_MEMBERS,
16
17
  STRUCTURE_RULES,
17
18
  STRUCTURE_VERSION,
18
19
  STRUCTURE_VERSIONS,
@@ -32,6 +33,7 @@ export const StructureTabPaneRoutes = ({}) => (
32
33
  <Route path={STRUCTURE_NOTES} component={StructureTabPane} />
33
34
  <Route path={STRUCTURE_GRANTS} component={StructureTabPane} />
34
35
  <Route path={STRUCTURE_METADATA} component={StructureTabPane} />
36
+ <Route path={STRUCTURE_MEMBERS} component={StructureTabPane} />
35
37
  <Route path={STRUCTURE_RULES} component={StructureTabPane} />
36
38
  <Route path={STRUCTURE_CHILDREN} component={StructureTabPane} />
37
39
  <Route path={STRUCTURE_PARENTS} component={StructureTabPane} />
@@ -12,6 +12,7 @@ import {
12
12
  STRUCTURE_NOTES,
13
13
  STRUCTURE_PARENTS,
14
14
  STRUCTURE_PROFILE,
15
+ STRUCTURE_MEMBERS,
15
16
  STRUCTURE_RULES,
16
17
  STRUCTURE_VERSION,
17
18
  STRUCTURE_VERSIONS,
@@ -31,6 +32,7 @@ export const StructureTabRoutes = () => (
31
32
  <Route path={STRUCTURE_EVENTS} component={StructureTabs} />
32
33
  <Route path={STRUCTURE_METADATA} component={StructureTabs} />
33
34
  <Route path={STRUCTURE_NOTES} component={StructureTabs} />
35
+ <Route path={STRUCTURE_MEMBERS} component={StructureTabs} />
34
36
  <Route path={STRUCTURE_RULES} component={StructureTabs} />
35
37
  <Route path={STRUCTURE_LINEAGE} component={StructureTabs} />
36
38
  <Route path={STRUCTURE_CHILDREN} component={StructureTabs} />
@@ -36,6 +36,7 @@ export const StructureTabs = ({
36
36
  parents,
37
37
  profile,
38
38
  rules,
39
+ roles,
39
40
  versions,
40
41
  } = {},
41
42
  structure,
@@ -144,6 +145,13 @@ export const StructureTabs = ({
144
145
  content: events ? formatMessage({ id: "tabs.dd.audit" }) : false,
145
146
  key: "events",
146
147
  },
148
+ {
149
+ active: activeTab === "roles",
150
+ as: Link,
151
+ to: linkTo.STRUCTURE_MEMBERS(structure),
152
+ content: roles ? formatMessage({ id: "tabs.dd.roles" }) : false,
153
+ key: "roles",
154
+ },
147
155
  ])
148
156
  : null;
149
157
  return (
@@ -1,5 +1,6 @@
1
1
  import _ from "lodash/fp";
2
2
  import React from "react";
3
+
3
4
  import { useIntl } from "react-intl";
4
5
  import { Route, Switch, useRouteMatch } from "react-router-dom";
5
6
  import { Unauthorized } from "@truedat/core/components";
@@ -12,7 +13,9 @@ import {
12
13
  STRUCTURES_BULK_UPDATE,
13
14
  STRUCTURE_EVENTS,
14
15
  STRUCTURE_VERSION,
16
+ STRUCTURE_MEMBERS_NEW,
15
17
  } from "@truedat/core/routes";
18
+ import AddStructureMember from "./AddStructureMember";
16
19
  import StructureCrumbs from "./StructureCrumbs";
17
20
  import StructureLoader from "./StructureLoader";
18
21
  import UserSearchFiltersLoader from "./UserSearchFiltersLoader";
@@ -32,6 +35,10 @@ const RuleImplementationsLoader = React.lazy(() =>
32
35
  import("@truedat/dq/components/RuleImplementationsLoader")
33
36
  );
34
37
 
38
+ const RolesLoader = React.lazy(() =>
39
+ import("@truedat/auth/roles/components/RolesLoader")
40
+ );
41
+
35
42
  const ConceptSelectorConceptsLoader = () => (
36
43
  <ConceptsLoader
37
44
  pageSize={7}
@@ -118,7 +125,18 @@ const AuthorizedRoutes = () => {
118
125
  );
119
126
  }}
120
127
  />
121
- <Route path={[STRUCTURE]} component={StructureView} />
128
+ <Switch>
129
+ <Route
130
+ path={STRUCTURE_MEMBERS_NEW}
131
+ render={({ match }) => (
132
+ <>
133
+ <RolesLoader />
134
+ <AddStructureMember id={parseInt(match?.params?.id)} />
135
+ </>
136
+ )}
137
+ />
138
+ <Route path={STRUCTURE} component={StructureView} />
139
+ </Switch>
122
140
  </>
123
141
  );
124
142
  };
@@ -0,0 +1,12 @@
1
+ import React from "react";
2
+ import { render } from "@truedat/test/render";
3
+ import AddStructureMember from "../AddStructureMember";
4
+
5
+ const props = { id: 1 };
6
+
7
+ describe("<AddStructureMember />", () => {
8
+ it("matches the latest snapshot", () => {
9
+ const { container } = render(<AddStructureMember {...props} />);
10
+ expect(container).toMatchSnapshot();
11
+ });
12
+ });
@@ -0,0 +1,23 @@
1
+ import React from "react";
2
+ import { render } from "@testing-library/react";
3
+ import { intl } from "@truedat/test/intl-stub";
4
+ import StructureRoles from "../StructureRoles";
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
+ jest.mock("react-router-dom", () => ({
10
+ ...jest.requireActual("react-router-dom"),
11
+ useParams: jest.fn().mockReturnValue({ id: 1 }),
12
+ }));
13
+
14
+ describe("<StructureRoles />", () => {
15
+ const props = {
16
+ type: "domain",
17
+ };
18
+
19
+ it("matches the latest snapshot", () => {
20
+ const { container } = render(<StructureRoles {...props} />);
21
+ expect(container).toMatchSnapshot();
22
+ });
23
+ });
@@ -16,6 +16,7 @@ describe("<StructureTabPane />", () => {
16
16
  "events",
17
17
  "foo",
18
18
  "metadata",
19
+ "roles",
19
20
  "grants",
20
21
  ].forEach((tab) =>
21
22
  it("matches the latest snapshot: " + tab, () => {
@@ -15,6 +15,7 @@ const renderOpts = {
15
15
  "tabs.dd.versions": "Versions",
16
16
  "tabs.dd.grants": "Grants",
17
17
  "tabs.dd.rules": "Rules",
18
+ "tabs.dd.roles": "Roles",
18
19
  },
19
20
  },
20
21
  };
@@ -32,6 +33,7 @@ describe("<StructureTabs />", () => {
32
33
  "grants",
33
34
  "notes",
34
35
  "rules",
36
+ "roles",
35
37
  "events",
36
38
  ];
37
39
  const structure = { id: 123 };
@@ -0,0 +1,160 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<AddStructureMember /> matches the latest snapshot 1`] = `
4
+ <div>
5
+ <div
6
+ class="ui segment ui text container"
7
+ >
8
+ <h2
9
+ class="ui header"
10
+ >
11
+ <i
12
+ aria-hidden="true"
13
+ class="id card outline icon"
14
+ />
15
+ <div
16
+ class="content"
17
+ >
18
+ Add
19
+ </div>
20
+ </h2>
21
+ <form
22
+ class="ui form"
23
+ >
24
+ <div
25
+ class="required field"
26
+ >
27
+ <label
28
+ for="principal"
29
+ >
30
+ Member
31
+ </label>
32
+ <div
33
+ aria-expanded="false"
34
+ class="ui search selection dropdown"
35
+ required=""
36
+ role="combobox"
37
+ >
38
+ <input
39
+ aria-autocomplete="list"
40
+ autocomplete="off"
41
+ class="search"
42
+ tabindex="0"
43
+ type="text"
44
+ value=""
45
+ />
46
+ <div
47
+ aria-atomic="true"
48
+ aria-live="polite"
49
+ class="divider default text"
50
+ role="alert"
51
+ >
52
+ Member
53
+ </div>
54
+ <i
55
+ aria-hidden="true"
56
+ class="dropdown icon"
57
+ />
58
+ <div
59
+ class="menu transition"
60
+ role="listbox"
61
+ >
62
+ <div
63
+ class="message"
64
+ >
65
+ No results found.
66
+ </div>
67
+ </div>
68
+ </div>
69
+ </div>
70
+ <div
71
+ class="required field"
72
+ >
73
+ <label
74
+ for="role"
75
+ >
76
+ Role
77
+ </label>
78
+ <div
79
+ aria-expanded="false"
80
+ class="ui basic search selection dropdown"
81
+ required=""
82
+ role="combobox"
83
+ >
84
+ <input
85
+ aria-autocomplete="list"
86
+ autocomplete="off"
87
+ class="search"
88
+ id="role"
89
+ tabindex="0"
90
+ type="text"
91
+ value=""
92
+ />
93
+ <div
94
+ aria-atomic="true"
95
+ aria-live="polite"
96
+ class="divider default text"
97
+ role="alert"
98
+ >
99
+ Role
100
+ </div>
101
+ <i
102
+ aria-hidden="true"
103
+ class="dropdown icon"
104
+ />
105
+ <div
106
+ class="menu transition"
107
+ role="listbox"
108
+ >
109
+ <div
110
+ class="message"
111
+ >
112
+ No results found.
113
+ </div>
114
+ </div>
115
+ </div>
116
+ </div>
117
+ <div
118
+ class="field"
119
+ >
120
+ <label
121
+ for="description"
122
+ >
123
+ Description
124
+ </label>
125
+ <div
126
+ class="ui input"
127
+ >
128
+ <input
129
+ autocomplete="off"
130
+ id="description"
131
+ maxlength="120"
132
+ placeholder="Description"
133
+ type="text"
134
+ value=""
135
+ />
136
+ </div>
137
+ </div>
138
+ <div
139
+ class="actions"
140
+ >
141
+ <button
142
+ class="ui primary disabled right floated button"
143
+ disabled=""
144
+ tabindex="-1"
145
+ type="submit"
146
+ >
147
+ Add
148
+ </button>
149
+ <a
150
+ class="ui secondary button"
151
+ href="/"
152
+ role="button"
153
+ >
154
+ Cancel
155
+ </a>
156
+ </div>
157
+ </form>
158
+ </div>
159
+ </div>
160
+ `;
@@ -0,0 +1,44 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<StructureRoles /> matches the latest snapshot 1`] = `
4
+ <div>
5
+ <div
6
+ class="ui centered one column grid"
7
+ >
8
+ <div
9
+ class="column"
10
+ >
11
+ <div
12
+ class="ui icon input"
13
+ >
14
+ <input
15
+ placeholder="user.search.placeholder"
16
+ type="text"
17
+ value=""
18
+ />
19
+ <i
20
+ aria-hidden="true"
21
+ class="search link icon"
22
+ />
23
+ </div>
24
+ </div>
25
+ </div>
26
+ <div
27
+ class="ui icon message"
28
+ >
29
+ <i
30
+ aria-hidden="true"
31
+ class="warning icon"
32
+ />
33
+ <div
34
+ class="content"
35
+ >
36
+ <div
37
+ class="header"
38
+ >
39
+ domain.members.empty
40
+ </div>
41
+ </div>
42
+ </div>
43
+ </div>
44
+ `;
@@ -59,6 +59,12 @@ exports[`<StructureTabPane /> matches the latest snapshot: profile 1`] = `
59
59
  </ErrorBoundary>
60
60
  `;
61
61
 
62
+ exports[`<StructureTabPane /> matches the latest snapshot: roles 1`] = `
63
+ <ErrorBoundary>
64
+ <StructureRoles />
65
+ </ErrorBoundary>
66
+ `;
67
+
62
68
  exports[`<StructureTabPane /> matches the latest snapshot: versions 1`] = `
63
69
  <ErrorBoundary>
64
70
  <Connect(StructureVersions) />
@@ -38,6 +38,10 @@ exports[`<StructureTabRoutes /> matches the latest snapshot 1`] = `
38
38
  component={[Function]}
39
39
  path="/structures/:id/notes"
40
40
  />
41
+ <Route
42
+ component={[Function]}
43
+ path="/structures/:id/members"
44
+ />
41
45
  <Route
42
46
  component={[Function]}
43
47
  path="/structures/:id/rules"
@@ -59,6 +59,12 @@ exports[`<StructureTabs /> matches the latest snapshot: metadata 1`] = `
59
59
  >
60
60
  Audit
61
61
  </a>
62
+ <a
63
+ class="item"
64
+ href="/structures/123/members"
65
+ >
66
+ Roles
67
+ </a>
62
68
  </div>
63
69
  <div
64
70
  class="ui hidden divider"
@@ -13,6 +13,7 @@ const structureFields = (fn) =>
13
13
  "domains",
14
14
  "external_id",
15
15
  "id",
16
+ "roles",
16
17
  "inserted_at",
17
18
  "updated_at",
18
19
  ])
@@ -11,6 +11,7 @@ import {
11
11
  STRUCTURE_PARENTS,
12
12
  STRUCTURE_PROFILE,
13
13
  STRUCTURE_RULES,
14
+ STRUCTURE_MEMBERS,
14
15
  STRUCTURE_VERSION,
15
16
  STRUCTURE_VERSIONS,
16
17
  STRUCTURE_VERSION_VERSIONS,
@@ -26,6 +27,7 @@ describe("selectors: defaultTab", () => {
26
27
  const parents = truthy;
27
28
  const profile = truthy;
28
29
  const rules = truthy;
30
+ const roles = truthy;
29
31
  const links = truthy;
30
32
  const versions = truthy;
31
33
  const grants = truthy;
@@ -38,6 +40,7 @@ describe("selectors: defaultTab", () => {
38
40
  expect(defaultTab({ parents, profile })).toBe("parents");
39
41
  expect(defaultTab({ profile, rules })).toBe("profile");
40
42
  expect(defaultTab({ links, rules })).toBe("rules");
43
+ expect(defaultTab({ links, roles })).toBe("roles");
41
44
  expect(defaultTab({ links, versions })).toBe("links");
42
45
  expect(defaultTab({ versions })).toBe("versions");
43
46
  expect(defaultTab({})).toBe(undefined);
@@ -56,6 +59,7 @@ describe("selectors: getActiveTab", () => {
56
59
  expect(getActiveTab(tabVisibility, STRUCTURE_PARENTS)).toBe("parents");
57
60
  expect(getActiveTab(tabVisibility, STRUCTURE_PROFILE)).toBe("profile");
58
61
  expect(getActiveTab(tabVisibility, STRUCTURE_RULES)).toBe("rules");
62
+ expect(getActiveTab(tabVisibility, STRUCTURE_MEMBERS)).toBe("roles");
59
63
  expect(getActiveTab(tabVisibility, STRUCTURE_LINKS)).toBe("links");
60
64
  expect(getActiveTab(tabVisibility, STRUCTURE_LINKS_NEW)).toBe("links");
61
65
  expect(getActiveTab(tabVisibility, STRUCTURE_VERSIONS)).toBe("versions");
@@ -196,4 +196,11 @@ describe("selectors: getTabVisibility", () => {
196
196
  grants: true,
197
197
  });
198
198
  });
199
+
200
+ it("should include roles if manage_structure_acl_entry action is present in structureActions", () => {
201
+ expect(getTabVisibility({})).toMatchObject({ roles: false });
202
+ expect(
203
+ getTabVisibility({ structureActions: { manage_structure_acl_entry: {} } })
204
+ ).toMatchObject({ roles: true });
205
+ });
199
206
  });
@@ -9,6 +9,7 @@ import {
9
9
  STRUCTURE_PARENTS,
10
10
  STRUCTURE_PROFILE,
11
11
  STRUCTURE_STRUCTURE_LINKS,
12
+ STRUCTURE_MEMBERS,
12
13
  STRUCTURE_RULES,
13
14
  STRUCTURE_LINKS,
14
15
  STRUCTURE_LINKS_NEW,
@@ -29,6 +30,7 @@ export const defaultTab = _.cond([
29
30
  [_.conformsTo({ parents: _.identity }), _.constant("parents")],
30
31
  [_.conformsTo({ profile: _.identity }), _.constant("profile")],
31
32
  [_.conformsTo({ rules: _.identity }), _.constant("rules")],
33
+ [_.conformsTo({ roles: _.identity }), _.constant("roles")],
32
34
  [_.conformsTo({ links: _.identity }), _.constant("links")],
33
35
  [_.conformsTo({ versions: _.identity }), _.constant("versions")],
34
36
  ]);
@@ -45,6 +47,7 @@ export const getActiveTab = (tabVisibility, path) => {
45
47
  [_.eq(STRUCTURE_PARENTS), _.constant("parents")],
46
48
  [_.eq(STRUCTURE_PROFILE), _.constant("profile")],
47
49
  [_.eq(STRUCTURE_STRUCTURE_LINKS), _.constant("structureLinks")],
50
+ [_.eq(STRUCTURE_MEMBERS), _.constant("roles")],
48
51
  [_.eq(STRUCTURE_RULES), _.constant("rules")],
49
52
  [_.eq(STRUCTURE_LINKS), _.constant("links")],
50
53
  [_.eq(STRUCTURE_LINKS_NEW), _.constant("links")],
@@ -39,6 +39,9 @@ const versionsTabVisible = notEmptyPath("structureVersions");
39
39
  const structureLinksTabVisible = (state) =>
40
40
  _.has("create_struct_to_struct_link", state?.structureActions) ||
41
41
  state?.structure?.data_structure_link_count > 0;
42
+ const rolesTabVisible = (state) =>
43
+ _.has("manage_structure_acl_entry", state?.structureActions) ||
44
+ !_.isEmpty(state?.structure?.roles);
42
45
  const rulesTabVisible = (state) => state?.structure?.implementation_count > 0;
43
46
  const lineageTabVisible = _.anyPass([_.path("structure.degree.in")]);
44
47
  const impactTabVisible = _.anyPass([_.path("structure.degree.out")]);
@@ -65,4 +68,5 @@ export const getTabVisibility = (state) => ({
65
68
  parents: parentsTabVisible(state),
66
69
  children: childrenTabVisible(state),
67
70
  grants: grantsTabVisible(state),
71
+ roles: rolesTabVisible(state),
68
72
  });