diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/Constants.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/Constants.java index f70c46ba943a5..69306862a46ef 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/Constants.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/Constants.java @@ -24,6 +24,7 @@ private Constants() {} public static final String PROPERTIES_SCHEMA_FILE = "properties.graphql"; public static final String FORMS_SCHEMA_FILE = "forms.graphql"; public static final String ASSERTIONS_SCHEMA_FILE = "assertions.graphql"; + public static final String COMMON_SCHEMA_FILE = "common.graphql"; public static final String INCIDENTS_SCHEMA_FILE = "incident.graphql"; public static final String CONTRACTS_SCHEMA_FILE = "contract.graphql"; public static final String CONNECTIONS_SCHEMA_FILE = "connection.graphql"; 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 cba214365fdac..af5d6bc81d614 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 @@ -96,6 +96,7 @@ import com.linkedin.datahub.graphql.generated.MLPrimaryKey; import com.linkedin.datahub.graphql.generated.MLPrimaryKeyProperties; import com.linkedin.datahub.graphql.generated.MatchedField; +import com.linkedin.datahub.graphql.generated.MetadataAttribution; import com.linkedin.datahub.graphql.generated.Notebook; import com.linkedin.datahub.graphql.generated.Owner; import com.linkedin.datahub.graphql.generated.OwnershipTypeEntity; @@ -284,6 +285,8 @@ import com.linkedin.datahub.graphql.resolvers.search.SearchAcrossEntitiesResolver; import com.linkedin.datahub.graphql.resolvers.search.SearchAcrossLineageResolver; import com.linkedin.datahub.graphql.resolvers.search.SearchResolver; +import com.linkedin.datahub.graphql.resolvers.settings.docPropagation.DocPropagationSettingsResolver; +import com.linkedin.datahub.graphql.resolvers.settings.docPropagation.UpdateDocPropagationSettingsResolver; import com.linkedin.datahub.graphql.resolvers.settings.user.UpdateCorpUserViewsSettingsResolver; import com.linkedin.datahub.graphql.resolvers.settings.view.GlobalViewsSettingsResolver; import com.linkedin.datahub.graphql.resolvers.settings.view.UpdateGlobalViewsSettingsResolver; @@ -695,7 +698,8 @@ public GmsGraphQLEngine(final GmsGraphQLEngineArgs args) { businessAttributeType)); this.loadableTypes = new ArrayList<>(entityTypes); // Extend loadable types with types from the plugins - // This allows us to offer search and browse capabilities out of the box for those types + // This allows us to offer search and browse capabilities out of the box for + // those types for (GmsGraphQLPlugin plugin : this.graphQLPlugins) { this.entityTypes.addAll(plugin.getEntityTypes()); Collection> pluginLoadableTypes = plugin.getLoadableTypes(); @@ -790,6 +794,7 @@ public void configureRuntimeWiring(final RuntimeWiring.Builder builder) { configureBusinessAttributeAssociationResolver(builder); configureConnectionResolvers(builder); configureDeprecationResolvers(builder); + configureMetadataAttributionResolver(builder); } private void configureOrganisationRoleResolvers(RuntimeWiring.Builder builder) { @@ -843,7 +848,8 @@ public GraphQLEngine.Builder builder() { .addSchema(fileBasedSchema(CONNECTIONS_SCHEMA_FILE)) .addSchema(fileBasedSchema(ASSERTIONS_SCHEMA_FILE)) .addSchema(fileBasedSchema(INCIDENTS_SCHEMA_FILE)) - .addSchema(fileBasedSchema(CONTRACTS_SCHEMA_FILE)); + .addSchema(fileBasedSchema(CONTRACTS_SCHEMA_FILE)) + .addSchema(fileBasedSchema(COMMON_SCHEMA_FILE)); for (GmsGraphQLPlugin plugin : this.graphQLPlugins) { List pluginSchemaFiles = plugin.getSchemaFiles(); @@ -1087,8 +1093,10 @@ private void configureQueryResolvers(final RuntimeWiring.Builder builder) { new BrowseV2Resolver(this.entityClient, this.viewService, this.formService)) .dataFetcher("businessAttribute", getResolver(businessAttributeType)) .dataFetcher( - "listBusinessAttributes", - new ListBusinessAttributesResolver(this.entityClient))); + "listBusinessAttributes", new ListBusinessAttributesResolver(this.entityClient)) + .dataFetcher( + "docPropagationSettings", + new DocPropagationSettingsResolver(this.settingsService))); } private DataFetcher getEntitiesResolver() { @@ -1340,7 +1348,11 @@ private void configureMutationResolvers(final RuntimeWiring.Builder builder) { .dataFetcher( "createForm", new CreateFormResolver(this.entityClient, this.formService)) .dataFetcher("deleteForm", new DeleteFormResolver(this.entityClient)) - .dataFetcher("updateForm", new UpdateFormResolver(this.entityClient)); + .dataFetcher("updateForm", new UpdateFormResolver(this.entityClient)) + .dataFetcher( + "updateDocPropagationSettings", + new UpdateDocPropagationSettingsResolver(this.settingsService)); + if (featureFlags.isBusinessAttributeEntityEnabled()) { typeWiring .dataFetcher( @@ -2823,7 +2835,8 @@ private void configureContractResolvers(final RuntimeWiring.Builder builder) { } private void configurePolicyResolvers(final RuntimeWiring.Builder builder) { - // Register resolvers for "resolvedUsers" and "resolvedGroups" field of the Policy type. + // Register resolvers for "resolvedUsers" and "resolvedGroups" field of the + // Policy type. builder.type( "ActorFilter", typeWiring -> @@ -3176,4 +3189,20 @@ private void configureDeprecationResolvers(final RuntimeWiring.Builder builder) new EntityTypeResolver( entityTypes, (env) -> ((Deprecation) env.getSource()).getActorEntity()))); } + + private void configureMetadataAttributionResolver(final RuntimeWiring.Builder builder) { + builder.type( + "MetadataAttribution", + typeWiring -> + typeWiring + .dataFetcher( + "actor", + new EntityTypeResolver( + entityTypes, (env) -> ((MetadataAttribution) env.getSource()).getActor())) + .dataFetcher( + "source", + new EntityTypeResolver( + entityTypes, + (env) -> ((MetadataAttribution) env.getSource()).getSource()))); + } } diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/authorization/AuthorizationUtils.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/authorization/AuthorizationUtils.java index b9b4e4f4ef292..4fb49d79a0aa7 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/authorization/AuthorizationUtils.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/authorization/AuthorizationUtils.java @@ -398,6 +398,11 @@ public static boolean canManageForms(@Nonnull QueryContext context) { PoliciesConfig.MANAGE_DOCUMENTATION_FORMS_PRIVILEGE); } + public static boolean canManageFeatures(@Nonnull QueryContext context) { + return AuthUtil.isAuthorized( + context.getAuthorizer(), context.getActorUrn(), PoliciesConfig.MANAGE_FEATURES_PRIVILEGE); + } + public static boolean isAuthorized( @Nonnull Authorizer authorizer, @Nonnull String actor, diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/settings/docPropagation/DocPropagationSettingsResolver.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/settings/docPropagation/DocPropagationSettingsResolver.java new file mode 100644 index 0000000000000..84d3bcd7b376c --- /dev/null +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/settings/docPropagation/DocPropagationSettingsResolver.java @@ -0,0 +1,57 @@ +package com.linkedin.datahub.graphql.resolvers.settings.docPropagation; + +import com.linkedin.datahub.graphql.QueryContext; +import com.linkedin.datahub.graphql.concurrency.GraphQLConcurrencyUtils; +import com.linkedin.datahub.graphql.generated.DocPropagationSettings; +import com.linkedin.metadata.service.SettingsService; +import com.linkedin.settings.global.GlobalSettingsInfo; +import graphql.schema.DataFetcher; +import graphql.schema.DataFetchingEnvironment; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import javax.annotation.Nonnull; +import lombok.extern.slf4j.Slf4j; + +/** Retrieves the Global Settings related to the Actions feature. */ +@Slf4j +public class DocPropagationSettingsResolver + implements DataFetcher> { + + private final SettingsService _settingsService; + + public DocPropagationSettingsResolver(final SettingsService settingsService) { + _settingsService = Objects.requireNonNull(settingsService, "settingsService must not be null"); + } + + @Override + public CompletableFuture get(final DataFetchingEnvironment environment) + throws Exception { + final QueryContext context = environment.getContext(); + return GraphQLConcurrencyUtils.supplyAsync( + () -> { + try { + final GlobalSettingsInfo globalSettings = + _settingsService.getGlobalSettings(context.getOperationContext()); + final DocPropagationSettings defaultSettings = new DocPropagationSettings(); + defaultSettings.setDocColumnPropagation(true); + return globalSettings != null && globalSettings.hasDocPropagation() + ? mapDocPropagationSettings(globalSettings.getDocPropagation()) + : defaultSettings; + } catch (Exception e) { + throw new RuntimeException("Failed to retrieve Action Settings", e); + } + }, + this.getClass().getSimpleName(), + "get"); + } + + private static DocPropagationSettings mapDocPropagationSettings( + @Nonnull final com.linkedin.settings.global.DocPropagationFeatureSettings settings) { + final DocPropagationSettings result = new DocPropagationSettings(); + + // Map docColumnPropagation settings field + result.setDocColumnPropagation(settings.isColumnPropagationEnabled()); + + return result; + } +} diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/settings/docPropagation/UpdateDocPropagationSettingsResolver.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/settings/docPropagation/UpdateDocPropagationSettingsResolver.java new file mode 100644 index 0000000000000..198c36faad0bd --- /dev/null +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/settings/docPropagation/UpdateDocPropagationSettingsResolver.java @@ -0,0 +1,77 @@ +package com.linkedin.datahub.graphql.resolvers.settings.docPropagation; + +import static com.linkedin.datahub.graphql.resolvers.ResolverUtils.*; + +import com.linkedin.datahub.graphql.QueryContext; +import com.linkedin.datahub.graphql.authorization.AuthorizationUtils; +import com.linkedin.datahub.graphql.concurrency.GraphQLConcurrencyUtils; +import com.linkedin.datahub.graphql.exception.AuthorizationException; +import com.linkedin.datahub.graphql.generated.UpdateDocPropagationSettingsInput; +import com.linkedin.metadata.service.SettingsService; +import com.linkedin.settings.global.DocPropagationFeatureSettings; +import com.linkedin.settings.global.GlobalSettingsInfo; +import graphql.schema.DataFetcher; +import graphql.schema.DataFetchingEnvironment; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import javax.annotation.Nonnull; + +/** Resolver responsible for updating the actions settings. */ +public class UpdateDocPropagationSettingsResolver + implements DataFetcher> { + + private final SettingsService _settingsService; + + public UpdateDocPropagationSettingsResolver(@Nonnull final SettingsService settingsService) { + _settingsService = Objects.requireNonNull(settingsService, "settingsService must not be null"); + } + + @Override + public CompletableFuture get(final DataFetchingEnvironment environment) + throws Exception { + final QueryContext context = environment.getContext(); + final UpdateDocPropagationSettingsInput input = + bindArgument(environment.getArgument("input"), UpdateDocPropagationSettingsInput.class); + + return GraphQLConcurrencyUtils.supplyAsync( + () -> { + if (AuthorizationUtils.canManageFeatures(context)) { + try { + // First, fetch the existing global settings. This does a R-M-F. + final GlobalSettingsInfo maybeGlobalSettings = + _settingsService.getGlobalSettings(context.getOperationContext()); + + final GlobalSettingsInfo newGlobalSettings = + maybeGlobalSettings != null ? maybeGlobalSettings : new GlobalSettingsInfo(); + + final DocPropagationFeatureSettings newDocPropagationSettings = + newGlobalSettings.hasDocPropagation() + ? newGlobalSettings.getDocPropagation() + : new DocPropagationFeatureSettings().setEnabled(true); + + // Next, patch the actions settings. + updateDocPropagationSettings(newDocPropagationSettings, input); + newGlobalSettings.setDocPropagation(newDocPropagationSettings); + + // Finally, write back to GMS. + _settingsService.updateGlobalSettings( + context.getOperationContext(), newGlobalSettings); + return true; + } catch (Exception e) { + throw new RuntimeException( + String.format("Failed to update action settings! %s", input), e); + } + } + throw new AuthorizationException( + "Unauthorized to perform this action. Please contact your DataHub administrator."); + }, + this.getClass().getSimpleName(), + "get"); + } + + private static void updateDocPropagationSettings( + @Nonnull final com.linkedin.settings.global.DocPropagationFeatureSettings settings, + @Nonnull final UpdateDocPropagationSettingsInput input) { + settings.setColumnPropagationEnabled(input.getDocColumnPropagation()); + } +} diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/common/mappers/DocumentationMapper.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/common/mappers/DocumentationMapper.java new file mode 100644 index 0000000000000..dcb4921d35398 --- /dev/null +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/common/mappers/DocumentationMapper.java @@ -0,0 +1,54 @@ +package com.linkedin.datahub.graphql.types.common.mappers; + +import com.linkedin.common.urn.Urn; +import com.linkedin.datahub.graphql.QueryContext; +import com.linkedin.datahub.graphql.generated.DataHubConnection; +import com.linkedin.datahub.graphql.generated.Documentation; +import com.linkedin.datahub.graphql.generated.DocumentationAssociation; +import com.linkedin.datahub.graphql.generated.EntityType; +import com.linkedin.datahub.graphql.types.mappers.ModelMapper; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class DocumentationMapper + implements ModelMapper { + + public static final DocumentationMapper INSTANCE = new DocumentationMapper(); + + public static Documentation map( + @Nullable final QueryContext context, + @Nonnull final com.linkedin.common.Documentation metadata) { + return INSTANCE.apply(context, metadata); + } + + @Override + public Documentation apply( + @Nullable final QueryContext context, + @Nonnull final com.linkedin.common.Documentation input) { + final Documentation result = new Documentation(); + result.setDocumentations( + input.getDocumentations().stream() + .map(docAssociation -> mapDocAssociation(context, docAssociation)) + .collect(Collectors.toList())); + return result; + } + + private DocumentationAssociation mapDocAssociation( + @Nullable final QueryContext context, + @Nonnull final com.linkedin.common.DocumentationAssociation association) { + final DocumentationAssociation result = new DocumentationAssociation(); + result.setDocumentation(association.getDocumentation()); + if (association.getAttribution() != null) { + result.setAttribution(MetadataAttributionMapper.map(context, association.getAttribution())); + } + return result; + } + + private DataHubConnection mapConnectionEntity(@Nonnull final Urn urn) { + DataHubConnection connection = new DataHubConnection(); + connection.setUrn(urn.toString()); + connection.setType(EntityType.DATAHUB_CONNECTION); + return connection; + } +} diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/common/mappers/MetadataAttributionMapper.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/common/mappers/MetadataAttributionMapper.java new file mode 100644 index 0000000000000..55fb7ad6f3a2b --- /dev/null +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/common/mappers/MetadataAttributionMapper.java @@ -0,0 +1,35 @@ +package com.linkedin.datahub.graphql.types.common.mappers; + +import com.linkedin.datahub.graphql.QueryContext; +import com.linkedin.datahub.graphql.generated.MetadataAttribution; +import com.linkedin.datahub.graphql.types.mappers.ModelMapper; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class MetadataAttributionMapper + implements ModelMapper { + + public static final MetadataAttributionMapper INSTANCE = new MetadataAttributionMapper(); + + public static MetadataAttribution map( + @Nullable final QueryContext context, + @Nonnull final com.linkedin.common.MetadataAttribution metadata) { + return INSTANCE.apply(context, metadata); + } + + @Override + public MetadataAttribution apply( + @Nullable final QueryContext context, + @Nonnull final com.linkedin.common.MetadataAttribution input) { + final MetadataAttribution result = new MetadataAttribution(); + result.setTime(input.getTime()); + result.setActor(UrnToEntityMapper.map(context, input.getActor())); + if (input.getSource() != null) { + result.setSource(UrnToEntityMapper.map(context, input.getSource())); + } + if (input.getSourceDetail() != null) { + result.setSourceDetail(StringMapMapper.map(context, input.getSourceDetail())); + } + return result; + } +} diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/schemafield/SchemaFieldMapper.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/schemafield/SchemaFieldMapper.java index 85a6b9108cb54..b1f27357d4550 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/schemafield/SchemaFieldMapper.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/schemafield/SchemaFieldMapper.java @@ -1,14 +1,15 @@ package com.linkedin.datahub.graphql.types.schemafield; -import static com.linkedin.metadata.Constants.BUSINESS_ATTRIBUTE_ASPECT; -import static com.linkedin.metadata.Constants.STRUCTURED_PROPERTIES_ASPECT_NAME; +import static com.linkedin.metadata.Constants.*; import com.linkedin.businessattribute.BusinessAttributes; +import com.linkedin.common.Documentation; import com.linkedin.common.urn.Urn; import com.linkedin.datahub.graphql.QueryContext; import com.linkedin.datahub.graphql.generated.EntityType; import com.linkedin.datahub.graphql.generated.SchemaFieldEntity; import com.linkedin.datahub.graphql.types.businessattribute.mappers.BusinessAttributesMapper; +import com.linkedin.datahub.graphql.types.common.mappers.DocumentationMapper; import com.linkedin.datahub.graphql.types.common.mappers.UrnToEntityMapper; import com.linkedin.datahub.graphql.types.common.mappers.util.MappingHelper; import com.linkedin.datahub.graphql.types.mappers.ModelMapper; @@ -46,6 +47,10 @@ public SchemaFieldEntity apply( (((schemaField, dataMap) -> schemaField.setBusinessAttributes( BusinessAttributesMapper.map(new BusinessAttributes(dataMap), entityUrn))))); + mappingHelper.mapToResult( + DOCUMENTATION_ASPECT_NAME, + (entity, dataMap) -> + entity.setDocumentation(DocumentationMapper.map(context, new Documentation(dataMap)))); return result; } diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/schemafield/SchemaFieldType.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/schemafield/SchemaFieldType.java index f9fd7c7b819c5..2fa26d8cf2cdd 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/schemafield/SchemaFieldType.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/schemafield/SchemaFieldType.java @@ -1,8 +1,6 @@ package com.linkedin.datahub.graphql.types.schemafield; -import static com.linkedin.metadata.Constants.BUSINESS_ATTRIBUTE_ASPECT; -import static com.linkedin.metadata.Constants.SCHEMA_FIELD_ENTITY_NAME; -import static com.linkedin.metadata.Constants.STRUCTURED_PROPERTIES_ASPECT_NAME; +import static com.linkedin.metadata.Constants.*; import com.google.common.collect.ImmutableSet; import com.linkedin.common.urn.Urn; @@ -32,7 +30,8 @@ public class SchemaFieldType implements com.linkedin.datahub.graphql.types.EntityType { public static final Set ASPECTS_TO_FETCH = - ImmutableSet.of(STRUCTURED_PROPERTIES_ASPECT_NAME, BUSINESS_ATTRIBUTE_ASPECT); + ImmutableSet.of( + STRUCTURED_PROPERTIES_ASPECT_NAME, BUSINESS_ATTRIBUTE_ASPECT, DOCUMENTATION_ASPECT_NAME); private final EntityClient _entityClient; private final FeatureFlags _featureFlags; diff --git a/datahub-graphql-core/src/main/resources/app.graphql b/datahub-graphql-core/src/main/resources/app.graphql index be3418f146c0f..024a7a989f9db 100644 --- a/datahub-graphql-core/src/main/resources/app.graphql +++ b/datahub-graphql-core/src/main/resources/app.graphql @@ -17,6 +17,11 @@ extend type Query { Requires the 'Manage Global Views' Platform Privilege. """ globalViewsSettings: GlobalViewsSettings + + """ + Fetch the global settings related to the docs propagation feature. + """ + docPropagationSettings: DocPropagationSettings } extend type Mutation { @@ -25,6 +30,11 @@ extend type Mutation { Requires the 'Manage Global Views' Platform Privilege. """ updateGlobalViewsSettings(input: UpdateGlobalViewsSettingsInput!): Boolean! + + """ + Update the doc propagation settings. + """ + updateDocPropagationSettings(input: UpdateDocPropagationSettingsInput!): Boolean! } """ @@ -525,3 +535,23 @@ type GlobalViewsSettings { """ defaultView: String } + +""" +Input required to update doc propagation settings. +""" +input UpdateDocPropagationSettingsInput { + """ + The default doc propagation setting for the platform. + """ + docColumnPropagation: Boolean +} + +""" +Global (platform-level) settings related to the doc propagation feature +""" +type DocPropagationSettings { + """ + The default doc propagation setting for the platform. + """ + docColumnPropagation: Boolean +} \ No newline at end of file diff --git a/datahub-graphql-core/src/main/resources/common.graphql b/datahub-graphql-core/src/main/resources/common.graphql new file mode 100644 index 0000000000000..bac56c97f61cf --- /dev/null +++ b/datahub-graphql-core/src/main/resources/common.graphql @@ -0,0 +1,49 @@ +""" +Object containing the documentation aspect for an entity +""" +type Documentation { + """ + Structured properties on this entity + """ + documentations: [DocumentationAssociation!]! +} + +""" +Object containing the documentation aspect for an entity +""" +type DocumentationAssociation { + """ + Structured properties on this entity + """ + documentation: String! + + """ + Information about who, why, and how this metadata was applied + """ + attribution: MetadataAttribution +} + +""" +Information about who, why, and how this metadata was applied +""" +type MetadataAttribution { + """ + The time this metadata was applied + """ + time: Long! + + """ + The actor responsible for this metadata application + """ + actor: Entity! + + """ + The source of this metadata application. If propagated, this will be an action. + """ + source: Entity + + """ + Extra details about how this metadata was applied + """ + sourceDetail: [StringMapEntry!] +} diff --git a/datahub-graphql-core/src/main/resources/entity.graphql b/datahub-graphql-core/src/main/resources/entity.graphql index 50ec9c5f07993..a2440f7e3928e 100644 --- a/datahub-graphql-core/src/main/resources/entity.graphql +++ b/datahub-graphql-core/src/main/resources/entity.graphql @@ -3225,6 +3225,11 @@ type SchemaFieldEntity implements Entity { Business Attribute associated with the field """ businessAttributes: BusinessAttributes + + """ + Documentation aspect for this schema field + """ + documentation: Documentation } """ diff --git a/datahub-web-react/src/app/home/AcrylDemoBanner.tsx b/datahub-web-react/src/app/home/AcrylDemoBanner.tsx index d567e82cb5bbb..debcaab879ba9 100644 --- a/datahub-web-react/src/app/home/AcrylDemoBanner.tsx +++ b/datahub-web-react/src/app/home/AcrylDemoBanner.tsx @@ -52,8 +52,8 @@ export default function AcrylDemoBanner() { > Schedule a demo {' '} - of DataHub Cloud to see the advanced features that take it to the next level or purchase DataHub Cloud - on{' '} + of DataHub Cloud to see the advanced features that take it to the next level or purchase DataHub + Cloud on{' '} + +This document provides troubleshooting guidance for the Slack integration. For more details on setting up the Slack integration, [click here](./saas-slack-setup.md). + +## Prerequisites - Testing the Slack Integration +First and foremost, we recommend using the 'Send a test notification' feature to verify whether the issue is with the integration setup, slack's systems, or DataHub. The modal will provide a rich description of an error if there is one. +You can access this feature either by going to the Notifications page in your settings, or a subscription drawer. +

