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]: Bookmark to survey #376

Merged
merged 13 commits into from
Feb 23, 2024
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public Long saveTargetAndGetId(String name, Instant date) {
.nickname(name)
.createdAt(date)
.updatedAt(date)
.imageUrl("empty image")
.build();
entityManager.persist(targetEntity);
return targetEntity.getId();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ public abstract class AbstractSurveyTestSupporter {
private static final String API_VERSION = "/v1";
private static final Set<String> tableNameSet = Set.of("target", "survey", "form_question", "choice");

protected ResultActions bookmarkSurvey(String token, Long surveyId) throws Exception {
return mockMvc.perform(MockMvcRequestBuilders
.post("/{version}/surveys/{surveyId}/bookmarks", "v1", surveyId)
.accept(MediaType.APPLICATION_JSON)
dojinyou marked this conversation as resolved.
Show resolved Hide resolved
.header(HttpHeaders.AUTHORIZATION, token));
}

protected ResultActions createSurvey(String token, String content) throws Exception {
return mockMvc.perform(MockMvcRequestBuilders
.post(API_VERSION + "/surveys")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

import me.nalab.survey.web.adaptor.bookmark.response.SurveyBookmarkResponse;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.ResultActions;

Expand Down Expand Up @@ -101,4 +102,16 @@ public static void assertIsSurveyDoesNotExists(ResultActions resultActions) thro
);
}

public static void assertIsBookmarked(ResultActions resultActions) throws Exception {
resultActions.andExpectAll(
status().isOk(),
content().contentType(MediaType.APPLICATION_JSON),
jsonPath("$.target_id").isString(),
jsonPath("$.survey_id").isString(),
jsonPath("$.nickname").isString(),
jsonPath("$.position").doesNotExist(),
jsonPath("$.image_url").doesNotExist()
);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package me.nalab.luffy.api.acceptance.test.survey.bookmark;

import static me.nalab.luffy.api.acceptance.test.survey.SurveyAcceptanceValidator.assertIsBookmarked;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import me.nalab.auth.mock.api.MockUserRegisterEvent;
import me.nalab.luffy.api.acceptance.test.TargetInitializer;
import me.nalab.luffy.api.acceptance.test.survey.AbstractSurveyTestSupporter;
import me.nalab.luffy.api.acceptance.test.survey.RequestSample;
import me.nalab.survey.jpa.adaptor.findid.repository.SurveyIdFindJpaRepository;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.test.context.TestPropertySource;

@SpringBootTest
@AutoConfigureMockMvc
@TestPropertySource("classpath:h2.properties")
@ComponentScan("me.nalab")
@EnableJpaRepositories(basePackages = {"me.nalab"})
@EntityScan(basePackages = {"me.nalab"})
class SurveyBookmarkAcceptanceTest extends AbstractSurveyTestSupporter {

@Autowired
private ApplicationEventPublisher applicationEventPublisher;

@Autowired
private TargetInitializer targetInitializer;

@Autowired
private SurveyIdFindJpaRepository surveyIdFindJpaRepository;

@Test
@DisplayName("surveyBookmark api 는 token의 주인에게 survey를 북마크한다.")
void BOOKMARK_SURVEY_TO_TOKEN_OWNER() throws Exception {
// given
var targetId = targetInitializer.saveTargetAndGetId("luffy",
LocalDateTime.now().minusYears(24).toInstant(ZoneOffset.UTC));
var token = "luffy's-double-token";
applicationEventPublisher.publishEvent(MockUserRegisterEvent.builder()
.expectedToken(token)
.expectedId(targetId)
.build());
createSurvey(token, RequestSample.DEFAULT_JSON);

var surveyId = getSurveyId(targetId);

// when
var result = bookmarkSurvey(token, surveyId);

// then
assertIsBookmarked(result);
}

private Long getSurveyId(Long targetId) {
return surveyIdFindJpaRepository.findAllIdByTargetId(targetId).get(0);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
alter table target add `job` TEXT;
alter table target add image_url TEXT;
alter table target add `version` BIGINT;

create table if not exists bookmarked_survey (
dojinyou marked this conversation as resolved.
Show resolved Hide resolved
target_id BIGINT not null,
bookmarked_survey_id BIGINT not null,
foreign key (target_id) references target (target_id),
foreign key (bookmarked_survey_id) references survey (survey_id),
unique index target_id_bookmarked_survey_id_idx (target_id, bookmarked_survey_id)
);
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ public class JwtDecryptInterceptorConfigurer implements WebMvcConfigurer {
"/v1/reviewers/summary*",
"/v2/surveys/*/feedbacks",
"/v1/feedbacks/bookmarks",
"/v1/users"
"/v1/users",
"/v1/surveys/*/bookmarks",
};

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public class MockAuthConfigurer implements WebMvcConfigurer {
"/v1/reviewers*",
"/v1/reviewers/summary*",
"/v2/surveys/*/feedbacks",
"/v1/surveys/*/bookmarks",
};

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package me.nalab.core.data.target;

import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.JoinColumn;

@Embeddable
public class SurveyBookmarkEntity {

@Column(name = "bookmarked_survey_id")
@JoinColumn(name = "survey_id", nullable = false)
private Long surveyId;

}
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package me.nalab.core.data.target;

import java.util.Set;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.Table;

import javax.persistence.Version;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
Expand All @@ -31,4 +36,21 @@ public class TargetEntity extends TimeBaseEntity {
@Column(name = "position")
private String position;

@Column(name = "job", columnDefinition = "TEXT")
private String job;

@Column(name = "image_url", columnDefinition = "TEXT")
private String imageUrl;

@ElementCollection
@CollectionTable(
name = "bookmarked_survey",
joinColumns = @JoinColumn(name = "target_id")
)
private Set<SurveyBookmarkEntity> bookmarkedSurveys;
dojinyou marked this conversation as resolved.
Show resolved Hide resolved

@Version
@Column(name = "version")
private Long version;

}
113 changes: 113 additions & 0 deletions support/e2e/v1_7_bookmark_survey.hurl
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
POST http://nalab-server:8080/v1/oauth/default # Default provider를 통해서 로그인 진행
{
"nickname": "bookmark_survey",
"email": "hello@123456"
}

HTTP 200
[Asserts]
header "Content-type" == "application/json"

jsonpath "$.access_token" exists
jsonpath "$.token_type" exists

[Captures]
token_type: jsonpath "$.token_type"
auth_token: jsonpath "$.access_token"

##########

POST http://nalab-server:8080/v1/surveys # 발급받은 토큰으로 survey를 생성한다.
Authorization: {{ token_type }} {{ auth_token }}
{
"question_count": 2,
"question": [
{
"type": "choice",
"form_type": "tendency",
"title": "저는 UI, UI, GUI 중에 어떤 분야를 가장 잘하는 것 같나요?",
"choices": [
{
"content": "UI",
"order": 1
},
{
"content": "UX",
"order": 2
},
{
"content": "GUI",
"order": 3
}
],
"max_selectable_count": 1,
"order": 1
},
{
"type": "short",
"form_type": "strength",
"title": "저는 UX, UI, GUI 중에 어떤 분야에 더 강점이 있나요?",
"order": 2
}
]
}

HTTP 201
[Asserts]
header "Content-type" == "application/json"

jsonpath "$.survey_id" exists

[Captures]
survey_id: jsonpath "$.survey_id"

##########

GET http://nalab-server:8080/v1/surveys/{{ survey_id }} # 생성된 survey를 조회한다.

HTTP 200
[Asserts]
header "Content-type" == "application/json"

jsonpath "$.survey_id" exists

jsonpath "$.target.id" exists
jsonpath "$.target.nickname" == "bookmark_survey"

jsonpath "$.question_count" == 2
jsonpath "$.question.[0].question_id" exists
jsonpath "$.question.[0].type" == "choice"
jsonpath "$.question.[0].form_type" == "tendency"
jsonpath "$.question.[0].title" == "저는 UI, UI, GUI 중에 어떤 분야를 가장 잘하는 것 같나요?"
jsonpath "$.question.[0].order" == 1
jsonpath "$.question.[0].max_selectable_count" == 1
jsonpath "$.question.[0].choices.[0].choice_id" exists
jsonpath "$.question.[0].choices.[0].content" == "UI"
jsonpath "$.question.[0].choices.[0].order" == 1
jsonpath "$.question.[0].choices.[1].choice_id" exists
jsonpath "$.question.[0].choices.[1].content" == "UX"
jsonpath "$.question.[0].choices.[1].order" == 2
jsonpath "$.question.[0].choices.[2].choice_id" exists
jsonpath "$.question.[0].choices.[2].content" == "GUI"
jsonpath "$.question.[0].choices.[2].order" == 3
jsonpath "$.question.[1].question_id" exists
jsonpath "$.question.[1].type" == "short"
jsonpath "$.question.[1].form_type" == "strength"
jsonpath "$.question.[1].title" == "저는 UX, UI, GUI 중에 어떤 분야에 더 강점이 있나요?"
jsonpath "$.question.[1].order" == 2

[Captures]
target_id: jsonpath "$.target.id"

##########

POST http://nalab-server:8080/v1/surveys/{{ survey_id }}/bookmarks
Authorization: {{ token_type }} {{ auth_token }}

HTTP 200
[Asserts]
header "Content-type" == "application/json"

jsonpath "$.target_id" == {{ target_id }}
jsonpath "$.survey_id" == {{ survey_id }}
jsonpath "$.nickname" == "bookmark_survey"
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package me.nalab.survey.application.common.survey.dto;

import lombok.Builder;
import me.nalab.survey.domain.target.SurveyBookmark;
import me.nalab.survey.domain.target.Target;

@Builder
public record SurveyBookmarkDto(
Long targetId,
Long surveyId,
String nickname,
String position,
String job,
String imageUrl
) {
dojinyou marked this conversation as resolved.
Show resolved Hide resolved

public static SurveyBookmarkDto from(Long surveyId, Target target) {
return SurveyBookmarkDto.builder()
.surveyId(surveyId)
.targetId(target.getId())
.nickname(target.getNickname())
.job(target.getJob())
.imageUrl(target.getImageUrl())
.position(target.getPosition())
.build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package me.nalab.survey.application.port.in.web.bookmark;

import me.nalab.survey.application.common.survey.dto.SurveyBookmarkDto;

public interface SurveyBookmarkReplaceUseCase {

/**
* targetId에 해당하는 유저에게 survey를 북마크합니다.
* 이미 북마크되어있다면 북마크를 취소합니다.
*/
SurveyBookmarkDto bookmark(Long targetId, Long surveyId);

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,10 @@ public interface TargetFindPort {
*/
Optional<Target> findTargetById(Long targetId);

/**
* targetId를 받아 Target을 반환합니다.
* targetId에 해당하는 Target이 없다면 예외를 던집니다.
*/
Target getTargetById(Long targetId);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package me.nalab.survey.application.service.bookmark;

import lombok.RequiredArgsConstructor;
import me.nalab.survey.application.common.survey.dto.SurveyBookmarkDto;
import me.nalab.survey.application.exception.SurveyDoesNotExistException;
import me.nalab.survey.application.port.in.web.bookmark.SurveyBookmarkReplaceUseCase;
import me.nalab.survey.application.port.out.persistence.findfeedback.SurveyExistCheckPort;
import me.nalab.survey.application.port.out.persistence.findtarget.TargetFindPort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class SurveyBookmarkReplaceService implements SurveyBookmarkReplaceUseCase {

private final TargetFindPort targetFindPort;
private final SurveyExistCheckPort surveyExistCheckPort;

@Override
@Transactional
public SurveyBookmarkDto bookmark(Long targetId, Long surveyId) {
var target = targetFindPort.getTargetById(targetId);

if (!surveyExistCheckPort.isExistSurveyBySurveyId(surveyId)) {
throw new SurveyDoesNotExistException(surveyId);
}

target.bookmark(surveyId);

return SurveyBookmarkDto.from(surveyId, target);
}
}
Loading
Loading