@truedat/dq 4.41.3 → 4.42.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 (22) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/package.json +5 -5
  3. package/src/components/ConditionSummary.js +41 -27
  4. package/src/components/ImplementationSummary.js +5 -5
  5. package/src/components/NewRuleImplementation.js +76 -17
  6. package/src/components/RuleImplementationProperties.js +2 -1
  7. package/src/components/__test_samples__/NewRuleImplementationProps.js +44 -42
  8. package/src/components/__tests__/ImplementationSummary.spec.js +4 -4
  9. package/src/components/__tests__/__snapshots__/ImplementationSummary.spec.js.snap +68 -3
  10. package/src/components/__tests__/__snapshots__/NewRuleImplementation.spec.js.snap +108 -100
  11. package/src/components/__tests__/__snapshots__/RuleImplementationProperties.spec.js.snap +1 -1
  12. package/src/components/ruleImplementationForm/PopulationForm.js +1 -27
  13. package/src/components/ruleImplementationForm/RuleImplementationForm.js +69 -20
  14. package/src/components/ruleImplementationForm/RuleImplementationRawForm.js +163 -177
  15. package/src/components/ruleImplementationForm/__tests__/RuleImplementationRawForm.spec.js +3 -3
  16. package/src/components/ruleImplementationForm/__tests__/__snapshots__/RuleImplementationForm.spec.js.snap +1 -1
  17. package/src/components/ruleImplementationForm/__tests__/__snapshots__/RuleImplementationRawForm.spec.js.snap +159 -156
  18. package/src/messages/en.js +4 -2
  19. package/src/messages/es.js +3 -1
  20. package/src/reducers/ruleImplementation.js +1 -1
  21. package/src/sagas/__tests__/createRuleImplementation.spec.js +12 -10
  22. package/src/sagas/__tests__/updateRuleImplementation.spec.js +8 -6
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.42.1] 2022-04-12
4
+
5
+ # Changed
6
+
7
+ - [TD-4536] Support rule implementation with multiple populations
8
+
9
+ ## [4.41.4] 2022-04-04
10
+
11
+ ### Changed
12
+
13
+ - [TD-4450] Sources in implementation form are now fetched using GraphQL API
14
+ instead of from global state
15
+
3
16
  ## [4.40.13] 2022-03-21
4
17
 
5
18
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/dq",
3
- "version": "4.41.3",
3
+ "version": "4.42.1",
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.41.2",
34
+ "@truedat/test": "4.42.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.41.3",
86
- "@truedat/df": "4.41.3",
85
+ "@truedat/core": "4.42.0",
86
+ "@truedat/df": "4.42.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": "946829496288ea4ff05b3029b37d8c8d16393127"
106
+ "gitHead": "0661831301c7357a78ff0d5f72a7bb2da8d8dd72"
107
107
  }
@@ -163,41 +163,55 @@ ConditionCell.propTypes = {
163
163
  alias: PropTypes.array,
164
164
  };
165
165
 
