@truedat/dd 5.2.9 → 5.3.1
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 +12 -0
- package/package.json +6 -6
- package/src/api/queries.js +11 -0
- package/src/api.js +5 -0
- package/src/components/LinkLabelsSelector.js +74 -0
- package/src/components/ReferenceDatasetFieldSelector.js +146 -0
- package/src/components/StructureStructureForm.js +95 -0
- package/src/components/StructureStructureLinks.js +89 -3
- package/src/components/StructureTabPane.js +11 -1
- package/src/components/StructureTabPaneRoutes.js +2 -2
- package/src/components/StructureTabRoutes.js +2 -2
- package/src/components/StructureTabs.js +1 -1
- package/src/components/__tests__/ReferenceDatasetFieldSelector.spec.js +86 -0
- package/src/components/__tests__/StructureStructureForm.spec.js +138 -0
- package/src/components/__tests__/StructureStructureLinks.spec.js +109 -35
- package/src/messages/en.js +2 -0
- package/src/messages/es.js +3 -0
- package/src/reducers/structureRedirect.js +6 -0
- package/src/routines.js +3 -1
- package/src/sagas/__tests__/deleteStructureStructureLink.spec.js +101 -0
- package/src/sagas/deleteStructureStructureLink.js +45 -0
- package/src/sagas/index.js +6 -0
- package/src/sagas/linkStructureToStructure.js +46 -0
- package/src/selectors/__tests__/getStructuresFields.spec.js +2 -2
- package/src/selectors/getActiveTab.js +2 -2
- package/src/selectors/getTabVisibility.js +1 -0
- package/src/components/__tests__/__snapshots__/StructureStructureLinks.spec.js.snap +0 -287
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { waitFor } from "@testing-library/react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import { render } from "@truedat/test/render";
|
|
5
|
+
import { REFERENCE_DATASETS_QUERY } from "../../api/queries";
|
|
6
|
+
import ReferenceDatasetFieldSelector from "../ReferenceDatasetFieldSelector";
|
|
7
|
+
|
|
8
|
+
const referenceDatasets = [
|
|
9
|
+
{
|
|
10
|
+
__typename: "ReferenceDataset",
|
|
11
|
+
id: "1",
|
|
12
|
+
name: "countries",
|
|
13
|
+
headers: ["CODE", "DESC_ES", "DESC_EN"],
|
|
14
|
+
rowCount: 9,
|
|
15
|
+
updatedAt: "2023-03-04T12:10:53.245332Z",
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
__typename: "ReferenceDataset",
|
|
19
|
+
id: "2",
|
|
20
|
+
name: "currencies",
|
|
21
|
+
headers: ["ENTITY", "Currency", "Alphabetic Code", "Numeric Code"],
|
|
22
|
+
rowCount: 5,
|
|
23
|
+
updatedAt: "2023-03-05T09:57:59.765822Z",
|
|
24
|
+
},
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
const referenceDatasetsMock = {
|
|
28
|
+
request: {
|
|
29
|
+
query: REFERENCE_DATASETS_QUERY,
|
|
30
|
+
},
|
|
31
|
+
result: { data: { referenceDatasets } },
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
describe("<ReferenceDatasetFieldSelector />", () => {
|
|
35
|
+
it("selecting reference dataset fills fields dropdown; clicking a field calls onChange", async () => {
|
|
36
|
+
const onChange = jest.fn();
|
|
37
|
+
const renderOpts = {
|
|
38
|
+
mocks: [referenceDatasetsMock],
|
|
39
|
+
};
|
|
40
|
+
const { queryAllByText, getByRole } = render(
|
|
41
|
+
<ReferenceDatasetFieldSelector onChange={onChange} />,
|
|
42
|
+
renderOpts
|
|
43
|
+
);
|
|
44
|
+
const countriesOptionQuery = () =>
|
|
45
|
+
getByRole("option", { name: /countries/i });
|
|
46
|
+
await waitFor(() => {
|
|
47
|
+
// also matches "reference dataset field", get first
|
|
48
|
+
expect(queryAllByText(/Reference dataset/i)[0]).toBeInTheDocument();
|
|
49
|
+
// same text for label and dropdown, match the second one, the dropdown
|
|
50
|
+
expect(queryAllByText(/Reference dataset field/i)[1]).toBeInTheDocument();
|
|
51
|
+
expect(countriesOptionQuery()).toBeInTheDocument();
|
|
52
|
+
expect(getByRole("option", { name: /currencies/i })).toBeInTheDocument();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
userEvent.click(countriesOptionQuery());
|
|
56
|
+
|
|
57
|
+
const optionQuery = () => getByRole("option", { name: /CODE/i });
|
|
58
|
+
await waitFor(() => {
|
|
59
|
+
expect(optionQuery()).toBeInTheDocument();
|
|
60
|
+
});
|
|
61
|
+
userEvent.click(optionQuery());
|
|
62
|
+
expect(onChange).toHaveBeenNthCalledWith(
|
|
63
|
+
1,
|
|
64
|
+
null,
|
|
65
|
+
{
|
|
66
|
+
name: null,
|
|
67
|
+
referenceDataset: { id: "1" },
|
|
68
|
+
type: "reference_dataset_field",
|
|
69
|
+
},
|
|
70
|
+
null
|
|
71
|
+
);
|
|
72
|
+
expect(onChange).toHaveBeenNthCalledWith(
|
|
73
|
+
2,
|
|
74
|
+
null,
|
|
75
|
+
{
|
|
76
|
+
name: "CODE",
|
|
77
|
+
referenceDataset: {
|
|
78
|
+
id: "1",
|
|
79
|
+
name: "countries",
|
|
80
|
+
},
|
|
81
|
+
type: "reference_dataset_field",
|
|
82
|
+
},
|
|
83
|
+
null
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { waitFor } from "@testing-library/react";
|
|
3
|
+
import { render } from "@truedat/test/render";
|
|
4
|
+
import userEvent from "@testing-library/user-event";
|
|
5
|
+
import StructureStructureForm from "../StructureStructureForm";
|
|
6
|
+
import { linkStructureToStructure } from "../../routines";
|
|
7
|
+
import { LINK_LABELS_QUERY } from "../../api/queries";
|
|
8
|
+
|
|
9
|
+
const linkLabels = {
|
|
10
|
+
labels: [
|
|
11
|
+
{
|
|
12
|
+
id: "1",
|
|
13
|
+
name: "is_copy_from",
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
id: "2",
|
|
17
|
+
name: "reimu_ga_tsukutta",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: "3",
|
|
21
|
+
name: "marisa_ga_tsukutta",
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const linkLabelsMock = {
|
|
27
|
+
request: { query: LINK_LABELS_QUERY },
|
|
28
|
+
result: { data: linkLabels },
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
describe("<StructureStructureForm />", () => {
|
|
32
|
+
const structure = {
|
|
33
|
+
alias: null,
|
|
34
|
+
confidential: false,
|
|
35
|
+
domain_ids: [1],
|
|
36
|
+
domains: [
|
|
37
|
+
{
|
|
38
|
+
external_id: "Acme",
|
|
39
|
+
id: 1,
|
|
40
|
+
name: "Acme",
|
|
41
|
+
parent_id: null,
|
|
42
|
+
parents: [],
|
|
43
|
+
updated_at: "2022-03-22 12:56:39.639127Z",
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
external_id: "external_id",
|
|
47
|
+
id: 67583,
|
|
48
|
+
inserted_at: "2019-11-27T08:35:44.000000Z",
|
|
49
|
+
updated_at: "2021-06-10T12:01:17.343065Z",
|
|
50
|
+
class: null,
|
|
51
|
+
classes: null,
|
|
52
|
+
data_structure_link_count: 0,
|
|
53
|
+
data_structure_type: {
|
|
54
|
+
metadata_views: [],
|
|
55
|
+
template_id: 9,
|
|
56
|
+
translation: null,
|
|
57
|
+
},
|
|
58
|
+
degree: null,
|
|
59
|
+
deleted_at: null,
|
|
60
|
+
description: null,
|
|
61
|
+
grant: null,
|
|
62
|
+
grants: [],
|
|
63
|
+
group: "group",
|
|
64
|
+
implementation_count: 0,
|
|
65
|
+
metadata: {
|
|
66
|
+
database: "database",
|
|
67
|
+
host: "host",
|
|
68
|
+
},
|
|
69
|
+
name: "structure",
|
|
70
|
+
note: {},
|
|
71
|
+
siblings: [],
|
|
72
|
+
source: {
|
|
73
|
+
external_id: "oracle_meta_ds2",
|
|
74
|
+
id: 19,
|
|
75
|
+
},
|
|
76
|
+
system: {
|
|
77
|
+
id: 2,
|
|
78
|
+
name: "SYSTEM_1",
|
|
79
|
+
},
|
|
80
|
+
type: "Schema",
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
it("matches the latest snapshot", async () => {
|
|
84
|
+
const columns = ["name", "system", "group"].map((name) => ({ name }));
|
|
85
|
+
const structureActiveFilters = {};
|
|
86
|
+
const structuresRows = [{ id: 1 }, { id: 2 }];
|
|
87
|
+
const dispatch = jest.fn();
|
|
88
|
+
|
|
89
|
+
const props = {
|
|
90
|
+
linkStructureToStructure,
|
|
91
|
+
structuresRows,
|
|
92
|
+
columns,
|
|
93
|
+
onSelect: jest.fn(),
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const renderOpts = {
|
|
97
|
+
mocks: [linkLabelsMock],
|
|
98
|
+
state: {
|
|
99
|
+
structure,
|
|
100
|
+
structures: [structure],
|
|
101
|
+
structureActiveFilters,
|
|
102
|
+
},
|
|
103
|
+
dispatch,
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const { queryByText, findByText, getByRole } = render(
|
|
107
|
+
<StructureStructureForm {...props} />,
|
|
108
|
+
renderOpts
|
|
109
|
+
);
|
|
110
|
+
await waitFor(() =>
|
|
111
|
+
expect(queryByText(/loading/i)).not.toBeInTheDocument()
|
|
112
|
+
);
|
|
113
|
+
userEvent.click(await findByText("structure"));
|
|
114
|
+
|
|
115
|
+
const saveButton = getByRole("button", { name: "Save" });
|
|
116
|
+
await waitFor(() => {
|
|
117
|
+
expect(saveButton).toBeEnabled();
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const label1 = getByRole("option", { name: "is_copy_from" });
|
|
121
|
+
const label2 = getByRole("option", { name: "reimu_ga_tsukutta" });
|
|
122
|
+
|
|
123
|
+
userEvent.click(label1);
|
|
124
|
+
userEvent.click(label2);
|
|
125
|
+
|
|
126
|
+
userEvent.click(saveButton);
|
|
127
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
128
|
+
|
|
129
|
+
expect(dispatch).toHaveBeenCalledWith({
|
|
130
|
+
...linkStructureToStructure(),
|
|
131
|
+
payload: {
|
|
132
|
+
sourceStructureId: 67583,
|
|
133
|
+
targetStructureId: 67583,
|
|
134
|
+
labelIds: ["1", "2"],
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
});
|
|
@@ -1,22 +1,27 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { render } from "@truedat/test/render";
|
|
3
3
|
import { waitFor } from "@testing-library/react";
|
|
4
|
-
import
|
|
4
|
+
import userEvent from "@testing-library/user-event";
|
|
5
5
|
import { STRUCTURE_STRUCTURE_LINKS_QUERY } from "../../api/queries";
|
|
6
|
+
import { deleteStructureStructureLink } from "../../routines";
|
|
6
7
|
import StructureStructureLinks from "../StructureStructureLinks";
|
|
7
8
|
|
|
8
9
|
jest.mock("react-router-dom", () => ({
|
|
9
10
|
...jest.requireActual("react-router-dom"),
|
|
10
|
-
useParams:
|
|
11
|
+
useParams: () => ({ id: 60217 }),
|
|
11
12
|
}));
|
|
12
13
|
|
|
13
14
|
const dataStructureWithLinks = {
|
|
14
15
|
currentVersion: {
|
|
15
16
|
id: "212416",
|
|
16
|
-
name: "
|
|
17
|
+
name: "STRUCTURE_1",
|
|
17
18
|
},
|
|
18
19
|
dataStructureLinks: [
|
|
19
20
|
{
|
|
21
|
+
_actions: {
|
|
22
|
+
delete_struct_to_struct_link: true,
|
|
23
|
+
},
|
|
24
|
+
id: "1",
|
|
20
25
|
labels: [
|
|
21
26
|
{
|
|
22
27
|
name: "copy",
|
|
@@ -24,54 +29,58 @@ const dataStructureWithLinks = {
|
|
|
24
29
|
],
|
|
25
30
|
source: {
|
|
26
31
|
currentVersion: {
|
|
27
|
-
name: "
|
|
32
|
+
name: "STRUCTURE_1",
|
|
28
33
|
pathWithIds: [
|
|
29
34
|
{
|
|
30
35
|
data_structure_id: "114207",
|
|
31
|
-
name: "
|
|
36
|
+
name: "UNO",
|
|
32
37
|
},
|
|
33
38
|
{
|
|
34
39
|
data_structure_id: "58050",
|
|
35
|
-
name: "
|
|
40
|
+
name: "DOS",
|
|
36
41
|
},
|
|
37
42
|
{
|
|
38
43
|
data_structure_id: "58045",
|
|
39
|
-
name: "
|
|
44
|
+
name: "TRES",
|
|
40
45
|
},
|
|
41
46
|
],
|
|
42
47
|
},
|
|
43
48
|
id: "60217",
|
|
44
49
|
system: {
|
|
45
50
|
id: "2",
|
|
46
|
-
name: "
|
|
51
|
+
name: "SYSTEM_1",
|
|
47
52
|
},
|
|
48
53
|
},
|
|
49
54
|
target: {
|
|
50
55
|
currentVersion: {
|
|
51
|
-
name: "
|
|
56
|
+
name: "STRUCTURE_2",
|
|
52
57
|
pathWithIds: [
|
|
53
58
|
{
|
|
54
59
|
data_structure_id: "114207",
|
|
55
|
-
name: "
|
|
60
|
+
name: "UNO",
|
|
56
61
|
},
|
|
57
62
|
{
|
|
58
63
|
data_structure_id: "58050",
|
|
59
|
-
name: "
|
|
64
|
+
name: "DOS",
|
|
60
65
|
},
|
|
61
66
|
{
|
|
62
67
|
data_structure_id: "58045",
|
|
63
|
-
name: "
|
|
68
|
+
name: "TRES",
|
|
64
69
|
},
|
|
65
70
|
],
|
|
66
71
|
},
|
|
67
72
|
id: "60218",
|
|
68
73
|
system: {
|
|
69
74
|
id: "2",
|
|
70
|
-
name: "
|
|
75
|
+
name: "SYSTEM_1",
|
|
71
76
|
},
|
|
72
77
|
},
|
|
73
78
|
},
|
|
74
79
|
{
|
|
80
|
+
_actions: {
|
|
81
|
+
delete_struct_to_struct_link: true,
|
|
82
|
+
},
|
|
83
|
+
id: "2",
|
|
75
84
|
labels: [
|
|
76
85
|
{
|
|
77
86
|
name: "label_1_1",
|
|
@@ -82,46 +91,46 @@ const dataStructureWithLinks = {
|
|
|
82
91
|
],
|
|
83
92
|
source: {
|
|
84
93
|
currentVersion: {
|
|
85
|
-
name: "
|
|
94
|
+
name: "STRUCTURE_3",
|
|
86
95
|
pathWithIds: [
|
|
87
96
|
{
|
|
88
97
|
data_structure_id: "147587",
|
|
89
|
-
name: "
|
|
98
|
+
name: "UNO",
|
|
90
99
|
},
|
|
91
100
|
{
|
|
92
101
|
data_structure_id: "151648",
|
|
93
|
-
name: "
|
|
102
|
+
name: "DOS",
|
|
94
103
|
},
|
|
95
104
|
],
|
|
96
105
|
},
|
|
97
106
|
id: "151674",
|
|
98
107
|
system: {
|
|
99
108
|
id: "11",
|
|
100
|
-
name: "
|
|
109
|
+
name: "SYSTEM_2",
|
|
101
110
|
},
|
|
102
111
|
},
|
|
103
112
|
target: {
|
|
104
113
|
currentVersion: {
|
|
105
|
-
name: "
|
|
114
|
+
name: "STRUCTURE_1",
|
|
106
115
|
pathWithIds: [
|
|
107
116
|
{
|
|
108
117
|
data_structure_id: "114207",
|
|
109
|
-
name: "
|
|
118
|
+
name: "UNO",
|
|
110
119
|
},
|
|
111
120
|
{
|
|
112
121
|
data_structure_id: "58050",
|
|
113
|
-
name: "
|
|
122
|
+
name: "DOS",
|
|
114
123
|
},
|
|
115
124
|
{
|
|
116
125
|
data_structure_id: "58045",
|
|
117
|
-
name: "
|
|
126
|
+
name: "TRES",
|
|
118
127
|
},
|
|
119
128
|
],
|
|
120
129
|
},
|
|
121
130
|
id: "60217",
|
|
122
131
|
system: {
|
|
123
132
|
id: "2",
|
|
124
|
-
name: "
|
|
133
|
+
name: "SYSTEM_1",
|
|
125
134
|
},
|
|
126
135
|
},
|
|
127
136
|
},
|
|
@@ -138,24 +147,89 @@ const linksMock = {
|
|
|
138
147
|
};
|
|
139
148
|
|
|
140
149
|
describe("<StructureStructureLinks />", () => {
|
|
141
|
-
const props = {
|
|
150
|
+
const props = {
|
|
151
|
+
dataStructureWithLinks,
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const structureActions = {
|
|
155
|
+
create_struct_to_struct_link: {
|
|
156
|
+
href: "/some/url",
|
|
157
|
+
method: "POST",
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const renderOpts = {
|
|
162
|
+
routes: ["/structures/60217/structureLinks"],
|
|
163
|
+
mocks: [linksMock],
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
it("shows structure to structure links", async () => {
|
|
167
|
+
const { getByRole } = render(<StructureStructureLinks {...props} />, {
|
|
168
|
+
...renderOpts,
|
|
169
|
+
state: { structureActions },
|
|
170
|
+
});
|
|
171
|
+
await waitFor(() => {
|
|
172
|
+
expect(getByRole("link", { name: /STRUCTURE_2/i })).toBeInTheDocument();
|
|
173
|
+
expect(getByRole("link", { name: /STRUCTURE_3/i })).toBeInTheDocument();
|
|
174
|
+
});
|
|
175
|
+
await waitFor(() => {
|
|
176
|
+
expect(getByRole("button", { name: /Add link/i })).toBeInTheDocument();
|
|
177
|
+
});
|
|
178
|
+
expect(
|
|
179
|
+
document.getElementById("delete-link-60217-60218")
|
|
180
|
+
).toBeInTheDocument();
|
|
181
|
+
expect(
|
|
182
|
+
document.getElementById("delete-link-151674-60217")
|
|
183
|
+
).toBeInTheDocument();
|
|
184
|
+
});
|
|
142
185
|
|
|
143
|
-
it("
|
|
144
|
-
const
|
|
145
|
-
routes: ["/structures/60217/structureLinks"],
|
|
146
|
-
mocks: [linksMock],
|
|
147
|
-
};
|
|
148
|
-
useParams.mockImplementation(() => ({
|
|
149
|
-
id: 60217,
|
|
150
|
-
}));
|
|
151
|
-
const { container, findByText, getByRole, queryByText, debug } = render(
|
|
186
|
+
it("hides the 'add link' and delete link buttons if there are no permissions", async () => {
|
|
187
|
+
const { getByRole, queryByRole } = render(
|
|
152
188
|
<StructureStructureLinks {...props} />,
|
|
153
189
|
renderOpts
|
|
154
190
|
);
|
|
155
191
|
await waitFor(() => {
|
|
156
|
-
expect(getByRole("link", { name: /
|
|
157
|
-
expect(getByRole("link", { name: /
|
|
192
|
+
expect(getByRole("link", { name: /STRUCTURE_2/i })).toBeInTheDocument();
|
|
193
|
+
expect(getByRole("link", { name: /STRUCTURE_3/i })).toBeInTheDocument();
|
|
194
|
+
});
|
|
195
|
+
expect(
|
|
196
|
+
queryByRole("button", { name: /Add link/i })
|
|
197
|
+
).not.toBeInTheDocument();
|
|
198
|
+
expect(document.getElementById("delete-link-60217-60218")).toBeNull();
|
|
199
|
+
expect(document.getElementById("delete-link-151674-60217")).toBeNull();
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it("deletes link", async () => {
|
|
203
|
+
const dispatch = jest.fn();
|
|
204
|
+
const { getByText, getByRole } = render(
|
|
205
|
+
<StructureStructureLinks {...props} />,
|
|
206
|
+
{
|
|
207
|
+
...renderOpts,
|
|
208
|
+
state: { structureActions },
|
|
209
|
+
dispatch,
|
|
210
|
+
}
|
|
211
|
+
);
|
|
212
|
+
await waitFor(() => {
|
|
213
|
+
expect(getByRole("link", { name: /STRUCTURE_2/i })).toBeInTheDocument();
|
|
214
|
+
expect(getByRole("link", { name: /STRUCTURE_3/i })).toBeInTheDocument();
|
|
215
|
+
});
|
|
216
|
+
await waitFor(() => {
|
|
217
|
+
expect(getByRole("button", { name: /Add link/i })).toBeInTheDocument();
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
const deleteButton = document.getElementById("delete-link-60217-60218");
|
|
221
|
+
userEvent.click(deleteButton);
|
|
222
|
+
expect(getByText("Delete link")).toBeInTheDocument();
|
|
223
|
+
const yesButton = getByRole("button", { name: /Yes/i });
|
|
224
|
+
userEvent.click(yesButton);
|
|
225
|
+
|
|
226
|
+
expect(dispatch).toHaveBeenCalledWith({
|
|
227
|
+
...deleteStructureStructureLink(),
|
|
228
|
+
payload: {
|
|
229
|
+
dataStructureLinkId: "1",
|
|
230
|
+
sourceStructureId: "60217",
|
|
231
|
+
targetStructureId: "60218",
|
|
232
|
+
},
|
|
158
233
|
});
|
|
159
|
-
expect(container).toMatchSnapshot();
|
|
160
234
|
});
|
|
161
235
|
});
|
package/src/messages/en.js
CHANGED
|
@@ -424,6 +424,8 @@ export default {
|
|
|
424
424
|
"structureLink.currentStructure": "Current structure",
|
|
425
425
|
"structureLink.label": "Relation label",
|
|
426
426
|
"structureLink.relatedStructure": "Related structure",
|
|
427
|
+
"structureLink.selector.label": "Relation label",
|
|
428
|
+
"structureLink.selector.placeholder": "Select a relation label...",
|
|
427
429
|
"structureSelector.union.criteria": "Union criteria",
|
|
428
430
|
"structureSelector.union.type": "Union type",
|
|
429
431
|
"structureTag.comment": "Comments",
|
package/src/messages/es.js
CHANGED
|
@@ -433,6 +433,9 @@ export default {
|
|
|
433
433
|
"structureLink.currentStructure": "Estructura actual",
|
|
434
434
|
"structureLink.label": "Etiqueta de relación",
|
|
435
435
|
"structureLink.relatedStructure": "Estructura relacionada",
|
|
436
|
+
"structureLink.selector.label": "Etiqueta de relación",
|
|
437
|
+
"structureLink.selector.placeholder":
|
|
438
|
+
"Seleccione una etiqueta de relación...",
|
|
436
439
|
"structureSelector.union.criteria": "Criterio de unión",
|
|
437
440
|
"structureSelector.union.type": "Tipo de unión",
|
|
438
441
|
"structureTag.comment": "Comentario",
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
STRUCTURE_TAGS,
|
|
9
9
|
STRUCTURE_TYPES,
|
|
10
10
|
STRUCTURES,
|
|
11
|
+
STRUCTURE_STRUCTURE_LINKS,
|
|
11
12
|
linkTo,
|
|
12
13
|
} from "@truedat/core/routes";
|
|
13
14
|
import {
|
|
@@ -24,6 +25,7 @@ import {
|
|
|
24
25
|
deleteStructure,
|
|
25
26
|
deleteTag,
|
|
26
27
|
doStructureNoteAction,
|
|
28
|
+
linkStructureToStructure,
|
|
27
29
|
updateGrantApprovalRule,
|
|
28
30
|
updateReferenceDataset,
|
|
29
31
|
updateStructure,
|
|
@@ -50,6 +52,10 @@ export const structureRedirect = (
|
|
|
50
52
|
const id = _.path("data.id")(payload);
|
|
51
53
|
return linkTo.SYSTEM_STRUCTURES({ id });
|
|
52
54
|
}
|
|
55
|
+
case linkStructureToStructure.SUCCESS:
|
|
56
|
+
return linkTo.STRUCTURE_STRUCTURE_LINKS({
|
|
57
|
+
id: payload.sourceStructureId,
|
|
58
|
+
});
|
|
53
59
|
case updateSystem.SUCCESS: {
|
|
54
60
|
const id = _.path("data.id")(payload);
|
|
55
61
|
return linkTo.SYSTEM_STRUCTURES({ id });
|
package/src/routines.js
CHANGED
|
@@ -51,6 +51,7 @@ export const deleteGrantApprovalRule = createRoutine("DELETE_GRANT_APPROVAL_RULE
|
|
|
51
51
|
export const deleteReferenceDataset = createRoutine("DELETE_REFERENCE_DATASET");
|
|
52
52
|
export const deleteStructureTag = createRoutine("DELETE_STRUCTURE_TAG");
|
|
53
53
|
export const deleteStructure = createRoutine("DELETE_STRUCTURE");
|
|
54
|
+
export const deleteStructureStructureLink = createRoutine("DELETE_STRUCTURE_STRUCTURE_LINK");
|
|
54
55
|
export const deleteTag = createRoutine("DELETE_TAG");
|
|
55
56
|
export const deleteUserSearchFilter = createRoutine("DELETE_USER_SEARCH_FILTER");
|
|
56
57
|
export const doStructureNoteAction = createRoutine("DO_STRUCTURE_NOTE_ACTION");
|
|
@@ -86,7 +87,7 @@ export const fetchUserSearchFilters = createRoutine("FETCH_USER_SEARCH_FILTERS")
|
|
|
86
87
|
export const filterStructures = createRoutine("FILTER_STRUCTURES");
|
|
87
88
|
export const hoverNode = createRoutine("HOVER_NODE");
|
|
88
89
|
export const initSvg = createRoutine("INIT_SVG");
|
|
89
|
-
export const
|
|
90
|
+
export const linkStructureToStructure = createRoutine("LINK_STRUCTURE_TO_STRUCTURE");
|
|
90
91
|
export const loadLineageEvents = createRoutine("LOAD_LINEAGE_EVENTS");
|
|
91
92
|
export const openGrantFilter = createRoutine("OPEN_GRANT_FILTER");
|
|
92
93
|
export const openStructureFilter = createRoutine("OPEN_STRUCTURE_FILTER");
|
|
@@ -113,6 +114,7 @@ export const setGrantChangeRequestCart = createRoutine("SET_GRANT_CHANGE_REQUEST
|
|
|
113
114
|
export const setGrantRequestsQuery = createRoutine("SET_GRANT_REQUESTS_QUERY");
|
|
114
115
|
export const sortGrants = createRoutine("SORT_GRANTS");
|
|
115
116
|
export const sortStructures = createRoutine("SORT_STRUCTURES");
|
|
117
|
+
export const tagStructure = createRoutine("TAG_STRUCTURE");
|
|
116
118
|
export const toggleGrantDateFilter = createRoutine("TOGGLE_GRANT_DATE_FILTER");
|
|
117
119
|
export const toggleGrantFilterValue = createRoutine("TOGGLE_GRANT_FILTER_VALUE");
|
|
118
120
|
export const toggleStructureDateFilter = createRoutine("TOGGLE_STRUCTURE_DATE_FILTER");
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import { compile } from "path-to-regexp";
|
|
3
|
+
import { testSaga } from "redux-saga-test-plan";
|
|
4
|
+
import { apiJsonDelete, JSON_OPTS } from "@truedat/core/services/api";
|
|
5
|
+
import {
|
|
6
|
+
deleteStructureStructureLinkSaga,
|
|
7
|
+
deleteStructureStructureLinkRequestSaga,
|
|
8
|
+
} from "../deleteStructureStructureLink";
|
|
9
|
+
import { deleteStructureStructureLink } from "../../routines";
|
|
10
|
+
import { API_STRUCTURE_TO_STRUCTURE_LINK } from "../../api";
|
|
11
|
+
|
|
12
|
+
describe("sagas: deleteStructureStructureRequestSaga", () => {
|
|
13
|
+
it("should invoke deleteStructureStructureSaga on deleteStructureStructureLink.TRIGGER", () => {
|
|
14
|
+
expect(() => {
|
|
15
|
+
testSaga(deleteStructureStructureLinkRequestSaga)
|
|
16
|
+
.next()
|
|
17
|
+
.takeLatest(
|
|
18
|
+
deleteStructureStructureLink.TRIGGER,
|
|
19
|
+
deleteStructureStructureLinkSaga
|
|
20
|
+
)
|
|
21
|
+
.finish()
|
|
22
|
+
.isDone();
|
|
23
|
+
}).not.toThrow();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("should throw exception if an unhandled action is received", () => {
|
|
27
|
+
expect(() => {
|
|
28
|
+
testSaga(deleteStructureStructureLinkRequestSaga)
|
|
29
|
+
.next()
|
|
30
|
+
.takeLatest("FOO", deleteStructureStructureLinkSaga);
|
|
31
|
+
}).toThrow();
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe("sagas: deleteStructureStructureLinkSaga", () => {
|
|
36
|
+
const sourceStructureId = 1;
|
|
37
|
+
const targetStructureId = 2;
|
|
38
|
+
const dataStructureLinkId = 1;
|
|
39
|
+
const receivedPayload = {
|
|
40
|
+
sourceStructureId,
|
|
41
|
+
targetStructureId,
|
|
42
|
+
dataStructureLinkId,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const deletePayload = {
|
|
46
|
+
sourceStructureId,
|
|
47
|
+
targetStructureId,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const url = compile(API_STRUCTURE_TO_STRUCTURE_LINK)({
|
|
51
|
+
sourceStructureId,
|
|
52
|
+
targetStructureId,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const client = {
|
|
56
|
+
cache: {
|
|
57
|
+
identify: jest.fn(),
|
|
58
|
+
evict: jest.fn(),
|
|
59
|
+
gc: jest.fn(),
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
it("should put a success action when a response is returned", () => {
|
|
64
|
+
expect(() => {
|
|
65
|
+
testSaga(deleteStructureStructureLinkSaga, { payload: receivedPayload })
|
|
66
|
+
.next()
|
|
67
|
+
.getContext("client")
|
|
68
|
+
.next(client)
|
|
69
|
+
.put(deleteStructureStructureLink.request(deletePayload))
|
|
70
|
+
.next(client)
|
|
71
|
+
.call(apiJsonDelete, url, JSON_OPTS)
|
|
72
|
+
.next({ data: deletePayload })
|
|
73
|
+
.put(deleteStructureStructureLink.success(deletePayload))
|
|
74
|
+
.next()
|
|
75
|
+
.put(deleteStructureStructureLink.fulfill())
|
|
76
|
+
.next()
|
|
77
|
+
.isDone();
|
|
78
|
+
}).not.toThrow();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("should put a failure action when a call returns an error", () => {
|
|
82
|
+
const message = "Request failed";
|
|
83
|
+
const error = { message };
|
|
84
|
+
|
|
85
|
+
expect(() => {
|
|
86
|
+
testSaga(deleteStructureStructureLinkSaga, { payload: receivedPayload })
|
|
87
|
+
.next()
|
|
88
|
+
.getContext("client")
|
|
89
|
+
.next(client)
|
|
90
|
+
.put(deleteStructureStructureLink.request(deletePayload))
|
|
91
|
+
.next(client)
|
|
92
|
+
.call(apiJsonDelete, url, JSON_OPTS)
|
|
93
|
+
.throw(error)
|
|
94
|
+
.put(deleteStructureStructureLink.failure(message))
|
|
95
|
+
.next()
|
|
96
|
+
.put(deleteStructureStructureLink.fulfill())
|
|
97
|
+
.next()
|
|
98
|
+
.isDone();
|
|
99
|
+
}).not.toThrow();
|
|
100
|
+
});
|
|
101
|
+
});
|