@truedat/dq 4.42.5 → 4.43.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.
Files changed (72) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/package.json +5 -5
  3. package/src/api.js +8 -0
  4. package/src/components/ConditionSummary.js +1 -0
  5. package/src/components/FieldSummary.js +86 -0
  6. package/src/components/ImplementationStructureDelete.js +41 -0
  7. package/src/components/ImplementationStructureLink.js +24 -0
  8. package/src/components/ImplementationStructures.js +138 -0
  9. package/src/components/ImplementationStructuresNew.js +125 -0
  10. package/src/components/ImplementationSummary.js +11 -1
  11. package/src/components/NewRemediation.js +27 -20
  12. package/src/components/NewRuleImplementation.js +42 -6
  13. package/src/components/RemediationPlan.js +47 -78
  14. package/src/components/RuleImplementationProperties.js +2 -1
  15. package/src/components/RuleImplementationResultTabs.js +113 -0
  16. package/src/components/RuleImplementationResults.js +1 -1
  17. package/src/components/RuleImplementationTabs.js +16 -1
  18. package/src/components/RuleImplementationsTable.js +10 -3
  19. package/src/components/RuleResult.js +108 -0
  20. package/src/components/{ExecutionDetails.js → RuleResultDetails.js} +4 -21
  21. package/src/components/RuleResultRemediationLoader.js +44 -0
  22. package/src/components/RuleResultRemediations.js +47 -0
  23. package/src/components/RuleResultRow.js +0 -3
  24. package/src/components/RuleResultSegmentRow.js +83 -0
  25. package/src/components/RuleResultSegments.js +102 -0
  26. package/src/components/RuleResultSegmentsLoader.js +34 -0
  27. package/src/components/RuleResultsRoutes.js +73 -0
  28. package/src/components/RuleRoutes.js +38 -12
  29. package/src/components/__test_samples__/NewRuleImplementationProps.js +22 -2
  30. package/src/components/__tests__/ExecutionDetails.spec.js +5 -5
  31. package/src/components/__tests__/ImplementationStructureDelete.spec.js +27 -0
  32. package/src/components/__tests__/ImplementationStructureLink.spec.js +23 -0
  33. package/src/components/__tests__/ImplementationStructures.spec.js +88 -0
  34. package/src/components/__tests__/ImplementationStructuresNew.spec.js +34 -0
  35. package/src/components/__tests__/NewRuleImplementation.spec.js +27 -7
  36. package/src/components/__tests__/RuleImplementation.spec.js +2 -1
  37. package/src/components/__tests__/RuleImplementationTabs.spec.js +2 -1
  38. package/src/components/__tests__/RuleResultSegmentsLoader.spec.js +32 -0
  39. package/src/components/__tests__/__snapshots__/ExecutionDetails.spec.js.snap +2 -30
  40. package/src/components/__tests__/__snapshots__/ImplementationStructureDelete.spec.js.snap +10 -0
  41. package/src/components/__tests__/__snapshots__/ImplementationStructureLink.spec.js.snap +11 -0
  42. package/src/components/__tests__/__snapshots__/ImplementationStructures.spec.js.snap +208 -0
  43. package/src/components/__tests__/__snapshots__/ImplementationStructuresNew.spec.js.snap +106 -0
  44. package/src/components/__tests__/__snapshots__/NewRuleImplementation.spec.js.snap +1211 -1217
  45. package/src/components/__tests__/__snapshots__/RuleImplementation.spec.js.snap +7 -1
  46. package/src/components/__tests__/__snapshots__/RuleImplementationProperties.spec.js.snap +1 -0
  47. package/src/components/__tests__/__snapshots__/RuleImplementationTabs.spec.js.snap +8 -2
  48. package/src/components/index.js +2 -2
  49. package/src/components/ruleImplementationForm/FieldsGrid.js +57 -0
  50. package/src/components/ruleImplementationForm/FieldsGroup.js +107 -0
  51. package/src/components/ruleImplementationForm/RuleImplementationForm.js +45 -0
  52. package/src/components/ruleImplementationForm/SegmentsForm.js +35 -0
  53. package/src/components/ruleImplementationForm/__tests__/RuleImplementationForm.spec.js +27 -10
  54. package/src/components/ruleImplementationForm/__tests__/__snapshots__/RuleImplementationForm.spec.js.snap +315 -91
  55. package/src/messages/en.js +20 -1
  56. package/src/messages/es.js +21 -1
  57. package/src/reducers/__tests__/segmentResult.spec.js +46 -0
  58. package/src/reducers/index.js +2 -0
  59. package/src/reducers/ruleImplementation.js +2 -0
  60. package/src/reducers/ruleImplementationRedirect.js +4 -0
  61. package/src/reducers/segmentResults.js +19 -0
  62. package/src/routines.js +10 -0
  63. package/src/sagas/__tests__/createImplementationStructure.spec.js +86 -0
  64. package/src/sagas/__tests__/deleteImplementationStructure.spec.js +84 -0
  65. package/src/sagas/__tests__/fetchSegmentResults.spec.js +78 -0
  66. package/src/sagas/createImplementationStructure.js +32 -0
  67. package/src/sagas/deleteImplementationStructure.js +37 -0
  68. package/src/sagas/fetchSegmentResults.js +30 -0
  69. package/src/sagas/index.js +19 -10
  70. package/src/services/encodeRawContent.js +5 -5
  71. package/src/styles/remediationPlan.less +5 -0
  72. package/src/styles/ruleImplementationForm/DatasetForm.less +5 -0
