Skip to content

Commit

Permalink
feat : add post details table and update endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
rajadilipkolli committed Nov 10, 2023
1 parent 39384b7 commit 5abf4a8
Show file tree
Hide file tree
Showing 13 changed files with 264 additions and 86 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.example.graphql.querydsl.entities;

import jakarta.persistence.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
Expand All @@ -27,11 +26,13 @@ public class Post {
@Column(nullable = false)
private String content;

private LocalDateTime createdOn;

@OneToMany(cascade = CascadeType.ALL, mappedBy = "post", orphanRemoval = true)
private List<PostComment> comments = new ArrayList<>();

@JoinColumn(name = "details_Id")
@OneToOne(cascade = CascadeType.ALL, mappedBy = "post", orphanRemoval = true, fetch = FetchType.LAZY)
private PostDetails details;

public Post setId(Long id) {
this.id = id;
return this;
Expand All @@ -47,11 +48,6 @@ public Post setContent(String content) {
return this;
}

public Post setCreatedOn(LocalDateTime createdOn) {
this.createdOn = createdOn;
return this;
}

public Post setComments(List<PostComment> comments) {
if (comments == null) {
comments = new ArrayList<>();
Expand All @@ -60,6 +56,11 @@ public Post setComments(List<PostComment> comments) {
return this;
}

public Post setDetails(PostDetails details) {
this.details = details;
return this;
}

public void addComment(PostComment comment) {
this.comments.add(comment);
comment.setPost(this);
Expand All @@ -70,6 +71,16 @@ public void removeComment(PostComment comment) {
comment.setPost(null);
}

public void addDetails(PostDetails details) {
this.details = details;
details.setPost(this);
}

public void removeDetails() {
this.details.setPost(null);
this.details = null;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.example.graphql.querydsl.entities;

import jakarta.persistence.*;
import java.time.LocalDateTime;
import java.util.Objects;
import lombok.Getter;
import lombok.Setter;

@Entity(name = "PostDetails")
@Table(name = "post_details")
@Getter
@Setter
public class PostDetails {

@Id
private Long id;

@Column(name = "created_on", nullable = false, updatable = false)
private LocalDateTime createdOn;

@Column(name = "created_by", nullable = false, updatable = false)
private String createdBy;

@OneToOne(fetch = FetchType.LAZY)
@MapsId
private Post post;

public PostDetails setId(Long id) {
this.id = id;
return this;
}

public PostDetails setCreatedOn(LocalDateTime createdOn) {
this.createdOn = createdOn;
return this;
}

public PostDetails setCreatedBy(String createdBy) {
this.createdBy = createdBy;
return this;
}

public PostDetails setPost(Post post) {
this.post = post;
return this;
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
PostDetails other = (PostDetails) obj;
return Objects.equals(this.createdBy, other.createdBy);
}

@Override
public int hashCode() {
return Objects.hash(this.createdBy);
}
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,32 @@
package com.example.graphql.querydsl.mapper;

import com.example.graphql.querydsl.entities.Post;
import com.example.graphql.querydsl.entities.PostDetails;
import com.example.graphql.querydsl.model.request.CreatePostRequest;
import com.example.graphql.querydsl.model.request.PostRequest;
import com.example.graphql.querydsl.model.response.PostResponse;
import java.time.LocalDateTime;
import java.util.List;
import org.mapstruct.*;

@Mapper
public interface PostMapper {

@Mapping(target = "id", ignore = true)
@Mapping(target = "createdOn", expression = "java(java.time.LocalDateTime.now())")
Post toEntity(PostRequest postRequest);
Post toEntity(CreatePostRequest createPostRequest);

@Mapping(target = "id", ignore = true)
@Mapping(target = "createdOn", ignore = true)
// @Mapping(target = "createdOn", ignore = true)
void mapPostWithRequest(PostRequest postRequest, @MappingTarget Post post);

@Mapping(target = "createdOn", source = "details.createdOn")
PostResponse toResponse(Post post);

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

@AfterMapping
default void setAfterMappingToPost(CreatePostRequest createPostRequest, @MappingTarget Post post) {
post.addDetails(
new PostDetails().setCreatedBy(createPostRequest.createdBy()).setCreatedOn(LocalDateTime.now()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.example.graphql.querydsl.model.request;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;

public record CreatePostRequest(
@NotEmpty(message = "Title cannot be empty") String title,
@NotBlank(message = "Content cannot be blank") String content,
@NotBlank(message = "CreatedBy cannot be blank") String createdBy) {}
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.CreatePostRequest;
import com.example.graphql.querydsl.model.request.PostRequest;
import com.example.graphql.querydsl.model.response.PagedResult;
import com.example.graphql.querydsl.model.response.PostResponse;
Expand Down Expand Up @@ -52,8 +53,8 @@ public Optional<PostResponse> findPostById(Long id) {
}

@Transactional
public PostResponse savePost(PostRequest postRequest) {
Post post = postMapper.toEntity(postRequest);
public PostResponse savePost(CreatePostRequest createPostRequest) {
Post post = postMapper.toEntity(createPostRequest);
Post savedPost = postRepository.save(post);
return postMapper.toResponse(savedPost);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.example.graphql.querydsl.utils;

import java.time.format.DateTimeFormatter;

public final class AppConstants {
public static final String PROFILE_PROD = "prod";
public static final String PROFILE_NOT_PROD = "!" + PROFILE_PROD;
Expand All @@ -9,4 +11,7 @@ public final class AppConstants {
public static final String DEFAULT_PAGE_SIZE = "10";
public static final String DEFAULT_SORT_BY = "id";
public static final String DEFAULT_SORT_DIRECTION = "asc";

public static final DateTimeFormatter formatterWithMillis =
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSS");
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.example.graphql.querydsl.exception.PostNotFoundException;
import com.example.graphql.querydsl.model.query.FindPostsQuery;
import com.example.graphql.querydsl.model.request.CreatePostRequest;
import com.example.graphql.querydsl.model.request.PostRequest;
import com.example.graphql.querydsl.model.response.PagedResult;
import com.example.graphql.querydsl.model.response.PostResponse;
Expand Down Expand Up @@ -52,8 +53,8 @@ public ResponseEntity<PostResponse> getPostById(@PathVariable Long id) {
}

@PostMapping
public ResponseEntity<PostResponse> createPost(@RequestBody @Validated PostRequest postRequest) {
PostResponse response = postService.savePost(postRequest);
public ResponseEntity<PostResponse> createPost(@RequestBody @Validated CreatePostRequest createPostRequest) {
PostResponse response = postService.savePost(createPostRequest);
URI location = ServletUriComponentsBuilder.fromCurrentRequest()
.path("/api/posts/{id}")
.buildAndExpand(response.id())
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- https://docs.liquibase.com/concepts/changelogs/xml-format.html -->
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.20.xsd">

<property name="string.type" value="varchar(255)" dbms="!postgresql"/>
<property name="string.type" value="text" dbms="postgresql"/>

<changeSet author="app" id="createTable-post_details">
<createTable tableName="post_details">
<column name="post_id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="pk_post_details"/>
</column>
<column name="created_on" type="DATETIME">
<constraints nullable="false"/>
</column>
<column name="created_by" type="${string.type}">
<constraints nullable="false"/>
</column>
</createTable>
</changeSet>

<changeSet id="add_foreign_key_post_id" author="app">
<addForeignKeyConstraint baseColumnNames="post_id" baseTableName="post_details"
constraintName="FK_POST_DETAILS_ON_POST" referencedColumnNames="id"
referencedTableName="posts"/>
</changeSet>
<changeSet id="add_foreign_key_details_id" author="app">
<addForeignKeyConstraint baseColumnNames="details_id" baseTableName="posts" constraintName="FK_POST_ON_DETAILS"
referencedColumnNames="post_id" referencedTableName="post_details"/>
</changeSet>
</databaseChangeLog>
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
package com.example.graphql.querydsl;

import static org.assertj.core.api.Assertions.assertThat;

import com.example.graphql.querydsl.common.ContainersConfig;
import com.zaxxer.hikari.HikariDataSource;
import javax.sql.DataSource;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.context.annotation.Import;

@DataJpaTest(properties = {"spring.jpa.hibernate.ddl-auto=validate", "spring.test.database.replace=none"})
@Import(ContainersConfig.class)
class SchemaValidationTest {

@Autowired
private DataSource dataSource;

@Test
void validateJpaMappingsWithDbSchema() {}
void validateJpaMappingsWithDbSchema() {
assertThat(dataSource).isInstanceOf(HikariDataSource.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

import com.example.graphql.querydsl.common.AbstractIntegrationTest;
import com.example.graphql.querydsl.entities.Post;
import com.example.graphql.querydsl.entities.PostDetails;
import com.example.graphql.querydsl.model.request.CreatePostRequest;
import com.example.graphql.querydsl.model.request.PostRequest;
import com.example.graphql.querydsl.repositories.PostRepository;
import java.time.LocalDateTime;
Expand All @@ -33,12 +35,12 @@ class PostControllerIT extends AbstractIntegrationTest {

@BeforeEach
void setUp() {
postRepository.deleteAllInBatch();
postRepository.deleteAll();

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

Expand Down Expand Up @@ -68,12 +70,12 @@ void shouldFindPostById() throws Exception {
.andExpect(jsonPath("$.id", is(post.getId()), Long.class))
.andExpect(jsonPath("$.title", is(post.getTitle())))
.andExpect(jsonPath("$.content", is(post.getContent())))
.andExpect(jsonPath("$.createdOn", is(post.getCreatedOn()), LocalDateTime.class));
.andExpect(jsonPath("$.createdOn", is("2023-12-31T10:35:45")));
}

@Test
void shouldCreateNewPost() throws Exception {
PostRequest postRequest = new PostRequest("New Post", "New Content");
CreatePostRequest postRequest = new CreatePostRequest("New Post", "New Content", "Junit");
this.mockMvc
.perform(post("/api/posts")
.contentType(MediaType.APPLICATION_JSON)
Expand All @@ -87,24 +89,26 @@ void shouldCreateNewPost() throws Exception {

@Test
void shouldReturn400WhenCreateNewPostWithoutTitleAndContent() throws Exception {
PostRequest postRequest = new PostRequest(null, null);
CreatePostRequest createPostRequest = new CreatePostRequest(null, null, null);

this.mockMvc
.perform(post("/api/posts")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(postRequest)))
.content(objectMapper.writeValueAsString(createPostRequest)))
.andExpect(status().isBadRequest())
.andExpect(header().string("Content-Type", is("application/problem+json")))
.andExpect(jsonPath("$.type", is("about:blank")))
.andExpect(jsonPath("$.title", is("Constraint Violation")))
.andExpect(jsonPath("$.status", is(400)))
.andExpect(jsonPath("$.detail", is("Invalid request content.")))
.andExpect(jsonPath("$.instance", is("/api/posts")))
.andExpect(jsonPath("$.violations", hasSize(2)))
.andExpect(jsonPath("$.violations", hasSize(3)))
.andExpect(jsonPath("$.violations[0].field", is("content")))
.andExpect(jsonPath("$.violations[0].message", is("Content cannot be blank")))
.andExpect(jsonPath("$.violations[1].field", is("title")))
.andExpect(jsonPath("$.violations[1].message", is("Title cannot be empty")))
.andExpect(jsonPath("$.violations[1].field", is("createdBy")))
.andExpect(jsonPath("$.violations[1].message", is("CreatedBy cannot be blank")))
.andExpect(jsonPath("$.violations[2].field", is("title")))
.andExpect(jsonPath("$.violations[2].message", is("Title cannot be empty")))
.andReturn();
}

Expand Down Expand Up @@ -133,6 +137,14 @@ void shouldDeletePost() throws Exception {
.andExpect(jsonPath("$.id", is(post.getId()), Long.class))
.andExpect(jsonPath("$.title", is(post.getTitle())))
.andExpect(jsonPath("$.content", is(post.getContent())))
.andExpect(jsonPath("$.createdOn", is(post.getCreatedOn()), LocalDateTime.class));
.andExpect(jsonPath("$.createdOn", is("2023-12-31T10:35:45")));
}

private Post getPost(String title, String content) {
Post post = new Post().setTitle(title).setContent(content);
post.addDetails(new PostDetails()
.setCreatedOn(LocalDateTime.of(2023, 12, 31, 10, 35, 45, 99))
.setCreatedBy("appUser"));
return post;
}
}
Loading

0 comments on commit 5abf4a8

Please sign in to comment.