diff --git a/.github/.codecov.yml b/.github/.codecov.yml index 1faf5a6bab4644..4a459d13b60a2e 100644 --- a/.github/.codecov.yml +++ b/.github/.codecov.yml @@ -1,21 +1,19 @@ comment: - layout: "header, files, footer" # remove "new" from "header" and "footer" - hide_project_coverage: true # set to false + layout: "condensed_header, condensed_files, condensed_footer" + hide_project_coverage: true require_changes: false # if true: only post the comment if coverage changes codecov: #due to ci-optimization, reports for modules that have not changed may be quite old max_report_age: off +github_checks: + #Hide annotations that show up in github PR reviews. There still is a red bar next to lines not covered + annotations: false + flag_management: default_rules: # the rules that will be followed for any flag added, generally carryforward: true - statuses: - - type: project - target: auto - threshold: 0% #Not enforcing project coverage yet. - - type: patch - target: 90% individual_flags: # exceptions to the default rules above, stated flag by flag - name: frontend paths: @@ -55,11 +53,8 @@ flag_management: - "metadata-ingestion-modules/prefect-plugin/**" coverage: status: - project: - default: - target: 0% # no threshold enforcement yet - only_pulls: true + project: false patch: default: - target: 90% # for new code added in the patch - only_pulls: true + target: 75% # for new code added in the patch + only_pulls: true \ No newline at end of file diff --git a/README.md b/README.md index 3e85f68142d5fa..a6faa03657126a 100644 --- a/README.md +++ b/README.md @@ -18,27 +18,38 @@ export const Logo = (props) => {

+ DataHub +

