@truedat/dq 4.47.4 → 4.47.7
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 +16 -1
- package/package.json +5 -5
- package/src/api/queries.js +5 -0
- package/src/components/ConditionSummary.js +63 -21
- package/src/components/NewRuleImplementation.js +21 -46
- package/src/components/RuleImplementationResults.js +5 -3
- package/src/components/__test_samples__/newRuleImplementationHelper.js +1239 -0
- package/src/components/__tests__/NewRuleImplementation.spec.js +273 -6
- package/src/components/ruleImplementationForm/FiltersField.js +31 -11
- package/src/components/ruleImplementationForm/FiltersFormGroup.js +1 -1
- package/src/components/ruleImplementationForm/FiltersGrid.js +18 -6
- package/src/components/ruleImplementationForm/FiltersGroup.js +3 -3
- package/src/components/ruleImplementationForm/InformationForm.js +2 -0
- package/src/components/ruleImplementationForm/RuleImplementationForm.js +1 -1
- package/src/components/ruleImplementationForm/RuleImplementationRawForm.js +30 -1
- package/src/components/ruleImplementationForm/__tests__/RuleImplementationRawForm.spec.js +24 -24
- package/src/components/ruleImplementationForm/__tests__/__snapshots__/FiltersFormGroup.spec.js.snap +7 -1
- package/src/components/ruleImplementationForm/__tests__/__snapshots__/FiltersGroup.spec.js.snap +8 -1
- package/src/components/ruleImplementationForm/__tests__/__snapshots__/RuleImplementationRawForm.spec.js.snap +67 -0
- package/src/components/ruleImplementationForm/__tests__/__snapshots__/ValueConditions.spec.js.snap +7 -1
- package/src/messages/en.js +2 -1
- package/src/messages/es.js +2 -1
- package/src/selectors/getRuleImplementationOperators.js +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [4.47.6] 2022-07-01
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- [TD-4993]
|
|
8
|
+
- RuleImplementationForm visibility with correct permissions
|
|
9
|
+
- Show quality event errors on results view using graphql
|
|
10
|
+
- Add exclusive subscription events
|
|
11
|
+
|
|
12
|
+
## [4.47.5] 2022-06-30
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- [TD-4894] Multiple column operator in implementation creation
|
|
17
|
+
|
|
3
18
|
## [4.47.4] 2022-06-29
|
|
4
19
|
|
|
5
20
|
- [TD-4921] Add implementation workflow events
|
|
@@ -20,7 +35,7 @@
|
|
|
20
35
|
|
|
21
36
|
## [4.46.12] 2022-06-20
|
|
22
37
|
|
|
23
|
-
###
|
|
38
|
+
### Removed
|
|
24
39
|
|
|
25
40
|
- [TD-4894] Multiple column operator in implementation creation
|
|
26
41
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@truedat/dq",
|
|
3
|
-
"version": "4.47.
|
|
3
|
+
"version": "4.47.7",
|
|
4
4
|
"description": "Truedat Web Data Quality Module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"jsnext:main": "src/index.js",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"@testing-library/jest-dom": "^5.16.4",
|
|
35
35
|
"@testing-library/react": "^12.0.0",
|
|
36
36
|
"@testing-library/user-event": "^13.2.1",
|
|
37
|
-
"@truedat/test": "4.47.
|
|
37
|
+
"@truedat/test": "4.47.7",
|
|
38
38
|
"babel-jest": "^28.1.0",
|
|
39
39
|
"babel-plugin-dynamic-import-node": "^2.3.3",
|
|
40
40
|
"babel-plugin-lodash": "^3.3.4",
|
|
@@ -88,8 +88,8 @@
|
|
|
88
88
|
},
|
|
89
89
|
"dependencies": {
|
|
90
90
|
"@apollo/client": "^3.6.4",
|
|
91
|
-
"@truedat/core": "4.47.
|
|
92
|
-
"@truedat/df": "4.47.
|
|
91
|
+
"@truedat/core": "4.47.7",
|
|
92
|
+
"@truedat/df": "4.47.7",
|
|
93
93
|
"axios": "^0.19.2",
|
|
94
94
|
"graphql": "^15.5.3",
|
|
95
95
|
"path-to-regexp": "^1.7.0",
|
|
@@ -110,5 +110,5 @@
|
|
|
110
110
|
"react-dom": ">= 16.8.6 < 17",
|
|
111
111
|
"semantic-ui-react": ">= 0.88.2 < 2.1"
|
|
112
112
|
},
|
|
113
|
-
"gitHead": "
|
|
113
|
+
"gitHead": "5e85149774b055fd61014633988cdf6aa9b9da4f"
|
|
114
114
|
}
|
package/src/api/queries.js
CHANGED
|
@@ -8,43 +8,85 @@ import { linkTo } from "@truedat/core/routes";
|
|
|
8
8
|
|
|
9
9
|
const concatValues = (values, link) => _.join(link)(values);
|
|
10
10
|
|
|
11
|
-
const
|
|
12
|
-
|
|
11
|
+
const LinkToStructure = ({ value }) =>
|
|
12
|
+
value?.id ? (
|
|
13
13
|
<Link to={linkTo.STRUCTURE({ id: value.id })}>
|
|
14
14
|
<span className="highlighted">{`"${
|
|
15
15
|
!_.isEmpty(value.path) ? path(value) : value.name
|
|
16
16
|
}"`}</span>{" "}
|
|
17
17
|
</Link>
|
|
18
|
-
) :
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
18
|
+
) : null;
|
|
19
|
+
|
|
20
|
+
const FormattedLink = ({ value, operator = {} }) => {
|
|
21
|
+
switch (operator?.value_type) {
|
|
22
|
+
case "field":
|
|
23
|
+
return <LinkToStructure value={value} />;
|
|
24
|
+
case "field_list":
|
|
25
|
+
return (
|
|
26
|
+
<>
|
|
27
|
+
{_.map.convert({ cap: false })((v, i) => (
|
|
28
|
+
<LinkToStructure key={i} value={v} />
|
|
29
|
+
))(value)}
|
|
30
|
+
</>
|
|
31
|
+
);
|
|
32
|
+
default:
|
|
33
|
+
return (
|
|
34
|
+
<span className="highlighted">
|
|
35
|
+
<FormattedMessage
|
|
36
|
+
id={`ruleImplementation.filtersField.${value.raw}`}
|
|
37
|
+
defaultMessage={`"${
|
|
38
|
+
_.isArray(value.raw) ? _.join(", ")(value.raw) : value.raw
|
|
39
|
+
}"`}
|
|
40
|
+
></FormattedMessage>
|
|
41
|
+
</span>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
28
45
|
|
|
29
46
|
FormattedLink.propTypes = {
|
|
30
|
-
value: PropTypes.object,
|
|
47
|
+
value: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
|
|
31
48
|
operator: PropTypes.object,
|
|
32
49
|
};
|
|
33
50
|
|
|
34
51
|
const nilOrEmpty = (v) => _.isNil(v) || v === "";
|
|
35
52
|
|
|
36
|
-
const filterNilOrEmpties = (values, keys) =>
|
|
37
|
-
|
|
53
|
+
const filterNilOrEmpties = (values, keys) => {
|
|
54
|
+
if ("kv" in values) {
|
|
55
|
+
return _.every((k) => !nilOrEmpty(values.kv[k]))(keys) ? values : null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return _.flow(
|
|
59
|
+
_.map((v) => filterNilOrEmpties(v, keys)),
|
|
60
|
+
_.filter((v) => !!v)
|
|
61
|
+
)(values);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const pick = (value, keys, optionalKeys) => ({
|
|
65
|
+
kv: _.pick(keys)(value),
|
|
66
|
+
optv: _.pick(optionalKeys)(value),
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const convert = (v, index, value_modifier) => {
|
|
70
|
+
if (_.has("kv")(v)) {
|
|
71
|
+
return { ...v.kv, ...v.optv, modifier: _.nth(index)(value_modifier) };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return _.map.convert({ cap: false })((v, i) => {
|
|
75
|
+
return convert(v, i, value_modifier);
|
|
76
|
+
})(v);
|
|
77
|
+
};
|
|
38
78
|
|
|
39
79
|
const valuesFromKeys = (values, keys, optionalKeys, value_modifier) =>
|
|
40
80
|
_.flow(
|
|
41
|
-
_.map((
|
|
42
|
-
return
|
|
81
|
+
_.map((value) => {
|
|
82
|
+
return Array.isArray(value?.fields)
|
|
83
|
+
? _.map((innerValue) => pick(innerValue, keys, optionalKeys))(
|
|
84
|
+
value?.fields
|
|
85
|
+
)
|
|
86
|
+
: pick(value, keys, optionalKeys);
|
|
43
87
|
}),
|
|
44
88
|
(v) => filterNilOrEmpties(v, keys),
|
|
45
|
-
|
|
46
|
-
return { ...v.kv, ...v.optv, modifier: _.nth(i)(value_modifier) };
|
|
47
|
-
})
|
|
89
|
+
(v) => convert(v, 0, value_modifier)
|
|
48
90
|
)(values);
|
|
49
91
|
|
|
50
92
|
export const empty = (rows) =>
|
|
@@ -52,7 +94,7 @@ export const empty = (rows) =>
|
|
|
52
94
|
|
|
53
95
|
export const getValues = ({ value = [], operator = {}, value_modifier }) => {
|
|
54
96
|
const defaultOrValue = value || [];
|
|
55
|
-
return
|
|
97
|
+
return ["field", "field_list"].includes(operator?.value_type)
|
|
56
98
|
? valuesFromKeys(defaultOrValue, ["id", "name"], ["path"], value_modifier)
|
|
57
99
|
: valuesFromKeys(defaultOrValue, ["raw"], [], value_modifier);
|
|
58
100
|
};
|
|
@@ -4,8 +4,6 @@ import PropTypes from "prop-types";
|
|
|
4
4
|
import { connect } from "react-redux";
|
|
5
5
|
import { Divider, Header, Grid } from "semantic-ui-react";
|
|
6
6
|
import { FormattedMessage } from "react-intl";
|
|
7
|
-
import { gql, useQuery } from "@apollo/client";
|
|
8
|
-
import { Loading } from "@truedat/core/components";
|
|
9
7
|
import { getFieldType } from "@truedat/core/services/fieldType";
|
|
10
8
|
import {
|
|
11
9
|
createRuleImplementation,
|
|
@@ -25,6 +23,19 @@ const updateDatasetKey = (data) =>
|
|
|
25
23
|
{}
|
|
26
24
|
)(data);
|
|
27
25
|
|
|
26
|
+
// object either field, raw value, list of fields, list of raw values
|
|
27
|
+
const updateValueKeyContent = (object) => {
|
|
28
|
+
if (object?.id || object?.raw || object?.fields) {
|
|
29
|
+
return object?.id
|
|
30
|
+
? _.pick(["id", "parent_index"])(object)
|
|
31
|
+
: _.pick(["raw", "fields"])(object);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return (object || []).map((v) => {
|
|
35
|
+
return updateValueKeyContent(v);
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
|
|
28
39
|
const updateConditionValue = (acc, value, key) => {
|
|
29
40
|
if (key === "structure") {
|
|
30
41
|
return _.set(key, _.pick(["id", "parent_index"])(value))(acc);
|
|
@@ -48,13 +59,9 @@ const updateConditionValue = (acc, value, key) => {
|
|
|
48
59
|
_.pick(["name", "value_type", "value_type_filter"])(value)
|
|
49
60
|
)
|
|
50
61
|
)(acc);
|
|
51
|
-
if (key == "value")
|
|
52
|
-
return
|
|
53
|
-
|
|
54
|
-
_.map((v) =>
|
|
55
|
-
_.has("id")(v) ? _.pick(["id", "parent_index"])(v) : _.pick(["raw"])(v)
|
|
56
|
-
)(value)
|
|
57
|
-
)(acc);
|
|
62
|
+
if (key == "value") {
|
|
63
|
+
return { ...acc, value: updateValueKeyContent(value) };
|
|
64
|
+
}
|
|
58
65
|
if (key === "population") return _.set(key, conditionAttributes(value))(acc);
|
|
59
66
|
|
|
60
67
|
return acc;
|
|
@@ -103,6 +110,7 @@ const fieldTypeFromStructure = (row, structures, operators, scope) => {
|
|
|
103
110
|
field_type,
|
|
104
111
|
_.prop("operator")(row)
|
|
105
112
|
);
|
|
113
|
+
|
|
106
114
|
const updatedRow = {
|
|
107
115
|
...row,
|
|
108
116
|
operator,
|
|
@@ -238,8 +246,6 @@ export const NewRuleImplementation = ({
|
|
|
238
246
|
edition,
|
|
239
247
|
rule,
|
|
240
248
|
ruleImplementationProps,
|
|
241
|
-
sources,
|
|
242
|
-
sourcesLoading,
|
|
243
249
|
structuresFields,
|
|
244
250
|
structuresSiblings,
|
|
245
251
|
operators,
|
|
@@ -448,8 +454,9 @@ export const NewRuleImplementation = ({
|
|
|
448
454
|
const setImplementationKey = (implementationKey) =>
|
|
449
455
|
setRuleImplementation({ ...ruleImplementation, implementationKey });
|
|
450
456
|
|
|
451
|
-
const setImplementationRawContent = (rawContent) =>
|
|
457
|
+
const setImplementationRawContent = (rawContent) => {
|
|
452
458
|
setRuleImplementation({ ...ruleImplementation, rawContent });
|
|
459
|
+
};
|
|
453
460
|
|
|
454
461
|
const doSubmit = (props = {}) => {
|
|
455
462
|
const populations = _.reject(_.isEmpty)(ruleImplementation.populations);
|
|
@@ -457,8 +464,6 @@ export const NewRuleImplementation = ({
|
|
|
457
464
|
const segments = _.reject(_.isEmpty)(ruleImplementation.segments);
|
|
458
465
|
|
|
459
466
|
const raw_content = _.prop("rawContent")(ruleImplementation);
|
|
460
|
-
const source_id = _.prop("source_id")(raw_content);
|
|
461
|
-
const source = _.find({ id: source_id })(sources);
|
|
462
467
|
|
|
463
468
|
const rule_implementation =
|
|
464
469
|
ruleImplementation.implementationType == "raw"
|
|
@@ -469,7 +474,7 @@ export const NewRuleImplementation = ({
|
|
|
469
474
|
df_name: ruleImplementation?.dfName,
|
|
470
475
|
df_content: ruleImplementation?.dfContent,
|
|
471
476
|
rule_id: rule ? rule.id : ruleImplementation.rule_id,
|
|
472
|
-
raw_content: { ...raw_content
|
|
477
|
+
raw_content: { ...raw_content },
|
|
473
478
|
result_type: ruleImplementation.result_type,
|
|
474
479
|
minimum: ruleImplementation.minimum,
|
|
475
480
|
goal: ruleImplementation.goal,
|
|
@@ -536,8 +541,6 @@ export const NewRuleImplementation = ({
|
|
|
536
541
|
ruleImplementation={ruleImplementation}
|
|
537
542
|
onChange={onChange}
|
|
538
543
|
onSubmit={doSubmit}
|
|
539
|
-
sources={sources}
|
|
540
|
-
sourcesLoading={sourcesLoading}
|
|
541
544
|
/>
|
|
542
545
|
) : (
|
|
543
546
|
<RuleImplementationForm
|
|
@@ -573,39 +576,11 @@ NewRuleImplementation.propTypes = {
|
|
|
573
576
|
operators: PropTypes.object,
|
|
574
577
|
rule: PropTypes.object,
|
|
575
578
|
ruleImplementationProps: PropTypes.object,
|
|
576
|
-
sources: PropTypes.array,
|
|
577
|
-
sourcesLoading: PropTypes.bool,
|
|
578
579
|
structuresFields: PropTypes.object,
|
|
579
580
|
structuresSiblings: PropTypes.object,
|
|
580
581
|
updateRuleImplementation: PropTypes.func,
|
|
581
582
|
};
|
|
582
583
|
|
|
583
|
-
export const SOURCES_WITH_CONFIG = gql`
|
|
584
|
-
query SOURCES_WITH_CONFIG($jobTypes: String) {
|
|
585
|
-
sources(jobTypes: $jobTypes) {
|
|
586
|
-
id
|
|
587
|
-
externalId
|
|
588
|
-
config
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
`;
|
|
592
|
-
|
|
593
|
-
export const NewRuleImplementationLoader = (props) => {
|
|
594
|
-
const { data, error, loading } = useQuery(SOURCES_WITH_CONFIG, {
|
|
595
|
-
variables: { jobTypes: "quality" },
|
|
596
|
-
});
|
|
597
|
-
if (error) return null;
|
|
598
|
-
if (loading) return <Loading />;
|
|
599
|
-
const sources = data?.sources || [];
|
|
600
|
-
return (
|
|
601
|
-
<NewRuleImplementation
|
|
602
|
-
sources={sources}
|
|
603
|
-
sourcesLoading={loading}
|
|
604
|
-
{...props}
|
|
605
|
-
/>
|
|
606
|
-
);
|
|
607
|
-
};
|
|
608
|
-
|
|
609
584
|
const mapStateToProps = (state) => ({
|
|
610
585
|
rule: state.rule,
|
|
611
586
|
ruleImplementationProps: {
|
|
@@ -621,4 +596,4 @@ const mapStateToProps = (state) => ({
|
|
|
621
596
|
export default connect(mapStateToProps, {
|
|
622
597
|
createRuleImplementation,
|
|
623
598
|
updateRuleImplementation,
|
|
624
|
-
})(
|
|
599
|
+
})(NewRuleImplementation);
|
|
@@ -46,9 +46,11 @@ export const RuleImplementationResults = ({
|
|
|
46
46
|
formatMessage,
|
|
47
47
|
};
|
|
48
48
|
|
|
49
|
+
const { last_quality_event } = ruleImplementation;
|
|
50
|
+
|
|
49
51
|
return (
|
|
50
52
|
<>
|
|
51
|
-
{
|
|
53
|
+
{last_quality_event?.type === "FAILED" ? (
|
|
52
54
|
<Message negative style={{ marginTop: "14px" }}>
|
|
53
55
|
<Message.Header>
|
|
54
56
|
{formatMessage({
|
|
@@ -63,7 +65,7 @@ export const RuleImplementationResults = ({
|
|
|
63
65
|
right: "21px",
|
|
64
66
|
}}
|
|
65
67
|
locale={locale}
|
|
66
|
-
date={
|
|
68
|
+
date={last_quality_event.inserted_at}
|
|
67
69
|
format="YYYY-MM-DD HH:mm"
|
|
68
70
|
/>
|
|
69
71
|
<p
|
|
@@ -71,7 +73,7 @@ export const RuleImplementationResults = ({
|
|
|
71
73
|
whiteSpace: "pre-wrap",
|
|
72
74
|
}}
|
|
73
75
|
>
|
|
74
|
-
{
|
|
76
|
+
{last_quality_event.message}
|
|
75
77
|
</p>
|
|
76
78
|
</Message>
|
|
77
79
|
) : null}
|