@truedat/dq 4.46.2 → 4.46.5

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 CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.46.5] 2022-06-17
4
+
5
+ ### Added
6
+
7
+ - [TD-4894] Multiple column operator in implementation creation
8
+
9
+ ## [4.46.3] 2022-06-16
10
+
11
+ ### Changed
12
+
13
+ - [TD-4923] Allow change implementation_key on implementation edition
14
+ - [TD-4918] Add permission translations
15
+
3
16
  ## [4.46.2] 2022-06-16
4
17
 
5
18
  ### Changed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/dq",
3
- "version": "4.46.2",
3
+ "version": "4.46.5",
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.46.2",
37
+ "@truedat/test": "4.46.5",
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.46.2",
92
- "@truedat/df": "4.46.2",
91
+ "@truedat/core": "4.46.5",
92
+ "@truedat/df": "4.46.5",
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": "161f3fae4428ee4e4e26b62fd2f9764ce389d0e2"
113
+ "gitHead": "cc6e42515345adf2926f47216622e0fc4f7a0d86"
114
114
  }
@@ -8,23 +8,41 @@ import { linkTo } from "@truedat/core/routes";
8
8
 
9
9
  const concatValues = (values, link) => _.join(link)(values);
10
10
 
11
- const FormattedLink = ({ value, operator = {} }) =>
12
- _.propEq("value_type", "field")(operator) ? (
11
+ const LinkToStructure = ({ value }) => {
12
+ return 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
- <span className="highlighted">
20
- <FormattedMessage
21
- id={`ruleImplementation.filtersField.${value.raw}`}
22
- defaultMessage={`"${
23
- _.isArray(value.raw) ? _.join(", ")(value.raw) : value.raw
24
- }"`}
25
- ></FormattedMessage>
26
- </span>
27
- );
18
+ ) : null;
19
+ };
20
+
21
+ const FormattedLink = ({ value, operator = {} }) => {
22
+ switch (operator?.value_type) {
23
+ case "field":
24
+ return <LinkToStructure value={value} />;
25
+ case "field_list":
26
+ return (
27
+ <>
28
+ {_.map.convert({ cap: false })((v, i) => (
29
+ <LinkToStructure key={i} value={v} />
30
+ ))(value)}
31
+ </>
32
+ );
33
+ default:
34
+ return (
35
+ <span className="highlighted">
36
+ <FormattedMessage
37
+ id={`ruleImplementation.filtersField.${value.raw}`}
38
+ defaultMessage={`"${
39
+ _.isArray(value.raw) ? _.join(", ")(value.raw) : value.raw
40
+ }"`}
41
+ ></FormattedMessage>
42
+ </span>
43
+ );
44
+ }
45
+ };
28
46
 