# DataHub: The Data Discovery Platform for the Modern Data Stack -## Built with ❤️ by [Acryl Data](https://acryldata.io) and [LinkedIn](https://engineering.linkedin.com) -[![Version](https://img.shields.io/github/v/release/datahub-project/datahub?include_prereleases)](https://github.com/datahub-project/datahub/releases/latest) -[![PyPI version](https://badge.fury.io/py/acryl-datahub.svg)](https://badge.fury.io/py/acryl-datahub) -[![build & test](https://github.com/datahub-project/datahub/workflows/build%20&%20test/badge.svg?branch=master&event=push)](https://github.com/datahub-project/datahub/actions?query=workflow%3A%22build+%26+test%22+branch%3Amaster+event%3Apush) -[![Docker Pulls](https://img.shields.io/docker/pulls/acryldata/datahub-gms.svg)](https://hub.docker.com/r/acryldata/datahub-gms) -[![Slack](https://img.shields.io/badge/slack-join_chat-white.svg?logo=slack&style=social)](https://datahubproject.io/slack?utm_source=github&utm_medium=readme&utm_campaign=github_readme) -[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/datahub-project/datahub/blob/master/docs/CONTRIBUTING.md) -[![GitHub commit activity](https://img.shields.io/github/commit-activity/m/datahub-project/datahub)](https://github.com/datahub-project/datahub/pulls?q=is%3Apr) -[![License](https://img.shields.io/github/license/datahub-project/datahub)](https://github.com/datahub-project/datahub/blob/master/LICENSE) -[![YouTube](https://img.shields.io/youtube/channel/subscribers/UC3qFQC5IiwR5fvWEqi_tJ5w?style=social)](https://www.youtube.com/channel/UC3qFQC5IiwR5fvWEqi_tJ5w) -[![Medium](https://img.shields.io/badge/Medium-12100E?style=for-the-badge&logo=medium&logoColor=white)](https://medium.com/datahub-project) -[![Follow](https://img.shields.io/twitter/follow/datahubproject?label=Follow&style=social)](https://twitter.com/datahubproject) -### 🏠 Hosted DataHub Docs (Courtesy of Acryl Data): [datahubproject.io](https://datahubproject.io/docs) + +### Built with ❤️ by [Acryl Data](https://acryldata.io) and [LinkedIn](https://engineering.linkedin.com) + +
+ + Apache 2.0 License + + PyPI + + GitHub commit activity +
+ + Slack + + YouTube + + Medium + + X (formerly Twitter) Follow +
--- +### 🏠 Docs: [datahubproject.io](https://datahubproject.io/docs) + [Quickstart](https://datahubproject.io/docs/quickstart) | [Features](https://datahubproject.io/docs/) | [Roadmap](https://feature-requests.datahubproject.io/roadmap) | @@ -47,6 +58,7 @@ HOSTED_DOCS_ONLY--> [Town Hall](https://datahubproject.io/docs/townhalls) --- + > 📣 DataHub Town Hall is the 4th Thursday at 9am US PT of every month - [add it to your calendar!](https://rsvp.datahubproject.io/) > > - Town-hall Zoom link: [zoom.datahubproject.io](https://zoom.datahubproject.io) @@ -70,11 +82,11 @@ Check out DataHub's [Features](docs/features.md) & [Roadmap](https://feature-req ## Demo and Screenshots -There's a [hosted demo environment](https://demo.datahubproject.io/) courtesy of [Acryl Data](https://acryldata.io) where you can explore DataHub without installing it locally +There's a [hosted demo environment](https://demo.datahubproject.io/) courtesy of [Acryl Data](https://acryldata.io) where you can explore DataHub without installing it locally. ## Quickstart -Please follow the [DataHub Quickstart Guide](https://datahubproject.io/docs/quickstart) to get a copy of DataHub up & running locally using [Docker](https://docker.com). As the guide assumes some basic knowledge of Docker, we'd recommend you to go through the "Hello World" example of [A Docker Tutorial for Beginners](https://docker-curriculum.com) if Docker is completely foreign to you. +Please follow the [DataHub Quickstart Guide](https://datahubproject.io/docs/quickstart) to run DataHub locally using [Docker](https://docker.com). ## Development @@ -106,7 +118,7 @@ We welcome contributions from the community. Please refer to our [Contributing G ## Community -Join our [Slack workspace](https://datahubproject.io/slack?utm_source=github&utm_medium=readme&utm_campaign=github_readme) for discussions and important announcements. You can also find out more about our upcoming [town hall meetings](docs/townhalls.md) and view past recordings. +Join our [Slack workspace](https://pages.acryl.io/slack?utm_source=github&utm_medium=readme&utm_campaign=github_readme) for discussions and important announcements. You can also find out more about our upcoming [town hall meetings](docs/townhalls.md) and view past recordings. ## Security @@ -159,8 +171,6 @@ Here are the companies that have officially adopted DataHub. Please feel free to - [Wolt](https://wolt.com) - [Zynga](https://www.zynga.com) - - ## Select Articles & Talks - [DataHub Blog](https://blog.datahubproject.io/) diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/analytics/resolver/GetMetadataAnalyticsResolver.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/analytics/resolver/GetMetadataAnalyticsResolver.java index 6045b1e726c7a5..74f5a81dc0150c 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/analytics/resolver/GetMetadataAnalyticsResolver.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/analytics/resolver/GetMetadataAnalyticsResolver.java @@ -78,7 +78,7 @@ private List getCharts(MetadataAnalyticsInput input, OperationCo SearchResult searchResult = _entityClient.searchAcrossEntities( - opContext, entities, query, filter, 0, 0, Collections.emptyList(), null); + opContext, entities, query, filter, 0, 0, Collections.emptyList()); List aggregationMetadataList = searchResult.getMetadata().getAggregations(); diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/container/ContainerEntitiesResolver.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/container/ContainerEntitiesResolver.java index 82a476ec56ddcc..82d3dc642c99e2 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/container/ContainerEntitiesResolver.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/container/ContainerEntitiesResolver.java @@ -91,8 +91,7 @@ public CompletableFuture get(final DataFetchingEnvironment enviro new CriterionArray(ImmutableList.of(filterCriterion))))), start, count, - Collections.emptyList(), - null)); + Collections.emptyList())); } catch (Exception e) { throw new RuntimeException( diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/dataproduct/ListDataProductAssetsResolver.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/dataproduct/ListDataProductAssetsResolver.java index e59f7b3116acdb..f544b4a4f1f898 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/dataproduct/ListDataProductAssetsResolver.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/dataproduct/ListDataProductAssetsResolver.java @@ -185,7 +185,6 @@ public CompletableFuture get(DataFetchingEnvironment environment) finalFilter, start, count, - null, null)); results .getSearchResults() diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/domain/DomainEntitiesResolver.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/domain/DomainEntitiesResolver.java index c27fa1d195a76e..ed79829ddc94ae 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/domain/DomainEntitiesResolver.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/domain/DomainEntitiesResolver.java @@ -95,8 +95,7 @@ public CompletableFuture get(final DataFetchingEnvironment enviro new ConjunctiveCriterion().setAnd(criteria))), start, count, - Collections.emptyList(), - null)); + Collections.emptyList())); } catch (Exception e) { throw new RuntimeException( diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/lineage/UpdateLineageResolver.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/lineage/UpdateLineageResolver.java index 928e33d44c84e7..e8b227f1327ab8 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/lineage/UpdateLineageResolver.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/lineage/UpdateLineageResolver.java @@ -222,16 +222,15 @@ private void checkLineageEdgePrivileges( if (!isAuthorized(context, upstreamUrn, editLineagePrivileges)) { throw new AuthorizationException( String.format( - "Unauthorized to edit %s lineage. Please contact your DataHub administrator.", - upstreamUrn.getEntityType())); + "Unauthorized to edit %s lineage for %s", upstreamUrn, upstreamUrn.getEntityType())); } Urn downstreamUrn = UrnUtils.getUrn(lineageEdge.getDownstreamUrn()); if (!isAuthorized(context, downstreamUrn, editLineagePrivileges)) { throw new AuthorizationException( String.format( - "Unauthorized to edit %s lineage. Please contact your DataHub administrator.", - downstreamUrn.getEntityType())); + "Unauthorized to edit %s lineage for %s", + downstreamUrn, downstreamUrn.getEntityType())); } } diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/policy/GetGrantedPrivilegesResolver.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/policy/GetGrantedPrivilegesResolver.java index a9097fa68a07de..0f051b93d9af1d 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/policy/GetGrantedPrivilegesResolver.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/policy/GetGrantedPrivilegesResolver.java @@ -57,6 +57,6 @@ public CompletableFuture get(final DataFetchingEnvironment environme } private boolean isAuthorized(final QueryContext context, final String actor) { - return actor.equals(context.getActorUrn()); + return PolicyAuthUtils.canManagePolicies(context) || actor.equals(context.getActorUrn()); } } diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/search/GetQuickFiltersResolver.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/search/GetQuickFiltersResolver.java index b07e3fa9126412..27f96f588082b4 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/search/GetQuickFiltersResolver.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/search/GetQuickFiltersResolver.java @@ -108,8 +108,7 @@ private SearchResult getSearchResults( : null, 0, 0, - Collections.emptyList(), - null); + Collections.emptyList()); } /** diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/search/ScrollAcrossEntitiesResolver.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/search/ScrollAcrossEntitiesResolver.java index 77eef1b9a25c69..180ba39fb85886 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/search/ScrollAcrossEntitiesResolver.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/search/ScrollAcrossEntitiesResolver.java @@ -16,6 +16,7 @@ import com.linkedin.entity.client.EntityClient; import com.linkedin.metadata.query.SearchFlags; import com.linkedin.metadata.query.filter.Filter; +import com.linkedin.metadata.query.filter.SortCriterion; import com.linkedin.metadata.service.ViewService; import com.linkedin.view.DataHubViewInfo; import graphql.schema.DataFetcher; @@ -80,6 +81,7 @@ public CompletableFuture get(DataFetchingEnvironment environment) } else { searchFlags = null; } + List sortCriteria = SearchUtils.getSortCriteria(input.getSortInput()); try { log.debug( @@ -108,6 +110,7 @@ public CompletableFuture get(DataFetchingEnvironment environment) : baseFilter, scrollId, keepAlive, + sortCriteria, count)); } catch (Exception e) { log.error( diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/search/SearchAcrossEntitiesResolver.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/search/SearchAcrossEntitiesResolver.java index 29bc3a82a16498..791215a78764ec 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/search/SearchAcrossEntitiesResolver.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/search/SearchAcrossEntitiesResolver.java @@ -146,8 +146,7 @@ private List getStructuredPropertyFacets(final QueryContext context) { createStructuredPropertyFilter(), 0, 100, - Collections.emptyList(), - null); + Collections.emptyList()); return result.getEntities().stream() .map(entity -> String.format("structuredProperties.%s", entity.getEntity().getId())) .collect(Collectors.toList()); diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/search/SearchUtils.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/search/SearchUtils.java index 1591b206d99b90..3cd5043ee73b81 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/search/SearchUtils.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/search/SearchUtils.java @@ -378,6 +378,8 @@ public static CompletableFuture scrollAcrossEntities( Integer inputCount, String scrollId, String inputKeepAlive, + List sortCriteria, + List facets, String className) { final List entityTypes = @@ -431,7 +433,15 @@ public static CompletableFuture scrollAcrossEntities( try { final ScrollResult scrollResult = _entityClient.scrollAcrossEntities( - context, finalEntityNames, query, finalFilters, scrollId, keepAlive, count); + context, + finalEntityNames, + query, + finalFilters, + scrollId, + keepAlive, + sortCriteria, + count, + facets); return UrnScrollResultsMapper.map(inputContext, scrollResult); } catch (Exception e) { log.warn( @@ -518,14 +528,7 @@ public static CompletableFuture searchAcrossEntities( try { final SearchResult searchResult = _entityClient.searchAcrossEntities( - context, - finalEntityNames, - query, - finalFilters, - start, - count, - sortCriteria, - null); + context, finalEntityNames, query, finalFilters, start, count, sortCriteria); return UrnSearchResultsMapper.map(inputContext, searchResult); } catch (Exception e) { log.warn( diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/siblings/SiblingsSearchResolver.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/siblings/SiblingsSearchResolver.java index 6e1425bc44166f..2d7b1a4354c9cd 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/siblings/SiblingsSearchResolver.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/siblings/SiblingsSearchResolver.java @@ -19,6 +19,7 @@ import com.linkedin.metadata.utils.CriterionUtils; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; +import java.util.List; import java.util.concurrent.CompletableFuture; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -61,6 +62,8 @@ public CompletableFuture get(DataFetchingEnvironment environment) input.getCount(), input.getScrollId(), input.getKeepAlive(), + List.of(), + List.of(), this.getClass().getSimpleName()); } } diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/dataplatform/DataPlatformType.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/dataplatform/DataPlatformType.java index 921b1ab3b5edd1..c0c57086a9f2a5 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/dataplatform/DataPlatformType.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/dataplatform/DataPlatformType.java @@ -2,15 +2,26 @@ import static com.linkedin.metadata.Constants.*; +import com.google.common.collect.ImmutableSet; import com.linkedin.common.urn.Urn; import com.linkedin.common.urn.UrnUtils; import com.linkedin.datahub.graphql.QueryContext; +import com.linkedin.datahub.graphql.generated.AutoCompleteResults; import com.linkedin.datahub.graphql.generated.DataPlatform; import com.linkedin.datahub.graphql.generated.Entity; +import com.linkedin.datahub.graphql.generated.FacetFilterInput; +import com.linkedin.datahub.graphql.generated.SearchResults; +import com.linkedin.datahub.graphql.resolvers.ResolverUtils; import com.linkedin.datahub.graphql.types.EntityType; +import com.linkedin.datahub.graphql.types.SearchableEntityType; import com.linkedin.datahub.graphql.types.dataplatform.mappers.DataPlatformMapper; +import com.linkedin.datahub.graphql.types.mappers.AutoCompleteResultsMapper; +import com.linkedin.datahub.graphql.types.mappers.UrnSearchResultsMapper; import com.linkedin.entity.EntityResponse; import com.linkedin.entity.client.EntityClient; +import com.linkedin.metadata.query.AutoCompleteResult; +import com.linkedin.metadata.query.filter.Filter; +import com.linkedin.metadata.search.SearchResult; import graphql.execution.DataFetcherResult; import java.util.ArrayList; import java.util.HashSet; @@ -18,8 +29,11 @@ import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; -public class DataPlatformType implements EntityType { +public class DataPlatformType + implements SearchableEntityType, EntityType { private final EntityClient _entityClient; @@ -75,4 +89,39 @@ public com.linkedin.datahub.graphql.generated.EntityType type() { public Function getKeyProvider() { return Entity::getUrn; } + + @Override + public SearchResults search( + @Nonnull String query, + @Nullable List filters, + int start, + int count, + @Nonnull final QueryContext context) + throws Exception { + final Map facetFilters = + ResolverUtils.buildFacetFilters(filters, ImmutableSet.of()); + final SearchResult searchResult = + _entityClient.search( + context.getOperationContext().withSearchFlags(flags -> flags.setFulltext(true)), + DATA_PLATFORM_ENTITY_NAME, + query, + facetFilters, + start, + count); + return UrnSearchResultsMapper.map(context, searchResult); + } + + @Override + public AutoCompleteResults autoComplete( + @Nonnull String query, + @Nullable String field, + @Nullable Filter filters, + int limit, + @Nonnull final QueryContext context) + throws Exception { + final AutoCompleteResult result = + _entityClient.autoComplete( + context.getOperationContext(), DATA_PLATFORM_ENTITY_NAME, query, filters, limit); + return AutoCompleteResultsMapper.map(context, result); + } } diff --git a/datahub-graphql-core/src/main/resources/search.graphql b/datahub-graphql-core/src/main/resources/search.graphql index 34169d8c6d18aa..c0bec68cc23c5b 100644 --- a/datahub-graphql-core/src/main/resources/search.graphql +++ b/datahub-graphql-core/src/main/resources/search.graphql @@ -347,6 +347,11 @@ input ScrollAcrossEntitiesInput { Flags controlling search options """ searchFlags: SearchFlags + + """ + Optional - Information on how to sort this search result + """ + sortInput: SearchSortInput } diff --git a/datahub-graphql-core/src/test/java/com/linkedin/datahub/graphql/resolvers/container/ContainerEntitiesResolverTest.java b/datahub-graphql-core/src/test/java/com/linkedin/datahub/graphql/resolvers/container/ContainerEntitiesResolverTest.java index 5af236d7e81e50..654a22a078e3ca 100644 --- a/datahub-graphql-core/src/test/java/com/linkedin/datahub/graphql/resolvers/container/ContainerEntitiesResolverTest.java +++ b/datahub-graphql-core/src/test/java/com/linkedin/datahub/graphql/resolvers/container/ContainerEntitiesResolverTest.java @@ -60,8 +60,7 @@ public void testGetSuccess() throws Exception { new CriterionArray(ImmutableList.of(filterCriterion)))))), Mockito.eq(0), Mockito.eq(20), - Mockito.eq(Collections.emptyList()), - Mockito.eq(null))) + Mockito.eq(Collections.emptyList()))) .thenReturn( new SearchResult() .setFrom(0) diff --git a/datahub-graphql-core/src/test/java/com/linkedin/datahub/graphql/resolvers/domain/DomainEntitiesResolverTest.java b/datahub-graphql-core/src/test/java/com/linkedin/datahub/graphql/resolvers/domain/DomainEntitiesResolverTest.java index 5be65703846a9a..dafcea37adcb78 100644 --- a/datahub-graphql-core/src/test/java/com/linkedin/datahub/graphql/resolvers/domain/DomainEntitiesResolverTest.java +++ b/datahub-graphql-core/src/test/java/com/linkedin/datahub/graphql/resolvers/domain/DomainEntitiesResolverTest.java @@ -65,8 +65,7 @@ public void testGetSuccess() throws Exception { new CriterionArray(ImmutableList.of(filterCriterion)))))), Mockito.eq(0), Mockito.eq(20), - Mockito.eq(Collections.emptyList()), - Mockito.eq(null))) + Mockito.eq(Collections.emptyList()))) .thenReturn( new SearchResult() .setFrom(0) diff --git a/datahub-graphql-core/src/test/java/com/linkedin/datahub/graphql/resolvers/search/GetQuickFiltersResolverTest.java b/datahub-graphql-core/src/test/java/com/linkedin/datahub/graphql/resolvers/search/GetQuickFiltersResolverTest.java index 64042e82bbfe88..96ae7c231549a1 100644 --- a/datahub-graphql-core/src/test/java/com/linkedin/datahub/graphql/resolvers/search/GetQuickFiltersResolverTest.java +++ b/datahub-graphql-core/src/test/java/com/linkedin/datahub/graphql/resolvers/search/GetQuickFiltersResolverTest.java @@ -301,8 +301,7 @@ private static EntityClient initMockEntityClient( Mockito.eq(filter), Mockito.eq(start), Mockito.eq(limit), - Mockito.eq(Collections.emptyList()), - Mockito.eq(null))) + Mockito.eq(Collections.emptyList()))) .thenReturn(result); return client; } diff --git a/datahub-graphql-core/src/test/java/com/linkedin/datahub/graphql/resolvers/versioning/VersionsSearchResolverTest.java b/datahub-graphql-core/src/test/java/com/linkedin/datahub/graphql/resolvers/versioning/VersionsSearchResolverTest.java index 3554df074df698..04abb5b5e7231f 100644 --- a/datahub-graphql-core/src/test/java/com/linkedin/datahub/graphql/resolvers/versioning/VersionsSearchResolverTest.java +++ b/datahub-graphql-core/src/test/java/com/linkedin/datahub/graphql/resolvers/versioning/VersionsSearchResolverTest.java @@ -4,6 +4,7 @@ import static com.linkedin.metadata.Constants.*; import static com.linkedin.metadata.utils.CriterionUtils.*; import static org.mockito.ArgumentMatchers.*; +import static org.mockito.ArgumentMatchers.anyString; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertThrows; @@ -39,6 +40,7 @@ import com.linkedin.view.DataHubViewInfo; import com.linkedin.view.DataHubViewType; import graphql.schema.DataFetchingEnvironment; +import io.datahubproject.metadata.context.OperationContext; import java.util.List; import java.util.concurrent.CompletionException; import org.mockito.Mockito; @@ -144,8 +146,7 @@ public void testGetSuccessBasic() throws Exception { List.of( new com.linkedin.metadata.query.filter.SortCriterion() .setField(VERSION_SORT_ID_FIELD_NAME) - .setOrder(com.linkedin.metadata.query.filter.SortOrder.DESCENDING))), - any()); + .setOrder(com.linkedin.metadata.query.filter.SortOrder.DESCENDING)))); } @Test @@ -240,8 +241,7 @@ public void testGetSuccessComplex() throws Exception { .setOrder(com.linkedin.metadata.query.filter.SortOrder.ASCENDING), new com.linkedin.metadata.query.filter.SortCriterion() .setField(VERSION_SORT_ID_FIELD_NAME) - .setOrder(com.linkedin.metadata.query.filter.SortOrder.DESCENDING))), - any()); + .setOrder(com.linkedin.metadata.query.filter.SortOrder.DESCENDING)))); } @Test @@ -251,7 +251,7 @@ public void testThrowsError() throws Exception { Mockito.when( mockEntityClient.searchAcrossEntities( - any(), any(), any(), any(), Mockito.anyInt(), Mockito.anyInt(), any(), any())) + any(), any(), any(), any(), Mockito.anyInt(), Mockito.anyInt(), any())) .thenThrow(new RemoteInvocationException()); VersionsSearchResolver resolver = new VersionsSearchResolver(mockEntityClient, mockViewService); @@ -271,23 +271,36 @@ public void testThrowsError() throws Exception { private EntityClient initMockEntityClient() throws Exception { EntityClient client = Mockito.mock(EntityClient.class); + SearchResult result = + new SearchResult() + .setEntities(new SearchEntityArray()) + .setNumEntities(0) + .setFrom(0) + .setPageSize(0) + .setMetadata(new SearchResultMetadata()); + Mockito.when( client.searchAcrossEntities( - any(), + any(OperationContext.class), any(), Mockito.anyString(), - any(), + any(Filter.class), Mockito.anyInt(), Mockito.anyInt(), + anyList(), + anyList())) + .thenReturn(result); + + Mockito.when( + client.searchAcrossEntities( + any(OperationContext.class), any(), - Mockito.eq(null))) - .thenReturn( - new SearchResult() - .setEntities(new SearchEntityArray()) - .setNumEntities(0) - .setFrom(0) - .setPageSize(0) - .setMetadata(new SearchResultMetadata())); + anyString(), + any(Filter.class), + anyInt(), + anyInt(), + anyList())) + .thenReturn(result); return client; } diff --git a/datahub-web-react/package.json b/datahub-web-react/package.json index 01933de2d7a294..a28a37797397f8 100644 --- a/datahub-web-react/package.json +++ b/datahub-web-react/package.json @@ -57,7 +57,7 @@ "dayjs": "^1.11.7", "deepmerge": "^4.2.2", "diff": "^5.0.0", - "dompurify": "^2.5.4", + "dompurify": "^3.2.4", "dotenv": "^8.2.0", "faker": "5.5.3", "fuse.js": "^7.0.0", @@ -106,7 +106,7 @@ "ec2-dev": "yarn run generate && CI=true vite", "build": "yarn run generate && CI=false NODE_OPTIONS='--max-old-space-size=5120 --openssl-legacy-provider' vite build", "buildWithSourceMap": "yarn run generate && CI=false NODE_OPTIONS='--max-old-space-size=8192 --openssl-legacy-provider' vite build --sourcemap", - "test": "NODE_OPTIONS='--max-old-space-size=5120 --openssl-legacy-provider' vitest", + "test": "NODE_OPTIONS='--max-old-space-size=5120' vitest", "test-coverage": "yarn test run --coverage", "generate": "NODE_OPTIONS='--max-old-space-size=5120 --openssl-legacy-provider' graphql-codegen --config codegen.yml", "lint": "eslint . --ext .ts,.tsx --quiet && yarn format-check && yarn type-check", diff --git a/datahub-web-react/src/alchemy-components/components/Pills/Pill.tsx b/datahub-web-react/src/alchemy-components/components/Pills/Pill.tsx index 825566a4461caa..8a663f47b7361b 100644 --- a/datahub-web-react/src/alchemy-components/components/Pills/Pill.tsx +++ b/datahub-web-react/src/alchemy-components/components/Pills/Pill.tsx @@ -45,6 +45,7 @@ export function Pill({ onPillClick, customStyle, customIconRenderer, + className, }: PillProps) { if (!SUPPORTED_CONFIGURATIONS[variant].includes(color)) { console.debug(`Unsupported configuration for Pill: variant=${variant}, color=${color}`); @@ -62,6 +63,7 @@ export function Pill({ style={{ backgroundColor: customStyle?.backgroundColor, }} + className={className} > {customIconRenderer ? customIconRenderer() diff --git a/datahub-web-react/src/alchemy-components/components/Pills/components.ts b/datahub-web-react/src/alchemy-components/components/Pills/components.ts index c2822ef7e8e047..b7cb9283f8a8bb 100644 --- a/datahub-web-react/src/alchemy-components/components/Pills/components.ts +++ b/datahub-web-react/src/alchemy-components/components/Pills/components.ts @@ -30,6 +30,4 @@ export const PillText = styled.span({ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', - fontSize: '12px', - fontWeight: 400, }); diff --git a/datahub-web-react/src/alchemy-components/components/Pills/types.ts b/datahub-web-react/src/alchemy-components/components/Pills/types.ts index 379ba5e9657592..cd26a1df3b8bbd 100644 --- a/datahub-web-react/src/alchemy-components/components/Pills/types.ts +++ b/datahub-web-react/src/alchemy-components/components/Pills/types.ts @@ -18,6 +18,7 @@ export interface PillProps extends Partial, Omit) => void; onClickLeftIcon?: (e: React.MouseEvent) => void; onPillClick?: (e: React.MouseEvent) => void; + className?: string; } export type PillStyleProps = PillPropsDefaults & Pick; diff --git a/datahub-web-react/src/alchemy-components/components/Pills/utils.ts b/datahub-web-react/src/alchemy-components/components/Pills/utils.ts index 43cc1445656be0..1d277b0eac9c91 100644 --- a/datahub-web-react/src/alchemy-components/components/Pills/utils.ts +++ b/datahub-web-react/src/alchemy-components/components/Pills/utils.ts @@ -79,6 +79,7 @@ const getPillFontStyles = (variant: PillVariantOptions, size: SizeOptions): CSSO md: { fontSize: getFontSize(size), lineHeight: '24px' }, lg: { fontSize: getFontSize(size), lineHeight: '30px' }, xl: { fontSize: getFontSize(size), lineHeight: '34px' }, + inherit: { fontSize: 'inherit', lineHeight: 'inherit' }, }; const variantOverrides: Partial> = { diff --git a/datahub-web-react/src/alchemy-components/components/SelectItemsPopover/SelectItems.tsx b/datahub-web-react/src/alchemy-components/components/SelectItemsPopover/SelectItems.tsx index feac166e7f0da0..c153fef8f85744 100644 --- a/datahub-web-react/src/alchemy-components/components/SelectItemsPopover/SelectItems.tsx +++ b/datahub-web-react/src/alchemy-components/components/SelectItemsPopover/SelectItems.tsx @@ -1,5 +1,5 @@ import React, { ReactNode } from 'react'; -import { Button, Checkbox, Empty, Typography } from 'antd'; +import { Checkbox, Empty, Typography } from 'antd'; import styled from 'styled-components'; import { LoadingOutlined } from '@ant-design/icons'; import { Entity, EntityType } from '@src/types.generated'; @@ -9,6 +9,7 @@ import { InlineListSearch } from '@src/app/entityV2/shared/components/search/Inl import { useEntityRegistry } from '@src/app/useEntityRegistry'; import { useEntityOperations } from './hooks'; // Import your custom hook import { SelectItemCheckboxGroup } from './SelectItemCheckboxGroup'; +import { Button } from '../Button'; export interface SelectItemsProps { entities: Entity[]; @@ -56,18 +57,9 @@ const StyledGroupSection = styled.div` } `; -const StyledResetButton = styled(Button)` - background-color: ${REDESIGN_COLORS.ICON_ON_DARK}; - border: 1px solid ${REDESIGN_COLORS.GREY_300}; - color: ${REDESIGN_COLORS.GREY_300}; - border-radius: 4px; -`; - const StyledUpdateButton = styled(Button)` - background-color: ${REDESIGN_COLORS.TITLE_PURPLE}; - border: 1px solid ${REDESIGN_COLORS.TITLE_PURPLE}; - color: ${REDESIGN_COLORS.WHITE}; - border-radius: 4px; + display: flex; + justify-content: center; `; const StyledCheckBoxContainer = styled.div` @@ -201,9 +193,9 @@ export const SelectItems: React.FC = ({ )} {hasExistingEntities && ( - handleUpdate({ isRemoveAll: true })}> + )} { - let sizeValue = size || ''; - if (!size) sizeValue = 'md'; - return typography.fontSizes[sizeValue]; + if (size === 'inherit') return 'inherit'; + return typography.fontSizes[size || 'md']; }; /* diff --git a/datahub-web-react/src/app/domainV2/CreateDomainModal.tsx b/datahub-web-react/src/app/domainV2/CreateDomainModal.tsx index 056e0847c71d61..b05a9842018410 100644 --- a/datahub-web-react/src/app/domainV2/CreateDomainModal.tsx +++ b/datahub-web-react/src/app/domainV2/CreateDomainModal.tsx @@ -1,6 +1,7 @@ import React, { useState } from 'react'; import styled from 'styled-components'; -import { message, Button, Input, Modal, Typography, Form, Collapse, Tag } from 'antd'; +import { message, Input, Modal, Typography, Form, Collapse, Tag } from 'antd'; +import { Button } from '@src/alchemy-components'; import { useCreateDomainMutation } from '../../graphql/domain.generated'; import { useEnterKeyListener } from '../shared/useEnterKeyListener'; import { validateCustomUrnId } from '../shared/textUtil'; @@ -8,6 +9,7 @@ import analytics, { EventType } from '../analytics'; import DomainParentSelect from '../entityV2/shared/EntityDropdown/DomainParentSelect'; import { useIsNestedDomainsEnabled } from '../useAppConfig'; import { useDomainsContext as useDomainsContextV2 } from './DomainsContext'; +import { ModalButtonContainer } from '../shared/button/styledComponents'; const SuggestedNamesGroup = styled.div` margin-top: 8px; @@ -118,20 +120,20 @@ export default function CreateDomainModal({ onClose, onCreate }: Props) { visible onCancel={onClose} footer={ - <> - - + } >
{canEditRelatedTerms && ( - )} diff --git a/datahub-web-react/src/app/entity/shared/components/styled/search/SearchSelectModal.tsx b/datahub-web-react/src/app/entity/shared/components/styled/search/SearchSelectModal.tsx index 3a9b38b461f4ca..53d8afca8c47df 100644 --- a/datahub-web-react/src/app/entity/shared/components/styled/search/SearchSelectModal.tsx +++ b/datahub-web-react/src/app/entity/shared/components/styled/search/SearchSelectModal.tsx @@ -1,6 +1,8 @@ -import { Button, Modal } from 'antd'; +import { Modal } from 'antd'; import React, { useState } from 'react'; import styled from 'styled-components'; +import { Button } from '@src/alchemy-components'; +import { ModalButtonContainer } from '@src/app/shared/button/styledComponents'; import { EntityType } from '../../../../../../types.generated'; import ClickOutside from '../../../../../shared/ClickOutside'; import { EntityAndType } from '../../../types'; @@ -71,18 +73,19 @@ export const SearchSelectModal = ({ open onCancel={onCancelSelect} footer={ - <> - - + } > owner.owner.type === EntityType.CorpUser) @@ -98,6 +99,6 @@ export const transformResultsToCsvRow = (results: SearchResultInterface[], entit return results.map((result) => { const genericEntityProperties = entityRegistry.getGenericEntityProperties(result.entity.type, result.entity); const entityUrl = entityRegistry.getEntityUrl(result.entity.type, result.entity.urn); - return transformGenericEntityPropertiesToCsvRow(genericEntityProperties, entityUrl, result); + return transformGenericEntityPropertiesToCsvRow(entityRegistry, genericEntityProperties, entityUrl, result); }); }; diff --git a/datahub-web-react/src/app/entity/shared/tabs/Properties/Edit/EditStructuredPropertyModal.tsx b/datahub-web-react/src/app/entity/shared/tabs/Properties/Edit/EditStructuredPropertyModal.tsx index 92691b1ad2239a..a3ab1bc63aaa76 100644 --- a/datahub-web-react/src/app/entity/shared/tabs/Properties/Edit/EditStructuredPropertyModal.tsx +++ b/datahub-web-react/src/app/entity/shared/tabs/Properties/Edit/EditStructuredPropertyModal.tsx @@ -1,7 +1,9 @@ import analytics, { EventType } from '@src/app/analytics'; -import { Button, Modal, message } from 'antd'; +import { Modal, message } from 'antd'; import React, { useEffect, useMemo } from 'react'; import styled from 'styled-components'; +import { Button } from '@src/alchemy-components'; +import { ModalButtonContainer } from '@src/app/shared/button/styledComponents'; import { useUpsertStructuredPropertiesMutation } from '../../../../../../graphql/structuredProperties.generated'; import { EntityType, PropertyValueInput, StructuredPropertyEntity } from '../../../../../../types.generated'; import handleGraphQLError from '../../../../../shared/handleGraphQLError'; @@ -104,19 +106,18 @@ export default function EditStructuredPropertyModal({ open={isOpen} width={650} footer={ - <> - - + } destroyOnClose > diff --git a/datahub-web-react/src/app/entity/view/builder/ViewBuilderModal.tsx b/datahub-web-react/src/app/entity/view/builder/ViewBuilderModal.tsx index ff49a9d06d1ead..a85260a0321abf 100644 --- a/datahub-web-react/src/app/entity/view/builder/ViewBuilderModal.tsx +++ b/datahub-web-react/src/app/entity/view/builder/ViewBuilderModal.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useState } from 'react'; import styled from 'styled-components'; -import { Button, Modal, Typography } from 'antd'; +import { Modal, Typography } from 'antd'; +import { Button } from '@src/alchemy-components'; import { DEFAULT_BUILDER_STATE, ViewBuilderState } from '../types'; import { ViewBuilderForm } from './ViewBuilderForm'; import ClickOutside from '../../../shared/ClickOutside'; @@ -20,6 +21,7 @@ const SaveButtonContainer = styled.div` width: 100%; display: flex; justify-content: right; + gap: 8px; `; const CancelButton = styled(Button)` @@ -85,13 +87,12 @@ export const ViewBuilderModal = ({ mode, urn, initialState, onSubmit, onCancel } > - + Cancel {mode === ViewBuilderMode.EDITOR && ( - )} diff --git a/datahub-web-react/src/app/entityV2/glossaryTerm/profile/GlossaryRelatedTermsResult.tsx b/datahub-web-react/src/app/entityV2/glossaryTerm/profile/GlossaryRelatedTermsResult.tsx index 1312c3cdaff6e6..9f2e136dd4c35c 100644 --- a/datahub-web-react/src/app/entityV2/glossaryTerm/profile/GlossaryRelatedTermsResult.tsx +++ b/datahub-web-react/src/app/entityV2/glossaryTerm/profile/GlossaryRelatedTermsResult.tsx @@ -1,5 +1,6 @@ -import { Button, Typography } from 'antd'; +import { Typography } from 'antd'; import React, { useState } from 'react'; +import { Button } from '@src/alchemy-components'; import styled from 'styled-components/macro'; import { TermRelationshipType } from '../../../../types.generated'; import { Message } from '../../../shared/Message'; @@ -8,7 +9,6 @@ import AddRelatedTermsModal from './AddRelatedTermsModal'; import RelatedTerm from './RelatedTerm'; import { CustomIcon } from '../../../sharedV2/icons/customIcons/CustomIcon'; import addTerm from '../../../sharedV2/icons/customIcons/add-term.svg'; -import { REDESIGN_COLORS } from '../../shared/constants'; export enum RelatedTermTypes { hasRelatedTerms = 'Contains', @@ -45,20 +45,6 @@ const TitleContainer = styled.div` const messageStyle = { marginTop: '10%' }; -const ButtonStyle = styled(Button)` - border: 1px solid ${REDESIGN_COLORS.TITLE_PURPLE}; - color: ${REDESIGN_COLORS.TITLE_PURPLE}; - border-radius: 8px; - display: flex; - gap: 0.2rem; - - &:hover, - &:focus { - border: 1px solid ${REDESIGN_COLORS.TITLE_PURPLE}; - color: ${REDESIGN_COLORS.TITLE_PURPLE}; - } -`; - export default function GlossaryRelatedTermsResult({ glossaryRelatedTermType, glossaryRelatedTermResult }: Props) { const [isShowingAddModal, setIsShowingAddModal] = useState(false); const glossaryRelatedTermUrns: Array = []; @@ -86,9 +72,9 @@ export default function GlossaryRelatedTermsResult({ glossaryRelatedTermType, gl {glossaryRelatedTermType} {canEditRelatedTerms && ( - setIsShowingAddModal(true)}> + )} diff --git a/datahub-web-react/src/app/entityV2/group/GroupInfoHeaderSection.tsx b/datahub-web-react/src/app/entityV2/group/GroupInfoHeaderSection.tsx index 0dd47fcca11325..2c7f779f13dff7 100644 --- a/datahub-web-react/src/app/entityV2/group/GroupInfoHeaderSection.tsx +++ b/datahub-web-react/src/app/entityV2/group/GroupInfoHeaderSection.tsx @@ -43,13 +43,13 @@ export const GroupInfoHeaderSection = ({ isExternalGroup, groupName, }: Props) => { - const groupMemberRelationshipsCount = groupMemberRelationships?.count || 0; + const groupMemberRelationshipsTotal = groupMemberRelationships?.total || 0; return ( {groupName} - {groupMemberRelationshipsCount > 0 && {groupMemberRelationships?.count} members} + {groupMemberRelationshipsTotal > 0 && {groupMemberRelationshipsTotal} members} {isExternalGroup && ( ; + groupMemberRelationships: EntityRelationshipsResult; }; -const DEFAULT_MAX_ENTITIES_TO_SHOW = 4; +const DEFAULT_MAX_ENTITIES_TO_SHOW = 5; -export default function GroupMembersSidebarSectionContent({ relationships }: Props) { +export default function GroupMembersSidebarSectionContent({ groupMemberRelationships }: Props) { + const history = useHistory(); + const { url } = useRouteMatch(); const [entityCount, setEntityCount] = useState(DEFAULT_MAX_ENTITIES_TO_SHOW); const entityRegistry = useEntityRegistry(); - const relationshipsCount = relationships?.length || 0; + const relationshipsTotal = groupMemberRelationships?.total || 0; + const relationshipsAvailableCount = groupMemberRelationships.relationships?.length || 0; return ( <> - {relationships.length === 0 && ( + {relationshipsTotal === 0 && ( No members yet. )} - {relationships.length > 0 && - relationships.map((item, index) => { + {relationshipsTotal > 0 && + groupMemberRelationships.relationships.map((item, index) => { const user = item.entity as CorpUser; return index < entityCount && ; })} - {relationshipsCount > entityCount && ( + {relationshipsAvailableCount > entityCount && ( )} + {relationshipsTotal > relationshipsAvailableCount && entityCount >= relationshipsAvailableCount && ( + history.replace(`${url}/${TabType.Members.toLocaleLowerCase()}`)}> + View all members + + )} ); } diff --git a/datahub-web-react/src/app/entityV2/group/GroupProfile.tsx b/datahub-web-react/src/app/entityV2/group/GroupProfile.tsx index 8056d72535a6d3..6659178d12be46 100644 --- a/datahub-web-react/src/app/entityV2/group/GroupProfile.tsx +++ b/datahub-web-react/src/app/entityV2/group/GroupProfile.tsx @@ -26,14 +26,10 @@ import EntitySidebarContext from '../../sharedV2/EntitySidebarContext'; import SidebarCollapsibleHeader from '../shared/containers/profile/sidebar/SidebarCollapsibleHeader'; import { EntitySidebarTabs } from '../shared/containers/profile/sidebar/EntitySidebarTabs'; import { REDESIGN_COLORS } from '../shared/constants'; +import { TabType } from './types'; const messageStyle = { marginTop: '10%' }; -export enum TabType { - Assets = 'Owner Of', - Members = 'Members', -} - const ENABLED_TAB_TYPES = [TabType.Assets, TabType.Members]; const MEMBER_PAGE_SIZE = 15; @@ -119,7 +115,7 @@ export default function GroupProfile({ urn }: Props) { }, { name: TabType.Members, - path: TabType.Members.toLocaleLowerCase(), + path: TabType.Members.toLocaleLowerCase(), // do not remove toLocaleLowerCase as we link to this tab elsewhere content: ( - } + showFullCount + content={} /> ); }; diff --git a/datahub-web-react/src/app/entityV2/group/types.ts b/datahub-web-react/src/app/entityV2/group/types.ts new file mode 100644 index 00000000000000..ece22d739ccb70 --- /dev/null +++ b/datahub-web-react/src/app/entityV2/group/types.ts @@ -0,0 +1,4 @@ +export enum TabType { + Assets = 'Owner Of', + Members = 'Members', +} diff --git a/datahub-web-react/src/app/entityV2/ownership/OwnershipBuilderModal.tsx b/datahub-web-react/src/app/entityV2/ownership/OwnershipBuilderModal.tsx index ca5a5ee0253201..9837bdcc3da7dc 100644 --- a/datahub-web-react/src/app/entityV2/ownership/OwnershipBuilderModal.tsx +++ b/datahub-web-react/src/app/entityV2/ownership/OwnershipBuilderModal.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useState } from 'react'; -import { Button, Form, Input, Modal, Typography, message, notification } from 'antd'; +import { Form, Input, Modal, Typography, message, notification } from 'antd'; import styled from 'styled-components/macro'; +import { Button } from '@src/alchemy-components'; import { useCreateOwnershipTypeMutation, useUpdateOwnershipTypeMutation } from '../../../graphql/ownership.generated'; import { OwnershipTypeEntity } from '../../../types.generated'; import { OwnershipTypeBuilderState } from './table/types'; @@ -14,7 +15,7 @@ const TitleContainer = styled.div` `; const TitleText = styled(Typography.Text)` - font-size: 16px; + font-size: 14px; font-weight: 700; `; @@ -36,6 +37,7 @@ const SaveButtonContainer = styled.div` width: 100%; display: flex; justify-content: right; + margin-top: 12px; `; const CancelButton = styled(Button)` @@ -187,7 +189,7 @@ export const OwnershipBuilderModal = ({ isOpen, onClose, refetch, ownershipType > { setName(e.target.value); }} @@ -199,7 +201,7 @@ export const OwnershipBuilderModal = ({ isOpen, onClose, refetch, ownershipType { setDescription(e.target.value); }} @@ -208,14 +210,14 @@ export const OwnershipBuilderModal = ({ isOpen, onClose, refetch, ownershipType - + Cancel diff --git a/datahub-web-react/src/app/entityV2/ownership/table/ActionsColumn.tsx b/datahub-web-react/src/app/entityV2/ownership/table/ActionsColumn.tsx index cf4bf9a0fddf4c..c8f988b09bfb7c 100644 --- a/datahub-web-react/src/app/entityV2/ownership/table/ActionsColumn.tsx +++ b/datahub-web-react/src/app/entityV2/ownership/table/ActionsColumn.tsx @@ -137,7 +137,7 @@ export const ActionsColumn = ({ ownershipType, setIsOpen, setOwnershipType, refe return ( - + ); }; diff --git a/datahub-web-react/src/app/entityV2/shared/EntityDropdown/CreateGlossaryEntityModal.tsx b/datahub-web-react/src/app/entityV2/shared/EntityDropdown/CreateGlossaryEntityModal.tsx index 9cc9f16c80e213..1f3f0e8b1ccddd 100644 --- a/datahub-web-react/src/app/entityV2/shared/EntityDropdown/CreateGlossaryEntityModal.tsx +++ b/datahub-web-react/src/app/entityV2/shared/EntityDropdown/CreateGlossaryEntityModal.tsx @@ -1,8 +1,9 @@ import React, { useState } from 'react'; import styled from 'styled-components/macro'; import { EditOutlined } from '@ant-design/icons'; -import { message, Button, Input, Modal, Typography, Form, Collapse } from 'antd'; +import { message, Input, Modal, Typography, Form, Collapse } from 'antd'; import DOMPurify from 'dompurify'; +import { Button } from '@src/alchemy-components'; import { useCreateGlossaryTermMutation, useCreateGlossaryNodeMutation, @@ -25,8 +26,11 @@ const OptionalWrapper = styled.span` font-weight: normal; `; -const StyledButton = styled(Button)` - padding: 0; +const ButtonContainer = styled.div` + display: flex; + align-items: center; + justify-content: end; + gap: 16px; `; interface Props { @@ -115,19 +119,18 @@ function CreateGlossaryEntityModal(props: Props) { visible onCancel={onClose} footer={ - <> - - + } >
} > - setIsDocumentationModalVisible(true)}> + {isDocumentationModalVisible && ( @@ -76,7 +79,7 @@ function EntityMenuActions(props: Props) { )} {menuItems.has(EntityMenuItems.RAISE_INCIDENT) && } - {hasVersioningActions && ( + {entityVersioningEnabled && hasVersioningActions && ( - - - + } > diff --git a/datahub-web-react/src/app/entityV2/shared/EntityDropdown/MoveGlossaryEntityModal.tsx b/datahub-web-react/src/app/entityV2/shared/EntityDropdown/MoveGlossaryEntityModal.tsx index 71723f7fa871b9..7fbd004937736a 100644 --- a/datahub-web-react/src/app/entityV2/shared/EntityDropdown/MoveGlossaryEntityModal.tsx +++ b/datahub-web-react/src/app/entityV2/shared/EntityDropdown/MoveGlossaryEntityModal.tsx @@ -1,6 +1,8 @@ import React, { useState } from 'react'; import styled from 'styled-components/macro'; -import { message, Button, Modal, Typography, Form } from 'antd'; +import { message, Modal, Typography, Form } from 'antd'; +import { ModalButtonContainer } from '@src/app/shared/button/styledComponents'; +import { Button } from '@src/alchemy-components'; import { useRefetch } from '../../../entity/shared/EntityContext'; import { useEntityRegistry } from '../../../useEntityRegistry'; import { useUpdateParentNodeMutation } from '../../../../graphql/glossary.generated'; @@ -72,14 +74,14 @@ function MoveGlossaryEntityModal({ onClose, urn, entityData, entityType }: Props visible onCancel={onClose} footer={ - <> - - - + } > diff --git a/datahub-web-react/src/app/entityV2/shared/EntityDropdown/UpdateDeprecationModal.tsx b/datahub-web-react/src/app/entityV2/shared/EntityDropdown/UpdateDeprecationModal.tsx index fabb2be584a294..fc40be48cadae7 100644 --- a/datahub-web-react/src/app/entityV2/shared/EntityDropdown/UpdateDeprecationModal.tsx +++ b/datahub-web-react/src/app/entityV2/shared/EntityDropdown/UpdateDeprecationModal.tsx @@ -1,7 +1,9 @@ -import { Button, DatePicker, Form, message, Modal, Select, Skeleton } from 'antd'; +import { DatePicker, Form, message, Modal, Select, Skeleton } from 'antd'; import TextArea from 'antd/lib/input/TextArea'; import dayjs from 'dayjs'; import React from 'react'; +import { ModalButtonContainer } from '@src/app/shared/button/styledComponents'; +import { Button } from '@src/alchemy-components'; import { useGetEntitiesQuery } from '../../../../graphql/entity.generated'; import { useBatchUpdateDeprecationMutation } from '../../../../graphql/mutations.generated'; import { ResourceRefInput, SubResourceType } from '../../../../types.generated'; @@ -92,14 +94,14 @@ export const UpdateDeprecationModal = ({ urns, resourceRefs, onClose, refetch, z onCancel={handleClose} keyboard footer={ - <> - - - + } > @@ -164,7 +166,7 @@ export const UpdateDeprecationModal = ({ urns, resourceRefs, onClose, refetch, z )} {replacementUrn && isDeprecatingFields && ( )} diff --git a/datahub-web-react/src/app/entityV2/shared/announce/CreateEntityAnnouncementModal.tsx b/datahub-web-react/src/app/entityV2/shared/announce/CreateEntityAnnouncementModal.tsx index 9af91149a2dc59..47e119e11b426b 100644 --- a/datahub-web-react/src/app/entityV2/shared/announce/CreateEntityAnnouncementModal.tsx +++ b/datahub-web-react/src/app/entityV2/shared/announce/CreateEntityAnnouncementModal.tsx @@ -1,7 +1,8 @@ import React, { useEffect, useState } from 'react'; -import { Button, Form, Input, Modal, Typography, message } from 'antd'; +import { Form, Input, Modal, Typography, message } from 'antd'; import styled from 'styled-components'; -import EditNoteIcon from '@mui/icons-material/EditNote'; +import { Button, colors } from '@src/alchemy-components'; +import { ModalButtonContainer } from '@src/app/shared/button/styledComponents'; import { useCreatePostMutation, useUpdatePostMutation } from '../../../../graphql/mutations.generated'; import { MediaType, PostContentType, PostType, SubResourceType } from '../../../../types.generated'; import { PostEntry } from '../../../settings/posts/PostsListColumns'; @@ -43,10 +44,11 @@ const EditorContainer = styled.div` `; const ModalHeaderContainer = styled.div` - height: 86px; display: flex; align-items: center; - font-size: 20px; + font-size: 16px; + color: ${colors.gray[600]} + font-weight: bold; font-family: 'Mulish', sans-serif; font-weight: lighter; `; @@ -55,21 +57,10 @@ const ModalTitle = styled.div` display: flex; align-items: center; gap: 10px; - max-height: 240px; `; const StyledModal = styled(Modal)` width: 680px !important; - .ant-modal-header { - padding: 0px 32px; - } -`; - -const StyledButton = styled(Button)` - background: #533fd1; - color: #f9fafc; - border-radius: 5px; - border: 1px; `; export default function CreateEntityAnnouncementModal({ @@ -202,28 +193,25 @@ export default function CreateEntityAnnouncementModal({ - - - {titleText} - + {titleText} } open onCancel={onCloseModal} footer={ - <> - - {!editData ? 'Create' : 'Update'} - - + + } > ; @@ -71,35 +58,48 @@ export const AddLinkModal = ({ buttonProps, refetch, buttonType }: AddLinkProps) } }; - return ( - <> - {buttonType === 'transparent' ? ( - } - onClick={showModal} - {...buttonProps} - > - Add Link - - ) : ( - - )} + ); + } + if (bType === 'text') { + return ( + + + Add Link + + ); + } + return ( + + ); + }; + + return ( + <> + {renderButton(buttonType)} - Cancel - , - , + + + + , ]} > diff --git a/datahub-web-react/src/app/entityV2/shared/components/styled/EmptyTab.tsx b/datahub-web-react/src/app/entityV2/shared/components/styled/EmptyTab.tsx index 479d5cdd01bea7..679ba215a32f2c 100644 --- a/datahub-web-react/src/app/entityV2/shared/components/styled/EmptyTab.tsx +++ b/datahub-web-react/src/app/entityV2/shared/components/styled/EmptyTab.tsx @@ -9,6 +9,10 @@ const StyledEmpty = styled(Empty)<{ $hideImage?: boolean }>` ${({ $hideImage }) => ($hideImage ? '.ant-empty-image { margin: 0; }' : '.ant-empty-image { height: 86px; }')} .ant-empty-footer { + display: flex; + align-items: center; + gap: 16px; + justify-content: center; .ant-btn:not(:last-child) { margin-right: 8px; } diff --git a/datahub-web-react/src/app/entityV2/shared/components/styled/search/EmbeddedListSearch.tsx b/datahub-web-react/src/app/entityV2/shared/components/styled/search/EmbeddedListSearch.tsx index 4c7218b9df9fa7..b8d47ba4d4c6d5 100644 --- a/datahub-web-react/src/app/entityV2/shared/components/styled/search/EmbeddedListSearch.tsx +++ b/datahub-web-react/src/app/entityV2/shared/components/styled/search/EmbeddedListSearch.tsx @@ -354,8 +354,8 @@ export const EmbeddedListSearch = ({ let errorMessage = ''; if (error) { - errorMessage = - 'Failed to load results! An unexpected error occurred. This may be due to a timeout when fetching lineage results.'; + console.error('Failed to load results', error); + errorMessage = `Failed to load results due to an unexpected error. Please try again later.`; } return ( diff --git a/datahub-web-react/src/app/entityV2/shared/components/styled/search/EmbeddedListSearchModal.tsx b/datahub-web-react/src/app/entityV2/shared/components/styled/search/EmbeddedListSearchModal.tsx index f286803c6ab140..6fbf8a2a90e7bf 100644 --- a/datahub-web-react/src/app/entityV2/shared/components/styled/search/EmbeddedListSearchModal.tsx +++ b/datahub-web-react/src/app/entityV2/shared/components/styled/search/EmbeddedListSearchModal.tsx @@ -1,6 +1,7 @@ import React, { useState } from 'react'; -import { Button, Modal } from 'antd'; +import { Modal } from 'antd'; import styled from 'styled-components'; +import { Button } from '@src/alchemy-components'; import { AndFilterInput, EntityType, @@ -90,7 +91,11 @@ export const EmbeddedListSearchModal = ({ title={title} visible onCancel={onClose} - footer={} + footer={ + + } > - - + } > { const genericEntityProperties = entityRegistry.getGenericEntityProperties(result.entity.type, result.entity); const entityUrl = entityRegistry.getEntityUrl(result.entity.type, result.entity.urn); - return transformGenericEntityPropertiesToCsvRow(genericEntityProperties, entityUrl, result); + return transformGenericEntityPropertiesToCsvRow(entityRegistry, genericEntityProperties, entityUrl, result); }); }; diff --git a/datahub-web-react/src/app/entityV2/shared/containers/profile/sidebar/DataProduct/SetDataProductModal.tsx b/datahub-web-react/src/app/entityV2/shared/containers/profile/sidebar/DataProduct/SetDataProductModal.tsx index d2e994e8ce1c27..b0813285e95017 100644 --- a/datahub-web-react/src/app/entityV2/shared/containers/profile/sidebar/DataProduct/SetDataProductModal.tsx +++ b/datahub-web-react/src/app/entityV2/shared/containers/profile/sidebar/DataProduct/SetDataProductModal.tsx @@ -1,12 +1,14 @@ import { LoadingOutlined } from '@ant-design/icons'; import Modal from 'antd/lib/modal/Modal'; -import { Button, Empty, Select, message } from 'antd'; +import { Empty, Select, message } from 'antd'; import { getModalDomContainer } from '@src/utils/focus'; import { ANTD_GRAY } from '@src/app/entityV2/shared/constants'; import { debounce } from 'lodash'; import React, { useMemo, useRef, useState } from 'react'; import styled from 'styled-components'; import { useGetRecommendations } from '@src/app/shared/recommendation'; +import { ModalButtonContainer } from '@src/app/shared/button/styledComponents'; +import { Button } from '@src/alchemy-components'; import { useGetAutoCompleteMultipleResultsLazyQuery } from '../../../../../../../graphql/search.generated'; import { DataProduct, Entity, EntityType } from '../../../../../../../types.generated'; import { useEnterKeyListener } from '../../../../../../shared/useEnterKeyListener'; @@ -167,14 +169,14 @@ export default function SetDataProductModal({ onCancel={onModalClose} getContainer={getModalDomContainer} footer={ - <> - - - + } >