Skip to content
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

feat: 공지 CRUD API 구현 #323

Merged
merged 11 commits into from
Oct 30, 2024
Merged
2 changes: 2 additions & 0 deletions src/main/java/com/listywave/common/exception/ErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ public enum ErrorCode {
DUPLICATE_NICKNAME_EXCEPTION(BAD_REQUEST, "중복된 닉네임입니다."),
DUPLICATE_COLLABORATOR_EXCEPTION(BAD_REQUEST, "이미 동일한 콜라보레이터가 존재합니다"),
DUPLICATE_FOLDER_NAME_EXCEPTION(BAD_REQUEST, "중복된 폴더명입니다."),
NULL_OR_BLANK_EXCEPTION(BAD_REQUEST, "값이 null이거나 공백일 수 없습니다."),
NOT_EXIST_CODE(BAD_REQUEST, "존재하지 않는 코드입니다."),

// S3
S3_DELETE_OBJECTS_EXCEPTION(INTERNAL_SERVER_ERROR, "S3의 이미지를 삭제 요청하는 과정에서 에러가 발생했습니다."),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.listywave.notice.application.converter;

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

개행 제거!

Copy link
Collaborator Author

@kdkdhoho kdkdhoho Oct 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

원래 package 아래는 개행 한 줄이 들어갑니다 😃 (출처)


import com.listywave.notice.application.domain.ContentType;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;

@Converter(autoApply = true)
public class ContentTypeConverter implements AttributeConverter<ContentType, String> {

@Override
public String convertToDatabaseColumn(ContentType contentType) {
return contentType.name().toLowerCase();
}

@Override
public ContentType convertToEntityAttribute(String s) {
return ContentType.valueOf(s.toUpperCase());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.listywave.notice.application.domain;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public enum ContentType {

SUBTITLE,
BODY,
IMAGE,
BUTTON,
LINE,
NOTE,
;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.listywave.notice.application.domain;

import static jakarta.persistence.CascadeType.ALL;
import static jakarta.persistence.FetchType.LAZY;
import static lombok.AccessLevel.PROTECTED;

import com.listywave.common.BaseEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.OneToMany;
import java.util.ArrayList;
import java.util.List;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@NoArgsConstructor(access = PROTECTED)
public class Notice extends BaseEntity {

@Column(name = "code", nullable = false)
private NoticeType type;

@Embedded
private NoticeTitle title;

@Embedded
private NoticeDescription description;

@OneToMany(mappedBy = "notice", fetch = LAZY, cascade = ALL, orphanRemoval = true)
private final List<NoticeContent> contents = new ArrayList<>();

public Notice(NoticeType type, NoticeTitle title, NoticeDescription description) {
this.type = type;
this.title = title;
this.description = description;
}

public void addContents(List<NoticeContent> contents) {
this.contents.addAll(contents);
}
Comment on lines +46 to +48
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아직도 햇갈리는거 contents에 데잍터 바인딩 시켰으면 NoticeContent에서도 Notice 양방향으로 채워줘야하는 거 아니였어요? 이게 아직도 햇갈리네 ㅠ

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

연관관계의 주인이 NoticeContent이니까 NoticeContent에도 Notice를 참조하도록 하는 게 맞습니다!

Notice#addContents()를 호출하는 NoticeService#create()를 살펴보면,
List<NoticeContent>를 만들 때 객체 참조를 할당해주고 있습니다!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아아 DTO에서 해주는군요 ㅎㅎ 확인했슴다!

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.listywave.notice.application.domain;

import static jakarta.persistence.GenerationType.IDENTITY;
import static lombok.AccessLevel.PRIVATE;
import static lombok.AccessLevel.PROTECTED;

import jakarta.annotation.Nullable;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@AllArgsConstructor(access = PRIVATE)
@NoArgsConstructor(access = PROTECTED)
public class NoticeContent {

@Id
@GeneratedValue(strategy = IDENTITY)
private Long id;

@ManyToOne
@JoinColumn(name = "notice_id", nullable = false)
private Notice notice;

@Column(name = "orders", nullable = false)
private int order;

@Column(nullable = false, length = 30)
private ContentType type;

@Column(nullable = true, length = 1000)
private String description;

@Column(nullable = true, length = 2048)
private String imageUrl;

@Column(nullable = true, length = 50)
private String buttonName;

@Column(nullable = true, length = 2048)
private String buttonLink;

public static NoticeContent create(
Notice notice,
int order,
ContentType type,
@Nullable String description,
@Nullable String imageUrl,
@Nullable String buttonName,
@Nullable String buttonLink
) {
return new NoticeContent(null, notice, order, type, description, imageUrl, buttonName, buttonLink);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.listywave.notice.application.domain;

import static com.listywave.common.exception.ErrorCode.LENGTH_EXCEEDED_EXCEPTION;
import static com.listywave.common.exception.ErrorCode.NULL_OR_BLANK_EXCEPTION;
import static lombok.AccessLevel.PROTECTED;

import com.listywave.common.exception.CustomException;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Embeddable
@EqualsAndHashCode
@NoArgsConstructor(access = PROTECTED, force = true)
public class NoticeDescription {

private static final int MAX_LENGTH = 30;

@Column(name = "description", nullable = false, length = MAX_LENGTH)
private final String value;

public NoticeDescription(String value) {
validate(value);
this.value = value;
}

private void validate(String value) {
if (value == null || value.isBlank()) {
throw new CustomException(NULL_OR_BLANK_EXCEPTION, NULL_OR_BLANK_EXCEPTION.getDetail() + " 입력값: " + value);
}
if (value.length() > MAX_LENGTH) {
throw new CustomException(LENGTH_EXCEEDED_EXCEPTION, LENGTH_EXCEEDED_EXCEPTION.getDetail() + " 입력값의 길이: " + value.length());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.listywave.notice.application.domain;

import static com.listywave.common.exception.ErrorCode.LENGTH_EXCEEDED_EXCEPTION;
import static com.listywave.common.exception.ErrorCode.NULL_OR_BLANK_EXCEPTION;
import static lombok.AccessLevel.PROTECTED;

import com.listywave.common.exception.CustomException;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Embeddable
@EqualsAndHashCode
@NoArgsConstructor(access = PROTECTED, force = true)
public class NoticeTitle {

private static final int MAX_LENGTH = 30;

@Column(name = "title", nullable = false, length = MAX_LENGTH)
private final String value;

public NoticeTitle(String value) {
validate(value);
this.value = value;
}

private void validate(String value) {
if (value == null || value.isBlank()) {
throw new CustomException(NULL_OR_BLANK_EXCEPTION, NULL_OR_BLANK_EXCEPTION.getDetail() + " 입력값: " + value);
}
if (value.length() > MAX_LENGTH) {
throw new CustomException(LENGTH_EXCEEDED_EXCEPTION, LENGTH_EXCEEDED_EXCEPTION.getDetail() + " 입력값의 길이: " + value.length());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.listywave.notice.application.domain;

import static com.listywave.common.exception.ErrorCode.LENGTH_EXCEEDED_EXCEPTION;
import static com.listywave.common.exception.ErrorCode.NULL_OR_BLANK_EXCEPTION;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatNoException;
import static org.junit.jupiter.api.Assertions.assertThrows;

import com.listywave.common.exception.CustomException;
import com.listywave.common.exception.ErrorCode;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.NullAndEmptySource;

class NoticeDescriptionTest {

@Test
void 공지_제목의_최대_길이를_넘으면_예외를_발생한다() {
// given
String value = IntStream.range(0, 30)
.mapToObj(String::valueOf)
.collect(Collectors.joining(""));

// when
ErrorCode result = assertThrows(CustomException.class, () -> new NoticeDescription(value))
.getErrorCode();

// then
assertThat(result).isEqualTo(LENGTH_EXCEEDED_EXCEPTION);
}

@ParameterizedTest
@NullAndEmptySource
void 값이_null이거나_빈_값이면_예외를_발생한다(String value) {
// when
ErrorCode result = assertThrows(CustomException.class, () -> new NoticeDescription(value))
.getErrorCode();

// then
assertThat(result).isEqualTo(NULL_OR_BLANK_EXCEPTION);
}

@Test
void 공지_제목을_정상적으로_생성한다() {
// given
String value = "12345678911234567891123456789";

// expect
assertThatNoException().isThrownBy(() -> new NoticeDescription(value));
}

@Test
void 값이_같으면_같은_객체다() {
// given
NoticeDescription title1 = new NoticeDescription("123456789");
NoticeDescription title2 = new NoticeDescription("123456789");

// expect
assertThat(title1).isEqualTo(title2);
assertThat(title1).hasSameHashCodeAs(title2);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.listywave.notice.application.domain;

import static com.listywave.common.exception.ErrorCode.LENGTH_EXCEEDED_EXCEPTION;
import static com.listywave.common.exception.ErrorCode.NULL_OR_BLANK_EXCEPTION;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatNoException;
import static org.junit.jupiter.api.Assertions.assertThrows;

import com.listywave.common.exception.CustomException;
import com.listywave.common.exception.ErrorCode;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.NullAndEmptySource;

class NoticeTitleTest {

@Test
void 공지_제목의_최대_길이를_넘으면_예외를_발생한다() {
// given
String value = IntStream.range(0, 30)
.mapToObj(String::valueOf)
.collect(Collectors.joining(""));

// when
ErrorCode result = assertThrows(CustomException.class, () -> new NoticeTitle(value))
.getErrorCode();

// then
assertThat(result).isEqualTo(LENGTH_EXCEEDED_EXCEPTION);
}

@ParameterizedTest
@NullAndEmptySource
void 값이_null이거나_빈_값이면_예외를_발생한다(String value) {
// when
ErrorCode result = assertThrows(CustomException.class, () -> new NoticeTitle(value))
.getErrorCode();

// then
assertThat(result).isEqualTo(NULL_OR_BLANK_EXCEPTION);
}

@Test
void 공지_제목을_정상적으로_생성한다() {
// given
String value = "12345678911234567891123456789";

// expect
assertThatNoException().isThrownBy(() -> new NoticeTitle(value));
}

@Test
void 값이_같으면_같은_객체다() {
// given
NoticeTitle title1 = new NoticeTitle("123456789");
NoticeTitle title2 = new NoticeTitle("123456789");

// expect
assertThat(title1).isEqualTo(title2);
assertThat(title1).hasSameHashCodeAs(title2);
}
}