@truedat/dd 8.11.4 → 8.11.7
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 +4 -3
- package/src/components/Graphs.js +30 -4
- package/src/components/LineageContextMenu.js +185 -58
- package/src/components/LineageDownloadButton.js +16 -6
- package/src/components/LineageEventsPanel.js +80 -27
- package/src/components/LineageEventsTable.js +7 -3
- package/src/components/LineageGraph.js +5144 -306
- package/src/components/LineageGraphToolbar.js +896 -0
- package/src/components/LineageInspectorPanel.js +748 -0
- package/src/components/LineageResourceNode.js +501 -0
- package/src/components/LineageRoutes.js +1 -2
- package/src/components/LineageSelectionPanel.js +28 -12
- package/src/components/StructureGraph.js +8 -1
- package/src/components/StructureGraphView.js +14 -0
- package/src/components/StructureImpact.js +2 -22
- package/src/components/StructureLineage.js +2 -19
- package/src/components/StructuresUploadEventsTable.js +0 -3
- package/src/components/__tests__/LineageContextMenu.spec.js +42 -0
- package/src/components/__tests__/LineageGraph.spec.js +896 -0
- package/src/components/__tests__/LineageGraphInteractions.spec.js +660 -0
- package/src/components/__tests__/LineageGraphLayout.spec.js +620 -0
- package/src/components/__tests__/LineageGraphToolbar.spec.js +366 -0
- package/src/components/__tests__/LineageHierarchy.spec.js +116 -0
- package/src/components/__tests__/LineageInspectorPanel.spec.js +357 -0
- package/src/components/__tests__/LineageResourceNode.spec.js +338 -0
- package/src/components/__tests__/LineageRoutes.spec.js +27 -9
- package/src/components/__tests__/__snapshots__/LineageDownloadButton.spec.js.snap +23 -3
- package/src/components/__tests__/__snapshots__/LineageForm.spec.js.snap +23 -3
- package/src/components/__tests__/lineageEventHelpers.spec.js +60 -0
- package/src/components/lineageEventHelpers.js +53 -17
- package/src/reducers/__tests__/lineageEvents.spec.js +164 -0
- package/src/reducers/__tests__/lineageQuery.spec.js +11 -0
- package/src/reducers/lineageEvents.js +68 -2
- package/src/reducers/lineageQuery.js +2 -6
- package/src/styles/LineageGraphColors.less +105 -0
- package/src/styles/LineageGraphCore.less +20 -0
- package/src/styles/LineageResourceNode.less +711 -0
- package/src/styles/lineageGraph.less +2973 -84
- package/src/utils/__tests__/lineageGraphIndex.spec.js +52 -0
- package/src/utils/__tests__/lineageLayout.spec.js +234 -0
- package/src/utils/__tests__/lineageTopology.spec.js +256 -0
- package/src/utils/lineageGraphIndex.js +296 -0
- package/src/utils/lineageLayout.js +1047 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@truedat/dd",
|
|
3
|
-
"version": "8.11.
|
|
3
|
+
"version": "8.11.7",
|
|
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.11.
|
|
57
|
+
"@truedat/test": "8.11.7",
|
|
58
58
|
"identity-obj-proxy": "^3.0.0",
|
|
59
59
|
"jest": "^29.7.0",
|
|
60
60
|
"redux-saga-test-plan": "^4.0.6"
|
|
@@ -67,6 +67,7 @@
|
|
|
67
67
|
"is-hotkey": "^0.2.0",
|
|
68
68
|
"is-url": "^1.2.4",
|
|
69
69
|
"lodash": "^4.17.21",
|
|
70
|
+
"lucide-react": "^1.41.0",
|
|
70
71
|
"match-sorter": "^8.0.1",
|
|
71
72
|
"moment": "^2.30.1",
|
|
72
73
|
"path-to-regexp": "^8.2.0",
|
|
@@ -89,5 +90,5 @@
|
|
|
89
90
|
"svg-pan-zoom": "^3.6.2",
|
|
90
91
|
"swr": "^2.3.3"
|
|
91
92
|
},
|
|
92
|
-
"gitHead": "
|
|
93
|
+
"gitHead": "ae97c12c9013e8881f997e7e407402fe6b1eff0a"
|
|
93
94
|
}
|
package/src/components/Graphs.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
useCallback,
|
|
3
|
+
useEffect,
|
|
4
|
+
useLayoutEffect,
|
|
5
|
+
useRef,
|
|
6
|
+
useState,
|
|
7
|
+
} from "react";
|
|
3
8
|
import { useParams } from "react-router";
|
|
4
9
|
import { Segment } from "semantic-ui-react";
|
|
5
10
|
import LineageHeader from "./LineageHeader";
|
|
@@ -9,6 +14,27 @@ import LineageGraph from "./LineageGraph";
|
|
|
9
14
|
|
|
10
15
|
export const Graphs = () => {
|
|
11
16
|
const { id } = useParams();
|
|
17
|
+
const canvasRef = useRef(null);
|
|
18
|
+
useLayoutEffect(() => {
|
|
19
|
+
const canvas = canvasRef.current;
|
|
20
|
+
if (!canvas) return undefined;
|
|
21
|
+
const updateCanvasTop = () => {
|
|
22
|
+
canvas.style.setProperty(
|
|
23
|
+
"--td-lineage-canvas-top",
|
|
24
|
+
`${canvas.getBoundingClientRect().top}px`,
|
|
25
|
+
);
|
|
26
|
+
};
|
|
27
|
+
updateCanvasTop();
|
|
28
|
+
const observer = new ResizeObserver(updateCanvasTop);
|
|
29
|
+
observer.observe(canvas.parentElement);
|
|
30
|
+
observer.observe(canvas.parentElement.parentElement);
|
|
31
|
+
window.addEventListener("resize", updateCanvasTop);
|
|
32
|
+
return () => {
|
|
33
|
+
observer.disconnect();
|
|
34
|
+
window.removeEventListener("resize", updateCanvasTop);
|
|
35
|
+
};
|
|
36
|
+
}, []);
|
|
37
|
+
|
|
12
38
|
const [sidebarCollapsed, setSidebarCollapsed] = useState(Boolean(id));
|
|
13
39
|
const [sidebarHintActive, setSidebarHintActive] = useState(Boolean(id));
|
|
14
40
|
const [sidebarHintOn, setSidebarHintOn] = useState(Boolean(id));
|
|
@@ -97,9 +123,9 @@ export const Graphs = () => {
|
|
|
97
123
|
sidebarHintOn={sidebarHintOn}
|
|
98
124
|
sidebarHintToken={sidebarHintToken}
|
|
99
125
|
/>
|
|
100
|
-
<main className="lineage-view__main">
|
|
126
|
+
<main className="lineage-view__main" ref={canvasRef}>
|
|
101
127
|
<GraphRedirector />
|
|
102
|
-
<LineageGraph />
|
|
128
|
+
<LineageGraph height="var(--td-lineage-canvas-height)" />
|
|
103
129
|
</main>
|
|
104
130
|
</div>
|
|
105
131
|
</Segment>
|
|
@@ -2,7 +2,7 @@ import _ from "lodash/fp";
|
|
|
2
2
|
import PropTypes from "prop-types";
|
|
3
3
|
import { connect } from "react-redux";
|
|
4
4
|
import { Link } from "react-router";
|
|
5
|
-
import {
|
|
5
|
+
import { Icon } from "semantic-ui-react";
|
|
6
6
|
import { FormattedMessage } from "react-intl";
|
|
7
7
|
import { linkTo } from "@truedat/core/routes";
|
|
8
8
|
import { excludeNode, createGraph } from "../routines";
|
|
@@ -14,64 +14,188 @@ export const LineageContextMenu = ({
|
|
|
14
14
|
resource,
|
|
15
15
|
excludeNode,
|
|
16
16
|
createGraph,
|
|
17
|
+
onAction,
|
|
18
|
+
onTogglePin,
|
|
19
|
+
pinned,
|
|
17
20
|
structureId,
|
|
18
|
-
}) =>
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
{
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
>
|
|
49
|
-
|
|
50
|
-
<
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
<
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
21
|
+
}) => {
|
|
22
|
+
const handleCreateGraph = (type) => () => {
|
|
23
|
+
onAction && onAction();
|
|
24
|
+
createGraph({
|
|
25
|
+
ids: [id],
|
|
26
|
+
levels,
|
|
27
|
+
type,
|
|
28
|
+
isRedirected: true,
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
const handleExclude = () => {
|
|
32
|
+
onAction && onAction();
|
|
33
|
+
excludeNode({ id, isRedirected: true });
|
|
34
|
+
};
|
|
35
|
+
const handleNavigate = () => {
|
|
36
|
+
onAction && onAction();
|
|
37
|
+
};
|
|
38
|
+
const handleTogglePin = () => {
|
|
39
|
+
onTogglePin && onTogglePin();
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<div
|
|
44
|
+
className={`lineage-context-menu${
|
|
45
|
+
_.size(name) > 25 ? " lineage-context-menu--wide" : ""
|
|
46
|
+
}`}
|
|
47
|
+
>
|
|
48
|
+
<div className="lineage-context-menu__header">
|
|
49
|
+
<span className="lineage-context-menu__header-icon" aria-hidden="true">
|
|
50
|
+
<Icon name={resource ? "sitemap" : "folder open outline"} />
|
|
51
|
+
</span>
|
|
52
|
+
<div className="lineage-context-menu__header-copy">
|
|
53
|
+
<span className="lineage-context-menu__eyebrow">
|
|
54
|
+
<FormattedMessage
|
|
55
|
+
id={
|
|
56
|
+
resource
|
|
57
|
+
? "lineage.sidebar.tabs.lineage"
|
|
58
|
+
: "navigation.lineage.view"
|
|
59
|
+
}
|
|
60
|
+
defaultMessage={resource ? "Lineage" : "View detail"}
|
|
61
|
+
/>
|
|
62
|
+
</span>
|
|
63
|
+
<span className="lineage-context-menu__title" title={name}>
|
|
64
|
+
{name}
|
|
65
|
+
</span>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
<div className="lineage-context-menu__actions">
|
|
69
|
+
{onTogglePin ? (
|
|
70
|
+
<button
|
|
71
|
+
aria-pressed={pinned}
|
|
72
|
+
className={`lineage-context-menu__action lineage-context-menu__action--pin${
|
|
73
|
+
pinned ? " lineage-context-menu__action--active" : ""
|
|
74
|
+
}`}
|
|
75
|
+
onClick={handleTogglePin}
|
|
76
|
+
type="button"
|
|
77
|
+
>
|
|
78
|
+
<span className="lineage-context-menu__action-icon">
|
|
79
|
+
<Icon
|
|
80
|
+
className={pinned ? "lineage-unpin-icon" : undefined}
|
|
81
|
+
name="pin"
|
|
82
|
+
/>
|
|
83
|
+
</span>
|
|
84
|
+
<span className="lineage-context-menu__action-copy">
|
|
85
|
+
<span className="lineage-context-menu__action-title">
|
|
86
|
+
<FormattedMessage
|
|
87
|
+
id={
|
|
88
|
+
pinned
|
|
89
|
+
? "lineage.context_menu.unpin_focus"
|
|
90
|
+
: "lineage.context_menu.pin_focus"
|
|
91
|
+
}
|
|
92
|
+
defaultMessage={pinned ? "Unpin focus" : "Pin focus"}
|
|
93
|
+
/>
|
|
94
|
+
</span>
|
|
95
|
+
</span>
|
|
96
|
+
</button>
|
|
97
|
+
) : null}
|
|
98
|
+
{resource ? (
|
|
99
|
+
<>
|
|
100
|
+
<button
|
|
101
|
+
className="lineage-context-menu__action"
|
|
102
|
+
onClick={handleCreateGraph("lineage")}
|
|
103
|
+
type="button"
|
|
104
|
+
>
|
|
105
|
+
<span className="lineage-context-menu__action-icon">
|
|
106
|
+
<Icon name="random" flipped="horizontally" />
|
|
107
|
+
</span>
|
|
108
|
+
<span className="lineage-context-menu__action-copy">
|
|
109
|
+
<span className="lineage-context-menu__action-title">
|
|
110
|
+
<FormattedMessage
|
|
111
|
+
id="navigation.lineage.lineage"
|
|
112
|
+
defaultMessage="Lineage"
|
|
113
|
+
/>
|
|
114
|
+
</span>
|
|
115
|
+
</span>
|
|
116
|
+
</button>
|
|
117
|
+
<button
|
|
118
|
+
className="lineage-context-menu__action"
|
|
119
|
+
onClick={handleCreateGraph("impact")}
|
|
120
|
+
type="button"
|
|
121
|
+
>
|
|
122
|
+
<span className="lineage-context-menu__action-icon">
|
|
123
|
+
<Icon name="random" />
|
|
124
|
+
</span>
|
|
125
|
+
<span className="lineage-context-menu__action-copy">
|
|
126
|
+
<span className="lineage-context-menu__action-title">
|
|
127
|
+
<FormattedMessage
|
|
128
|
+
id="navigation.lineage.impact"
|
|
129
|
+
defaultMessage="Impact"
|
|
130
|
+
/>
|
|
131
|
+
</span>
|
|
132
|
+
</span>
|
|
133
|
+
</button>
|
|
134
|
+
<button
|
|
135
|
+
className="lineage-context-menu__action"
|
|
136
|
+
onClick={handleExclude}
|
|
137
|
+
type="button"
|
|
138
|
+
>
|
|
139
|
+
<span className="lineage-context-menu__action-icon">
|
|
140
|
+
<Icon name="eye slash outline" />
|
|
141
|
+
</span>
|
|
142
|
+
<span className="lineage-context-menu__action-copy">
|
|
143
|
+
<span className="lineage-context-menu__action-title">
|
|
144
|
+
<FormattedMessage
|
|
145
|
+
id="navigation.lineage.exclude"
|
|
146
|
+
defaultMessage="Exclude"
|
|
147
|
+
/>
|
|
148
|
+
</span>
|
|
149
|
+
</span>
|
|
150
|
+
</button>
|
|
151
|
+
</>
|
|
152
|
+
) : null}
|
|
153
|
+
{structureId ? (
|
|
154
|
+
<Link
|
|
155
|
+
className="lineage-context-menu__action lineage-context-menu__action--link"
|
|
156
|
+
onClick={handleNavigate}
|
|
157
|
+
target="_blank"
|
|
158
|
+
rel="noopener noreferrer"
|
|
159
|
+
to={linkTo.STRUCTURE({ id: structureId })}
|
|
160
|
+
>
|
|
161
|
+
<span className="lineage-context-menu__action-icon">
|
|
162
|
+
<Icon name="th list" />
|
|
163
|
+
</span>
|
|
164
|
+
<span className="lineage-context-menu__action-copy">
|
|
165
|
+
<span className="lineage-context-menu__action-title">
|
|
166
|
+
<FormattedMessage
|
|
167
|
+
id="navigation.lineage.view"
|
|
168
|
+
defaultMessage="View in Data Catalog"
|
|
169
|
+
/>
|
|
170
|
+
</span>
|
|
171
|
+
</span>
|
|
172
|
+
</Link>
|
|
173
|
+
) : null}
|
|
174
|
+
{structureId ? (
|
|
175
|
+
<Link
|
|
176
|
+
className="lineage-context-menu__action lineage-context-menu__action--link"
|
|
177
|
+
onClick={handleNavigate}
|
|
178
|
+
target="_blank"
|
|
179
|
+
rel="noopener noreferrer"
|
|
180
|
+
to={linkTo.STRUCTURE_RULES({ id: structureId })}
|
|
181
|
+
>
|
|
182
|
+
<span className="lineage-context-menu__action-icon">
|
|
183
|
+
<Icon name="check circle outline" />
|
|
184
|
+
</span>
|
|
185
|
+
<span className="lineage-context-menu__action-copy">
|
|
186
|
+
<span className="lineage-context-menu__action-title">
|
|
187
|
+
<FormattedMessage
|
|
188
|
+
id="lineage.context_menu.quality_rules"
|
|
189
|
+
defaultMessage="View Quality Rules"
|
|
190
|
+
/>
|
|
191
|
+
</span>
|
|
192
|
+
</span>
|
|
193
|
+
</Link>
|
|
194
|
+
) : null}
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
);
|
|
198
|
+
};
|
|
75
199
|
|
|
76
200
|
LineageContextMenu.propTypes = {
|
|
77
201
|
createGraph: PropTypes.func,
|
|
@@ -79,6 +203,9 @@ LineageContextMenu.propTypes = {
|
|
|
79
203
|
id: PropTypes.string,
|
|
80
204
|
levels: PropTypes.number,
|
|
81
205
|
name: PropTypes.string,
|
|
206
|
+
onAction: PropTypes.func,
|
|
207
|
+
onTogglePin: PropTypes.func,
|
|
208
|
+
pinned: PropTypes.bool,
|
|
82
209
|
resource: PropTypes.bool,
|
|
83
210
|
structureId: PropTypes.number,
|
|
84
211
|
};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import _ from "lodash/fp";
|
|
2
2
|
import PropTypes from "prop-types";
|
|
3
3
|
import { connect } from "react-redux";
|
|
4
|
-
import { Button, Popup
|
|
4
|
+
import { Button, Popup } from "semantic-ui-react";
|
|
5
|
+
import { Loader2, Download } from "lucide-react";
|
|
5
6
|
import { useIntl } from "react-intl";
|
|
6
7
|
import { downloadCsvGraph } from "../routines";
|
|
7
8
|
|
|
@@ -26,6 +27,7 @@ export const LineageDownloadButton = ({
|
|
|
26
27
|
content,
|
|
27
28
|
compact = false,
|
|
28
29
|
labelPosition = "left",
|
|
30
|
+
tooltipPosition = "top right",
|
|
29
31
|
size,
|
|
30
32
|
}) => {
|
|
31
33
|
const { formatMessage } = useIntl();
|
|
@@ -40,11 +42,12 @@ export const LineageDownloadButton = ({
|
|
|
40
42
|
return (
|
|
41
43
|
!graphLoading && (
|
|
42
44
|
<Popup
|
|
43
|
-
position=
|
|
45
|
+
position={tooltipPosition}
|
|
44
46
|
content={messagePopup}
|
|
45
47
|
trigger={
|
|
46
48
|
<span>
|
|
47
49
|
<Button
|
|
50
|
+
aria-label={content || messagePopup}
|
|
48
51
|
basic={basic}
|
|
49
52
|
className={className}
|
|
50
53
|
compact={compact}
|
|
@@ -60,10 +63,16 @@ export const LineageDownloadButton = ({
|
|
|
60
63
|
secondary={!basic}
|
|
61
64
|
size={size}
|
|
62
65
|
>
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
{csvGraphDownloading ? (
|
|
67
|
+
<Loader2
|
|
68
|
+
aria-hidden="true"
|
|
69
|
+
className="lineage-graph__dock-download-spinner"
|
|
70
|
+
size={20}
|
|
71
|
+
strokeWidth={2}
|
|
72
|
+
/>
|
|
73
|
+
) : (
|
|
74
|
+
<Download aria-hidden="true" size={20} strokeWidth={2} />
|
|
75
|
+
)}
|
|
67
76
|
</Button>
|
|
68
77
|
</span>
|
|
69
78
|
}
|
|
@@ -83,6 +92,7 @@ LineageDownloadButton.propTypes = {
|
|
|
83
92
|
graphLoading: PropTypes.bool,
|
|
84
93
|
downloadCsvGraph: PropTypes.func,
|
|
85
94
|
labelPosition: PropTypes.string,
|
|
95
|
+
tooltipPosition: PropTypes.string,
|
|
86
96
|
size: PropTypes.string,
|
|
87
97
|
};
|
|
88
98
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import React, { useRef, useState } from "react";
|
|
2
2
|
import PropTypes from "prop-types";
|
|
3
|
-
import { useNavigate } from "react-router";
|
|
3
|
+
import { useNavigate, useParams } from "react-router";
|
|
4
4
|
import { connect } from "react-redux";
|
|
5
5
|
import { FormattedMessage, useIntl } from "react-intl";
|
|
6
6
|
import { Icon, Popup } from "semantic-ui-react";
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
isCompletedStatus,
|
|
11
11
|
isFailedStatus,
|
|
12
12
|
lineageEventType,
|
|
13
|
+
graphIdentity,
|
|
13
14
|
graphLabel,
|
|
14
15
|
graphScope,
|
|
15
16
|
graphDomain,
|
|
@@ -18,6 +19,7 @@ import {
|
|
|
18
19
|
graphTypeIcon,
|
|
19
20
|
eventErrorMessage,
|
|
20
21
|
eventTimestamp,
|
|
22
|
+
groupLineageEvents,
|
|
21
23
|
} from "./lineageEventHelpers";
|
|
22
24
|
|
|
23
25
|
export const LineageEventsPanel = ({
|
|
@@ -27,7 +29,13 @@ export const LineageEventsPanel = ({
|
|
|
27
29
|
}) => {
|
|
28
30
|
const { formatMessage } = useIntl();
|
|
29
31
|
const navigate = useNavigate();
|
|
30
|
-
const
|
|
32
|
+
const { id: activeGraphId } = useParams();
|
|
33
|
+
const [copiedId, setCopiedId] = useState(null);
|
|
34
|
+
const copiedTimer = useRef(null);
|
|
35
|
+
const dedupedEvents = groupLineageEvents(lineageEvents).flatMap((group) =>
|
|
36
|
+
Object.values(group.eventsByType),
|
|
37
|
+
);
|
|
38
|
+
const sortedEvents = [...dedupedEvents].sort(
|
|
31
39
|
(a, b) => eventTimestamp(b) - eventTimestamp(a),
|
|
32
40
|
);
|
|
33
41
|
|
|
@@ -59,6 +67,21 @@ export const LineageEventsPanel = ({
|
|
|
59
67
|
const scope = graphScope(event.graph_data);
|
|
60
68
|
const domain = graphDomain(event);
|
|
61
69
|
const firstLevel = graphFirstLevel(event);
|
|
70
|
+
const title = graphLabel(event, index);
|
|
71
|
+
const identity = graphIdentity(event, index);
|
|
72
|
+
|
|
73
|
+
const handleCopyError = (e) => {
|
|
74
|
+
e.stopPropagation();
|
|
75
|
+
const text = errorMessage || "";
|
|
76
|
+
navigator.clipboard.writeText(text).then(() => {
|
|
77
|
+
setCopiedId(event.graph_id || event.id);
|
|
78
|
+
clearTimeout(copiedTimer.current);
|
|
79
|
+
copiedTimer.current = setTimeout(
|
|
80
|
+
() => setCopiedId(null),
|
|
81
|
+
2000,
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
};
|
|
62
85
|
|
|
63
86
|
const handleCardClick = () => {
|
|
64
87
|
if (onGraphLoad) onGraphLoad();
|
|
@@ -75,7 +98,13 @@ export const LineageEventsPanel = ({
|
|
|
75
98
|
return (
|
|
76
99
|
<article
|
|
77
100
|
className={`lineage-events-panel__item${
|
|
101
|
+
type ? ` lineage-events-panel__item--${type}` : ""
|
|
102
|
+
}${
|
|
78
103
|
canLoadGraph ? " lineage-events-panel__item--clickable" : ""
|
|
104
|
+
}${
|
|
105
|
+
String(event.graph_id) === String(activeGraphId)
|
|
106
|
+
? " lineage-events-panel__item--active"
|
|
107
|
+
: ""
|
|
79
108
|
}`}
|
|
80
109
|
key={event.graph_id || event.id || index}
|
|
81
110
|
onClick={canLoadGraph ? handleCardClick : undefined}
|
|
@@ -91,7 +120,7 @@ export const LineageEventsPanel = ({
|
|
|
91
120
|
<div className="lineage-events-panel__item-header">
|
|
92
121
|
<div className="lineage-events-panel__item-title">
|
|
93
122
|
<Icon name="code branch" />
|
|
94
|
-
<span>{
|
|
123
|
+
<span title={title}>{title}</span>
|
|
95
124
|
</div>
|
|
96
125
|
<div
|
|
97
126
|
className={`lineage-events-panel__card-status${
|
|
@@ -104,36 +133,56 @@ export const LineageEventsPanel = ({
|
|
|
104
133
|
: ""
|
|
105
134
|
}`}
|
|
106
135
|
>
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
136
|
+
<Popup
|
|
137
|
+
on="hover"
|
|
138
|
+
role="tooltip"
|
|
139
|
+
aria-label={formatMessage({
|
|
140
|
+
id: "lineage.events.panel.graph.id",
|
|
141
|
+
})}
|
|
142
|
+
position="top center"
|
|
143
|
+
wide="very"
|
|
144
|
+
className="lineage-events-panel__status-popup"
|
|
145
|
+
trigger={
|
|
146
|
+
failed ? (
|
|
113
147
|
<button
|
|
114
148
|
aria-label={formatMessage({
|
|
115
149
|
id: "lineage.events.panel.error",
|
|
116
150
|
})}
|
|
117
151
|
className="lineage-events-panel__card-status-trigger"
|
|
118
152
|
type="button"
|
|
153
|
+
onClick={handleCopyError}
|
|
119
154
|
>
|
|
120
155
|
<Icon name="warning circle" />
|
|
121
|
-
|
|
156
|
+
{copiedId === (event.graph_id || event.id)
|
|
157
|
+
? "Copiado ✓"
|
|
158
|
+
: "KO"}
|
|
122
159
|
</button>
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
160
|
+
) : (
|
|
161
|
+
<span className="lineage-events-panel__card-status-trigger">
|
|
162
|
+
<Icon
|
|
163
|
+
name={
|
|
164
|
+
completed ? "check circle" : "circle notched"
|
|
165
|
+
}
|
|
166
|
+
/>
|
|
167
|
+
{completed ? "OK" : "..."}
|
|
168
|
+
</span>
|
|
169
|
+
)
|
|
170
|
+
}
|
|
171
|
+
>
|
|
172
|
+
<span className="lineage-events-panel__status-popup-id">
|
|
173
|
+
{identity}
|
|
135
174
|
</span>
|
|
136
|
-
|
|
175
|
+
{failed ? (
|
|
176
|
+
<>
|
|
177
|
+
<div className="lineage-events-panel__status-popup-divider" />
|
|
178
|
+
<div className="lineage-events-panel__status-popup-error">
|
|
179
|
+
{errorMessage || (
|
|
180
|
+
<FormattedMessage id="lineage.events.panel.error.empty" />
|
|
181
|
+
)}
|
|
182
|
+
</div>
|
|
183
|
+
</>
|
|
184
|
+
) : null}
|
|
185
|
+
</Popup>
|
|
137
186
|
</div>
|
|
138
187
|
</div>
|
|
139
188
|
{domain || firstLevel ? (
|
|
@@ -152,14 +201,18 @@ export const LineageEventsPanel = ({
|
|
|
152
201
|
) : null}
|
|
153
202
|
</div>
|
|
154
203
|
) : null}
|
|
155
|
-
{scope ? (
|
|
204
|
+
{scope && scope !== title ? (
|
|
156
205
|
<p className="lineage-events-panel__scope">{scope}</p>
|
|
157
206
|
) : null}
|
|
158
207
|
<div className="lineage-events-panel__row">
|
|
159
208
|
<div className="lineage-events-panel__row-main">
|
|
160
209
|
<div className="lineage-events-panel__type">
|
|
161
|
-
|
|
162
|
-
|
|
210
|
+
{type ? (
|
|
211
|
+
<>
|
|
212
|
+
<Icon name={graphTypeIcon(type)} />
|
|
213
|
+
<FormattedMessage id={graphTypeMessageId(type)} />
|
|
214
|
+
</>
|
|
215
|
+
) : null}
|
|
163
216
|
</div>
|
|
164
217
|
<div className="lineage-events-panel__date">
|
|
165
218
|
{event.inserted_at ? (
|
|
@@ -10,6 +10,7 @@ import { columnDecorator } from "@truedat/core/services";
|
|
|
10
10
|
import { linkTo } from "@truedat/core/routes";
|
|
11
11
|
import { lineageEventColumns as columns } from "../selectors/getLineageEventColumns";
|
|
12
12
|
import { fetchLineageEvents } from "../routines";
|
|
13
|
+
import { groupLineageEvents, eventTimestamp } from "./lineageEventHelpers";
|
|
13
14
|
|
|
14
15
|
export const LineageEventsTable = ({
|
|
15
16
|
fetchLineageEvents,
|
|
@@ -18,6 +19,9 @@ export const LineageEventsTable = ({
|
|
|
18
19
|
}) => {
|
|
19
20
|
const { formatMessage } = useIntl();
|
|
20
21
|
const navigate = useNavigate();
|
|
22
|
+
const dedupedEvents = groupLineageEvents(lineageEvents)
|
|
23
|
+
.flatMap((group) => Object.values(group.eventsByType))
|
|
24
|
+
.sort((a, b) => eventTimestamp(b) - eventTimestamp(a));
|
|
21
25
|
|
|
22
26
|
useEffect(() => {
|
|
23
27
|
fetchLineageEvents();
|
|
@@ -25,7 +29,7 @@ export const LineageEventsTable = ({
|
|
|
25
29
|
|
|
26
30
|
return (
|
|
27
31
|
<>
|
|
28
|
-
{!_.isEmpty(
|
|
32
|
+
{!_.isEmpty(dedupedEvents) && (
|
|
29
33
|
<Table sortable selectable>
|
|
30
34
|
<Table.Header>
|
|
31
35
|
<Table.Row>
|
|
@@ -44,7 +48,7 @@ export const LineageEventsTable = ({
|
|
|
44
48
|
</Table.Row>
|
|
45
49
|
</Table.Header>
|
|
46
50
|
<Table.Body>
|
|
47
|
-
{
|
|
51
|
+
{dedupedEvents.map((event, i) => (
|
|
48
52
|
<Table.Row
|
|
49
53
|
key={i}
|
|
50
54
|
onClick={
|
|
@@ -66,7 +70,7 @@ export const LineageEventsTable = ({
|
|
|
66
70
|
</Table.Body>
|
|
67
71
|
</Table>
|
|
68
72
|
)}
|
|
69
|
-
{_.isEmpty(
|
|
73
|
+
{_.isEmpty(dedupedEvents) && !lineageEventsLoading && (
|
|
70
74
|
<Header as="h4">
|
|
71
75
|
<Icon name="search" />
|
|
72
76
|
<Header.Content>
|