@truedat/dq 4.36.2 → 4.36.6

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.36.4] 2022-01-19
4
+
5
+ ### Added
6
+
7
+ - [TD-4293] Add rule implementations `events`
8
+
3
9
  ## [4.36.2] 2022-01-17
4
10
 
5
11
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/dq",
3
- "version": "4.36.2",
3
+ "version": "4.36.6",
4
4
  "description": "Truedat Web Data Quality Module",
5
5
  "sideEffects": false,
6
6
  "jsnext:main": "src/index.js",
@@ -82,8 +82,8 @@
82
82
  },
83
83
  "dependencies": {
84
84
  "@apollo/client": "^3.4.10",
85
- "@truedat/core": "4.36.2",
86
- "@truedat/df": "4.36.2",
85
+ "@truedat/core": "4.36.6",
86
+ "@truedat/df": "4.36.6",
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": "0f4c09d90582210ae7c98e945b8c0b7af0f9a61d"
106
+ "gitHead": "24162cf652f85f30de8669736abc28391fd5f6a8"
107
107
  }
@@ -0,0 +1,48 @@
1
+ import _ from "lodash/fp";
2
+ import React from "react";
3
+ import PropTypes from "prop-types";
4
+ import { Feed } from "semantic-ui-react";
5
+ import { useIntl } from "react-intl";
6
+ import Moment from "react-moment";
7
+
8
+ export const RuleImplementationEventRow = ({
9
+ user,
10
+ user_name,
11
+ ts,
12
+ payload,
13
+ }) => {
14
+ const { formatMessage, locale } = useIntl();
15
+ const userName = _.propOr(user_name, "user_name")(user);
16
+
17
+ return _.isEmpty(payload) ? null : (
18
+ <Feed.Event>
19
+ <Feed.Content>
20
+ <Feed.Summary>
21
+ <Feed.User>{userName}</Feed.User>{" "}
22
+ <Feed.Date>
23
+ <Moment locale={locale} date={ts} format="YYYY-MM-DD HH:mm" />
24
+ </Feed.Date>
25
+ </Feed.Summary>
26
+ {payload.map(({ field, action, value }, i) => (
27
+ <Feed.Extra
28
+ key={i}
29
+ text
30
+ content={formatMessage(
31
+ { id: "ruleImplementations.events.action_" + action },
32
+ [field, value]
33
+ )}
34
+ />
35
+ ))}
36
+ </Feed.Content>
37
+ </Feed.Event>
38
+ );
39
+ };
40
+
41
+ RuleImplementationEventRow.propTypes = {
42
+ user: PropTypes.object,
43
+ user_name: PropTypes.string,
44
+ ts: PropTypes.string,
45
+ payload: PropTypes.array,
46
+ };
47
+
48
+ export default RuleImplementationEventRow;
@@ -0,0 +1,29 @@
1
+ import React from "react";
2
+ import PropTypes from "prop-types";
3
+ import { Feed, Segment } from "semantic-ui-react";
4
+ import { connect } from "react-redux";
5
+ import { getParsedEvents } from "../selectors";
6
+ import { RuleImplementationEventRow } from "./RuleImplementationEventRow";
7
+
8
+ export const RuleImplementationEvents = ({ events, eventsLoading }) => {
9
+ return eventsLoading ? null : (
10
+ <Segment attached="bottom">
11
+ <Feed size="small">
12
+ {events.map((e, i) => (
13
+ <RuleImplementationEventRow key={i} {...e} />
14
+ ))}
15
+ </Feed>
16
+ </Segment>
17
+ );
18
+ };
19
+ RuleImplementationEvents.propTypes = {
20
+ events: PropTypes.array.isRequired,
21
+ eventsLoading: PropTypes.bool,
22
+ };
23
+
24
+ const mapStateToProps = (state) => ({
25
+ events: getParsedEvents(state),
26
+ eventsLoading: state.eventsLoading,
27
+ });
28
+
29
+ export default connect(mapStateToProps)(RuleImplementationEvents);
@@ -7,6 +7,7 @@ import { compose } from "redux";
7
7
  import { connect } from "react-redux";
8
8
  import { FormattedMessage } from "react-intl";
