@truedat/dq 4.50.2 → 4.51.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 +12 -0
- package/package.json +4 -4
- package/src/api/queries.js +33 -0
- package/src/components/ExecutionGroup.js +28 -22
- package/src/components/ExecutionGroupBreadcrumbs.js +25 -0
- package/src/components/ExecutionGroupContent.js +42 -0
- package/src/components/ExecutionGroupLink.js +18 -0
- package/src/components/ExecutionGroupLoader.js +1 -2
- package/src/components/ExecutionGroupMessage.js +1 -1
- package/src/components/ExecutionGroups.js +80 -0
- package/src/components/ExecutionGroupsTable.js +61 -0
- package/src/components/QualityRoutes.js +20 -12
- package/src/components/__tests__/ExecutionGroup.spec.js +44 -47
- package/src/components/__tests__/ExecutionGroupBreadcrumbs.spec.js +11 -0
- package/src/components/__tests__/ExecutionGroupContent.spec.js +30 -0
- package/src/components/__tests__/ExecutionGroupLink.spec.js +11 -0
- package/src/components/__tests__/ExecutionGroups.spec.js +51 -0
- package/src/components/__tests__/ExecutionGroupsTable.spec.js +30 -0
- package/src/components/__tests__/__snapshots__/ExecutionGroup.spec.js.snap +136 -100
- package/src/components/__tests__/__snapshots__/ExecutionGroupBreadcrumbs.spec.js.snap +29 -0
- package/src/components/__tests__/__snapshots__/ExecutionGroupContent.spec.js.snap +37 -0
- package/src/components/__tests__/__snapshots__/ExecutionGroupLink.spec.js.snap +15 -0
- package/src/components/__tests__/__snapshots__/ExecutionGroups.spec.js.snap +155 -0
- package/src/components/__tests__/__snapshots__/ExecutionGroupsTable.spec.js.snap +84 -0
- package/src/components/ruleImplementationForm/InformationForm.js +1 -0
- package/src/components/ruleImplementationForm/RuleImplementationRawForm.js +1 -0
- package/src/messages/en.js +106 -96
- package/src/messages/es.js +178 -168
- package/src/reducers/executionGroup.js +7 -1
- package/src/reducers/executionGroupLoading.js +9 -1
- package/src/selectors/__tests__/executionGroupsColumnsSelector.spec.js +19 -0
- package/src/selectors/executionGroupsColumnsSelector.js +41 -0
- package/src/selectors/index.js +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [4.51.0] 2022-09-13
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- [TD-3415] View with current user's execution groups
|
|
8
|
+
|
|
9
|
+
## [4.50.4] 2022-08-31
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- [TD-5091] Add labels attribute for DomainSelector component
|
|
14
|
+
|
|
3
15
|
## [4.50.0] 2022-08-18
|
|
4
16
|
|
|
5
17
|
## Changed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@truedat/dq",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.51.0",
|
|
4
4
|
"description": "Truedat Web Data Quality Module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"jsnext:main": "src/index.js",
|
|
@@ -93,8 +93,8 @@
|
|
|
93
93
|
},
|
|
94
94
|
"dependencies": {
|
|
95
95
|
"@apollo/client": "^3.6.4",
|
|
96
|
-
"@truedat/core": "4.
|
|
97
|
-
"@truedat/df": "4.
|
|
96
|
+
"@truedat/core": "4.51.0",
|
|
97
|
+
"@truedat/df": "4.51.0",
|
|
98
98
|
"graphql": "^15.5.3",
|
|
99
99
|
"path-to-regexp": "^1.7.0",
|
|
100
100
|
"prop-types": "^15.8.1",
|
|
@@ -114,5 +114,5 @@
|
|
|
114
114
|
"react-dom": ">= 16.8.6 < 17",
|
|
115
115
|
"semantic-ui-react": ">= 0.88.2 < 2.1"
|
|
116
116
|
},
|
|
117
|
-
"gitHead": "
|
|
117
|
+
"gitHead": "a3d9290433e773d9c605d9a0d9fa51908c5becad"
|
|
118
118
|
}
|
package/src/api/queries.js
CHANGED
|
@@ -65,3 +65,36 @@ export const IMPLEMENTATION_RESULT = gql`
|
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
`;
|
|
68
|
+
|
|
69
|
+
export const MY_EXECUTION_GROUPS_QUERY = gql`
|
|
70
|
+
query MyExecutionGroups(
|
|
71
|
+
$after: Cursor
|
|
72
|
+
$before: Cursor
|
|
73
|
+
$last: Int
|
|
74
|
+
$first: Int
|
|
75
|
+
) {
|
|
76
|
+
me {
|
|
77
|
+
id
|
|
78
|
+
executionGroupsConnection(
|
|
79
|
+
first: $first
|
|
80
|
+
last: $last
|
|
81
|
+
before: $before
|
|
82
|
+
after: $after
|
|
83
|
+
) {
|
|
84
|
+
totalCount
|
|
85
|
+
page {
|
|
86
|
+
id
|
|
87
|
+
dfContent
|
|
88
|
+
insertedAt
|
|
89
|
+
statusCounts
|
|
90
|
+
}
|
|
91
|
+
pageInfo {
|
|
92
|
+
startCursor
|
|
93
|
+
endCursor
|
|
94
|
+
hasNextPage
|
|
95
|
+
hasPreviousPage
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
`;
|
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
import
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
2
|
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
6
|
import { Table } from "semantic-ui-react";
|
|
7
|
+
import { DateTime } from "@truedat/core/components";
|
|
7
8
|
import { columnDecorator } from "@truedat/core/services";
|
|
8
|
-
import DateTime from "@truedat/core/components/DateTime";
|
|
9
9
|
import ExecutionGroupMessage from "./ExecutionGroupMessage";
|
|
10
10
|
import RuleResultDecorator from "./RuleResultDecorator";
|
|
11
11
|
import RuleImplementationResultsLink from "./RuleImplementationResultsLink";
|
|
12
12
|
import RuleEventDecorator from "./RuleEventDecorator";
|
|
13
|
+
import ExecutionGroupContent from "./ExecutionGroupContent";
|
|
14
|
+
import ExecutionGroupBreadcrumbs from "./ExecutionGroupBreadcrumbs";
|
|
13
15
|
|
|
14
|
-
const
|
|
16
|
+
const COLUMNS = [
|
|
15
17
|
{
|
|
16
18
|
name: "ruleImplementations.props.status",
|
|
17
19
|
fieldDecorator: RuleEventDecorator,
|
|
18
|
-
fieldSelector: pick(["_embedded.quality_events"]),
|
|
20
|
+
fieldSelector: _.pick(["_embedded.quality_events"]),
|
|
19
21
|
},
|
|
20
22
|
{
|
|
21
23
|
name: "ruleImplementations.props.implementation_key",
|
|
@@ -54,9 +56,9 @@ const columns = [
|
|
|
54
56
|
|
|
55
57
|
export const ExecutionRow = ({ execution }) => (
|
|
56
58
|
<Table.Row>
|
|
57
|
-
{
|
|
59
|
+
{COLUMNS.map(({ textAlign, ...column }, i) => (
|
|
58
60
|
<Table.Cell
|
|
59
|
-
key={
|
|
61
|
+
key={i}
|
|
60
62
|
content={columnDecorator(column)(execution)}
|
|
61
63
|
textAlign={textAlign}
|
|
62
64
|
/>
|
|
@@ -68,31 +70,35 @@ ExecutionRow.propTypes = {
|
|
|
68
70
|
execution: PropTypes.object,
|
|
69
71
|
};
|
|
70
72
|
|
|
71
|
-
|
|
73
|
+
const isCompleted = _.flow(_.prop("_embedded.status"), (s) =>
|
|
74
|
+
_.includes(s)(["SUCCEEDED", "FAILED"])
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
export const ExecutionGroup = ({ executionGroup }) => {
|
|
72
78
|
const { formatMessage } = useIntl();
|
|
73
|
-
const executions = sortBy("_embedded.implementation.implementation_key")(
|
|
79
|
+
const executions = _.sortBy("_embedded.implementation.implementation_key")(
|
|
74
80
|
executionGroup?._embedded?.executions
|
|
75
81
|
);
|
|
76
|
-
const count = size(executions);
|
|
77
|
-
const
|
|
78
|
-
const
|
|
79
|
-
filter(flow(prop("_embedded.quality_events"), size)),
|
|
80
|
-
size
|
|
81
|
-
)(executions);
|
|
82
|
+
const count = _.size(executions);
|
|
83
|
+
const pendingCount = _.flow(_.reject(isCompleted), _.size)(executions);
|
|
84
|
+
const content = executionGroup?.df_content;
|
|
82
85
|
|
|
83
|
-
|
|
86
|
+
/*
|
|
87
|
+
* executionGroupLoading is causing an empty table to flash briefly when
|
|
88
|
+
* navigating between execution groups, so executionGroup === null is
|
|
89
|
+
* used here instead.
|
|
90
|
+
*/
|
|
91
|
+
return executionGroup === null ? null : (
|
|
84
92
|
<>
|
|
85
|
-
<
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
error={errorCount}
|
|
89
|
-
/>
|
|
93
|
+
<ExecutionGroupBreadcrumbs timestamp={executionGroup?.inserted_at} />
|
|
94
|
+
{_.isEmpty(content) ? null : <ExecutionGroupContent content={content} />}
|
|
95
|
+
<ExecutionGroupMessage count={count} pending={pendingCount} />
|
|
90
96
|
<Table>
|
|
91
97
|
<Table.Header>
|
|
92
98
|
<Table.Row>
|
|
93
|
-
{
|
|
99
|
+
{COLUMNS.map(({ name: id, textAlign }, i) => (
|
|
94
100
|
<Table.HeaderCell
|
|
95
|
-
key={
|
|
101
|
+
key={i}
|
|
96
102
|
content={formatMessage({ id })}
|
|
97
103
|
textAlign={textAlign}
|
|
98
104
|
/>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import PropTypes from "prop-types";
|
|
3
|
+
import { Breadcrumb } from "semantic-ui-react";
|
|
4
|
+
import { Link } from "react-router-dom";
|
|
5
|
+
import { FormattedMessage } from "react-intl";
|
|
6
|
+
import { DateTime } from "@truedat/core/components";
|
|
7
|
+
import { EXECUTION_GROUPS } from "@truedat/core/routes";
|
|
8
|
+
|
|
9
|
+
export const ExecutionGroupBreadcrumbs = ({ timestamp }) => (
|
|
10
|
+
<Breadcrumb>
|
|
11
|
+
<Breadcrumb.Section as={Link} to={EXECUTION_GROUPS}>
|
|
12
|
+
<FormattedMessage id="sidemenu.executions" />
|
|
13
|
+
</Breadcrumb.Section>
|
|
14
|
+
<Breadcrumb.Divider icon="right angle" />
|
|
15
|
+
<Breadcrumb.Section active>
|
|
16
|
+
<DateTime value={timestamp} />
|
|
17
|
+
</Breadcrumb.Section>
|
|
18
|
+
</Breadcrumb>
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
ExecutionGroupBreadcrumbs.propTypes = {
|
|
22
|
+
timestamp: PropTypes.string,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default ExecutionGroupBreadcrumbs;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import PropTypes from "prop-types";
|
|
4
|
+
import { useQuery } from "@apollo/client";
|
|
5
|
+
import { TEMPLATES_QUERY } from "@truedat/core/api/queries";
|
|
6
|
+
|
|
7
|
+
const DynamicFormViewer = React.lazy(() =>
|
|
8
|
+
import("@truedat/df/components/DynamicFormViewer")
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
const templateFieldNames = _.flow(
|
|
12
|
+
_.prop("content"),
|
|
13
|
+
_.flatMap("fields"),
|
|
14
|
+
_.map("name")
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
export const ExecutionGroupContent = ({ content }) => {
|
|
18
|
+
const { data } = useQuery(TEMPLATES_QUERY, {
|
|
19
|
+
variables: { scope: "qe" },
|
|
20
|
+
});
|
|
21
|
+
// NOTE: We don't have the template name, so select one with fields matching content
|
|
22
|
+
const contentFieldNames = _.keys(content);
|
|
23
|
+
const matchingFieldCount = _.flow(
|
|
24
|
+
templateFieldNames,
|
|
25
|
+
_.intersection(contentFieldNames),
|
|
26
|
+
_.size
|
|
27
|
+
);
|
|
28
|
+
const template = _.flow(
|
|
29
|
+
_.propOr([], "templates"),
|
|
30
|
+
_.maxBy(matchingFieldCount)
|
|
31
|
+
)(data);
|
|
32
|
+
|
|
33
|
+
return template ? (
|
|
34
|
+
<DynamicFormViewer template={template} content={content} />
|
|
35
|
+
) : null;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
ExecutionGroupContent.propTypes = {
|
|
39
|
+
content: PropTypes.object,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export default ExecutionGroupContent;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import PropTypes from "prop-types";
|
|
3
|
+
import { Link } from "react-router-dom";
|
|
4
|
+
import { DateTime } from "@truedat/core/components";
|
|
5
|
+
import { linkTo } from "@truedat/core/routes";
|
|
6
|
+
|
|
7
|
+
export const ExecutionGroupLink = ({ id, insertedAt }) => (
|
|
8
|
+
<Link to={linkTo.EXECUTION_GROUP({ id })}>
|
|
9
|
+
<DateTime value={insertedAt} />
|
|
10
|
+
</Link>
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
ExecutionGroupLink.propTypes = {
|
|
14
|
+
id: PropTypes.string,
|
|
15
|
+
insertedAt: PropTypes.string,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default ExecutionGroupLink;
|
|
@@ -6,7 +6,7 @@ import { fetchExecutionGroup, clearExecutionGroup } from "../routines";
|
|
|
6
6
|
|
|
7
7
|
export const ExecutionGroupLoader = ({
|
|
8
8
|
fetchExecutionGroup,
|
|
9
|
-
clearExecutionGroup
|
|
9
|
+
clearExecutionGroup,
|
|
10
10
|
}) => {
|
|
11
11
|
const { id } = useParams();
|
|
12
12
|
useEffect(() => {
|
|
@@ -23,7 +23,6 @@ export const ExecutionGroupLoader = ({
|
|
|
23
23
|
ExecutionGroupLoader.propTypes = {
|
|
24
24
|
clearExecutionGroup: PropTypes.func,
|
|
25
25
|
fetchExecutionGroup: PropTypes.func,
|
|
26
|
-
id: PropTypes.number
|
|
27
26
|
};
|
|
28
27
|
|
|
29
28
|
const mapDispatchToProps = { clearExecutionGroup, fetchExecutionGroup };
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import PropTypes from "prop-types";
|
|
3
|
+
import { useQuery } from "@apollo/client";
|
|
4
|
+
import queryString from "query-string";
|
|
5
|
+
import { useIntl } from "react-intl";
|
|
6
|
+
import { useLocation, Link } from "react-router-dom";
|
|
7
|
+
import { Header, Menu, Segment } from "semantic-ui-react";
|
|
8
|
+
import { MY_EXECUTION_GROUPS_QUERY } from "../api/queries";
|
|
9
|
+
import ExecutionGroupsTable from "./ExecutionGroupsTable";
|
|
10
|
+
|
|
11
|
+
const PAGE_SIZE = 20;
|
|
12
|
+
|
|
13
|
+
export const Pagination = ({ pageInfo }) => {
|
|
14
|
+
const { endCursor, startCursor, hasPreviousPage, hasNextPage } =
|
|
15
|
+
pageInfo || {};
|
|
16
|
+
const next =
|
|
17
|
+
hasNextPage && endCursor ? { search: "?after=" + endCursor } : null;
|
|
18
|
+
const prev =
|
|
19
|
+
hasPreviousPage && startCursor
|
|
20
|
+
? { search: "?before=" + startCursor }
|
|
21
|
+
: null;
|
|
22
|
+
return (
|
|
23
|
+
<Menu pagination role="navigation">
|
|
24
|
+
<Menu.Item
|
|
25
|
+
as={next ? Link : null}
|
|
26
|
+
link={!!next}
|
|
27
|
+
content="«"
|
|
28
|
+
disabled={!hasNextPage}
|
|
29
|
+
to={{ search: "" }}
|
|
30
|
+
replace={next ? true : null}
|
|
31
|
+
aria-label="Latest"
|
|
32
|
+
/>
|
|
33
|
+
<Menu.Item
|
|
34
|
+
as={next ? Link : null}
|
|
35
|
+
link={!!next}
|
|
36
|
+
content="⟨"
|
|
37
|
+
disabled={!hasNextPage}
|
|
38
|
+
to={next}
|
|
39
|
+
replace={next ? true : null}
|
|
40
|
+
aria-label="Later"
|
|
41
|
+
/>
|
|
42
|
+
<Menu.Item
|
|
43
|
+
as={prev ? Link : null}
|
|
44
|
+
link={!!prev}
|
|
45
|
+
content="⟩"
|
|
46
|
+
disabled={!hasPreviousPage}
|
|
47
|
+
to={prev}
|
|
48
|
+
replace={prev ? true : null}
|
|
49
|
+
aria-label="Earlier"
|
|
50
|
+
/>
|
|
51
|
+
</Menu>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
Pagination.propTypes = {
|
|
56
|
+
pageInfo: PropTypes.object,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const ExecutionGroups = () => {
|
|
60
|
+
const { formatMessage } = useIntl();
|
|
61
|
+
const { search } = useLocation();
|
|
62
|
+
const { before, after } = queryString.parse(search);
|
|
63
|
+
const variables = after
|
|
64
|
+
? { after, before, first: PAGE_SIZE }
|
|
65
|
+
: { after, before, last: PAGE_SIZE };
|
|
66
|
+
const { loading, error, data } = useQuery(MY_EXECUTION_GROUPS_QUERY, {
|
|
67
|
+
variables,
|
|
68
|
+
});
|
|
69
|
+
if (error) return null;
|
|
70
|
+
const connection = data?.me?.executionGroupsConnection;
|
|
71
|
+
return (
|
|
72
|
+
<Segment loading={loading}>
|
|
73
|
+
<Header as="h2" content={formatMessage({ id: "sidemenu.executions" })} />
|
|
74
|
+
<Pagination {...connection} />
|
|
75
|
+
{loading ? null : <ExecutionGroupsTable {...connection} />}
|
|
76
|
+
</Segment>
|
|
77
|
+
);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export default ExecutionGroups;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import PropTypes from "prop-types";
|
|
3
|
+
import { FormattedMessage } from "react-intl";
|
|
4
|
+
import { connect } from "react-redux";
|
|
5
|
+
import { Table } from "semantic-ui-react";
|
|
6
|
+
import { columnDecorator } from "@truedat/core/services";
|
|
7
|
+
import { executionGroupsColumnsSelector } from "../selectors/executionGroupsColumnsSelector";
|
|
8
|
+
|
|
9
|
+
export const HeaderRow = ({ columns }) => (
|
|
10
|
+
<Table.Row>
|
|
11
|
+
{columns.map(({ name: id, textAlign }, i) => (
|
|
12
|
+
<Table.HeaderCell key={i} textAlign={textAlign}>
|
|
13
|
+
<FormattedMessage id={id} defaultMessage={id} />
|
|
14
|
+
</Table.HeaderCell>
|
|
15
|
+
))}
|
|
16
|
+
</Table.Row>
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
HeaderRow.propTypes = {
|
|
20
|
+
columns: PropTypes.arrayOf(PropTypes.object),
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const ExecutionGroupRow = ({ columns, ...props }) => (
|
|
24
|
+
<Table.Row>
|
|
25
|
+
{columns.map((col, i) => (
|
|
26
|
+
<Table.Cell
|
|
27
|
+
key={i}
|
|
28
|
+
textAlign={col.textAlign}
|
|
29
|
+
content={columnDecorator(col)(props)}
|
|
30
|
+
/>
|
|
31
|
+
))}
|
|
32
|
+
</Table.Row>
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
ExecutionGroupRow.propTypes = {
|
|
36
|
+
columns: PropTypes.arrayOf(PropTypes.object),
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const ExecutionGroupsTable = ({ columns, page }) => (
|
|
40
|
+
<Table>
|
|
41
|
+
<Table.Header>
|
|
42
|
+
<HeaderRow columns={columns} />
|
|
43
|
+
</Table.Header>
|
|
44
|
+
<Table.Body>
|
|
45
|
+
{page?.map((props, key) => (
|
|
46
|
+
<ExecutionGroupRow key={key} columns={columns} {...props} />
|
|
47
|
+
))}
|
|
48
|
+
</Table.Body>
|
|
49
|
+
</Table>
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
ExecutionGroupsTable.propTypes = {
|
|
53
|
+
columns: PropTypes.array,
|
|
54
|
+
page: PropTypes.array,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const mapStateToProps = (state) => ({
|
|
58
|
+
columns: executionGroupsColumnsSelector(state),
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
export default connect(mapStateToProps)(ExecutionGroupsTable);
|
|
@@ -1,35 +1,43 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { Route } from "react-router-dom";
|
|
2
|
+
import { Route, Switch } from "react-router-dom";
|
|
3
3
|
import { Unauthorized } from "@truedat/core/components";
|
|
4
4
|
import { useAuthorized } from "@truedat/core/hooks";
|
|
5
5
|
import {
|
|
6
6
|
EXECUTION_GROUP,
|
|
7
|
+
EXECUTION_GROUPS,
|
|
7
8
|
IMPLEMENTATIONS,
|
|
8
9
|
IMPLEMENTATIONS_PENDING,
|
|
9
10
|
RULES,
|
|
10
11
|
} from "@truedat/core/routes";
|
|
11
12
|
import ExecutionGroup from "./ExecutionGroup";
|
|
12
13
|
import ExecutionGroupLoader from "./ExecutionGroupLoader";
|
|
14
|
+
import ExecutionGroups from "./ExecutionGroups";
|
|
13
15
|
import ImplementationsRoutes from "./ImplementationsRoutes";
|
|
14
16
|
import RulesRoutes from "./RulesRoutes";
|
|
15
17
|
|
|
18
|
+
const ExecutionsRoutes = () => (
|
|
19
|
+
<Switch>
|
|
20
|
+
<Route
|
|
21
|
+
path={EXECUTION_GROUP}
|
|
22
|
+
render={() => (
|
|
23
|
+
<>
|
|
24
|
+
<ExecutionGroupLoader />
|
|
25
|
+
<ExecutionGroup />
|
|
26
|
+
</>
|
|
27
|
+
)}
|
|
28
|
+
/>
|
|
29
|
+
<Route path={EXECUTION_GROUPS} render={() => <ExecutionGroups />} />
|
|
30
|
+
</Switch>
|
|
31
|
+
);
|
|
32
|
+
|
|
16
33
|
const QualityRoutes = () => {
|
|
17
34
|
const authorized = useAuthorized("quality");
|
|
18
35
|
|
|
19
36
|
return (
|
|
20
37
|
<>
|
|
21
38
|
<Route
|
|
22
|
-
path={
|
|
23
|
-
render={() =>
|
|
24
|
-
authorized ? (
|
|
25
|
-
<>
|
|
26
|
-
<ExecutionGroupLoader />
|
|
27
|
-
<ExecutionGroup />
|
|
28
|
-
</>
|
|
29
|
-
) : (
|
|
30
|
-
<Unauthorized />
|
|
31
|
-
)
|
|
32
|
-
}
|
|
39
|
+
path={EXECUTION_GROUPS}
|
|
40
|
+
render={() => (authorized ? <ExecutionsRoutes /> : <Unauthorized />)}
|
|
33
41
|
/>
|
|
34
42
|
<Route
|
|
35
43
|
path={RULES}
|
|
@@ -1,54 +1,51 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import { ExecutionGroup } from "../ExecutionGroup";
|
|
2
|
+
import { render } from "@truedat/test/render";
|
|
3
|
+
import ExecutionGroup from "../ExecutionGroup";
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
5
|
+
const rule = {
|
|
6
|
+
id: 1,
|
|
7
|
+
name: "rule_name",
|
|
8
|
+
};
|
|
9
|
+
const implementation = {
|
|
10
|
+
id: 1,
|
|
11
|
+
implementation_key: "foo",
|
|
12
|
+
rule_id: 1,
|
|
13
|
+
result_type: "percentage",
|
|
14
|
+
minimum: 10,
|
|
15
|
+
goal: 20,
|
|
16
|
+
};
|
|
17
|
+
const result = {
|
|
18
|
+
id: 1,
|
|
19
|
+
implementation_key: "foo",
|
|
20
|
+
date: "2020-01-01 00:00:00Z",
|
|
21
|
+
result: 30,
|
|
22
|
+
records: 100,
|
|
23
|
+
errors: 30,
|
|
24
|
+
result_type: "percentage",
|
|
25
|
+
minimum: 10,
|
|
26
|
+
goal: 20,
|
|
27
|
+
};
|
|
28
|
+
const quality_events = [
|
|
29
|
+
{
|
|
30
|
+
inserted_at: "2020-01-01 00:00:00Z",
|
|
31
|
+
execution_id: 1,
|
|
16
32
|
id: 1,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
errors: 30,
|
|
30
|
-
result_type: "percentage",
|
|
31
|
-
minimum: 10,
|
|
32
|
-
goal: 20,
|
|
33
|
-
};
|
|
34
|
-
const quality_events = [
|
|
35
|
-
{
|
|
36
|
-
inserted_at: "2020-01-01 00:00:00Z",
|
|
37
|
-
execution_id: 1,
|
|
38
|
-
id: 1,
|
|
39
|
-
type: "SUCCESS",
|
|
40
|
-
message: "",
|
|
41
|
-
},
|
|
42
|
-
];
|
|
43
|
-
const executions = [
|
|
44
|
-
{ id: 1, _embedded: { rule, implementation, result, quality_events } },
|
|
45
|
-
];
|
|
46
|
-
const props = {
|
|
47
|
-
executionGroup: { id: 1, _embedded: { executions } },
|
|
48
|
-
};
|
|
33
|
+
type: "SUCCESS",
|
|
34
|
+
message: "",
|
|
35
|
+
},
|
|
36
|
+
];
|
|
37
|
+
const executions = [
|
|
38
|
+
{ id: 1, _embedded: { rule, implementation, result, quality_events } },
|
|
39
|
+
];
|
|
40
|
+
const state = {
|
|
41
|
+
executionGroup: { id: 1, _embedded: { executions } },
|
|
42
|
+
executionGroupsLoading: false,
|
|
43
|
+
};
|
|
44
|
+
const renderOpts = { state };
|
|
49
45
|
|
|
46
|
+
describe("<ExecutionGroup />", () => {
|
|
50
47
|
it("matches the latest snapshot", () => {
|
|
51
|
-
const
|
|
52
|
-
expect(
|
|
48
|
+
const { container } = render(<ExecutionGroup />, renderOpts);
|
|
49
|
+
expect(container).toMatchSnapshot();
|
|
53
50
|
});
|
|
54
51
|
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render } from "@truedat/test/render";
|
|
3
|
+
import ExecutionGroupBreadcrumbs from "../ExecutionGroupBreadcrumbs";
|
|
4
|
+
|
|
5
|
+
describe("<ExecutionGroupBreadcrumbs />", () => {
|
|
6
|
+
it("matches the latest snapshot", () => {
|
|
7
|
+
const props = { timestamp: "2022-02-02T12:34:56.789Z" };
|
|
8
|
+
const { container } = render(<ExecutionGroupBreadcrumbs {...props} />);
|
|
9
|
+
expect(container).toMatchSnapshot();
|
|
10
|
+
});
|
|
11
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { waitFor } from "@testing-library/react";
|
|
3
|
+
import { render } from "@truedat/test/render";
|
|
4
|
+
import { multipleTemplatesMock } from "@truedat/test/mocks";
|
|
5
|
+
import ExecutionGroupContent from "../ExecutionGroupContent";
|
|
6
|
+
|
|
7
|
+
const renderOpts = {
|
|
8
|
+
fallback: "lazy",
|
|
9
|
+
mocks: [multipleTemplatesMock({ scope: "qe" })],
|
|
10
|
+
state: {},
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
describe("<ExecutionGroupContent />", () => {
|
|
14
|
+
const content = { field1: "foo" };
|
|
15
|
+
|
|
16
|
+
it("matches the latest snapshot and renders dynamic content", async () => {
|
|
17
|
+
const { container, queryByText } = render(
|
|
18
|
+
<ExecutionGroupContent content={content} />,
|
|
19
|
+
renderOpts
|
|
20
|
+
);
|
|
21
|
+
await waitFor(() => expect(queryByText(/lazy/i)).not.toBeInTheDocument());
|
|
22
|
+
await waitFor(() =>
|
|
23
|
+
expect(queryByText(/loading/i)).not.toBeInTheDocument()
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
expect(container).toMatchSnapshot();
|
|
27
|
+
expect(queryByText("field1")).toBeInTheDocument();
|
|
28
|
+
expect(queryByText("foo")).toBeInTheDocument();
|
|
29
|
+
});
|
|
30
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render } from "@truedat/test/render";
|
|
3
|
+
import ExecutionGroupLink from "../ExecutionGroupLink";
|
|
4
|
+
|
|
5
|
+
describe("<ExecutionGroupLink />", () => {
|
|
6
|
+
it("matches the latest snapshot", () => {
|
|
7
|
+
const props = { id: "123", insertedAt: "2022-02-02T12:34:56.789Z" };
|
|
8
|
+
const { container } = render(<ExecutionGroupLink {...props} />);
|
|
9
|
+
expect(container).toMatchSnapshot();
|
|
10
|
+
});
|
|
11
|
+
});
|