@truedat/dq 6.16.2 → 6.16.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/dq",
3
- "version": "6.16.2",
3
+ "version": "6.16.4",
4
4
  "description": "Truedat Web Data Quality Module",
5
5
  "sideEffects": false,
6
6
  "jsnext:main": "src/index.js",
@@ -93,7 +93,7 @@
93
93
  "dependencies": {
94
94
  "@apollo/client": "^3.7.1",
95
95
  "@truedat/core": "6.15.2",
96
- "@truedat/df": "6.16.1",
96
+ "@truedat/df": "6.15.2",
97
97
  "decode-uri-component": "^0.2.2",
98
98
  "graphql": "^15.5.3",
99
99
  "moment": "^2.29.4",
@@ -118,5 +118,5 @@
118
118
  "react-dom": ">= 16.8.6 < 17",
119
119
  "semantic-ui-react": ">= 2.0.3 < 2.2"
120
120
  },
121
- "gitHead": "be00925ba0aa8943442f7460ee4a97e021f2db31"
121
+ "gitHead": "ecf57a30185b9e353b6e2692702b5599e4425edb"
122
122
  }
@@ -3,28 +3,57 @@ import React from "react";
3
3
  import PropTypes from "prop-types";
4
4
  import { FormattedMessage, useIntl } from "react-intl";
5
5
  import { Link } from "react-router-dom";
6
- import { Table, Button, Message, Segment } from "semantic-ui-react";
6
+ import { Table, Button, Message, Segment, TableCell } from "semantic-ui-react";
7
7
  import { connect } from "react-redux";
8
8
  import { linkTo } from "@truedat/core/routes";
9
9
 
10
- const ConceptRuleRow = ({ active, name, id }) => (
11
- <Table.Row>
12
- <Table.Cell>
13
- <Link to={linkTo.RULE({ id })}>{name}</Link>
14
- </Table.Cell>
15
- <Table.Cell>
16
- <FormattedMessage id={`rule.props.active.${active}`} />
17
- </Table.Cell>
18
- </Table.Row>
19
- );
10
+ const ConceptRuleRow = ({
11
+ rule: { active, name, id, business_concept_id, business_concept_name },
12
+ showExpandableColumn,
13
+ }) => {
14
+ return (
15
+ <Table.Row>
16
+ <Table.Cell>
17
+ <Link to={linkTo.RULE({ id })}>{name}</Link>
18
+ </Table.Cell>
19
+ <Table.Cell>
20
+ <FormattedMessage id={`rule.props.active.${active}`} />
21
+ </Table.Cell>
22
+ {showExpandableColumn ? (
23
+ <TableCell>
24
+ {business_concept_id ? (
25
+ <Link
26
+ to={linkTo.CONCEPT_VERSION({
27
+ business_concept_id: business_concept_id,
28
+ id: "current",
29
+ })}
30
+ >
31
+ {business_concept_name}
32
+ </Link>
33
+ ) : null}
34
+ </TableCell>
35
+ ) : null}
36
+ </Table.Row>
37
+ );
38
+ };
20
39
 
21
40
  ConceptRuleRow.propTypes = {
22
- name: PropTypes.string,
23
- active: PropTypes.bool,
24
- id: PropTypes.number,
41
+ rule: PropTypes.shape({
42
+ name: PropTypes.string,
43
+ active: PropTypes.bool,
44
+ id: PropTypes.number,
45
+ business_concept_id: PropTypes.number,
46
+ business_concept_name: PropTypes.string,
47
+ }),
48
+ showExpandableColumn: PropTypes.bool,
25
49
  };
26
50
 