9
9
  import {
10
+ RULE_IMPLEMENTATION_EVENTS,
10
11
  RULE_IMPLEMENTATION_RESULTS_DETAILS,
11
12
  RULE_IMPLEMENTATION_MOVE,
12
13
  RULE_IMPLEMENTATION_RESULTS,
@@ -59,6 +60,16 @@ export const RuleImplementationTabs = ({ rule, ruleImplementation, match }) => {
59
60
  <FormattedMessage id="tabs.dq.ruleImplementation.details" />
60
61
  </Menu.Item>
61
62
  )}
63
+ <Menu.Item
64
+ active={match.path === RULE_IMPLEMENTATION_EVENTS}
65
+ as={Link}
66
+ to={linkTo.RULE_IMPLEMENTATION_EVENTS({
67
+ id: rule.id,
68
+ implementation_id: ruleImplementation.id,
69
+ })}
70
+ >
71
+ <FormattedMessage id="tabs.dq.ruleImplementation.audit" />
72
+ </Menu.Item>
62
73
  </Menu>
63
74
  );
64
75
  };
@@ -1,7 +1,7 @@
1
1
  import _ from "lodash/fp";
2
2
  import React from "react";
3
3
  import PropTypes from "prop-types";
4
- import { Route, Switch } from "react-router-dom";
4
+ import { Route, Switch, useRouteMatch } from "react-router-dom";
5
5
  import { connect } from "react-redux";
6
6
  import { Segment } from "semantic-ui-react";
7
7
  import { Unauthorized } from "@truedat/core/components";
