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 5b265b6714..d1da55268a 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
@@ -63,6 +63,7 @@ import com.linkedin.datahub.graphql.generated.Deprecation;
import com.linkedin.datahub.graphql.generated.Domain;
import com.linkedin.datahub.graphql.generated.ERModelRelationship;
import com.linkedin.datahub.graphql.generated.ERModelRelationshipProperties;
+import com.linkedin.datahub.graphql.generated.Entity;
import com.linkedin.datahub.graphql.generated.EntityPath;
import com.linkedin.datahub.graphql.generated.EntityRelationship;
import com.linkedin.datahub.graphql.generated.EntityRelationshipLegacy;
@@ -312,6 +313,7 @@ import com.linkedin.datahub.graphql.resolvers.type.EntityInterfaceTypeResolver;
import com.linkedin.datahub.graphql.resolvers.type.HyperParameterValueTypeResolver;
import com.linkedin.datahub.graphql.resolvers.type.PlatformSchemaUnionTypeResolver;
import com.linkedin.datahub.graphql.resolvers.type.PropertyValueResolver;
+import com.linkedin.datahub.graphql.resolvers.type.ResolvedActorResolver;
import com.linkedin.datahub.graphql.resolvers.type.ResultsTypeResolver;
import com.linkedin.datahub.graphql.resolvers.type.TimeSeriesAspectInterfaceTypeResolver;
import com.linkedin.datahub.graphql.resolvers.user.CreateNativeUserResetTokenResolver;
@@ -1730,12 +1732,22 @@ public class GmsGraphQLEngine {
.type(
"InstitutionalMemoryMetadata",
typeWiring ->
- typeWiring.dataFetcher(
- "author",
- new LoadableTypeResolver<>(
- corpUserType,
- (env) ->
- ((InstitutionalMemoryMetadata) env.getSource()).getAuthor().getUrn())))
+ typeWiring
+ .dataFetcher(
+ "author",
+ new LoadableTypeResolver<>(
+ corpUserType,
+ (env) ->
+ ((InstitutionalMemoryMetadata) env.getSource())
+ .getAuthor()
+ .getUrn()))
+ .dataFetcher(
+ "actor",
+ new EntityTypeResolver(
+ this.entityTypes,
+ (env) ->
+ (Entity)
+ ((InstitutionalMemoryMetadata) env.getSource()).getActor())))
.type(
"DatasetStatsSummary",
typeWiring ->
@@ -2242,6 +2254,7 @@ public class GmsGraphQLEngine {
"HyperParameterValueType",
typeWiring -> typeWiring.typeResolver(new HyperParameterValueTypeResolver()))
.type("PropertyValue", typeWiring -> typeWiring.typeResolver(new PropertyValueResolver()))
+ .type("ResolvedActor", typeWiring -> typeWiring.typeResolver(new ResolvedActorResolver()))
.type("Aspect", typeWiring -> typeWiring.typeResolver(new AspectInterfaceTypeResolver()))
.type(
"TimeSeriesAspect",
diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/type/ResolvedActorResolver.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/type/ResolvedActorResolver.java
new file mode 100644
index 0000000000..7ae719a23b
--- /dev/null
+++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/type/ResolvedActorResolver.java
@@ -0,0 +1,25 @@
+package com.linkedin.datahub.graphql.resolvers.type;
+
+import com.linkedin.datahub.graphql.generated.CorpGroup;
+import com.linkedin.datahub.graphql.generated.CorpUser;
+import graphql.TypeResolutionEnvironment;
+import graphql.schema.GraphQLObjectType;
+import graphql.schema.TypeResolver;
+
+public class ResolvedActorResolver implements TypeResolver {
+
+ public static final String CORP_USER = "CorpUser";
+ public static final String CORP_GROUP = "CorpGroup";
+
+ @Override
+ public GraphQLObjectType getType(TypeResolutionEnvironment env) {
+ if (env.getObject() instanceof CorpUser) {
+ return env.getSchema().getObjectType(CORP_USER);
+ } else if (env.getObject() instanceof CorpGroup) {
+ return env.getSchema().getObjectType(CORP_GROUP);
+ } else {
+ throw new RuntimeException(
+ "Unrecognized object type provided to type resolver, Type:" + env.getObject().toString());
+ }
+ }
+}
diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/common/mappers/InstitutionalMemoryMetadataMapper.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/common/mappers/InstitutionalMemoryMetadataMapper.java
index 7c6de02ecc..9781643c41 100644
--- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/common/mappers/InstitutionalMemoryMetadataMapper.java
+++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/common/mappers/InstitutionalMemoryMetadataMapper.java
@@ -28,6 +28,7 @@ public class InstitutionalMemoryMetadataMapper {
result.setDescription(input.getDescription()); // deprecated field
result.setLabel(input.getDescription());
result.setAuthor(getAuthor(input.getCreateStamp().getActor().toString()));
+ result.setActor(ResolvedActorMapper.map(input.getCreateStamp().getActor()));
result.setCreated(AuditStampMapper.map(context, input.getCreateStamp()));
result.setAssociatedUrn(entityUrn.toString());
return result;
diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/common/mappers/ResolvedActorMapper.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/common/mappers/ResolvedActorMapper.java
new file mode 100644
index 0000000000..c00ffd0b82
--- /dev/null
+++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/common/mappers/ResolvedActorMapper.java
@@ -0,0 +1,31 @@
+package com.linkedin.datahub.graphql.types.common.mappers;
+
+import com.linkedin.common.urn.Urn;
+import com.linkedin.datahub.graphql.generated.CorpGroup;
+import com.linkedin.datahub.graphql.generated.CorpUser;
+import com.linkedin.datahub.graphql.generated.EntityType;
+import com.linkedin.datahub.graphql.generated.ResolvedActor;
+import com.linkedin.metadata.Constants;
+import javax.annotation.Nonnull;
+
+public class ResolvedActorMapper {
+
+ public static final ResolvedActorMapper INSTANCE = new ResolvedActorMapper();
+
+ public static ResolvedActor map(@Nonnull final Urn actorUrn) {
+ return INSTANCE.apply(actorUrn);
+ }
+
+ public ResolvedActor apply(@Nonnull final Urn actorUrn) {
+ if (actorUrn.getEntityType().equals(Constants.CORP_GROUP_ENTITY_NAME)) {
+ CorpGroup partialGroup = new CorpGroup();
+ partialGroup.setUrn(actorUrn.toString());
+ partialGroup.setType(EntityType.CORP_GROUP);
+ return partialGroup;
+ }
+ CorpUser partialUser = new CorpUser();
+ partialUser.setUrn(actorUrn.toString());
+ partialUser.setType(EntityType.CORP_USER);
+ return (ResolvedActor) partialUser;
+ }
+}
diff --git a/datahub-graphql-core/src/main/resources/entity.graphql b/datahub-graphql-core/src/main/resources/entity.graphql
index 732a782139..049527e5d7 100644
--- a/datahub-graphql-core/src/main/resources/entity.graphql
+++ b/datahub-graphql-core/src/main/resources/entity.graphql
@@ -3005,8 +3005,14 @@ type InstitutionalMemoryMetadata {
"""
The author of this metadata
+ Deprecated! Use actor instead for users or groups.
"""
- author: CorpUser!
+ author: CorpUser! @deprecated(reason: "Use `actor`")
+
+ """
+ The author of this metadata
+ """
+ actor: ResolvedActor!
"""
An AuditStamp corresponding to the creation of this resource
@@ -3834,6 +3840,8 @@ enum CorpUserStatus {
ACTIVE
}
+union ResolvedActor = CorpUser | CorpGroup
+
"""
A DataHub User entity, which represents a Person on the Metadata Entity Graph
"""
diff --git a/datahub-web-react/src/Mocks.tsx b/datahub-web-react/src/Mocks.tsx
index aed672a34e..329d6250e5 100644
--- a/datahub-web-react/src/Mocks.tsx
+++ b/datahub-web-react/src/Mocks.tsx
@@ -566,6 +566,12 @@ export const dataset3 = {
username: 'datahub',
type: EntityType.CorpUser,
},
+ actor: {
+ __typename: 'CorpUser',
+ urn: 'urn:li:corpuser:datahub',
+ username: 'datahub',
+ type: EntityType.CorpUser,
+ },
description: 'This only points to Google',
label: 'This only points to Google',
created: {
diff --git a/datahub-web-react/src/app/entity/shared/containers/profile/sidebar/LinkButton.tsx b/datahub-web-react/src/app/entity/shared/containers/profile/sidebar/LinkButton.tsx
index 0ce3c9641d..c3896baeda 100644
--- a/datahub-web-react/src/app/entity/shared/containers/profile/sidebar/LinkButton.tsx
+++ b/datahub-web-react/src/app/entity/shared/containers/profile/sidebar/LinkButton.tsx
@@ -29,7 +29,7 @@ export default function LinkButton({ link }: Props) {
href={link.url}
target="_blank"
rel="noreferrer"
- key={`${link.label}-${link.url}-${link.author}`}
+ key={`${link.label}-${link.url}-${link.actor.urn}`}
>
{link.description || link.label}
diff --git a/datahub-web-react/src/app/entity/shared/tabs/Documentation/components/LinkList.tsx b/datahub-web-react/src/app/entity/shared/tabs/Documentation/components/LinkList.tsx
index 7212198bbf..6eb6807855 100644
--- a/datahub-web-react/src/app/entity/shared/tabs/Documentation/components/LinkList.tsx
+++ b/datahub-web-react/src/app/entity/shared/tabs/Documentation/components/LinkList.tsx
@@ -3,7 +3,7 @@ import { Link } from 'react-router-dom';
import styled from 'styled-components/macro';
import { message, Button, List, Typography, Modal, Form, Input } from 'antd';
import { LinkOutlined, DeleteOutlined, EditOutlined } from '@ant-design/icons';
-import { EntityType, InstitutionalMemoryMetadata } from '../../../../../../types.generated';
+import { InstitutionalMemoryMetadata } from '../../../../../../types.generated';
import { useEntityData, useMutationUrn } from '../../../EntityContext';
import { useEntityRegistry } from '../../../../../useEntityRegistry';
import { ANTD_GRAY } from '../../../constants';
@@ -182,10 +182,8 @@ export const LinkList = ({ refetch }: LinkListProps) => {
description={
<>
Added {formatDateString(link.created.time)} by{' '}
-
- {link.author.username}
+
+ {entityRegistry.getDisplayName(link.actor.type, link.actor)}
>
}
diff --git a/datahub-web-react/src/graphql-mock/mutationHelper.ts b/datahub-web-react/src/graphql-mock/mutationHelper.ts
index a97b41b53b..0cf4f5f87f 100644
--- a/datahub-web-react/src/graphql-mock/mutationHelper.ts
+++ b/datahub-web-react/src/graphql-mock/mutationHelper.ts
@@ -99,6 +99,7 @@ export const updateEntityLink = ({ entity, institutionalMemory }: UpdateEntityLi
description: e.description as string,
label: e.description as string,
author: { urn: e.author, username: '', type: EntityType.CorpUser },
+ actor: { urn: e.author, username: '', type: EntityType.CorpUser },
created: { time: Date.now(), actor: getActor(), __typename: 'AuditStamp' },
associatedUrn: dataEntity.urn,
};
diff --git a/datahub-web-react/src/graphql/domain.graphql b/datahub-web-react/src/graphql/domain.graphql
index 3897a2ced8..2e96a78b0f 100644
--- a/datahub-web-react/src/graphql/domain.graphql
+++ b/datahub-web-react/src/graphql/domain.graphql
@@ -19,9 +19,8 @@ query getDomain($urn: String!) {
institutionalMemory {
elements {
url
- author {
- urn
- username
+ actor {
+ ...resolvedActorFields
}
description
created {
diff --git a/datahub-web-react/src/graphql/fragments.graphql b/datahub-web-react/src/graphql/fragments.graphql
index 7ce4082c42..67dbdbbb22 100644
--- a/datahub-web-react/src/graphql/fragments.graphql
+++ b/datahub-web-react/src/graphql/fragments.graphql
@@ -202,12 +202,22 @@ fragment embedFields on Embed {
renderUrl
}
+fragment resolvedActorFields on ResolvedActor {
+ ... on CorpUser {
+ urn
+ ...entityDisplayNameFields
+ }
+ ... on CorpGroup {
+ urn
+ ...entityDisplayNameFields
+ }
+}
+
fragment institutionalMemoryFields on InstitutionalMemory {
elements {
url
- author {
- urn
- username
+ actor {
+ ...resolvedActorFields
}
description
created {