From f18f619a2cb70a15bfe1a0458f623b9b2017cdc7 Mon Sep 17 00:00:00 2001 From: John Joyce Date: Mon, 16 May 2022 13:24:56 -0700 Subject: [PATCH] feat(ui): Adding Search Bar to all List Views (groups, users, domains, policies, ingestion) (#4919) --- .../resolvers/domain/ListDomainsResolver.java | 22 +++++++---- .../resolvers/group/ListGroupsResolver.java | 19 +++++---- .../ingest/secret/ListSecretsResolver.java | 19 ++++++--- .../source/ListIngestionSourcesResolver.java | 19 ++++++--- .../policy/ListPoliciesResolver.java | 4 +- .../resolvers/user/ListUsersResolver.java | 20 ++++++---- .../src/main/resources/entity.graphql | 20 ++++++++++ .../src/main/resources/ingestion.graphql | 10 +++++ .../domain/ListDomainsResolverTest.java | 26 +++++++------ .../secret/ListSecretsResolverTest.java | 23 ++++++----- .../ListIngestionSourceResolverTest.java | 23 ++++++----- .../src/app/domain/DomainsList.tsx | 30 +++++++++++++- .../src/app/identity/group/GroupList.tsx | 30 +++++++++++++- .../src/app/identity/user/UserList.tsx | 36 ++++++++++++++++- .../src/app/ingest/secret/SecretsList.tsx | 30 +++++++++++++- .../app/ingest/source/IngestionSourceList.tsx | 30 +++++++++++++- .../src/app/policy/PoliciesPage.tsx | 39 +++++++++++++++++-- .../src/app/search/SearchBar.tsx | 3 +- .../com/linkedin/policy/DataHubPolicyInfo.pdl | 5 ++- .../datahub/authorization/PolicyFetcher.java | 7 +++- .../authorization/DataHubAuthorizerTest.java | 4 +- 21 files changed, 340 insertions(+), 79 deletions(-) diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/domain/ListDomainsResolver.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/domain/ListDomainsResolver.java index 88c6b137fb..0763874498 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/domain/ListDomainsResolver.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/domain/ListDomainsResolver.java @@ -1,6 +1,5 @@ package com.linkedin.datahub.graphql.resolvers.domain; -import com.linkedin.common.UrnArray; import com.linkedin.common.urn.Urn; import com.linkedin.datahub.graphql.QueryContext; import com.linkedin.datahub.graphql.authorization.AuthorizationUtils; @@ -11,13 +10,15 @@ import com.linkedin.datahub.graphql.generated.ListDomainsInput; import com.linkedin.datahub.graphql.generated.ListDomainsResult; import com.linkedin.entity.client.EntityClient; import com.linkedin.metadata.Constants; -import com.linkedin.metadata.query.ListResult; +import com.linkedin.metadata.search.SearchEntity; +import com.linkedin.metadata.search.SearchResult; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; import static com.linkedin.datahub.graphql.resolvers.ResolverUtils.*; @@ -29,6 +30,7 @@ public class ListDomainsResolver implements DataFetcher mapUnresolvedDomains(final UrnArray entityUrns) { + private List mapUnresolvedDomains(final List entityUrns) { final List results = new ArrayList<>(); for (final Urn urn : entityUrns) { final Domain unresolvedDomain = new Domain(); diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/group/ListGroupsResolver.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/group/ListGroupsResolver.java index ea8a28e324..0715f6dba9 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/group/ListGroupsResolver.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/group/ListGroupsResolver.java @@ -10,7 +10,8 @@ import com.linkedin.datahub.graphql.generated.ListGroupsResult; import com.linkedin.datahub.graphql.types.corpgroup.mappers.CorpGroupMapper; import com.linkedin.entity.EntityResponse; import com.linkedin.entity.client.EntityClient; -import com.linkedin.metadata.query.ListResult; +import com.linkedin.metadata.search.SearchEntity; +import com.linkedin.metadata.search.SearchResult; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import java.util.Collection; @@ -29,6 +30,7 @@ public class ListGroupsResolver implements DataFetcher { try { // First, get all group Urns. - final ListResult gmsResult = - _entityClient.list(CORP_GROUP_ENTITY_NAME, Collections.emptyMap(), start, count, context.getAuthentication()); + final SearchResult gmsResult = + _entityClient.search(CORP_GROUP_ENTITY_NAME, query, Collections.emptyMap(), start, count, context.getAuthentication()); // Then, get hydrate all groups. final Map entities = _entityClient.batchGetV2(CORP_GROUP_ENTITY_NAME, - new HashSet<>(gmsResult.getEntities()), null, context.getAuthentication()); + new HashSet<>(gmsResult.getEntities().stream() + .map(SearchEntity::getEntity) + .collect(Collectors.toList())), null, context.getAuthentication()); // Now that we have entities we can bind this to a result. final ListGroupsResult result = new ListGroupsResult(); - result.setStart(gmsResult.getStart()); - result.setCount(gmsResult.getCount()); - result.setTotal(gmsResult.getTotal()); + result.setStart(gmsResult.getFrom()); + result.setCount(gmsResult.getPageSize()); + result.setTotal(gmsResult.getNumEntities()); result.setGroups(mapEntities(entities.values())); return result; } catch (Exception e) { diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/ingest/secret/ListSecretsResolver.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/ingest/secret/ListSecretsResolver.java index 410a6b5bca..15c924b5ea 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/ingest/secret/ListSecretsResolver.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/ingest/secret/ListSecretsResolver.java @@ -14,7 +14,8 @@ import com.linkedin.entity.EnvelopedAspect; import com.linkedin.entity.EnvelopedAspectMap; import com.linkedin.entity.client.EntityClient; import com.linkedin.metadata.Constants; -import com.linkedin.metadata.query.ListResult; +import com.linkedin.metadata.search.SearchEntity; +import com.linkedin.metadata.search.SearchResult; import com.linkedin.secret.DataHubSecretValue; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; @@ -25,6 +26,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import static com.linkedin.datahub.graphql.resolvers.ResolverUtils.*; @@ -38,6 +40,7 @@ public class ListSecretsResolver implements DataFetcher { try { // First, get all secrets - final ListResult gmsResult = _entityClient.list(Constants.SECRETS_ENTITY_NAME, Collections.emptyMap(), start, count, context.getAuthentication()); + final SearchResult + gmsResult = _entityClient.search(Constants.SECRETS_ENTITY_NAME, query, Collections.emptyMap(), start, count, context.getAuthentication()); // Then, resolve all secrets final Map entities = _entityClient.batchGetV2( Constants.SECRETS_ENTITY_NAME, - new HashSet<>(gmsResult.getEntities()), + new HashSet<>(gmsResult.getEntities().stream() + .map(SearchEntity::getEntity) + .collect(Collectors.toList())), ImmutableSet.of(Constants.SECRET_VALUE_ASPECT_NAME), context.getAuthentication()); // Now that we have entities we can bind this to a result. final ListSecretsResult result = new ListSecretsResult(); - result.setStart(gmsResult.getStart()); - result.setCount(gmsResult.getCount()); - result.setTotal(gmsResult.getTotal()); + result.setStart(gmsResult.getFrom()); + result.setCount(gmsResult.getPageSize()); + result.setTotal(gmsResult.getNumEntities()); result.setSecrets(mapEntities(entities.values())); return result; 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 cccdcb79cb..15263adf35 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 @@ -11,13 +11,15 @@ import com.linkedin.datahub.graphql.resolvers.ingest.IngestionResolverUtils; import com.linkedin.entity.EntityResponse; import com.linkedin.entity.client.EntityClient; import com.linkedin.metadata.Constants; -import com.linkedin.metadata.query.ListResult; +import com.linkedin.metadata.search.SearchEntity; +import com.linkedin.metadata.search.SearchResult; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; import static com.linkedin.datahub.graphql.resolvers.ResolverUtils.*; @@ -28,6 +30,7 @@ public class ListIngestionSourcesResolver implements DataFetcher { try { // First, get all ingestion sources Urns. - final ListResult gmsResult = _entityClient.list( + final SearchResult gmsResult = _entityClient.search( Constants.INGESTION_SOURCE_ENTITY_NAME, + query, Collections.emptyMap(), start, count, @@ -58,15 +63,17 @@ public class ListIngestionSourcesResolver implements DataFetcher entities = _entityClient.batchGetV2( Constants.INGESTION_SOURCE_ENTITY_NAME, - new HashSet<>(gmsResult.getEntities()), + new HashSet<>(gmsResult.getEntities().stream() + .map(SearchEntity::getEntity) + .collect(Collectors.toList())), ImmutableSet.of(Constants.INGESTION_INFO_ASPECT_NAME), context.getAuthentication()); // Now that we have entities we can bind this to a result. final ListIngestionSourcesResult result = new ListIngestionSourcesResult(); - result.setStart(gmsResult.getStart()); - result.setCount(gmsResult.getCount()); - result.setTotal(gmsResult.getTotal()); + result.setStart(gmsResult.getFrom()); + result.setCount(gmsResult.getPageSize()); + result.setTotal(gmsResult.getNumEntities()); result.setIngestionSources(IngestionResolverUtils.mapIngestionSources(entities.values())); return result; diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/policy/ListPoliciesResolver.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/policy/ListPoliciesResolver.java index fe8a962c9d..516d6fa2d3 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/policy/ListPoliciesResolver.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/policy/ListPoliciesResolver.java @@ -21,6 +21,7 @@ public class ListPoliciesResolver implements DataFetcher { try { // First, get all policy Urns. final PolicyFetcher.PolicyFetchResult policyFetchResult = - _policyFetcher.fetchPolicies(start, count, context.getAuthentication()); + _policyFetcher.fetchPolicies(start, count, query, context.getAuthentication()); // Now that we have entities we can bind this to a result. final ListPoliciesResult result = new ListPoliciesResult(); diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/user/ListUsersResolver.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/user/ListUsersResolver.java index e2d540ef19..32cbb2251f 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/user/ListUsersResolver.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/user/ListUsersResolver.java @@ -10,7 +10,8 @@ import com.linkedin.datahub.graphql.generated.ListUsersResult; import com.linkedin.datahub.graphql.types.corpuser.mappers.CorpUserMapper; import com.linkedin.entity.EntityResponse; import com.linkedin.entity.client.EntityClient; -import com.linkedin.metadata.query.ListResult; +import com.linkedin.metadata.search.SearchEntity; +import com.linkedin.metadata.search.SearchResult; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import java.util.Collection; @@ -29,6 +30,7 @@ public class ListUsersResolver implements DataFetcher { try { // First, get all policy Urns. - final ListResult gmsResult = - _entityClient.list(CORP_USER_ENTITY_NAME, Collections.emptyMap(), start, count, context.getAuthentication()); + final SearchResult gmsResult = + _entityClient.search(CORP_USER_ENTITY_NAME, query, Collections.emptyMap(), start, count, context.getAuthentication()); // Then, get hydrate all users. final Map entities = _entityClient.batchGetV2(CORP_USER_ENTITY_NAME, - new HashSet<>(gmsResult.getEntities()), null, context.getAuthentication()); + new HashSet<>(gmsResult.getEntities().stream() + .map(SearchEntity::getEntity) + .collect(Collectors.toList()) + ), null, context.getAuthentication()); // Now that we have entities we can bind this to a result. final ListUsersResult result = new ListUsersResult(); - result.setStart(gmsResult.getStart()); - result.setCount(gmsResult.getCount()); - result.setTotal(gmsResult.getTotal()); + result.setStart(gmsResult.getFrom()); + result.setCount(gmsResult.getPageSize()); + result.setTotal(gmsResult.getNumEntities()); result.setUsers(mapEntities(entities.values())); return result; } catch (Exception e) { diff --git a/datahub-graphql-core/src/main/resources/entity.graphql b/datahub-graphql-core/src/main/resources/entity.graphql index da2d2d4a07..fae40bf078 100644 --- a/datahub-graphql-core/src/main/resources/entity.graphql +++ b/datahub-graphql-core/src/main/resources/entity.graphql @@ -6336,6 +6336,11 @@ input ListPoliciesInput { The maximum number of Policies to be returned in the result set """ count: Int + + """ + Optional search query + """ + query: String } """ @@ -6416,6 +6421,11 @@ input ListUsersInput { The maximum number of Policies to be returned in the result set """ count: Int + + """ + Optional search query + """ + query: String } """ @@ -6456,6 +6466,11 @@ input ListGroupsInput { The maximum number of Policies to be returned in the result set """ count: Int + + """ + Optional search query + """ + query: String } type EntityCountResults { @@ -7577,6 +7592,11 @@ input ListDomainsInput { The maximum number of Domains to be returned in the result set """ count: Int + + """ + Optional search query + """ + query: String } """ diff --git a/datahub-graphql-core/src/main/resources/ingestion.graphql b/datahub-graphql-core/src/main/resources/ingestion.graphql index 74f5783446..3bca513974 100644 --- a/datahub-graphql-core/src/main/resources/ingestion.graphql +++ b/datahub-graphql-core/src/main/resources/ingestion.graphql @@ -188,6 +188,11 @@ input ListSecretsInput { The number of results to be returned """ count: Int + + """ + An optional search query + """ + query: String } """ @@ -354,6 +359,11 @@ input ListIngestionSourcesInput { The number of results to be returned """ count: Int + + """ + An optional search query + """ + query: String } """ diff --git a/datahub-graphql-core/src/test/java/com/linkedin/datahub/graphql/resolvers/domain/ListDomainsResolverTest.java b/datahub-graphql-core/src/test/java/com/linkedin/datahub/graphql/resolvers/domain/ListDomainsResolverTest.java index cf0cfef218..615e23ed3e 100644 --- a/datahub-graphql-core/src/test/java/com/linkedin/datahub/graphql/resolvers/domain/ListDomainsResolverTest.java +++ b/datahub-graphql-core/src/test/java/com/linkedin/datahub/graphql/resolvers/domain/ListDomainsResolverTest.java @@ -2,13 +2,14 @@ package com.linkedin.datahub.graphql.resolvers.domain; import com.datahub.authentication.Authentication; import com.google.common.collect.ImmutableSet; -import com.linkedin.common.UrnArray; import com.linkedin.common.urn.Urn; import com.linkedin.datahub.graphql.QueryContext; import com.linkedin.datahub.graphql.generated.ListDomainsInput; import com.linkedin.entity.client.EntityClient; import com.linkedin.metadata.Constants; -import com.linkedin.metadata.query.ListResult; +import com.linkedin.metadata.search.SearchEntity; +import com.linkedin.metadata.search.SearchEntityArray; +import com.linkedin.metadata.search.SearchResult; import com.linkedin.r2.RemoteInvocationException; import graphql.schema.DataFetchingEnvironment; import java.util.Collections; @@ -25,7 +26,7 @@ public class ListDomainsResolverTest { private static final Urn TEST_DOMAIN_URN = Urn.createFromTuple("domain", "test-id"); private static final ListDomainsInput TEST_INPUT = new ListDomainsInput( - 0, 20 + 0, 20, null ); @Test @@ -33,18 +34,19 @@ public class ListDomainsResolverTest { // Create resolver EntityClient mockClient = Mockito.mock(EntityClient.class); - Mockito.when(mockClient.list( + Mockito.when(mockClient.search( Mockito.eq(Constants.DOMAIN_ENTITY_NAME), + Mockito.eq(""), Mockito.eq(Collections.emptyMap()), Mockito.eq(0), Mockito.eq(20), Mockito.any(Authentication.class) )).thenReturn( - new ListResult() - .setStart(0) - .setCount(1) - .setTotal(1) - .setEntities(new UrnArray(ImmutableSet.of(TEST_DOMAIN_URN))) + new SearchResult() + .setFrom(0) + .setPageSize(1) + .setNumEntities(1) + .setEntities(new SearchEntityArray(ImmutableSet.of(new SearchEntity().setEntity(TEST_DOMAIN_URN)))) ); ListDomainsResolver resolver = new ListDomainsResolver(mockClient); @@ -77,8 +79,9 @@ public class ListDomainsResolverTest { Mockito.when(mockEnv.getContext()).thenReturn(mockContext); assertThrows(CompletionException.class, () -> resolver.get(mockEnv).join()); - Mockito.verify(mockClient, Mockito.times(0)).list( + Mockito.verify(mockClient, Mockito.times(0)).search( Mockito.any(), + Mockito.eq("*"), Mockito.anyMap(), Mockito.anyInt(), Mockito.anyInt(), @@ -89,8 +92,9 @@ public class ListDomainsResolverTest { public void testGetEntityClientException() throws Exception { // Create resolver EntityClient mockClient = Mockito.mock(EntityClient.class); - Mockito.doThrow(RemoteInvocationException.class).when(mockClient).list( + Mockito.doThrow(RemoteInvocationException.class).when(mockClient).search( Mockito.any(), + Mockito.eq(""), Mockito.anyMap(), Mockito.anyInt(), Mockito.anyInt(), diff --git a/datahub-graphql-core/src/test/java/com/linkedin/datahub/graphql/resolvers/ingest/secret/ListSecretsResolverTest.java b/datahub-graphql-core/src/test/java/com/linkedin/datahub/graphql/resolvers/ingest/secret/ListSecretsResolverTest.java index fd5c17000f..638d1ab961 100644 --- a/datahub-graphql-core/src/test/java/com/linkedin/datahub/graphql/resolvers/ingest/secret/ListSecretsResolverTest.java +++ b/datahub-graphql-core/src/test/java/com/linkedin/datahub/graphql/resolvers/ingest/secret/ListSecretsResolverTest.java @@ -3,7 +3,6 @@ package com.linkedin.datahub.graphql.resolvers.ingest.secret; import com.datahub.authentication.Authentication; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import com.linkedin.common.UrnArray; import com.linkedin.datahub.graphql.QueryContext; import com.linkedin.datahub.graphql.generated.ListSecretsInput; import com.linkedin.entity.Aspect; @@ -12,7 +11,9 @@ import com.linkedin.entity.EnvelopedAspect; import com.linkedin.entity.EnvelopedAspectMap; import com.linkedin.entity.client.EntityClient; import com.linkedin.metadata.Constants; -import com.linkedin.metadata.query.ListResult; +import com.linkedin.metadata.search.SearchEntity; +import com.linkedin.metadata.search.SearchEntityArray; +import com.linkedin.metadata.search.SearchResult; import com.linkedin.r2.RemoteInvocationException; import com.linkedin.secret.DataHubSecretValue; import graphql.schema.DataFetchingEnvironment; @@ -28,7 +29,7 @@ import static org.testng.Assert.*; public class ListSecretsResolverTest { private static final ListSecretsInput TEST_INPUT = new ListSecretsInput( - 0, 20 + 0, 20, null ); @Test @@ -38,18 +39,19 @@ public class ListSecretsResolverTest { DataHubSecretValue returnedValue = getTestSecretValue(); - Mockito.when(mockClient.list( + Mockito.when(mockClient.search( Mockito.eq(Constants.SECRETS_ENTITY_NAME), + Mockito.eq(""), Mockito.eq(Collections.emptyMap()), Mockito.eq(0), Mockito.eq(20), Mockito.any(Authentication.class) )).thenReturn( - new ListResult() - .setStart(0) - .setCount(1) - .setTotal(1) - .setEntities(new UrnArray(ImmutableSet.of(TEST_SECRET_URN))) + new SearchResult() + .setFrom(0) + .setPageSize(1) + .setNumEntities(1) + .setEntities(new SearchEntityArray(ImmutableSet.of(new SearchEntity().setEntity(TEST_SECRET_URN)))) ); Mockito.when(mockClient.batchGetV2( @@ -104,8 +106,9 @@ public class ListSecretsResolverTest { Mockito.anySet(), Mockito.anySet(), Mockito.any(Authentication.class)); - Mockito.verify(mockClient, Mockito.times(0)).list( + Mockito.verify(mockClient, Mockito.times(0)).search( Mockito.any(), + Mockito.eq(""), Mockito.anyMap(), Mockito.anyInt(), Mockito.anyInt(), 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 80b7d963d7..d47f37d0c5 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 @@ -3,7 +3,6 @@ package com.linkedin.datahub.graphql.resolvers.ingest.source; import com.datahub.authentication.Authentication; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import com.linkedin.common.UrnArray; import com.linkedin.datahub.graphql.QueryContext; import com.linkedin.datahub.graphql.generated.ListIngestionSourcesInput; import com.linkedin.entity.Aspect; @@ -13,7 +12,9 @@ import com.linkedin.entity.EnvelopedAspectMap; import com.linkedin.entity.client.EntityClient; import com.linkedin.ingestion.DataHubIngestionSourceInfo; import com.linkedin.metadata.Constants; -import com.linkedin.metadata.query.ListResult; +import com.linkedin.metadata.search.SearchEntity; +import com.linkedin.metadata.search.SearchEntityArray; +import com.linkedin.metadata.search.SearchResult; import com.linkedin.r2.RemoteInvocationException; import graphql.schema.DataFetchingEnvironment; import java.util.Collections; @@ -27,7 +28,7 @@ import static org.testng.Assert.*; public class ListIngestionSourceResolverTest { - private static final ListIngestionSourcesInput TEST_INPUT = new ListIngestionSourcesInput(0, 20); + private static final ListIngestionSourcesInput TEST_INPUT = new ListIngestionSourcesInput(0, 20, null); @Test public void testGetSuccess() throws Exception { @@ -36,18 +37,19 @@ public class ListIngestionSourceResolverTest { DataHubIngestionSourceInfo returnedInfo = getTestIngestionSourceInfo(); - Mockito.when(mockClient.list( + Mockito.when(mockClient.search( Mockito.eq(Constants.INGESTION_SOURCE_ENTITY_NAME), + Mockito.eq(""), Mockito.eq(Collections.emptyMap()), Mockito.eq(0), Mockito.eq(20), Mockito.any(Authentication.class) )).thenReturn( - new ListResult() - .setStart(0) - .setCount(1) - .setTotal(1) - .setEntities(new UrnArray(ImmutableSet.of(TEST_INGESTION_SOURCE_URN))) + new SearchResult() + .setFrom(0) + .setPageSize(1) + .setNumEntities(1) + .setEntities(new SearchEntityArray(ImmutableSet.of(new SearchEntity().setEntity(TEST_INGESTION_SOURCE_URN)))) ); Mockito.when(mockClient.batchGetV2( @@ -102,8 +104,9 @@ public class ListIngestionSourceResolverTest { Mockito.anySet(), Mockito.anySet(), Mockito.any(Authentication.class)); - Mockito.verify(mockClient, Mockito.times(0)).list( + Mockito.verify(mockClient, Mockito.times(0)).search( Mockito.any(), + Mockito.eq(""), Mockito.anyMap(), Mockito.anyInt(), Mockito.anyInt(), diff --git a/datahub-web-react/src/app/domain/DomainsList.tsx b/datahub-web-react/src/app/domain/DomainsList.tsx index def0dd2be1..4c2d97dbeb 100644 --- a/datahub-web-react/src/app/domain/DomainsList.tsx +++ b/datahub-web-react/src/app/domain/DomainsList.tsx @@ -1,6 +1,8 @@ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { Button, Empty, List, message, Pagination, Typography } from 'antd'; +import { useLocation } from 'react-router'; import styled from 'styled-components'; +import * as QueryString from 'query-string'; import { PlusOutlined } from '@ant-design/icons'; import { Domain } from '../../types.generated'; import { useListDomainsQuery } from '../../graphql/domain.generated'; @@ -8,6 +10,8 @@ import CreateDomainModal from './CreateDomainModal'; import { Message } from '../shared/Message'; import TabToolbar from '../entity/shared/components/styled/TabToolbar'; import DomainListItem from './DomainListItem'; +import { SearchBar } from '../search/SearchBar'; +import { useEntityRegistry } from '../useEntityRegistry'; const DomainsContainer = styled.div``; @@ -37,6 +41,13 @@ const PaginationInfo = styled(Typography.Text)` const DEFAULT_PAGE_SIZE = 25; export const DomainsList = () => { + const entityRegistry = useEntityRegistry(); + const location = useLocation(); + const params = QueryString.parse(location.search, { arrayFormat: 'comma' }); + const paramsQuery = (params?.query as string) || undefined; + const [query, setQuery] = useState(undefined); + useEffect(() => setQuery(paramsQuery), [paramsQuery]); + const [page, setPage] = useState(1); const [isCreatingDomain, setIsCreatingDomain] = useState(false); @@ -48,6 +59,7 @@ export const DomainsList = () => { input: { start, count: pageSize, + query, }, }, fetchPolicy: 'no-cache', @@ -76,6 +88,22 @@ export const DomainsList = () => { New Domain + null} + onQueryChange={(q) => setQuery(q)} + entityRegistry={entityRegistry} + /> { + const entityRegistry = useEntityRegistry(); + const location = useLocation(); + const params = QueryString.parse(location.search, { arrayFormat: 'comma' }); + const paramsQuery = (params?.query as string) || undefined; + const [query, setQuery] = useState(undefined); + useEffect(() => setQuery(paramsQuery), [paramsQuery]); + const [page, setPage] = useState(1); const [isCreatingGroup, setIsCreatingGroup] = useState(false); const [removedUrns, setRemovedUrns] = useState([]); @@ -39,6 +50,7 @@ export const GroupList = () => { input: { start, count: pageSize, + query, }, }, fetchPolicy: 'no-cache', @@ -72,6 +84,22 @@ export const GroupList = () => { Create group + null} + onQueryChange={(q) => setQuery(q)} + entityRegistry={entityRegistry} + /> { + const entityRegistry = useEntityRegistry(); + const location = useLocation(); + const params = QueryString.parse(location.search, { arrayFormat: 'comma' }); + const paramsQuery = (params?.query as string) || undefined; + const [query, setQuery] = useState(undefined); + useEffect(() => setQuery(paramsQuery), [paramsQuery]); + const [page, setPage] = useState(1); const [removedUrns, setRemovedUrns] = useState([]); @@ -34,6 +46,7 @@ export const UserList = () => { input: { start, count: pageSize, + query, }, }, fetchPolicy: 'no-cache', @@ -61,6 +74,27 @@ export const UserList = () => { {!data && loading && } {error && message.error('Failed to load users :(')} + +
+ <> +
+ null} + onQueryChange={(q) => setQuery(q)} + entityRegistry={entityRegistry} + /> +
{ + const entityRegistry = useEntityRegistry(); + const location = useLocation(); + const params = QueryString.parse(location.search, { arrayFormat: 'comma' }); + const paramsQuery = (params?.query as string) || undefined; + const [query, setQuery] = useState(undefined); + useEffect(() => setQuery(paramsQuery), [paramsQuery]); + const [page, setPage] = useState(1); const pageSize = DEFAULT_PAGE_SIZE; @@ -42,6 +53,7 @@ export const SecretsList = () => { input: { start, count: pageSize, + query, }, }, fetchPolicy: 'no-cache', @@ -163,6 +175,22 @@ export const SecretsList = () => { Create new secret + null} + onQueryChange={(q) => setQuery(q)} + entityRegistry={entityRegistry} + /> { }; export const IngestionSourceList = () => { + const entityRegistry = useEntityRegistry(); + const location = useLocation(); + const params = QueryString.parse(location.search, { arrayFormat: 'comma' }); + const paramsQuery = (params?.query as string) || undefined; + const [query, setQuery] = useState(undefined); + useEffect(() => setQuery(paramsQuery), [paramsQuery]); + const [page, setPage] = useState(1); const pageSize = DEFAULT_PAGE_SIZE; @@ -83,6 +94,7 @@ export const IngestionSourceList = () => { input: { start, count: pageSize, + query, }, }, }); @@ -404,6 +416,22 @@ export const IngestionSourceList = () => { Refresh + null} + onQueryChange={(q) => setQuery(q)} + entityRegistry={entityRegistry} + /> ): PolicyUpdateInput => { // TODO: Cleanup the styling. export const PoliciesPage = () => { const entityRegistry = useEntityRegistry(); + const location = useLocation(); + const params = QueryString.parse(location.search, { arrayFormat: 'comma' }); + const paramsQuery = (params?.query as string) || undefined; + const [query, setQuery] = useState(undefined); + useEffect(() => setQuery(paramsQuery), [paramsQuery]); + const { config: { policiesConfig }, } = useAppConfig(); @@ -166,7 +175,13 @@ export const PoliciesPage = () => { refetch: policiesRefetch, } = useListPoliciesQuery({ fetchPolicy: 'no-cache', - variables: { input: { start, count: pageSize } }, + variables: { + input: { + start, + count: pageSize, + query, + }, + }, }); // Any time a policy is removed, edited, or created, refetch the list. @@ -402,7 +417,9 @@ export const PoliciesPage = () => { return ( - {policiesLoading && } + {policiesLoading && !policiesData && ( + + )} {policiesError && message.error('Failed to load policies :(')} {updateError && message.error('Failed to update the Policy :(')} @@ -420,6 +437,22 @@ export const PoliciesPage = () => { Create new policy + null} + onQueryChange={(q) => setQuery(q)} + entityRegistry={entityRegistry} + /> (); const [selected, setSelected] = useState(); + useEffect(() => setSelected(initialQuery), [initialQuery]); const searchEntityTypes = entityRegistry.getSearchEntityTypes(); const userUrn = useGetAuthenticatedUserUrn(); const { data } = useListRecommendationsQuery({ diff --git a/metadata-models/src/main/pegasus/com/linkedin/policy/DataHubPolicyInfo.pdl b/metadata-models/src/main/pegasus/com/linkedin/policy/DataHubPolicyInfo.pdl index 23d7a0019d..8e43e531eb 100644 --- a/metadata-models/src/main/pegasus/com/linkedin/policy/DataHubPolicyInfo.pdl +++ b/metadata-models/src/main/pegasus/com/linkedin/policy/DataHubPolicyInfo.pdl @@ -12,13 +12,16 @@ record DataHubPolicyInfo { * Display name of the Policy */ @Searchable = { - "fieldType": "KEYWORD" + "fieldType": "TEXT_PARTIAL" } displayName: string /** * Description of the Policy */ + @Searchable = { + "fieldType": "TEXT" + } description: string /** diff --git a/metadata-service/auth-impl/src/main/java/com/datahub/authorization/PolicyFetcher.java b/metadata-service/auth-impl/src/main/java/com/datahub/authorization/PolicyFetcher.java index 8ac9f1e961..efa36f030e 100644 --- a/metadata-service/auth-impl/src/main/java/com/datahub/authorization/PolicyFetcher.java +++ b/metadata-service/auth-impl/src/main/java/com/datahub/authorization/PolicyFetcher.java @@ -39,10 +39,15 @@ public class PolicyFetcher { public PolicyFetchResult fetchPolicies(int start, int count, Authentication authentication) throws RemoteInvocationException, URISyntaxException { + return fetchPolicies(start, count, "", authentication); + } + + public PolicyFetchResult fetchPolicies(int start, int count, String query, Authentication authentication) + throws RemoteInvocationException, URISyntaxException { log.debug(String.format("Batch fetching policies. start: %s, count: %s ", start, count)); // First fetch all policy urns from start - start + count SearchResult result = - _entityClient.search(POLICY_ENTITY_NAME, "*", null, POLICY_SORT_CRITERION, start, count, authentication); + _entityClient.search(POLICY_ENTITY_NAME, query, null, POLICY_SORT_CRITERION, start, count, authentication); List policyUrns = result.getEntities().stream().map(SearchEntity::getEntity).collect(Collectors.toList()); if (policyUrns.isEmpty()) { diff --git a/metadata-service/auth-impl/src/test/java/com/datahub/authorization/DataHubAuthorizerTest.java b/metadata-service/auth-impl/src/test/java/com/datahub/authorization/DataHubAuthorizerTest.java index 2dac53e418..ef6b83fe37 100644 --- a/metadata-service/auth-impl/src/test/java/com/datahub/authorization/DataHubAuthorizerTest.java +++ b/metadata-service/auth-impl/src/test/java/com/datahub/authorization/DataHubAuthorizerTest.java @@ -75,7 +75,7 @@ public class DataHubAuthorizerTest { policySearchResult.setEntities(new SearchEntityArray(ImmutableList.of(new SearchEntity().setEntity(activePolicyUrn), new SearchEntity().setEntity(inactivePolicyUrn)))); - when(_entityClient.search(eq("dataHubPolicy"), eq("*"), isNull(), any(), anyInt(), anyInt(), any())).thenReturn( + when(_entityClient.search(eq("dataHubPolicy"), eq(""), isNull(), any(), anyInt(), anyInt(), any())).thenReturn( policySearchResult); when(_entityClient.batchGetV2(eq(POLICY_ENTITY_NAME), eq(ImmutableSet.of(activePolicyUrn, inactivePolicyUrn)), eq(null), any())).thenReturn( @@ -192,7 +192,7 @@ public class DataHubAuthorizerTest { emptyResult.setNumEntities(0); emptyResult.setEntities(new SearchEntityArray()); - when(_entityClient.search(eq("dataHubPolicy"), eq("*"), isNull(), any(), anyInt(), anyInt(), any())).thenReturn( + when(_entityClient.search(eq("dataHubPolicy"), eq(""), isNull(), any(), anyInt(), anyInt(), any())).thenReturn( emptyResult); when(_entityClient.batchGetV2(eq(POLICY_ENTITY_NAME), eq(Collections.emptySet()), eq(null), any())).thenReturn( Collections.emptyMap());