@truedat/dd 7.11.0 → 7.11.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": "7.11.0",
3
+ "version": "7.11.2",
4
4
  "description": "Truedat Web Data Dictionary",
5
5
  "sideEffects": false,
6
6
  "module": "src/index.js",
@@ -48,7 +48,7 @@
48
48
  "@testing-library/jest-dom": "^6.6.3",
49
49
  "@testing-library/react": "^16.3.0",
50
50
  "@testing-library/user-event": "^14.6.1",
51
- "@truedat/test": "7.11.0",
51
+ "@truedat/test": "7.11.2",
52
52
  "identity-obj-proxy": "^3.0.0",
53
53
  "jest": "^29.7.0",
54
54
  "redux-saga-test-plan": "^4.0.6"
@@ -83,5 +83,5 @@
83
83
  "svg-pan-zoom": "^3.6.2",
84
84
  "swr": "^2.3.3"
85
85
  },
86
- "gitHead": "c8c0d3e62835df86440869c7b797bbe2fc12f3a6"
86
+ "gitHead": "c7c9de44c34d3105c78711fa63f6ee8cf7730eaf"
87
87
  }
@@ -242,7 +242,6 @@ export const DATA_STRUCTURE_VERSION_QUERY = gql`
242
242
  query DataStructureVersion(
243
243
  $dataStructureId: ID!
244
244
  $version: String!
245
- $note_fields: [String]
246
245
  ) {
247
246
  dataStructureVersion(dataStructureId: $dataStructureId, version: $version) {
248
247
  id
@@ -285,106 +284,33 @@ export const DATA_STRUCTURE_VERSION_QUERY = gql`
285
284
  alias
286
285
  classes
287
286
  data_structure_id
288
- deleted_at
289
287
  id
290
- metadata
291
288
  name
292
289
  type
293
- has_note
294
290
  note
295
291
  }
296
292
  children {
297
293
  alias
298
294
  classes
299
295
  data_structure_id
300
- deleted_at
301
296
  id
302
- metadata
303
297
  name
304
298
  type
305
- has_note
306
299
  note
307
300
  }
308
301
  siblings {
309
302
  alias
310
303
  classes
311
304
  data_structure_id
312
- deleted_at
313
305
  id
314
- metadata
315
306
  name
316
307
  type
317
308
  }
318
- versions {
319
- deleted_at
320
- inserted_at
321
- updated_at
322
- version
323
- }
309
+ version_count
310
+
324
311
  ancestry
325
312
 
326
- grants {
327
- data_structure {
328
- external_id
329
- id
330
- system_id
331
- }
332
- data_structure_version {
333
- ancestry
334
- name
335
- }
336
- detail
337
- end_date
338
- external_ref
339
- id
340
- inserted_at
341
- pending_removal
342
- source_user_name
343
- start_date
344
- system {
345
- external_id
346
- id
347
- name
348
- }
349
- updated_at
350
- user {
351
- email
352
- full_name
353
- user_name
354
- }
355
- user_id
356
- }
357
- grant {
358
- data_structure {
359
- external_id
360
- id
361
- system_id
362
- }
363
- data_structure_version {
364
- ancestry
365
- name
366
- }
367
- detail
368
- end_date
369
- external_ref
370
- id
371
- inserted_at
372
- pending_removal
373
- source_user_name
374
- start_date
375
- system {
376
- external_id
377
- id
378
- name
379
- }
380
- updated_at
381
- user {
382
- email
383
- full_name
384
- user_name
385
- }
386
- user_id
387
- }
313
+ grants_count
388
314
 
389
315
  classes
390
316
  implementation_count
@@ -419,33 +345,7 @@ export const DATA_STRUCTURE_VERSION_QUERY = gql`
419
345
  in
420
346
  out
421
347
  }
422
- data_fields {
423
- alias
424
- classes
425
- data_structure_id
426
- deleted_at
427
- description
428
- degree {
429
- in
430
- out
431
- }
432
- links
433
- id
434
- metadata
435
- name
436
- type
437
- profile {
438
- max
439
- min
440
- most_frequent
441
- null_count
442
- patterns
443
- total_count
444
- unique_count
445
- }
446
- has_note
447
- note(select_fields: $note_fields)
448
- }
348
+ data_fields_count
449
349
  relations {
450
350
  parents {
451
351
  id
@@ -476,23 +376,136 @@ export const DATA_STRUCTURE_VERSION_QUERY = gql`
476
376
  }
477
377
  }
478
378
 
479
- links
480
379
  _actions
481
380
  user_permissions
482
381
  }
483
382
  }
484
383
  `;
485
-
384
+
486
385
  export const DATA_STRUCTURE_VERSION_METADATA_QUERY = gql`