166
- export const ConditionSummary = ({ rows, type, icon, alias }) =>
167
- empty(rows) ? null : (
166
+ export const ConditionSummary = ({ rows, type, icon, alias }) => {
167
+ const areMultidimensionalRows =
168
+ rows.filter(Array.isArray).length == rows.length;
169
+ const wrappedRows = areMultidimensionalRows ? rows : [rows];
170
+ return empty(rows) ? null : (
168
171
  <>
169
172
  <Header as="h3">
170
173
  <Icon name={icon} size="small" />
171
174
  <Header.Content>
172
- <FormattedMessage
173
- id={`ruleImplementations.summary.headers.${type}`}
174
- />
175
+ {areMultidimensionalRows ? (
176
+ <FormattedMessage
177
+ id={`ruleImplementations.summary.headers.${type}s`}
178
+ />
179
+ ) : (
180
+ <FormattedMessage
181
+ id={`ruleImplementations.summary.headers.${type}`}
182
+ />
183
+ )}
175
184
  </Header.Content>
176
185
  </Header>
177
- <Segment>
178
- <Table basic="very">
179
- <Table.Header>
180
- <Table.Row>
181
- <Table.HeaderCell>
182
- <FormattedMessage id={`ruleImplementation.summary.field`} />
183
- </Table.HeaderCell>
184
- <Table.HeaderCell>
185
- <FormattedMessage id={`ruleImplementation.summary.operator`} />
186
- </Table.HeaderCell>
187
- <Table.HeaderCell>
188
- <FormattedMessage id={`ruleImplementation.summary.values`} />
189
- </Table.HeaderCell>
190
- </Table.Row>
191
- </Table.Header>
192
- <Table.Body>
193
- {rows.map((row, i) => (
194
- <ConditionCell key={i} row={row} alias={alias} />
195
- ))}
196
- </Table.Body>
197
- </Table>
198
- </Segment>
186
+ {wrappedRows.map((rows, i) => (
187
+ <Segment key={i}>
188
+ <Table basic="very">
189
+ <Table.Header>
190
+ <Table.Row>
191
+ <Table.HeaderCell>
192
+ <FormattedMessage id={`ruleImplementation.summary.field`} />
193
+ </Table.HeaderCell>
194
+ <Table.HeaderCell>
195
+ <FormattedMessage
196
+ id={`ruleImplementation.summary.operator`}
197
+ />
198
+ </Table.HeaderCell>
199
+ <Table.HeaderCell>
200
+ <FormattedMessage id={`ruleImplementation.summary.values`} />
201
+ </Table.HeaderCell>
202
+ </Table.Row>
203
+ </Table.Header>
204
+ <Table.Body>
205
+ {rows.map((row, i) => (
206
+ <ConditionCell key={i} row={row} alias={alias} />
207
+ ))}
208
+ </Table.Body>
209
+ </Table>
210
+ </Segment>
211
+ ))}
199
212
  </>
200
213
  );
214
+ };
201
215
 
202
216
  ConditionSummary.propTypes = {
203
217
  icon: PropTypes.string,
@@ -13,7 +13,7 @@ const defaults = [
13
13
  "executable",
14
14
  "implementationKey",
15
15
  "dataset",
16
- "population",
16
+ "populations",
17
17
  "validations",
18
18
  ];
19
19
 
@@ -130,7 +130,7 @@ DatasetSummary.propTypes = {
130
130
 
131
131
  export const ImplementationSummary = ({ ruleImplementation, activeSteps }) => {
132
132
  const steps = _.isEmpty(activeSteps) ? defaults : activeSteps;
133
- const { dataset, population, validations } =
133
+ const { dataset, populations, validations } =
134
134
  _.pick(steps)(ruleImplementation);
135
135
  const alias = _.map(_.propOr({}, "alias"))(dataset);
136
136
  return (
@@ -142,14 +142,14 @@ export const ImplementationSummary = ({ ruleImplementation, activeSteps }) => {
142
142
  {dataset && _.includes("dataset")(steps) && (
143
143
  <DatasetSummary rows={dataset} alias={alias} />
144
144
  )}
145
- {population && _.includes("population")(steps) && (
145
+ {populations && _.includes("populations")(steps) ? (
146
146
  <ConditionSummary
147
147
  type="population"
148
148
  icon="user"
149
- rows={population}
149
+ rows={populations}
150
150
  alias={alias}
151
151
  />
152
- )}
152
+ ) : null}
153
153
  {validations && _.includes("validations")(steps) && (
154
154
  <ConditionSummary
155
155
  type="validations"
@@ -4,6 +4,8 @@ import PropTypes from "prop-types";
4
4
  import { connect } from "react-redux";
5
5
  import { Button, 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";
7
9
  import { getFieldType } from "@truedat/core/services/fieldType";
8
10
  import { applyTemplate } from "@truedat/df/utils";
9
11
  import {
@@ -90,6 +92,9 @@ const conditionAttributes = (condition) =>
90
92
  _.map(updateConditionKey)
91
93
  )(condition);
92
94
 
95
+ const populationsAttributes = (populations) =>
96
+ _.flow(_.map(conditionAttributes))(populations);
97
+
93
98
  const fieldTypeFromStructure = (row, structures, operators, scope) => {
94
99
  const structure_id = _.path("structure.id")(row);
95
100
  const field = _.find(_.propEq("data_structure_id", structure_id))(structures);
@@ -240,6 +245,7 @@ export const NewRuleImplementation = ({
240
245
  rule,
241
246
  ruleImplementationProps,
242
247
  sources,
248
+ sourcesLoading,
243
249
  structuresFields,
244
250
  structuresSiblings,
245
251
  operators,
@@ -250,6 +256,7 @@ export const NewRuleImplementation = ({
250
256
  _.propOr([{}], "dataset"),
251
257
  withDefaultAlias
252
258
  )(ruleImplementationProps);
259
+
253
260
  const [ruleImplementation, setRuleImplementation] = useState(
254
261
  edition
255
262
  ? {
@@ -262,17 +269,22 @@ export const NewRuleImplementation = ({
262
269
  ruleImplementationProps
263
270
  ),
264
271
  dataset: precalculatedDataset,
265
- population: withConditionDefaultAlias(
266
- addFieldType(
267
- ruleImplementationProps,
268
- structuresFields,
269
- structuresSiblings,
270
- "population",
271
- "population",
272
- operators
273
- ),
274
- precalculatedDataset
275
- ),
272
+ populations: _.flow(
273
+ _.pathOr([], "populations"),
274
+ _.map((population) =>
275
+ withConditionDefaultAlias(
276
+ addFieldType(
277
+ { population: population },
278
+ structuresFields,
279
+ structuresSiblings,
280
+ "population",
281
+ "population",
282
+ operators
283
+ ),
284
+ precalculatedDataset
285
+ )
286
+ )
287
+ )(ruleImplementationProps),
276
288
  validations: withConditionDefaultAlias(
277
289
  addFieldType(
278
290
  ruleImplementationProps,
@@ -299,6 +311,7 @@ export const NewRuleImplementation = ({
299
311
  implementationType: "default",
300
312
  dataset: [{}],
301
313
  population: [],
314
+ populations: [],
302
315
  validations: [{}],
303
316
  rawContent: {
304
317
  database: "",
@@ -392,6 +405,23 @@ export const NewRuleImplementation = ({
392
405
  setRuleImplementation({ ...ruleImplementation, population });
393
406
  };
394
407
 
408
+ const addPopulation = () => {
409
+ const populations = _.concat(
410
+ _.pathOr([], "populations")(ruleImplementation)
411
+ )([[{}]]);
412
+ setRuleImplementation({ ...ruleImplementation, populations: populations });
413
+ };
414
+
415
+ const setPopulations = (population, index) => {
416
+ const populations = _.flow(
417
+ _.pathOr([], "populations"),
418
+ _.update(`[${index}]`, (_previousPopulation) => population),
419
+ _.reject(_.isEmpty)
420
+ )(ruleImplementation);
421
+
422
+ setRuleImplementation({ ...ruleImplementation, populations: populations });
423
+ };
424
+
395
425
  const setValidations = (validations) => {
396
426
  setRuleImplementation({ ...ruleImplementation, validations });
397
427
  };
@@ -413,8 +443,8 @@ export const NewRuleImplementation = ({
413
443
  }, [template]);
414
444
 
415
445
  const doSubmit = () => {
416
- const population = _.filter(_.negate(_.isEmpty))(
417
- ruleImplementation.population
446
+ const populations = _.filter(_.negate(_.isEmpty))(
447
+ ruleImplementation.populations
418
448
  );
419
449
  const validations = _.filter(_.negate(_.isEmpty))(
420
450
  ruleImplementation.validations
@@ -449,7 +479,7 @@ export const NewRuleImplementation = ({
449
479
  : {
450
480
  executable: ruleImplementation.executable,
451
481
  dataset: datasetAttributes(_.prop("dataset")(ruleImplementation)),
452
- population: conditionAttributes(population),
482
+ populations: populationsAttributes(populations),
453
483
  validations: conditionAttributes(validations),
454
484
  implementation_key: _.prop("implementationKey")(ruleImplementation),
455
485
  implementation_type:
@@ -522,12 +552,15 @@ export const NewRuleImplementation = ({
522
552
  ruleImplementation={ruleImplementation}
523
553
  onChange={onChange}
524
554
  handleSubmit={doSubmit}
555
+ sources={sources}
556
+ sourcesLoading={sourcesLoading}
525
557
  />
526
558
  ) : (
527
559
  <RuleImplementationForm
528
560
  ruleImplementation={ruleImplementation}
529
561
  setStructures={setDataset}
530
- setPopulation={setPopulation}
562
+ addPopulation={addPopulation}
563
+ setPopulations={setPopulations}
531
564
  setValidations={setValidations}
532
565
  operators={operators}
533
566
  setImplementationKey={setImplementationKey}
@@ -561,6 +594,33 @@ NewRuleImplementation.propTypes = {
561
594
  template: PropTypes.object,
562
595
  applyTemplate: PropTypes.func,
563
596
  sources: PropTypes.array,
597
+ sourcesLoading: PropTypes.bool,
598
+ };
599
+
600
+ export const SOURCES_WITH_CONFIG = gql`
601
+ query SOURCES_WITH_CONFIG($jobTypes: String) {
602
+ sources(jobTypes: $jobTypes) {
603
+ id
604
+ externalId
605
+ config
606
+ }
607
+ }
608
+ `;
609
+
610
+ export const NewRuleImplementationLoader = (props) => {
611
+ const { data, error, loading } = useQuery(SOURCES_WITH_CONFIG, {
612
+ variables: { jobTypes: "quality" },
613
+ });
614
+ if (loading) return <Loading />;
615
+ if (error) return null;
616
+ const sources = data?.sources || [];
617
+ return (
618
+ <NewRuleImplementation
619
+ sources={sources}
620
+ sourcesLoading={loading}
621
+ {...props}
622
+ />
623
+ );
564
624
  };
565
625
 
566
626
  const mapStateToProps = (state) => ({
@@ -570,7 +630,6 @@ const mapStateToProps = (state) => ({
570
630
  raw_content: state.ruleImplementationRaw,
571
631
  },
572
632
  ruleImplementationRaw: state.ruleImplementationRaw,
573
- sources: state.sources,
574
633
  structuresFields: state.structuresFields,
575
634
  structuresSiblings: state.structuresSiblings,
576
635
  canManageRaw: _.prop("manage_raw_quality_rule_implementations")(
@@ -584,4 +643,4 @@ const mapStateToProps = (state) => ({
584
643
  export default connect(mapStateToProps, {
585
644
  createRuleImplementation,
586
645
  updateRuleImplementation,
587
- })(NewRuleImplementation);
646
+ })(NewRuleImplementationLoader);
@@ -12,7 +12,7 @@ const DynamicFormViewer = React.lazy(() =>
12
12
  import("@truedat/df/components/DynamicFormViewer")
13
13
  );
14
14
 
15
- const summarySteps = ["dataset", "population", "validations"];
15
+ const summarySteps = ["dataset", "populations", "validations"];
16
16
 
17
17
  export const RuleImplementationProperties = ({
18
18
  ruleImplementation,
@@ -74,6 +74,7 @@ const mapStateToProps = ({
74
74
  }) => ({
75
75
  ruleImplementation: _.pick([
76
76
  ...summarySteps,
77
+ "populations",
77
78
  "executable",
78
79
  "event_type",
79
80
  "event_message",
@@ -130,56 +130,58 @@ export const newRuleImplementationProps = {
130
130
  id: 852,
131
131
  implementation_key: "impl_example_1",
132
132
  implementation_type: "default",
133
- population: [
134
- {
135
- operator: {
136
- name: "eq",
137
- value_type: "field",
138
- },
139
- structure: {
140
- external_id:
141
- "https://glue.eu-west-1.amazonaws.com/#/576759405678/cepsa-demo-s3/02_es_provincias/descripcion",
142
- id: 4817544,
143
- metadata: {
144
- account: "576759405678",
145
- data_type_class: "string",
146
- database: "cepsa-demo-s3",
147
- order: "2.0",
148
- region: "eu-west-1",
149
- tablename: "02_es_provincias",
150
- type: "string",
151
- },
152
- name: "descripcion",
153
- path: ["cepsa-demo-s3", "02_es_provincias"],
154
- system: {
155
- external_id: "glue",
156
- id: 94,
157
- name: "Glue",
133
+ populations: [
134
+ [
135
+ {
136
+ operator: {
137
+ name: "eq",
138
+ value_type: "field",
158
139
  },
159
- type: "Column",
160
- },
161
- value: [
162
- {
140
+ structure: {
163
141
  external_id:
164
- "/home/alberto/projects/connectors/td-connector-txt-files/data/########_D_PELAYO_POLIZAS_20201202033016_I.txt[Aux. Externo]",
165
- id: 4814767,
142
+ "https://glue.eu-west-1.amazonaws.com/#/576759405678/cepsa-demo-s3/02_es_provincias/descripcion",
143
+ id: 4817544,
166
144
  metadata: {
167
- order: "8",
145
+ account: "576759405678",
146
+ data_type_class: "string",
147
+ database: "cepsa-demo-s3",
148
+ order: "2.0",
149
+ region: "eu-west-1",
150
+ tablename: "02_es_provincias",
151
+ type: "string",
168
152
  },
169
- name: "Aux. Externo",
170
- path: [
171
- "Area_Vida",
172
- "########_D_PELAYO_POLIZAS_20201202033016_I.txt",
173
- ],
153
+ name: "descripcion",
154
+ path: ["cepsa-demo-s3", "02_es_provincias"],
174
155
  system: {
175
- external_id: "file_system",
176
- id: 33,
177
- name: "File System",
156
+ external_id: "glue",
157
+ id: 94,
158
+ name: "Glue",
178
159
  },
179
160
  type: "Column",
180
161
  },
181
- ],
182
- },
162
+ value: [
163
+ {
164
+ external_id:
165
+ "/home/alberto/projects/connectors/td-connector-txt-files/data/########_D_PELAYO_POLIZAS_20201202033016_I.txt[Aux. Externo]",
166
+ id: 4814767,
167
+ metadata: {
168
+ order: "8",
169
+ },
170
+ name: "Aux. Externo",
171
+ path: [
172
+ "Area_Vida",
173
+ "########_D_PELAYO_POLIZAS_20201202033016_I.txt",
174
+ ],
175
+ system: {
176
+ external_id: "file_system",
177
+ id: 33,
178
+ name: "File System",
179
+ },
180
+ type: "Column",
181
+ },
182
+ ],
183
+ },
184
+ ],
183
185
  ],
184
186
  results: [
185
187
  {
@@ -12,7 +12,7 @@ const renderOpts = {
12
12
  "quality.threshold": "threshold",
13
13
  "quality.goal": "goal",
14
14
  "ruleImplementations.props.result_type.percentage": "percentage",
15
-
15
+ "ruleImplementations.summary.headers.populations": "populations",
16
16
  "ruleImplementation.summary.field": "Field",
17
17
  "ruleImplementation.summary.operator": "Operator",
18
18
  "ruleImplementation.summary.structure": "Structure",
@@ -46,7 +46,7 @@ const ruleImplementation = {
46
46
  },
47
47
  },
48
48
  ],
49
- population: [{}],
49
+ populations: [[{}]],
50
50
  validations: [
51
51
  {
52
52
  structure: { field_type: "string", name: "Mes", id: 2598 },
@@ -67,7 +67,7 @@ describe("<ImplementationSummary />", () => {
67
67
  "implementationKey",
68
68
  "executable",
69
69
  "dataset",
70
- "population",
70
+ "populations",
71
71
  "validations",
72
72
  ];
73
73
  const props = {
@@ -79,7 +79,7 @@ describe("<ImplementationSummary />", () => {
79
79
  });
80
80
 
81
81
  it("displays correctly validations values of list type", () => {
82
- const steps = ["dataset", "population", "validations"];
82
+ const steps = ["dataset", "populations", "validations"];
83
83
  const props = {
84
84
  steps,
85
85
  ruleImplementation,
@@ -144,6 +144,67 @@ exports[`<ImplementationSummary /> displays correctly validations values of list
144
144
  </tbody>
145
145
  </table>
146
146
  </div>
147
+ <h3
148
+ class="ui header"
149
+ >
150
+ <i
151
+ aria-hidden="true"
152
+ class="user small icon"
153
+ />
154
+ <div
155
+ class="content"
156
+ >
157
+ populations
158
+ </div>
159
+ </h3>
160
+ <div
161
+ class="ui segment"
162
+ >
163
+ <table
164
+ class="ui very basic table"
165
+ >
166
+ <thead
167
+ class=""
168
+ >
169
+ <tr
170
+ class=""
171
+ >
172
+ <th
173
+ class=""
174
+ >
175
+ Field
176
+ </th>
177
+ <th
178
+ class=""
179
+ >
180
+ Operator
181
+ </th>
182
+ <th
183
+ class=""
184
+ >
185
+ Values
186
+ </th>
187
+ </tr>
188
+ </thead>
189
+ <tbody
190
+ class=""
191
+ >
192
+ <tr
193
+ class=""
194
+ >
195
+ <td
196
+ class="five wide"
197
+ />
198
+ <td
199
+ class="five wide"
200
+ />
201
+ <td
202
+ class="five wide"
203
+ />
204
+ </tr>
205
+ </tbody>
206
+ </table>
207
+ </div>
147
208
  <h3
148
209
  class="ui header"
149
210
  >
@@ -264,8 +325,10 @@ exports[`<ImplementationSummary /> matches the latest snapshot 1`] = `
264
325
  "implementationKey": "ImplKey",
265
326
  "implementationType": "",
266
327
  "minimum": 10,
267
- "population": Array [
268
- Object {},
328
+ "populations": Array [
329
+ Array [
330
+ Object {},
331
+ ],
269
332
  ],
270
333
  "rawContent": Object {
271
334
  "dataset": "",
@@ -340,7 +403,9 @@ exports[`<ImplementationSummary /> matches the latest snapshot 1`] = `
340
403
  icon="user"
341
404
  rows={
342
405
  Array [
343
- Object {},
406
+ Array [
407
+ Object {},
408
+ ],
344
409
  ]
345
410
  }
346
411
  type="population"