@@ -12,6 +12,7 @@ import {
12
12
  RULE_IMPLEMENTATION_RESULT_DETAILS,
13
13
  RULE_IMPLEMENTATION_RESULTS_DETAILS,
14
14
  RULE_IMPLEMENTATION,
15
+ RULE_IMPLEMENTATION_EVENTS,
15
16
  RULE_IMPLEMENTATION_NEW,
16
17
  RULE_IMPLEMENTATION_CLONE,
17
18
  RULE_IMPLEMENTATION_EDIT,
@@ -32,6 +33,7 @@ import NewRuleImplementation from "./NewRuleImplementation";
32
33
  import Rule from "./Rule";
33
34
  import RuleCrumbs from "./RuleCrumbs";
34
35
  import RuleImplementation from "./RuleImplementation";
36
+ import RuleImplementationEvents from "./RuleImplementationEvents";
35
37
  import RuleImplementationLoader from "./RuleImplementationLoader";
36
38
  import RuleImplementationProperties from "./RuleImplementationProperties";
37
39
  import RuleImplementationResults from "./RuleImplementationResults";
@@ -47,6 +49,9 @@ const DomainsLoader = React.lazy(() =>
47
49
  const DynamicFormViewer = React.lazy(() =>
48
50
  import("@truedat/df/components/DynamicFormViewer")
49
51
  );
52
+ const EventsLoader = React.lazy(() =>
53
+ import("@truedat/audit/components/EventsLoader")
54
+ );
50
55
  const TemplatesLoader = React.lazy(() =>
51
56
  import("@truedat/df/templates/components/TemplatesLoader")
52
57
  );
@@ -56,6 +61,12 @@ const ImplementationStructuresLoader = React.lazy(() =>
56
61
  const QualityTemplatesLoader = () => <TemplatesLoader scope="dq" />;
57
62
  const ImplementationTemplatesLoader = () => <TemplatesLoader scope="ri" />;
58
63
 
64
+ const ImplementationEventsLoader = () => {
65
+ const match = useRouteMatch();
66
+ const id = _.path("params.implementation_id")(match);
67
+ return <EventsLoader resource_id={id} resource_type="implementation" />;
68
+ };
69
+
59
70
  const RuleRoutes = ({
60
71
  ruleLoaded,
61
72
  ruleImplementationLoaded,
@@ -132,6 +143,7 @@ const RuleRoutes = ({
132
143
  <RuleCrumbs />
133
144
  <Segment>
134
145
  <RuleSubscriptionLoader />
146
+ <RuleImplementationLoader />
135
147
  <RuleImplementationsFromRuleLoader />
136
148
  {ruleLoaded && <Rule />}
137
149
  {ruleLoaded && <RuleFormImplementations />}
@@ -139,6 +151,25 @@ const RuleRoutes = ({
139
151
  </>
140
152
  )}
141
153
  />
154
+ <Route
155
+ exact
156
+ path={RULE_IMPLEMENTATION_EVENTS}
157
+ render={() => (
158
+ <>
159
+ <RuleCrumbs />
160
+ <Segment>
161
+ <ImplementationEventsLoader />
162
+ <RuleImplementationLoader />
163
+ {ruleLoaded && ruleImplementationLoaded && (
164
+ <RuleImplementation>
165
+ <RuleImplementationEvents />
166
+ </RuleImplementation>
167
+ )}
168
+ </Segment>
169
+ </>
170
+ )}
171
+ />
172
+
142
173
  <Route
143
174
  exact
144
175
  path={RULE_IMPLEMENTATION_NEW}
@@ -271,7 +302,6 @@ const RuleRoutes = ({
271
302
  <>
272
303
  <Segment>
273
304
  <RuleImplementationLoader />
274
-
275
305
  {ruleLoaded && ruleImplementationLoaded && (
276
306
  <>
277
307
  <RuleCrumbs />
@@ -21,6 +21,7 @@ const renderOpts = {
21
21
  en: {
22
22
  "tabs.dq.ruleImplementation.results": "results",
23
23
  "tabs.dq.ruleImplementation": "implementation",
24
+ "tabs.dq.ruleImplementation.audit": "events",
24
25
  "ruleImplementation.actions.clone": "clone",
25
26
  "ruleImplementation.actions.move": "move",
26
27
  "ruleImplementation.actions.edit": "edit",
@@ -0,0 +1,43 @@
1
+ import React from "react";
2
+ import { render } from "@truedat/test/render";
3
+ import { RuleImplementationEvents } from "../RuleImplementationEvents";
4
+
5
+ describe("<RuleImplementationEvents />", () => {
6
+ const events = [
7
+ {
8
+ id: 1,
9
+ service: "service",
10
+ event: "implementation_restored",
11
+ resource_id: 100,
12
+ ts: "2019-08-12T02:00:00Z",
13
+ payload: [{ field: "foo", action: "restored" }],
14
+ },
15
+ {
16
+ id: 2,
17
+ service: "service",
18
+ event: "implementation_deprecated",
19
+ resource_id: 100,
20
+ ts: "2019-09-12T02:00:00Z",
21
+ payload: [{ field: "bar", action: "deprecated" }],
22
+ },
23
+ ];
24
+ const renderOpts = {
25
+ messages: {
26
+ en: {
27
+ "ruleImplementations.events.field_restored": "implementation restored",
28
+ "ruleImplementations.events.field_deprecated":
29
+ "implementation deprecated",
30
+ },
31
+ },
32
+ };
33
+
34
+ const props = { events };
35
+
36
+ it("matches the latest snapshot", () => {
37
+ const { container } = render(
38
+ <RuleImplementationEvents {...props} />,
39
+ renderOpts
40
+ );
41
+ expect(container).toMatchSnapshot();
42
+ });
43
+ });
@@ -140,6 +140,12 @@ exports[`<RuleImplementation /> matches the latest snapshot 1`] = `
140
140
  >
141
141
  results
142
142
  </a>
143
+ <a
144
+ class="item"
145
+ href="/rules/1/implementations/1/events"
146
+ >
147
+ events
148
+ </a>
143
149
  </div>
144
150
  </div>
145
151
  `;
@@ -0,0 +1,74 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<RuleImplementationEvents /> matches the latest snapshot 1`] = `
4
+ <div>
5
+ <div
6
+ class="ui bottom attached segment"
7
+ >
8
+ <div
9
+ class="ui small feed"
10
+ >
11
+ <div
12
+ class="event"
13
+ >
14
+ <div
15
+ class="content"
16
+ >
17
+ <div
18
+ class="summary"
19
+ >
20
+ <a
21
+ class="user"
22
+ />
23
+
24
+ <div
25
+ class="date"
26
+ >
27
+ <time
28
+ datetime="1565575200000"
29
+ >
30
+ 2019-08-12 02:00
31
+ </time>
32
+ </div>
33
+ </div>
34
+ <div
35
+ class="text extra"
36
+ >
37
+ ruleImplementations.events.action_restored
38
+ </div>
39
+ </div>
40
+ </div>
41
+ <div
42
+ class="event"
43
+ >
44
+ <div
45
+ class="content"
46
+ >
47
+ <div
48
+ class="summary"
49
+ >
50
+ <a
51
+ class="user"
52
+ />
53
+
54
+ <div
55
+ class="date"
56
+ >
57
+ <time
58
+ datetime="1568253600000"
59
+ >
60
+ 2019-09-12 02:00
61
+ </time>
62
+ </div>
63
+ </div>
64
+ <div
65
+ class="text extra"
66
+ >
67
+ ruleImplementations.events.action_deprecated
68
+ </div>
69
+ </div>
70
+ </div>
71
+ </div>
72
+ </div>
73
+ </div>
74
+ `;
@@ -12,6 +12,7 @@ import QualityRoutes from "./QualityRoutes";
12
12
  import Rule from "./Rule";
13
13
  import RuleForm from "./RuleForm";
14
14
  import RuleFormImplementations from "./RuleFormImplementations";
15
+ import RuleImplementationEvents from "./RuleImplementationEvents";
15
16
  import RuleImplementationsFromRuleLoader from "./RuleImplementationsFromRuleLoader";
16
17
  import RuleImplementationsLoader from "./RuleImplementationsLoader";
17
18
  import RuleLoader from "./RuleLoader";
@@ -35,6 +36,7 @@ export {
35
36
  Rule,
36
37
  RuleForm,
37
38
  RuleFormImplementations,
39
+ RuleImplementationEvents,
38
40
  RuleImplementationsLoader,
39
41
  RuleImplementationsFromRuleLoader,
40
42
  RuleLoader,
@@ -5,6 +5,7 @@ export default {
5
5
  "actions.prev": "Previous",
6
6
  "actions.save": "Save",
7
7
  "actions.submit": "Submit",
8
+ "alert.deleteRuleImplementation.failed.header": "Error deprecating implementation",
8
9
  "alert.createExecutionGroup.success.content":
9
10
  "{count} implementations queued for execution",
10
11
  "alert.createExecutionGroup.success.header": "Executions scheduled",
@@ -464,6 +465,12 @@ export default {
464
465
  "ruleImplementations.actions.upload.tooltip": "Upload Implementations",
465
466
  "ruleImplementations.actions.upload.confirmation.header":
466
467
  "Confirm bulk upload",
468
+ "ruleImplementations.events.action_created": "Implementation created on rule: {0}",
469
+ "ruleImplementations.events.action_changed": "Field {0} changed to {1}",
470
+ "ruleImplementations.events.action_deprecated": "Implementation deprecated",
471
+ "ruleImplementations.events.action_moved": "Implementation moved to rule: {0}",
472
+ "ruleImplementations.events.action_restored": "Implementation restored",
473
+ "ruleImplementations.events.action_updated": "Implementation updated",
467
474
  "ruleImplementations.header": "Quality Implementations",
468
475
  "ruleImplementations.props.business_concept": "Concept",
469
476
  "ruleImplementations.props.status": "Status",
@@ -549,6 +556,7 @@ export default {
549
556
  "summary.link.and": "and",
550
557
  "tabs.dq.rule": "Rule",
551
558
  "tabs.dq.ruleImplementation": "Implementation",
559
+ "tabs.dq.ruleImplementation.audit": "Audit",
552
560
  "tabs.dq.ruleImplementation.details": "Details",
553
561
  "tabs.dq.ruleImplementation.results": "Results",
554
562
  "tabs.dq.ruleImplementations": "Implementations",
@@ -5,6 +5,7 @@ export default {
5
5
  "actions.prev": "Anterior",
6
6
  "actions.save": "Guardar",
7
7
  "actions.submit": "Guardar",
8
+ "alert.deleteRuleImplementation.failed.header": "Error al archivar implementación",
8
9
  "alert.createExecutionGroup.success.content":
9
10
  "Se ha solicitado la ejecución de {count} implementaciones",
10
11
  "alert.createExecutionGroup.success.header": "Ejecuciones programadas",
@@ -476,6 +477,12 @@ export default {
476
477
  "ruleImplementations.actions.create": "Crear nueva implementación",
477
478
  "ruleImplementations.actions.edit": "Editar implementación",
478
479
  "ruleImplementations.actions.popup.deprecated": "Implementaciones Archivadas",
480
+ "ruleImplementations.events.action_created": "Implementación creada en regla: {0}",
481
+ "ruleImplementations.events.action_changed": "Campo {0} actualizado a {1}",
482
+ "ruleImplementations.events.action_deprecated": "Implementación archivada",
483
+ "ruleImplementations.events.action_moved": "Implementación movida a la regla: {0}",
484
+ "ruleImplementations.events.action_restored": "Implementación restaurada",
485
+ "ruleImplementations.events.action_updated": "Implementación actualizada",
479
486
  "ruleImplementations.header": "Implementaciones de calidad",
480
487
  "ruleImplementations.props.business_concept": "Concepto",
481
488
  "ruleImplementations.props.executable.false": "Interna",
@@ -564,6 +571,7 @@ export default {
564
571
  "structureFields.dropdown.placeholder": "Campos",
565
572
  "summary.link.and": "y",
566
573
  "tabs.dq.rule": "Regla",
574
+ "tabs.dq.ruleImplementation.audit": "Auditoría",
567
575
  "tabs.dq.ruleImplementation.details": "Detalles",
568
576
  "tabs.dq.ruleImplementation.results": "Resultados",
569
577
  "tabs.dq.ruleImplementation": "Implementación",
@@ -0,0 +1,86 @@
1
+ import _ from "lodash/fp";
2
+ import { createSelector } from "reselect";
3
+
4
+ const getEvents = ({ events }) => {
5
+ return events;
6
+ };
7
+
8
+ const transformFieldFormat = (field) =>
9
+ _.isBoolean(field) ? _.toString(field) : field;
10
+
11
+ const getParsedEvents = createSelector([getEvents], (events) => {
12
+ return _.map((d) => {
13
+ switch (d.event) {
14
+ case "implementation_created":
15
+ return {
16
+ ...d,
17
+ payload: [
18
+ {
19
+ field: _.path("payload.rule_name")(d),
20
+ action: "created",
21
+ },
22
+ ],
23
+ };
24
+ case "implementation_moved":
25
+ return {
26
+ ...d,
27
+ payload: [
28
+ {
29
+ field: _.path("payload.rule_name")(d),
30
+ action: "moved",
31
+ },
32
+ ],
33
+ };
34
+ case "implementation_restored":
35
+ return {
36
+ ...d,
37
+ payload: [
38
+ {
39
+ field: _.path("payload.implementation_key")(d),
40
+ action: "restored",
41
+ },
42
+ ],
43
+ };
44
+ case "implementation_deleted":
45
+ return {};
46
+ case "implementation_deprecated":
47
+ return {
48
+ ...d,
49
+ payload: [
50
+ {
51
+ field: _.path("payload.implementation_key")(d),
52
+ action: "deprecated",
53
+ },
54
+ ],
55
+ };
56
+ case "implementation_changed":
57
+ return {
58
+ ...d,
59
+ payload: _.flow(
60
+ _.path("payload.df_content.changed"),
61
+ _.keys,
62
+ _.map((e) => ({
63
+ field: e,
64
+ action: "changed",
65
+ value: transformFieldFormat(
66
+ _.path(`payload.df_content.changed.${e}`)(d)
67
+ ),
68
+ }))
69
+ )(d),
70
+ };
71
+
72
+ default:
73
+ return {
74
+ ...d,
75
+ payload: [
76
+ {
77
+ field: _.path("payload.implementation_key")(d),
78
+ action: "updated",
79
+ },
80
+ ],
81
+ };
82
+ }
83
+ })(events);
84
+ });
85
+
86
+ export { getParsedEvents };
@@ -28,3 +28,4 @@ export { getRuleImplementationSelectedFilters } from "./getRuleImplementationSel
28
28
  export { getRuleImplementationSelectedFilterValues } from "./getRuleImplementationSelectedFilterValues";
29
29
  export { getRuleImplementationSelectedFilterActiveValues } from "./getRuleImplementationSelectedFilterActiveValues";
30
30
  export * from "./getImplementationStructures";
31
+ export { getParsedEvents } from "./getParsedEvents";