Skip to content

Commit

Permalink
feat: 검수 중인 장소에 대한 등록 승인 거절 API 구현 (#401)
Browse files Browse the repository at this point in the history
* refactor: 바뀐 API 명세에 따라 컨트롤러 로직 리팩터링

* feat: 리베이스 충돌 해결

* feat: 검수 장소에 대한 테스트 빌더 구현

* feat: 검수 장소 삭제 API 구현

* feat: 검수 장소 삭제 API 에 ManagerAuthInterceptor 적용

* feat: 검수 장소에서 Player 에 대해 지연로딩 적용

* test: mocking 삭제 및 상태 검증 테스트로 변경

* test: 행위 검증에서 상태 검증 테스트로 변경
  • Loading branch information
kokodak committed Oct 5, 2023
1 parent 49e11c1 commit eb5e8a9
Show file tree
Hide file tree
Showing 9 changed files with 249 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,14 @@ private HandlerInterceptor mapAuthInterceptor() {
.excludeRequestPattern("/**/*.ico")
.excludeRequestPattern("/ranks")
.excludeRequestPattern("/**", HttpMethod.OPTIONS)
.excludeRequestPattern("/places", HttpMethod.POST);
.excludeRequestPattern("/places", HttpMethod.POST)
.excludeRequestPattern("/temporary-places/**", HttpMethod.DELETE);
}

private HandlerInterceptor mapManagerAuthInterceptor() {
return new RequestMatcherInterceptor(managerAuthInterceptor)
.includeRequestPattern("/places", HttpMethod.POST);
.includeRequestPattern("/places", HttpMethod.POST)
.includeRequestPattern("/temporary-places/**", HttpMethod.DELETE);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,17 @@ public ResponseEntity<PlaceResponse> createPlace(@RequestBody final CreatePlaceR
.location(URI.create("/places/" + place.getId()))
.body(response);
}

// TODO: 2023/10/03 장소 검수 API 구현 이후 삭제 예정
// @PostMapping
// public ResponseEntity<PlaceResponse> createPlace(@Auth final PlayerRequest playerRequest,
// @ModelAttribute final CreatePlaceRequest createPlaceRequest) {
// CreatePlaceCommand createPlaceCommand = CreatePlaceCommand.of(playerRequest, createPlaceRequest);
// final Place savedPlace = placeService.createPlace(createPlaceCommand);
// final PlaceResponse response = PlaceResponse.from(savedPlace);
// return ResponseEntity
// .status(HttpStatus.CREATED)
// .location(URI.create("/places/" + savedPlace.getId()))
// .body(response);
// }
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
package com.now.naaga.temporaryplace.application;

import com.now.naaga.temporaryplace.repository.TemporaryPlaceRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Transactional
@Service
public class TemporaryPlaceService {

private final TemporaryPlaceRepository temporaryPlaceRepository;

public TemporaryPlaceService(final TemporaryPlaceRepository temporaryPlaceRepository) {
this.temporaryPlaceRepository = temporaryPlaceRepository;
}

public void deleteById(final Long id) {
return;
temporaryPlaceRepository.deleteById(id);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.now.naaga.player.domain.Player;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
Expand All @@ -28,7 +29,7 @@ public class TemporaryPlace extends BaseEntity {

private String imageUrl;

@ManyToOne
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "player_id")
private Player registeredPlayer;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,28 @@
package com.now.naaga.temporaryplace.presentation;

import com.now.naaga.temporaryplace.application.TemporaryPlaceService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/temporary-places")
@RestController
public class TemporaryPlaceController {

private final TemporaryPlaceService temporaryPlaceService;

public TemporaryPlaceController(final TemporaryPlaceService temporaryPlaceService) {
this.temporaryPlaceService = temporaryPlaceService;
}

@DeleteMapping("/{temporaryPlaceId}")
public ResponseEntity<Void> deleteTemporaryPlace(@PathVariable final Long temporaryPlaceId) {
temporaryPlaceService.deleteById(temporaryPlaceId);
return ResponseEntity
.status(HttpStatus.NO_CONTENT)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package com.now.naaga.common.builder;

import static com.now.naaga.common.fixture.PlaceFixture.DESCRIPTION;
import static com.now.naaga.common.fixture.PlaceFixture.IMAGE_URL;
import static com.now.naaga.common.fixture.PlaceFixture.NAME;
import static com.now.naaga.common.fixture.PositionFixture.잠실역_교보문고_좌표;

import com.now.naaga.place.domain.Position;
import com.now.naaga.player.domain.Player;
import com.now.naaga.temporaryplace.domain.TemporaryPlace;
import com.now.naaga.temporaryplace.repository.TemporaryPlaceRepository;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class TemporaryPlaceBuilder {

@Autowired
private TemporaryPlaceRepository temporaryPlaceRepository;

@Autowired
private PlayerBuilder playerBuilder;

private String name;

private String description;

private Position position;

private String imageUrl;

private Optional<Player> registeredPlayer;

public TemporaryPlaceBuilder init() {
this.name = NAME;
this.description = DESCRIPTION;
this.position = 잠실역_교보문고_좌표;
this.imageUrl = IMAGE_URL;
this.registeredPlayer = Optional.empty();
return this;
}

public TemporaryPlaceBuilder name(final String name) {
this.name = name;
return this;
}

public TemporaryPlaceBuilder description(final String description) {
this.description = description;
return this;
}

public TemporaryPlaceBuilder position(final Position position) {
this.position = position;
return this;
}

public TemporaryPlaceBuilder imageUrl(final String imageUrl) {
this.imageUrl = imageUrl;
return this;
}

public TemporaryPlaceBuilder registeredPlayer(final Player persistedPlayer) {
this.registeredPlayer = Optional.ofNullable(persistedPlayer);
return this;
}

public TemporaryPlace build() {
final Player persistedPlayer = registeredPlayer.orElseGet(this::getPersistedPlayer);
final TemporaryPlace temporaryPlace = new TemporaryPlace(name, description, position, imageUrl, persistedPlayer);
return temporaryPlaceRepository.save(temporaryPlace);
}

private Player getPersistedPlayer() {
return playerBuilder.init()
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,24 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.SoftAssertions.assertSoftly;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

import com.now.naaga.common.builder.PlaceBuilder;
import com.now.naaga.common.builder.PlayerBuilder;
import com.now.naaga.common.builder.TemporaryPlaceBuilder;
import com.now.naaga.common.exception.BaseExceptionType;
import com.now.naaga.place.application.dto.CreatePlaceCommand;
import com.now.naaga.place.domain.Place;
import com.now.naaga.place.domain.Position;
import com.now.naaga.place.exception.PlaceException;
import com.now.naaga.place.exception.PlaceExceptionType;
import com.now.naaga.player.domain.Player;
import com.now.naaga.temporaryplace.application.TemporaryPlaceService;
import com.now.naaga.temporaryplace.domain.TemporaryPlace;
import com.now.naaga.temporaryplace.repository.TemporaryPlaceRepository;
import org.junit.jupiter.api.DisplayNameGeneration;
import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -33,12 +32,15 @@
@SpringBootTest
class PlaceServiceTest {

@MockBean
private TemporaryPlaceService temporaryPlaceService;
@Autowired
private TemporaryPlaceRepository temporaryPlaceRepository;

@Autowired
private PlaceService placeService;

@Autowired
private TemporaryPlaceBuilder temporaryPlaceBuilder;

@Autowired
private PlaceBuilder placeBuilder;

Expand All @@ -52,7 +54,10 @@ class PlaceServiceTest {
final Player player = playerBuilder.init()
.build();

final Long temporaryPlaceId = 1L;
final TemporaryPlace temporaryPlace = temporaryPlaceBuilder.init()
.build();

final Long temporaryPlaceId = temporaryPlace.getId();

final CreatePlaceCommand createPlaceCommand = new CreatePlaceCommand("루터회관",
"이곳은 루터회관이다 알겠냐",
Expand All @@ -71,11 +76,14 @@ class PlaceServiceTest {
createPlaceCommand.imageUrl(),
player);

final TemporaryPlace found = temporaryPlaceRepository.findById(temporaryPlaceId)
.orElse(null);

assertSoftly(softAssertions -> {
assertThat(actual).usingRecursiveComparison()
.ignoringExpectedNullFields()
.isEqualTo(expected);
verify(temporaryPlaceService, times(1)).deleteById(temporaryPlaceId);
assertThat(found).isNull();
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.now.naaga.temporaryplace.application;

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

import com.now.naaga.common.builder.TemporaryPlaceBuilder;
import com.now.naaga.temporaryplace.domain.TemporaryPlace;
import com.now.naaga.temporaryplace.repository.TemporaryPlaceRepository;
import org.junit.jupiter.api.DisplayNameGeneration;
import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.jdbc.Sql;

@SuppressWarnings("NonAsciiCharacters")
@DisplayNameGeneration(ReplaceUnderscores.class)
@Sql("/truncate.sql")
@ActiveProfiles("test")
@SpringBootTest
class TemporaryPlaceServiceTest {

@Autowired
private TemporaryPlaceRepository temporaryPlaceRepository;

@Autowired
private TemporaryPlaceService temporaryPlaceService;

@Autowired
private TemporaryPlaceBuilder temporaryPlaceBuilder;

@Test
void ID로_검수_장소_데이터를_삭제한다() {
// given
final TemporaryPlace temporaryPlace = temporaryPlaceBuilder.init()
.build();

final Long id = temporaryPlace.getId();

// when
temporaryPlaceService.deleteById(id);

// then
final TemporaryPlace actual = temporaryPlaceRepository.findById(id)
.orElse(null);

assertThat(actual).isNull();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.now.naaga.temporaryplace.presentation;

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

import com.now.naaga.common.CommonControllerTest;
import com.now.naaga.common.builder.TemporaryPlaceBuilder;
import com.now.naaga.temporaryplace.domain.TemporaryPlace;
import io.restassured.RestAssured;
import io.restassured.response.ExtractableResponse;
import io.restassured.response.Response;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayNameGeneration;
import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.test.context.ActiveProfiles;

@SuppressWarnings("NonAsciiCharacters")
@DisplayNameGeneration(ReplaceUnderscores.class)
@ActiveProfiles("test")
class TemporaryPlaceControllerTest extends CommonControllerTest {

@Autowired
private TemporaryPlaceBuilder temporaryPlaceBuilder;

@Value("${manager.id}")
private String id;

@Value("${manager.password}")
private String password;

@BeforeEach
protected void setUp() {
super.setUp();
}

@Test
void ID를_통한_삭제_요청이_성공하면_204_응답코드를_반환한다() {
// given
final TemporaryPlace temporaryPlace = temporaryPlaceBuilder.init()
.build();

// when
final ExtractableResponse<Response> extract = RestAssured.given()
.log().all()
.auth().preemptive().basic(id, password)
.when()
.delete("/temporary-places/{temporaryPlaceId}", temporaryPlace.getId())
.then()
.log().all()
.extract();

// then
final int statusCode = extract.statusCode();
assertThat(statusCode).isEqualTo(HttpStatus.NO_CONTENT.value());
}
}

0 comments on commit eb5e8a9

Please sign in to comment.