From b57de90fd1f64f82c9937a71d87be6a5b0fb4d5e Mon Sep 17 00:00:00 2001 From: Chris Collins Date: Wed, 18 Sep 2024 15:16:40 -0400 Subject: [PATCH 1/2] feat(structuredProps) Add created and lastModified timestamps to structured prop entity (#11419) --- .../datahub/graphql/GmsGraphQLEngine.java | 1 + .../CreateStructuredPropertyResolver.java | 2 ++ .../UpdateStructuredPropertyResolver.java | 1 + .../graphql/types/mappers/MapperUtils.java | 12 ++++++++ .../StructuredPropertyMapper.java | 8 +++++ .../src/main/resources/entity.graphql | 5 ++++ .../src/main/resources/properties.graphql | 10 +++++++ ...ucturedPropertyDefinitionPatchBuilder.java | 29 +++++++++++++++++++ .../StructuredPropertyDefinition.pdl | 23 +++++++++++++++ 9 files changed, 91 insertions(+) diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/GmsGraphQLEngine.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/GmsGraphQLEngine.java index de77ff9444c6e7..bb4c26d89f5cea 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/GmsGraphQLEngine.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/GmsGraphQLEngine.java @@ -1034,6 +1034,7 @@ private void configureQueryResolvers(final RuntimeWiring.Builder builder) { .dataFetcher("assertion", getResolver(assertionType)) .dataFetcher("form", getResolver(formType)) .dataFetcher("view", getResolver(dataHubViewType)) + .dataFetcher("structuredProperty", getResolver(structuredPropertyType)) .dataFetcher("listPolicies", new ListPoliciesResolver(this.entityClient)) .dataFetcher("getGrantedPrivileges", new GetGrantedPrivilegesResolver()) .dataFetcher("listUsers", new ListUsersResolver(this.entityClient)) diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/structuredproperties/CreateStructuredPropertyResolver.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/structuredproperties/CreateStructuredPropertyResolver.java index 3be7ea505abbf3..328f63b893d06f 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/structuredproperties/CreateStructuredPropertyResolver.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/structuredproperties/CreateStructuredPropertyResolver.java @@ -83,6 +83,8 @@ public CompletableFuture get(final DataFetchingEnviron builder.setCardinality( PropertyCardinality.valueOf(input.getCardinality().toString())); } + builder.setCreated(context.getOperationContext().getAuditStamp()); + builder.setLastModified(context.getOperationContext().getAuditStamp()); MetadataChangeProposal mcp = builder.build(); _entityClient.ingestProposal(context.getOperationContext(), mcp, false); diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/structuredproperties/UpdateStructuredPropertyResolver.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/structuredproperties/UpdateStructuredPropertyResolver.java index 2549f303bacd95..c432281ec16848 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/structuredproperties/UpdateStructuredPropertyResolver.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/structuredproperties/UpdateStructuredPropertyResolver.java @@ -76,6 +76,7 @@ public CompletableFuture get(final DataFetchingEnviron if (input.getNewEntityTypes() != null) { input.getNewEntityTypes().forEach(builder::addEntityType); } + builder.setLastModified(context.getOperationContext().getAuditStamp()); MetadataChangeProposal mcp = builder.build(); _entityClient.ingestProposal(context.getOperationContext(), mcp, false); diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/mappers/MapperUtils.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/mappers/MapperUtils.java index 0eb74210971d9f..0d69e62c621a60 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/mappers/MapperUtils.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/mappers/MapperUtils.java @@ -3,15 +3,18 @@ import static com.linkedin.datahub.graphql.util.SearchInsightsUtil.*; import static com.linkedin.metadata.utils.SearchUtil.*; +import com.linkedin.common.AuditStamp; import com.linkedin.common.UrnArray; import com.linkedin.common.urn.Urn; import com.linkedin.data.template.StringMap; import com.linkedin.datahub.graphql.QueryContext; import com.linkedin.datahub.graphql.generated.AggregationMetadata; +import com.linkedin.datahub.graphql.generated.CorpUser; import com.linkedin.datahub.graphql.generated.EntityPath; import com.linkedin.datahub.graphql.generated.ExtraProperty; import com.linkedin.datahub.graphql.generated.FacetMetadata; import com.linkedin.datahub.graphql.generated.MatchedField; +import com.linkedin.datahub.graphql.generated.ResolvedAuditStamp; import com.linkedin.datahub.graphql.generated.SearchResult; import com.linkedin.datahub.graphql.generated.SearchSuggestion; import com.linkedin.datahub.graphql.types.common.mappers.UrnToEntityMapper; @@ -132,4 +135,13 @@ public static EntityPath mapPath(@Nullable final QueryContext context, UrnArray path.stream().map(p -> UrnToEntityMapper.map(context, p)).collect(Collectors.toList())); return entityPath; } + + public static ResolvedAuditStamp createResolvedAuditStamp(AuditStamp auditStamp) { + final ResolvedAuditStamp resolvedAuditStamp = new ResolvedAuditStamp(); + final CorpUser emptyCreatedUser = new CorpUser(); + emptyCreatedUser.setUrn(auditStamp.getActor().toString()); + resolvedAuditStamp.setActor(emptyCreatedUser); + resolvedAuditStamp.setTime(auditStamp.getTime()); + return resolvedAuditStamp; + } } diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/structuredproperty/StructuredPropertyMapper.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/structuredproperty/StructuredPropertyMapper.java index ff54131506a7cc..cacb6958dc2020 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/structuredproperty/StructuredPropertyMapper.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/structuredproperty/StructuredPropertyMapper.java @@ -17,6 +17,7 @@ import com.linkedin.datahub.graphql.generated.StructuredPropertyEntity; import com.linkedin.datahub.graphql.generated.TypeQualifier; import com.linkedin.datahub.graphql.types.common.mappers.util.MappingHelper; +import com.linkedin.datahub.graphql.types.mappers.MapperUtils; import com.linkedin.datahub.graphql.types.mappers.ModelMapper; import com.linkedin.entity.EntityResponse; import com.linkedin.entity.EnvelopedAspectMap; @@ -74,6 +75,13 @@ private void mapStructuredPropertyDefinition( if (gmsDefinition.hasTypeQualifier()) { definition.setTypeQualifier(mapTypeQualifier(gmsDefinition.getTypeQualifier())); } + if (gmsDefinition.getCreated() != null) { + definition.setCreated(MapperUtils.createResolvedAuditStamp(gmsDefinition.getCreated())); + } + if (gmsDefinition.getLastModified() != null) { + definition.setLastModified( + MapperUtils.createResolvedAuditStamp(gmsDefinition.getLastModified())); + } definition.setEntityTypes( gmsDefinition.getEntityTypes().stream() .map(this::createEntityTypeEntity) diff --git a/datahub-graphql-core/src/main/resources/entity.graphql b/datahub-graphql-core/src/main/resources/entity.graphql index 52e81f8094dea6..a2e2fe9163f536 100644 --- a/datahub-graphql-core/src/main/resources/entity.graphql +++ b/datahub-graphql-core/src/main/resources/entity.graphql @@ -84,6 +84,11 @@ type Query { """ role(urn: String!): Role + """ + Fetch a Structured Property by primary key (urn) + """ + structuredProperty(urn: String!): StructuredPropertyEntity + """ Fetch a ERModelRelationship by primary key (urn) """ diff --git a/datahub-graphql-core/src/main/resources/properties.graphql b/datahub-graphql-core/src/main/resources/properties.graphql index dfe84686456814..6c1f910e02b0ee 100644 --- a/datahub-graphql-core/src/main/resources/properties.graphql +++ b/datahub-graphql-core/src/main/resources/properties.graphql @@ -95,6 +95,16 @@ type StructuredPropertyDefinition { Whether or not this structured property is immutable """ immutable: Boolean! + + """ + Audit stamp for when this structured property was created + """ + created: ResolvedAuditStamp + + """ + Audit stamp for when this structured property was last modified + """ + lastModified: ResolvedAuditStamp } """ diff --git a/entity-registry/src/main/java/com/linkedin/metadata/aspect/patch/builder/StructuredPropertyDefinitionPatchBuilder.java b/entity-registry/src/main/java/com/linkedin/metadata/aspect/patch/builder/StructuredPropertyDefinitionPatchBuilder.java index 0811e2f52d003e..1feae36b1462d9 100644 --- a/entity-registry/src/main/java/com/linkedin/metadata/aspect/patch/builder/StructuredPropertyDefinitionPatchBuilder.java +++ b/entity-registry/src/main/java/com/linkedin/metadata/aspect/patch/builder/StructuredPropertyDefinitionPatchBuilder.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.linkedin.common.AuditStamp; import com.linkedin.data.template.StringArrayMap; import com.linkedin.metadata.aspect.patch.PatchOperationType; import com.linkedin.structured.PropertyCardinality; @@ -29,6 +30,10 @@ public class StructuredPropertyDefinitionPatchBuilder public static final String ENTITY_TYPES_FIELD = "entityTypes"; public static final String DESCRIPTION_FIELD = "description"; public static final String IMMUTABLE_FIELD = "immutable"; + private static final String LAST_MODIFIED_KEY = "lastModified"; + private static final String CREATED_KEY = "created"; + private static final String TIME_KEY = "time"; + private static final String ACTOR_KEY = "actor"; // can only be used when creating a new structured property public StructuredPropertyDefinitionPatchBuilder setQualifiedName(@Nonnull String name) { @@ -134,6 +139,30 @@ public StructuredPropertyDefinitionPatchBuilder setImmutable(boolean immutable) return this; } + public StructuredPropertyDefinitionPatchBuilder setLastModified( + @Nonnull AuditStamp lastModified) { + ObjectNode lastModifiedValue = instance.objectNode(); + lastModifiedValue.put(TIME_KEY, lastModified.getTime()); + lastModifiedValue.put(ACTOR_KEY, lastModified.getActor().toString()); + + pathValues.add( + ImmutableTriple.of( + PatchOperationType.ADD.getValue(), "/" + LAST_MODIFIED_KEY, lastModifiedValue)); + + return this; + } + + public StructuredPropertyDefinitionPatchBuilder setCreated(@Nonnull AuditStamp created) { + ObjectNode createdValue = instance.objectNode(); + createdValue.put(TIME_KEY, created.getTime()); + createdValue.put(ACTOR_KEY, created.getActor().toString()); + + pathValues.add( + ImmutableTriple.of(PatchOperationType.ADD.getValue(), "/" + CREATED_KEY, createdValue)); + + return this; + } + @Override protected String getAspectName() { return STRUCTURED_PROPERTY_DEFINITION_ASPECT_NAME; diff --git a/metadata-models/src/main/pegasus/com/linkedin/structured/StructuredPropertyDefinition.pdl b/metadata-models/src/main/pegasus/com/linkedin/structured/StructuredPropertyDefinition.pdl index bf0bf65099b2e8..3ddb2d2e571da3 100644 --- a/metadata-models/src/main/pegasus/com/linkedin/structured/StructuredPropertyDefinition.pdl +++ b/metadata-models/src/main/pegasus/com/linkedin/structured/StructuredPropertyDefinition.pdl @@ -1,5 +1,6 @@ namespace com.linkedin.structured +import com.linkedin.common.AuditStamp import com.linkedin.common.Urn import com.linkedin.datahub.DataHubSearchConfig @@ -86,5 +87,27 @@ record StructuredPropertyDefinition { * 20240610, 20240611 */ version: optional string + + /** + * Created Audit stamp + */ + @Searchable = { + "/time": { + "fieldName": "createdTime", + "fieldType": "DATETIME" + } + } + created: optional AuditStamp + + /** + * Created Audit stamp + */ + @Searchable = { + "/time": { + "fieldName": "lastModified", + "fieldType": "DATETIME" + } + } + lastModified: optional AuditStamp } From cc6cbe208645d6fe148f4d1876ef40273d99d505 Mon Sep 17 00:00:00 2001 From: Jay <159848059+jayacryl@users.noreply.github.com> Date: Wed, 18 Sep 2024 17:59:46 -0400 Subject: [PATCH 2/2] feat(docs-site) tours to open in a modal (#11420) --- .../CaseStudy/case-study.module.scss | 4 +- .../src/pages/_components/Community/index.js | 3 +- .../pages/_components/Hero/hero.module.scss | 3 +- .../src/pages/_components/Hero/index.js | 9 ++-- .../src/pages/_components/Trial/index.js | 7 ++- .../pages/_components/Trial/trial.module.scss | 2 +- .../src/pages/cloud/UnifiedTabs/index.js | 2 +- docs-website/src/pages/index.js | 22 +++++++-- docs-website/src/styles/global.scss | 47 +++++++++++++++++++ 9 files changed, 83 insertions(+), 16 deletions(-) diff --git a/docs-website/src/pages/_components/CaseStudy/case-study.module.scss b/docs-website/src/pages/_components/CaseStudy/case-study.module.scss index a90788b9c2047e..e17f1f74656dc1 100644 --- a/docs-website/src/pages/_components/CaseStudy/case-study.module.scss +++ b/docs-website/src/pages/_components/CaseStudy/case-study.module.scss @@ -167,14 +167,14 @@ font-family: "Manrope"; div { - width: 70%; + width: 80%; margin: auto; font-size: 2rem; line-height: normal; font-weight: 400; } p { - width: 60%; + width: 80%; margin: auto; font-size: 1.1rem; line-height: 1.5rem; diff --git a/docs-website/src/pages/_components/Community/index.js b/docs-website/src/pages/_components/Community/index.js index 2ef6eba45092c1..a4f2b2304e51e4 100644 --- a/docs-website/src/pages/_components/Community/index.js +++ b/docs-website/src/pages/_components/Community/index.js @@ -74,8 +74,7 @@ const Community = () => {

- Q&A. Office Hours.  Monthly Town Hall.  Job - Postings. + Q&A.  Office Hours.  Think Tanks.  Job Postings.

Join Slack diff --git a/docs-website/src/pages/_components/Hero/hero.module.scss b/docs-website/src/pages/_components/Hero/hero.module.scss index 9e3fa5a1c516c1..2484b259a32c28 100644 --- a/docs-website/src/pages/_components/Hero/hero.module.scss +++ b/docs-website/src/pages/_components/Hero/hero.module.scss @@ -83,7 +83,7 @@ margin-left: 4px; height: 1.8rem; padding-left: 2px; - padding-right: 8px; + padding-right: 6px; font-family: 'Manrope'; font-style: normal; font-weight: 400; @@ -213,6 +213,7 @@ font-size: .8rem; height: 1.2rem; padding-left: 0px; + padding-right: 4px; } } .hero__cta { diff --git a/docs-website/src/pages/_components/Hero/index.js b/docs-website/src/pages/_components/Hero/index.js index daae86c8094e71..2369c4668bfcd9 100644 --- a/docs-website/src/pages/_components/Hero/index.js +++ b/docs-website/src/pages/_components/Hero/index.js @@ -24,7 +24,7 @@ import { animate, motion, useMotionValue, useTransform } from "framer-motion"; const SOLUTION_TEXTS = ["AI Governance", "Data Discovery", "AI Collaboration", "Data Governance", "Data Democratization", "Data Observability"]; -const Hero = ({}) => { +const Hero = ({ onOpenTourModal }) => { // const { colorMode } = useColorMode(); const textIndex = useMotionValue(0); const baseText = useTransform(textIndex, (latest) => SOLUTION_TEXTS[latest] || ""); @@ -71,12 +71,13 @@ const Hero = ({}) => { Book a Demo - Product Tour - + Get started with Core → diff --git a/docs-website/src/pages/_components/Trial/index.js b/docs-website/src/pages/_components/Trial/index.js index 5c0ac992d13668..0f707993c55b7d 100644 --- a/docs-website/src/pages/_components/Trial/index.js +++ b/docs-website/src/pages/_components/Trial/index.js @@ -3,7 +3,7 @@ import styles from "./trial.module.scss"; import useBaseUrl from "@docusaurus/useBaseUrl"; import Link from "@docusaurus/Link"; -const Trial = () => { +const Trial = ({onOpenTourModal}) => { return (
@@ -15,7 +15,10 @@ const Trial = () => {

Book a Demo - Product Tour + Product Tour
Get started with Core →
diff --git a/docs-website/src/pages/_components/Trial/trial.module.scss b/docs-website/src/pages/_components/Trial/trial.module.scss index 64d92c9abe08c8..f774bfffa3f7a9 100644 --- a/docs-website/src/pages/_components/Trial/trial.module.scss +++ b/docs-website/src/pages/_components/Trial/trial.module.scss @@ -191,7 +191,7 @@ flex-direction: column; width: 90vw; min-width: 0; - margin: 2rem auto; + margin: 4rem auto; } .trial { flex-direction: column; diff --git a/docs-website/src/pages/cloud/UnifiedTabs/index.js b/docs-website/src/pages/cloud/UnifiedTabs/index.js index 7ae348885bca27..c0fbc25a8de6bc 100644 --- a/docs-website/src/pages/cloud/UnifiedTabs/index.js +++ b/docs-website/src/pages/cloud/UnifiedTabs/index.js @@ -56,7 +56,7 @@ const TabbedComponent = () => { {activeTab === index && (
-
)} diff --git a/docs-website/src/pages/index.js b/docs-website/src/pages/index.js index f79875f423d477..d538831ca3dca1 100644 --- a/docs-website/src/pages/index.js +++ b/docs-website/src/pages/index.js @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useState } from "react"; import Layout from "@theme/Layout"; import Link from "@docusaurus/Link"; import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; @@ -17,6 +17,7 @@ import Community from "./_components/Community"; import SocialMedia from "./_components/SocialMedia"; import CaseStudy from "./_components/CaseStudy"; import Trial from "./_components/Trial"; +import CloseButton from "@ant-design/icons/CloseCircleFilled"; const companyIndexes = require("../../adoptionStoriesIndexes.json"); const companies = companyIndexes.companies; @@ -33,12 +34,27 @@ function Home() { window.location.replace("/docs"); } + const [isTourModalVisible, setIsTourModalVisible] = useState(false); + const onOpenTourModal = () => { + setIsTourModalVisible(true); + }; + const onCloseTourModal = () => { + setIsTourModalVisible(false); + }; return !siteConfig.customFields.isSaas ? ( - + {isTourModalVisible ? ( +
+
+ +
+