29
47
  FormattedLink.propTypes = {
30
48
  value: PropTypes.object,
@@ -33,26 +51,52 @@ FormattedLink.propTypes = {
33
51
 
34
52
  const nilOrEmpty = (v) => _.isNil(v) || v === "";
35
53
 
36
- const filterNilOrEmpties = (values, keys) =>
37
- _.filter((v) => _.every((k) => !nilOrEmpty(_.prop(k)(v.kv)))(keys))(values);
54
+ const filterNilOrEmpties = (values, keys) => {
55
+ if ("kv" in values) {
56
+ return _.every((k) => !nilOrEmpty(values.kv[k]))(keys) ? values : null;
57
+ }
38
58
 
39
- const valuesFromKeys = (values, keys, optionalKeys, value_modifier) =>
40
- _.flow(
41
- _.map((v) => {
42
- return { kv: _.pick(keys)(v), optv: _.pick(optionalKeys)(v) };
59
+ return _.flow(
60
+ _.map((v) => filterNilOrEmpties(v, keys)),
61
+ _.filter((v) => !!v)
62
+ )(values);
63
+ };
64
+
65
+ const pick = (value, keys, optionalKeys) => ({
66
+ kv: _.pick(keys)(value),
67
+ optv: _.pick(optionalKeys)(value),
68
+ });
69
+
70
+ const convert = (v, index, value_modifier) => {
71
+ if (_.has("kv")(v)) {
72
+ return { ...v.kv, ...v.optv, modifier: _.nth(index)(value_modifier) };
73
+ }
74
+
75
+ return _.map.convert({ cap: false })((v, i) => {
76
+ return convert(v, i, value_modifier);
77
+ })(v);
78
+ };
79
+
80
+ const valuesFromKeys = (values, keys, optionalKeys, value_modifier) => {
81
+ return _.flow(
82
+ _.map((value) => {
83
+ return Array.isArray(value?.fields)
84
+ ? _.map((innerValue) => pick(innerValue, keys, optionalKeys))(
85
+ value?.fields
86
+ )
87
+ : pick(value, keys, optionalKeys);
43
88
  }),
44
89
  (v) => filterNilOrEmpties(v, keys),
45
- _.map.convert({ cap: false })((v, i) => {
46
- return { ...v.kv, ...v.optv, modifier: _.nth(i)(value_modifier) };
47
- })
90
+ (v) => convert(v, 0, value_modifier)
48
91
  )(values);
92
+ };
49
93
 
50
94
  export const empty = (rows) =>
51
95
  rows && _.every((r) => _.isEmpty(r) || _.isNil(r))(rows);
52
96
 
53
97
  export const getValues = ({ value = [], operator = {}, value_modifier }) => {
54
98
  const defaultOrValue = value || [];
55
- return _.propEq("value_type", "field")(operator)
99
+ return ["field", "field_list"].includes(operator?.value_type)
56
100
  ? valuesFromKeys(defaultOrValue, ["id", "name"], ["path"], value_modifier)
57
101
  : valuesFromKeys(defaultOrValue, ["raw"], [], value_modifier);
58
102
  };
@@ -25,6 +25,24 @@ const updateDatasetKey = (data) =>
25
25
  {}
26
26
  )(data);
27
27
 
28
+ // https://stackoverflow.com/a/38417085
29
+ const updateValueKeyValue = (object) => {
30
+ if (object?.id || object?.raw || object?.fields) {
31
+ return object?.id
32
+ ? _.pick(["id", "parent_index"])(object)
33
+ : _.pick(["raw", "fields"])(object);
34
+ }
35
+
36
+ return Object.keys(object).reduce(
37
+ (output, key) => {
38
+ // eslint-disable-next-line fp/no-mutation
39
+ output[key] = updateValueKeyValue(object[key]);
40
+ return output;
41
+ },
42
+ Array.isArray(object) ? [] : {}
43
+ );
44
+ };
45
+
28
46
  const updateConditionValue = (acc, value, key) => {
29
47
  if (key === "structure") {
30
48
  return _.set(key, _.pick(["id", "parent_index"])(value))(acc);
@@ -49,12 +67,10 @@ const updateConditionValue = (acc, value, key) => {
49
67
  )
50
68
  )(acc);
51
69
  if (key == "value")
52
- return _.set(
53
- key,
54
- _.map((v) =>
55
- _.has("id")(v) ? _.pick(["id", "parent_index"])(v) : _.pick(["raw"])(v)
56
- )(value)
57
- )(acc);
70
+ return {
71
+ ...acc,
72
+ value: updateValueKeyValue(value),
73
+ };
58
74
  if (key === "population") return _.set(key, conditionAttributes(value))(acc);
59
75
 
60
76
  return acc;
@@ -103,6 +119,7 @@ const fieldTypeFromStructure = (row, structures, operators, scope) => {
103
119
  field_type,
104
120
  _.prop("operator")(row)
105
121
  );
122
+
106
123
  const updatedRow = {
107
124
  ...row,
108
125
  operator,
@@ -48,6 +48,24 @@ export const FiltersField = ({
48
48
  const { value_type, value_type_filter, fixed_values } = operator;
49
49
 
50
50
  const modifierDef = _.find({ name: modifier?.name })(typeCastModifiers);
51
+ const pickFromValue = _.pick(["data_structure_id", "name", "parent_index"]);
52
+
53
+ const getVal = (value) => {
54
+ return _.isNil(_.prop("parent_index")(value))
55
+ ? _.prop("id")(value)
56
+ : `${_.prop("id")(value)}/${_.prop("parent_index")(value)}`;
57
+ };
58
+
59
+ const getValue = (valueOrValues, operator) => {
60
+ const getValueResultado =
61
+ operator?.value_type === "field_list"
62
+ ? (valueOrValues?.fields || []).map((v) => {
63
+ return getVal(v);
64
+ })
65
+ : getVal(valueOrValues);
66
+
67
+ return getValueResultado;
68
+ };
51
69
 
52
70
  switch (value_type) {
53
71
  case "string":
@@ -89,6 +107,7 @@ export const FiltersField = ({
89
107
  case "timestamp":
90
108
  return <DateTimeField label={label} value={value} onChange={onChange} />;
91
109
  case "field":
110
+ case "field_list":
92
111
  const structureFields = getStructureFields(parentStructures);
93
112
  return value_type_filter == "any" ? (
94
113
  <StructureSelectorInputField
@@ -111,24 +130,30 @@ export const FiltersField = ({
111
130
  ) : (
112
131
  <>
113
132
  <StructureFieldsDropdown
133
+ multiple={value_type === "field_list"}
114
134
  label={label}
115
135
  inline={false}
116
136
  parentStructures={parentStructures}
117
137
  structureFields={structureFields}
118
138
  typeCastModifiers={typeCastModifiers}
119
- filters={{ field_type: [fieldType] }}
120
- onSelectField={(value, modifier) =>
139
+ filters={
140
+ value_type === "field_list" ? null : { field_type: [fieldType] }
141
+ }
142
+ onSelectField={(value, modifier) => {
121
143
  onChange(
122
144
  null,
123
- _.pick(["data_structure_id", "name", "parent_index"])(value),
145
+ pickFromValue(value),
124
146
  modifier ? { name: modifier.name } : null
125
- )
126
- }
127
- value={
128
- _.isNil(_.prop("parent_index")(value))
129
- ? _.prop("id")(value)
130
- : `${_.prop("id")(value)}/${_.prop("parent_index")(value)}`
131
- }
147
+ );
148
+ }}
149
+ onSelectFields={(values) => {
150
+ onChange(
151
+ null,
152
+ values.map((value) => pickFromValue(value)),
153
+ null
154
+ );
155
+ }}
156
+ value={getValue(value, operator)}
132
157
  />
133
158
  {modifier && (
134
159
  <FieldModifier
@@ -65,7 +65,9 @@ export const FiltersFormGroup = ({
65
65
  inline={false}
66
66
  parentStructures={parentStructures}
67
67
  structureFields={structureFields}
68
- onSelectField={(value) => onStructureChange(index, value)}
68
+ onSelectField={(value) => {
69
+ return onStructureChange(index, value /*[0]*/);
70
+ }}
69
71
  value={
70
72
  _.isNil(_.prop("parent_index")(clause?.structure))
71
73
  ? _.prop("id")(clause?.structure)
@@ -122,7 +124,9 @@ export const FiltersFormGroup = ({
122
124
  })}
123
125
  options={operatorsOptions}
124
126
  value={getOperatorValue(clause)}
125
- onClick={({ id }) => onOperatorChange(index, id)}
127
+ onClick={(whatever) => {
128
+ onOperatorChange(index, whatever.id);
129
+ }}
126
130
  placeholder={formatMessage({
127
131
  id: "operator.dropdown.placeholder",
128
132
  })}
@@ -56,13 +56,23 @@ export const FiltersGrid = ({
56
56
  });
57
57
  };
58
58
 
59
+ const composeFieldValue = (value) => {
60
+ return {
61
+ id: value.data_structure_id || value.id,
62
+ name: value.name,
63
+ path: value.path,
64
+ parent_index: value.parent_index,
65
+ };
66
+ };
67
+
59
68
  const composeValue = (value_type, value) => {
60
69
  if (value_type == "field") {
70
+ return composeFieldValue(value);
71
+ } else if (value_type === "field_list") {
61
72
  return {
62
- id: value.data_structure_id || value.id,
63
- name: value.name,
64
- path: value.path,
65
- parent_index: value.parent_index,
73
+ fields: _.map((v) => {
74
+ return composeFieldValue(v);
75
+ })(value),
66
76
  };
67
77
  } else {
68
78
  return { raw: value };
@@ -62,7 +62,6 @@ export const InformationForm = ({
62
62
  id: "ruleImplementation.props.name.placeholder",
63
63
  })}
64
64
  autoComplete="off"
65
- disabled={ruleImplementation?.status === "published"}
66
65
  />
67
66
  </Form.Field>
68
67
  {_.isEmpty(rule) && !ruleImplementation?.rule_id ? (
@@ -50,7 +50,13 @@ exports[`<FiltersFormGroup /> matches the latest snapshot 1`] = `
50
50
  <div
51
51
  class="menu transition"
52
52
  role="listbox"
53
- />
53
+ >
54
+ <div
55
+ class="message"
56
+ >
57
+ No results found.
58
+ </div>
59
+ </div>
54
60
  </div>
55
61
  </div>
56
62
  <div
@@ -56,7 +56,13 @@ exports[`<FiltersGroup /> matches the latest snapshot when siblings provided 1`]
56
56
  <div
57
57
  class="menu transition"
58
58
  role="listbox"
59
- />
59
+ >
60
+ <div
61
+ class="message"
62
+ >
63
+ No results found.
64
+ </div>
65
+ </div>
60
66
  </div>
61
67
  </div>
62
68
  <div
@@ -56,7 +56,13 @@ exports[`<ValueConditions /> matches the latest snapshot when siblings provided
56
56
  <div
57
57
  class="menu transition"
58
58
  role="listbox"
59
- />
59
+ >
60
+ <div
61
+ class="message"
62
+ >
63
+ No results found.
64
+ </div>
65
+ </div>
60
66
  </div>
61
67
  </div>
62
68
  <div
@@ -423,7 +423,8 @@ export default {
423
423
  "matches regular expression",
424
424
  "ruleImplementation.operator.starts_with": "starts with",
425
425
  "ruleImplementation.operator.starts_with.string": "starts with",
426
- "ruleImplementation.operator.unique": "has unique value",
426
+ "ruleImplementation.operator.unique": "unique",
427
+ "ruleImplementation.operator.unique.field_list": "unique across fields",
427
428
  "ruleImplementation.operator.variation_on_count": "count variation",
428
429
  "ruleImplementation.operator.variation_on_count.string": "count variation",
429
430
  "ruleImplementation.props.esquema": "Structure",
@@ -435,7 +435,8 @@ export default {
435
435
  "ruleImplementation.operator.regex_format": "cumple la expresión regular",
436
436
  "ruleImplementation.operator.starts_with.string": "empieza por",
437
437
  "ruleImplementation.operator.starts_with": "empieza por",
438
- "ruleImplementation.operator.unique": "tiene valor único",
438
+ "ruleImplementation.operator.unique": "único",
439
+ "ruleImplementation.operator.unique.field_list": "único en conjunto",
439
440
  "ruleImplementation.operator.variation_on_count.string": "variación conteo",
440
441
  "ruleImplementation.operator.variation_on_count": "variación conteo",
441
442
  "ruleImplementation.props.esquema.placeholder": "añade una estructura",
@@ -5,6 +5,7 @@ const defaultOperators = {
5
5
  any: {
6
6
  operators: [
7
7
  { name: "unique", scope: "validation" },
8
+ { name: "unique", value_type: "field_list" },
8
9
  { name: "not_empty" },
9
10
  { name: "empty" },
10
11
  {