@truedat/dq 4.28.5 → 4.29.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.28.8] 2021-09-20
4
+
5
+ ### Added
6
+
7
+ - [TD-3970] Rule implementation support for casting fields as other types
8
+
3
9
  ## [4.28.3] 2021-09-14
4
10
 
5
11
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/dq",
3
- "version": "4.28.5",
3
+ "version": "4.29.0",
4
4
  "description": "Truedat Web Data Quality Module",
5
5
  "sideEffects": false,
6
6
  "jsnext:main": "src/index.js",
@@ -31,7 +31,7 @@
31
31
  "@babel/plugin-transform-modules-commonjs": "^7.15.0",
32
32
  "@babel/preset-env": "^7.15.0",
33
33
  "@babel/preset-react": "^7.14.5",
34
- "@truedat/test": "4.28.5",
34
+ "@truedat/test": "4.29.0",
35
35
  "babel-jest": "^27.0.6",
36
36
  "babel-plugin-dynamic-import-node": "^2.3.3",
37
37
  "babel-plugin-lodash": "^3.3.4",
@@ -82,8 +82,8 @@
82
82
  },
83
83
  "dependencies": {
84
84
  "@apollo/client": "^3.4.10",
85
- "@truedat/core": "4.28.5",
86
- "@truedat/df": "4.28.5",
85
+ "@truedat/core": "4.29.0",
86
+ "@truedat/df": "4.29.0",
87
87
  "axios": "^0.19.2",
88
88
  "graphql": "^15.5.3",
89
89
  "path-to-regexp": "^1.7.0",
@@ -103,5 +103,5 @@
103
103
  "react-dom": ">= 16.8.6 < 17",
104
104
  "semantic-ui-react": ">= 0.88.2 < 2.1"
105
105
  },
106
- "gitHead": "e004c6a673d6824a4807e4378b6e5c83e947c992"
106
+ "gitHead": "42028609748cd7f1686ea783264a9cdfa7b0ca8f"
107
107
  }
@@ -11,7 +11,7 @@ const defaults = [
11
11
  "implementationKey",
12
12
  "dataset",
13
13
  "population",
14
- "validations"
14
+ "validations",
15
15
  ];
16
16
 
17
17
  const concatValues = (values, link) => _.join(link)(values);
@@ -36,34 +36,35 @@ const FormattedLink = ({ value, operator = {} }) =>
36
36
 
37
37
  FormattedLink.propTypes = {
38
38
  value: PropTypes.object,
39
- operator: PropTypes.object
39
+ operator: PropTypes.object,
40
40
  };
41
41
 
42
42
  const path = ({ name, path = [] }) => concatValues([...path, name], " > ");
43
43
 
44
- const empty = rows => rows && _.every(r => _.isEmpty(r) || _.isNil(r))(rows);
44
+ const empty = (rows) =>
45
+ rows && _.every((r) => _.isEmpty(r) || _.isNil(r))(rows);
45
46
 
46
- const nilOrEmpty = v => _.isNil(v) || v === "";
47
+ const nilOrEmpty = (v) => _.isNil(v) || v === "";
47
48
 
48
49
  const filterNilOrEmpties = (values, keys) =>
49
- _.filter(v => _.every(k => !nilOrEmpty(_.prop(k)(v.kv)))(keys))(values);
50
+ _.filter((v) => _.every((k) => !nilOrEmpty(_.prop(k)(v.kv)))(keys))(values);
50
51
 