487
- query DataStructureVersion(
488
- $dataStructureId: ID!
489
- $version: String!
490
- ) {
491
- dataStructureVersion(dataStructureId: $dataStructureId, version: $version) {
492
- metadata
386
+ query DataStructureVersion(
387
+ $dataStructureId: ID!
388
+ $version: String!
389
+ ) {
390
+ dataStructureVersion(dataStructureId: $dataStructureId, version: $version) {
391
+ metadata
392
+ }
393
+ }
394
+ `;
395
+
396
+ export const DATA_STRUCTURE_VERSION_LINKS_QUERY = gql`
397
+ query DataStructureVersion(
398
+ $dataStructureId: ID!
399
+ $version: String!
400
+ ) {
401
+ dataStructureVersion(dataStructureId: $dataStructureId, version: $version) {
402
+ links
403
+ }
404
+ }
405
+ `;
406
+
407
+
408
+ export const DATA_STRUCTURE_VERSION_VERSIONS_QUERY = gql`
409
+ query DataStructureVersion(
410
+ $dataStructureId: ID!
411
+ $version: String!
412
+ ) {
413
+ dataStructureVersion(dataStructureId: $dataStructureId, version: $version) {
414
+ versions {
415
+ deleted_at
416
+ inserted_at
417
+ updated_at
418
+ version
419
+ }
420
+ }
493
421
  }
494
- }
495
422
  `;
423
+
424
+ export const DATA_STRUCTURE_VERSION_GRANTS_QUERY = gql`
425
+ query DataStructureVersion(
426
+ $dataStructureId: ID!
427
+ $version: String!
428
+ ) {
429
+ dataStructureVersion(dataStructureId: $dataStructureId, version: $version) {
430
+ grants {
431
+ data_structure {
432
+ external_id
433
+ id
434
+ system_id
435
+ }
436
+ data_structure_version {
437
+ ancestry
438
+ name
439
+ }
440
+ detail
441
+ end_date
442
+ external_ref
443
+ id
444
+ inserted_at
445
+ pending_removal
446
+ source_user_name
447
+ start_date
448
+ system {
449
+ external_id
450
+ id
451
+ name
452
+ }
453
+ updated_at
454
+ user {
455
+ email
456
+ full_name
457
+ user_name
458
+ }
459
+ user_id
460
+ }
461
+ }
462
+ }
463
+ `;
464
+
465
+ export const DATA_STRUCTURE_PARENTS_QUERY = gql`
466
+ query DataStructureVersion(
467
+ $dataStructureId: ID!
468
+ $version: String!
469
+ ) {
470
+ dataStructureVersion(dataStructureId: $dataStructureId, version: $version) {
471
+ parents {
472
+ alias
473
+ classes
474
+ data_structure_id
475
+ deleted_at
476
+ id
477
+ metadata
478
+ name
479
+ type
480
+ has_note
481
+ note
482
+ }
483
+ }
484
+ }
485
+ `;
486
+
487
+ export const DATA_STRUCTURE_CHILDREN_QUERY = gql`
488
+ query DataStructureVersion(
489
+ $dataStructureId: ID!
490
+ $version: String!
491
+ ) {
492
+ dataStructureVersion(dataStructureId: $dataStructureId, version: $version) {
493
+ children {
494
+ alias
495
+ classes
496
+ data_structure_id
497
+ deleted_at
498
+ id
499
+ metadata
500
+ name
501
+ type
502
+ has_note
503
+ note
504
+ }
505
+ }
506
+ }
507
+ `;
508
+
496
509
  export const TASKS_QUERY = gql`
497
510
  query Tasks {
498
511
  tasks {
@@ -8,23 +8,23 @@ import { columnDecorator } from "@truedat/core/services";
8
8
  import { linkTo } from "@truedat/core/routes";
9
9
  import { getGrantsColumns } from "../selectors";
10
10
  import GrantRemoval from "./GrantRemoval";
11
+ import { useQuery } from "@apollo/client";
12
+ import { DATA_STRUCTURE_VERSION_GRANTS_QUERY } from "../api/queries";
11
13
 
12
14
  const matchesStructure = ({ id }) => _.pathEq("data_structure.id", id);
13
15
 
14
- export const StructureGrants = ({
16
+ const StructureGrantsContent = ({
15
17
  columns,
16
- grants,
17
18
  grantsActions,
18
19
  structure,
19
20
  userPermissions,
21
+ grants,
22
+ canUpdateRemoval,
20
23
  }) => {
21
24
  const { formatMessage } = useIntl();
22
25
  const navigate = useNavigate();
23
- const canUpdateRemoval =
24
- userPermissions?.update_grant_removal &&
25
- _.any(matchesStructure(structure))(grants);
26
26
 
27
- return grants ? (
27
+ return (
28
28
  <Table basic="very">
29
29
  <Table.Header>
30
30
  <Table.Row>
@@ -76,7 +76,50 @@ export const StructureGrants = ({
76
76
  ))}
77
77
  </Table.Body>
78
78
  </Table>
79
- ) : null;
79
+ );
80
+ };
81
+
82
+ StructureGrantsContent.propTypes = {
83
+ columns: PropTypes.array,
84
+ grantsActions: PropTypes.object,
85
+ structure: PropTypes.object,
86
+ userPermissions: PropTypes.object,
87
+ grants: PropTypes.array,
88
+ canUpdateRemoval: PropTypes.bool,
89
+ };
90
+
91
+ export const StructureGrants = ({
92
+ columns,
93
+ grantsActions,
94
+ structure,
95
+ userPermissions,
96
+ }) => {
97
+ const { data } = useQuery(DATA_STRUCTURE_VERSION_GRANTS_QUERY, {
98
+ fetchPolicy: "network-only",
99
+ variables: { dataStructureId: structure.id, version: "latest" },
100
+ skip: !structure?.id,
101
+ });
102
+ const grants = data?.dataStructureVersion.grants;
103
+
104
+ const canUpdateRemoval =
105
+ userPermissions?.update_grant_removal &&
106
+ grants &&
107
+ _.any(matchesStructure(structure))(grants);
108
+
109
+ if (!grants) {
110
+ return null;
111
+ }
112
+
113
+ return (
114
+ <StructureGrantsContent
115
+ columns={columns}
116
+ grantsActions={grantsActions}
117
+ structure={structure}
118
+ userPermissions={userPermissions}
119
+ grants={grants}
120
+ canUpdateRemoval={canUpdateRemoval}
121
+ />
122
+ );
80
123
  };
81
124
 
82
125
  StructureGrants.propTypes = {
@@ -41,21 +41,30 @@ const StructureSuggestionLinkForm = lazy(
41
41
  );
42
42
 
43
43
  const RuleImplementationsTable = lazy(
44
- () => import("@truedat/dq/components/RuleImplementationsTable")
44
+ () => import("@truedat/dq/components/SimpleRuleImplementationsTable")
45
45
  );
46
46
 
47
47
  const CustomTab = ({ tabs, activeTab }) => {
48
48
  if (_.isEmpty(tabs)) return null;
49
- const tab = _.find((tab) => (tab.name === activeTab))(tabs);
49
+ const tab = _.find((tab) => tab.name === activeTab)(tabs);
50
50
  return tab?.content;
51
- }
51
+ };
52
52
 
53
53
  export const StructureTabPane = ({ path }) => {
54
54
  const customTabs = useStructureCustomTabs();
55
55
  const tabVisibility = useSelector(getTabVisibility);
56
- const structureTabsOrder = useSelector(state => state.structureTabsOrder);
57
- const columns = useSelector(state => getLinkedImplementationsToStructuresColumns(state));
58
- const activeTab = getActiveTab({ tabVisibility, path, structureTabsOrder, customTabs });
56
+ const structureTabsOrder = useSelector((state) => state.structureTabsOrder);
57
+ const columns = useSelector((state) =>
58
+ getLinkedImplementationsToStructuresColumns(state)
59
+ );
60
+ const ruleImplementations = useSelector((state) => state.ruleImplementations);
61
+
62
+ const activeTab = getActiveTab({
63
+ tabVisibility,
64
+ path,
65
+ structureTabsOrder,
66
+ customTabs,
67
+ });
59
68
 
60
69
  return (
61
70
  <ErrorBoundary>
@@ -90,7 +99,10 @@ export const StructureTabPane = ({ path }) => {
90
99
  {activeTab === "events" && <StructureEvents />}
91
100
  {activeTab === "roles" && <StructureRoles />}
92
101
  {activeTab === "rules" && tabVisibility.rules && (
93
- <RuleImplementationsTable columns={columns} />
102
+ <RuleImplementationsTable
103
+ columns={columns}
104
+ ruleImplementations={ruleImplementations}
105
+ />
94
106
  )}
95
107
  {activeTab === "metadata" && tabVisibility.metadata && (
96
108
  <StructureMetadata />
@@ -102,6 +114,6 @@ export const StructureTabPane = ({ path }) => {
102
114
  );
103
115
  };
104
116
 
105
- StructureTabPane.propTypes = { path: PropTypes.string, };
117
+ StructureTabPane.propTypes = { path: PropTypes.string };
106
118
 
107
119
  export default StructureTabPane;
@@ -1,17 +1,30 @@
1
1
  import _ from "lodash/fp";
2
2
  import PropTypes from "prop-types";
3
3
  import { connect } from "react-redux";
4
+ import { useParams } from "react-router";
4
5
  import { List } from "semantic-ui-react";
5
6
  import StructureVersionListItem from "./StructureVersionListItem";
7
+ import { useQuery } from "@apollo/client";
8
+ import { DATA_STRUCTURE_VERSION_VERSIONS_QUERY } from "../api/queries";
6
9
 
7
- export const StructureVersions = ({ id, versions }) =>
10
+ export const StructureVersions = () =>{
11
+ const { id } = useParams();
12
+
13
+ const { data } = useQuery(DATA_STRUCTURE_VERSION_VERSIONS_QUERY, {
14
+ fetchPolicy: "network-only",
15
+ variables: { dataStructureId: id, version: "latest" },
16
+ });
17
+
18
+ const versions = data?.dataStructureVersion.versions;
19
+ return (
8
20
  !_.isEmpty(versions) && (
9
21
  <List selection>
10
22
  {versions.map((v, i) => (
11
23
  <StructureVersionListItem key={i} id={id} {...v} />
12
24
  ))}
13
25
  </List>
14
- );
26
+ ));
27
+ }
15
28
 
16
29
  StructureVersions.propTypes = {
17
30
  id: PropTypes.number,
@@ -12,11 +12,13 @@ export const StructuresEditableDownloadOption = ({
12
12
  pendingStructureNotes,
13
13
  }) => {
14
14
  const { formatMessage, locale } = useIntl();
15
+
15
16
  const {
16
17
  searchData,
17
18
  loading: structuresLoading,
18
19
  filterParams: searchParams,
19
20
  } = useSearchContext();
21
+
20
22
  const { trigger: triggerDownload, isMutating: structuresDownloading } =
21
23
  useDataStructureDownload();
22
24
 
@@ -2,6 +2,32 @@ import _ from "lodash/fp";
2
2
  import { render } from "@truedat/test/render";
3
3
  import { StructureGrants } from "../StructureGrants";
4
4
 
5
+ jest.mock("@apollo/client", () => ({
6
+ gql: jest.fn((query) => query),
7
+ useQuery: jest.fn(() => ({
8
+ data: {
9
+ dataStructureVersion: {
10
+ grants: [
11
+ {
12
+ id: 1,
13
+ user: { id: 1, full_name: "foo user" },
14
+ data_structure: { id: 99, external_id: "foo_strucutre" },
15
+ end_date: "2021/08/02",
16
+ pending_removal: false,
17
+ },
18
+ {
19
+ id: 2,
20
+ user: { id: 2, full_name: "bar user" },
21
+ data_structure: { name: "bar structure" },
22
+ end_date: "2021/09/02",
23
+ pending_removal: false,
24
+ },
25
+ ],
26
+ },
27
+ },
28
+ })),
29
+ }));
30
+
5
31
  describe("<StructureGrants />", () => {
6
32
  const idDecorator = ({ id }) => <p>{id}</p>;
7
33
  const userDecorator = ({ user }) => <p>{user.full_name}</p>;
@@ -56,7 +56,7 @@ jest.mock("@truedat/lm/components/StructureRelationForm", () => () => (
56
56
  <div>StructureRelationForm</div>
57
57
  ));
58
58
  jest.mock("@truedat/profile", () => () => <div>StructureProfile</div>);
59
- jest.mock("@truedat/dq/components/RuleImplementationsTable", () => () => (
59
+ jest.mock("@truedat/dq/components/SimpleRuleImplementationsTable", () => () => (
60
60
  <div>RuleImplementationsTable</div>
61
61
  ));
62
62
 
@@ -73,132 +73,193 @@ describe("<StructureTabPane />", () => {
73
73
  };
74
74
 
75
75
  it("renders correctly with fields tab", async () => {
76
- jest.spyOn(getTabVisibilityModule, "getTabVisibility").mockReturnValue(tabVisibility);
77
- const rendered = render(<StructureTabPane path={STRUCTURE_FIELDS} />, { state });
76
+ jest
77
+ .spyOn(getTabVisibilityModule, "getTabVisibility")
78
+ .mockReturnValue(tabVisibility);
79
+ const rendered = render(<StructureTabPane path={STRUCTURE_FIELDS} />, {
80
+ state,
81
+ });
78
82
  await waitForLoad(rendered);
79
83
  expect(rendered.container).toMatchSnapshot();
80
84
  });
81
85
 
82
86
  it("renders correctly with notes edit route", async () => {
83
- const rendered = render(
84
- <StructureTabPane path={STRUCTURE_NOTES_EDIT} />,
85
- { routes: [STRUCTURE_NOTES_EDIT], state }
86
- );
87
+ const rendered = render(<StructureTabPane path={STRUCTURE_NOTES_EDIT} />, {
88
+ routes: [STRUCTURE_NOTES_EDIT],
89
+ state,
90
+ });
87
91
  await waitForLoad(rendered);
88
92
  expect(rendered.container).toMatchSnapshot();
89
93
  });
90
94
 
91
95
  it("renders correctly with notes default route", async () => {
92
- const rendered = render(
93
- <StructureTabPane path={STRUCTURE_NOTES} />, { state }
94
- );
96
+ const rendered = render(<StructureTabPane path={STRUCTURE_NOTES} />, {
97
+ state,
98
+ });
95
99
  await waitForLoad(rendered);
96
100
  expect(rendered.container).toMatchSnapshot();
97
101
  });
98
102
 
99
103
  it("renders correctly with grants tab", async () => {
100
- jest.spyOn(getTabVisibilityModule, "getTabVisibility").mockReturnValue(tabVisibility);
101
- const rendered = render(<StructureTabPane path={STRUCTURE_GRANTS} />, { state });
104
+ jest
105
+ .spyOn(getTabVisibilityModule, "getTabVisibility")
106
+ .mockReturnValue(tabVisibility);
107
+ const rendered = render(<StructureTabPane path={STRUCTURE_GRANTS} />, {
108
+ state,
109
+ });
102
110
  await waitForLoad(rendered);
103
111
  expect(rendered.container).toMatchSnapshot();
104
112
  });
105
113
 
106
114
  it("renders correctly with profile tab", async () => {
107
- jest.spyOn(getTabVisibilityModule, "getTabVisibility").mockReturnValue(tabVisibility);
108
- const rendered = render(
109
- <StructureTabPane path={STRUCTURE_PROFILE} />, { state }
110
- );
115
+ jest
116
+ .spyOn(getTabVisibilityModule, "getTabVisibility")
117
+ .mockReturnValue(tabVisibility);
118
+ const rendered = render(<StructureTabPane path={STRUCTURE_PROFILE} />, {
119
+ state,
120
+ });
111
121
  await waitForLoad(rendered);
112
122
  expect(rendered.container).toMatchSnapshot();
113
123
  });
114
124
 
115
125
  it("renders correctly with metadata tab", async () => {
116
- jest.spyOn(getTabVisibilityModule, "getTabVisibility").mockReturnValue(tabVisibility);
117
- const rendered = render(<StructureTabPane path={STRUCTURE_METADATA} />, { state });
126
+ jest
127
+ .spyOn(getTabVisibilityModule, "getTabVisibility")
128
+ .mockReturnValue(tabVisibility);
129
+ const rendered = render(<StructureTabPane path={STRUCTURE_METADATA} />, {
130
+ state,
131
+ });
118
132
  await waitForLoad(rendered);
119
133
  expect(rendered.container).toMatchSnapshot();
120
134
  });
121
135
 
122
136
  it("renders correctly with rules tab", async () => {
123
- jest.spyOn(getTabVisibilityModule, "getTabVisibility").mockReturnValue(tabVisibility);
124
- const rendered = render(<StructureTabPane path={STRUCTURE_RULES} />, { state });
137
+ jest
138
+ .spyOn(getTabVisibilityModule, "getTabVisibility")
139
+ .mockReturnValue(tabVisibility);
140
+ const rendered = render(<StructureTabPane path={STRUCTURE_RULES} />, {
141
+ state,
142
+ });
125
143
  await waitForLoad(rendered);
126
144
  expect(rendered.container).toMatchSnapshot();
127
145
  });
128
146
 
129
147
  it("renders correctly with links tab", async () => {
130
- jest.spyOn(getTabVisibilityModule, "getTabVisibility").mockReturnValue(tabVisibility);
131
- const rendered = render(<StructureTabPane path={STRUCTURE_LINKS} />, { state });
148
+ jest
149
+ .spyOn(getTabVisibilityModule, "getTabVisibility")
150
+ .mockReturnValue(tabVisibility);
151
+ const rendered = render(<StructureTabPane path={STRUCTURE_LINKS} />, {
152
+ state,
153
+ });
132
154
  await waitForLoad(rendered);
133
155
  expect(rendered.container).toMatchSnapshot();
134
156
  });
135
157
 
136
158
  it("renders correctly with structure links tab", async () => {
137
- jest.spyOn(getTabVisibilityModule, "getTabVisibility").mockReturnValue(tabVisibility);
138
- const rendered = render(<StructureTabPane path={STRUCTURE_STRUCTURE_LINKS} />, { state }
159
+ jest
160
+ .spyOn(getTabVisibilityModule, "getTabVisibility")
161
+ .mockReturnValue(tabVisibility);
162
+ const rendered = render(
163
+ <StructureTabPane path={STRUCTURE_STRUCTURE_LINKS} />,
164
+ { state }
139
165
  );
140
166
  await waitForLoad(rendered);
141
167
  expect(rendered.container).toMatchSnapshot();
142
168
  });
143
169
 
144
170
  it("renders correctly with parents tab", async () => {
145
- jest.spyOn(getTabVisibilityModule, "getTabVisibility").mockReturnValue(tabVisibility);
146
- const rendered = render(<StructureTabPane path={STRUCTURE_PARENTS} />, { state });
171
+ jest
172
+ .spyOn(getTabVisibilityModule, "getTabVisibility")
173
+ .mockReturnValue(tabVisibility);
174
+ const rendered = render(<StructureTabPane path={STRUCTURE_PARENTS} />, {
175
+ state,
176
+ });
147
177
  await waitForLoad(rendered);
148
178
  expect(rendered.container).toMatchSnapshot();
149
179
  });
150
180
 
151
181
  it("renders correctly with children tab", async () => {
152
- jest.spyOn(getTabVisibilityModule, "getTabVisibility").mockReturnValue(tabVisibility);
153
- const rendered = render(<StructureTabPane path={STRUCTURE_CHILDREN} />, { state });
182
+ jest
183
+ .spyOn(getTabVisibilityModule, "getTabVisibility")
184
+ .mockReturnValue(tabVisibility);
185
+ const rendered = render(<StructureTabPane path={STRUCTURE_CHILDREN} />, {
186
+ state,
187
+ });
154
188
  await waitForLoad(rendered);
155
189
  expect(rendered.container).toMatchSnapshot();
156
190
  });
157
191
 
158
192
  it("renders correctly with versions tab", async () => {
159
- jest.spyOn(getTabVisibilityModule, "getTabVisibility").mockReturnValue(tabVisibility);
160
- const rendered = render(<StructureTabPane path={STRUCTURE_VERSIONS} />, { state });
193
+ jest
194
+ .spyOn(getTabVisibilityModule, "getTabVisibility")
195
+ .mockReturnValue(tabVisibility);
196
+ const rendered = render(<StructureTabPane path={STRUCTURE_VERSIONS} />, {
197
+ state,
198
+ });
161
199
  await waitForLoad(rendered);
162
200
  expect(rendered.container).toMatchSnapshot();
163
201
  });
164
202
 
165
203
  it("renders correctly with events tab", async () => {
166
- jest.spyOn(getTabVisibilityModule, "getTabVisibility").mockReturnValue(tabVisibility);
167
- const rendered = render(<StructureTabPane path={STRUCTURE_EVENTS} />, { state });
204
+ jest
205
+ .spyOn(getTabVisibilityModule, "getTabVisibility")
206
+ .mockReturnValue(tabVisibility);
207
+ const rendered = render(<StructureTabPane path={STRUCTURE_EVENTS} />, {
208
+ state,
209
+ });
168
210
  await waitForLoad(rendered);
169
211
  expect(rendered.container).toMatchSnapshot();
170
212
  });
171
213
 
172
214
  it("renders correctly with roles tab", async () => {
173
- jest.spyOn(getTabVisibilityModule, "getTabVisibility").mockReturnValue(tabVisibility);
174
- const rendered = render(<StructureTabPane path={STRUCTURE_MEMBERS} />, { state });
215
+ jest
216
+ .spyOn(getTabVisibilityModule, "getTabVisibility")
217
+ .mockReturnValue(tabVisibility);
218
+ const rendered = render(<StructureTabPane path={STRUCTURE_MEMBERS} />, {
219
+ state,
220
+ });
175
221
  await waitForLoad(rendered);
176
222
  expect(rendered.container).toMatchSnapshot();
177
223
  });
178
224
 
179
225
  it("renders correctly with lineage tab", async () => {
180
- jest.spyOn(getTabVisibilityModule, "getTabVisibility").mockReturnValue(tabVisibility);
181
- const rendered = render(
182
- <StructureTabPane path={STRUCTURE_LINEAGE} />, { state }
183
- );
226
+ jest
227
+ .spyOn(getTabVisibilityModule, "getTabVisibility")
228
+ .mockReturnValue(tabVisibility);
229
+ const rendered = render(<StructureTabPane path={STRUCTURE_LINEAGE} />, {
230
+ state,
231
+ });
184
232
  await waitForLoad(rendered);
185
233
  expect(rendered.container).toMatchSnapshot();
186
234
  });
187
235
 
188
236
  it("renders correctly with impact tab", async () => {
189
- jest.spyOn(getTabVisibilityModule, "getTabVisibility").mockReturnValue(tabVisibility);
190
- const rendered = render(
191
- <StructureTabPane path={STRUCTURE_IMPACT} />, { state }
192
- );
237
+ jest
238
+ .spyOn(getTabVisibilityModule, "getTabVisibility")
239
+ .mockReturnValue(tabVisibility);
240
+ const rendered = render(<StructureTabPane path={STRUCTURE_IMPACT} />, {
241
+ state,
242
+ });
193
243
  await waitForLoad(rendered);
194
244
  expect(rendered.container).toMatchSnapshot();
195
245
  });
196
246
 
197
247
  it("renders correctly with custom tab", async () => {
198
- jest.spyOn(getTabVisibilityModule, "getTabVisibility").mockReturnValue(tabVisibility);
199
- jest.spyOn(useCustomTabsModule, "useStructureCustomTabs").mockReturnValue([{ name: "custom", route: "/structures/:id/custom", content: <p>Custom</p> },]);
248
+ jest
249
+ .spyOn(getTabVisibilityModule, "getTabVisibility")
250
+ .mockReturnValue(tabVisibility);
251
+ jest
252
+ .spyOn(useCustomTabsModule, "useStructureCustomTabs")
253
+ .mockReturnValue([
254
+ {
255
+ name: "custom",
256
+ route: "/structures/:id/custom",
257
+ content: <p>Custom</p>,
258
+ },
259
+ ]);
200
260
  const rendered = render(
201
- <StructureTabPane path={"/structures/:id/custom"} />, { state }
261
+ <StructureTabPane path={"/structures/:id/custom"} />,
262
+ { state }
202
263
  );
203
264
  await waitForLoad(rendered);
204
265
  await waitFor(() => {
@@ -1,6 +1,32 @@
1
1
  import { render, waitForLoad } from "@truedat/test/render";
2
2
  import { StructureVersions } from "../StructureVersions";
3
3
 
4
+ jest.mock("react-router", () => ({
5
+ ...jest.requireActual("react-router"),
6
+ useParams: jest.fn(() => ({ id: "1" })),
7
+ }));
8
+
9
+ jest.mock("@apollo/client", () => ({
10
+ gql: jest.fn((query) => query),
11
+ useQuery: jest.fn(() => ({
12
+ data: {
13
+ dataStructureVersion: {
14
+ versions: [
15
+ {
16
+ version: "0",
17
+ inserted_at: "2018-01-01T01:01:01.123456Z",
18
+ },
19
+ {
20
+ version: "1",
21
+ inserted_at: "2018-02-01T01:01:01.123456Z",
22
+ },
23
+ ],
24
+ },
25
+ },
26
+ })),
27
+ }));
28
+
29
+
4
30
  describe("<StructureVersions />", () => {
5
31
  it("matches the latest snapshot", async () => {
6
32
  const versions = [
@@ -1,4 +1,4 @@
1
- import { clearStructure, fetchStructure } from "../../routines";
1
+ import { clearStructure, fetchStructure, fetchStructureChildrens, fetchStructureParents } from "../../routines";
2
2
  import { childrenRelations, parentRelations } from "..";
3
3
 
4
4
  const fooState = { foo: "bar" };
@@ -48,7 +48,7 @@ describe("reducers: childrenRelations", () => {
48
48
  it("should handle the fetchStructures.SUCCESS action", () => {
49
49
  expect(
50
50
  childrenRelations(fooState, {
51
- type: fetchStructure.SUCCESS,
51
+ type: fetchStructureChildrens.SUCCESS,
52
52
  payload: { data }
53
53
  })
54
54
  ).toMatchObject(children);
@@ -77,7 +77,7 @@ describe("reducers: parentRelations", () => {
77
77
  it("should handle the fetchStructures.SUCCESS action", () => {
78
78
  expect(
79
79
  parentRelations(fooState, {
80
- type: fetchStructure.SUCCESS,
80
+ type: fetchStructureParents.SUCCESS,
81
81
  payload: { data }
82
82
  })
83
83
  ).toMatchObject(parents);
@@ -1,4 +1,4 @@
1
- import { clearStructure, fetchStructure } from "../../routines";
1
+ import { clearStructure, fetchStructure, fetchStructureVersions } from "../../routines";
2
2
  import { structureVersions } from "..";
3
3
 
4
4
  const fooState = { foo: "bar" };
@@ -19,7 +19,7 @@ describe("reducers: structureVersions", () => {
19
19
  it("should handle the fetchStructure.TRIGGER action", () => {
20
20
  expect(
21
21
  structureVersions(fooState, { type: fetchStructure.TRIGGER })
22
- ).toEqual(initialState);
22
+ ).toEqual(fooState);
23
23
  });
24
24
 
25
25
  it("should handle the fetchStructure.SUCCESS action", () => {
@@ -28,7 +28,7 @@ describe("reducers: structureVersions", () => {
28
28
 
29
29
  expect(
30
30
  structureVersions(fooState, {
31
- type: fetchStructure.SUCCESS,
31
+ type: fetchStructureVersions.SUCCESS,
32
32
  payload: { data }
33
33
  })
34
34
  ).toMatchObject(versions);
@@ -29,9 +29,10 @@ const structureVersionFields = _.pick([
29
29
  "deleted_at",
30
30
  "description",
31
31
  "grant",
32
- "grants",
32
+ "grants_count",
33
33
  "group",
34
34
  "implementation_count",
35
+ "data_fields_count",
35
36
  "metadata",
36
37
  "name",
37
38
  "note",
@@ -42,6 +43,7 @@ const structureVersionFields = _.pick([
42
43
  "system",
43
44
  "type",
44
45
  "_actions",
46
+ "version_count",
45
47
  ]);
46
48
 
47
49
  const structure = (state = initialState, { type, payload }) => {
@@ -1,5 +1,5 @@
1
1
  import _ from "lodash/fp";
2
- import { clearStructure, fetchStructure } from "../routines";
2
+ import { clearStructure, fetchStructureChildrens, fetchStructureParents } from "../routines";
3
3
 
4
4
  export const initialState = [];
5
5
 
@@ -7,9 +7,9 @@ export const childrenRelations = (state = initialState, { type, payload }) => {
7
7
  switch (type) {
8
8
  case clearStructure.TRIGGER:
9
9
  return initialState;
10
- case fetchStructure.TRIGGER:
10
+ case fetchStructureChildrens.TRIGGER:
11
11
  return initialState;
12
- case fetchStructure.SUCCESS:
12
+ case fetchStructureChildrens.SUCCESS:
13
13
  return _.pathOr([], "data.relations.children")(payload);
14
14
  default:
15
15
  return state;
@@ -20,7 +20,7 @@ export const parentRelations = (state = initialState, { type, payload }) => {
20
20
  switch (type) {
21
21
  case clearStructure.TRIGGER:
22
22
  return initialState;
23
- case fetchStructure.SUCCESS:
23
+ case fetchStructureParents.SUCCESS:
24
24
  return _.pathOr([], "data.relations.parents")(payload);
25
25
  default:
26
26
  return state;
@@ -1,19 +1,20 @@
1
1
  import _ from "lodash/fp";
2
- import { clearStructure, fetchStructure } from "../routines";
2
+ import { clearStructure, fetchStructureVersions } from "../routines";
3
3
 
4
4
  const initialState = [];
5
5
 
6
- const structureVersions = (state = initialState, { type, payload }) => {
6
+ const structureVersions = (state = initialState, { type, payload }) => {
7
7
  switch (type) {
8
8
  case clearStructure.TRIGGER:
9
9
  return initialState;
10
- case fetchStructure.TRIGGER:
10
+ case fetchStructureVersions.TRIGGER:
11
11
  return initialState;
12
- case fetchStructure.SUCCESS:
12
+ case fetchStructureVersions.SUCCESS:
13
+ console.log("payload", payload);
13
14
  return _.pathOr([], "data.versions")(payload);
14
15
  default:
15
16
  return state;
16
17
  }
17
18
  };
18
19
 
19
- export { structureVersions };
20
+ export { structureVersions };
package/src/routines.js CHANGED
@@ -89,6 +89,9 @@ export const fetchNodes = createRoutine("FETCH_NODES");
89
89
  export const fetchProfileExecution = createRoutine("FETCH_EXECUTION_PROFILE");
90
90
  export const fetchProfileGroup = createRoutine("FETCH_PROFILE_GROUP");
91
91
  export const fetchStructure = createRoutine("FETCH_STRUCTURE");
92
+ export const fetchStructureParents = createRoutine("FETCH_STRUCTURE_PARENTS");
93
+ export const fetchStructureChildrens = createRoutine("FETCH_STRUCTURE_CHILDREN");
94
+ export const fetchStructureVersions = createRoutine("FETCH_STRUCTURE_VERSIONS");
92
95
  export const fetchStructureGraph = createRoutine("FETCH_STRUCTURE_GRAPH");
93
96
  export const fetchStructureNotes = createRoutine("FETCH_STRUCTURE_NOTES");
94
97
  export const fetchStructures = createRoutine("FETCH_STRUCTURES");
@@ -0,0 +1,48 @@
1
+ import _ from "lodash/fp";
2
+ import { call, put, takeLatest, select, getContext } from "redux-saga/effects";
3
+ import { fetchStructureChildrens } from "../routines";
4
+ import { DATA_STRUCTURE_CHILDREN_QUERY } from "../api/queries";
5
+ import { getStructureFieldColumns } from "../selectors";
6
+
7
+ export function* fetchStructureChildrenSaga({ payload }) {
8
+ try {
9
+ const client = yield getContext("client");
10
+
11
+ yield put(fetchStructureChildrens.request());
12
+
13
+ const { id, version = "latest" } = payload;
14
+ const {
15
+ data: { dataStructureVersion },
16
+ } = yield call(client.query, {
17
+ fetchPolicy: "network-only",
18
+ query: DATA_STRUCTURE_CHILDREN_QUERY,
19
+ variables: {
20
+ dataStructureId: id,
21
+ version,
22
+ },
23
+ });
24
+
25
+ const data = {
26
+ data: dataStructureVersion,
27
+ _actions: dataStructureVersion._actions,
28
+ user_permissions: dataStructureVersion.user_permissions,
29
+ };
30
+
31
+ const meta = { version };
32
+
33
+ yield put({ meta, ...fetchStructureChildrens.success(data) });
34
+ } catch (error) {
35
+ if (error.response) {
36
+ const { status, data } = error.response;
37
+ yield put(fetchStructureChildrens.failure({ status, data }));
38
+ } else {
39
+ yield put(fetchStructureChildrens.failure(error.message));
40
+ }
41
+ } finally {
42
+ yield put(fetchStructureChildrens.fulfill());
43
+ }
44
+ }
45
+
46
+ export function* fetchStructureChildrensRequestSaga() {
47
+ yield takeLatest(fetchStructureChildrens.TRIGGER, fetchStructureChildrensSaga);
48
+ }
@@ -0,0 +1,48 @@
1
+ import _ from "lodash/fp";
2
+ import { call, put, takeLatest, select, getContext } from "redux-saga/effects";
3
+ import { fetchStructureParents } from "../routines";
4
+ import { DATA_STRUCTURE_PARENTS_QUERY } from "../api/queries";
5
+ import { getStructureFieldColumns } from "../selectors";
6
+
7
+ export function* fetchStructureParentsSaga({ payload }) {
8
+ try {
9
+ const client = yield getContext("client");
10
+
11
+ yield put(fetchStructureParents.request());
12
+
13
+ const { id, version = "latest" } = payload;
14
+ const {
15
+ data: { dataStructureVersion },
16
+ } = yield call(client.query, {
17
+ fetchPolicy: "network-only",
18
+ query: DATA_STRUCTURE_PARENTS_QUERY,
19
+ variables: {
20
+ dataStructureId: id,
21
+ version,
22
+ },
23
+ });
24
+
25
+ const data = {
26
+ data: dataStructureVersion,
27
+ _actions: dataStructureVersion._actions,
28
+ user_permissions: dataStructureVersion.user_permissions,
29
+ };
30
+
31
+ const meta = { version };
32
+
33
+ yield put({ meta, ...fetchStructureParents.success(data) });
34
+ } catch (error) {
35
+ if (error.response) {
36
+ const { status, data } = error.response;
37
+ yield put(fetchStructureParents.failure({ status, data }));
38
+ } else {
39
+ yield put(fetchStructureParents.failure(error.message));
40
+ }
41
+ } finally {
42
+ yield put(fetchStructureParents.fulfill());
43
+ }
44
+ }
45
+
46
+ export function* fetchStructureParentsRequestSaga() {
47
+ yield takeLatest(fetchStructureParents.TRIGGER, fetchStructureParentsSaga);
48
+ }
@@ -0,0 +1,46 @@
1
+ import { call, put, takeLatest, getContext } from "redux-saga/effects";
2
+ import { fetchStructureVersions } from "../routines";
3
+ import { DATA_STRUCTURE_VERSION_VERSIONS_QUERY } from "../api/queries";
4
+
5
+ export function* fetchStructureVersionsSaga({ payload }) {
6
+ try {
7
+ const client = yield getContext("client");
8
+
9
+ yield put(fetchStructureVersions.request());
10
+
11
+ const { id, version = "latest" } = payload;
12
+ const {
13
+ data: { dataStructureVersion },
14
+ } = yield call(client.query, {
15
+ fetchPolicy: "network-only",
16
+ query: DATA_STRUCTURE_VERSION_VERSIONS_QUERY,
17
+ variables: {
18
+ dataStructureId: id,
19
+ version,
20
+ },
21
+ });
22
+
23
+ const data = {
24
+ data: dataStructureVersion,
25
+ _actions: dataStructureVersion._actions,
26
+ user_permissions: dataStructureVersion.user_permissions,
27
+ };
28
+
29
+ const meta = { version };
30
+
31
+ yield put({ meta, ...fetchStructureVersions.success(data) });
32
+ } catch (error) {
33
+ if (error.response) {
34
+ const { status, data } = error.response;
35
+ yield put(fetchStructureVersions.failure({ status, data }));
36
+ } else {
37
+ yield put(fetchStructureVersions.failure(error.message));
38
+ }
39
+ } finally {
40
+ yield put(fetchStructureVersions.fulfill());
41
+ }
42
+ }
43
+
44
+ export function* fetchStructureVersionsRequestSaga() {
45
+ yield takeLatest(fetchStructureVersions.TRIGGER, fetchStructureVersionsSaga);
46
+ }
@@ -32,6 +32,8 @@ import { fetchProfileGroupRequestSaga } from "./fetchProfileGroup";
32
32
  import { fetchStructureGraphRequestSaga } from "./fetchStructureGraph";
33
33
  import { fetchStructureNotesRequestSaga } from "./fetchStructureNotes";
34
34
  import { fetchStructureRequestSaga } from "./fetchStructure";
35
+ import { fetchStructureParentsRequestSaga } from "./fetchStructureParents";
36
+ import { fetchStructureChildrensRequestSaga } from "./fetchStructureChildrens";
35
37
  import { fetchStructureTypeRequestSaga } from "./fetchStructureType";
36
38
  import { fetchStructureTypesRequestSaga } from "./fetchStructureTypes";
37
39
  import { fetchStructuresRequestSaga } from "./fetchStructures";
@@ -2,10 +2,10 @@ import { getTabVisibility } from "../getTabVisibility";
2
2
 
3
3
  describe("selectors: getTabVisibility", () => {
4
4
  it("should include fields iff structureFields is not empty", () => {
5
- expect(getTabVisibility({ structureFields: [1] })).toMatchObject({
5
+ expect(getTabVisibility({ structure: { data_fields_count: 1 } })).toMatchObject({
6
6
  fields: true,
7
7
  });
8
- expect(getTabVisibility({ structureFields: [] })).toMatchObject({
8
+ expect(getTabVisibility({ structure: { data_fields_count: 0 } })).toMatchObject({
9
9
  fields: false,
10
10
  });
11
11
  });
@@ -122,10 +122,10 @@ describe("selectors: getTabVisibility", () => {
122
122
 
123
123
  it("should include versions iff structureVersions is not empty", () => {
124
124
  expect(getTabVisibility({})).toMatchObject({ versions: false });
125
- expect(getTabVisibility({ structureVersions: [] })).toMatchObject({
125
+ expect(getTabVisibility({ structure: { version_count: 0 } })).toMatchObject({
126
126
  versions: false,
127
127
  });
128
- expect(getTabVisibility({ structureVersions: [{}] })).toMatchObject({
128
+ expect(getTabVisibility({ structure: { version_count: 1 } })).toMatchObject({
129
129
  versions: true,
130
130
  });
131
131
  });
@@ -224,10 +224,10 @@ describe("selectors: getTabVisibility", () => {
224
224
  expect(getTabVisibility({ structure: {} })).toMatchObject({
225
225
  grants: false,
226
226
  });
227
- expect(getTabVisibility({ structure: { grants: [] } })).toMatchObject({
227
+ expect(getTabVisibility({ structure: { grants_count: 0 } })).toMatchObject({
228
228
  grants: false,
229
229
  });
230
- expect(getTabVisibility({ structure: { grants: [{}] } })).toMatchObject({
230
+ expect(getTabVisibility({ structure: { grants_count: 1 } })).toMatchObject({
231
231
  grants: true,
232
232
  });
233
233
  });
@@ -6,7 +6,7 @@ const notEmpty = _.negate(_.isEmpty);
6
6
  const notEmptyPath = (path) => _.flow(_.path(path), notEmpty);
7
7
  const hasProfile = notEmptyPath("profile");
8
8
 
9
- const fieldsTabVisible = _.conformsTo({ structureFields: notEmpty });
9
+ const fieldsTabVisible = (state) => state?.structure?.data_fields_count > 0;
10
10
 
11
11
  const profileTabVisible = ({
12
12
  userPermissions,
@@ -36,7 +36,8 @@ const notesTabVisible = ({ templatesLoading, templates, structure }) =>
36
36
  _.negate(_.isNil)
37
37
  )(templates);
38
38
 
39
- const versionsTabVisible = notEmptyPath("structureVersions");
39
+ // const versionsTabVisible = notEmptyPath("structureVersions");
40
+ const versionsTabVisible = (state) => state?.structure?.version_count > 0;
40
41
 
41
42
  const structureLinksTabVisible = (state) =>
42
43
  _.has("create_struct_to_struct_link", state?.structureActions) ||
@@ -56,7 +57,8 @@ const eventsTabVisible = _.flow(
56
57
  _.prop("structureVersion"),
57
58
  _.isEqual("latest")
58
59
  );
59
- const grantsTabVisible = _.flow(_.path("structure.grants"), notEmpty);
60
+ const grantsTabVisible = (state) => state?.structure?.grants_count > 0;
61
+
60
62
  const metatadaVisible = (state) => !_.isEmpty(_.path(["structure", "structure_type", "metadata_views"])(state))
61
63
 
62
64
  export const getTabVisibility = createSelector([state => state], (state) => ({