forked from datahub-project/datahub
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(posts): add posts feature to DataHub (datahub-project#6110)
- Loading branch information
1 parent
25d3931
commit 20eec38
Showing
29 changed files
with
1,246 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
60 changes: 60 additions & 0 deletions
60
...ql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/post/CreatePostResolver.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package com.linkedin.datahub.graphql.resolvers.post; | ||
|
||
import com.datahub.authentication.Authentication; | ||
import com.datahub.authentication.post.PostService; | ||
import com.linkedin.common.Media; | ||
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.generated.CreatePostInput; | ||
import com.linkedin.datahub.graphql.generated.PostContentType; | ||
import com.linkedin.datahub.graphql.generated.PostType; | ||
import com.linkedin.datahub.graphql.generated.UpdateMediaInput; | ||
import com.linkedin.datahub.graphql.generated.UpdatePostContentInput; | ||
import com.linkedin.post.PostContent; | ||
import graphql.schema.DataFetcher; | ||
import graphql.schema.DataFetchingEnvironment; | ||
import java.util.concurrent.CompletableFuture; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
|
||
import static com.linkedin.datahub.graphql.resolvers.ResolverUtils.*; | ||
|
||
|
||
@Slf4j | ||
@RequiredArgsConstructor | ||
public class CreatePostResolver implements DataFetcher<CompletableFuture<Boolean>> { | ||
private final PostService _postService; | ||
|
||
@Override | ||
public CompletableFuture<Boolean> get(final DataFetchingEnvironment environment) throws Exception { | ||
final QueryContext context = environment.getContext(); | ||
|
||
if (!AuthorizationUtils.canCreateGlobalAnnouncements(context)) { | ||
throw new AuthorizationException( | ||
"Unauthorized to create posts. Please contact your DataHub administrator if this needs corrective action."); | ||
} | ||
|
||
final CreatePostInput input = bindArgument(environment.getArgument("input"), CreatePostInput.class); | ||
final PostType type = input.getPostType(); | ||
final UpdatePostContentInput content = input.getContent(); | ||
final PostContentType contentType = content.getContentType(); | ||
final String title = content.getTitle(); | ||
final String link = content.getLink(); | ||
final String description = content.getDescription(); | ||
final UpdateMediaInput updateMediaInput = content.getMedia(); | ||
final Authentication authentication = context.getAuthentication(); | ||
|
||
Media media = updateMediaInput == null ? null | ||
: _postService.mapMedia(updateMediaInput.getType().toString(), updateMediaInput.getLocation()); | ||
PostContent postContent = _postService.mapPostContent(contentType.toString(), title, description, link, media); | ||
|
||
return CompletableFuture.supplyAsync(() -> { | ||
try { | ||
return _postService.createPost(type.toString(), postContent, authentication); | ||
} catch (Exception e) { | ||
throw new RuntimeException("Failed to create a new post", e); | ||
} | ||
}); | ||
} | ||
} |
72 changes: 72 additions & 0 deletions
72
...hql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/post/ListPostsResolver.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package com.linkedin.datahub.graphql.resolvers.post; | ||
|
||
import com.datahub.authentication.Authentication; | ||
import com.linkedin.common.urn.Urn; | ||
import com.linkedin.datahub.graphql.QueryContext; | ||
import com.linkedin.datahub.graphql.generated.ListPostsInput; | ||
import com.linkedin.datahub.graphql.generated.ListPostsResult; | ||
import com.linkedin.datahub.graphql.types.post.PostMapper; | ||
import com.linkedin.entity.EntityResponse; | ||
import com.linkedin.entity.client.EntityClient; | ||
import com.linkedin.metadata.query.filter.SortCriterion; | ||
import com.linkedin.metadata.query.filter.SortOrder; | ||
import com.linkedin.metadata.search.SearchEntity; | ||
import com.linkedin.metadata.search.SearchResult; | ||
import graphql.schema.DataFetcher; | ||
import graphql.schema.DataFetchingEnvironment; | ||
import java.util.HashSet; | ||
import java.util.Map; | ||
import java.util.concurrent.CompletableFuture; | ||
import java.util.stream.Collectors; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
|
||
import static com.linkedin.datahub.graphql.resolvers.ResolverUtils.*; | ||
import static com.linkedin.metadata.Constants.*; | ||
|
||
|
||
@Slf4j | ||
@RequiredArgsConstructor | ||
public class ListPostsResolver implements DataFetcher<CompletableFuture<ListPostsResult>> { | ||
private static final Integer DEFAULT_START = 0; | ||
private static final Integer DEFAULT_COUNT = 20; | ||
private static final String DEFAULT_QUERY = ""; | ||
|
||
private final EntityClient _entityClient; | ||
|
||
@Override | ||
public CompletableFuture<ListPostsResult> get(final DataFetchingEnvironment environment) throws Exception { | ||
final QueryContext context = environment.getContext(); | ||
final Authentication authentication = context.getAuthentication(); | ||
|
||
final ListPostsInput input = bindArgument(environment.getArgument("input"), ListPostsInput.class); | ||
final Integer start = input.getStart() == null ? DEFAULT_START : input.getStart(); | ||
final Integer count = input.getCount() == null ? DEFAULT_COUNT : input.getCount(); | ||
final String query = input.getQuery() == null ? DEFAULT_QUERY : input.getQuery(); | ||
|
||
return CompletableFuture.supplyAsync(() -> { | ||
try { | ||
final SortCriterion sortCriterion = | ||
new SortCriterion().setField(LAST_MODIFIED_FIELD_NAME).setOrder(SortOrder.DESCENDING); | ||
|
||
// First, get all Post Urns. | ||
final SearchResult gmsResult = _entityClient.search(POST_ENTITY_NAME, query, null, sortCriterion, start, count, | ||
context.getAuthentication()); | ||
|
||
// Then, get and hydrate all Posts. | ||
final Map<Urn, EntityResponse> entities = _entityClient.batchGetV2(POST_ENTITY_NAME, | ||
new HashSet<>(gmsResult.getEntities().stream().map(SearchEntity::getEntity).collect(Collectors.toList())), | ||
null, authentication); | ||
|
||
final ListPostsResult result = new ListPostsResult(); | ||
result.setStart(gmsResult.getFrom()); | ||
result.setCount(gmsResult.getPageSize()); | ||
result.setTotal(gmsResult.getNumEntities()); | ||
result.setPosts(entities.values().stream().map(PostMapper::map).collect(Collectors.toList())); | ||
return result; | ||
} catch (Exception e) { | ||
throw new RuntimeException("Failed to list posts", e); | ||
} | ||
}); | ||
} | ||
} |
76 changes: 76 additions & 0 deletions
76
datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/post/PostMapper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package com.linkedin.datahub.graphql.types.post; | ||
|
||
import com.linkedin.data.DataMap; | ||
import com.linkedin.datahub.graphql.generated.AuditStamp; | ||
import com.linkedin.datahub.graphql.generated.EntityType; | ||
import com.linkedin.datahub.graphql.generated.Media; | ||
import com.linkedin.datahub.graphql.generated.MediaType; | ||
import com.linkedin.datahub.graphql.generated.Post; | ||
import com.linkedin.datahub.graphql.generated.PostContent; | ||
import com.linkedin.datahub.graphql.generated.PostContentType; | ||
import com.linkedin.datahub.graphql.generated.PostType; | ||
import com.linkedin.datahub.graphql.types.common.mappers.util.MappingHelper; | ||
import com.linkedin.datahub.graphql.types.mappers.ModelMapper; | ||
import com.linkedin.entity.EntityResponse; | ||
import com.linkedin.entity.EnvelopedAspectMap; | ||
import com.linkedin.post.PostInfo; | ||
import javax.annotation.Nonnull; | ||
|
||
import static com.linkedin.metadata.Constants.*; | ||
|
||
|
||
public class PostMapper implements ModelMapper<EntityResponse, Post> { | ||
|
||
public static final PostMapper INSTANCE = new PostMapper(); | ||
|
||
public static Post map(@Nonnull final EntityResponse entityResponse) { | ||
return INSTANCE.apply(entityResponse); | ||
} | ||
|
||
@Override | ||
public Post apply(@Nonnull final EntityResponse entityResponse) { | ||
final Post result = new Post(); | ||
|
||
result.setUrn(entityResponse.getUrn().toString()); | ||
result.setType(EntityType.POST); | ||
EnvelopedAspectMap aspectMap = entityResponse.getAspects(); | ||
MappingHelper<Post> mappingHelper = new MappingHelper<>(aspectMap, result); | ||
mappingHelper.mapToResult(POST_INFO_ASPECT_NAME, this::mapPostInfo); | ||
return mappingHelper.getResult(); | ||
} | ||
|
||
private void mapPostInfo(@Nonnull Post post, @Nonnull DataMap dataMap) { | ||
PostInfo postInfo = new PostInfo(dataMap); | ||
post.setPostType(PostType.valueOf(postInfo.getType().toString())); | ||
post.setContent(mapPostContent(postInfo.getContent())); | ||
AuditStamp lastModified = new AuditStamp(); | ||
lastModified.setTime(postInfo.getLastModified()); | ||
post.setLastModified(lastModified); | ||
} | ||
|
||
@Nonnull | ||
private com.linkedin.datahub.graphql.generated.PostContent mapPostContent( | ||
@Nonnull com.linkedin.post.PostContent postContent) { | ||
PostContent result = new PostContent(); | ||
result.setContentType(PostContentType.valueOf(postContent.getType().toString())); | ||
result.setTitle(postContent.getTitle()); | ||
if (postContent.hasDescription()) { | ||
result.setDescription(postContent.getDescription()); | ||
} | ||
if (postContent.hasLink()) { | ||
result.setLink(postContent.getLink().toString()); | ||
} | ||
if (postContent.hasMedia()) { | ||
result.setMedia(mapPostMedia(postContent.getMedia())); | ||
} | ||
return result; | ||
} | ||
|
||
@Nonnull | ||
private Media mapPostMedia(@Nonnull com.linkedin.common.Media postMedia) { | ||
Media result = new Media(); | ||
result.setType(MediaType.valueOf(postMedia.getType().toString())); | ||
result.setLocation(postMedia.getLocation().toString()); | ||
return result; | ||
} | ||
} |
Oops, something went wrong.