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

[BE] MySQL 대소문자 구분 문제로 태그 생성 시 다른 태그까지 생성되는 버그 픽스 #769

Merged
merged 12 commits into from
Oct 10, 2024
Merged
12 changes: 4 additions & 8 deletions backend/src/main/java/codezap/global/cors/CorsProperties.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.DefaultValue;

import lombok.Getter;

@Getter
@ConfigurationProperties(prefix = "cors")
public class CorsProperties {

private final String[] allowedOrigins;
private final String[] allowedOriginsPatterns;

Expand All @@ -16,12 +20,4 @@ public CorsProperties(
this.allowedOrigins = allowedOrigins;
this.allowedOriginsPatterns = allowedOriginsPatterns;
}

public String[] getAllowedOrigins() {
return allowedOrigins;
}

public String[] getAllowedOriginsPatterns() {
return allowedOriginsPatterns;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

@Getter
public class CodeZapException extends RuntimeException {

private final HttpStatusCode httpStatusCode;

public CodeZapException(HttpStatusCode httpStatusCode, String message) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public class DataSourceRouter extends AbstractRoutingDataSource {
protected Object determineCurrentLookupKey() {
boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();

if(readOnly) {
if (readOnly) {
return READER_KEY;
}
return WRITER_KEY;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

@Configuration
public class DateTimeFormatConfiguration {

private static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
*/
@Component
public class AuthOperationCustomizer implements OperationCustomizer {

private static final ObjectMapper objectMapper = new ObjectMapper();

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiErrorResponses {

ApiErrorResponse[] value();
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

@Component
public class ApiErrorResponsesCustomizer implements OperationCustomizer {

@Override
public Operation customize(Operation operation, HandlerMethod handlerMethod) {
List<ApiErrorResponse> apiErrorResponses = getApiErrorResponses(handlerMethod);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package codezap.global.validation;

public class ValidationGroups {

public interface NotNullGroup {}

public interface SourceCodeOrdinalGroup {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import codezap.template.domain.Template;

public interface LikesRepository {

Likes save(Likes likes);

boolean existsByMemberAndTemplate(Member member, Template template);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

@Tag(name = "태그 API", description = "태그 조회 API")
public interface SpringDocTagController {

@SecurityRequirement(name = "쿠키 인증 토큰")
@Operation(summary = "태그 조회", description = "해당 멤버의 템플릿들에 포함된 태그를 조회합니다.")
@ApiResponse(responseCode = "200", description = "태그 조회 성공")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ default Tag fetchByName(String name) {
() -> new CodeZapException(HttpStatus.NOT_FOUND, "이름이 " + name + "인 태그는 존재하지 않습니다."));
}

Optional<Tag> findByName(String name);
@Query(value = "SELECT * FROM tag WHERE tag.name = BINARY :name", nativeQuery = true)
Optional<Tag> findByName(@Param("name") String name);

@Query("select t.name from Tag t where t.name in :names")
List<String> findNameByNamesIn(@Param("names") List<String> names);
@Query(value = "SELECT * FROM tag WHERE tag.name COLLATE utf8mb4_bin IN :names", nativeQuery = true)
List<Tag> findAllByNames(@Param("names") List<String> names);
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public interface TagRepository {

Optional<Tag> findByName(String name);

List<Tag> findByNameIn(List<String> names);
List<Tag> findAllByNames(List<String> names);

Tag save(Tag tag);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import codezap.tag.domain.Tag;
import codezap.template.domain.Template;
Expand All @@ -22,19 +23,19 @@ public interface TemplateTagJpaRepository extends TemplateTagRepository, JpaRepo
List<Tag> findAllTagsByTemplate(Template template);

@Query("""
SELECT tt, t
FROM TemplateTag tt
JOIN FETCH tt.tag t
WHERE tt.id.templateId = :templateId
""")
SELECT tt, t
FROM TemplateTag tt
JOIN FETCH tt.tag t
WHERE tt.id.templateId = :templateId
""")
List<TemplateTag> findAllByTemplateId(Long templateId);

@Query("""
SELECT tt, t
FROM TemplateTag tt
JOIN FETCH tt.tag t
WHERE tt.id.templateId in :templateIds
""")
SELECT tt, t
FROM TemplateTag tt
JOIN FETCH tt.tag t
WHERE tt.id.templateId in :templateIds
""")
List<TemplateTag> findAllByTemplateIdsIn(List<Long> templateIds);

@Query("""
Expand All @@ -43,13 +44,14 @@ public interface TemplateTagJpaRepository extends TemplateTagRepository, JpaRepo
WHERE t.id IN (
SELECT DISTINCT tt.id.tagId
FROM TemplateTag tt
WHERE tt.id.templateId IN
(SELECT te.id FROM Template te WHERE te.member.id = :memberId)
)
""")
WHERE tt.id.templateId IN (
SELECT te.id FROM Template te WHERE te.member.id = :memberId
)
)
""")
List<Tag> findAllTagDistinctByMemberId(Long memberId);

@Modifying(clearAutomatically = true)
@Query("DELETE FROM TemplateTag t WHERE t.template.id in :templateIds")
void deleteByTemplateIds(List<Long> templateIds);
void deleteByTemplateIds(@Param("templateIds") List<Long> templateIds);
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ public interface TemplateTagRepository {

List<TemplateTag> findAllByTemplateId(Long templateId);

List<Long> findDistinctByTemplateIn(List<Long> templateIds);

List<TemplateTag> findAllByTemplateIdsIn(List<Long> templateIds);

TemplateTag save(TemplateTag templateTag);
Expand Down
2 changes: 1 addition & 1 deletion backend/src/main/java/codezap/tag/service/TagService.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class TagService {

@Transactional
public void createTags(Template template, List<String> tagNames) {
List<Tag> existingTags = new ArrayList<>(tagRepository.findByNameIn(tagNames));
List<Tag> existingTags = new ArrayList<>(tagRepository.findAllByNames(tagNames));
List<String> existNames = existingTags.stream()
.map(Tag::getName)
.toList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public interface SpringDocTemplateController {
새로운 템플릿을 생성합니다. \n
템플릿명, 템플릿 설명, 소스 코드 목록, 썸네일 순서, 카테고리 ID, 태그 목록이 필요합니다. \n
* 템플릿 이름은 비어있거나 공백일 수 없다.

소스 코드 목록은 파일명, 소스 코드, 소스 코드 순서가 필요합니다. \n
* 소스 코드 순서는 1부터 시작합니다.
* 소스 코드 순서는 오름차순으로 정렬하여 보내야 합니다.
Expand Down Expand Up @@ -62,13 +62,13 @@ public interface SpringDocTemplateController {
- 검색 키워드 (템플릿명, 템플릿 설명, 파일명, 소스 코드)
- 카테고리 ID
- 태그 ID들 \n

페이징 조건을 줄 수 있습니다. 페이지 번호는 1, 템플릿 개수는 20, 정렬 방식은 최신순이 기본 값입니다. \n
- 페이징 조건 \n
- 페이지 번호(pageNumber)
- 한 페이지에 템플릿 개수(pageSize)
- 페이지 정렬 방식(sort) \n

- 정렬 방식 \n
- 최신순 (modifiedAt,asc)
- 오래된순 (modifiedAt,desc)
Expand Down Expand Up @@ -102,13 +102,13 @@ ResponseEntity<FindAllTemplatesResponse> findAllTemplates(
- 검색 키워드 (템플릿명, 템플릿 설명, 파일명, 소스 코드)
- 카테고리 ID
- 태그 ID들 \n

페이징 조건을 줄 수 있습니다. 페이지 번호는 1, 템플릿 개수는 20, 정렬 방식은 최신순이 기본 값입니다. \n
- 페이징 조건 \n
- 페이지 번호(pageNumber)
- 한 페이지에 템플릿 개수(pageSize)
- 페이지 정렬 방식(sort) \n

- 정렬 방식 \n
- 최신순 (modifiedAt,asc)
- 오래된순 (modifiedAt,desc)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,12 @@ public ExactPhraseMatchFunction() {
}

@Override
public void render(SqlAppender sqlAppender, List<? extends SqlAstNode> arguments, ReturnableType<?> returnType, SqlAstTranslator<?> translator) {
public void render(
SqlAppender sqlAppender,
List<? extends SqlAstNode> arguments,
ReturnableType<?> returnType,
SqlAstTranslator<?> translator
) {
sqlAppender.appendSql("MATCH(");
translator.render(arguments.get(0), SqlAstNodeRenderingMode.DEFAULT);
sqlAppender.appendSql(", ");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

@SuppressWarnings("unused")
public interface SourceCodeJpaRepository extends SourceCodeRepository, JpaRepository<SourceCode, Long> {

default SourceCode fetchById(Long id) {
return findById(id).orElseThrow(
() -> new CodeZapException(HttpStatus.NOT_FOUND, "식별자 " + id + "에 해당하는 소스 코드가 존재하지 않습니다."));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import codezap.template.domain.Template;

public interface SourceCodeRepository {

SourceCode fetchById(Long id);

List<SourceCode> findAllByTemplate(Template template);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import codezap.template.domain.TemplateTag;

public class TemplateSpecification implements Specification<Template> {

private final Long memberId;
private final String keyword;
private final Long categoryId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ default Thumbnail fetchByTemplate(Template template) {
}

@Query("""
SELECT t, sc
FROM Thumbnail t
join fetch t.sourceCode sc
WHERE t.template = :template
""")
SELECT t, sc
FROM Thumbnail t
join fetch t.sourceCode sc
WHERE t.template = :template
""")
Optional<Thumbnail> findByTemplate(Template template);


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ class CookieCredentialManagerTest {

private MockHttpServletRequest request;
private MockHttpServletResponse response;

private CookieCredentialManager cookieCredentialManager;

@BeforeEach
Expand All @@ -34,7 +33,7 @@ void setUp() {

@Nested
@DisplayName("인증 정보 반환")
class getCredential {
class GetCredential {

@Test
@DisplayName("인증 정보 반환 성공")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ void createCredential() {

@Nested
@DisplayName("인증 정보로부터 회원 추출")
class extractMember {
class ExtractMember {

@Test
@DisplayName("회원 추출 성공")
void extractMember() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
import codezap.auth.dto.LoginAndCredentialDto;
import codezap.auth.dto.request.LoginRequest;
import codezap.auth.dto.response.LoginResponse;
import codezap.auth.encryption.PasswordEncryptor;
import codezap.auth.encryption.SHA2PasswordEncryptor;
import codezap.auth.provider.CredentialProvider;
import codezap.global.DatabaseIsolation;
import codezap.global.exception.CodeZapException;
Expand All @@ -29,6 +27,7 @@
@SpringBootTest
@DatabaseIsolation
public class AuthServiceTest {

@Autowired
private MemberRepository memberRepository;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class CategoryControllerTest extends MockMvcTest {

@Nested
@DisplayName("카테고리 생성 테스트")
class createCategoryTest {
class CreateCategoryTest {

@Test
@DisplayName("카테고리 생성 성공")
Expand Down Expand Up @@ -109,7 +109,7 @@ void findAllCategoriesSuccess() throws Exception {

@Nested
@DisplayName("카테고리 수정 테스트")
class updateCategoryTest {
class UpdateCategoryTest {

@Test
@DisplayName("카테고리 수정 성공")
Expand Down Expand Up @@ -174,7 +174,7 @@ void updateCategoryFailWithDuplicatedName() throws Exception {

@Nested
@DisplayName("카테고리 삭제 테스트")
class deleteCategoryTest {
class DeleteCategoryTest {

@Test
@DisplayName("카테고리 삭제 성공")
Expand Down Expand Up @@ -205,7 +205,7 @@ void deleteCategoryFailWithUnauthorized() throws Exception {

@Test
@DisplayName("카테고리 삭제 실패: 존재하지 않는 카테고리의 삭제 요청")
void updateCategoryFailWithDuplicatedName() throws Exception {
void deleteCategoryFailWithDuplicatedName() throws Exception {
long id = 2L;

doThrow(new CodeZapException(HttpStatus.NOT_FOUND, "식별자 " + id + "에 해당하는 카테고리가 존재하지 않습니다."))
Expand All @@ -220,7 +220,7 @@ void updateCategoryFailWithDuplicatedName() throws Exception {

@Test
@DisplayName("카테고리 삭제 실패: 템플릿이 존재하는 카테고리는 삭제 불가능")
void updateCategoryFailWithlongName() throws Exception {
void deleteCategoryFailWithlongName() throws Exception {
long categoryId = 1L;
doThrow(new CodeZapException(HttpStatus.BAD_REQUEST, "템플릿이 존재하는 카테고리는 삭제할 수 없습니다."))
.when(categoryService).deleteById(any(), any());
Expand Down
Loading