27
- export const ConceptRules = ({ conceptRules, visible, createRuleUrl }) => {
51
+ export const ConceptRules = ({
52
+ conceptRules,
53
+ visible,
54
+ createRuleUrl,
55
+ showExpandableColumn,
56
+ }) => {
28
57
  const { formatMessage } = useIntl();
29
58
 
30
59
  return (
@@ -56,12 +85,23 @@ export const ConceptRules = ({ conceptRules, visible, createRuleUrl }) => {
56
85
  <Table.HeaderCell
57
86
  content={formatMessage({ id: "concepts.rules.status" })}
58
87
  />
88
+ {showExpandableColumn ? (
89
+ <Table.HeaderCell
90
+ content={formatMessage({
91
+ id: "concepts.rules.expandable",
92
+ })}
93
+ />
94
+ ) : null}
59
95
  </Table.Row>
60
96
  </Table.Header>
61
97
  <Table.Body>
62
- {_.map((rule) => <ConceptRuleRow key={rule.id} {...rule} />)(
63
- conceptRules
64
- )}
98
+ {_.map((rule) => (
99
+ <ConceptRuleRow
100
+ key={rule.id}
101
+ rule={rule}
102
+ showExpandableColumn={showExpandableColumn}
103
+ />
104
+ ))(conceptRules)}
65
105
  </Table.Body>
66
106
  </Table>
67
107
  )}
@@ -73,11 +113,16 @@ ConceptRules.propTypes = {
73
113
  conceptRules: PropTypes.array,
74
114
  createRuleUrl: PropTypes.string,
75
115
  visible: PropTypes.bool,
116
+ showExpandableColumn: PropTypes.bool,
76
117
  };
77
118
 
78
119
  const mapStateToProps = ({ conceptRules, concept, conceptRulesActions }) => ({
79
120
  concept,
80
- conceptRules: _.reject("deleted_at")(conceptRules),
121
+ conceptRules: _.flow(
122
+ _.reject("deleted_at"),
123
+ _.orderBy(["business_concept_id", "df_name"], ["desc", "asc"])
124
+ )(conceptRules),
125
+ showExpandableColumn: _.some("business_concept_name")(conceptRules),
81
126
  createRuleUrl: conceptRulesActions?.create
82
127
  ? linkTo.CONCEPT_RULES_NEW(concept)
83
128
  : null,
@@ -25,7 +25,7 @@ export const ImplementationStructureLinksActions = ({
25
25
 
26
26
  ImplementationStructureLinksActions.propTypes = {
27
27
  implementation_id: PropTypes.number,
28
- canCreateLink: PropTypes.bool,
28
+ canCreateLink: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]),
29
29
  };
30
30
 
31
31
  const mapStateToProps = (state) => ({
@@ -6,7 +6,10 @@ import { getSortInfo, sortColumn } from "@truedat/core/services/sort";
6
6
  import { Checkbox, Table, Header, Icon } from "semantic-ui-react";
7
7
  import { useIntl } from "react-intl";
8
8
  import { sortImplementations } from "../routines";
9
- import { getRuleImplementationColumns } from "../selectors";
9
+ import {
10
+ getBusinessConceptsLinksToImplementationsColumns,
11
+ getRuleImplementationColumns,
12
+ } from "../selectors";
10
13
  import RuleImplementationRow from "./RuleImplementationRow";
11
14
 
12
15
  export const RuleImplementationsTable = ({
@@ -23,9 +26,9 @@ export const RuleImplementationsTable = ({
23
26
  withoutColumns,
24
27
  }) => {
25
28
  const { formatMessage } = useIntl();
26
- const effectiveColumns = _.filter(({ name }) =>
27
- _.negate(_.includes(name))(withoutColumns)
28
- )(columns);
29
+ const effectiveColumns = _.filter(({ name }) => {
30
+ return _.negate(_.includes(name))(withoutColumns);
31
+ })(columns);
29
32
  const [sortedColumn, setColumn] = useState(
30
33
  _.prop("column")(getSortInfo(implementationsSort))
31
34
  );
@@ -127,8 +130,14 @@ RuleImplementationsTable.propTypes = {
127
130
  };
128
131
 
129
132
  const mapStateToProps = (state, props) => ({
130
- columns: getRuleImplementationColumns(state),
131
- ruleImplementations: props.ruleImplementations || state.ruleImplementations,
133
+ columns: _.some("business_concept_id")(
134
+ props.ruleImplementations || state.ruleImplementations
135
+ )
136
+ ? getBusinessConceptsLinksToImplementationsColumns(state)
137
+ : getRuleImplementationColumns(state),
138
+ ruleImplementations: _.orderBy(["business_concept_id", "implementation_key"])(
139
+ ["desc", "asc"]
140
+ )(props.ruleImplementations || state.ruleImplementations),
132
141
  ruleImplementationsLoading: state.ruleImplementationsLoading,
133
142
  implementationsSort: _.path("ruleImplementationQuery.sort")(state),
134
143
  });
@@ -9,6 +9,8 @@ describe("<ConceptRules />", () => {
9
9
  active: true,
10
10
  name: "foo",
11
11
  id: 1,
12
+ business_concept_id: 1,
13
+ business_concept_name: "foo foo",
12
14
  },
13
15
  {
14
16
  active: true,
@@ -34,6 +36,8 @@ describe("<ConceptRules />", () => {
34
36
  "concepts.rules.empty": "empty",
35
37
  "concepts.rules.name": "name",
36
38
  "concepts.rules.status": "status",
39
+ "concepts.rules.expandable": "Linked by",
40
+ "concepts.rule.create": "Create",
37
41
  },
38
42
  },
39
43
  };
@@ -59,4 +63,25 @@ describe("<ConceptRules />", () => {
59
63
  expect(container).toMatchSnapshot();
60
64
  expect(queryByText(/empty/)).toBeTruthy();
61
65
  });
66
+
67
+ it("renders expandable", async () => {
68
+ const newProps = {
69
+ ...props,
70
+ showExpandableColumn: true,
71
+ };
72
+ const { queryByText } = render(<ConceptRules {...newProps} />, renderOpts);
73
+
74
+ expect(queryByText(/Linked by/)).toBeTruthy();
75
+ });
76
+
77
+ it("renders create url", async () => {
78
+ const newProps = {
79
+ ...props,
80
+ createRuleUrl: "/url/create",
81
+ visible: true,
82
+ };
83
+ const { queryByText } = render(<ConceptRules {...newProps} />, renderOpts);
84
+
85
+ expect(queryByText(/Create/)).toBeTruthy();
86
+ });
62
87
  });
@@ -0,0 +1,23 @@
1
+ import {
2
+ getBusinessConceptsLinksToImplementationsColumns,
3
+ defaultBusinessConceptsLinksToImplementationsColumns,
4
+ } from "..";
5
+
6
+ describe("selectors: getBusinessConceptsLinksToImplementationsColumns", () => {
7
+ it("should return custom businessConceptsLinksToImplementationsColumns when present", () => {
8
+ const businessConceptsLinksToImplementationsColumns = [{ name: "test" }];
9
+ const res = getBusinessConceptsLinksToImplementationsColumns({
10
+ businessConceptsLinksToImplementationsColumns,
11
+ });
12
+ expect(res).toHaveLength(1);
13
+ expect(res).toEqual(businessConceptsLinksToImplementationsColumns);
14
+ });
15
+
16
+ it("should return default getBusinessConceptsLinksToImplementationsColumns when no customized", () => {
17
+ const res = getBusinessConceptsLinksToImplementationsColumns({});
18
+
19
+ expect(res).toHaveLength(
20
+ defaultBusinessConceptsLinksToImplementationsColumns.length
21
+ );
22
+ });
23
+ });
@@ -0,0 +1,116 @@
1
+ import _ from "lodash/fp";
2
+ import React from "react";
3
+ import { createSelector } from "reselect";
4
+ import { FormattedMessage } from "react-intl";
5
+ import DateTime from "@truedat/core/components/DateTime";
6
+ import { formatNumber } from "@truedat/core/services/format";
7
+ import ArrayDecorator from "@truedat/core/components/ArrayDecorator";
8
+ import { linkTo } from "@truedat/core/routes";
9
+ import { Link } from "react-router-dom";
10
+ import RuleResultDecorator from "../components/RuleResultDecorator";
11
+ import RuleImplementationLink from "../components/RuleImplementationLink";
12
+ import RuleLink from "../components/RuleLink";
13
+
14
+ const translateDecorator = (id) =>
15
+ id ? <FormattedMessage id={id} defaultMessage={id} /> : null;
16
+
17
+ const resultTypeDecorator = (result, result_type, resultType) =>
18
+ _.defaultTo(result_type)(resultType) === "errors_number"
19
+ ? formatNumber(result)
20
+ : `${result}%`;
21
+
22
+ export const defaultBusinessConceptsLinksToImplementationsColumns = [
23
+ {
24
+ name: "implementation_key",
25
+ fieldSelector: _.pick(["id", "implementation_key", "rule_id"]),
26
+ fieldDecorator: RuleImplementationLink,
27
+ width: 2,
28
+ },
29
+ {
30
+ name: "rule",
31
+ fieldSelector: ({ rule, rule_id: id }) => ({ id, name: rule?.name }),
32
+ fieldDecorator: RuleLink,
33
+ width: 2,
34
+ },
35
+ {
36
+ name: "status",
37
+ fieldSelector: ({ status }) =>
38
+ translateDecorator(`ruleImplementation.status.${status}`),
39
+ width: 2,
40
+ },
41
+ {
42
+ name: "last_execution_at",
43
+ fieldSelector: ({ execution_result_info }) => ({
44
+ value: execution_result_info?.date,
45
+ }),
46
+ fieldDecorator: DateTime,
47
+ width: 2,
48
+ },
49
+ {
50
+ name: "result_type",
51
+ fieldDecorator: (value) =>
52
+ _.isNil(value)
53
+ ? null
54
+ : translateDecorator(`ruleImplementations.props.result_type.${value}`),
55
+ width: 2,
56
+ },
57
+ {
58
+ name: "minimum",
59
+ fieldSelector: _.pick(["minimum", "result_type"]),
60
+ fieldDecorator: (field) =>
61
+ resultTypeDecorator(field.minimum, field.result_type),
62
+ textAlign: "right",
63
+ width: 1,
64
+ },
65
+ {
66
+ name: "goal",
67
+ fieldSelector: _.pick(["goal", "result_type"]),
68
+ fieldDecorator: (field) =>
69
+ resultTypeDecorator(field.goal, field.result_type),
70
+ textAlign: "right",
71
+ width: 1,
72
+ },
73
+ {
74
+ name: "result",
75
+ fieldSelector: (ruleImplementation) => ({
76
+ ruleResult: ruleImplementation?.execution_result_info,
77
+ ruleImplementation,
78
+ }),
79
+ fieldDecorator: RuleResultDecorator,
80
+ textAlign: "center",
81
+ width: 2,
82
+ },
83
+ {
84
+ name: "inserted_at",
85
+ fieldSelector: ({ inserted_at }) => ({ value: inserted_at }),
86
+ fieldDecorator: DateTime,
87
+ },
88
+ {
89
+ name: "updated_at",
90
+ fieldSelector: ({ updated_at }) => ({ value: updated_at }),
91
+ fieldDecorator: DateTime,
92
+ },
93
+ {
94
+ name: "related_by",
95
+ fieldSelector: _.pick(["business_concept_id", "business_concept_name"]),
96
+ fieldDecorator: ({ business_concept_id, business_concept_name }) => {
97
+ return _.isEmpty(business_concept_id) ? (
98
+ <></>
99
+ ) : (
100
+ <Link
101
+ to={linkTo.CONCEPT_VERSION({
102
+ business_concept_id: business_concept_id,
103
+ id: "current",
104
+ })}
105
+ >
106
+ {business_concept_name}
107
+ </Link>
108
+ );
109
+ },
110
+ },
111
+ ];
112
+
113
+ export const getBusinessConceptsLinksToImplementationsColumns = createSelector(
114
+ _.prop("businessConceptsLinksToImplementationsColumns"),
115
+ _.defaultTo(defaultBusinessConceptsLinksToImplementationsColumns)
116
+ );
@@ -27,6 +27,10 @@ export {
27
27
  getRuleImplementationColumns,
28
28
  defaultImplementationColumns,
29
29
  } from "./getRuleImplementationColumns";
30
+ export {
31
+ getBusinessConceptsLinksToImplementationsColumns,
32
+ defaultBusinessConceptsLinksToImplementationsColumns,
33
+ } from "./getBusinessConceptsLinksToImplementationsColumns";
30
34
  export { getPreviousRuleImplementationQuery } from "./getPreviousRuleImplementationQuery";
31
35
  export { getRuleImplementationAvailableFilters } from "./getRuleImplementationAvailableFilters";
32
36
  export { getRuleImplementationFilterTypes } from "./getRuleImplementationFilterTypes";