@@ -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"
@@ -65,6 +65,7 @@ exports[`<RuleImplementationProperties /> matches the latest snapshot 1`] = `
65
65
  "dataset",
66
66
  "populations",
67
67
  "validations",
68
+ "segments",
68
69
  ]
69
70
  }
70
71
  ruleImplementation={
@@ -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"
@@ -1,7 +1,7 @@
1
1
  import ConceptRules from "./ConceptRules";
2
2
  import DynamicRuleForm from "./DynamicRuleForm";
3
3
  import EditRule from "./EditRule";
4
- import ExecutionDetails from "./ExecutionDetails";
4
+ import RuleResultDetails from "./RuleResultDetails";
5
5
  import ExecutionGroup from "./ExecutionGroup";
6
6
  import ExecutionGroupLoader from "./ExecutionGroupLoader";
7
7
  import ImplementationFiltersLoader from "./ImplementationFiltersLoader";
@@ -25,7 +25,7 @@ export {
25
25
  ConceptRules,
26
26
  DynamicRuleForm,
27
27
  EditRule,
28
- ExecutionDetails,
28
+ RuleResultDetails,
29
29
  ExecutionGroup,
30
30
  ExecutionGroupLoader,
31
31
  ImplementationFiltersLoader,
@@ -0,0 +1,57 @@
1
+ import _ from "lodash/fp";
2
+ import React, { useState } from "react";
3
+ import PropTypes from "prop-types";
4
+ import { Button, Grid } from "semantic-ui-react";
5
+ import FieldsGroup from "./FieldsGroup";
6
+
7
+ export const FieldsGrid = ({ rows, setRowValue, structures }) => {
8
+ const [activeConditionIndex, setActiveConditionIndex] = useState();
9
+
10
+ const parentStructures = _.map((s) =>
11
+ _.isNil(_.prop("alias")(s))
12
+ ? _.prop("structure")(s)
13
+ : { ..._.prop("structure")(s), alias: _.prop("alias")(s) }
14
+ )(structures);
15
+
16
+ const onStructureChange = (index, value) => {
17
+ const structure = {
18
+ ..._.pick(["field_type", "name", "parent_index"])(value),
19
+ id: value.data_structure_id,
20
+ };
21
+ setRowValue({
22
+ index,
23
+ value: {
24
+ structure,
25
+ },
26
+ });
27
+ };
28
+
29
+ const onRowAddition = () => {
30
+ setRowValue({});
31
+ setActiveConditionIndex(-1);
32
+ };
33
+
34
+ return (
35
+ <>
36
+ <Grid verticalAlign="middle" divided="vertically">
37
+ <FieldsGroup
38
+ activeConditionIndex={activeConditionIndex}
39
+ onStructureChange={onStructureChange}
40
+ parentStructures={parentStructures}
41
+ rows={rows}
42
+ setActiveConditionIndex={setActiveConditionIndex}
43
+ setRowValue={setRowValue}
44
+ />
45
+ </Grid>
46
+ <Button onClick={onRowAddition} icon="plus circle" />
47
+ </>
48
+ );
49
+ };
50
+
51
+ FieldsGrid.propTypes = {
52
+ rows: PropTypes.array,
53
+ setRowValue: PropTypes.func,
54
+ structures: PropTypes.array,
55
+ };
56
+
57
+ export default FieldsGrid;
@@ -0,0 +1,107 @@
1
+ import _ from "lodash/fp";
2
+ import React from "react";
3
+ import { useIntl } from "react-intl";
4
+ import { Grid, Icon } from "semantic-ui-react";
5
+ import PropTypes from "prop-types";
6
+ import { getStructureFields } from "../../selectors/getStructureFields";
7
+
8
+ const StructureFieldsDropdown = React.lazy(() =>
9
+ import("@truedat/dd/components/StructureFieldsDropdown")
10
+ );
11
+
12
+ const Field = ({
13
+ clause,
14
+ index,
15
+ onStructureChange,
16
+ parentStructures,
17
+ setRowValue,
18
+ }) => {
19
+ const { formatMessage } = useIntl();
20
+ const structureFields = getStructureFields(parentStructures);
21
+ return (
22
+ <>
23
+ <Grid.Row>
24
+ <Grid.Column width={4}>
25
+ <StructureFieldsDropdown
26
+ label={formatMessage({ id: "filtersGrid.field.label" })}
27
+ inline={false}
28
+ parentStructures={parentStructures}
29
+ structureFields={structureFields}
30
+ onSelectField={(value) => onStructureChange(index, value)}
31
+ value={
32
+ _.isNil(_.prop("parent_index")(clause?.structure))
33
+ ? _.prop("id")(clause?.structure)
34
+ : `${_.prop("id")(clause?.structure)}/${_.prop("parent_index")(
35
+ clause?.structure
36
+ )}`
37
+ }
38
+ />
39
+ </Grid.Column>
40
+ <Grid.Column width={2}>
41
+ <Icon
42
+ name="trash"
43
+ className="selectable segment-trash-buttom"
44
+ onClick={() => setRowValue({ index })}
45
+ />
46
+ </Grid.Column>
47
+ </Grid.Row>
48
+ </>
49
+ );
50
+ };
51
+
52
+ Field.propTypes = {
53
+ activeConditionIndex: PropTypes.number,
54
+ allOperators: PropTypes.array,
55
+ operators: PropTypes.array,
56
+ clause: PropTypes.object,
57
+ composeValue: PropTypes.func,
58
+ onConditionChange: PropTypes.func,
59
+ onOperatorChange: PropTypes.func,
60
+ onStructureChange: PropTypes.func,
61
+ onValueChange: PropTypes.func,
62
+ onModifierChange: PropTypes.func,
63
+ parentStructures: PropTypes.array,
64
+ setActiveConditionIndex: PropTypes.func,
65
+ index: PropTypes.number,
66
+ setRowValue: PropTypes.func,
67
+ scope: PropTypes.string,
68
+ };
69
+
70
+ export const FieldsGroup = ({
71
+ activeConditionIndex,
72
+ composeValue,
73
+ onStructureChange,
74
+ parentStructures,
75
+ rows = [],
76
+ setActiveConditionIndex,
77
+ setRowValue,
78
+ scope,
79
+ }) => {
80
+ return rows.map((clause, index) => (
81
+ <Field
82
+ key={index}
83
+ activeConditionIndex={activeConditionIndex}
84
+ composeValue={composeValue}
85
+ clause={clause}
86
+ index={index}
87
+ onStructureChange={onStructureChange}
88
+ parentStructures={parentStructures}
89
+ setActiveConditionIndex={setActiveConditionIndex}
90
+ setRowValue={setRowValue}
91
+ scope={scope}
92
+ />
93
+ ));
94
+ };
95
+
96
+ FieldsGroup.propTypes = {
97
+ activeConditionIndex: PropTypes.number,
98
+ composeValue: PropTypes.func,
99
+ onStructureChange: PropTypes.func,
100
+ rows: PropTypes.array,
101
+ parentStructures: PropTypes.array,
102
+ setActiveConditionIndex: PropTypes.func,
103
+ setRowValue: PropTypes.func,
104
+ scope: PropTypes.string,
105
+ };
106
+
107
+ export default FieldsGroup;
@@ -18,6 +18,7 @@ import PopulationForm from "./PopulationForm";
18
18
  import DatasetForm from "./DatasetForm";
19
19
  import ValidationsForm from "./ValidationsForm";
20
20
  import InformationForm from "./InformationForm";
21
+ import SegmentsForm from "./SegmentsForm";
21
22
  import { areLimitsValid } from "./limitsValidation";
22
23
 
23
24
  const RuleImplementationStep = ({ activeStep, name, icon }) => (
@@ -44,6 +45,8 @@ export const RuleImplementationForm = ({
44
45
  setPopulations,
45
46
  setImplementationKey,
46
47
  setValidations,
48
+ addSegments,
49
+ setSegments,
47
50
  isSubmitting,
48
51
  handleSubmit,
49
52
  operators,
@@ -57,6 +60,11 @@ export const RuleImplementationForm = ({
57
60
  { name: "dataset", icon: "database", isValid: () => validDataSet() },
58
61
  { name: "populations", icon: "user", isValid: () => validPopulations() },
59
62
  { name: "validations", icon: "setting", isValid: () => validValidations() },
63
+ {
64
+ name: "segments",
65
+ icon: "grid layout",
66
+ isValid: () => validSegments(),
67
+ },
60
68
  ];
61
69
 
62
70
  const [activeStep, setActiveStep] = useState("information");
@@ -67,6 +75,7 @@ export const RuleImplementationForm = ({
67
75
  populations,
68
76
  validations,
69
77
  dfContent,
78
+ segments,
70
79
  } = ruleImplementation || {};
71
80
 
72
81
  const validInformation = () =>
@@ -81,6 +90,8 @@ export const RuleImplementationForm = ({
81
90
  const validValidations = () =>
82
91
  _.every(_.negate(_.isEmpty))(validations) && validationsComplete();
83
92
 
93
+ const validSegments = () => _.every(_.negate(_.isEmpty))(segments);
94
+
84
95
  const validDataSet = () => areStructuresComplete();
85
96
 
86
97
  const areStructuresComplete = () =>
@@ -271,6 +282,20 @@ export const RuleImplementationForm = ({
271
282
  typeOperators={operators}
272
283
  />
273
284
  )}
285
+ {activeStep == "segments" ? (
286
+ <>
287
+ {_.flow(_.flatten, _.isEmpty)(segments) ? (
288
+ <SegmentsTypeForm createFilter={() => addSegments()} />
289
+ ) : (
290
+ <SegmentsForm
291
+ segments={segments}
292
+ setSegments={setSegments}
293
+ structures={structures}
294
+ typeOperators={operators}
295
+ />
296
+ )}
297
+ </>
298
+ ) : null}
274
299
  </Form>
275
300
  </Grid.Row>
276
301
  <Divider hidden />
@@ -319,6 +344,8 @@ RuleImplementationForm.propTypes = {
319
344
  setPopulations: PropTypes.func,
320
345
  setImplementationKey: PropTypes.func,
321
346
  setValidations: PropTypes.func,
347
+ setSegments: PropTypes.func,
348
+ addSegments: PropTypes.func,
322
349
  operators: PropTypes.object,
323
350
  onChange: PropTypes.func,
324
351
  template: PropTypes.object,
@@ -354,3 +381,21 @@ const PopulationTypeForm = ({ createFilter }) => (
354
381
  PopulationTypeForm.propTypes = {
355
382
  createFilter: PropTypes.func,
356
383
  };
384
+
385
+ const SegmentsTypeForm = ({ createFilter }) => (
386
+ <Grid columns={2} textAlign="center">
387
+ <Grid.Column>
388
+ <Segment
389
+ className={`population selectable`}
390
+ onClick={() => createFilter()}
391
+ >
392
+ <Icon name="plus circle" />
393
+ <FormattedMessage id="segmentsForm.add.text" />
394
+ </Segment>
395
+ </Grid.Column>
396
+ </Grid>
397
+ );
398
+
399
+ SegmentsTypeForm.propTypes = {
400
+ createFilter: PropTypes.func,
401
+ };
@@ -0,0 +1,35 @@
1
+ import _ from "lodash/fp";
2
+ import React from "react";
3
+ import PropTypes from "prop-types";
4
+ import { dropAt, replaceAt } from "@truedat/core/services/arrays";
5
+ import { FieldsGrid } from "./FieldsGrid";
6
+
7
+ export const SegmentsForm = ({ segments, setSegments, structures }) => {
8
+ const setSegmentsRow = ({ index, value }) => {
9
+ if (_.isNil(index)) {
10
+ setSegments(_.isEmpty(segments) ? [{}] : [...segments, {}]);
11
+ } else if (value) {
12
+ const segment = { ..._.nth(index)(segments), ...value };
13
+ setSegments(replaceAt(index, segment)(segments));
14
+ } else {
15
+ const array = dropAt(index)(segments);
16
+ setSegments(_.isEmpty(array) ? [{}] : array);
17
+ }
18
+ };
19
+
20
+ return (
21
+ <FieldsGrid
22
+ rows={segments}
23
+ setRowValue={setSegmentsRow}
24
+ structures={structures}
25
+ />
26
+ );
27
+ };
28
+
29
+ SegmentsForm.propTypes = {
30
+ structures: PropTypes.array,
31
+ segments: PropTypes.array,
32
+ setSegments: PropTypes.func,
33
+ };
34
+
35
+ export default SegmentsForm;
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import { shallow } from "enzyme";
2
+ import { render } from "@truedat/test/render";
3
3
  import { intl } from "@truedat/test/intl-stub";
4
4
  import { RuleImplementationForm } from "../RuleImplementationForm";
5
5
 
@@ -8,7 +8,7 @@ import { RuleImplementationForm } from "../RuleImplementationForm";
8
8
  jest.spyOn(React, "useContext").mockImplementation(() => intl);
9
9
  jest.mock("react-router-dom", () => ({
10
10
  ...jest.requireActual("react-router-dom"),
11
- useHistory: () => ({ push: jest.fn() })
11
+ useHistory: () => ({ push: jest.fn() }),
12
12
  }));
13
13
 
14
14
  describe("<RuleImplementationForm />", () => {
@@ -18,29 +18,46 @@ describe("<RuleImplementationForm />", () => {
18
18
  params: {
19
19
  system_params: [
20
20
  { type: "string", name: "table" },
21
- { type: "string", name: "column" }
22
- ]
23
- }
24
- }
21
+ { type: "string", name: "column" },
22
+ ],
23
+ },
24
+ },
25
25
  };
26
+
26
27
  const ruleImplementation = {
27
28
  implementationKey: "foo",
28
- executable: true
29
+ executable: true,
30
+ result_type: "percentage",
31
+ goal: 20,
32
+ minimum: 10,
29
33
  };
30
34
  const handleSubmit = jest.fn();
31
35
  const isSubmitting = false;
32
36
  const systems = ["SYS"];
33
37
 
38
+ const renderOpts = {
39
+ messages: {
40
+ en: {
41
+ "ruleImplementation.form.tooltip.percentage.goal": "tooltip",
42
+ "ruleImplementation.form.tooltip.percentage.minimum": "Minimun",
43
+ "rule.form.validation.empty_required": "required",
44
+ },
45
+ },
46
+ };
34
47
  const props = {
35
48
  rule,
36
49
  handleSubmit,
37
50
  isSubmitting,
38
51
  systems,
39
- ruleImplementation
52
+ ruleImplementation,
53
+ isSubmitting: true,
40
54
  };
41
55
 
42
56
  it("matches the latest snapshot", () => {
43
- const wrapper = shallow(<RuleImplementationForm {...props} />);
44
- expect(wrapper).toMatchSnapshot();
57
+ const { container } = render(
58
+ <RuleImplementationForm {...props} />,
59
+ renderOpts
60
+ );
61
+ expect(container).toMatchSnapshot();
45
62
  });
46
63
  });