@truedat/dq 4.42.6 → 4.43.0

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.
Files changed (32) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/package.json +5 -5
  3. package/src/api.js +6 -0
  4. package/src/components/ImplementationStructureDelete.js +41 -0
  5. package/src/components/ImplementationStructureLink.js +24 -0
  6. package/src/components/ImplementationStructures.js +138 -0
  7. package/src/components/ImplementationStructuresNew.js +125 -0
  8. package/src/components/RuleImplementationTabs.js +16 -1
  9. package/src/components/RuleImplementationsTable.js +10 -3
  10. package/src/components/RuleRoutes.js +37 -1
  11. package/src/components/__tests__/ImplementationStructureDelete.spec.js +27 -0
  12. package/src/components/__tests__/ImplementationStructureLink.spec.js +23 -0
  13. package/src/components/__tests__/ImplementationStructures.spec.js +88 -0
  14. package/src/components/__tests__/ImplementationStructuresNew.spec.js +34 -0
  15. package/src/components/__tests__/RuleImplementation.spec.js +2 -1
  16. package/src/components/__tests__/RuleImplementationTabs.spec.js +2 -1
  17. package/src/components/__tests__/__snapshots__/ImplementationStructureDelete.spec.js.snap +10 -0
  18. package/src/components/__tests__/__snapshots__/ImplementationStructureLink.spec.js.snap +11 -0
  19. package/src/components/__tests__/__snapshots__/ImplementationStructures.spec.js.snap +208 -0
  20. package/src/components/__tests__/__snapshots__/ImplementationStructuresNew.spec.js.snap +106 -0
  21. package/src/components/__tests__/__snapshots__/RuleImplementation.spec.js.snap +7 -1
  22. package/src/components/__tests__/__snapshots__/RuleImplementationTabs.spec.js.snap +8 -2
  23. package/src/messages/en.js +13 -1
  24. package/src/messages/es.js +14 -1
  25. package/src/reducers/ruleImplementation.js +1 -0
  26. package/src/reducers/ruleImplementationRedirect.js +4 -0
  27. package/src/routines.js +7 -0
  28. package/src/sagas/__tests__/createImplementationStructure.spec.js +86 -0
  29. package/src/sagas/__tests__/deleteImplementationStructure.spec.js +84 -0
  30. package/src/sagas/createImplementationStructure.js +32 -0
  31. package/src/sagas/deleteImplementationStructure.js +37 -0
  32. package/src/sagas/index.js +16 -10
