Skip to content

Commit

Permalink
feat: add addTagsToPost and getPostsByUserName QL endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
rajadilipkolli committed Nov 11, 2023
1 parent c7c035c commit 7b41b4c
Show file tree
Hide file tree
Showing 15 changed files with 188 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.example.graphql.querydsl.config;

import com.example.graphql.querydsl.entities.Post;
import com.example.graphql.querydsl.entities.PostComment;
import com.example.graphql.querydsl.entities.PostDetails;
import com.example.graphql.querydsl.entities.Tag;
import com.example.graphql.querydsl.repositories.PostRepository;
import java.time.LocalDateTime;
import lombok.RequiredArgsConstructor;
Expand All @@ -20,12 +20,13 @@ public class Initializer implements CommandLineRunner {
@Override
public void run(String... args) {
log.info("Running Initializer.....");
postRepository.deleteAll();
Post post = new Post().setTitle("title").setContent("content");
post.addDetails(new PostDetails()
.setCreatedOn(LocalDateTime.of(2023, 12, 31, 10, 35, 45, 99))
.setCreatedBy("appUser"));
post.addTag(new Tag().setName("java"));
post.addTag(new Tag().setName("spring"));
post.addComment(
new PostComment().setReview("review").setCreatedOn(LocalDateTime.of(2023, 12, 31, 10, 35, 45, 99)));
postRepository.save(post);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,8 @@ public class PostNotFoundException extends ResourceNotFoundException {
public PostNotFoundException(Long id) {
super("Post with Id '%d' not found".formatted(id));
}

public PostNotFoundException(String name) {
super("Post with UserName '%s' not found".formatted(name));
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.example.graphql.querydsl.gql;

import com.example.graphql.querydsl.model.request.AddTagRequest;
import com.example.graphql.querydsl.model.request.CreatePostRequest;
import com.example.graphql.querydsl.model.response.PostResponse;
import com.example.graphql.querydsl.services.PostService;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.graphql.data.method.annotation.Argument;
import org.springframework.graphql.data.method.annotation.MutationMapping;
Expand All @@ -20,8 +22,18 @@ public Long countPosts() {
return this.postService.totalPosts();
}

@QueryMapping
public List<PostResponse> getPostsByUserName(@Argument("name") String name) {
return this.postService.getPostsByUserName(name);
}

@MutationMapping
public PostResponse createPost(@Argument("createPostRequest") CreatePostRequest createPostRequest) {
return this.postService.savePost(createPostRequest);
}

@MutationMapping
public PostResponse addTagsToPost(@Argument("addTagRequest") AddTagRequest addTagRequest) {
return this.postService.addTagsToPost(addTagRequest);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@

import com.example.graphql.querydsl.entities.*;
import com.example.graphql.querydsl.model.request.CreatePostRequest;
import com.example.graphql.querydsl.model.request.TagRequest;
import com.example.graphql.querydsl.model.request.UpdatePostRequest;
import com.example.graphql.querydsl.model.response.PostResponse;
import com.example.graphql.querydsl.model.response.TagResponse;
import java.util.List;
import org.mapstruct.DecoratedWith;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;

@Mapper(uses = PostMapperDecorator.class)
@Mapper
@DecoratedWith(PostMapperDecorator.class)
public interface PostMapper {

@Mapping(target = "tags", ignore = true)
Expand All @@ -35,4 +38,6 @@ public interface PostMapper {
TagResponse postTagToTagResponse(PostTag tag);

List<PostResponse> toResponseList(List<Post> postList);

Post setTags(List<TagRequest> tagRequests, Post post);
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import org.springframework.util.CollectionUtils;

@Mapper
public abstract class PostMapperDecorator {
public abstract class PostMapperDecorator implements PostMapper {

@Autowired
private TagRepository tagRepository;
Expand All @@ -38,7 +38,10 @@ void setAfterMappingToPost(CreatePostRequest createPostRequest, @MappingTarget P
new PostComment().setReview(postCommentRequest.review()).setCreatedOn(LocalDateTime.now())));
}

List<TagRequest> tagRequests = createPostRequest.tags();
setTags(createPostRequest.tags(), post);
}

public Post setTags(List<TagRequest> tagRequests, Post post) {
if (!CollectionUtils.isEmpty(tagRequests)) {
tagRequests.forEach(tagRequest -> {
Predicate predicate = QTag.tag.name.equalsIgnoreCase(tagRequest.name());
Expand All @@ -47,9 +50,11 @@ void setAfterMappingToPost(CreatePostRequest createPostRequest, @MappingTarget P
PostTag postTag = new PostTag(post, tag.get());
post.getTags().add(postTag);
} else {
post.addTag(new Tag().setName(tagRequest.name().toLowerCase(Locale.ENGLISH)));
post.addTag(tagRepository.save(
new Tag().setName(tagRequest.name().toLowerCase(Locale.ENGLISH))));
}
});
}
return post;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.example.graphql.querydsl.model.request;

import java.util.List;

public record AddTagRequest(List<TagRequest> tagNames, Long postId) {}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@
import com.example.graphql.querydsl.entities.QPost;
import com.querydsl.core.types.dsl.StringExpression;
import com.querydsl.core.types.dsl.StringPath;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.querydsl.binding.QuerydslBinderCustomizer;
import org.springframework.data.querydsl.binding.QuerydslBindings;
import org.springframework.data.querydsl.binding.SingleValueBinding;
import org.springframework.data.repository.query.Param;

public interface PostRepository
extends JpaRepository<Post, Long>, QuerydslPredicateExecutor<Post>, QuerydslBinderCustomizer<QPost> {
Expand All @@ -18,4 +21,11 @@ default void customize(QuerydslBindings bindings, QPost root) {
.first((SingleValueBinding<StringPath, String>) StringExpression::containsIgnoreCase);
bindings.excluding(root.id);
}

@Query(
"select o from Post o left join fetch o.details pd left join fetch o.comments where pd.createdBy = :createdBy")
List<Post> findByDetails_CreatedByEqualsIgnoreCase(@Param("createdBy") String createdBy);

@Query("select p from Post p left join fetch p.tags where p in :posts")
List<Post> findAllPostsWithTags(@Param("posts") List<Post> posts);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package com.example.graphql.querydsl.repositories;

import com.example.graphql.querydsl.entities.Tag;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;

public interface TagRepository extends JpaRepository<Tag, Long>, QuerydslPredicateExecutor<Tag> {}
public interface TagRepository extends JpaRepository<Tag, Long>, QuerydslPredicateExecutor<Tag> {

Optional<Tag> findByName(String name);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.example.graphql.querydsl.exception.PostNotFoundException;
import com.example.graphql.querydsl.mapper.PostMapper;
import com.example.graphql.querydsl.model.query.FindPostsQuery;
import com.example.graphql.querydsl.model.request.AddTagRequest;
import com.example.graphql.querydsl.model.request.CreatePostRequest;
import com.example.graphql.querydsl.model.request.UpdatePostRequest;
import com.example.graphql.querydsl.model.response.PagedResult;
Expand Down Expand Up @@ -80,4 +81,22 @@ public void deletePostById(Long id) {
public Long totalPosts() {
return postRepository.count();
}

public List<PostResponse> getPostsByUserName(String name) {
List<Post> posts = postRepository.findByDetails_CreatedByEqualsIgnoreCase(name);
if (posts.isEmpty()) {
throw new PostNotFoundException(name);
} else {
// Fixing MultiBagException
List<Post> fullyMappedPosts = postRepository.findAllPostsWithTags(posts);
return postMapper.toResponseList(fullyMappedPosts);
}
}

@Transactional
public PostResponse addTagsToPost(AddTagRequest addTagRequest) {
Post post = postMapper.setTags(
addTagRequest.tagNames(), this.postRepository.getReferenceById(addTagRequest.postId()));
return postMapper.toResponse(this.postRepository.save(post));
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
type Query {
countPosts: Int!
getPostsByUserName(name : String!) : [PostResponse]
}

type Mutation {
createPost(createPostRequest : CreatePostRequest! ) : PostResponse
addTagsToPost(addTagRequest: AddTagRequest!) : PostResponse
}

type PostResponse {
Expand Down Expand Up @@ -42,3 +44,8 @@ input TagRequest {
input PostCommentRequest {
review: String!
}

input AddTagRequest {
tagNames: [TagRequest]!
postId: ID
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.example.graphql.querydsl.model.request.PostCommentRequest;
import com.example.graphql.querydsl.model.request.TagRequest;
import com.example.graphql.querydsl.model.response.PostCommentResponse;
import com.example.graphql.querydsl.model.response.PostResponse;
import com.example.graphql.querydsl.model.response.TagResponse;
import com.example.graphql.querydsl.repositories.PostRepository;
import java.time.LocalDateTime;
Expand All @@ -31,9 +32,9 @@ void setUp() {
postRepository.deleteAll();

postList = new ArrayList<>();
postList.add(getPost("First Post", "First Content"));
postList.add(getPost("Second Post", "Second Content"));
postList.add(getPost("Third Post", "Third Content"));
postList.add(getPost("First Post", "First Content", "First Review"));
postList.add(getPost("Second Post", "Second Content", "Second Review"));
postList.add(getPost("Third Post", "Third Content", "Third Review"));
postList = postRepository.saveAll(postList);
}

Expand All @@ -53,7 +54,7 @@ void testCountPosts() {
}

@Test
void createPost() {
void testCreatePost() {
Map<String, Object> inputValues = new HashMap<>();
inputValues.put("title", "JunitTitle");
inputValues.put("content", "JunitContent");
Expand Down Expand Up @@ -89,4 +90,67 @@ void createPost() {
.satisfies(
tagResponses -> assertThat(tagResponses.get(0).name()).isEqualTo("junit"));
}

@Test
void testGetPostsByUserName() {
graphQlTester
.documentName("getPostsByUserName")
.variable("name", "appUser")
.execute()
.path("getPostsByUserName")
.entityList(PostResponse.class)
.hasSize(3)
.path("getPostsByUserName[0].id")
.entity(Long.class)
.satisfies(id -> assertThat(id).isGreaterThan(0))
.path("getPostsByUserName[0].title")
.entity(String.class)
.isEqualTo("First Post")
.path("getPostsByUserName[0].content")
.entity(String.class)
.isEqualTo("First Content")
.path("getPostsByUserName[0].comments")
.entityList(PostCommentResponse.class)
.hasSize(1)
.satisfies(postCommentResponses -> {
assertThat(postCommentResponses.get(0).review()).isEqualTo("First Review");
assertThat(postCommentResponses.get(0).createdOn()).isInstanceOf(LocalDateTime.class);
})
.path("getPostsByUserName[0].tags")
.entityList(TagResponse.class)
.hasSize(0);
}

@Test
void testAddTagsToPosts() {
Map<String, Object> inputValues = new HashMap<>();
inputValues.put("postId", postList.get(2).getId());
inputValues.put("tagNames", List.of(new TagRequest("junit")));

graphQlTester
.documentName("addTagsToPosts")
.variable("addTagRequest", inputValues)
.execute()
.path("addTagsToPost.id")
.entity(Long.class)
.satisfies(id -> assertThat(id).isGreaterThan(0))
.path("addTagsToPost.title")
.entity(String.class)
.isEqualTo("Third Post")
.path("addTagsToPost.content")
.entity(String.class)
.isEqualTo("Third Content")
.path("addTagsToPost.comments")
.entityList(PostCommentResponse.class)
.hasSize(1)
.satisfies(postCommentResponses -> {
assertThat(postCommentResponses.get(0).review()).isEqualTo("Third Review");
assertThat(postCommentResponses.get(0).createdOn()).isInstanceOf(LocalDateTime.class);
})
.path("addTagsToPost.tags")
.entityList(TagResponse.class)
.hasSize(1)
.satisfies(
tagResponses -> assertThat(tagResponses.get(0).name()).isEqualTo("junit"));
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
package com.example.graphql.querydsl.utils;

import com.example.graphql.querydsl.entities.Post;
import com.example.graphql.querydsl.entities.PostComment;
import com.example.graphql.querydsl.entities.PostDetails;
import java.time.LocalDateTime;

public final class TestData {

public static Post getPost(String title, String content) {
public static Post getPost(String title, String content, String review) {
Post post = new Post().setTitle(title).setContent(content);
post.addDetails(new PostDetails()
.setCreatedOn(LocalDateTime.of(2023, 12, 31, 10, 35, 45, 99))
.setCreatedBy("appUser"));
post.addComment(
new PostComment().setReview(review).setCreatedOn(LocalDateTime.of(2023, 12, 31, 10, 35, 45, 99)));
return post;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ void setUp() {
postRepository.deleteAll();

postList = new ArrayList<>();
postList.add(getPost("First Post", "First Content"));
postList.add(getPost("Second Post", "Second Content"));
postList.add(getPost("Third Post", "Third Content"));
postList.add(getPost("First Post", "First Content", "First Review"));
postList.add(getPost("Second Post", "Second Content", "Second Review"));
postList.add(getPost("Third Post", "Third Content", "Third Review"));
postList = postRepository.saveAll(postList);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
mutation addTagsToPost($addTagRequest: AddTagRequest!) {
addTagsToPost(addTagRequest: $addTagRequest) {
id
title
content
createdOn
comments {
id
review
createdOn
}
tags {
id
name
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
query getPostsByUserName($name: String!) {
getPostsByUserName(name: $name) {
id
title
content
comments {
id
review
createdOn
}
tags {
id
name
}
}
}

0 comments on commit 7b41b4c

Please sign in to comment.