[横書きアイテム]そんなREST APIで大丈夫ですか?本格的なREST APIを実装してみる-イベントパッケージを読み込む

30645 ワード

Event Read Allテストの実施


今回は,Pagingを用いて10個のイベントごとの機能を実現する.
彼に関する考慮事項は以下の通りです.
  • ページが正常かどうか.
  • Pagingによって得られた結果もHATEAOSを満たすかどうか.
  • ページのイベントがHATEAOSを満たしているかどうか.
  • 以下に示すように、テストコードとして実装します.
    	@Test
        public void get_sorted_event_list_by_paging() throws Exception {
            // Given
            IntStream.range(1,30).forEach( i ->{
                Event event = createEvent(i);
                this.eventRepository.save(event);
            });
    
            //When
            //Then
            mockMvc.perform(get("/api/events")
                            .param("page","1")
                            .param("size","10")
                            .param("sort", "name,DESC")
                    )
                    .andDo(print())
                    .andExpect(status().isOk())
                    // Result가 HATEOAS를 만족하는가
                    .andExpect(jsonPath("_links").exists())
                    // 제대로 Paging이 이뤄졌는가
                    .andExpect(jsonPath("page").exists())
                    // Page 내의 Event들도 제대로 HATEAOS를 만족하는가.
                    .andExpect(jsonPath("_embedded.eventResourceList.[0]._links.self").exists());
        }

    Serivceの変更


    PageでEventを読み取るreadWithPage機能を実現し、create機能の一部を変更しました.
  • create()内でEvent->EventResource->コントローラに変換EventResource変換とURI接続を担当
    package com.carrykim.restapi.event.service;
    
    import com.carrykim.restapi.event.infra.EventJpaRepository;
    import com.carrykim.restapi.event.infra.EventRepository;
    import com.carrykim.restapi.event.model.Event;
    import com.carrykim.restapi.event.model.dto.EventDto;
    import com.carrykim.restapi.event.model.dto.EventResource;
    import org.springframework.data.domain.Page;
    import org.springframework.data.domain.PageImpl;
    import org.springframework.data.domain.Pageable;
    import org.springframework.data.web.PagedResourcesAssembler;
    import org.springframework.hateoas.PagedModel;
    import org.springframework.stereotype.Service;
    
    @Service
    public class EventService {
    
        private final EventRepository eventRepository;
    
        public EventService(EventRepository eventRepository) {
            this.eventRepository = eventRepository;
        }
    
        public Event create(EventDto eventDto){
            Event newEvent = eventDto.toModel();
            return eventRepository.save(newEvent);
        }
    
        public Page<Event> readWithPage(Pageable pageable){
            return this.eventRepository.findAll(pageable);
        }
    
    }
    

    コントローラの変更

  • コントローラはイベント->イベントリソース変換とURIマッピングを担当し、
  • URIマッピング部をaddLink()法で
  • 分離する.
    package com.carrykim.restapi.event.controller;
    
    import com.carrykim.restapi.event.model.Event;
    import com.carrykim.restapi.event.model.dto.EventDto;
    import com.carrykim.restapi.event.model.dto.EventResource;
    import com.carrykim.restapi.event.service.EventService;
    import org.springframework.data.domain.Pageable;
    import org.springframework.data.web.PagedResourcesAssembler;
    import org.springframework.hateoas.MediaTypes;
    import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.*;
    
    import javax.validation.Valid;
    import java.net.URI;
    
    import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
    import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;
    
    @RestController()
    @RequestMapping(value = "/api/events", produces = MediaTypes.HAL_JSON_VALUE)
    public class EventController {
    
        private final EventService eventService;
    
        public EventController(EventService eventService) {
            this.eventService = eventService;
        }
    
        @PostMapping("")
        public ResponseEntity create(@RequestBody @Valid EventDto eventDto) {
            Event event = this.eventService.create(eventDto);
            EventResource eventResource = new EventResource(event);
            addLinks(eventResource);
            URI uri = linkTo(methodOn(EventController.class)
                    .create(new EventDto()))
                    .slash(eventResource.getEvent().getId()).toUri();
            return ResponseEntity.created(uri).body(eventResource);
        }
    
        @GetMapping("")
        public ResponseEntity readAll(Pageable pageable, PagedResourcesAssembler pagedResourcesAssembler) {
            var result =  pagedResourcesAssembler
                    .toModel(this.eventService.readWithPage(pageable).map(event -> {
                        EventResource eventResource =  new EventResource(event);
                        addLinks(eventResource);
                        return eventResource;
                    }));
            return ResponseEntity.ok(result);
        }
    
        private void addLinks(EventResource eventResource){
            WebMvcLinkBuilder selfAndUpdateLink =  linkTo(methodOn(EventController.class)
                    .create(new EventDto()))
                    .slash(eventResource.getEvent().getId());
            WebMvcLinkBuilder queryLink =  linkTo(methodOn(EventController.class));
            eventResource.add(queryLink.withRel("query-events"));
            eventResource.add(selfAndUpdateLink.withRel("update-event"));
            eventResource.add(selfAndUpdateLink.withSelfRel());
        }
    }
    

    テスト結果