@truedat/dq 4.53.9 → 4.53.11
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 +7 -1
- package/package.json +6 -6
- package/src/api/queries.js +76 -16
- package/src/components/ConceptRules.js +1 -1
- package/src/components/ConditionSummary.js +1 -1
- package/src/components/ExecutionGroups.js +4 -50
- package/src/components/ImplementationExecutionFilters.js +79 -0
- package/src/components/ImplementationExecutions.js +125 -0
- package/src/components/ImplementationStructureDelete.js +2 -6
- package/src/components/ImplementationsRoutes.js +18 -1
- package/src/components/ImplementationsUploadButton.js +1 -1
- package/src/components/RuleImplementationHistory.js +5 -8
- package/src/components/RuleImplementationHistoryRow.js +23 -28
- package/src/components/RuleImplementationResultTabs.js +2 -2
- package/src/components/RuleImplementationResults.js +24 -149
- package/src/components/RuleImplementationSelectedFilters.js +2 -1
- package/src/components/RuleImplementationTabs.js +14 -8
- package/src/components/RuleImplementationsTable.js +1 -4
- package/src/components/RuleResultDecorator.js +10 -9
- package/src/components/RuleResultRow.js +9 -15
- package/src/components/RuleResultsRoutes.js +2 -2
- package/src/components/RuleResultsTable.js +117 -0
- package/src/components/RuleResultsUpload.js +2 -6
- package/src/components/RulesUploadButton.js +1 -1
- package/src/components/__tests__/ImplementationExecutionFilters.spec.js +24 -0
- package/src/components/__tests__/ImplementationExecutions.spec.js +32 -0
- package/src/components/__tests__/RuleImplementationResults.spec.js +16 -45
- package/src/components/__tests__/RuleImplementationTabs.spec.js +1 -18
- package/src/components/__tests__/RuleResultDecorator.spec.js +12 -32
- package/src/components/__tests__/RuleResultDetails.spec.js +3 -3
- package/src/components/__tests__/RuleResultRemediations.spec.js +3 -3
- package/src/components/__tests__/RuleResultRoutes.spec.js +5 -5
- package/src/components/__tests__/RuleResultRow.spec.js +86 -82
- package/src/components/__tests__/RuleResultSegmentRow.spec.js +6 -6
- package/src/components/__tests__/RuleResultSegments.spec.js +1 -1
- package/src/components/__tests__/__fixtures__/executionMocks.js +80 -0
- package/src/components/__tests__/__snapshots__/ExecutionGroups.spec.js.snap +0 -1
- package/src/components/__tests__/__snapshots__/ImplementationExecutionFilters.spec.js.snap +37 -0
- package/src/components/__tests__/__snapshots__/ImplementationExecutions.spec.js.snap +194 -0
- package/src/components/__tests__/__snapshots__/RuleImplementation.spec.js.snap +6 -0
- package/src/components/__tests__/__snapshots__/RuleImplementationHistory.spec.js.snap +12 -24
- package/src/components/__tests__/__snapshots__/RuleImplementationResults.spec.js.snap +8 -8
- package/src/components/__tests__/__snapshots__/RuleImplementationTabs.spec.js.snap +12 -6
- package/src/components/__tests__/__snapshots__/RuleResultDecorator.spec.js.snap +4 -4
- package/src/components/ruleImplementationForm/DateField.js +2 -2
- package/src/components/ruleImplementationForm/DateTimeField.js +2 -2
- package/src/components/ruleImplementationForm/FieldModifier.js +1 -1
- package/src/components/ruleImplementationForm/FiltersFormGroup.js +3 -3
- package/src/components/ruleImplementationForm/FiltersGroup.js +2 -2
- package/src/components/ruleImplementationForm/LimitsForm.js +6 -6
- package/src/components/ruleImplementationForm/ValueConditions.js +1 -1
- package/src/functions/selectors.js +12 -4
- package/src/messages/en.js +6 -0
- package/src/messages/es.js +6 -0
- package/src/selectors/getRuleImplementationColumns.js +4 -2
- package/src/selectors/index.js +1 -1
- package/src/selectors/{getRuleResultsColumns.js → ruleResultsColumnsSelector.js} +1 -1
|
@@ -21,7 +21,7 @@ export const RuleImplementationResultTabs = ({
|
|
|
21
21
|
templates,
|
|
22
22
|
}) => {
|
|
23
23
|
const renderSegmentResult = () => {
|
|
24
|
-
return ruleResult.
|
|
24
|
+
return ruleResult.hasSegments ? (
|
|
25
25
|
<Menu.Item
|
|
26
26
|
active={match.path === IMPLEMENTATION_RESULT_SEGMENTS_RESULTS}
|
|
27
27
|
as={Link}
|
|
@@ -37,7 +37,7 @@ export const RuleImplementationResultTabs = ({
|
|
|
37
37
|
|
|
38
38
|
const renderRemediationPlan = () => {
|
|
39
39
|
const canCreateRemediation = authCreateRemediation && !_.isEmpty(templates);
|
|
40
|
-
const renderCondition = ruleResult.
|
|
40
|
+
const renderCondition = ruleResult.hasRemediation || canCreateRemediation;
|
|
41
41
|
|
|
42
42
|
return renderCondition ? (
|
|
43
43
|
<Menu.Item
|
|
@@ -4,31 +4,18 @@ import { useParams } from "react-router-dom";
|
|
|
4
4
|
import { useQuery } from "@apollo/client";
|
|
5
5
|
import PropTypes from "prop-types";
|
|
6
6
|
import { useIntl } from "react-intl";
|
|
7
|
-
import {
|
|
8
|
-
import { Table, Message, Divider } from "semantic-ui-react";
|
|
7
|
+
import { Message, Divider } from "semantic-ui-react";
|
|
9
8
|
import { columnDecorator } from "@truedat/core/services";
|
|
10
|
-
import
|
|
11
|
-
import {
|
|
12
|
-
import
|
|
13
|
-
import { IMPLEMENTATION_RESULTS } from "../api/queries";
|
|
14
|
-
import RuleResultRow from "./RuleResultRow";
|
|
15
|
-
|
|
16
|
-
const optionalColumns = ["errors", "records", "details"];
|
|
17
|
-
const getOptionalColumnsWithData = (ruleResults) =>
|
|
18
|
-
_.filter((column) =>
|
|
19
|
-
_.any(_.flow(_.prop(column), _.negate(_.isNil)))(ruleResults)
|
|
20
|
-
)(optionalColumns);
|
|
9
|
+
import { DateTime, Loading } from "@truedat/core/components";
|
|
10
|
+
import { IMPLEMENTATION_RESULTS_QUERY } from "../api/queries";
|
|
11
|
+
import RuleResultsTable from "./RuleResultsTable";
|
|
21
12
|
|
|
22
13
|
export const getCustomColumnsWithData = (ruleResults, columns) =>
|
|
23
14
|
_.filter((column) =>
|
|
24
15
|
_.any(_.flow(columnDecorator(column), _.negate(_.isEmpty)))(ruleResults)
|
|
25
16
|
)(columns);
|
|
26
17
|
|
|
27
|
-
export const RuleImplementationResults = ({
|
|
28
|
-
ruleImplementation,
|
|
29
|
-
ruleResultsColumns,
|
|
30
|
-
isAdmin,
|
|
31
|
-
}) => {
|
|
18
|
+
export const RuleImplementationResults = ({ ruleImplementation }) => {
|
|
32
19
|
const { formatMessage, locale } = useIntl();
|
|
33
20
|
|
|
34
21
|
if (_.isEmpty(ruleImplementation)) return null;
|
|
@@ -39,164 +26,52 @@ export const RuleImplementationResults = ({
|
|
|
39
26
|
_.first
|
|
40
27
|
)(implementationVersions);
|
|
41
28
|
|
|
42
|
-
const
|
|
43
|
-
ruleImplementation,
|
|
44
|
-
isAdmin,
|
|
45
|
-
ruleResultsColumns,
|
|
46
|
-
formatMessage,
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const { last_quality_event } = ruleImplementation;
|
|
29
|
+
const { lastQualityEvent } = ruleImplementation;
|
|
50
30
|
|
|
51
31
|
return (
|
|
52
32
|
<>
|
|
53
|
-
{
|
|
33
|
+
{lastQualityEvent?.type === "FAILED" ? (
|
|
54
34
|
<Message negative style={{ marginTop: "14px" }}>
|
|
55
|
-
<Message.Header
|
|
56
|
-
{formatMessage({
|
|
57
|
-
id: "quality.error",
|
|
58
|
-
})}
|
|
59
|
-
</Message.Header>
|
|
35
|
+
<Message.Header content={formatMessage({ id: "quality.error" })} />
|
|
60
36
|
<Divider />
|
|
61
|
-
<
|
|
37
|
+
<span
|
|
62
38
|
style={{
|
|
63
39
|
position: "absolute",
|
|
64
40
|
top: "14px",
|
|
65
41
|
right: "21px",
|
|
66
42
|
}}
|
|
67
|
-
locale={locale}
|
|
68
|
-
date={last_quality_event.inserted_at}
|
|
69
|
-
format="YYYY-MM-DD HH:mm"
|
|
70
|
-
/>
|
|
71
|
-
<p
|
|
72
|
-
style={{
|
|
73
|
-
whiteSpace: "pre-wrap",
|
|
74
|
-
}}
|
|
75
43
|
>
|
|
76
|
-
{
|
|
77
|
-
</
|
|
44
|
+
<DateTime value={lastQualityEvent.insertedAt} locale={locale} />
|
|
45
|
+
</span>
|
|
46
|
+
<p style={{ whiteSpace: "pre-wrap" }}>{lastQualityEvent.message}</p>
|
|
78
47
|
</Message>
|
|
79
48
|
) : null}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
implementationVersions
|
|
83
|
-
|
|
84
|
-
})}
|
|
49
|
+
<RuleResultsTable
|
|
50
|
+
currentImplementationVersion={currentImplementationVersion}
|
|
51
|
+
implementationVersions={implementationVersions}
|
|
52
|
+
/>
|
|
85
53
|
</>
|
|
86
54
|
);
|
|
87
55
|
};
|
|
88
56
|
|
|
89
|
-
export const resultsTable = ({
|
|
90
|
-
commonProps,
|
|
91
|
-
implementationVersions,
|
|
92
|
-
currentImplementationVersion,
|
|
93
|
-
}) => {
|
|
94
|
-
const { isAdmin, ruleResultsColumns, formatMessage } = commonProps;
|
|
95
|
-
|
|
96
|
-
const ruleResults = _.flow(
|
|
97
|
-
_.map((iv) => _.pathOr([], "results")(iv)),
|
|
98
|
-
_.flatten
|
|
99
|
-
)(implementationVersions);
|
|
100
|
-
const optionalColumns = getOptionalColumnsWithData(ruleResults);
|
|
101
|
-
const customColumns = getCustomColumnsWithData(
|
|
102
|
-
ruleResults,
|
|
103
|
-
ruleResultsColumns
|
|
104
|
-
);
|
|
105
|
-
|
|
106
|
-
return _.isEmpty(ruleResults) ? (
|
|
107
|
-
<Message
|
|
108
|
-
style={{ marginTop: "14px" }}
|
|
109
|
-
header={formatMessage({
|
|
110
|
-
id: "rule.ruleImplementation.results.empty",
|
|
111
|
-
})}
|
|
112
|
-
/>
|
|
113
|
-
) : (
|
|
114
|
-
<Table className="implementation-results small">
|
|
115
|
-
<Table.Header>
|
|
116
|
-
<Table.Row>
|
|
117
|
-
<Table.HeaderCell
|
|
118
|
-
content={formatMessage({ id: "ruleResult.props.date" })}
|
|
119
|
-
/>
|
|
120
|
-
<Table.HeaderCell
|
|
121
|
-
content={formatMessage({ id: "ruleResult.props.quality" })}
|
|
122
|
-
/>
|
|
123
|
-
{_.includes("records")(optionalColumns) ? (
|
|
124
|
-
<Table.HeaderCell
|
|
125
|
-
content={formatMessage({ id: "ruleResult.props.records" })}
|
|
126
|
-
/>
|
|
127
|
-
) : null}
|
|
128
|
-
{_.includes("errors")(optionalColumns) ? (
|
|
129
|
-
<Table.HeaderCell
|
|
130
|
-
content={formatMessage({ id: "ruleResult.props.errors" })}
|
|
131
|
-
/>
|
|
132
|
-
) : null}
|
|
133
|
-
{customColumns.map((column, index) => (
|
|
134
|
-
<Table.HeaderCell
|
|
135
|
-
key={index}
|
|
136
|
-
content={formatMessage({
|
|
137
|
-
id: `ruleResult.props.${column.name}`,
|
|
138
|
-
defaultMessage: column.name,
|
|
139
|
-
})}
|
|
140
|
-
/>
|
|
141
|
-
))}
|
|
142
|
-
{<Table.HeaderCell />}
|
|
143
|
-
{isAdmin ? <Table.HeaderCell /> : null}
|
|
144
|
-
</Table.Row>
|
|
145
|
-
</Table.Header>
|
|
146
|
-
<Table.Body>
|
|
147
|
-
{implementationVersions.map((version, i) =>
|
|
148
|
-
_.pathOr(
|
|
149
|
-
[],
|
|
150
|
-
"results"
|
|
151
|
-
)(version).map((result, j) => (
|
|
152
|
-
<RuleResultRow
|
|
153
|
-
key={"version" + i + "_result" + j}
|
|
154
|
-
optionalColumns={optionalColumns}
|
|
155
|
-
ruleResult={result}
|
|
156
|
-
customColumns={customColumns}
|
|
157
|
-
isAdmin={isAdmin}
|
|
158
|
-
ruleImplementation={version}
|
|
159
|
-
active={currentImplementationVersion.id == version.id}
|
|
160
|
-
tagLabel={
|
|
161
|
-
j === 0
|
|
162
|
-
? `${formatMessage({
|
|
163
|
-
id: "ruleImplementations.props.version",
|
|
164
|
-
})}: ${version.version}`
|
|
165
|
-
: null
|
|
166
|
-
}
|
|
167
|
-
/>
|
|
168
|
-
))
|
|
169
|
-
)}
|
|
170
|
-
</Table.Body>
|
|
171
|
-
</Table>
|
|
172
|
-
);
|
|
173
|
-
};
|
|
174
|
-
|
|
175
57
|
RuleImplementationResults.propTypes = {
|
|
176
58
|
ruleImplementation: PropTypes.object,
|
|
177
|
-
isAdmin: PropTypes.bool,
|
|
178
|
-
ruleResultsColumns: PropTypes.array,
|
|
179
59
|
};
|
|
180
60
|
|
|
181
|
-
const mapStateToProps = (state) => ({ state: state });
|
|
182
|
-
|
|
183
61
|
export const RuleImplementationResultsLoader = (props) => {
|
|
184
|
-
const { state } = props;
|
|
185
62
|
const { implementation_id: id } = useParams();
|
|
186
|
-
const { loading, error, data } = useQuery(
|
|
63
|
+
const { loading, error, data } = useQuery(IMPLEMENTATION_RESULTS_QUERY, {
|
|
187
64
|
variables: { id },
|
|
188
65
|
});
|
|
189
66
|
if (error) return null;
|
|
190
67
|
if (loading) return <Loading />;
|
|
191
68
|
const ruleImplementation = data?.implementation;
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
69
|
+
return (
|
|
70
|
+
<RuleImplementationResults
|
|
71
|
+
ruleImplementation={ruleImplementation}
|
|
72
|
+
{...props}
|
|
73
|
+
/>
|
|
74
|
+
);
|
|
198
75
|
};
|
|
199
76
|
|
|
200
|
-
export default
|
|
201
|
-
RuleImplementationResultsLoader
|
|
202
|
-
);
|
|
77
|
+
export default RuleImplementationResultsLoader;
|
|
@@ -24,7 +24,8 @@ import {
|
|
|
24
24
|
} from "../selectors";
|
|
25
25
|
|
|
26
26
|
const translations = (formatMessage) => ({
|
|
27
|
-
"execution_result_info.result_text": (v) =>
|
|
27
|
+
"execution_result_info.result_text": (v) =>
|
|
28
|
+
formatMessage({ id: v, defaultMessage: v }),
|
|
28
29
|
});
|
|
29
30
|
|
|
30
31
|
export const mapStateToProps = (state, ownProps) => {
|
|
@@ -2,14 +2,14 @@ import _ from "lodash/fp";
|
|
|
2
2
|
import React from "react";
|
|
3
3
|
import PropTypes from "prop-types";
|
|
4
4
|
import { Menu } from "semantic-ui-react";
|
|
5
|
-
import { Link,
|
|
6
|
-
import { compose } from "redux";
|
|
5
|
+
import { Link, useRouteMatch } from "react-router-dom";
|
|
7
6
|
import { connect } from "react-redux";
|
|
8
7
|
import { FormattedMessage } from "react-intl";
|
|
9
8
|
import {
|
|
10
9
|
IMPLEMENTATION_CONCEPT_LINKS_NEW,
|
|
11
10
|
IMPLEMENTATION_CONCEPT_LINKS,
|
|
12
11
|
IMPLEMENTATION_EVENTS,
|
|
12
|
+
IMPLEMENTATION_EXECUTIONS,
|
|
13
13
|
IMPLEMENTATION_HISTORY,
|
|
14
14
|
IMPLEMENTATION_MOVE,
|
|
15
15
|
IMPLEMENTATION_RESULTS_DETAILS,
|
|
@@ -23,9 +23,9 @@ import {
|
|
|
23
23
|
export const RuleImplementationTabs = ({
|
|
24
24
|
rule,
|
|
25
25
|
ruleImplementation,
|
|
26
|
-
match,
|
|
27
26
|
canCreateLink,
|
|
28
27
|
}) => {
|
|
28
|
+
const match = useRouteMatch();
|
|
29
29
|
const latest_rule_id = _.flow(
|
|
30
30
|
_.pathOr([], "results"),
|
|
31
31
|
_.head,
|
|
@@ -83,7 +83,16 @@ export const RuleImplementationTabs = ({
|
|
|
83
83
|
>
|
|
84
84
|
<FormattedMessage id="tabs.dq.ruleImplementation.results" />
|
|
85
85
|
</Menu.Item>
|
|
86
|
-
|
|
86
|
+
<Menu.Item
|
|
87
|
+
active={match.path === IMPLEMENTATION_EXECUTIONS}
|
|
88
|
+
as={Link}
|
|
89
|
+
to={linkTo.IMPLEMENTATION_EXECUTIONS({
|
|
90
|
+
implementation_id: ruleImplementation.id,
|
|
91
|
+
})}
|
|
92
|
+
>
|
|
93
|
+
<FormattedMessage id="tabs.dq.ruleImplementation.executions" />
|
|
94
|
+
</Menu.Item>
|
|
95
|
+
{_.isNil(latest_rule_id) ? null : (
|
|
87
96
|
<Menu.Item
|
|
88
97
|
active={match.path === IMPLEMENTATION_RESULTS_DETAILS}
|
|
89
98
|
as={Link}
|
|
@@ -135,7 +144,4 @@ const mapStateToProps = ({
|
|
|
135
144
|
canCreateLink: _.propOr(false, "link_concept")(implementationActions),
|
|
136
145
|
});
|
|
137
146
|
|
|
138
|
-
export default
|
|
139
|
-
withRouter,
|
|
140
|
-
connect(mapStateToProps)
|
|
141
|
-
)(RuleImplementationTabs);
|
|
147
|
+
export default connect(mapStateToProps)(RuleImplementationTabs);
|
|
@@ -128,10 +128,7 @@ RuleImplementationsTable.propTypes = {
|
|
|
128
128
|
|
|
129
129
|
const mapStateToProps = (state, props) => ({
|
|
130
130
|
columns: getRuleImplementationColumns(state),
|
|
131
|
-
ruleImplementations:
|
|
132
|
-
props.ruleImplementations ||
|
|
133
|
-
state.structure?.implementations?.map(_.prop("implementation")) ||
|
|
134
|
-
state.ruleImplementations,
|
|
131
|
+
ruleImplementations: props.ruleImplementations || state.ruleImplementations,
|
|
135
132
|
ruleImplementationsLoading: state.ruleImplementationsLoading,
|
|
136
133
|
implementationsSort: _.path("ruleImplementationQuery.sort")(state),
|
|
137
134
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { has, isNil } from "lodash/fp";
|
|
1
|
+
import { defaultTo, has, isNil } from "lodash/fp";
|
|
2
2
|
import React from "react";
|
|
3
3
|
import PropTypes from "prop-types";
|
|
4
4
|
import { Icon } from "semantic-ui-react";
|
|
@@ -28,25 +28,26 @@ export const calculateResultDecoration = ({
|
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
const resultType = ruleImplementation
|
|
31
|
+
const { resultType, result_type } = ruleImplementation;
|
|
32
|
+
const type = defaultTo(result_type)(resultType);
|
|
32
33
|
const result =
|
|
33
|
-
|
|
34
|
+
type === "errors_number"
|
|
34
35
|
? formatNumber(ruleResult?.errors)
|
|
35
36
|
: ruleResult?.result;
|
|
36
37
|
const resultText = isNil(result)
|
|
37
38
|
? formatMessage({ id: "quality.result.no.data" })
|
|
38
39
|
: date
|
|
39
40
|
? formatMessage(
|
|
40
|
-
{ id: `quality.result.${
|
|
41
|
+
{ id: `quality.result.${type}.date` },
|
|
41
42
|
{ result, date: <DateTime value={date} /> }
|
|
42
43
|
)
|
|
43
|
-
: formatMessage(
|
|
44
|
-
{ id: `quality.result.${resultType}.description` },
|
|
45
|
-
{ result }
|
|
46
|
-
);
|
|
44
|
+
: formatMessage({ id: `quality.result.${type}.description` }, { result });
|
|
47
45
|
const color = selectColor({ ...ruleImplementation, ...ruleResult });
|
|
48
46
|
const iconTitle = ruleResult?.result_text
|
|
49
|
-
? formatMessage({
|
|
47
|
+
? formatMessage({
|
|
48
|
+
id: ruleResult?.result_text,
|
|
49
|
+
defaultMessage: ruleResult?.result_text,
|
|
50
|
+
})
|
|
50
51
|
: null;
|
|
51
52
|
|
|
52
53
|
return { iconName: "circle", color, resultText, iconTitle };
|
|
@@ -3,10 +3,10 @@ import React from "react";
|
|
|
3
3
|
import PropTypes from "prop-types";
|
|
4
4
|
import { connect } from "react-redux";
|
|
5
5
|
import { useIntl } from "react-intl";
|
|
6
|
-
import { useHistory } from "react-router-dom";
|
|
7
6
|
import { Link } from "react-router-dom";
|
|
8
7
|
import { Table, Icon, Label } from "semantic-ui-react";
|
|
9
8
|
import { ConfirmModal, DateTime } from "@truedat/core/components";
|
|
9
|
+
import { useAuthorized } from "@truedat/core/hooks";
|
|
10
10
|
import { columnDecorator } from "@truedat/core/services";
|
|
11
11
|
import { linkTo } from "@truedat/core/routes";
|
|
12
12
|
import { selectColor } from "../functions/selectors";
|
|
@@ -19,30 +19,25 @@ export const RuleResultRow = ({
|
|
|
19
19
|
optionalColumns,
|
|
20
20
|
ruleResult,
|
|
21
21
|
customColumns,
|
|
22
|
-
isAdmin,
|
|
23
22
|
ruleImplementation,
|
|
24
23
|
tagLabel,
|
|
25
24
|
}) => {
|
|
25
|
+
const isAdmin = useAuthorized();
|
|
26
26
|
const { formatMessage, formatNumber: _formatNumber } = useIntl();
|
|
27
27
|
const formatNumber = (num) => (_.isNil(num) ? num : _formatNumber(num));
|
|
28
|
-
const history = useHistory();
|
|
29
28
|
|
|
30
29
|
return (
|
|
31
30
|
<>
|
|
32
31
|
{tagLabel ? (
|
|
33
32
|
<Table.Row>
|
|
34
|
-
<Table.Cell
|
|
33
|
+
<Table.Cell>
|
|
35
34
|
<Label
|
|
36
|
-
as=
|
|
35
|
+
as={Link}
|
|
36
|
+
to={linkTo.IMPLEMENTATION({
|
|
37
|
+
implementation_id: ruleImplementation.id,
|
|
38
|
+
})}
|
|
37
39
|
active={active}
|
|
38
40
|
ribbon
|
|
39
|
-
onClick={() =>
|
|
40
|
-
history.push(
|
|
41
|
-
linkTo.IMPLEMENTATION({
|
|
42
|
-
implementation_id: ruleImplementation.id,
|
|
43
|
-
})
|
|
44
|
-
)
|
|
45
|
-
}
|
|
46
41
|
>
|
|
47
42
|
{tagLabel}
|
|
48
43
|
</Label>
|
|
@@ -59,7 +54,7 @@ export const RuleResultRow = ({
|
|
|
59
54
|
})}
|
|
60
55
|
>
|
|
61
56
|
<DateTime className="rule-result-date" value={ruleResult.date} />
|
|
62
|
-
{ruleResult.
|
|
57
|
+
{ruleResult.hasSegments ? (
|
|
63
58
|
<Icon
|
|
64
59
|
circular
|
|
65
60
|
inverted
|
|
@@ -69,7 +64,7 @@ export const RuleResultRow = ({
|
|
|
69
64
|
/>
|
|
70
65
|
) : null}
|
|
71
66
|
|
|
72
|
-
{ruleResult.
|
|
67
|
+
{ruleResult.hasRemediation ? (
|
|
73
68
|
<Icon circular inverted name="rain" color="blue" size="small" />
|
|
74
69
|
) : null}
|
|
75
70
|
</Link>
|
|
@@ -121,7 +116,6 @@ RuleResultRow.propTypes = {
|
|
|
121
116
|
active: PropTypes.bool,
|
|
122
117
|
customColumns: PropTypes.array,
|
|
123
118
|
deleteRuleResult: PropTypes.func,
|
|
124
|
-
isAdmin: PropTypes.bool,
|
|
125
119
|
optionalColumns: PropTypes.array,
|
|
126
120
|
ruleImplementation: PropTypes.object,
|
|
127
121
|
ruleResult: PropTypes.object,
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
IMPLEMENTATION_RESULT_SEGMENTS_RESULTS,
|
|
9
9
|
IMPLEMENTATION_RESULT_REMEDIATION_PLAN,
|
|
10
10
|
} from "@truedat/core/routes";
|
|
11
|
-
import {
|
|
11
|
+
import { IMPLEMENTATION_RESULT_QUERY } from "../api/queries";
|
|
12
12
|
import RuleResult from "./RuleResult";
|
|
13
13
|
import RuleResultDetails from "./RuleResultDetails";
|
|
14
14
|
import RuleResultSegments from "./RuleResultSegments";
|
|
@@ -18,7 +18,7 @@ import RuleResultSegmentsLoader from "./RuleResultSegmentsLoader";
|
|
|
18
18
|
|
|
19
19
|
export const RuleResultsRoutes = ({}) => {
|
|
20
20
|
const { rule_result_id } = useParams();
|
|
21
|
-
const { loading, error, data } = useQuery(
|
|
21
|
+
const { loading, error, data } = useQuery(IMPLEMENTATION_RESULT_QUERY, {
|
|
22
22
|
variables: { id: rule_result_id },
|
|
23
23
|
});
|
|
24
24
|
if (error) return null;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import PropTypes from "prop-types";
|
|
4
|
+
import { useIntl } from "react-intl";
|
|
5
|
+
import { connect } from "react-redux";
|
|
6
|
+
import { Table, Message } from "semantic-ui-react";
|
|
7
|
+
import { useAuthorized } from "@truedat/core/hooks";
|
|
8
|
+
import { columnDecorator } from "@truedat/core/services";
|
|
9
|
+
import { ruleResultsColumnsSelector } from "../selectors";
|
|
10
|
+
import RuleResultRow from "./RuleResultRow";
|
|
11
|
+
|
|
12
|
+
const optionalColumns = ["errors", "records", "details"];
|
|
13
|
+
|
|
14
|
+
const getOptionalColumnsWithData = (ruleResults) =>
|
|
15
|
+
_.filter((column) =>
|
|
16
|
+
_.any(_.flow(_.prop(column), _.negate(_.isNil)))(ruleResults)
|
|
17
|
+
)(optionalColumns);
|
|
18
|
+
|
|
19
|
+
export const getCustomColumnsWithData = (ruleResults, columns) =>
|
|
20
|
+
_.filter((column) =>
|
|
21
|
+
_.any(_.flow(columnDecorator(column), _.negate(_.isEmpty)))(ruleResults)
|
|
22
|
+
)(columns);
|
|
23
|
+
|
|
24
|
+
export const RuleResultsTable = ({
|
|
25
|
+
columns,
|
|
26
|
+
implementationVersions,
|
|
27
|
+
currentImplementationVersion,
|
|
28
|
+
}) => {
|
|
29
|
+
const isAdmin = useAuthorized();
|
|
30
|
+
const { formatMessage } = useIntl();
|
|
31
|
+
|
|
32
|
+
const ruleResults = _.flow(
|
|
33
|
+
_.map((iv) => _.pathOr([], "results")(iv)),
|
|
34
|
+
_.flatten
|
|
35
|
+
)(implementationVersions);
|
|
36
|
+
const optionalColumns = getOptionalColumnsWithData(ruleResults);
|
|
37
|
+
const customColumns = getCustomColumnsWithData(ruleResults, columns);
|
|
38
|
+
|
|
39
|
+
return _.isEmpty(ruleResults) ? (
|
|
40
|
+
<Message
|
|
41
|
+
style={{ marginTop: "14px" }}
|
|
42
|
+
header={formatMessage({
|
|
43
|
+
id: "rule.ruleImplementation.results.empty",
|
|
44
|
+
})}
|
|
45
|
+
/>
|
|
46
|
+
) : (
|
|
47
|
+
<Table className="implementation-results small">
|
|
48
|
+
<Table.Header>
|
|
49
|
+
<Table.Row>
|
|
50
|
+
<Table.HeaderCell
|
|
51
|
+
content={formatMessage({ id: "ruleResult.props.date" })}
|
|
52
|
+
/>
|
|
53
|
+
<Table.HeaderCell
|
|
54
|
+
content={formatMessage({ id: "ruleResult.props.quality" })}
|
|
55
|
+
/>
|
|
56
|
+
{_.includes("records")(optionalColumns) ? (
|
|
57
|
+
<Table.HeaderCell
|
|
58
|
+
content={formatMessage({ id: "ruleResult.props.records" })}
|
|
59
|
+
/>
|
|
60
|
+
) : null}
|
|
61
|
+
{_.includes("errors")(optionalColumns) ? (
|
|
62
|
+
<Table.HeaderCell
|
|
63
|
+
content={formatMessage({ id: "ruleResult.props.errors" })}
|
|
64
|
+
/>
|
|
65
|
+
) : null}
|
|
66
|
+
{customColumns.map((column, index) => (
|
|
67
|
+
<Table.HeaderCell
|
|
68
|
+
key={index}
|
|
69
|
+
content={formatMessage({
|
|
70
|
+
id: `ruleResult.props.${column.name}`,
|
|
71
|
+
defaultMessage: column.name,
|
|
72
|
+
})}
|
|
73
|
+
/>
|
|
74
|
+
))}
|
|
75
|
+
{<Table.HeaderCell />}
|
|
76
|
+
{isAdmin ? <Table.HeaderCell /> : null}
|
|
77
|
+
</Table.Row>
|
|
78
|
+
</Table.Header>
|
|
79
|
+
<Table.Body>
|
|
80
|
+
{implementationVersions.map((version) =>
|
|
81
|
+
_.pathOr(
|
|
82
|
+
[],
|
|
83
|
+
"results"
|
|
84
|
+
)(version).map((result, key) => (
|
|
85
|
+
<RuleResultRow
|
|
86
|
+
key={key}
|
|
87
|
+
optionalColumns={optionalColumns}
|
|
88
|
+
ruleResult={result}
|
|
89
|
+
customColumns={customColumns}
|
|
90
|
+
ruleImplementation={version}
|
|
91
|
+
active={currentImplementationVersion.id === version.id}
|
|
92
|
+
tagLabel={
|
|
93
|
+
key === 0
|
|
94
|
+
? `${formatMessage({
|
|
95
|
+
id: "ruleImplementations.props.version",
|
|
96
|
+
})}: ${version.version}`
|
|
97
|
+
: null
|
|
98
|
+
}
|
|
99
|
+
/>
|
|
100
|
+
))
|
|
101
|
+
)}
|
|
102
|
+
</Table.Body>
|
|
103
|
+
</Table>
|
|
104
|
+
);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
RuleResultsTable.propTypes = {
|
|
108
|
+
columns: PropTypes.array,
|
|
109
|
+
implementationVersions: PropTypes.array,
|
|
110
|
+
currentImplementationVersion: PropTypes.object,
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const mapStateToProps = (state) => ({
|
|
114
|
+
columns: ruleResultsColumnsSelector(state),
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
export default connect(mapStateToProps)(RuleResultsTable);
|
|
@@ -25,12 +25,8 @@ export const RuleResultsUpload = ({ uploadResults, loading }) => {
|
|
|
25
25
|
content={formatMessage({
|
|
26
26
|
id: "uploadModal.actions.upload.confirmation.content",
|
|
27
27
|
})}
|
|
28
|
-
param=
|
|
29
|
-
handleSubmit={(data) =>
|
|
30
|
-
uploadResults({
|
|
31
|
-
data,
|
|
32
|
-
})
|
|
33
|
-
}
|
|
28
|
+
param="rule_results"
|
|
29
|
+
handleSubmit={(data) => uploadResults({ data })}
|
|
34
30
|
/>
|
|
35
31
|
);
|
|
36
32
|
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render } from "@truedat/test/render";
|
|
3
|
+
import ImplementationExecutionFilters from "../ImplementationExecutionFilters";
|
|
4
|
+
import { implementationExecutionFiltersMock } from "./__fixtures__/executionMocks";
|
|
5
|
+
|
|
6
|
+
jest.mock("react-router-dom", () => ({
|
|
7
|
+
...jest.requireActual("react-router-dom"),
|
|
8
|
+
useParams: () => ({ implementation_id: "123" }),
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
const renderOpts = {
|
|
12
|
+
mocks: [implementationExecutionFiltersMock({ id: "123" })],
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
describe("<ImplementationExecutionFilters />", () => {
|
|
16
|
+
it("matches snapshot with filter links", async () => {
|
|
17
|
+
const { container, findAllByRole } = render(
|
|
18
|
+
<ImplementationExecutionFilters />,
|
|
19
|
+
renderOpts
|
|
20
|
+
);
|
|
21
|
+
expect(await findAllByRole("link")).toHaveLength(4);
|
|
22
|
+
expect(container).toMatchSnapshot();
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render } from "@truedat/test/render";
|
|
3
|
+
import ImplementationExecutions from "../ImplementationExecutions";
|
|
4
|
+
import {
|
|
5
|
+
implementationExecutionFiltersMock,
|
|
6
|
+
implementationExecutionsMock,
|
|
7
|
+
} from "./__fixtures__/executionMocks";
|
|
8
|
+
|
|
9
|
+
jest.mock("react-router-dom", () => ({
|
|
10
|
+
...jest.requireActual("react-router-dom"),
|
|
11
|
+
useParams: () => ({ implementation_id: "123" }),
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
const renderOpts = {
|
|
15
|
+
mocks: [
|
|
16
|
+
implementationExecutionFiltersMock({ id: "123" }),
|
|
17
|
+
implementationExecutionsMock({ id: "123", last: 20, filters: [] }),
|
|
18
|
+
],
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
describe("<ImplementationExecutions />", () => {
|
|
22
|
+
it("matches snapshot with executions table", async () => {
|
|
23
|
+
const { container, findByRole, getByRole, getAllByRole } = render(
|
|
24
|
+
<ImplementationExecutions />,
|
|
25
|
+
renderOpts
|
|
26
|
+
);
|
|
27
|
+
expect(await findByRole("table")).toBeInTheDocument();
|
|
28
|
+
expect(container).toMatchSnapshot();
|
|
29
|
+
expect(getAllByRole("row")).toHaveLength(3);
|
|
30
|
+
expect(getByRole("cell", { name: /succeeded/ })).toBeInTheDocument();
|
|
31
|
+
});
|
|
32
|
+
});
|