+ +

+ +## Test notification failed with 'Re-Connect DataHub to Slack' +There are several reasons why sending a test notification would fail. The description in the modal should give you insights as to what's broken and what you can do to resolve this issue. +If you're seeing a message that recommends the DataHub admin to re-connect Slack to DataHub, you may want to try the following options: + +### Refresh the existing app installation (Recommended) +:::note +Whomever originally installed the Slack app will need to perform this. +If they are unable to do this, you may need to go down the 'Install a new app' path below. +::: +1. Get your App Config tokens by following the first few steps outlined in the [installation guide](https://datahubproject.io/docs/managed-datahub/slack/saas-slack-setup/#step-by-step-guide). If it's showing expired tokens, feel free to delete them and create a new set. +2. Paste them into their respective text inputs, and hit **'Re-connect'** +

+ +

+3. You will be re-directed to a page where you can finalize the app refresh. + +### Install a new app +:::note +If you choose to install a new app, your team will have to re-add the new bot into any private channels the old one was previously in. +If you'd like support in getting a list of the private channels that are subscribed to Slack notifications on DataHub, please reach out to your customer success representative. +::: + +1. Get your App Config tokens by following the first few steps outlined in the [installation guide](https://datahubproject.io/docs/managed-datahub/slack/saas-slack-setup/#step-by-step-guide). If it's showing expired tokens, feel free to delete them and create a new set. +2. Paste them into their respective text inputs, and hit **'create a new installation'** +