51
- const valuesFromKeys = (values, keys, optionalKeys) =>
52
+ const valuesFromKeys = (values, keys, optionalKeys, value_modifier) =>
52
53
  _.flow(
53
- _.map(v => {
54
+ _.map((v) => {
54
55
  return { kv: _.pick(keys)(v), optv: _.pick(optionalKeys)(v) };
55
56
  }),
56
- v => filterNilOrEmpties(v, keys),
57
- _.map(v => {
58
- return { ...v.kv, ...v.optv };
57
+ (v) => filterNilOrEmpties(v, keys),
58
+ _.map.convert({ cap: false })((v, i) => {
59
+ return { ...v.kv, ...v.optv, modifier: _.nth(i)(value_modifier) };
59
60
  })
60
61
  )(values);
61
62
 
62
- export const getValues = ({ value = [], operator = {} }) => {
63
+ export const getValues = ({ value = [], operator = {}, value_modifier }) => {
63
64
  const defaultOrValue = value || [];
64
65
  return _.propEq("value_type", "field")(operator)
65
- ? valuesFromKeys(defaultOrValue, ["id", "name"], ["path"])
66
- : valuesFromKeys(defaultOrValue, ["raw"], []);
66
+ ? valuesFromKeys(defaultOrValue, ["id", "name"], ["path"], value_modifier)
67
+ : valuesFromKeys(defaultOrValue, ["raw"], [], value_modifier);
67
68
  };
68
69
 
69
70
  const GeneralSummary = ({ executable, implementationKey }) => (
@@ -102,7 +103,7 @@ const GeneralSummary = ({ executable, implementationKey }) => (
102
103
 
103
104
  GeneralSummary.propTypes = {
104
105
  executable: PropTypes.bool,
105
- implementationKey: PropTypes.string
106
+ implementationKey: PropTypes.string,
106
107
  };
107
108
 
108
109
  const DatasetSummary = ({ rows }) =>
@@ -129,7 +130,7 @@ const DatasetSummary = ({ rows }) =>
129
130
  <Table.Cell key={i}>
130
131
  <Link
131
132
  to={linkTo.STRUCTURE({
132
- id: _.pathOr("", "structure.id")(row)
133
+ id: _.pathOr("", "structure.id")(row),
133
134
  })}
134
135
  >
135
136
  <span key={i} className="highlighted">{`"${path(
@@ -146,7 +147,7 @@ const DatasetSummary = ({ rows }) =>
146
147
  );
147
148
 
148
149
  DatasetSummary.propTypes = {
149
- rows: PropTypes.array
150
+ rows: PropTypes.array,
150
151
  };
151
152
 
152
153
  const OperatorMessage = ({ operator }) =>
@@ -155,12 +156,12 @@ const OperatorMessage = ({ operator }) =>
155
156
  id={`ruleImplementation.operator.${_.prop("name")(operator)}`}
156
157
  defaultMessage={_.prop("name")(operator)}
157
158
  >
158
- {message => <span className="highlighted">{`"${message}"`}</span>}
159
+ {(message) => <span className="highlighted">{`"${message}"`}</span>}
159
160
  </FormattedMessage>
160
161
  ) : null;
161
162
 
162
163
  OperatorMessage.propTypes = {
163
- operator: PropTypes.object
164
+ operator: PropTypes.object,
164
165
  };
165
166
 
166
167
  const ConditionCell = ({ row }) => {
@@ -170,16 +171,37 @@ const ConditionCell = ({ row }) => {
170
171
  <Table.Row>
171
172
  <Table.Cell width={5}>
172
173
  {_.has("structure")(row) && (
173
- <Link
174
- to={linkTo.STRUCTURE({
175
- id: _.path("structure.id")(row)
176
- })}
177
- >
178
- <span className="highlighted">{`"${_.pathOr(
179
- "",
180
- "structure.name"
181
- )(row)}"`}</span>
182
- </Link>
174
+ <>
175
+ <Link
176
+ to={linkTo.STRUCTURE({
177
+ id: _.path("structure.id")(row),
178
+ })}
179
+ >
180
+ <span className="highlighted">{`"${_.pathOr(
181
+ "",
182
+ "structure.name"
183
+ )(row)}"`}</span>
184
+ </Link>
185
+ {row?.modifier && (
186
+ <>
187
+ <div className="smaller">
188
+ <FormattedMessage
189
+ id={`filtersGrid.field.modifier.${row.modifier.name}`}
190
+ defaultMessage={row.modifier.name}
191
+ />
192
+ </div>
193
+ {_.flow(
194
+ _.prop("modifier.params"),
195
+ _.toPairs,
196
+ _.map(([key, value]) => (
197
+ <div key={key} className="smaller">
198
+ {value}
199
+ </div>
200
+ ))
201
+ )(row)}
202
+ </>
203
+ )}
204
+ </>
183
205
  )}
184
206
  </Table.Cell>
185
207
  <Table.Cell width={5}>
@@ -195,6 +217,25 @@ const ConditionCell = ({ row }) => {
195
217
  <FormattedMessage id="summary.link.and" />{" "}
196
218
  </span>
197
219
  )}
220
+ {value?.modifier && (
221
+ <>
222
+ <div className="smaller">
223
+ <FormattedMessage
224
+ id={`filtersGrid.field.modifier.${value.modifier.name}`}
225
+ defaultMessage={value.modifier.name}
226
+ />
227
+ </div>
228
+ {_.flow(
229
+ _.prop("modifier.params"),
230
+ _.toPairs,
231
+ _.map(([key, value]) => (
232
+ <div key={key} className="smaller">
233
+ {value}
234
+ </div>
235
+ ))
236
+ )(value)}
237
+ </>
238
+ )}
198
239
  </Fragment>
199
240
  ))(values)}
