diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/ingest/source/ListIngestionSourcesResolver.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/ingest/source/ListIngestionSourcesResolver.java index 15263adf35faad..2a53dd9d8ab015 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/ingest/source/ListIngestionSourcesResolver.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/ingest/source/ListIngestionSourcesResolver.java @@ -15,7 +15,9 @@ import com.linkedin.metadata.search.SearchResult; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; +import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.HashSet; import java.util.Map; import java.util.concurrent.CompletableFuture; @@ -66,15 +68,20 @@ public CompletableFuture get(final DataFetchingEnvir new HashSet<>(gmsResult.getEntities().stream() .map(SearchEntity::getEntity) .collect(Collectors.toList())), - ImmutableSet.of(Constants.INGESTION_INFO_ASPECT_NAME), + ImmutableSet.of(Constants.INGESTION_INFO_ASPECT_NAME, Constants.INGESTION_SOURCE_KEY_ASPECT_NAME), context.getAuthentication()); + final Collection sortedEntities = entities.values() + .stream() + .sorted(Comparator.comparingLong(s -> -s.getAspects().get(Constants.INGESTION_SOURCE_KEY_ASPECT_NAME).getCreated().getTime())) + .collect(Collectors.toList()); + // Now that we have entities we can bind this to a result. final ListIngestionSourcesResult result = new ListIngestionSourcesResult(); result.setStart(gmsResult.getFrom()); result.setCount(gmsResult.getPageSize()); result.setTotal(gmsResult.getNumEntities()); - result.setIngestionSources(IngestionResolverUtils.mapIngestionSources(entities.values())); + result.setIngestionSources(IngestionResolverUtils.mapIngestionSources(sortedEntities)); return result; } catch (Exception e) { diff --git a/datahub-graphql-core/src/test/java/com/linkedin/datahub/graphql/resolvers/ingest/source/ListIngestionSourceResolverTest.java b/datahub-graphql-core/src/test/java/com/linkedin/datahub/graphql/resolvers/ingest/source/ListIngestionSourceResolverTest.java index d47f37d0c5ce5c..07383b9c01224b 100644 --- a/datahub-graphql-core/src/test/java/com/linkedin/datahub/graphql/resolvers/ingest/source/ListIngestionSourceResolverTest.java +++ b/datahub-graphql-core/src/test/java/com/linkedin/datahub/graphql/resolvers/ingest/source/ListIngestionSourceResolverTest.java @@ -12,6 +12,7 @@ import com.linkedin.entity.client.EntityClient; import com.linkedin.ingestion.DataHubIngestionSourceInfo; import com.linkedin.metadata.Constants; +import com.linkedin.metadata.key.DataHubIngestionSourceKey; import com.linkedin.metadata.search.SearchEntity; import com.linkedin.metadata.search.SearchEntityArray; import com.linkedin.metadata.search.SearchResult; @@ -36,6 +37,8 @@ public void testGetSuccess() throws Exception { EntityClient mockClient = Mockito.mock(EntityClient.class); DataHubIngestionSourceInfo returnedInfo = getTestIngestionSourceInfo(); + final DataHubIngestionSourceKey key = new DataHubIngestionSourceKey(); + key.setId("test"); Mockito.when(mockClient.search( Mockito.eq(Constants.INGESTION_SOURCE_ENTITY_NAME), @@ -55,7 +58,7 @@ public void testGetSuccess() throws Exception { Mockito.when(mockClient.batchGetV2( Mockito.eq(Constants.INGESTION_SOURCE_ENTITY_NAME), Mockito.eq(new HashSet<>(ImmutableSet.of(TEST_INGESTION_SOURCE_URN))), - Mockito.eq(ImmutableSet.of(Constants.INGESTION_INFO_ASPECT_NAME)), + Mockito.eq(ImmutableSet.of(Constants.INGESTION_INFO_ASPECT_NAME, Constants.INGESTION_SOURCE_KEY_ASPECT_NAME)), Mockito.any(Authentication.class) )).thenReturn( ImmutableMap.of( @@ -65,7 +68,9 @@ public void testGetSuccess() throws Exception { .setUrn(TEST_INGESTION_SOURCE_URN) .setAspects(new EnvelopedAspectMap(ImmutableMap.of( Constants.INGESTION_INFO_ASPECT_NAME, - new EnvelopedAspect().setValue(new Aspect(returnedInfo.data())) + new EnvelopedAspect().setValue(new Aspect(returnedInfo.data())), + Constants.INGESTION_SOURCE_KEY_ASPECT_NAME, + new EnvelopedAspect().setValue(new Aspect(key.data())) ))) ) ); diff --git a/datahub-web-react/src/app/ingest/source/IngestionSourceTable.tsx b/datahub-web-react/src/app/ingest/source/IngestionSourceTable.tsx index 1e5254dcbe4a7a..40344f1c072f12 100644 --- a/datahub-web-react/src/app/ingest/source/IngestionSourceTable.tsx +++ b/datahub-web-react/src/app/ingest/source/IngestionSourceTable.tsx @@ -49,12 +49,14 @@ function IngestionSourceTable({ dataIndex: 'type', key: 'type', render: TypeColumn, + sorter: (sourceA, sourceB) => sourceA.type.localeCompare(sourceB.type), }, { title: 'Name', dataIndex: 'name', key: 'name', render: (name: string) => name || '', + sorter: (sourceA, sourceB) => sourceA.name.localeCompare(sourceB.name), }, { title: 'Schedule', @@ -67,12 +69,14 @@ function IngestionSourceTable({ dataIndex: 'execCount', key: 'execCount', render: (execCount: any) => {execCount || '0'}, + sorter: (sourceA, sourceB) => sourceA.execCount - sourceB.execCount, }, { title: 'Last Execution', dataIndex: 'lastExecTime', key: 'lastExecTime', render: LastExecutionColumn, + sorter: (sourceA, sourceB) => sourceA.lastExecTime - sourceB.lastExecTime, }, { title: 'Last Status', @@ -81,6 +85,7 @@ function IngestionSourceTable({ render: (status: any, record) => ( ), + sorter: (sourceA, sourceB) => (sourceA.lastExecStatus || '').localeCompare(sourceB.lastExecStatus || ''), }, { title: '',