+ +

+3. You will be re-directed to a page where you can finalize the app installation. +4. Now to uninstall the old app, visit the **'Manage Apps'** page for your Slack workspace. +

+ +

+5. Find the previously installed DataHub Slack bot in the list of installed apps, and open it. +6. Open the 'App details' page +

+ +

+6. Then, switch to the Configuration tab +

+ +

+6. Finally, scroll to the bottom to find the remove button. +

+ +

+ + +## Test notification works, but not receiving notifications +There are a few reasons why you may not receive notifications when you'd expect. + +### Actors do not receive notifications for their actions +If you've subscribed to an entity, and then performed an action (i.e., raised an incident or added a tag), you will not be notified about your own action. + +### There is an issue with DataHub's systems +If sending a test notification works, and you've verified that none of the above cases apply, then you should contact your DataHub Customer Success rep to help troubleshoot and resolve the issue. + diff --git a/li-utils/src/main/java/com/linkedin/metadata/Constants.java b/li-utils/src/main/java/com/linkedin/metadata/Constants.java index 9a7b8287e2c6a..959125be12c4c 100644 --- a/li-utils/src/main/java/com/linkedin/metadata/Constants.java +++ b/li-utils/src/main/java/com/linkedin/metadata/Constants.java @@ -27,7 +27,8 @@ public class Constants { ".", STRUCTURED_PROPERTY_MAPPING_FIELD, STRUCTURED_PROPERTY_MAPPING_VERSIONED_FIELD, ""); // !!!!!!! IMPORTANT !!!!!!! - // This effectively sets the max aspect size to 16 MB. Used in deserialization of messages. + // This effectively sets the max aspect size to 16 MB. Used in deserialization + // of messages. // Without this the limit is // whatever Jackson is defaulting to (5 MB currently). public static final String MAX_JACKSON_STRING_SIZE = "16000000"; @@ -37,10 +38,12 @@ public class Constants { /** System Metadata */ public static final String DEFAULT_RUN_ID = "no-run-id-provided"; - // Forces indexing for no-ops, enabled for restore indices calls. Only considered in the no-op + // Forces indexing for no-ops, enabled for restore indices calls. Only + // considered in the no-op // case public static final String FORCE_INDEXING_KEY = "forceIndexing"; - // Indicates an event source from an application with hooks that have already been processed and + // Indicates an event source from an application with hooks that have already + // been processed and // should not be reprocessed public static final String APP_SOURCE = "appSource"; @@ -116,6 +119,7 @@ public class Constants { public static final String INPUT_FIELDS_ASPECT_NAME = "inputFields"; public static final String EMBED_ASPECT_NAME = "embed"; public static final String INCIDENTS_SUMMARY_ASPECT_NAME = "incidentsSummary"; + public static final String DOCUMENTATION_ASPECT_NAME = "documentation"; // User public static final String CORP_USER_KEY_ASPECT_NAME = "corpUserKey"; diff --git a/metadata-models/src/main/pegasus/com/linkedin/settings/global/DocPropagationFeatureSettings.pdl b/metadata-models/src/main/pegasus/com/linkedin/settings/global/DocPropagationFeatureSettings.pdl new file mode 100644 index 0000000000000..9838184734d86 --- /dev/null +++ b/metadata-models/src/main/pegasus/com/linkedin/settings/global/DocPropagationFeatureSettings.pdl @@ -0,0 +1,8 @@ +namespace com.linkedin.settings.global + + +record DocPropagationFeatureSettings includes FeatureSettings { + + columnPropagationEnabled: boolean = true + +} \ No newline at end of file diff --git a/metadata-models/src/main/pegasus/com/linkedin/settings/global/FeatureSettings.pdl b/metadata-models/src/main/pegasus/com/linkedin/settings/global/FeatureSettings.pdl new file mode 100644 index 0000000000000..dbc82c8e71a6b --- /dev/null +++ b/metadata-models/src/main/pegasus/com/linkedin/settings/global/FeatureSettings.pdl @@ -0,0 +1,22 @@ +namespace com.linkedin.settings.global + +/** + * A standardized settings template for a feature. + */ + +record FeatureSettings { + + enabled: boolean + + /** + * The configuration for the feature, in JSON format. + */ + config: optional string + + /** + * The version of the configuration schema that has been used to serialize + the config. + * If not provided, the version is assumed to be the latest version. + */ + configVersion: optional string +} \ No newline at end of file diff --git a/metadata-models/src/main/pegasus/com/linkedin/settings/global/GlobalSettingsInfo.pdl b/metadata-models/src/main/pegasus/com/linkedin/settings/global/GlobalSettingsInfo.pdl index 91dca1de0c452..8d4121b767dc3 100644 --- a/metadata-models/src/main/pegasus/com/linkedin/settings/global/GlobalSettingsInfo.pdl +++ b/metadata-models/src/main/pegasus/com/linkedin/settings/global/GlobalSettingsInfo.pdl @@ -16,4 +16,12 @@ record GlobalSettingsInfo { * Settings related to the Views Feature */ views: optional GlobalViewsSettings + /** + * Settings related to the documentation propagation feature + */ + docPropagation: DocPropagationFeatureSettings = { + "enabled": true + "columnPropagationEnabled": true + } + } \ No newline at end of file diff --git a/metadata-service/restli-api/src/main/snapshot/com.linkedin.entity.aspects.snapshot.json b/metadata-service/restli-api/src/main/snapshot/com.linkedin.entity.aspects.snapshot.json index eb92cf75a4d4e..c85e57a35eac7 100644 --- a/metadata-service/restli-api/src/main/snapshot/com.linkedin.entity.aspects.snapshot.json +++ b/metadata-service/restli-api/src/main/snapshot/com.linkedin.entity.aspects.snapshot.json @@ -902,6 +902,51 @@ "type" : "string", "doc" : "Additional context about the association", "optional" : true + }, { + "name" : "attribution", + "type" : { + "type" : "record", + "name" : "MetadataAttribution", + "doc" : "Information about who, why, and how this metadata was applied", + "fields" : [ { + "name" : "time", + "type" : "Time", + "doc" : "When this metadata was updated." + }, { + "name" : "actor", + "type" : "Urn", + "doc" : "The entity (e.g. a member URN) responsible for applying the assocated metadata. This can\neither be a user (in case of UI edits) or the datahub system for automation." + }, { + "name" : "source", + "type" : "Urn", + "doc" : "The DataHub source responsible for applying the associated metadata. This will only be filled out\nwhen a DataHub source is responsible. This includes the specific metadata test urn, the automation urn.", + "optional" : true + }, { + "name" : "sourceDetail", + "type" : { + "type" : "map", + "values" : "string" + }, + "doc" : "The details associated with why this metadata was applied. For example, this could include\nthe actual regex rule, sql statement, ingestion pipeline ID, etc.", + "default" : { } + } ] + }, + "doc" : "Information about who, why, and how this metadata was applied", + "optional" : true, + "Searchable" : { + "/actor" : { + "fieldName" : "tagAttributionActors", + "fieldType" : "URN" + }, + "/source" : { + "fieldName" : "tagAttributionSources", + "fieldType" : "URN" + }, + "/time" : { + "fieldName" : "tagAttributionDates", + "fieldType" : "DATETIME" + } + } } ] } }, @@ -1010,6 +1055,25 @@ "type" : "string", "doc" : "Additional context about the association", "optional" : true + }, { + "name" : "attribution", + "type" : "MetadataAttribution", + "doc" : "Information about who, why, and how this metadata was applied", + "optional" : true, + "Searchable" : { + "/actor" : { + "fieldName" : "termAttributionActors", + "fieldType" : "URN" + }, + "/source" : { + "fieldName" : "termAttributionSources", + "fieldType" : "URN" + }, + "/time" : { + "fieldName" : "termAttributionDates", + "fieldType" : "DATETIME" + } + } } ] }, "com.linkedin.common.GlossaryTermUrn", { "type" : "record", @@ -1121,7 +1185,7 @@ "owningTeam" : "urn:li:internalTeam:datahub" } } - }, { + }, "com.linkedin.common.MetadataAttribution", { "type" : "record", "name" : "Owner", "namespace" : "com.linkedin.common", @@ -3060,6 +3124,18 @@ } }, "Searchable" : { + "/tags/*/attribution/actor" : { + "fieldName" : "fieldTagAttributionActors", + "fieldType" : "URN" + }, + "/tags/*/attribution/source" : { + "fieldName" : "fieldTagAttributionSources", + "fieldType" : "URN" + }, + "/tags/*/attribution/time" : { + "fieldName" : "fieldTagAttributionDates", + "fieldType" : "DATETIME" + }, "/tags/*/tag" : { "boostScore" : 0.5, "fieldName" : "fieldTags", @@ -3078,6 +3154,18 @@ } }, "Searchable" : { + "/terms/*/attribution/actor" : { + "fieldName" : "fieldTermAttributionActors", + "fieldType" : "URN" + }, + "/terms/*/attribution/source" : { + "fieldName" : "fieldTermAttributionSources", + "fieldType" : "URN" + }, + "/terms/*/attribution/time" : { + "fieldName" : "fieldTermAttributionDates", + "fieldType" : "DATETIME" + }, "/terms/*/urn" : { "boostScore" : 0.5, "fieldName" : "fieldGlossaryTerms", @@ -3247,6 +3335,18 @@ } }, "Searchable" : { + "/tags/*/attribution/actor" : { + "fieldName" : "editedFieldTagAttributionActors", + "fieldType" : "URN" + }, + "/tags/*/attribution/source" : { + "fieldName" : "editedFieldTagAttributionSources", + "fieldType" : "URN" + }, + "/tags/*/attribution/time" : { + "fieldName" : "editedFieldTagAttributionDates", + "fieldType" : "DATETIME" + }, "/tags/*/tag" : { "boostScore" : 0.5, "fieldName" : "editedFieldTags", @@ -3265,6 +3365,18 @@ } }, "Searchable" : { + "/terms/*/attribution/actor" : { + "fieldName" : "editedFieldTermAttributionActors", + "fieldType" : "URN" + }, + "/terms/*/attribution/source" : { + "fieldName" : "editedFieldTermAttributionSources", + "fieldType" : "URN" + }, + "/terms/*/attribution/time" : { + "fieldName" : "editedFieldTermAttributionDates", + "fieldType" : "DATETIME" + }, "/terms/*/urn" : { "boostScore" : 0.5, "fieldName" : "editedFieldGlossaryTerms", diff --git a/metadata-service/restli-api/src/main/snapshot/com.linkedin.entity.entities.snapshot.json b/metadata-service/restli-api/src/main/snapshot/com.linkedin.entity.entities.snapshot.json index 0c983a021d4e7..305b3de156350 100644 --- a/metadata-service/restli-api/src/main/snapshot/com.linkedin.entity.entities.snapshot.json +++ b/metadata-service/restli-api/src/main/snapshot/com.linkedin.entity.entities.snapshot.json @@ -897,6 +897,51 @@ "type" : "string", "doc" : "Additional context about the association", "optional" : true + }, { + "name" : "attribution", + "type" : { + "type" : "record", + "name" : "MetadataAttribution", + "doc" : "Information about who, why, and how this metadata was applied", + "fields" : [ { + "name" : "time", + "type" : "Time", + "doc" : "When this metadata was updated." + }, { + "name" : "actor", + "type" : "Urn", + "doc" : "The entity (e.g. a member URN) responsible for applying the assocated metadata. This can\neither be a user (in case of UI edits) or the datahub system for automation." + }, { + "name" : "source", + "type" : "Urn", + "doc" : "The DataHub source responsible for applying the associated metadata. This will only be filled out\nwhen a DataHub source is responsible. This includes the specific metadata test urn, the automation urn.", + "optional" : true + }, { + "name" : "sourceDetail", + "type" : { + "type" : "map", + "values" : "string" + }, + "doc" : "The details associated with why this metadata was applied. For example, this could include\nthe actual regex rule, sql statement, ingestion pipeline ID, etc.", + "default" : { } + } ] + }, + "doc" : "Information about who, why, and how this metadata was applied", + "optional" : true, + "Searchable" : { + "/actor" : { + "fieldName" : "tagAttributionActors", + "fieldType" : "URN" + }, + "/source" : { + "fieldName" : "tagAttributionSources", + "fieldType" : "URN" + }, + "/time" : { + "fieldName" : "tagAttributionDates", + "fieldType" : "DATETIME" + } + } } ] } }, @@ -1005,6 +1050,25 @@ "type" : "string", "doc" : "Additional context about the association", "optional" : true + }, { + "name" : "attribution", + "type" : "MetadataAttribution", + "doc" : "Information about who, why, and how this metadata was applied", + "optional" : true, + "Searchable" : { + "/actor" : { + "fieldName" : "termAttributionActors", + "fieldType" : "URN" + }, + "/source" : { + "fieldName" : "termAttributionSources", + "fieldType" : "URN" + }, + "/time" : { + "fieldName" : "termAttributionDates", + "fieldType" : "DATETIME" + } + } } ] }, "com.linkedin.common.GlossaryTermUrn", { "type" : "record", @@ -1152,7 +1216,7 @@ "owningTeam" : "urn:li:internalTeam:datahub" } } - }, { + }, "com.linkedin.common.MetadataAttribution", { "type" : "record", "name" : "Owner", "namespace" : "com.linkedin.common", @@ -3443,6 +3507,18 @@ } }, "Searchable" : { + "/tags/*/attribution/actor" : { + "fieldName" : "fieldTagAttributionActors", + "fieldType" : "URN" + }, + "/tags/*/attribution/source" : { + "fieldName" : "fieldTagAttributionSources", + "fieldType" : "URN" + }, + "/tags/*/attribution/time" : { + "fieldName" : "fieldTagAttributionDates", + "fieldType" : "DATETIME" + }, "/tags/*/tag" : { "boostScore" : 0.5, "fieldName" : "fieldTags", @@ -3461,6 +3537,18 @@ } }, "Searchable" : { + "/terms/*/attribution/actor" : { + "fieldName" : "fieldTermAttributionActors", + "fieldType" : "URN" + }, + "/terms/*/attribution/source" : { + "fieldName" : "fieldTermAttributionSources", + "fieldType" : "URN" + }, + "/terms/*/attribution/time" : { + "fieldName" : "fieldTermAttributionDates", + "fieldType" : "DATETIME" + }, "/terms/*/urn" : { "boostScore" : 0.5, "fieldName" : "fieldGlossaryTerms", @@ -3630,6 +3718,18 @@ } }, "Searchable" : { + "/tags/*/attribution/actor" : { + "fieldName" : "editedFieldTagAttributionActors", + "fieldType" : "URN" + }, + "/tags/*/attribution/source" : { + "fieldName" : "editedFieldTagAttributionSources", + "fieldType" : "URN" + }, + "/tags/*/attribution/time" : { + "fieldName" : "editedFieldTagAttributionDates", + "fieldType" : "DATETIME" + }, "/tags/*/tag" : { "boostScore" : 0.5, "fieldName" : "editedFieldTags", @@ -3648,6 +3748,18 @@ } }, "Searchable" : { + "/terms/*/attribution/actor" : { + "fieldName" : "editedFieldTermAttributionActors", + "fieldType" : "URN" + }, + "/terms/*/attribution/source" : { + "fieldName" : "editedFieldTermAttributionSources", + "fieldType" : "URN" + }, + "/terms/*/attribution/time" : { + "fieldName" : "editedFieldTermAttributionDates", + "fieldType" : "DATETIME" + }, "/terms/*/urn" : { "boostScore" : 0.5, "fieldName" : "editedFieldGlossaryTerms", diff --git a/metadata-service/restli-api/src/main/snapshot/com.linkedin.entity.runs.snapshot.json b/metadata-service/restli-api/src/main/snapshot/com.linkedin.entity.runs.snapshot.json index 4af65cdb48b50..dc226adf635c4 100644 --- a/metadata-service/restli-api/src/main/snapshot/com.linkedin.entity.runs.snapshot.json +++ b/metadata-service/restli-api/src/main/snapshot/com.linkedin.entity.runs.snapshot.json @@ -639,6 +639,51 @@ "type" : "string", "doc" : "Additional context about the association", "optional" : true + }, { + "name" : "attribution", + "type" : { + "type" : "record", + "name" : "MetadataAttribution", + "doc" : "Information about who, why, and how this metadata was applied", + "fields" : [ { + "name" : "time", + "type" : "Time", + "doc" : "When this metadata was updated." + }, { + "name" : "actor", + "type" : "Urn", + "doc" : "The entity (e.g. a member URN) responsible for applying the assocated metadata. This can\neither be a user (in case of UI edits) or the datahub system for automation." + }, { + "name" : "source", + "type" : "Urn", + "doc" : "The DataHub source responsible for applying the associated metadata. This will only be filled out\nwhen a DataHub source is responsible. This includes the specific metadata test urn, the automation urn.", + "optional" : true + }, { + "name" : "sourceDetail", + "type" : { + "type" : "map", + "values" : "string" + }, + "doc" : "The details associated with why this metadata was applied. For example, this could include\nthe actual regex rule, sql statement, ingestion pipeline ID, etc.", + "default" : { } + } ] + }, + "doc" : "Information about who, why, and how this metadata was applied", + "optional" : true, + "Searchable" : { + "/actor" : { + "fieldName" : "tagAttributionActors", + "fieldType" : "URN" + }, + "/source" : { + "fieldName" : "tagAttributionSources", + "fieldType" : "URN" + }, + "/time" : { + "fieldName" : "tagAttributionDates", + "fieldType" : "DATETIME" + } + } } ] } }, @@ -747,6 +792,25 @@ "type" : "string", "doc" : "Additional context about the association", "optional" : true + }, { + "name" : "attribution", + "type" : "MetadataAttribution", + "doc" : "Information about who, why, and how this metadata was applied", + "optional" : true, + "Searchable" : { + "/actor" : { + "fieldName" : "termAttributionActors", + "fieldType" : "URN" + }, + "/source" : { + "fieldName" : "termAttributionSources", + "fieldType" : "URN" + }, + "/time" : { + "fieldName" : "termAttributionDates", + "fieldType" : "DATETIME" + } + } } ] }, "com.linkedin.common.GlossaryTermUrn", { "type" : "record", @@ -858,7 +922,7 @@ "owningTeam" : "urn:li:internalTeam:datahub" } } - }, { + }, "com.linkedin.common.MetadataAttribution", { "type" : "record", "name" : "Owner", "namespace" : "com.linkedin.common", @@ -2788,6 +2852,18 @@ } }, "Searchable" : { + "/tags/*/attribution/actor" : { + "fieldName" : "fieldTagAttributionActors", + "fieldType" : "URN" + }, + "/tags/*/attribution/source" : { + "fieldName" : "fieldTagAttributionSources", + "fieldType" : "URN" + }, + "/tags/*/attribution/time" : { + "fieldName" : "fieldTagAttributionDates", + "fieldType" : "DATETIME" + }, "/tags/*/tag" : { "boostScore" : 0.5, "fieldName" : "fieldTags", @@ -2806,6 +2882,18 @@ } }, "Searchable" : { + "/terms/*/attribution/actor" : { + "fieldName" : "fieldTermAttributionActors", + "fieldType" : "URN" + }, + "/terms/*/attribution/source" : { + "fieldName" : "fieldTermAttributionSources", + "fieldType" : "URN" + }, + "/terms/*/attribution/time" : { + "fieldName" : "fieldTermAttributionDates", + "fieldType" : "DATETIME" + }, "/terms/*/urn" : { "boostScore" : 0.5, "fieldName" : "fieldGlossaryTerms", @@ -2975,6 +3063,18 @@ } }, "Searchable" : { + "/tags/*/attribution/actor" : { + "fieldName" : "editedFieldTagAttributionActors", + "fieldType" : "URN" + }, + "/tags/*/attribution/source" : { + "fieldName" : "editedFieldTagAttributionSources", + "fieldType" : "URN" + }, + "/tags/*/attribution/time" : { + "fieldName" : "editedFieldTagAttributionDates", + "fieldType" : "DATETIME" + }, "/tags/*/tag" : { "boostScore" : 0.5, "fieldName" : "editedFieldTags", @@ -2993,6 +3093,18 @@ } }, "Searchable" : { + "/terms/*/attribution/actor" : { + "fieldName" : "editedFieldTermAttributionActors", + "fieldType" : "URN" + }, + "/terms/*/attribution/source" : { + "fieldName" : "editedFieldTermAttributionSources", + "fieldType" : "URN" + }, + "/terms/*/attribution/time" : { + "fieldName" : "editedFieldTermAttributionDates", + "fieldType" : "DATETIME" + }, "/terms/*/urn" : { "boostScore" : 0.5, "fieldName" : "editedFieldGlossaryTerms", diff --git a/metadata-service/restli-api/src/main/snapshot/com.linkedin.operations.operations.snapshot.json b/metadata-service/restli-api/src/main/snapshot/com.linkedin.operations.operations.snapshot.json index e788c5d28ce71..7bc02ce2017fd 100644 --- a/metadata-service/restli-api/src/main/snapshot/com.linkedin.operations.operations.snapshot.json +++ b/metadata-service/restli-api/src/main/snapshot/com.linkedin.operations.operations.snapshot.json @@ -639,6 +639,51 @@ "type" : "string", "doc" : "Additional context about the association", "optional" : true + }, { + "name" : "attribution", + "type" : { + "type" : "record", + "name" : "MetadataAttribution", + "doc" : "Information about who, why, and how this metadata was applied", + "fields" : [ { + "name" : "time", + "type" : "Time", + "doc" : "When this metadata was updated." + }, { + "name" : "actor", + "type" : "Urn", + "doc" : "The entity (e.g. a member URN) responsible for applying the assocated metadata. This can\neither be a user (in case of UI edits) or the datahub system for automation." + }, { + "name" : "source", + "type" : "Urn", + "doc" : "The DataHub source responsible for applying the associated metadata. This will only be filled out\nwhen a DataHub source is responsible. This includes the specific metadata test urn, the automation urn.", + "optional" : true + }, { + "name" : "sourceDetail", + "type" : { + "type" : "map", + "values" : "string" + }, + "doc" : "The details associated with why this metadata was applied. For example, this could include\nthe actual regex rule, sql statement, ingestion pipeline ID, etc.", + "default" : { } + } ] + }, + "doc" : "Information about who, why, and how this metadata was applied", + "optional" : true, + "Searchable" : { + "/actor" : { + "fieldName" : "tagAttributionActors", + "fieldType" : "URN" + }, + "/source" : { + "fieldName" : "tagAttributionSources", + "fieldType" : "URN" + }, + "/time" : { + "fieldName" : "tagAttributionDates", + "fieldType" : "DATETIME" + } + } } ] } }, @@ -747,6 +792,25 @@ "type" : "string", "doc" : "Additional context about the association", "optional" : true + }, { + "name" : "attribution", + "type" : "MetadataAttribution", + "doc" : "Information about who, why, and how this metadata was applied", + "optional" : true, + "Searchable" : { + "/actor" : { + "fieldName" : "termAttributionActors", + "fieldType" : "URN" + }, + "/source" : { + "fieldName" : "termAttributionSources", + "fieldType" : "URN" + }, + "/time" : { + "fieldName" : "termAttributionDates", + "fieldType" : "DATETIME" + } + } } ] }, "com.linkedin.common.GlossaryTermUrn", { "type" : "record", @@ -858,7 +922,7 @@ "owningTeam" : "urn:li:internalTeam:datahub" } } - }, { + }, "com.linkedin.common.MetadataAttribution", { "type" : "record", "name" : "Owner", "namespace" : "com.linkedin.common", @@ -2782,6 +2846,18 @@ } }, "Searchable" : { + "/tags/*/attribution/actor" : { + "fieldName" : "fieldTagAttributionActors", + "fieldType" : "URN" + }, + "/tags/*/attribution/source" : { + "fieldName" : "fieldTagAttributionSources", + "fieldType" : "URN" + }, + "/tags/*/attribution/time" : { + "fieldName" : "fieldTagAttributionDates", + "fieldType" : "DATETIME" + }, "/tags/*/tag" : { "boostScore" : 0.5, "fieldName" : "fieldTags", @@ -2800,6 +2876,18 @@ } }, "Searchable" : { + "/terms/*/attribution/actor" : { + "fieldName" : "fieldTermAttributionActors", + "fieldType" : "URN" + }, + "/terms/*/attribution/source" : { + "fieldName" : "fieldTermAttributionSources", + "fieldType" : "URN" + }, + "/terms/*/attribution/time" : { + "fieldName" : "fieldTermAttributionDates", + "fieldType" : "DATETIME" + }, "/terms/*/urn" : { "boostScore" : 0.5, "fieldName" : "fieldGlossaryTerms", @@ -2969,6 +3057,18 @@ } }, "Searchable" : { + "/tags/*/attribution/actor" : { + "fieldName" : "editedFieldTagAttributionActors", + "fieldType" : "URN" + }, + "/tags/*/attribution/source" : { + "fieldName" : "editedFieldTagAttributionSources", + "fieldType" : "URN" + }, + "/tags/*/attribution/time" : { + "fieldName" : "editedFieldTagAttributionDates", + "fieldType" : "DATETIME" + }, "/tags/*/tag" : { "boostScore" : 0.5, "fieldName" : "editedFieldTags", @@ -2987,6 +3087,18 @@ } }, "Searchable" : { + "/terms/*/attribution/actor" : { + "fieldName" : "editedFieldTermAttributionActors", + "fieldType" : "URN" + }, + "/terms/*/attribution/source" : { + "fieldName" : "editedFieldTermAttributionSources", + "fieldType" : "URN" + }, + "/terms/*/attribution/time" : { + "fieldName" : "editedFieldTermAttributionDates", + "fieldType" : "DATETIME" + }, "/terms/*/urn" : { "boostScore" : 0.5, "fieldName" : "editedFieldGlossaryTerms", diff --git a/metadata-service/restli-api/src/main/snapshot/com.linkedin.platform.platform.snapshot.json b/metadata-service/restli-api/src/main/snapshot/com.linkedin.platform.platform.snapshot.json index dbdba0040d443..cf43490cbdd6d 100644 --- a/metadata-service/restli-api/src/main/snapshot/com.linkedin.platform.platform.snapshot.json +++ b/metadata-service/restli-api/src/main/snapshot/com.linkedin.platform.platform.snapshot.json @@ -897,6 +897,51 @@ "type" : "string", "doc" : "Additional context about the association", "optional" : true + }, { + "name" : "attribution", + "type" : { + "type" : "record", + "name" : "MetadataAttribution", + "doc" : "Information about who, why, and how this metadata was applied", + "fields" : [ { + "name" : "time", + "type" : "Time", + "doc" : "When this metadata was updated." + }, { + "name" : "actor", + "type" : "Urn", + "doc" : "The entity (e.g. a member URN) responsible for applying the assocated metadata. This can\neither be a user (in case of UI edits) or the datahub system for automation." + }, { + "name" : "source", + "type" : "Urn", + "doc" : "The DataHub source responsible for applying the associated metadata. This will only be filled out\nwhen a DataHub source is responsible. This includes the specific metadata test urn, the automation urn.", + "optional" : true + }, { + "name" : "sourceDetail", + "type" : { + "type" : "map", + "values" : "string" + }, + "doc" : "The details associated with why this metadata was applied. For example, this could include\nthe actual regex rule, sql statement, ingestion pipeline ID, etc.", + "default" : { } + } ] + }, + "doc" : "Information about who, why, and how this metadata was applied", + "optional" : true, + "Searchable" : { + "/actor" : { + "fieldName" : "tagAttributionActors", + "fieldType" : "URN" + }, + "/source" : { + "fieldName" : "tagAttributionSources", + "fieldType" : "URN" + }, + "/time" : { + "fieldName" : "tagAttributionDates", + "fieldType" : "DATETIME" + } + } } ] } }, @@ -1005,6 +1050,25 @@ "type" : "string", "doc" : "Additional context about the association", "optional" : true + }, { + "name" : "attribution", + "type" : "MetadataAttribution", + "doc" : "Information about who, why, and how this metadata was applied", + "optional" : true, + "Searchable" : { + "/actor" : { + "fieldName" : "termAttributionActors", + "fieldType" : "URN" + }, + "/source" : { + "fieldName" : "termAttributionSources", + "fieldType" : "URN" + }, + "/time" : { + "fieldName" : "termAttributionDates", + "fieldType" : "DATETIME" + } + } } ] }, "com.linkedin.common.GlossaryTermUrn", { "type" : "record", @@ -1152,7 +1216,7 @@ "owningTeam" : "urn:li:internalTeam:datahub" } } - }, { + }, "com.linkedin.common.MetadataAttribution", { "type" : "record", "name" : "Owner", "namespace" : "com.linkedin.common", @@ -3437,6 +3501,18 @@ } }, "Searchable" : { + "/tags/*/attribution/actor" : { + "fieldName" : "fieldTagAttributionActors", + "fieldType" : "URN" + }, + "/tags/*/attribution/source" : { + "fieldName" : "fieldTagAttributionSources", + "fieldType" : "URN" + }, + "/tags/*/attribution/time" : { + "fieldName" : "fieldTagAttributionDates", + "fieldType" : "DATETIME" + }, "/tags/*/tag" : { "boostScore" : 0.5, "fieldName" : "fieldTags", @@ -3455,6 +3531,18 @@ } }, "Searchable" : { + "/terms/*/attribution/actor" : { + "fieldName" : "fieldTermAttributionActors", + "fieldType" : "URN" + }, + "/terms/*/attribution/source" : { + "fieldName" : "fieldTermAttributionSources", + "fieldType" : "URN" + }, + "/terms/*/attribution/time" : { + "fieldName" : "fieldTermAttributionDates", + "fieldType" : "DATETIME" + }, "/terms/*/urn" : { "boostScore" : 0.5, "fieldName" : "fieldGlossaryTerms", @@ -3624,6 +3712,18 @@ } }, "Searchable" : { + "/tags/*/attribution/actor" : { + "fieldName" : "editedFieldTagAttributionActors", + "fieldType" : "URN" + }, + "/tags/*/attribution/source" : { + "fieldName" : "editedFieldTagAttributionSources", + "fieldType" : "URN" + }, + "/tags/*/attribution/time" : { + "fieldName" : "editedFieldTagAttributionDates", + "fieldType" : "DATETIME" + }, "/tags/*/tag" : { "boostScore" : 0.5, "fieldName" : "editedFieldTags", @@ -3642,6 +3742,18 @@ } }, "Searchable" : { + "/terms/*/attribution/actor" : { + "fieldName" : "editedFieldTermAttributionActors", + "fieldType" : "URN" + }, + "/terms/*/attribution/source" : { + "fieldName" : "editedFieldTermAttributionSources", + "fieldType" : "URN" + }, + "/terms/*/attribution/time" : { + "fieldName" : "editedFieldTermAttributionDates", + "fieldType" : "DATETIME" + }, "/terms/*/urn" : { "boostScore" : 0.5, "fieldName" : "editedFieldGlossaryTerms", diff --git a/metadata-service/war/src/main/resources/boot/policies.json b/metadata-service/war/src/main/resources/boot/policies.json index 53c773d130f32..b684fd88e61c2 100644 --- a/metadata-service/war/src/main/resources/boot/policies.json +++ b/metadata-service/war/src/main/resources/boot/policies.json @@ -2,15 +2,15 @@ { "urn": "urn:li:dataHubPolicy:0", "info": { - "actors":{ - "resourceOwners":false, - "allUsers":false, - "allGroups":false, - "users":[ + "actors": { + "resourceOwners": false, + "allUsers": false, + "allGroups": false, + "users": [ "urn:li:corpuser:datahub" ] }, - "privileges":[ + "privileges": [ "MANAGE_POLICIES", "MANAGE_INGESTION", "MANAGE_SECRETS", @@ -36,27 +36,28 @@ "CREATE_BUSINESS_ATTRIBUTE", "MANAGE_BUSINESS_ATTRIBUTE", "MANAGE_STRUCTURED_PROPERTIES", - "MANAGE_DOCUMENTATION_FORMS" + "MANAGE_DOCUMENTATION_FORMS", + "MANAGE_FEATURES" ], - "displayName":"Root User - All Platform Privileges", - "description":"Grants all platform privileges to root user.", - "state":"ACTIVE", - "type":"PLATFORM", - "editable":false + "displayName": "Root User - All Platform Privileges", + "description": "Grants all platform privileges to root user.", + "state": "ACTIVE", + "type": "PLATFORM", + "editable": false } }, { "urn": "urn:li:dataHubPolicy:1", "info": { - "actors":{ - "resourceOwners":false, - "allUsers":false, - "allGroups":false, - "users":[ + "actors": { + "resourceOwners": false, + "allUsers": false, + "allGroups": false, + "users": [ "urn:li:corpuser:datahub" ] }, - "privileges":[ + "privileges": [ "EDIT_ENTITY", "VIEW_ENTITY_PAGE", "EDIT_LINEAGE", @@ -72,43 +73,43 @@ "DELETE_ENTITY", "ES_EXPLAIN_QUERY_PRIVILEGE" ], - "displayName":"Root User - Edit and View All Resources", - "description":"Grants all edit and view privileges for all resources to root user.", - "state":"ACTIVE", - "type":"METADATA", - "editable":false + "displayName": "Root User - Edit and View All Resources", + "description": "Grants all edit and view privileges for all resources to root user.", + "state": "ACTIVE", + "type": "METADATA", + "editable": false } }, { "urn": "urn:li:dataHubPolicy:7", "info": { - "actors":{ - "resourceOwners":false, - "allUsers":true, - "allGroups":false, - "users":[] + "actors": { + "resourceOwners": false, + "allUsers": true, + "allGroups": false, + "users": [] }, - "privileges":[ + "privileges": [ "VIEW_ANALYTICS", "GENERATE_PERSONAL_ACCESS_TOKENS" ], - "displayName":"All Users - Base Platform Privileges", - "description":"Grants base platform privileges to ALL users of DataHub. Change this policy to alter that behavior.", - "state":"ACTIVE", - "type":"PLATFORM", - "editable":true + "displayName": "All Users - Base Platform Privileges", + "description": "Grants base platform privileges to ALL users of DataHub. Change this policy to alter that behavior.", + "state": "ACTIVE", + "type": "PLATFORM", + "editable": true } }, { "urn": "urn:li:dataHubPolicy:view-entity-page-all", "info": { - "actors":{ - "resourceOwners":false, - "allUsers":true, - "allGroups":false, - "users":[] + "actors": { + "resourceOwners": false, + "allUsers": true, + "allGroups": false, + "users": [] }, - "privileges":[ + "privileges": [ "VIEW_ENTITY_PAGE", "SEARCH_PRIVILEGE", "GET_COUNTS_PRIVILEGE", @@ -117,47 +118,47 @@ "GET_TIMELINE_PRIVILEGE", "ES_EXPLAIN_QUERY_PRIVILEGE" ], - "displayName":"All Users - View Entity Page", - "description":"Grants entity view to all users", - "state":"ACTIVE", - "type":"METADATA", - "editable":true + "displayName": "All Users - View Entity Page", + "description": "Grants entity view to all users", + "state": "ACTIVE", + "type": "METADATA", + "editable": true } }, { "urn": "urn:li:dataHubPolicy:view-dataset-sensitive", "info": { - "actors":{ - "resourceOwners":false, - "allUsers":true, - "allGroups":false, - "users":[] + "actors": { + "resourceOwners": false, + "allUsers": true, + "allGroups": false, + "users": [] }, - "privileges":[ + "privileges": [ "VIEW_DATASET_USAGE", "VIEW_DATASET_PROFILE" ], - "displayName":"All Users - View Dataset Sensitive Information", - "description":"Grants viewing privileges of usage and profile information of all datasets for all users", - "state":"ACTIVE", - "type":"METADATA", - "editable":true + "displayName": "All Users - View Dataset Sensitive Information", + "description": "Grants viewing privileges of usage and profile information of all datasets for all users", + "state": "ACTIVE", + "type": "METADATA", + "editable": true } }, { "urn": "urn:li:dataHubPolicy:admin-platform-policy", "info": { - "actors":{ - "resourceOwners":false, - "allUsers":false, - "allGroups":false, - "users":[], - "groups":[], - "roles":[ + "actors": { + "resourceOwners": false, + "allUsers": false, + "allGroups": false, + "users": [], + "groups": [], + "roles": [ "urn:li:dataHubRole:Admin" ] }, - "privileges":[ + "privileges": [ "MANAGE_POLICIES", "MANAGE_INGESTION", "MANAGE_SECRETS", @@ -183,29 +184,30 @@ "CREATE_BUSINESS_ATTRIBUTE", "MANAGE_BUSINESS_ATTRIBUTE", "MANAGE_STRUCTURED_PROPERTIES", - "MANAGE_DOCUMENTATION_FORMS" + "MANAGE_DOCUMENTATION_FORMS", + "MANAGE_FEATURES" ], - "displayName":"Admins - Platform Policy", - "description":"Admins have all platform privileges.", - "state":"ACTIVE", - "type":"PLATFORM", - "editable":false + "displayName": "Admins - Platform Policy", + "description": "Admins have all platform privileges.", + "state": "ACTIVE", + "type": "PLATFORM", + "editable": false } }, { "urn": "urn:li:dataHubPolicy:admin-metadata-policy", "info": { - "actors":{ - "resourceOwners":false, - "allUsers":false, - "allGroups":false, - "users":[], - "groups":[], - "roles":[ + "actors": { + "resourceOwners": false, + "allUsers": false, + "allGroups": false, + "users": [], + "groups": [], + "roles": [ "urn:li:dataHubRole:Admin" ] }, - "privileges":[ + "privileges": [ "VIEW_ENTITY_PAGE", "EDIT_ENTITY_TAGS", "EDIT_ENTITY_GLOSSARY_TERMS", @@ -243,27 +245,27 @@ "ES_EXPLAIN_QUERY_PRIVILEGE", "EDIT_ENTITY_PROPERTIES" ], - "displayName":"Admins - Metadata Policy", - "description":"Admins have all metadata privileges.", - "state":"ACTIVE", - "type":"METADATA", - "editable":false + "displayName": "Admins - Metadata Policy", + "description": "Admins have all metadata privileges.", + "state": "ACTIVE", + "type": "METADATA", + "editable": false } }, { "urn": "urn:li:dataHubPolicy:editor-platform-policy", "info": { - "actors":{ - "resourceOwners":false, - "allUsers":false, - "allGroups":false, - "users":[], - "groups":[], - "roles":[ + "actors": { + "resourceOwners": false, + "allUsers": false, + "allGroups": false, + "users": [], + "groups": [], + "roles": [ "urn:li:dataHubRole:Editor" ] }, - "privileges":[ + "privileges": [ "GENERATE_PERSONAL_ACCESS_TOKENS", "MANAGE_DOMAINS", "MANAGE_GLOBAL_ANNOUNCEMENTS", @@ -271,29 +273,30 @@ "MANAGE_TAGS", "MANAGE_BUSINESS_ATTRIBUTE", "MANAGE_STRUCTURED_PROPERTIES", - "MANAGE_DOCUMENTATION_FORMS" + "MANAGE_DOCUMENTATION_FORMS", + "MANAGE_FEATURES" ], - "displayName":"Editors - Platform Policy", - "description":"Editors can manage ingestion and view analytics.", - "state":"ACTIVE", - "type":"PLATFORM", - "editable":false + "displayName": "Editors - Platform Policy", + "description": "Editors can manage ingestion and view analytics.", + "state": "ACTIVE", + "type": "PLATFORM", + "editable": false } }, { "urn": "urn:li:dataHubPolicy:editor-metadata-policy", "info": { - "actors":{ - "resourceOwners":false, - "allUsers":false, - "allGroups":false, - "users":[], - "groups":[], - "roles":[ + "actors": { + "resourceOwners": false, + "allUsers": false, + "allGroups": false, + "users": [], + "groups": [], + "roles": [ "urn:li:dataHubRole:Editor" ] }, - "privileges":[ + "privileges": [ "VIEW_ENTITY_PAGE", "EDIT_ENTITY_TAGS", "EDIT_ENTITY_GLOSSARY_TERMS", @@ -324,23 +327,23 @@ "ES_EXPLAIN_QUERY_PRIVILEGE", "EDIT_ENTITY_PROPERTIES" ], - "displayName":"Editors - Metadata Policy", - "description":"Editors have all metadata privileges.", - "state":"ACTIVE", - "type":"METADATA", - "editable":false + "displayName": "Editors - Metadata Policy", + "description": "Editors have all metadata privileges.", + "state": "ACTIVE", + "type": "METADATA", + "editable": false } }, { "urn": "urn:li:dataHubPolicy:editor-metadata-policy-entities", "info": { - "actors":{ - "resourceOwners":false, - "allUsers":false, - "allGroups":false, - "users":[], - "groups":[], - "roles":[ + "actors": { + "resourceOwners": false, + "allUsers": false, + "allGroups": false, + "users": [], + "groups": [], + "roles": [ "urn:li:dataHubRole:Editor" ] }, @@ -369,51 +372,51 @@ ] } }, - "privileges":[ + "privileges": [ "EDIT_ENTITY" ], - "displayName":"Editors - Edit Metadata Entities", - "description":"Editors can edit primary metadata entities.", - "state":"ACTIVE", - "type":"METADATA", - "editable":true + "displayName": "Editors - Edit Metadata Entities", + "description": "Editors can edit primary metadata entities.", + "state": "ACTIVE", + "type": "METADATA", + "editable": true } }, { "urn": "urn:li:dataHubPolicy:reader-platform-policy", "info": { - "actors":{ - "resourceOwners":false, - "allUsers":false, - "allGroups":false, - "users":[], - "groups":[], - "roles":[ + "actors": { + "resourceOwners": false, + "allUsers": false, + "allGroups": false, + "users": [], + "groups": [], + "roles": [ "urn:li:dataHubRole:Reader" ] }, - "privileges":[], - "displayName":"Readers - Platform Policy", - "description":"Readers can view analytics.", - "state":"ACTIVE", - "type":"PLATFORM", - "editable":false + "privileges": [], + "displayName": "Readers - Platform Policy", + "description": "Readers can view analytics.", + "state": "ACTIVE", + "type": "PLATFORM", + "editable": false } }, { "urn": "urn:li:dataHubPolicy:reader-metadata-policy", "info": { - "actors":{ - "resourceOwners":false, - "allUsers":false, - "allGroups":false, - "users":[], - "groups":[], - "roles":[ + "actors": { + "resourceOwners": false, + "allUsers": false, + "allGroups": false, + "users": [], + "groups": [], + "roles": [ "urn:li:dataHubRole:Reader" ] }, - "privileges":[ + "privileges": [ "VIEW_ENTITY_PAGE", "VIEW_DATASET_USAGE", "VIEW_DATASET_PROFILE", @@ -424,24 +427,24 @@ "GET_TIMELINE_PRIVILEGE", "ES_EXPLAIN_QUERY_PRIVILEGE" ], - "displayName":"Readers - Metadata Policy", - "description":"Readers can view all assets.", - "state":"ACTIVE", - "type":"METADATA", - "editable":false + "displayName": "Readers - Metadata Policy", + "description": "Readers can view all assets.", + "state": "ACTIVE", + "type": "METADATA", + "editable": false } }, { "urn": "urn:li:dataHubPolicy:asset-owners-metadata-policy", "info": { - "actors":{ - "resourceOwners":true, - "allUsers":false, - "allGroups":false, - "users":[], - "groups":[] + "actors": { + "resourceOwners": true, + "allUsers": false, + "allGroups": false, + "users": [], + "groups": [] }, - "privileges":[ + "privileges": [ "VIEW_ENTITY_PAGE", "EDIT_ENTITY_TAGS", "EDIT_ENTITY_GLOSSARY_TERMS", @@ -475,11 +478,11 @@ "ES_EXPLAIN_QUERY_PRIVILEGE", "EDIT_ENTITY_PROPERTIES" ], - "displayName":"Asset Owners - Metadata Policy", - "description":"Asset Owners have all metadata privileges ONLY for assets they own.", - "state":"ACTIVE", - "type":"METADATA", - "editable":true + "displayName": "Asset Owners - Metadata Policy", + "description": "Asset Owners have all metadata privileges ONLY for assets they own.", + "state": "ACTIVE", + "type": "METADATA", + "editable": true } } -] +] \ No newline at end of file diff --git a/metadata-utils/src/main/java/com/linkedin/metadata/authorization/PoliciesConfig.java b/metadata-utils/src/main/java/com/linkedin/metadata/authorization/PoliciesConfig.java index c20b287a47141..a282c6be673d0 100644 --- a/metadata-utils/src/main/java/com/linkedin/metadata/authorization/PoliciesConfig.java +++ b/metadata-utils/src/main/java/com/linkedin/metadata/authorization/PoliciesConfig.java @@ -163,6 +163,10 @@ public class PoliciesConfig { "Manage Documentation Forms", "Manage forms assigned to assets to assist in documentation efforts."); + public static final Privilege MANAGE_FEATURES_PRIVILEGE = + Privilege.of( + "MANAGE_FEATURES", "Manage Features", "Umbrella privilege to manage all features."); + public static final List PLATFORM_PRIVILEGES = ImmutableList.of( MANAGE_POLICIES_PRIVILEGE, @@ -189,7 +193,8 @@ public class PoliciesConfig { MANAGE_BUSINESS_ATTRIBUTE_PRIVILEGE, MANAGE_CONNECTIONS_PRIVILEGE, MANAGE_STRUCTURED_PROPERTIES_PRIVILEGE, - MANAGE_DOCUMENTATION_FORMS_PRIVILEGE); + MANAGE_DOCUMENTATION_FORMS_PRIVILEGE, + MANAGE_FEATURES_PRIVILEGE); // Resource Privileges //