200
241
  </Table.Cell>
@@ -203,7 +244,7 @@ const ConditionCell = ({ row }) => {
203
244
  };
204
245
 
205
246
  ConditionCell.propTypes = {
206
- row: PropTypes.object
247
+ row: PropTypes.object,
207
248
  };
208
249
 
209
250
  const ConditionSummary = ({ rows, type, icon }) =>
@@ -243,18 +284,13 @@ const ConditionSummary = ({ rows, type, icon }) =>
243
284
  ConditionSummary.propTypes = {
244
285
  icon: PropTypes.string,
245
286
  rows: PropTypes.array,
246
- type: PropTypes.string
287
+ type: PropTypes.string,
247
288
  };
248
289
 
249
290
  export const ImplementationSummary = ({ ruleImplementation, activeSteps }) => {
250
291
  const steps = _.isEmpty(activeSteps) ? defaults : activeSteps;
251
- const {
252
- executable,
253
- implementationKey,
254
- dataset,
255
- population,
256
- validations
257
- } = _.pick(steps)(ruleImplementation);
292
+ const { executable, implementationKey, dataset, population, validations } =
293
+ _.pick(steps)(ruleImplementation);
258
294
  return (
259
295
  <>
260
296
  {(_.includes("implementationKey")(steps) ||
@@ -283,7 +319,7 @@ export const ImplementationSummary = ({ ruleImplementation, activeSteps }) => {
283
319
 
284
320
  ImplementationSummary.propTypes = {
285
321
  activeSteps: PropTypes.array,
286
- ruleImplementation: PropTypes.object
322
+ ruleImplementation: PropTypes.object,
287
323
  };
288
324
 
289
325
  export default ImplementationSummary;
@@ -30,6 +30,18 @@ const updateDatasetKey = (data) =>
30
30
 
31
31
  const updateConditionValue = (acc, value, key) => {
32
32
  if (key === "structure") return _.set(key, _.pick(["id"])(value))(acc);
33
+ if (key === "modifier")
34
+ return _.set(
35
+ key,
36
+ _.isNil(value) ? null : _.pick(["name", "params"])(value)
37
+ )(acc);
38
+ if (key === "value_modifier")
39
+ return _.set(
40
+ key,
41
+ _.every(_.isNil)(value)
42
+ ? []
43
+ : _.map((v) => _.pick(["name", "params"])(v))(value)
44
+ )(acc);
33
45
  if (key === "operator")
34
46
  return _.set(
35
47
  key,
@@ -63,7 +75,16 @@ const datasetAttributes = (dataset) =>
63
75
 
64
76
  const conditionAttributes = (condition) =>
65
77
  _.flow(
66
- _.map(_.pickAll(["structure", "operator", "value", "population"])),
78
+ _.map(
79
+ _.pickAll([
80
+ "structure",
81
+ "modifier",
82
+ "operator",
83
+ "value",
84
+ "value_modifier",
85
+ "population",
86
+ ])
87
+ ),
67
88
  _.map(updateConditionKey)
68
89
  )(condition);
69
90
 
@@ -72,6 +93,8 @@ const fieldTypeFromStructure = (row, structures, operators, scope) => {
72
93
  const field = _.find(_.propEq("data_structure_id", structure_id))(structures);
73
94
  const field_type = getFieldType(field);
74
95
  const structure = _.prop("structure")(row);
96
+ const modifier = _.prop("modifier")(row);
97
+ const value_modifier = _.prop("value_modifier")(row);
75
98
  const operator = enrichOperator(
76
99
  operators,
77
100
  scope,
@@ -80,7 +103,9 @@ const fieldTypeFromStructure = (row, structures, operators, scope) => {
80
103
  );
81
104
  const updatedRow = {
82
105
  ...row,
83
- operator: operator,
106
+ operator,
107
+ modifier,
108
+ value_modifier,
84
109
  structure: { ...structure, field_type },
85
110
  };
86
111
  return updatedRow;
@@ -116,7 +141,7 @@ const addFieldType = (
116
141
  const rows = _.map((row) =>
117
142
  rowFieldType(row, fields, siblings, operators, scope)
118
143
  )(_.prop(implementationPropertyName)(ruleImplementationProps));
119
- return _.isEmpty(rows) ? [{}] : rows;
144
+ return _.isEmpty(rows) ? [] : rows;
120
145
  };
121
146
 
122
147
  const enrichOperator = (operators, scope, field_type, operator) => {
@@ -201,7 +226,7 @@ export const NewRuleImplementation = ({
201
226
  implementationKey: "",
202
227
  implementationType: "default",
203
228
  dataset: [{}],
204
- population: [{}],
229
+ population: [],
205
230
  validations: [{}],
206
231
  rawContent: {
207
232
  database: "",
@@ -253,7 +278,7 @@ export const NewRuleImplementation = ({
253
278
  ...ruleImplementation,
254
279
  dataset,
255
280
  population: _.isEmpty(population_within_dataset)
256
- ? [{}]
281
+ ? []
257
282
  : population_within_dataset,
258
283
  validations: _.isEmpty(validations_within_dataset)
259
284
  ? [{}]
@@ -383,11 +408,8 @@ export const NewRuleImplementation = ({
383
408
  {ruleImplementation.implementationType != "raw" && (
384
409
  <RuleImplementationForm
385
410
  ruleImplementation={ruleImplementation}
386
- structures={_.prop("dataset")(ruleImplementation)}
387
411
  setStructures={setDataset}
388
- population={_.prop("population")(ruleImplementation)}
389
412
  setPopulation={setPopulation}
390
- validations={_.prop("validations")(ruleImplementation)}
391
413
  setValidations={setValidations}
392
414
  operators={operators}
393
415
  setImplementationKey={setImplementationKey}
@@ -31,11 +31,6 @@ exports[`<NewRuleImplementation /> matches the latest snapshot 1`] = `
31
31
  <Connect(RuleImplementationForm)
32
32
  handleSubmit={[Function]}
33
33
  onChange={[Function]}
34
- population={
35
- Array [
36
- Object {},
37
- ]
38
- }
39
34
  ruleImplementation={
40
35
  Object {
41
36
  "dataset": Array [
@@ -46,9 +41,7 @@ exports[`<NewRuleImplementation /> matches the latest snapshot 1`] = `
46
41
  "executable": true,
47
42
  "implementationKey": "",
48
43
  "implementationType": "default",
49
- "population": Array [
50
- Object {},
51
- ],
44
+ "population": Array [],
52
45
  "rawContent": Object {
53
46
  "database": "",
54
47
  "dataset": "",
@@ -65,16 +58,6 @@ exports[`<NewRuleImplementation /> matches the latest snapshot 1`] = `
65
58
  setPopulation={[Function]}
66
59
  setStructures={[Function]}
67
60
  setValidations={[Function]}
68
- structures={
69
- Array [
70
- Object {},
71
- ]
72
- }
73
- validations={
74
- Array [
75
- Object {},
76
- ]
77
- }
78
61
  />
79
62
  </GridRow>
80
63
  </GridColumn>
@@ -92,9 +75,7 @@ exports[`<NewRuleImplementation /> matches the latest snapshot 1`] = `
92
75
  "executable": true,
93
76
  "implementationKey": "",
94
77
  "implementationType": "default",
95
- "population": Array [
96
- Object {},
97
- ],
78
+ "population": Array [],
98
79
  "rawContent": Object {
99
80
  "database": "",
100
81
  "dataset": "",
@@ -0,0 +1,51 @@
1
+ import { getOr } from "lodash/fp";
2
+ import React from "react";
3
+ import { useIntl } from "react-intl";
4
+ import { Form, Icon } from "semantic-ui-react";
5
+
6
+ export default function FieldModifier({
7
+ modifier,
8
+ value,
9
+ onChange,
10
+ removable = false,
11
+ }) {
12
+ const { formatMessage } = useIntl();
13
+
14
+ return (
15
+ <div className={"flex-row-align-end"}>
16
+ <Form.Field>
17
+ <label>
18
+ {formatMessage({ id: `filtersGrid.field.modifier.${modifier.name}` })}
19
+ </label>
20
+ {getOr(
21
+ [],
22
+ "params"
23
+ )(modifier).map((param, key) => (
24
+ <input
25
+ key={key}
26
+ placeholder={formatMessage({
27
+ id: `filtersGrid.field.modifier.${modifier.name}.${param.name}`,
28
+ })}
29
+ value={getOr("", `params.${param.name}`)(value)}
30
+ onChange={({ target }) => {
31
+ onChange({
32
+ ...value,
33
+ params: {
34
+ ...getOr({}, "params")(value),
35
+ [param.name]: target.value,
36
+ },
37
+ });
38
+ }}
39
+ />
40
+ ))}
41
+ </Form.Field>
42
+ {removable && (
43
+ <Icon
44
+ name="x"
45
+ className="selectable force-margin-bottom"
46
+ onClick={() => onChange(null)}
47
+ />
48
+ )}
49
+ </div>
50
+ );
51
+ }
@@ -10,6 +10,7 @@ import FixedListField from "./FixedListField";
10
10
  import NumberField from "./NumberField";
11
11
  import StringField from "./StringField";
12
12
  import StringListField from "./StringListField";
13
+ import FieldModifier from "./FieldModifier";
13
14
 
14
15
  const StructureFieldsDropdown = React.lazy(() =>
15
16
  import("@truedat/dd/components/StructureFieldsDropdown")
@@ -41,12 +42,16 @@ export const FiltersField = ({
41
42
  operator,
42
43
  fieldType,
43
44
  value,
45
+ modifier,
44
46
  name,
45
47
  onChange,
48
+ typeCastModifiers,
46
49
  }) => {
47
50
  const [active, setActive] = useState(false);
48
51
  const { value_type, value_type_filter, fixed_values } = operator;
49
52
 
53
+ const modifierDef = _.find({ name: modifier?.name })(typeCastModifiers);
54
+
50
55
  switch (value_type) {
51
56
  case "string":
52
57
  return !fixed_values ? (
@@ -109,17 +114,31 @@ export const FiltersField = ({
109
114
  fieldWidth={15}
110
115
  />
111
116
  ) : (
112
- <StructureFieldsDropdown
113
- label={label}
114
- inline={false}
115
- parentStructures={parentStructures}
116
- structureFields={structureFields}
117
- filters={{ field_type: [fieldType] }}
118
- onSelectField={(value) => {
119
- onChange(null, _.pick(["data_structure_id", "name"])(value));
120
- }}
121
- value={_.prop("id")(value)}
122
- />
117
+ <>
118
+ <StructureFieldsDropdown
119
+ label={label}
120
+ inline={false}
121
+ parentStructures={parentStructures}
122
+ structureFields={structureFields}
123
+ typeCastModifiers={typeCastModifiers}
124
+ filters={{ field_type: [fieldType] }}
125
+ onSelectField={(value, modifier) =>
126
+ onChange(
127
+ null,
128
+ _.pick(["data_structure_id", "name"])(value),
129
+ modifier ? { name: modifier.name } : null
130
+ )
131
+ }
132
+ value={_.prop("id")(value)}
133
+ />
134
+ {modifier && (
135
+ <FieldModifier
136
+ modifier={modifierDef}
137
+ value={modifier}
138
+ onChange={(modifier) => onChange(null, value, modifier)}
139
+ />
140
+ )}
141
+ </>
123
142
  );
124
143
  case "string_list":
125
144
  return (
@@ -144,6 +163,8 @@ FiltersField.propTypes = {
144
163
  value: PropTypes.string,
145
164
  name: PropTypes.string,
146
165
  onChange: PropTypes.func,
166
+ modifier: PropTypes.object,
167
+ typeCastModifiers: PropTypes.array,
147
168
  };
148
169
 
149
170
  const mapStateToProps = (state) => ({