1
1
import { DeliveredProcedureOutlined } from '@ant-design/icons' ;
2
- import { Button , Pagination , Table , Tooltip , Typography } from 'antd' ;
3
- import ButtonGroup from 'antd/lib/button/button-group' ;
2
+ import { Pagination , Table , Tooltip , Typography } from 'antd' ;
4
3
import React , { useState } from 'react' ;
5
4
import styled from 'styled-components' ;
6
5
7
- import { useGetDatasetRunsQuery } from '../../../../graphql/dataset.generated' ;
6
+ import { GetDatasetRunsQuery , useGetDatasetRunsQuery } from '../../../../graphql/dataset.generated' ;
8
7
import {
9
8
DataProcessInstanceRunResultType ,
10
9
DataProcessRunStatus ,
10
+ EntityType ,
11
11
RelationshipDirection ,
12
12
} from '../../../../types.generated' ;
13
13
import {
@@ -20,6 +20,8 @@ import { ANTD_GRAY } from '../../shared/constants';
20
20
import { useEntityData } from '../../shared/EntityContext' ;
21
21
import LoadingSvg from '../../../../images/datahub-logo-color-loading_pendulum.svg?react' ;
22
22
import { scrollToTop } from '../../../shared/searchUtils' ;
23
+ import { formatDuration } from '../../../shared/formatDuration' ;
24
+ import { notEmpty } from '../../shared/utils' ;
23
25
24
26
const ExternalUrlLink = styled . a `
25
27
font-size: 16px;
@@ -32,10 +34,6 @@ const PaginationControlContainer = styled.div`
32
34
text-align: center;
33
35
` ;
34
36
35
- const ReadWriteButtonGroup = styled ( ButtonGroup ) `
36
- padding: 12px;
37
- ` ;
38
-
39
37
const LoadingText = styled . div `
40
38
margin-top: 18px;
41
39
font-size: 12px;
@@ -67,6 +65,12 @@ const columns = [
67
65
< Tooltip title = { new Date ( Number ( value ) ) . toUTCString ( ) } > { new Date ( Number ( value ) ) . toLocaleString ( ) } </ Tooltip >
68
66
) ,
69
67
} ,
68
+ {
69
+ title : 'Duration' ,
70
+ dataIndex : 'duration' ,
71
+ key : 'duration' ,
72
+ render : ( durationMs : number ) => formatDuration ( durationMs ) ,
73
+ } ,
70
74
{
71
75
title : 'Run ID' ,
72
76
dataIndex : 'name' ,
@@ -129,14 +133,59 @@ const columns = [
129
133
const PAGE_SIZE = 20 ;
130
134
131
135
export const OperationsTab = ( ) => {
132
- const { urn } = useEntityData ( ) ;
136
+ const { urn, entityData } = useEntityData ( ) ;
133
137
const [ page , setPage ] = useState ( 1 ) ;
134
- const [ direction , setDirection ] = useState ( RelationshipDirection . Incoming ) ;
135
138
136
- const { loading, data } = useGetDatasetRunsQuery ( {
137
- variables : { urn, start : ( page - 1 ) * PAGE_SIZE , count : PAGE_SIZE , direction } ,
139
+ // Fetch data across all siblings.
140
+ const allUrns = [ urn , ...( entityData ?. siblings ?. siblings || [ ] ) . map ( ( sibling ) => sibling ?. urn ) . filter ( notEmpty ) ] ;
141
+ const loadings : boolean [ ] = [ ] ;
142
+ const datas : GetDatasetRunsQuery [ ] = [ ] ;
143
+ allUrns . forEach ( ( entityUrn ) => {
144
+ // Because there's a consistent number and order of the urns,
145
+ // this usage of a hook within a loop should be safe.
146
+ // eslint-disable-next-line react-hooks/rules-of-hooks
147
+ const { loading, data } = useGetDatasetRunsQuery ( {
148
+ variables : {
149
+ urn : entityUrn ,
150
+ start : ( page - 1 ) * PAGE_SIZE ,
151
+ count : PAGE_SIZE ,
152
+ direction : RelationshipDirection . Outgoing ,
153
+ } ,
154
+ } ) ;
155
+ loadings . push ( loading ) ;
156
+ if ( data ) {
157
+ datas . push ( data ) ;
158
+ }
138
159
} ) ;
139
- const runs = data && data ?. dataset ?. runs ?. runs ;
160
+
161
+ const loading = loadings . some ( ( loadingEntry ) => loadingEntry ) ;
162
+
163
+ // Merge the runs data from all entities.
164
+ // If there's more than one entity contributing to the data, then we can't do pagination.
165
+ let canPaginate = true ;
166
+ let dataRuns : NonNullable < GetDatasetRunsQuery [ 'dataset' ] > [ 'runs' ] | undefined ;
167
+ if ( datas . length > 0 ) {
168
+ let numWithRuns = 0 ;
169
+ for ( let i = 0 ; i < datas . length ; i ++ ) {
170
+ if ( datas [ i ] ?. dataset ?. runs ?. total ) {
171
+ numWithRuns ++ ;
172
+ }
173
+
174
+ if ( dataRuns && dataRuns . runs ) {
175
+ dataRuns . runs . push ( ...( datas [ i ] ?. dataset ?. runs ?. runs || [ ] ) ) ;
176
+ dataRuns . total = ( dataRuns . total ?? 0 ) + ( datas [ i ] ?. dataset ?. runs ?. total ?? 0 ) ;
177
+ } else {
178
+ dataRuns = JSON . parse ( JSON . stringify ( datas [ i ] ?. dataset ?. runs ) ) ;
179
+ }
180
+ }
181
+
182
+ if ( numWithRuns > 1 ) {
183
+ canPaginate = false ;
184
+ }
185
+ }
186
+
187
+ // This also sorts the runs data across all entities.
188
+ const runs = dataRuns ?. runs ?. sort ( ( a , b ) => ( b ?. created ?. time ?? 0 ) - ( a ?. created ?. time ?? 0 ) ) ;
140
189
141
190
const tableData = runs
142
191
?. filter ( ( run ) => run )
@@ -145,33 +194,27 @@ export const OperationsTab = () => {
145
194
name : run ?. name ,
146
195
status : run ?. state ?. [ 0 ] ?. status ,
147
196
resultType : run ?. state ?. [ 0 ] ?. result ?. resultType ,
197
+ duration : run ?. state ?. [ 0 ] ?. durationMillis ,
148
198
inputs : run ?. inputs ?. relationships . map ( ( relationship ) => relationship . entity ) ,
149
199
outputs : run ?. outputs ?. relationships . map ( ( relationship ) => relationship . entity ) ,
150
200
externalUrl : run ?. externalUrl ,
151
201
parentTemplate : run ?. parentTemplate ?. relationships ?. [ 0 ] ?. entity ,
152
202
} ) ) ;
153
203
204
+ // If the table contains jobs, we need to show the job-related columns. Otherwise we can simplify the table.
205
+ const containsJobs = tableData ?. some ( ( run ) => run . parentTemplate ?. type !== EntityType . Dataset ) ;
206
+ const simplifiedColumns = containsJobs
207
+ ? columns
208
+ : columns . filter ( ( column ) => ! [ 'name' , 'inputs' , 'outputs' ] . includes ( column . key ) ) ;
209
+
154
210
const onChangePage = ( newPage : number ) => {
155
211
scrollToTop ( ) ;
156
212
setPage ( newPage ) ;
157
213
} ;
158
214
215
+ // TODO: Much of this file is duplicated from RunsTab.tsx. We should refactor this to share code.
159
216
return (
160
217
< >
161
- < ReadWriteButtonGroup >
162
- < Button
163
- type = { direction === RelationshipDirection . Incoming ? 'primary' : 'default' }
164
- onClick = { ( ) => setDirection ( RelationshipDirection . Incoming ) }
165
- >
166
- Reads
167
- </ Button >
168
- < Button
169
- type = { direction === RelationshipDirection . Outgoing ? 'primary' : 'default' }
170
- onClick = { ( ) => setDirection ( RelationshipDirection . Outgoing ) }
171
- >
172
- Writes
173
- </ Button >
174
- </ ReadWriteButtonGroup >
175
218
{ loading && (
176
219
< LoadingContainer >
177
220
< LoadingSvg height = { 80 } width = { 80 } />
@@ -180,17 +223,19 @@ export const OperationsTab = () => {
180
223
) }
181
224
{ ! loading && (
182
225
< >
183
- < Table dataSource = { tableData } columns = { columns } pagination = { false } />
184
- < PaginationControlContainer >
185
- < Pagination
186
- current = { page }
187
- pageSize = { PAGE_SIZE }
188
- total = { data ?. dataset ?. runs ?. total || 0 }
189
- showLessItems
190
- onChange = { onChangePage }
191
- showSizeChanger = { false }
192
- />
193
- </ PaginationControlContainer >
226
+ < Table dataSource = { tableData } columns = { simplifiedColumns } pagination = { false } />
227
+ { canPaginate && (
228
+ < PaginationControlContainer >
229
+ < Pagination
230
+ current = { page }
231
+ pageSize = { PAGE_SIZE }
232
+ total = { dataRuns ?. total || 0 }
233
+ showLessItems
234
+ onChange = { onChangePage }
235
+ showSizeChanger = { false }
236
+ />
237
+ </ PaginationControlContainer >
238
+ ) }
194
239
</ >
195
240
) }
196
241
</ >
0 commit comments