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 6fc6edc66f3572..f70c46ba943a5a 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 @@ -1,5 +1,8 @@ package com.linkedin.datahub.graphql; +import com.google.common.collect.ImmutableSet; +import java.util.Set; + /** Constants relating to GraphQL type system & execution. */ public class Constants { @@ -28,4 +31,11 @@ private Constants() {} public static final String BROWSE_PATH_V2_DELIMITER = "␟"; public static final String VERSION_STAMP_FIELD_NAME = "versionStamp"; public static final String ENTITY_FILTER_NAME = "_entityType"; + + public static final Set DEFAULT_PERSONA_URNS = + ImmutableSet.of( + "urn:li:dataHubPersona:technicalUser", + "urn:li:dataHubPersona:businessUser", + "urn:li:dataHubPersona:dataLeader", + "urn:li:dataHubPersona:dataSteward"); } 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 b17e4bd386bdac..6f2e250c17c34e 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 @@ -42,6 +42,7 @@ import com.linkedin.datahub.graphql.generated.CorpGroup; import com.linkedin.datahub.graphql.generated.CorpGroupInfo; import com.linkedin.datahub.graphql.generated.CorpUser; +import com.linkedin.datahub.graphql.generated.CorpUserEditableProperties; import com.linkedin.datahub.graphql.generated.CorpUserInfo; import com.linkedin.datahub.graphql.generated.CorpUserViewsSettings; import com.linkedin.datahub.graphql.generated.Dashboard; @@ -53,6 +54,7 @@ import com.linkedin.datahub.graphql.generated.DataHubView; import com.linkedin.datahub.graphql.generated.DataJob; import com.linkedin.datahub.graphql.generated.DataJobInputOutput; +import com.linkedin.datahub.graphql.generated.DataPlatform; import com.linkedin.datahub.graphql.generated.DataPlatformInstance; import com.linkedin.datahub.graphql.generated.DataQualityContract; import com.linkedin.datahub.graphql.generated.Dataset; @@ -1823,6 +1825,18 @@ private void configureCorpUserResolvers(final RuntimeWiring.Builder builder) { new LoadableTypeResolver<>( corpUserType, (env) -> ((CorpUserInfo) env.getSource()).getManager().getUrn()))); + builder.type( + "CorpUserEditableProperties", + typeWiring -> + typeWiring.dataFetcher( + "platforms", + new LoadableTypeBatchResolver<>( + dataPlatformType, + (env) -> + ((CorpUserEditableProperties) env.getSource()) + .getPlatforms().stream() + .map(DataPlatform::getUrn) + .collect(Collectors.toList())))); } /** diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/corpuser/CorpUserType.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/corpuser/CorpUserType.java index b1ce42e72482a5..3c2bfd7225edf5 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/corpuser/CorpUserType.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/corpuser/CorpUserType.java @@ -1,11 +1,13 @@ package com.linkedin.datahub.graphql.types.corpuser; +import static com.linkedin.datahub.graphql.Constants.DEFAULT_PERSONA_URNS; import static com.linkedin.datahub.graphql.resolvers.mutate.MutationUtils.*; import static com.linkedin.metadata.Constants.*; import com.datahub.authorization.ConjunctivePrivilegeGroup; import com.datahub.authorization.DisjunctivePrivilegeGroup; import com.google.common.collect.ImmutableList; +import com.linkedin.common.UrnArray; import com.linkedin.common.url.Url; import com.linkedin.common.urn.Urn; import com.linkedin.common.urn.UrnUtils; @@ -14,6 +16,8 @@ import com.linkedin.datahub.graphql.QueryContext; import com.linkedin.datahub.graphql.authorization.AuthorizationUtils; import com.linkedin.datahub.graphql.exception.AuthorizationException; +import com.linkedin.datahub.graphql.exception.DataHubGraphQLErrorCode; +import com.linkedin.datahub.graphql.exception.DataHubGraphQLException; import com.linkedin.datahub.graphql.featureflags.FeatureFlags; import com.linkedin.datahub.graphql.generated.AutoCompleteResults; import com.linkedin.datahub.graphql.generated.CorpUser; @@ -246,7 +250,20 @@ private RecordTemplate mapCorpUserEditableInfo( if (input.getEmail() != null) { result.setEmail(input.getEmail()); } - + if (input.getPlatformUrns() != null) { + result.setPlatforms( + new UrnArray( + input.getPlatformUrns().stream().map(UrnUtils::getUrn).collect(Collectors.toList()))); + } + if (input.getPersonaUrn() != null) { + if (DEFAULT_PERSONA_URNS.contains(input.getPersonaUrn())) { + result.setPersona(UrnUtils.getUrn(input.getPersonaUrn())); + } else { + throw new DataHubGraphQLException( + String.format("Provided persona urn %s does not exist", input.getPersonaUrn()), + DataHubGraphQLErrorCode.NOT_FOUND); + } + } return result; } } diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/corpuser/mappers/CorpUserEditableInfoMapper.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/corpuser/mappers/CorpUserEditableInfoMapper.java index 1ff2f069b8112c..38f3c75d7a9fa8 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/corpuser/mappers/CorpUserEditableInfoMapper.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/corpuser/mappers/CorpUserEditableInfoMapper.java @@ -2,7 +2,10 @@ import com.linkedin.datahub.graphql.QueryContext; import com.linkedin.datahub.graphql.generated.CorpUserEditableProperties; +import com.linkedin.datahub.graphql.generated.DataHubPersona; +import com.linkedin.datahub.graphql.generated.DataPlatform; import com.linkedin.datahub.graphql.types.mappers.ModelMapper; +import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -38,6 +41,22 @@ public CorpUserEditableProperties apply( if (info.hasPictureLink()) { result.setPictureLink(info.getPictureLink().toString()); } + if (info.hasPlatforms()) { + result.setPlatforms( + info.getPlatforms().stream() + .map( + urn -> { + DataPlatform platform = new DataPlatform(); + platform.setUrn(urn.toString()); + return platform; + }) + .collect(Collectors.toList())); + } + if (info.hasPersona()) { + DataHubPersona persona = new DataHubPersona(); + persona.setUrn(info.getPersona().toString()); + result.setPersona(persona); + } return result; } } diff --git a/datahub-graphql-core/src/main/resources/entity.graphql b/datahub-graphql-core/src/main/resources/entity.graphql index d48a9976e15d77..89c7b4a4cd0556 100644 --- a/datahub-graphql-core/src/main/resources/entity.graphql +++ b/datahub-graphql-core/src/main/resources/entity.graphql @@ -4139,6 +4139,16 @@ type CorpUserEditableProperties { Email address for the user """ email: String + + """ + User persona, if present + """ + persona: DataHubPersona + + """ + Platforms commonly used by the user, if present. + """ + platforms: [DataPlatform!] } """ @@ -4189,6 +4199,16 @@ input CorpUserUpdateInput { Email address for the user """ email: String + + """ + The platforms that the user frequently works with + """ + platformUrns: [String!] + + """ + The user's persona urn" + """ + personaUrn: String } """ @@ -12142,6 +12162,7 @@ input CreateDataProductPropertiesInput { description: String } + """ Input properties required for update a DataProduct """ @@ -12307,6 +12328,16 @@ input UpdateOwnershipTypeInput { description: String } +""" +A standardized type of a user +""" +type DataHubPersona { + """ + The urn of the persona type + """ + urn: String! +} + """ Describes a generic filter on a dataset """ diff --git a/metadata-models/src/main/pegasus/com/linkedin/identity/CorpUserEditableInfo.pdl b/metadata-models/src/main/pegasus/com/linkedin/identity/CorpUserEditableInfo.pdl index 48ee53377e5820..9667c93c8b7709 100644 --- a/metadata-models/src/main/pegasus/com/linkedin/identity/CorpUserEditableInfo.pdl +++ b/metadata-models/src/main/pegasus/com/linkedin/identity/CorpUserEditableInfo.pdl @@ -1,6 +1,7 @@ namespace com.linkedin.identity import com.linkedin.common.Url +import com.linkedin.common.Urn /** * Linkedin corp user information that can be edited from UI @@ -56,6 +57,26 @@ record CorpUserEditableInfo { */ title: optional string + /** + * The platforms that the user commonly works with + */ + @Relationship = { + "/*": { + "name": "IsUserOf", + "entityTypes": ["dataPlatform"] + } + } + platforms: optional array[Urn] + + /** + * The user's persona type, based on their role + */ + @Relationship = { + "name": "IsPersona", + "entityTypes": ["dataHubPersona"] + } + persona: optional Urn + /** * Slack handle for the user */ diff --git a/metadata-models/src/main/pegasus/com/linkedin/metadata/key/DataHubPersonaKey.pdl b/metadata-models/src/main/pegasus/com/linkedin/metadata/key/DataHubPersonaKey.pdl new file mode 100644 index 00000000000000..296444221af3a5 --- /dev/null +++ b/metadata-models/src/main/pegasus/com/linkedin/metadata/key/DataHubPersonaKey.pdl @@ -0,0 +1,14 @@ +namespace com.linkedin.metadata.key + +/** + * Key for a persona type + */ +@Aspect = { + "name": "dataHubPersonaKey" +} +record DataHubPersonaKey { + /** + * A unique id for the persona type + */ + id: string +} diff --git a/metadata-models/src/main/pegasus/com/linkedin/persona/DataHubPersonaInfo.pdl b/metadata-models/src/main/pegasus/com/linkedin/persona/DataHubPersonaInfo.pdl new file mode 100644 index 00000000000000..fb422993b27ddc --- /dev/null +++ b/metadata-models/src/main/pegasus/com/linkedin/persona/DataHubPersonaInfo.pdl @@ -0,0 +1,10 @@ +namespace com.linkedin.persona + +/** + * Placeholder aspect for persona type info + */ +@Aspect = { + "name": "dataHubPersonaInfo" +} +record DataHubPersonaInfo { +} diff --git a/metadata-models/src/main/resources/entity-registry.yml b/metadata-models/src/main/resources/entity-registry.yml index ed19cd3a1d4860..6a6683418bf386 100644 --- a/metadata-models/src/main/resources/entity-registry.yml +++ b/metadata-models/src/main/resources/entity-registry.yml @@ -542,6 +542,11 @@ entities: - dataContractProperties - dataContractStatus - status + - name: dataHubPersona + category: internal + keyAspect: dataHubPersonaKey + aspects: + - dataHubPersonaInfo - name: entityType doc: A type of entity in the DataHub Metadata Model. category: core 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 bfa887ffda1175..c40137b265cff0 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 @@ -2353,6 +2353,29 @@ "type" : "string", "doc" : "DataHub-native Title, e.g. 'Software Engineer'", "optional" : true + }, { + "name" : "platforms", + "type" : { + "type" : "array", + "items" : "com.linkedin.common.Urn" + }, + "doc" : "The platforms that the user commonly works with", + "optional" : true, + "Relationship" : { + "/*" : { + "entityTypes" : [ "dataPlatform" ], + "name" : "IsUserOf" + } + } + }, { + "name" : "persona", + "type" : "com.linkedin.common.Urn", + "doc" : "The user's persona type, based on their role", + "optional" : true, + "Relationship" : { + "entityTypes" : [ "dataHubPersona" ], + "name" : "IsPersona" + } }, { "name" : "slack", "type" : "string", 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 5dcedfecf99ca4..aeb5fbef5af2f2 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 @@ -2766,6 +2766,29 @@ "type" : "string", "doc" : "DataHub-native Title, e.g. 'Software Engineer'", "optional" : true + }, { + "name" : "platforms", + "type" : { + "type" : "array", + "items" : "com.linkedin.common.Urn" + }, + "doc" : "The platforms that the user commonly works with", + "optional" : true, + "Relationship" : { + "/*" : { + "entityTypes" : [ "dataPlatform" ], + "name" : "IsUserOf" + } + } + }, { + "name" : "persona", + "type" : "com.linkedin.common.Urn", + "doc" : "The user's persona type, based on their role", + "optional" : true, + "Relationship" : { + "entityTypes" : [ "dataHubPersona" ], + "name" : "IsPersona" + } }, { "name" : "slack", "type" : "string", 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 a665548fcd078d..18ef55011ed5af 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 @@ -2086,6 +2086,29 @@ "type" : "string", "doc" : "DataHub-native Title, e.g. 'Software Engineer'", "optional" : true + }, { + "name" : "platforms", + "type" : { + "type" : "array", + "items" : "com.linkedin.common.Urn" + }, + "doc" : "The platforms that the user commonly works with", + "optional" : true, + "Relationship" : { + "/*" : { + "entityTypes" : [ "dataPlatform" ], + "name" : "IsUserOf" + } + } + }, { + "name" : "persona", + "type" : "com.linkedin.common.Urn", + "doc" : "The user's persona type, based on their role", + "optional" : true, + "Relationship" : { + "entityTypes" : [ "dataHubPersona" ], + "name" : "IsPersona" + } }, { "name" : "slack", "type" : "string", 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 e08a6eecd0e6e3..cf059788209119 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 @@ -2080,6 +2080,29 @@ "type" : "string", "doc" : "DataHub-native Title, e.g. 'Software Engineer'", "optional" : true + }, { + "name" : "platforms", + "type" : { + "type" : "array", + "items" : "com.linkedin.common.Urn" + }, + "doc" : "The platforms that the user commonly works with", + "optional" : true, + "Relationship" : { + "/*" : { + "entityTypes" : [ "dataPlatform" ], + "name" : "IsUserOf" + } + } + }, { + "name" : "persona", + "type" : "com.linkedin.common.Urn", + "doc" : "The user's persona type, based on their role", + "optional" : true, + "Relationship" : { + "entityTypes" : [ "dataHubPersona" ], + "name" : "IsPersona" + } }, { "name" : "slack", "type" : "string", 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 8f4c871405e245..15f16dd2ea6cd0 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 @@ -2760,6 +2760,29 @@ "type" : "string", "doc" : "DataHub-native Title, e.g. 'Software Engineer'", "optional" : true + }, { + "name" : "platforms", + "type" : { + "type" : "array", + "items" : "com.linkedin.common.Urn" + }, + "doc" : "The platforms that the user commonly works with", + "optional" : true, + "Relationship" : { + "/*" : { + "entityTypes" : [ "dataPlatform" ], + "name" : "IsUserOf" + } + } + }, { + "name" : "persona", + "type" : "com.linkedin.common.Urn", + "doc" : "The user's persona type, based on their role", + "optional" : true, + "Relationship" : { + "entityTypes" : [ "dataHubPersona" ], + "name" : "IsPersona" + } }, { "name" : "slack", "type" : "string",