Skip to content

[#108] 범용 게시판 만들기 #111

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions community-backend/app/app-monolith/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ dependencies {
implementation project(':core:core-user-resource')
implementation project(':domain:domain-user-resource')

implementation project(':core:core-article')
implementation project(':domain:domain-article')

implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package org.kiworkshop.community.article.api;

import lombok.RequiredArgsConstructor;
import org.kiworkshop.community.article.dto.ArticleRequestDto;
import org.kiworkshop.community.article.dto.ArticleResponseDto;
import org.kiworkshop.community.article.domain.service.ArticleService;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.security.Principal;

@RestController
@RequestMapping("/article")
@RequiredArgsConstructor
public class ArticleController {
private final ArticleService articleService;

@PostMapping
public Long create(@RequestBody ArticleRequestDto articleRequestDto, Principal principal) {
return articleService.create(articleRequestDto);
}

@GetMapping
public Page<ArticleResponseDto> readPage(
@PageableDefault(sort = "id", direction = Sort.Direction.DESC) Pageable pageable
) {
return articleService.readPage(pageable);
}

@GetMapping("/{id}")
public ArticleResponseDto read(@PathVariable Long id) {
return articleService.read(id);
}

@DeleteMapping("/{id}")
public void delete(@PathVariable Long id, Principal principal) {
articleService.delete(id, principal);
}

@PutMapping("/{id}")
public void update(@PathVariable Long id, @RequestBody ArticleRequestDto articleRequestDto, Principal principal) {
articleService.update(id, articleRequestDto);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package org.kiworkshop.community.article.api;

public class ArticleControllerTest {
}
15 changes: 15 additions & 0 deletions community-backend/core/core-article/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
plugins {
id 'java'
}

version 'unspecified'

repositories {
mavenCentral()
}

dependencies {
implementation "javax.validation:validation-api:${javaxValidationApiVersion}"

testFixturesImplementation "org.springframework:spring-test:${springTestVersion}"
}
3 changes: 3 additions & 0 deletions community-backend/core/core-article/lombok.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# This file is generated by the 'io.freefair.lombok' Gradle plugin
config.stopBubbling = true
lombok.addLombokGeneratedAnnotation = true
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.kiworkshop.community.article.dto;


import javax.validation.constraints.NotEmpty;

import lombok.Getter;

@Getter
public class ArticleRequestDto {
@NotEmpty
private String title;
@NotEmpty
private String content;
@NotEmpty
private String username;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package org.kiworkshop.community.article.dto;

import java.time.ZonedDateTime;

import lombok.Builder;
import lombok.Getter;

@Getter
public class ArticleResponseDto {
private Long id;
private String title;
private String content;
private String username;
private ZonedDateTime createdAt;
private ZonedDateTime updatedAt;

@Builder
private ArticleResponseDto(
Long id,
String title,
String content,
String username,
ZonedDateTime createdAt,
ZonedDateTime updatedAt
) {
this.id = id;
this.title = title;
this.content =content;
this.username = username;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package org.kiworkshop.community.article.dto;

import static org.assertj.core.api.BDDAssertions.then;

import javax.validation.Validation;
import javax.validation.Validator;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.springframework.test.util.ReflectionTestUtils;

class ArticleRequestDtoTest {

private static final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
private ArticleRequestDto articleRequestDto;

@BeforeEach
void setUp() {
articleRequestDto = ArticleRequestDtoFixture.get();
}

@ParameterizedTest
@CsvSource({
"title,empty",
"content,empty",
"username,empty"
})
void validate_InvalidValue_ThrowException(String fieldName, String value) {
// given
String valueToInject = "empty".equals(value) ? "" : value;
ReflectionTestUtils.setField(articleRequestDto, fieldName, valueToInject);

var violations = validator.validate(articleRequestDto);

then(violations.size()).isEqualTo(1);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.kiworkshop.community.article.dto;

import org.springframework.test.util.ReflectionTestUtils;

public class ArticleRequestDtoFixture {
public static ArticleRequestDto get() {
ArticleRequestDto articleRequestDto = new ArticleRequestDto();

ReflectionTestUtils.setField(articleRequestDto, "title", "title");
ReflectionTestUtils.setField(articleRequestDto, "content", "content");
ReflectionTestUtils.setField(articleRequestDto, "username", "username");

return articleRequestDto;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.kiworkshop.community.article.dto;

import java.time.ZonedDateTime;

public class ArticleResponseDtoFixture {
public static ArticleResponseDto get() {
return ArticleResponseDto.builder()
.id(1L)
.title("title")
.content("content")
.username("username")
.createdAt(ZonedDateTime.now())
.updatedAt(ZonedDateTime.now())
.build();
}
}
10 changes: 10 additions & 0 deletions community-backend/db-migration/V8__Create_table_article.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
create table article (
id bigint not null auto_increment,
created_at datetime(6),
updated_at datetime(6),
title varchar(255) not null,
content LONGTEXT not null,
username varchar(255) not null,
active bit,
primary key (id)
);
21 changes: 21 additions & 0 deletions community-backend/domain/domain-article/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
plugins {
id 'java'
}

version 'unspecified'

repositories {
mavenCentral()
}

dependencies {
implementation project(':domain:domain-common')
implementation project(':core:core-article')

implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

testImplementation testFixtures(project(":core:core-article"))
testFixturesImplementation project(":core:core-article")
testFixturesImplementation project(":domain:domain-common")
testFixturesImplementation "org.springframework:spring-test:${springTestVersion}"
}
3 changes: 3 additions & 0 deletions community-backend/domain/domain-article/lombok.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# This file is generated by the 'io.freefair.lombok' Gradle plugin
config.stopBubbling = true
lombok.addLombokGeneratedAnnotation = true
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.kiworkshop.community.article.domain.exception;

import javax.persistence.EntityNotFoundException;

public class ArticleNotFoundException extends EntityNotFoundException {
public ArticleNotFoundException(Long id) {
super("Article of id:" + id + " is not found.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package org.kiworkshop.community.article.domain.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Lob;

import org.kiworkshop.community.common.domain.BaseEntity;
import org.springframework.util.Assert;

import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@NoArgsConstructor
@Getter
@Entity
public class Article extends BaseEntity {
private static final String DELETED_ARTICLE_MESSAGE = "삭제된 게시글입니다.";

@Column(nullable = false)
private String title;
@Lob
@Column(nullable = false)
private String content;
@Column(nullable = false)
private String username;
private boolean active = true;

@Builder
private Article(String title, String content, String username) {
Assert.hasLength(title, "title must have length.");
Assert.hasLength(content, "content must have length.");
Assert.hasLength(username, "username must have length.");
this.title = title;
this.content = content;
this.username = username;
}

public void update(Article article) {
// TODO: 20. 8. 31. Error Code 가 400이 되어야 할지? 403이 되어야 할지 고민
Assert.isTrue(this.username.equals(article.username), "unauthorized username");
this.title = article.title;
this.content = article.content;
}

public boolean isAuthor(String name) {
return this.username.equals(name);
}

public void deactivate(String username) {
if (!this.username.equals(username)) {
throw new IllegalArgumentException("unauthorized user");
}
this.active = false;
}

public String getTitle() {
if (this.active) {
return DELETED_ARTICLE_MESSAGE;
}
return this.title;
}

public String getContent() {
if (this.active) {
return DELETED_ARTICLE_MESSAGE;
}
return this.content;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.kiworkshop.community.article.domain.model;


import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ArticleRepository extends JpaRepository<Article, Long> {
Page<Article> findAllByActiveIsTrue(Pageable pageable);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.kiworkshop.community.article.domain.service;

import org.kiworkshop.community.article.domain.model.Article;
import org.kiworkshop.community.article.dto.ArticleRequestDto;
import org.kiworkshop.community.article.dto.ArticleResponseDto;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ArticleConverter {
public static Article toEntity(ArticleRequestDto articleRequestDto) {
return Article.builder()
.title(articleRequestDto.getTitle())
.content(articleRequestDto.getContent())
.username(articleRequestDto.getUsername())
.build();
}

public static ArticleResponseDto from(Article article) {
return ArticleResponseDto.builder()
.id(article.getId())
.title(article.getTitle())
.content(article.getContent())
.username(article.getUsername())
.createdAt(article.getCreatedAt())
.updatedAt(article.getUpdatedAt())
.build();
}
}
Loading