mock route 등록/수정 로직 변경, 복제 기능 추가
This commit is contained in:
+32
-16
@@ -1,8 +1,9 @@
|
||||
package com.eactive.httpmockserver.api.controller;
|
||||
|
||||
import com.eactive.httpmockserver.api.dto.MockResponseDTO;
|
||||
import com.eactive.httpmockserver.api.dto.MockRouteDTO;
|
||||
import com.eactive.httpmockserver.api.dto.MockRouteRegisterDTO;
|
||||
import com.eactive.httpmockserver.api.dto.MockRouteSearch;
|
||||
import com.eactive.httpmockserver.api.dto.MockRouteUpdateDTO;
|
||||
import com.eactive.httpmockserver.api.entity.MockRoute;
|
||||
import com.eactive.httpmockserver.api.mapper.MockRouteMapper;
|
||||
import com.eactive.httpmockserver.api.service.MockRouteService;
|
||||
@@ -14,6 +15,7 @@ import org.springframework.ui.ModelMap;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.List;
|
||||
@@ -48,37 +50,51 @@ public class MockRouteMgmtController {
|
||||
public static final String MOCK_ROUTE_LIST_VIEW = "redirect:/mgmt/routes/list_view.do";
|
||||
|
||||
@GetMapping("/list_view.do")
|
||||
public String listView(@ModelAttribute("mockRouteSearch") MockRouteSearch mockRouteSearch, ModelMap model, Pageable pageable) {
|
||||
|
||||
public ModelAndView listView(@ModelAttribute("mockRouteSearch") MockRouteSearch mockRouteSearch, Pageable pageable) {
|
||||
ModelAndView mav = new ModelAndView("page/routes/mockRouteList");
|
||||
Page<MockRoute> page = mockRouteService.findAll(mockRouteSearch.buildSpecification(), pageable);
|
||||
model.addAttribute("page", page);
|
||||
model.addAttribute("pageable", pageable);
|
||||
model.addAttribute("modelSearch", mockRouteSearch);
|
||||
mav.addObject("page", page);
|
||||
mav.addObject("pageable", pageable);
|
||||
mav.addObject("modelSearch", mockRouteSearch);
|
||||
|
||||
return "page/routes/mockRouteList";
|
||||
return mav;
|
||||
}
|
||||
|
||||
@GetMapping("/create_view.do")
|
||||
public String createView(ModelMap model) {
|
||||
MockRouteDTO defaultRoute = new MockRouteDTO();
|
||||
public ModelAndView createView() {
|
||||
ModelAndView mav = new ModelAndView(MOCK_ROUTE_EDIT);
|
||||
MockRouteRegisterDTO defaultRoute = new MockRouteRegisterDTO();
|
||||
MockResponseDTO defaultResponse = new MockResponseDTO();
|
||||
defaultResponse.setDefaultResponse("true");
|
||||
defaultRoute.getResponses().add(defaultResponse);
|
||||
model.addAttribute(MOCK_ROUTE, defaultRoute);
|
||||
return MOCK_ROUTE_EDIT;
|
||||
mav.addObject(MOCK_ROUTE, defaultRoute);
|
||||
return mav;
|
||||
}
|
||||
|
||||
@GetMapping("/update_view.do")
|
||||
public String updateView(ModelMap model, @RequestParam("id") Long id) {
|
||||
public ModelAndView updateView(@RequestParam("id") Long id) {
|
||||
|
||||
ModelAndView mav = new ModelAndView(MOCK_ROUTE_EDIT);
|
||||
MockRoute found = mockRouteService.findById(id);
|
||||
mav.addObject(MOCK_ROUTE, mockRouteMapper.map(found));
|
||||
|
||||
model.addAttribute(MOCK_ROUTE, mockRouteMapper.map(found));
|
||||
return MOCK_ROUTE_EDIT;
|
||||
return mav;
|
||||
}
|
||||
|
||||
@PostMapping("/clone_view.do")
|
||||
public ModelAndView cloneView(@RequestParam("id") Long id) {
|
||||
|
||||
ModelAndView mav = new ModelAndView(MOCK_ROUTE_EDIT);
|
||||
MockRoute found = mockRouteService.findById(id);
|
||||
MockRouteRegisterDTO dto = mockRouteMapper.map(found);
|
||||
dto.setId(null);
|
||||
mav.addObject(MOCK_ROUTE, dto);
|
||||
|
||||
return mav;
|
||||
}
|
||||
|
||||
@PostMapping("/register.do")
|
||||
public String register(@ModelAttribute("mockRoute") MockRouteDTO dto, BindingResult bindingResult, ModelMap model) {
|
||||
public String register(@ModelAttribute("mockRoute") MockRouteRegisterDTO dto, BindingResult bindingResult, ModelMap model) {
|
||||
|
||||
if (dto == null) {
|
||||
return "redirect:/mgmt/routes/create_view.do";
|
||||
@@ -101,7 +117,7 @@ public class MockRouteMgmtController {
|
||||
|
||||
@PostMapping("/update.do")
|
||||
public String update(@RequestParam("id") String id,
|
||||
@ModelAttribute("mockRoute") MockRouteDTO dto,
|
||||
@ModelAttribute("mockRoute") MockRouteUpdateDTO dto,
|
||||
BindingResult bindingResult, ModelMap model) {
|
||||
|
||||
if (StringUtils.isEmpty(id)) {
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.eactive.httpmockserver.api.dto;
|
||||
|
||||
import com.eactive.httpmockserver.common.validator.UniqueRoute;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@UniqueRoute(path = "pathPattern", method = "method")
|
||||
public class MockRouteRegisterDTO {
|
||||
|
||||
private Long id;
|
||||
|
||||
@NotEmpty
|
||||
private String method;
|
||||
|
||||
@NotEmpty
|
||||
private String pathPattern;
|
||||
|
||||
@NotEmpty
|
||||
private String name;
|
||||
|
||||
private String description;
|
||||
|
||||
@Size(min = 1, message = "Response must have at least one item")
|
||||
private List<MockResponseDTO> responses = new ArrayList<>();
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public void setMethod(String method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public String getPathPattern() {
|
||||
return pathPattern;
|
||||
}
|
||||
|
||||
public void setPathPattern(String pathPattern) {
|
||||
this.pathPattern = pathPattern;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public List<MockResponseDTO> getResponses() {
|
||||
return responses;
|
||||
}
|
||||
|
||||
public void setResponses(List<MockResponseDTO> responses) {
|
||||
this.responses = responses;
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -1,11 +1,13 @@
|
||||
package com.eactive.httpmockserver.api.dto;
|
||||
|
||||
import com.eactive.httpmockserver.common.validator.UniqueRoute;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MockRouteDTO {
|
||||
public class MockRouteUpdateDTO {
|
||||
|
||||
private Long id;
|
||||
|
||||
@@ -2,7 +2,8 @@ package com.eactive.httpmockserver.api.mapper;
|
||||
|
||||
import com.eactive.httpmockserver.api.dto.MockHeaderDTO;
|
||||
import com.eactive.httpmockserver.api.dto.MockResponseDTO;
|
||||
import com.eactive.httpmockserver.api.dto.MockRouteDTO;
|
||||
import com.eactive.httpmockserver.api.dto.MockRouteRegisterDTO;
|
||||
import com.eactive.httpmockserver.api.dto.MockRouteUpdateDTO;
|
||||
import com.eactive.httpmockserver.api.entity.MockResponse;
|
||||
import com.eactive.httpmockserver.api.entity.MockRoute;
|
||||
import com.eactive.httpmockserver.common.mapper.CommonMapper;
|
||||
@@ -34,10 +35,11 @@ public abstract class MockRouteMapper {
|
||||
XML_ESCAPE_MAP.put("'", "'");
|
||||
}
|
||||
|
||||
public abstract MockRoute map(MockRouteDTO dto);
|
||||
public abstract MockRoute map(MockRouteRegisterDTO dto);
|
||||
public abstract MockRoute map(MockRouteUpdateDTO dto);
|
||||
|
||||
|
||||
public abstract MockRouteDTO map(MockRoute entity);
|
||||
public abstract MockRouteRegisterDTO map(MockRoute entity);
|
||||
|
||||
@Mapping(target = "headers", source = "headers", qualifiedByName = "mapHeadersToMap")
|
||||
@Mapping(target = "defaultResponse", source = "defaultResponse", qualifiedByName = "mapEntityDefaultResponse")
|
||||
|
||||
@@ -5,4 +5,6 @@ import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
|
||||
public interface MockRouteRepository extends JpaRepository<MockRoute, Long>, JpaSpecificationExecutor<MockRoute> {
|
||||
|
||||
long countByMethodAndPathPattern(String method, String pathPattern);
|
||||
}
|
||||
|
||||
@@ -74,8 +74,8 @@ public class MockRouteService {
|
||||
|
||||
route.setName(updated.getName());
|
||||
route.setDescription(updated.getDescription());
|
||||
route.setMethod(updated.getMethod());
|
||||
route.setPathPattern(updated.getPathPattern());
|
||||
// route.setMethod(updated.getMethod());
|
||||
// route.setPathPattern(updated.getPathPattern());
|
||||
if (!route.getPathPattern().startsWith("/")) {
|
||||
route.setPathPattern("/" + route.getPathPattern());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.eactive.httpmockserver.common.validator;
|
||||
|
||||
import javax.validation.Constraint;
|
||||
import javax.validation.Payload;
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Constraint(validatedBy = UniqueRouteValidator.class)
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface UniqueRoute {
|
||||
String message() default "등록된 API가 존재합니다.";
|
||||
|
||||
Class<?>[] groups() default {};
|
||||
|
||||
Class<? extends Payload>[] payload() default {};
|
||||
|
||||
String path();
|
||||
|
||||
String method();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.eactive.httpmockserver.common.validator;
|
||||
|
||||
import com.eactive.httpmockserver.api.repository.MockRouteRepository;
|
||||
import com.eactive.httpmockserver.common.exception.UserNotFoundException;
|
||||
import com.eactive.httpmockserver.common.util.ApplicationContextUtil;
|
||||
import com.eactive.httpmockserver.user.service.UserService;
|
||||
import org.apache.commons.beanutils.PropertyUtils;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
|
||||
import javax.validation.ConstraintValidator;
|
||||
import javax.validation.ConstraintValidatorContext;
|
||||
|
||||
public class UniqueRouteValidator implements ConstraintValidator<UniqueRoute, Object> {
|
||||
|
||||
private String method;
|
||||
|
||||
private String path;
|
||||
|
||||
@Override
|
||||
public void initialize(UniqueRoute constraintAnnotation) {
|
||||
ConstraintValidator.super.initialize(constraintAnnotation);
|
||||
this.method = constraintAnnotation.method();
|
||||
this.path = constraintAnnotation.path();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(Object value, ConstraintValidatorContext context) {
|
||||
String methodValue;
|
||||
String pathValue;
|
||||
try {
|
||||
methodValue = (String) PropertyUtils.getProperty(value, this.method);
|
||||
pathValue = (String) PropertyUtils.getProperty(value, this.path);
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
MockRouteRepository mockRouteRepository = ApplicationContextUtil.getContext().getBean(MockRouteRepository.class);
|
||||
long found = mockRouteRepository.countByMethodAndPathPattern(methodValue, pathValue);
|
||||
|
||||
String message = "등록된 API가 존재합니다.";
|
||||
|
||||
if (found > 0L){
|
||||
context.disableDefaultConstraintViolation();
|
||||
context.buildConstraintViolationWithTemplate(message)
|
||||
.addPropertyNode(this.path)
|
||||
.addConstraintViolation();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@
|
||||
</div>
|
||||
<div class="d-flex align-items-center mt-2">
|
||||
<div class="form-floating col-md-1 me-2">
|
||||
<select name="method" class="form-select"
|
||||
<select name="method" class="form-select" th:readonly="${mockRoute.id != null}"
|
||||
th:classappend="${#fields.hasErrors('method')}? 'is-invalid'" id="method">
|
||||
<option th:selected="${mockRoute.method == 'POST'}" value="POST">POST</option>
|
||||
<option th:selected="${mockRoute.method == 'GET'}" value="GET">GET</option>
|
||||
@@ -45,11 +45,18 @@
|
||||
</div>
|
||||
<div class="form-floating col-md">
|
||||
<input type="text" name="pathPattern" class="form-control" id="pathPattern"
|
||||
th:field="*{pathPattern}"
|
||||
th:field="*{pathPattern}" th:readonly="${mockRoute.id != null}"
|
||||
placeholder="서버에 노출 되는 경로 (/부터 입력)" required>
|
||||
<label for="pathPattern" class="must">Path</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-floating col-md">
|
||||
<input type="hidden" th:classappend="${#fields.hasGlobalErrors() || #fields.hasErrors('pathPattern')}? 'is-invalid'"
|
||||
placeholder="서버에 노출 되는 경로 (/부터 입력)" required th:readonly="${mockRoute.id != null}">
|
||||
<th:block th:each="err : ${#fields.errors('pathPattern')}">
|
||||
<div th:text="${err}" class="invalid-feedback"></div>
|
||||
</th:block>
|
||||
</div>
|
||||
<div id="responses" class="mt-1">
|
||||
<div class="row" th:each="response, status: *{responses}">
|
||||
<input type="number" th:field="*{responses[__${status.index}__].statusCode}" required/>
|
||||
|
||||
@@ -90,6 +90,9 @@
|
||||
<button type="button" class="btn btn-primary btn-sm me-2" th:onclick="fnSelectItem([[${row.id}]])" th:value="#{button.update}"><i class="fa-sharp fa-solid fa-edit"></i>
|
||||
[[#{button.update}]]
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary btn-sm me-2" th:onclick="fnCloneItem([[${row.id}]])"><i class="fa-sharp fa-solid fa-copy"></i>
|
||||
복제
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -100,6 +103,9 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
<form name="cloneForm" th:action="@{/mgmt/routes/clone_view.do}" method="post" style="display: none">
|
||||
<input type="hidden" name="id">
|
||||
</form>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<div th:replace="~{fragment/pagination :: pagination(jsFunction='fn_select_page')}"></div>
|
||||
@@ -123,6 +129,11 @@
|
||||
location.href = "[[@{/mgmt/routes/update_view.do}]]" + "?id=" + id;
|
||||
}
|
||||
|
||||
function fnCloneItem(id){
|
||||
document.cloneForm.id.value = id;
|
||||
document.cloneForm.submit();
|
||||
}
|
||||
|
||||
$(function () {
|
||||
let fnSearch = document.querySelector(".btn_search");
|
||||
let fnDelete = document.querySelector(".btn_delete");
|
||||
|
||||
Reference in New Issue
Block a user