@@ -0,0 +1,34 @@
1
+ import React, { Suspense } from "react";
2
+ import { render } from "@truedat/test/render";
3
+ import { ImplementationStructuresNew } from "../ImplementationStructuresNew";
4
+
5
+ describe("<ImplementationStructuresNew />", () => {
6
+ const renderOpts = {
7
+ messages: {
8
+ en: {
9
+ "actions.create": "create",
10
+ "actions.cancel": "cancel",
11
+ "implementationsStructures.new.header": "header",
12
+ "implementationsStructures.type": "type",
13
+ "implementationsStructures.type.dataset": "dataset",
14
+ "implementationsStructures.type.population": "population",
15
+ "implementationsStructures.type.validation": "validation",
16
+ },
17
+ },
18
+ };
19
+
20
+ it("matches the latest snapshot", () => {
21
+ const props = {
22
+ implementation: {},
23
+ creatingImplementationStructure: false,
24
+ createImplementationStructure: () => {},
25
+ };
26
+ const { container } = render(
27
+ <Suspense fallback={null}>
28
+ <ImplementationStructuresNew {...props} />,
29
+ </Suspense>,
30
+ renderOpts
31
+ );
32
+ expect(container).toMatchSnapshot();
33
+ });
34
+ });
@@ -27,7 +27,8 @@ const renderOpts = {
27
27
  "ruleImplementation.actions.deprecate": "archive",
28
28
  "ruleImplementation.actions.edit": "edit",
29
29
  "ruleImplementation.actions.move": "move",
30
- "tabs.dq.implementation.links": "Related concepts",
30
+ "tabs.dq.implementation.links.concepts": "Related concepts",
31
+ "tabs.dq.implementation.structures": "Structures",
31
32
  "tabs.dq.ruleImplementation.audit": "events",
32
33
  "tabs.dq.ruleImplementation.results": "results",
33
34
  "tabs.dq.ruleImplementation": "implementation",
@@ -7,7 +7,8 @@ describe("<RuleImplementationTabs />", () => {
7
7
  messages: {
8
8
  en: {
9
9
  "tabs.dq.ruleImplementation": "ruleImplementation",
10
- "tabs.dq.implementation.links": "links",
10
+ "tabs.dq.implementation.links.concepts": "concepts",
11
+ "tabs.dq.implementation.structures": "structures",
11
12
  "tabs.dq.ruleImplementation.results": "results",
12
13
  "tabs.dq.ruleImplementation.details": "details",
13
14
  "tabs.dq.ruleImplementation.audit": "audit",
@@ -0,0 +1,10 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<ImplementationStructureDelete /> matches the latest snapshot 1`] = `
4
+ <div>
5
+ <i
6
+ aria-hidden="true"
7
+ class="red trash alternate outline icon"
8
+ />
9
+ </div>
10
+ `;
@@ -0,0 +1,11 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<ImplementationStructureLink /> matches the latest snapshot 1`] = `
4
+ <div>
5
+ <a
6
+ href="/structures/1"
7
+ >
8
+ foo
9
+ </a>
10
+ </div>
11
+ `;
@@ -0,0 +1,208 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<ImplementationStructures /> matches the latest snapshot 1`] = `
4
+ <div>
5
+ <div
6
+ class="ui bottom attached segment"
7
+ >
8
+ <div
9
+ class="ui grid"
10
+ >
11
+ <div
12
+ class="sixteen wide column"
13
+ >
14
+ <table
15
+ class="ui table"
16
+ >
17
+ <thead
18
+ class=""
19
+ >
20
+ <tr
21
+ class=""
22
+ >
23
+ <th
24
+ class=""
25
+ >
26
+ name
27
+ </th>
28
+ <th
29
+ class=""
30
+ >
31
+ system
32
+ </th>
33
+ <th
34
+ class=""
35
+ >
36
+ path
37
+ </th>
38
+ <th
39
+ class=""
40
+ >
41
+ type
42
+ </th>
43
+ <th
44
+ class=""
45
+ />
46
+ </tr>
47
+ </thead>
48
+ <tbody
49
+ class=""
50
+ >
51
+ <tr
52
+ class=""
53
+ >
54
+ <td
55
+ class=""
56
+ >
57
+ <a
58
+ href="/structures/1"
59
+ >
60
+ qux
61
+ </a>
62
+ </td>
63
+ <td
64
+ class=""
65
+ >
66
+ bar
67
+ </td>
68
+ <td
69
+ class=""
70
+ >
71
+ <span
72
+ title="baz"
73
+ >
74
+ baz
75
+ </span>
76
+ </td>
77
+ <td
78
+ class=""
79
+ >
80
+ dataset
81
+ </td>
82
+ <td
83
+ class=""
84
+ >
85
+ <i
86
+ aria-hidden="true"
87
+ class="red trash alternate outline icon"
88
+ />
89
+ </td>
90
+ </tr>
91
+ </tbody>
92
+ </table>
93
+ </div>
94
+ </div>
95
+ </div>
96
+ </div>
97
+ `;
98
+
99
+ exports[`<ImplementationStructures /> matches the latest snapshot without create permission 1`] = `
100
+ <div>
101
+ <div
102
+ class="ui bottom attached segment"
103
+ >
104
+ <div
105
+ class="ui grid"
106
+ >
107
+ <div
108
+ class="sixteen wide column"
109
+ >
110
+ <table
111
+ class="ui table"
112
+ >
113
+ <thead
114
+ class=""
115
+ >
116
+ <tr
117
+ class=""
118
+ >
119
+ <th
120
+ class=""
121
+ >
122
+ name
123
+ </th>
124
+ <th
125
+ class=""
126
+ >
127
+ system
128
+ </th>
129
+ <th
130
+ class=""
131
+ >
132
+ path
133
+ </th>
134
+ <th
135
+ class=""
136
+ >
137
+ type
138
+ </th>
139
+ </tr>
140
+ </thead>
141
+ <tbody
142
+ class=""
143
+ >
144
+ <tr
145
+ class=""
146
+ >
147
+ <td
148
+ class=""
149
+ >
150
+ <a
151
+ href="/structures/1"
152
+ >
153
+ qux
154
+ </a>
155
+ </td>
156
+ <td
157
+ class=""
158
+ >
159
+ bar
160
+ </td>
161
+ <td
162
+ class=""
163
+ >
164
+ <span
165
+ title="baz"
166
+ >
167
+ baz
168
+ </span>
169
+ </td>
170
+ <td
171
+ class=""
172
+ >
173
+ dataset
174
+ </td>
175
+ </tr>
176
+ </tbody>
177
+ </table>
178
+ </div>
179
+ </div>
180
+ </div>
181
+ </div>
182
+ `;
183
+
184
+ exports[`<ImplementationStructures /> matches the latest snapshot without data_structures 1`] = `
185
+ <div>
186
+ <div
187
+ class="ui bottom attached segment"
188
+ >
189
+ <div
190
+ class="ui grid"
191
+ >
192
+ <h4
193
+ class="ui header"
194
+ >
195
+ <i
196
+ aria-hidden="true"
197
+ class="search icon"
198
+ />
199
+ <div
200
+ class="content"
201
+ >
202
+ empty
203
+ </div>
204
+ </h4>
205
+ </div>
206
+ </div>
207
+ </div>
208
+ `;
@@ -0,0 +1,106 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<ImplementationStructuresNew /> matches the latest snapshot 1`] = `
4
+ <div>
5
+ <div
6
+ class="ui bottom attached segment"
7
+ style="display: none;"
8
+ >
9
+ <div
10
+ class="ui grid"
11
+ >
12
+ <div
13
+ class="column"
14
+ with="16"
15
+ >
16
+ <h4
17
+ class="ui header"
18
+ >
19
+ header
20
+ </h4>
21
+ <div
22
+ aria-expanded="false"
23
+ class="ui selection dropdown"
24
+ role="listbox"
25
+ tabindex="0"
26
+ >
27
+ <div
28
+ aria-atomic="true"
29
+ aria-live="polite"
30
+ class="divider text"
31
+ role="alert"
32
+ >
33
+ dataset
34
+ </div>
35
+ <i
36
+ aria-hidden="true"
37
+ class="dropdown icon"
38
+ />
39
+ <div
40
+ class="menu transition"
41
+ >
42
+ <div
43
+ aria-checked="true"
44
+ aria-selected="true"
45
+ class="active selected item"
46
+ role="option"
47
+ style="pointer-events: all;"
48
+ >
49
+ <span
50
+ class="text"
51
+ >
52
+ dataset
53
+ </span>
54
+ </div>
55
+ <div
56
+ aria-checked="false"
57
+ aria-selected="false"
58
+ class="item"
59
+ role="option"
60
+ style="pointer-events: all;"
61
+ >
62
+ <span
63
+ class="text"
64
+ >
65
+ population
66
+ </span>
67
+ </div>
68
+ <div
69
+ aria-checked="false"
70
+ aria-selected="false"
71
+ class="item"
72
+ role="option"
73
+ style="pointer-events: all;"
74
+ >
75
+ <span
76
+ class="text"
77
+ >
78
+ validation
79
+ </span>
80
+ </div>
81
+ </div>
82
+ </div>
83
+ <div
84
+ class="actions"
85
+ >
86
+ <a
87
+ class="ui secondary button"
88
+ href="/"
89
+ role="button"
90
+ >
91
+ cancel
92
+ </a>
93
+ <button
94
+ class="ui primary disabled button"
95
+ disabled=""
96
+ tabindex="-1"
97
+ >
98
+ create
99
+ </button>
100
+ </div>
101
+ </div>
102
+ </div>
103
+ </div>
104
+
105
+ </div>
106
+ `;
@@ -136,10 +136,16 @@ exports[`<RuleImplementation /> matches the latest snapshot 1`] = `
136
136
  </a>
137
137
  <a
138
138
  class="item"
139
- href="/rules/1/implementations/1/links"
139
+ href="/rules/1/implementations/1/links/concepts"
140
140
  >
141
141
  Related concepts
142
142
  </a>
143
+ <a
144
+ class="item"
145
+ href="/rules/1/implementations/1/structures"
146
+ >
147
+ Structures
148
+ </a>
143
149
  <a
144
150
  class="item"
145
151
  href="/rules/1/implementations/1/results"
@@ -13,9 +13,15 @@ exports[`<RuleImplementationTabs /> matches the latest snapshot 1`] = `
13
13
  </a>
14
14
  <a
15
15
  class="item"
16
- href="/rules/8/implementations/1/links"
16
+ href="/rules/8/implementations/1/links/concepts"
17
17
  >
18
- links
18
+ concepts
19
+ </a>
20
+ <a
21
+ class="item"
22
+ href="/rules/8/implementations/1/structures"
23
+ >
24
+ structures
19
25
  </a>
20
26
  <a
21
27
  class="item"
@@ -10,6 +10,7 @@ export default {
10
10
  "alert.createExecutionGroup.success.content":
11
11
  "{count} implementations queued for execution",
12
12
  "alert.createExecutionGroup.success.header": "Executions scheduled",
13
+ "alert.createImplementationStructure.failed.header": "Error linking to structure",
13
14
  "alert.createRule.failed.header": "Error creating rule",
14
15
  "alert.createRuleImplementation.failed.header":
15
16
  "Error creating implementation",
@@ -18,6 +19,8 @@ export default {
18
19
  "alert.updateRuleImplementation.failed.header":
19
20
  "Error updating implementation",
20
21
  "concepts.rules.empty": "This concept has no quality rules",
22
+ "createImplementationStructure.error.implementation_id.has already been taken":
23
+ "A link with same structure and type already exists",
21
24
  "createRule.error.domain_id.required": "Domain is required",
22
25
  "createRuleImplementation.error.implementation_key.duplicated":
23
26
  "Duplicated implementation key",
@@ -66,6 +69,14 @@ export default {
66
69
  "implementations.actions.execution.confirmation.legend": "Template fields",
67
70
  "implementations.execute.filtered": "{count} implementations to execute",
68
71
  "implementations.search.placeholder": "Search implementations...",
72
+ "implementationStructures.empty": "No structures found",
73
+ "implementationsStructures.new.header": "Link to Structure",
74
+ "implementationsStructures.type": "Type",
75
+ "implementationsStructures.type.dataset": "Dataset",
76
+ "implementationsStructures.type.population": "Population",
77
+ "implementationsStructures.type.validation": "Validation",
78
+ "implementationsStructures.delete.confirmation.header": "Delete Structure relation",
79
+ "implementationsStructures.delete.confirmation.content": "The relation will be deleted. Are you sure?",
69
80
  "navigation.quality": "Quality",
70
81
  "navigation.quality.rules": "Rules",
71
82
  "operator.dropdown.placeholder": "Operator",
@@ -571,7 +582,8 @@ export default {
571
582
  "structureFields.dropdown.label": "Select Field",
572
583
  "structureFields.dropdown.placeholder": "Fields",
573
584
  "summary.link.and": "and",
574
- "tabs.dq.implementation.links": "Link to concepts",
585
+ "tabs.dq.implementation.links.concepts": "Link to concepts",
586
+ "tabs.dq.implementation.structures": "Structures",
575
587
  "tabs.dq.rule": "Rule",
576
588
  "tabs.dq.rule.audit": "Audit",
577
589
  "tabs.dq.ruleImplementation": "Implementation",
@@ -11,6 +11,7 @@ export default {
11
11
  "alert.createExecutionGroup.success.content":
12
12
  "Se ha solicitado la ejecución de {count} implementaciones",
13
13
  "alert.createExecutionGroup.success.header": "Ejecuciones programadas",
14
+ "alert.createImplementationStructure.failed.header": "Error al vincular con estructura",
14
15
  "alert.createRule.failed.header": "Error creando regla",
15
16
  "alert.createRuleImplementation.failed.header":
16
17
  "Error creando implementación",
@@ -19,6 +20,8 @@ export default {
19
20
  "alert.updateRuleImplementation.failed.header":
20
21
  "Error actualizando implementación",
21
22
  "concepts.rules.empty": "Ese concepto no tiene reglas de calidad",
23
+ "createImplementationStructure.error.implementation_id.has already been taken":
24
+ "Una relación con misma estructura y tipo ya existe",
22
25
  "createRule.error.domain_id.required": "Dominio es requerido",
23
26
  "createRuleImplementation.error.implementation_key.duplicated":
24
27
  "Identificador duplicado",
@@ -68,6 +71,15 @@ export default {
68
71
  "Campos de la plantilla",
69
72
  "implementations.execute.filtered": "{count} implementaciones a ejecutar",
70
73
  "implementations.search.placeholder": "Buscar implementaciones...",
74
+ "implementationStructures.empty": "Ningúna estructura encontrada",
75
+ "implementationsStructures.new.header": "Vincular a una estructura",
76
+ "implementationsStructures.type": "Tipo",
77
+ "implementationsStructures.type.dataset": "Conjunto de Datos",
78
+ "implementationsStructures.type.population": "Populación",
79
+ "implementationsStructures.type.validation": "Validación",
80
+ "implementationsStructures.delete.confirmation.header": "Borrar relación con Estructura",
81
+ "implementationsStructures.delete.confirmation.content":
82
+ "La relación será eliminada. ¿Estás seguro?",
71
83
  "navigation.quality.rules": "Reglas",
72
84
  "navigation.quality": "Calidad",
73
85
  "operator.dropdown.placeholder": "Operador",
@@ -597,7 +609,8 @@ export default {
597
609
  "structureFields.dropdown.label": "Seleccionar Campo",
598
610
  "structureFields.dropdown.placeholder": "Campos",
599
611
  "summary.link.and": "y",
600
- "tabs.dq.implementation.links": "Conceptos relacionados",
612
+ "tabs.dq.implementation.links.concepts": "Conceptos relacionados",
613
+ "tabs.dq.implementation.structures": "Estructuras",
601
614
  "tabs.dq.rule": "Regla",
602
615
  "tabs.dq.rule.audit": "Auditoría",
603
616
  "tabs.dq.ruleImplementation.audit": "Auditoría",
@@ -5,6 +5,7 @@ const initialState = {};
5
5
 
6
6
  const pickFields = _.pick([
7
7
  "dataset",
8
+ "data_structures",
8
9
  "deleted_at",
9
10
  "df_content",
10
11
  "df_name",
@@ -2,6 +2,7 @@ import { IMPLEMENTATIONS, linkTo } from "@truedat/core/routes";
2
2
  import { clearRedirect } from "@truedat/core/routines";
3
3
  import {
4
4
  createExecutionGroup,
5
+ createImplementationStructure,
5
6
  createRuleImplementation,
6
7
  updateRuleImplementation,
7
8
  deleteRuleResult,
@@ -21,6 +22,9 @@ export const ruleImplementationRedirect = (
21
22
  const id = payload?.data?.rule_id;
22
23
  return linkTo.RULE_IMPLEMENTATIONS({ id });
23
24
  }
25
+ case createImplementationStructure.SUCCESS: {
26
+ return payload?.redirectUrl;
27
+ }
24
28
  case createExecutionGroup.SUCCESS: {
25
29
  const id = payload?.data?.id;
26
30
  return linkTo.EXECUTION_GROUP({ id });
package/src/routines.js CHANGED
@@ -96,3 +96,10 @@ export const createExecutionGroup = createRoutine("CREATE_EXECUTION_GROUP");
96
96
  export const fetchExecutionGroup = createRoutine("FETCH_EXECUTION_GROUP");
97
97
  export const clearExecutionGroup = createRoutine("CLEAR_EXECUTION_GROUP");
98
98
  export const fetchRemediation = createRoutine("FETCH_REMEDIATION");
99
+
100
+ export const createImplementationStructure = createRoutine(
101
+ "CREATE_IMPLEMENTATION_STRUCTURE"
102
+ );
103
+ export const deleteImplementationStructure = createRoutine(
104
+ "DELETE_IMPLEMENTATION_STRUCTURE"
105
+ );
@@ -0,0 +1,86 @@
1
+ import { compile } from "path-to-regexp";
2
+ import { testSaga } from "redux-saga-test-plan";
3
+ import { apiJsonPost, JSON_OPTS } from "@truedat/core/services/api";
4
+ import {
5
+ createImplementationStructureRequestSaga,
6
+ createImplementationStructureSaga,
7
+ } from "../createImplementationStructure";
8
+ import { API_IMPLEMENTATIONS_STRUCTURES } from "../../api";
9
+ import { createImplementationStructure } from "../../routines";
10
+
11
+ describe("sagas: createImplementationStructureRequestSaga", () => {
12
+ it("should invoke createImplementationStructureSaga on trigger", () => {
13
+ expect(() => {
14
+ testSaga(createImplementationStructureRequestSaga)
15
+ .next()
16
+ .takeLatest(
17
+ createImplementationStructure.TRIGGER,
18
+ createImplementationStructureSaga
19
+ )
20
+ .finish()
21
+ .isDone();
22
+ }).not.toThrow();
23
+ });
24
+
25
+ it("should throw exception if an unhandled action is received", () => {
26
+ expect(() => {
27
+ testSaga(createImplementationStructureRequestSaga)
28
+ .next()
29
+ .takeLatest("FOO", createImplementationStructure);
30
+ }).toThrow();
31
+ });
32
+ });
33
+
34
+ describe("sagas: createImplementationStructureSaga", () => {
35
+ const implementationId = 1;
36
+ const dataStructureId = 2;
37
+ const type = "dataset";
38
+ const redirectUrl = "uri";
39
+
40
+ const url = compile(API_IMPLEMENTATIONS_STRUCTURES)({ implementationId });
41
+ const payload = {
42
+ implementationId,
43
+ dataStructureId,
44
+ type,
45
+ redirectUrl,
46
+ };
47
+
48
+ const requestData = { data_structure_id: dataStructureId, type };
49
+
50
+ const data = { response: "foo" };
51
+
52
+ it("should encode request content and put a success action when a response is returned", () => {
53
+ expect(() => {
54
+ testSaga(createImplementationStructureSaga, { payload })
55
+ .next()
56
+ .put(createImplementationStructure.request(payload))
57
+ .next()
58
+ .call(apiJsonPost, url, requestData, JSON_OPTS)
59
+ .next({ data })
60
+ .put(createImplementationStructure.success({ data, redirectUrl }))
61
+ .next()
62
+ .put(createImplementationStructure.fulfill())
63
+ .next()
64
+ .isDone();
65
+ }).not.toThrow();
66
+ });
67
+
68
+ it("should put a failure action when the call returns an error", () => {
69
+ const message = "Request failed";
70
+ const error = { message };
71
+
72
+ expect(() => {
73
+ testSaga(createImplementationStructureSaga, { payload })
74
+ .next()
75
+ .put(createImplementationStructure.request(payload))
76
+ .next()
77
+ .call(apiJsonPost, url, requestData, JSON_OPTS)
78
+ .throw(error)
79
+ .put(createImplementationStructure.failure(message))
80
+ .next()
81
+ .put(createImplementationStructure.fulfill())
82
+ .next()
83
+ .isDone();
84
+ }).not.toThrow();
85
+ });
86
+ });