새 컬렉션 추가, 로딩, 새 요청 추가

This commit is contained in:
현성필
2023-05-15 15:47:33 +09:00
parent f12352db52
commit 78277d271d
22 changed files with 653 additions and 87 deletions
@@ -2,13 +2,9 @@ package com.eactive.httpmockserver.client.controller;
import com.eactive.httpmockserver.client.dto.ApiRequestDTO;
import com.eactive.httpmockserver.client.dto.ApiResponse;
import com.eactive.httpmockserver.client.entity.ApiCollection;
import com.eactive.httpmockserver.client.entity.Server;
import com.eactive.httpmockserver.client.repository.ServerRepository;
import com.eactive.httpmockserver.client.service.ApiTesterService;
import com.eactive.httpmockserver.client.service.NettyApiClient;
import com.eactive.httpmockserver.common.security.CurrentUser;
import com.eactive.httpmockserver.user.entity.StaffUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
@@ -25,13 +21,10 @@ public class ApiClientController {
@Autowired
private ServerRepository serverRepository;
@Autowired
private ApiTesterService apiTesterService;
@Autowired
private NettyApiClient nettyApiClient;
@PostMapping("/api/test.do")
@PostMapping("/mgmt/api/test.do")
@ResponseBody
public ApiResponse handleApiRequest(@RequestBody ApiRequestDTO request) {
@@ -43,14 +36,10 @@ public class ApiClientController {
}
@GetMapping("/mgmt/api_tester.do")
public String testView(ModelMap model,
@CurrentUser StaffUser user) {
public String testView(ModelMap model) {
List<Server> servers = serverRepository.findAll();
List<ApiCollection> collections = apiTesterService.findMyCollections(user.getEsntlId());
model.addAttribute("servers", servers);
model.addAttribute("collections", collections);
return "page/apiTester";
}
@@ -0,0 +1,75 @@
package com.eactive.httpmockserver.client.controller;
import com.eactive.httpmockserver.client.dto.ApiCollectionDTO;
import com.eactive.httpmockserver.client.dto.ApiRequestDTO;
import com.eactive.httpmockserver.client.entity.ApiCollection;
import com.eactive.httpmockserver.client.entity.ApiRequestInfo;
import com.eactive.httpmockserver.client.mapper.ApiRequestMapper;
import com.eactive.httpmockserver.client.service.ApiRequestMgmtService;
import com.eactive.httpmockserver.common.security.CurrentUser;
import com.eactive.httpmockserver.user.entity.StaffUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
public class ApiRequestMgmtController {
@Autowired
ApiRequestMgmtService apiRequestMgmtService;
@Autowired
ApiRequestMapper apiRequestMapper;
@GetMapping("/mgmt/collections/list.do")
public ResponseEntity<List<ApiCollectionDTO>> listApiRequests(@CurrentUser StaffUser user) {
List<ApiCollection> collections = apiRequestMgmtService.getMyApis(user);
return ResponseEntity.ok(apiRequestMapper.mapCollections(collections));
}
@PostMapping("/mgmt/collections/create.do")
public ResponseEntity<String> createApiCollection(
@CurrentUser StaffUser user,
@RequestBody ApiCollectionDTO dto) {
ApiCollection collection = apiRequestMapper.map(dto);
collection.setOwner(user);
apiRequestMgmtService.createNewApiCollection(collection);
return ResponseEntity.ok("success");
}
@PutMapping("/mgmt/collections/update.do")
public ResponseEntity<String> updateApiCollection(@RequestBody ApiCollectionDTO dto) {
ApiCollection updated = apiRequestMapper.map(dto);
apiRequestMgmtService.updateApiCollection(updated.getId(), updated);
return ResponseEntity.ok("success");
}
@PostMapping("/mgmt/collections/delete.do")
public ResponseEntity<String> deleteApiCollection(@RequestBody ApiCollectionDTO dto) {
apiRequestMgmtService.deleteApiCollection(dto.getId());
return ResponseEntity.ok("success");
}
@PostMapping("/mgmt/collections/{id}/apis/save.do")
public ResponseEntity<String> addApiToCollection(@PathVariable(name = "id") String id,
@RequestBody ApiRequestDTO dto,
@CurrentUser StaffUser user) {
ApiRequestInfo apiRequestInfo = apiRequestMapper.map(dto);
apiRequestInfo.setOwner(user);
String apiId = apiRequestMgmtService.saveApiToCollection(id, apiRequestInfo);
return ResponseEntity.ok(apiId);
}
@PostMapping("/mgmt/collections/{id}/apis/delete.do")
public ResponseEntity<String> removeApiFromCollection(@PathVariable(name = "id") String id,
@RequestBody ApiRequestDTO dto) {
apiRequestMgmtService.removeApiFromCollection(id, dto.getId());
return ResponseEntity.ok("success");
}
}
@@ -0,0 +1,39 @@
package com.eactive.httpmockserver.client.dto;
import javax.validation.constraints.NotEmpty;
import java.util.ArrayList;
import java.util.List;
public class ApiCollectionDTO {
private String id;
@NotEmpty
private String name;
private List<ApiRequestDTO> apis = new ArrayList<>();
public void setId(String id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setApis(List<ApiRequestDTO> apis) {
this.apis = apis;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public List<ApiRequestDTO> getApis() {
return apis;
}
}
@@ -1,17 +1,40 @@
package com.eactive.httpmockserver.client.dto;
import java.util.ArrayList;
import java.util.List;
public class ApiRequestDTO {
private String id;
private String name;
private String path;
private String method;
private Long server;
private List<ApiHeaderDTO> headers;
private List<ApiRequestKeyValueDTO> headers = new ArrayList<>();
private List<ApiRequestKeyValueDTO> queryParams = new ArrayList<>();
private String requestBody;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPath() {
return path;
}
@@ -36,14 +59,22 @@ public class ApiRequestDTO {
this.method = method;
}
public List<ApiHeaderDTO> getHeaders() {
public List<ApiRequestKeyValueDTO> getHeaders() {
return headers;
}
public void setHeaders(List<ApiHeaderDTO> headers) {
public void setHeaders(List<ApiRequestKeyValueDTO> headers) {
this.headers = headers;
}
public List<ApiRequestKeyValueDTO> getQueryParams() {
return queryParams;
}
public void setQueryParams(List<ApiRequestKeyValueDTO> queryParams) {
this.queryParams = queryParams;
}
public String getRequestBody() {
return requestBody;
}
@@ -1,6 +1,6 @@
package com.eactive.httpmockserver.client.dto;
public class ApiHeaderDTO {
public class ApiRequestKeyValueDTO {
private boolean enabled;
@@ -30,4 +30,8 @@ public class ApiHeaderDTO {
public void setValue(String value) {
this.value = value;
}
public String toString() {
return (enabled?"//":"")+ key + "=" + value;
}
}
@@ -6,7 +6,7 @@ public class ApiResponse {
private int status;
private List<ApiHeaderDTO> headers;
private List<ApiRequestKeyValueDTO> headers;
private String body;
@@ -20,11 +20,11 @@ public class ApiResponse {
this.status = status;
}
public List<ApiHeaderDTO> getHeaders() {
public List<ApiRequestKeyValueDTO> getHeaders() {
return headers;
}
public void setHeaders(List<ApiHeaderDTO> headers) {
public void setHeaders(List<ApiRequestKeyValueDTO> headers) {
this.headers = headers;
}
@@ -3,6 +3,7 @@ package com.eactive.httpmockserver.client.entity;
import com.eactive.httpmockserver.user.entity.StaffUser;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
@@ -20,11 +21,11 @@ public class ApiCollection {
@ManyToMany
@JoinTable(
name = "API_COLLECTION_API_REQUEST_INFO",
joinColumns = @JoinColumn(name = "API_COLLECTION_ID"),
inverseJoinColumns = @JoinColumn(name = "API_REQUEST_INFO_ID")
name = "API_COLLECTION_API_REQUEST_INFO",
joinColumns = @JoinColumn(name = "API_COLLECTION_ID"),
inverseJoinColumns = @JoinColumn(name = "API_REQUEST_INFO_ID")
)
private List<ApiRequestInfo> apis;
private List<ApiRequestInfo> apis = new ArrayList<>();
public String getId() {
return id;
@@ -3,6 +3,7 @@ package com.eactive.httpmockserver.client.entity;
import com.eactive.httpmockserver.user.entity.StaffUser;
import javax.persistence.*;
import java.util.Objects;
@Entity
@@ -20,6 +21,8 @@ public class ApiRequestInfo {
@JoinColumn(name = "USER_ID")
private StaffUser owner;
private String name;
private String method;
private String path;
@@ -30,6 +33,14 @@ public class ApiRequestInfo {
private String requestBody;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
@@ -93,4 +104,26 @@ public class ApiRequestInfo {
public void setRequestBody(String requestBody) {
this.requestBody = requestBody;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ApiRequestInfo that = (ApiRequestInfo) o;
return Objects.equals(id, that.id);
}
@Override
public int hashCode() {
return Objects.hash(id, server, owner, method, path, headers, queryParams, requestBody);
}
public static void main(String[] args) {
ApiRequestInfo a= new ApiRequestInfo();
a.setId("a");
ApiRequestInfo b = new ApiRequestInfo();
a.setId("a");
System.out.println(a.equals(b));
}
}
@@ -0,0 +1,34 @@
package com.eactive.httpmockserver.client.mapper;
import com.eactive.httpmockserver.client.dto.ApiCollectionDTO;
import com.eactive.httpmockserver.client.dto.ApiRequestDTO;
import com.eactive.httpmockserver.client.dto.ApiRequestKeyValueDTO;
import com.eactive.httpmockserver.client.entity.ApiCollection;
import com.eactive.httpmockserver.client.entity.ApiRequestInfo;
import com.eactive.httpmockserver.common.mapper.CommonMapper;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.ReportingPolicy;
import org.springframework.stereotype.Component;
import java.util.List;
@Mapper(componentModel = "spring",
uses = {CommonMapper.class, ServerMapper.class},
unmappedTargetPolicy = ReportingPolicy.IGNORE)
@Component
public interface ApiRequestMapper {
ApiCollection map(ApiCollectionDTO dto);
ApiRequestInfo map(ApiRequestDTO dto);
ApiRequestDTO map(ApiRequestInfo entity);
List<ApiRequestInfo> mapApis(List<ApiRequestDTO> apis);
List<ApiCollectionDTO> mapCollections(List<ApiCollection> apis);
}
@@ -0,0 +1,30 @@
package com.eactive.httpmockserver.client.mapper;
import com.eactive.httpmockserver.client.entity.Server;
import com.eactive.httpmockserver.client.repository.ServerRepository;
import com.eactive.httpmockserver.common.exception.NotFoundException;
import org.mapstruct.Mapper;
import org.mapstruct.ReportingPolicy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Mapper(componentModel = "spring",
unmappedTargetPolicy = ReportingPolicy.IGNORE)
@Component
public class ServerMapper {
@Autowired
ServerRepository serverRepository;
Server map(Long id) {
if (id == null) return null;
return serverRepository.findById(id).orElseThrow(() -> new NotFoundException("Server[" + id + "] not found"));
}
Long map(Server server) {
if (server == null) return null;
return server.getId();
}
}
@@ -1,8 +1,13 @@
package com.eactive.httpmockserver.client.repository;
import com.eactive.httpmockserver.client.entity.ApiCollection;
import com.eactive.httpmockserver.client.entity.ApiRequestInfo;
import com.eactive.httpmockserver.user.entity.StaffUser;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import java.util.List;
public interface ApiRequestRepository extends JpaRepository<ApiRequestInfo, String>, JpaSpecificationExecutor<ApiRequestInfo> {
}
@@ -0,0 +1,84 @@
package com.eactive.httpmockserver.client.service;
import com.eactive.httpmockserver.client.entity.ApiCollection;
import com.eactive.httpmockserver.client.entity.ApiRequestInfo;
import com.eactive.httpmockserver.client.repository.ApiCollectionRepository;
import com.eactive.httpmockserver.client.repository.ApiRequestRepository;
import com.eactive.httpmockserver.common.exception.NotFoundException;
import com.eactive.httpmockserver.user.entity.StaffUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thymeleaf.util.StringUtils;
import javax.transaction.Transactional;
import java.util.List;
import java.util.UUID;
@Service
@Transactional
public class ApiRequestMgmtService {
@Autowired
ApiCollectionRepository apiCollectionRepository;
@Autowired
ApiRequestRepository apiRequestRepository;
public List<ApiCollection> getMyApis(StaffUser user) {
return apiCollectionRepository.findAllByOwner(user);
}
public void deleteApiCollection(String id) {
apiCollectionRepository.deleteById(id);
}
public void createNewApiCollection(ApiCollection collection) {
collection.setId(UUID.randomUUID().toString());
apiCollectionRepository.save(collection);
}
public void updateApiCollection(String id, ApiCollection updated) {
ApiCollection collection = apiCollectionRepository.findById(id).orElseThrow(() -> new NotFoundException("api collection[" + id + "] not found"));
collection.setName(updated.getName());
apiCollectionRepository.save(collection);
}
public String saveApiToCollection(String id, ApiRequestInfo api) {
ApiCollection collection = apiCollectionRepository.findById(id).orElseThrow(() -> new NotFoundException("api collection[" + id + "] not found"));
if (StringUtils.isEmpty(api.getId())) {
api.setId(UUID.randomUUID().toString());
apiRequestRepository.save(api);
} else {
String apiId = api.getId();
ApiRequestInfo existing = apiRequestRepository.findById(apiId).orElseThrow(() -> new NotFoundException("api[" + apiId + "] not found"));
existing.setName(api.getName());
existing.setMethod(api.getMethod());
existing.setPath(api.getPath());
existing.setHeaders(api.getHeaders());
existing.setQueryParams(api.getQueryParams());
existing.setServer(api.getServer());
existing.setRequestBody(api.getRequestBody());
apiRequestRepository.save(existing);
api = existing;
}
if (!collection.getApis().contains(api)) {
collection.getApis().add(api);
apiCollectionRepository.save(collection);
}
return api.getId();
}
public void removeApiFromCollection(String id, String apiId) {
ApiCollection collection = apiCollectionRepository.findById(id).orElseThrow(() -> new NotFoundException("api collection[" + id + "] not found"));
ApiRequestInfo api = apiRequestRepository.findById(apiId).orElseThrow(() -> new NotFoundException("api[" + apiId + "] not found"));
collection.getApis().remove(api);
apiCollectionRepository.save(collection);
}
}
@@ -1,25 +0,0 @@
package com.eactive.httpmockserver.client.service;
import com.eactive.httpmockserver.client.entity.ApiCollection;
import com.eactive.httpmockserver.client.repository.ApiCollectionRepository;
import com.eactive.httpmockserver.user.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
import java.util.List;
@Service
@Transactional
public class ApiTesterService {
@Autowired
private UserService userService;
@Autowired
ApiCollectionRepository apiCollectionRepository;
public List<ApiCollection> findMyCollections(String esntlId) {
return apiCollectionRepository.findAllByOwner(userService.findByEsntlId(esntlId));
}
}
@@ -1,6 +1,6 @@
package com.eactive.httpmockserver.client.service;
import com.eactive.httpmockserver.client.dto.ApiHeaderDTO;
import com.eactive.httpmockserver.client.dto.ApiRequestKeyValueDTO;
import com.eactive.httpmockserver.client.dto.ApiResponse;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
@@ -49,10 +49,10 @@ public class HttptHandler extends SimpleChannelInboundHandler<FullHttpResponse>
return headerSize;
}
public List<ApiHeaderDTO> convertHttpHeadersToList(HttpHeaders headers) {
List<ApiHeaderDTO> headerList = new ArrayList<>();
public List<ApiRequestKeyValueDTO> convertHttpHeadersToList(HttpHeaders headers) {
List<ApiRequestKeyValueDTO> headerList = new ArrayList<>();
for (Map.Entry<String, String> entry : headers.entries()) {
ApiHeaderDTO header = new ApiHeaderDTO();
ApiRequestKeyValueDTO header = new ApiRequestKeyValueDTO();
header.setKey(entry.getKey());
header.setValue(entry.getValue());
headerList.add(header);
@@ -1,6 +1,6 @@
package com.eactive.httpmockserver.client.service;
import com.eactive.httpmockserver.client.dto.ApiHeaderDTO;
import com.eactive.httpmockserver.client.dto.ApiRequestKeyValueDTO;
import com.eactive.httpmockserver.client.dto.ApiRequestDTO;
import com.eactive.httpmockserver.client.dto.ApiResponse;
import com.eactive.httpmockserver.client.entity.Server;
@@ -91,7 +91,7 @@ public class NettyApiClient {
if (apiRequest.getHeaders().size() > 0) {
HttpHeaders customHeaders = new DefaultHttpHeaders();
apiRequest.getHeaders().stream().filter(ApiHeaderDTO::isEnabled).forEach((header) -> {
apiRequest.getHeaders().stream().filter(ApiRequestKeyValueDTO::isEnabled).forEach((header) -> {
if(StringUtils.isNotEmpty(header.getKey()) && StringUtils.isNotEmpty(header.getValue())){
customHeaders.set(header.getKey(), header.getValue());
}
@@ -0,0 +1,34 @@
package com.eactive.httpmockserver.common.exception;
import java.time.LocalDateTime;
public class CustomErrorResponse {
private LocalDateTime timestamp;
private String error;
private int status;
public LocalDateTime getTimestamp() {
return timestamp;
}
public void setTimestamp(LocalDateTime timestamp) {
this.timestamp = timestamp;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
}
@@ -0,0 +1,24 @@
package com.eactive.httpmockserver.common.exception;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import java.time.LocalDateTime;
@RestControllerAdvice
public class CustomGlobalExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(NotFoundException.class)
public ResponseEntity<CustomErrorResponse> customHandleNotFound(Exception ex, WebRequest request) {
CustomErrorResponse errors = new CustomErrorResponse();
errors.setTimestamp(LocalDateTime.now());
errors.setError(ex.getMessage());
errors.setStatus(HttpStatus.NOT_FOUND.value());
return new ResponseEntity<>(errors, HttpStatus.NOT_FOUND);
}
}
@@ -1,5 +1,6 @@
package com.eactive.httpmockserver.common.mapper;
import com.eactive.httpmockserver.client.dto.ApiRequestKeyValueDTO;
import org.mapstruct.Mapper;
import org.mapstruct.Named;
import org.mapstruct.ReportingPolicy;
@@ -41,4 +42,40 @@ public class CommonMapper {
return grantedAuthorities.stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());
}
public String mapKeyValue(List<ApiRequestKeyValueDTO> list) {
if (list == null) return "";
StringBuilder result = new StringBuilder();
for (ApiRequestKeyValueDTO dto : list) {
result.append(dto.toString()).append("||");
}
if (result.length() > 0) {
result.setLength(result.length() - 2); // Remove the last "||"
}
return result.toString();
}
public List<ApiRequestKeyValueDTO> keyValueToList(String keyValue) {
List<ApiRequestKeyValueDTO> list = new ArrayList<>();
if (!StringUtils.hasLength(keyValue)) return list;
String[] pairs = keyValue.split("\\|\\|");
for (String pair : pairs) {
ApiRequestKeyValueDTO dto = new ApiRequestKeyValueDTO();
if (pair.startsWith("//")) {
dto.setEnabled(false);
pair = pair.substring(2);
} else {
dto.setEnabled(true);
}
String[] keyValueArray = pair.split("=");
if (keyValueArray.length == 2) {
dto.setKey(keyValueArray[0]);
dto.setValue(keyValueArray[1]);
list.add(dto);
}
}
return list;
}
}
@@ -16,14 +16,14 @@
<div class="d-flex" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item" sec:authorize="isAuthenticated()">
<a class="nav-link" href="/mgmt/routes/list_view.do">API 관리</a>
<a class="nav-link" href="/mgmt/routes/list_view.do">응답 관리</a>
</li>
<li class="nav-item" sec:authorize="isAuthenticated()">
<a class="nav-link" href="/mgmt/api_tester.do">API Tester</a>
<a class="nav-link" href="/mgmt/api_tester.do">Tester</a>
</li>
<li class="nav-item dropdown" sec:authorize="hasRole('ROLE_ADMIN')">
<a class="nav-link dropdown-toggle" href="#" id="mgmtDropDown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
관리
시스템 관리
</a>
<ul class="dropdown-menu" aria-labelledby="mgmtDropDown">
<li><a class="dropdown-item" href="/mgmt/users/list_view.do">사용자 관리</a></li>
@@ -15,35 +15,31 @@
</div>
</div>
<!-- new collection button -->
<div class="pt-new-collection p-1">
<div class="p-1">
<button type="button" class="btn btn-primary btn-sm" id="new_collection"><i class="fa fa-plus"></i>새 컬렉션</button>
<button type="button" class="btn btn-primary btn-sm" id="new_request"><i class="fa fa-plus"></i>새 요청</button>
</div>
<div class="collapse pt-links" id="side_menubar" aria-label="sidebar menu">
<ul class="list-unstyled mb-0 py-3 pt-md-1">
<li th:each="collection, status: *{collections}">
<a href="#" class="d-inline-flex align-items-center rounded">기본</a>
</li>
<div class="collapse pt-links" aria-label="sidebar menu">
<ul class="list-unstyled mb-0 py-3 pt-md-1" id="side_menubar">
</ul>
</div>
<div class="modal fade" id="new_collection_modal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal fade" id="new_collection_modal" tabindex="-1" aria-labelledby="new_collection_modal" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">새로운 컬렉션 추가</h5>
<h5 class="modal-title">새로운 컬렉션 추가</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<input type="text" class="form-control" placeholder="이름을 입력하세요" aria-label="이름을 입력하세요" aria-describedby="button-addon2" name="collection_name"/>
<input type="text" class="form-control" placeholder="이름을 입력하세요" aria-label="이름을 입력하세요" aria-describedby="button-addon2" name="collection_name" required/>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">닫기</button>
<button type="button" class="btn btn-primary">저장</button>
<button type="button" class="btn btn-primary save">저장</button>
</div>
</div>
</div>
</div>
</aside>
<!-- menu script -->
@@ -51,7 +47,8 @@
<script th:inline="javascript">
$(function () {
//when new_collection button clicked show modal popup for entering name
$('#new_collection').click(function () {
$('#new_collection').on('click', function () {
$('#new_collection_modal').find('input').val('');
$('#new_collection_modal').modal('show');
});
});
+190 -16
View File
@@ -25,6 +25,23 @@
<div class="tab-content" id="apiRequestTabContent">
</div>
</div>
<div class="modal fade" id="new_api_request_modal" tabindex="-1" aria-labelledby="new_api_request_modal" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">새로운 API 추가</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<select class="form-select" id="modal_collection"></select>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">닫기</button>
<button type="button" class="btn btn-primary add_new_request">추가</button>
</div>
</div>
</div>
</div>
<div id="apiRequestTemplate" style="display: none;">
<div class="tab-pane fade mt-1">
<div class="d-flex align-items-center">
@@ -208,6 +225,26 @@
</tbody>
</table>
</div>
<ul id="collectionTemplate" style="display: none;">
<li>
<div class="d-flex justify-content-between">
<button class="btn d-inline-flex align-items-center rounded new_collection" data-bs-toggle="collapse"
data-bs-target="" aria-expanded="false" aria-current="false">컬렉션
</button>
<button class="p-1 hover:text-white delete_collection"><i class="fa fa-trash-can"></i></button>
</div>
<div class="collapse">
<ul class="list-unstyled fw-normal pb-1 small">
</ul>
</div>
</li>
</ul>
<ul id="collectionApiTemplate" style="display: none;">
<li class="d-flex justify-content-between">
<a href="#" class="d-inline-flex align-items-center rounded"></a>
<button class="p-1 hover:text-white delete_api_request"><i class="fa fa-trash-can"></i></button>
</li>
</ul>
</section>
</body>
@@ -216,12 +253,14 @@
$(function () {
let apiRequests = [];
let collections = [];
let view = {
currentIdx: 0,
responses: [],
requests: [],
init: function () {
$("#cancel-ajax").on("click", function () {
if (controller.currentAjaxRequest) {
controller.currentAjaxRequest.abort();
@@ -230,12 +269,19 @@
});
$('#new_request').on('click', function () {
controller.addAPIRequest();
$('#new_api_request_modal').modal('show');
});
if (apiRequests.length === 0)
controller.addAPIRequest();
// this.render();
$('#new_api_request_modal').find('.add_new_request').on('click', function () {
controller.addAPIRequest($('#new_api_request_modal').find('select').val());
});
$('#new_collection_modal').find('.save').on('click', function () {
controller.addCollection($('#new_collection_modal').find('input').val());
});
controller.loadCollections();
},
renderParam: function (tabIdx) {
let target = $('#api_request_' + tabIdx);
@@ -397,7 +443,7 @@
target.find('.send').click(function () {
controller.send(idx);
});
var me = this;
let me = this;
require(['vs/editor/editor.main'], function() {
let requestBodyEditor = monaco.editor.create(target.find(".request_body")[0], {
@@ -470,6 +516,38 @@
target.find(".response_status").text("status: " + model.responseModel.status);
target.find(".response_time").text("time: " + model.responseModel.time + "ms");
target.find(".response_size").text("size: " + model.responseModel.size + "bytes");
},
renderCollections: function(){
let select = $('#modal_collection');
let sidemenu = $('#side_menubar');
select.empty();
sidemenu.empty();
for (let i = 0; i < collections.length; i++) {
let template = $('#collectionTemplate').children().clone();
template.find('button.new_collection').text(collections[i].name);
template.find('button').attr('data-bs-target', '#collection_' + i);
template.find('.collapse').attr('id', 'collection_' +i);
for (let j = 0; j < collections[i].apis.length; j++) {
let collectionApiTemplate = $('#collectionApiTemplate').children().clone();
let link = collectionApiTemplate.find('a');
link.text(collections[i].apis[j].name);
link.attr('data-collection', collections[i].id);
link.attr('data-id', collections[i].apis[j].id);
link.on('click', function(){
controller.openApiRequest($(this).attr('data-collection'),$(this).attr('data-id'));
})
template.find('.collapse ul').append(collectionApiTemplate);
}
let option = $('<option>');
option.attr('value', collections[i].id);
option.text(collections[i].name);
select.append(option);
sidemenu.append(template);
}
}
}
@@ -531,6 +609,25 @@
hideLoadingOverlay: function () {
$("#loading-overlay").hide();
},
openApiRequest: function(collectionId, apiId) {
let selectedApi = collections.filter(function (collection) {
return collection.id === collectionId;
})[0].apis.filter(function (api) {
return api.id === apiId;
})[0];
if (selectedApi !== undefined){
let tab = apiRequests.filter(function(api){
return api.id === selectedApi.id;
})[0];
if (tab === undefined){
apiRequests.push(selectedApi);
view.currentIdx = apiRequests.length - 1;
view.renderTabHeader();
view.renderTabContent(view.currentIdx);
}
}
},
send: function (tabIdx) {
let model = apiRequests[tabIdx];
let target = $('#api_request_' + tabIdx);
@@ -541,7 +638,7 @@
if (model.path !== '') {
controller.showLoadingOverlay();
this.currentAjaxRequest = $.ajax({
url: '/api/test.do',
url: '/mgmt/api/test.do',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(model),
@@ -564,15 +661,68 @@
});
}
},
addCollection: function () {
loadCollections: function(){
controller.showLoadingOverlay();
this.currentAjaxRequest = $.ajax({
url: '/mgmt/collections/list.do',
type: 'GET',
contentType: 'application/json',
success: function (data) {
collections = data;
},
error: function (jqXHR, textStatus, errorThrown) {
console.error(textStatus, errorThrown);
controller.hideLoadingOverlay();
},
complete: function () {
view.renderCollections();
controller.hideLoadingOverlay();
}
});
},
removeCollection: function () {
addCollection: function (collection) {
controller.showLoadingOverlay();
this.currentAjaxRequest = $.ajax({
url: '/mgmt/collections/create.do',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({name: collection}),
success: function (data) {
$('#new_collection_modal').modal('hide');
},
error: function (jqXHR, textStatus, errorThrown) {
console.error(textStatus, errorThrown);
controller.hideLoadingOverlay();
},
complete: function () {
controller.loadCollections();
controller.hideLoadingOverlay();
}
});
},
addAPIRequest: function () {
let name = 'New Request ' + (apiRequests.length + 1);
apiRequests.push({
removeCollection: function (id) {
controller.showLoadingOverlay();
this.currentAjaxRequest = $.ajax({
url: '/mgmt/collections/delete.do',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({id: id}),
success: function () {
},
error: function (jqXHR, textStatus, errorThrown) {
console.error(textStatus, errorThrown);
controller.hideLoadingOverlay();
},
complete: function () {
controller.loadCollections();
controller.hideLoadingOverlay();
}
});
},
addAPIRequest: function (collectionId) {
let name = 'New Request';
let newApi = {
id : '',
server: null,
method: 'GET',
name: name,
@@ -587,10 +737,34 @@
headers: [],
body: ''
}
};
this.currentAjaxRequest = $.ajax({
url: '/mgmt/collections/{id}/apis/save.do'.replace('{id}', collectionId),
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(newApi),
success: function (data) {
console.log(data);
newApi.id = data.id;
},
error: function (jqXHR, textStatus, errorThrown) {
console.error(textStatus, errorThrown);
controller.hideLoadingOverlay();
},
complete: function () {
controller.loadCollections();
controller.hideLoadingOverlay();
apiRequests.push(newApi);
view.currentIdx = apiRequests.length - 1;
view.renderTabHeader();
view.renderTabContent(view.currentIdx);
$('#new_api_request_modal').modal('hide');
controller.loadCollections();
}
});
view.currentIdx = apiRequests.length - 1;
view.renderTabHeader();
view.renderTabContent(view.currentIdx);
},
removeAPIRequest: function (idx) {
apiRequests.splice(idx, 1);
@@ -10,7 +10,7 @@
<div class="container">
<div class="card mt-1">
<div class="card-header">
<h1>API 목록</h1>
<h1>응답 API 목록</h1>
</div>
<div class="card-body">
<div class="d-flex justify-content-between">