@truedat/dd 8.10.2 → 8.10.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 +3 -3
- package/src/components/Graphs.js +109 -0
- package/src/components/LineageDomainSelectorSkeleton.js +8 -0
- package/src/components/LineageDownloadButton.js +29 -6
- package/src/components/LineageEventsPanel.js +206 -0
- package/src/components/LineageForm.js +265 -144
- package/src/components/LineageGraph.js +0 -1
- package/src/components/LineageHeader.js +33 -0
- package/src/components/LineageResourceToggle.js +56 -0
- package/src/components/LineageRoutes.js +2 -31
- package/src/components/LineageSelectionPanel.js +165 -0
- package/src/components/__tests__/LineageForm.spec.js +343 -3
- package/src/components/__tests__/LineageRoutes.spec.js +62 -76
- package/src/components/__tests__/__snapshots__/LineageForm.spec.js.snap +291 -250
- package/src/components/__tests__/__snapshots__/LineageRoutes.spec.js.snap +19 -43
- package/src/components/__tests__/__snapshots__/StructureFieldsDropdown.spec.js.snap +0 -12
- package/src/components/__tests__/__snapshots__/ValueConditionStructure.spec.js.snap +0 -3
- package/src/components/index.js +12 -0
- package/src/components/lineageDomainCache.js +101 -0
- package/src/components/lineageEventHelpers.js +195 -0
- package/src/styles/lineageGraph.less +1213 -47
- package/src/components/Lineage.js +0 -20
- package/src/components/LineageSample.js +0 -20
- package/src/components/__tests__/Lineage.spec.js +0 -16
- package/src/components/__tests__/__snapshots__/Lineage.spec.js.snap +0 -163
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@truedat/dd",
|
|
3
|
-
"version": "8.10.
|
|
3
|
+
"version": "8.10.4",
|
|
4
4
|
"description": "Truedat Web Data Dictionary",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"module": "src/index.js",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"@testing-library/jest-dom": "^6.6.3",
|
|
55
55
|
"@testing-library/react": "^16.3.0",
|
|
56
56
|
"@testing-library/user-event": "^14.6.1",
|
|
57
|
-
"@truedat/test": "8.10.
|
|
57
|
+
"@truedat/test": "8.10.4",
|
|
58
58
|
"identity-obj-proxy": "^3.0.0",
|
|
59
59
|
"jest": "^29.7.0",
|
|
60
60
|
"redux-saga-test-plan": "^4.0.6"
|
|
@@ -89,5 +89,5 @@
|
|
|
89
89
|
"svg-pan-zoom": "^3.6.2",
|
|
90
90
|
"swr": "^2.3.3"
|
|
91
91
|
},
|
|
92
|
-
"gitHead": "
|
|
92
|
+
"gitHead": "fe4fda03b2b73d26f644219d806dcc45767a3c7a"
|
|
93
93
|
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import React, { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
import PropTypes from "prop-types";
|
|
3
|
+
import { useParams } from "react-router";
|
|
4
|
+
import { Segment } from "semantic-ui-react";
|
|
5
|
+
import LineageHeader from "./LineageHeader";
|
|
6
|
+
import LineageSelectionPanel from "./LineageSelectionPanel";
|
|
7
|
+
import GraphRedirector from "./GraphRedirector";
|
|
8
|
+
import LineageGraph from "./LineageGraph";
|
|
9
|
+
|
|
10
|
+
export const Graphs = () => {
|
|
11
|
+
const { id } = useParams();
|
|
12
|
+
const [sidebarCollapsed, setSidebarCollapsed] = useState(Boolean(id));
|
|
13
|
+
const [sidebarHintActive, setSidebarHintActive] = useState(Boolean(id));
|
|
14
|
+
const [sidebarHintOn, setSidebarHintOn] = useState(Boolean(id));
|
|
15
|
+
const [sidebarHintToken, setSidebarHintToken] = useState(0);
|
|
16
|
+
const sidebarHintTimeoutRef = useRef();
|
|
17
|
+
const sidebarHintIntervalRef = useRef();
|
|
18
|
+
const clearSidebarHint = useCallback(() => {
|
|
19
|
+
if (sidebarHintTimeoutRef.current) {
|
|
20
|
+
clearTimeout(sidebarHintTimeoutRef.current);
|
|
21
|
+
sidebarHintTimeoutRef.current = undefined;
|
|
22
|
+
}
|
|
23
|
+
if (sidebarHintIntervalRef.current) {
|
|
24
|
+
clearInterval(sidebarHintIntervalRef.current);
|
|
25
|
+
sidebarHintIntervalRef.current = undefined;
|
|
26
|
+
}
|
|
27
|
+
setSidebarHintActive(false);
|
|
28
|
+
setSidebarHintOn(false);
|
|
29
|
+
}, []);
|
|
30
|
+
const showSidebarHint = useCallback(() => {
|
|
31
|
+
if (sidebarHintTimeoutRef.current)
|
|
32
|
+
clearTimeout(sidebarHintTimeoutRef.current);
|
|
33
|
+
if (sidebarHintIntervalRef.current)
|
|
34
|
+
clearInterval(sidebarHintIntervalRef.current);
|
|
35
|
+
|
|
36
|
+
setSidebarHintToken((token) => token + 1);
|
|
37
|
+
setSidebarHintActive(true);
|
|
38
|
+
setSidebarHintOn(true);
|
|
39
|
+
const pulseCountRef = { current: 0 };
|
|
40
|
+
sidebarHintIntervalRef.current = setInterval(() => {
|
|
41
|
+
pulseCountRef.current += 1;
|
|
42
|
+
setSidebarHintOn((active) => !active);
|
|
43
|
+
|
|
44
|
+
if (pulseCountRef.current >= 3) {
|
|
45
|
+
clearInterval(sidebarHintIntervalRef.current);
|
|
46
|
+
sidebarHintIntervalRef.current = undefined;
|
|
47
|
+
}
|
|
48
|
+
}, 300);
|
|
49
|
+
sidebarHintTimeoutRef.current = setTimeout(() => {
|
|
50
|
+
setSidebarHintActive(false);
|
|
51
|
+
setSidebarHintOn(false);
|
|
52
|
+
sidebarHintTimeoutRef.current = undefined;
|
|
53
|
+
if (sidebarHintIntervalRef.current) {
|
|
54
|
+
clearInterval(sidebarHintIntervalRef.current);
|
|
55
|
+
sidebarHintIntervalRef.current = undefined;
|
|
56
|
+
}
|
|
57
|
+
}, 1200);
|
|
58
|
+
}, []);
|
|
59
|
+
const collapseSidebarWithHint = useCallback(() => {
|
|
60
|
+
setSidebarCollapsed(true);
|
|
61
|
+
showSidebarHint();
|
|
62
|
+
}, [showSidebarHint]);
|
|
63
|
+
const handleToggleSidebar = useCallback(() => {
|
|
64
|
+
clearSidebarHint();
|
|
65
|
+
setSidebarCollapsed((collapsed) => !collapsed);
|
|
66
|
+
}, [clearSidebarHint]);
|
|
67
|
+
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
if (id) collapseSidebarWithHint();
|
|
70
|
+
}, [collapseSidebarWithHint, id]);
|
|
71
|
+
|
|
72
|
+
useEffect(
|
|
73
|
+
() => () => {
|
|
74
|
+
if (sidebarHintTimeoutRef.current) {
|
|
75
|
+
clearTimeout(sidebarHintTimeoutRef.current);
|
|
76
|
+
}
|
|
77
|
+
if (sidebarHintIntervalRef.current) {
|
|
78
|
+
clearInterval(sidebarHintIntervalRef.current);
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
[],
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<Segment className="lineage-view">
|
|
86
|
+
<LineageHeader />
|
|
87
|
+
<div
|
|
88
|
+
className={`lineage-view__workspace${
|
|
89
|
+
sidebarCollapsed ? " lineage-view__workspace--sidebar-collapsed" : ""
|
|
90
|
+
}`}
|
|
91
|
+
>
|
|
92
|
+
<LineageSelectionPanel
|
|
93
|
+
collapsed={sidebarCollapsed}
|
|
94
|
+
onGraphLoad={collapseSidebarWithHint}
|
|
95
|
+
onToggle={handleToggleSidebar}
|
|
96
|
+
sidebarHintActive={sidebarHintActive}
|
|
97
|
+
sidebarHintOn={sidebarHintOn}
|
|
98
|
+
sidebarHintToken={sidebarHintToken}
|
|
99
|
+
/>
|
|
100
|
+
<main className="lineage-view__main">
|
|
101
|
+
<GraphRedirector />
|
|
102
|
+
<LineageGraph />
|
|
103
|
+
</main>
|
|
104
|
+
</div>
|
|
105
|
+
</Segment>
|
|
106
|
+
);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
export default Graphs;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
const LineageDomainSelectorSkeleton = () => (
|
|
2
|
+
<div className="lineage-domain-selector-skeleton" aria-hidden="true">
|
|
3
|
+
<span className="lineage-domain-selector-skeleton__label" />
|
|
4
|
+
<span className="lineage-domain-selector-skeleton__icon" />
|
|
5
|
+
</div>
|
|
6
|
+
);
|
|
7
|
+
|
|
8
|
+
export default LineageDomainSelectorSkeleton;
|
|
@@ -16,17 +16,25 @@ const staticLabels = [
|
|
|
16
16
|
];
|
|
17
17
|
|
|
18
18
|
export const LineageDownloadButton = ({
|
|
19
|
+
basic = false,
|
|
20
|
+
className,
|
|
19
21
|
graph,
|
|
20
22
|
csvGraphDownloading,
|
|
21
23
|
graphLoading,
|
|
22
24
|
downloadCsvGraph,
|
|
25
|
+
disabled = false,
|
|
26
|
+
content,
|
|
27
|
+
compact = false,
|
|
28
|
+
labelPosition = "left",
|
|
29
|
+
size,
|
|
23
30
|
}) => {
|
|
24
31
|
const { formatMessage } = useIntl();
|
|
25
32
|
const headerLabels = _.flow(
|
|
26
33
|
_.map((l) => [l, formatMessage({ id: `graph.header.${l}` })]),
|
|
27
34
|
_.fromPairs,
|
|
28
35
|
)(staticLabels);
|
|
29
|
-
const
|
|
36
|
+
const downloadDisabled = disabled || _.isEmpty(graph);
|
|
37
|
+
const messagePopup = !downloadDisabled
|
|
30
38
|
? formatMessage({ id: "graph.actions.download.tooltip" })
|
|
31
39
|
: formatMessage({ id: "graph.actions.download.popup" });
|
|
32
40
|
return (
|
|
@@ -37,13 +45,21 @@ export const LineageDownloadButton = ({
|
|
|
37
45
|
trigger={
|
|
38
46
|
<span>
|
|
39
47
|
<Button
|
|
40
|
-
|
|
48
|
+
basic={basic}
|
|
49
|
+
className={className}
|
|
50
|
+
compact={compact}
|
|
51
|
+
content={content}
|
|
41
52
|
icon="download"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
53
|
+
labelPosition={content ? labelPosition : undefined}
|
|
54
|
+
onClick={() => {
|
|
55
|
+
if (downloadDisabled) return;
|
|
56
|
+
|
|
57
|
+
downloadCsvGraph({ headerLabels, id: _.prop("id")(graph) });
|
|
58
|
+
}}
|
|
45
59
|
loading={csvGraphDownloading}
|
|
46
|
-
disabled={
|
|
60
|
+
disabled={downloadDisabled}
|
|
61
|
+
secondary={!basic}
|
|
62
|
+
size={size}
|
|
47
63
|
/>
|
|
48
64
|
</span>
|
|
49
65
|
}
|
|
@@ -53,10 +69,17 @@ export const LineageDownloadButton = ({
|
|
|
53
69
|
};
|
|
54
70
|
|
|
55
71
|
LineageDownloadButton.propTypes = {
|
|
72
|
+
basic: PropTypes.bool,
|
|
73
|
+
className: PropTypes.string,
|
|
74
|
+
compact: PropTypes.bool,
|
|
75
|
+
content: PropTypes.node,
|
|
76
|
+
disabled: PropTypes.bool,
|
|
56
77
|
graph: PropTypes.object,
|
|
57
78
|
csvGraphDownloading: PropTypes.bool,
|
|
58
79
|
graphLoading: PropTypes.bool,
|
|
59
80
|
downloadCsvGraph: PropTypes.func,
|
|
81
|
+
labelPosition: PropTypes.string,
|
|
82
|
+
size: PropTypes.string,
|
|
60
83
|
};
|
|
61
84
|
|
|
62
85
|
const mapStateToProps = (state, ownProps) => ({
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import PropTypes from "prop-types";
|
|
3
|
+
import { useNavigate } from "react-router";
|
|
4
|
+
import { connect } from "react-redux";
|
|
5
|
+
import { FormattedMessage, useIntl } from "react-intl";
|
|
6
|
+
import { Icon, Popup } from "semantic-ui-react";
|
|
7
|
+
import Moment from "react-moment";
|
|
8
|
+
import { linkTo } from "@truedat/core/routes";
|
|
9
|
+
import {
|
|
10
|
+
isCompletedStatus,
|
|
11
|
+
isFailedStatus,
|
|
12
|
+
lineageEventType,
|
|
13
|
+
graphLabel,
|
|
14
|
+
graphScope,
|
|
15
|
+
graphDomain,
|
|
16
|
+
graphFirstLevel,
|
|
17
|
+
graphTypeMessageId,
|
|
18
|
+
graphTypeIcon,
|
|
19
|
+
eventErrorMessage,
|
|
20
|
+
eventTimestamp,
|
|
21
|
+
} from "./lineageEventHelpers";
|
|
22
|
+
|
|
23
|
+
export const LineageEventsPanel = ({
|
|
24
|
+
lineageEvents = [],
|
|
25
|
+
lineageEventsLoading,
|
|
26
|
+
onGraphLoad,
|
|
27
|
+
}) => {
|
|
28
|
+
const { formatMessage } = useIntl();
|
|
29
|
+
const navigate = useNavigate();
|
|
30
|
+
const sortedEvents = [...lineageEvents].sort(
|
|
31
|
+
(a, b) => eventTimestamp(b) - eventTimestamp(a),
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div className="lineage-events-panel">
|
|
36
|
+
<div className="lineage-events-panel__header">
|
|
37
|
+
<span>
|
|
38
|
+
<FormattedMessage id="lineage.events.panel.header" />
|
|
39
|
+
</span>
|
|
40
|
+
<Icon name="clock outline" />
|
|
41
|
+
</div>
|
|
42
|
+
<div className="lineage-events-panel__scrollable">
|
|
43
|
+
{lineageEventsLoading ? (
|
|
44
|
+
<div className="lineage-events-panel__empty">
|
|
45
|
+
<Icon loading name="circle notched" />
|
|
46
|
+
<span>
|
|
47
|
+
<FormattedMessage id="lineage.events.panel.loading" />
|
|
48
|
+
</span>
|
|
49
|
+
</div>
|
|
50
|
+
) : null}
|
|
51
|
+
{!lineageEventsLoading && sortedEvents.length ? (
|
|
52
|
+
<div className="lineage-events-panel__list">
|
|
53
|
+
{sortedEvents.map((event, index) => {
|
|
54
|
+
const completed = isCompletedStatus(event.status);
|
|
55
|
+
const failed = isFailedStatus(event.status);
|
|
56
|
+
const canLoadGraph = completed && event.graph_id;
|
|
57
|
+
const type = lineageEventType(event);
|
|
58
|
+
const errorMessage = eventErrorMessage(event);
|
|
59
|
+
const scope = graphScope(event.graph_data);
|
|
60
|
+
const domain = graphDomain(event);
|
|
61
|
+
const firstLevel = graphFirstLevel(event);
|
|
62
|
+
|
|
63
|
+
const handleCardClick = () => {
|
|
64
|
+
if (onGraphLoad) onGraphLoad();
|
|
65
|
+
navigate(linkTo.GRAPH({ id: event.graph_id }));
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const handleCardKeyDown = (e) => {
|
|
69
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
70
|
+
e.preventDefault();
|
|
71
|
+
handleCardClick();
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<article
|
|
77
|
+
className={`lineage-events-panel__item${
|
|
78
|
+
canLoadGraph ? " lineage-events-panel__item--clickable" : ""
|
|
79
|
+
}`}
|
|
80
|
+
key={event.graph_id || event.id || index}
|
|
81
|
+
onClick={canLoadGraph ? handleCardClick : undefined}
|
|
82
|
+
onKeyDown={canLoadGraph ? handleCardKeyDown : undefined}
|
|
83
|
+
role={canLoadGraph ? "button" : undefined}
|
|
84
|
+
aria-label={
|
|
85
|
+
canLoadGraph
|
|
86
|
+
? formatMessage({ id: "lineage.events.panel.load" })
|
|
87
|
+
: undefined
|
|
88
|
+
}
|
|
89
|
+
tabIndex={canLoadGraph ? 0 : undefined}
|
|
90
|
+
>
|
|
91
|
+
<div className="lineage-events-panel__item-header">
|
|
92
|
+
<div className="lineage-events-panel__item-title">
|
|
93
|
+
<Icon name="code branch" />
|
|
94
|
+
<span>{graphLabel(event, index)}</span>
|
|
95
|
+
</div>
|
|
96
|
+
<div
|
|
97
|
+
className={`lineage-events-panel__card-status${
|
|
98
|
+
completed
|
|
99
|
+
? " lineage-events-panel__card-status--completed"
|
|
100
|
+
: ""
|
|
101
|
+
}${
|
|
102
|
+
failed
|
|
103
|
+
? " lineage-events-panel__card-status--failed"
|
|
104
|
+
: ""
|
|
105
|
+
}`}
|
|
106
|
+
>
|
|
107
|
+
{failed ? (
|
|
108
|
+
<Popup
|
|
109
|
+
inverted
|
|
110
|
+
on="hover click"
|
|
111
|
+
position="top center"
|
|
112
|
+
trigger={
|
|
113
|
+
<button
|
|
114
|
+
aria-label={formatMessage({
|
|
115
|
+
id: "lineage.events.panel.error",
|
|
116
|
+
})}
|
|
117
|
+
className="lineage-events-panel__card-status-trigger"
|
|
118
|
+
type="button"
|
|
119
|
+
>
|
|
120
|
+
<Icon name="warning circle" />
|
|
121
|
+
KO
|
|
122
|
+
</button>
|
|
123
|
+
}
|
|
124
|
+
>
|
|
125
|
+
{errorMessage || (
|
|
126
|
+
<FormattedMessage id="lineage.events.panel.error.empty" />
|
|
127
|
+
)}
|
|
128
|
+
</Popup>
|
|
129
|
+
) : (
|
|
130
|
+
<span className="lineage-events-panel__card-status-trigger">
|
|
131
|
+
<Icon
|
|
132
|
+
name={completed ? "check circle" : "circle notched"}
|
|
133
|
+
/>
|
|
134
|
+
{completed ? "OK" : "..."}
|
|
135
|
+
</span>
|
|
136
|
+
)}
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
{domain || firstLevel ? (
|
|
140
|
+
<div className="lineage-events-panel__meta">
|
|
141
|
+
{domain ? (
|
|
142
|
+
<span>
|
|
143
|
+
<FormattedMessage id="lineage.events.panel.domain" />:{" "}
|
|
144
|
+
{domain}
|
|
145
|
+
</span>
|
|
146
|
+
) : null}
|
|
147
|
+
{firstLevel ? (
|
|
148
|
+
<span>
|
|
149
|
+
<FormattedMessage id="lineage.events.panel.first_level" />
|
|
150
|
+
: {firstLevel}
|
|
151
|
+
</span>
|
|
152
|
+
) : null}
|
|
153
|
+
</div>
|
|
154
|
+
) : null}
|
|
155
|
+
{scope ? (
|
|
156
|
+
<p className="lineage-events-panel__scope">{scope}</p>
|
|
157
|
+
) : null}
|
|
158
|
+
<div className="lineage-events-panel__row">
|
|
159
|
+
<div className="lineage-events-panel__row-main">
|
|
160
|
+
<div className="lineage-events-panel__type">
|
|
161
|
+
<Icon name={graphTypeIcon(type)} />
|
|
162
|
+
<FormattedMessage id={graphTypeMessageId(type)} />
|
|
163
|
+
</div>
|
|
164
|
+
<div className="lineage-events-panel__date">
|
|
165
|
+
{event.inserted_at ? (
|
|
166
|
+
<Moment
|
|
167
|
+
locale="es"
|
|
168
|
+
date={event.inserted_at}
|
|
169
|
+
format="YYYY-MM-DD HH:mm"
|
|
170
|
+
/>
|
|
171
|
+
) : (
|
|
172
|
+
"-"
|
|
173
|
+
)}
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
</article>
|
|
178
|
+
);
|
|
179
|
+
})}
|
|
180
|
+
</div>
|
|
181
|
+
) : null}
|
|
182
|
+
{!lineageEventsLoading && !sortedEvents.length ? (
|
|
183
|
+
<div className="lineage-events-panel__empty">
|
|
184
|
+
<Icon name="clock outline" />
|
|
185
|
+
<span>
|
|
186
|
+
<FormattedMessage id="lineage.events.search.results.empty" />
|
|
187
|
+
</span>
|
|
188
|
+
</div>
|
|
189
|
+
) : null}
|
|
190
|
+
</div>
|
|
191
|
+
</div>
|
|
192
|
+
);
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
LineageEventsPanel.propTypes = {
|
|
196
|
+
lineageEvents: PropTypes.array,
|
|
197
|
+
lineageEventsLoading: PropTypes.bool,
|
|
198
|
+
onGraphLoad: PropTypes.func,
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const mapStateToProps = (state) => ({
|
|
202
|
+
lineageEvents: state.lineageEvents,
|
|
203
|
+
lineageEventsLoading: state.lineageEventsLoading,
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
export default connect(mapStateToProps)(LineageEventsPanel);
|