요청 관리 -> variable 적용

This commit is contained in:
현성필
2023-09-11 17:16:18 +09:00
parent 23062059c0
commit c68be7c948
15 changed files with 149 additions and 234 deletions
@@ -39,12 +39,17 @@ public class MockApiController {
public ResponseEntity<String> handleRequest(HttpServletRequest httpRequest) throws IOException {
MockRequest request = new MockRequest(httpRequest);
AntPathMatcher pathMatcher = new AntPathMatcher();
logger.debug("===========================");
logger.debug("Request: " + request.getMethod() + " " + request.getUri());
logger.debug("Headers:" + request.getHeaders().toString());
logger.debug("Params: " + request.getParams().toString());
logger.debug("Body: ");
logger.debug(request.getBody());
logger.debug("===========================");
for (MockRoute mockRoute : mockRouteService.getAllRoutes()) {
if (isRouteMatch(mockRoute, request)) {
logger.debug("===========================");
logger.debug(request.getBody());
logger.debug("===========================");
Map<String, String> pathVariables = pathMatcher.extractUriTemplateVariables(mockRoute.getPathPattern(), request.getUri());
MockResponse matchingResponse = findMatchingResponse(mockRoute, request, pathVariables);
if (matchingResponse != null) {
@@ -12,6 +12,7 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.concurrent.ExecutionException;
@@ -29,7 +30,7 @@ public class ApiClientController {
@PostMapping("/mgmt/api/test.do")
@ResponseBody
public ApiResponse handleApiRequest(@RequestBody ApiRequestDTO request) throws InterruptedException, ExecutionException {
public ApiResponse handleApiRequest(@RequestBody ApiRequestDTO request) throws InterruptedException, ExecutionException, InvocationTargetException, IllegalAccessException, InstantiationException, NoSuchMethodException {
return nettyApiClient.handleRequest(request).get();
}
@@ -17,6 +17,9 @@ public class ApiRequestDTO {
private List<ApiRequestKeyValueDTO> headers = new ArrayList<>();
private List<ApiRequestKeyValueDTO> queryParams = new ArrayList<>();
private List<ApiRequestKeyValueDTO> variables = new ArrayList<>();
private String requestBody;
public String getId() {
@@ -75,6 +78,14 @@ public class ApiRequestDTO {
this.queryParams = queryParams;
}
public List<ApiRequestKeyValueDTO> getVariables() {
return variables;
}
public void setVariables(List<ApiRequestKeyValueDTO> variables) {
this.variables = variables;
}
public String getRequestBody() {
return requestBody;
}
@@ -34,6 +34,9 @@ public class ApiRequestInfo {
@Lob
private String queryParams;
@Lob
private String variables;
@Lob
private String requestBody;
@@ -101,6 +104,14 @@ public class ApiRequestInfo {
this.queryParams = queryParams;
}
public String getVariables() {
return variables;
}
public void setVariables(String variables) {
this.variables = variables;
}
public String getRequestBody() {
return requestBody;
}
@@ -67,6 +67,7 @@ public class ApiRequestMgmtService {
existing.setPath(api.getPath());
existing.setHeaders(api.getHeaders());
existing.setQueryParams(api.getQueryParams());
existing.setVariables(api.getVariables());
existing.setServer(api.getServer());
existing.setRequestBody(api.getRequestBody());
apiRequestRepository.save(existing);
@@ -20,11 +20,13 @@ import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.util.CharsetUtil;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.net.ssl.SSLException;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Paths;
import java.util.concurrent.CompletableFuture;
@@ -34,8 +36,8 @@ public class NettyApiClient {
@Autowired
ServerRepository serverRepository;
public CompletableFuture<ApiResponse> handleRequest(ApiRequestDTO apiRequest) throws InterruptedException {
public CompletableFuture<ApiResponse> handleRequest(ApiRequestDTO originalRequest) throws InterruptedException, InvocationTargetException, IllegalAccessException, InstantiationException, NoSuchMethodException {
ApiRequestDTO apiRequest = replacePlaceholdersWithVariables(originalRequest);
Server server = serverRepository.findById(apiRequest.getServer()).orElseThrow(() -> new ServerNotFoundException("Server not found"));
String host = server.getHostname();
int port = server.getPort();
@@ -103,5 +105,35 @@ public class NettyApiClient {
return responseFuture;
}
private ApiRequestDTO replacePlaceholdersWithVariables(ApiRequestDTO original) throws InvocationTargetException, IllegalAccessException, InstantiationException, NoSuchMethodException {
ApiRequestDTO request = (ApiRequestDTO) BeanUtils.cloneBean(original);
if (request.getVariables() != null) {
for (ApiRequestKeyValueDTO variable : request.getVariables()) {
if (variable.isEnabled()) {
String placeholder = "{{" + variable.getKey() + "}}";
// Replace in requestBody
request.setPath(request.getPath().replace(placeholder, variable.getValue()));
if (request.getRequestBody() != null) {
request.setRequestBody(request.getRequestBody().replace(placeholder, variable.getValue()));
}
// Replace in headers
if (request.getHeaders() != null) {
for (ApiRequestKeyValueDTO header : request.getHeaders()) {
header.setKey(header.getKey().replace(placeholder, variable.getValue()));
header.setValue(header.getValue().replace(placeholder, variable.getValue()));
}
}
// Replace in queryParams
if (request.getQueryParams() != null) {
for (ApiRequestKeyValueDTO queryParam : request.getQueryParams()) {
queryParam.setKey(queryParam.getKey().replace(placeholder, variable.getValue()));
queryParam.setValue(queryParam.getValue().replace(placeholder, variable.getValue()));
}
}
}
}
}
return request;
}
}
-4
View File
@@ -682,8 +682,6 @@ a.btn.btn-primary {
@media (min-width: 768px) {
.pt-links {
position: -webkit-sticky;
position: sticky;
top: 5rem;
display: block !important;
height: calc(100vh - 7rem);
@@ -809,8 +807,6 @@ a.btn.btn-primary {
@media (min-width: 992px) {
.pt-toc {
position: -webkit-sticky;
position: sticky;
top: 5rem;
right: 0;
z-index: 2;
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -115,6 +115,12 @@
aria-selected="false">Header
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link btn_variables_tab" data-bs-toggle="tab"
type="button" role="tab" aria-controls="variables"
aria-selected="false">Variables
</button>
</li>
</ul>
<div class="tab-content border-bottom" style="min-height: 300px;">
<div class="tab-pane fade show request_param_tab" role="tabpanel"
@@ -126,8 +132,7 @@
<th scope="col">이름</th>
<th></th>
<th>
<button type="button" class="btn btn-secondary btn-sm btn_add_query">추가
</button>
<button type="button" class="btn btn-secondary btn-sm btn_add_query">추가</button>
</th>
</tr>
</thead>
@@ -144,8 +149,7 @@
<th scope="col">이름</th>
<th scope="col"></th>
<th scope="col">
<button type="button" class="btn btn-secondary btn-sm btn_add_header">추가
</button>
<button type="button" class="btn btn-secondary btn-sm btn_add_header">추가</button>
</th>
</tr>
</thead>
@@ -153,6 +157,24 @@
</tbody>
</table>
</div>
<div class="tab-pane fade variables_tab" role="tabpanel"
aria-labelledby="variables-tab">
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col"></th>
<th scope="col"></th>
<th scope="col">
<button type="button" class="btn btn-secondary btn-sm btn_add_variables">추가
</button>
</th>
</tr>
</thead>
<tbody class="variables">
</tbody>
</table>
</div>
<div class="tab-pane fade request_body_tab" role="tabpanel"
aria-labelledby="requestBody-tab">
<select class="form-select mt-1 request_body_type" name="request_type">
@@ -171,17 +193,10 @@
</div>
<ul class="nav nav-tabs mt-1" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" data-bs-toggle="tab"
data-bs-target="#responseBodyTab" type="button" role="tab" aria-controls="responseBody"
aria-selected="false">Body
</button>
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#responseBodyTab" type="button" role="tab" aria-controls="responseBody" aria-selected="false">Body</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" data-bs-toggle="tab"
data-bs-target="#responseHeaderTab" type="button" role="tab"
aria-controls="responseHeader"
aria-selected="false">Header
</button>
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#responseHeaderTab" type="button" role="tab" aria-controls="responseHeader" aria-selected="false">Header</button>
</li>
</ul>
<div class="tab-content border-bottom" style="min-height: 250px;">
@@ -365,6 +380,32 @@
target.find('.path').val(model.path);
},
renderVariables: function (tabIdx) {
let target = $('#api_request_' + tabIdx);
target.find('.variables').empty();
let model = apiRequests[tabIdx];
for (let i = 0; i < model.variables.length; i++) {
let template = $('#inputTemplate').children().find('tr').clone();
if (model.variables[i].enabled) {
template.find('input[name="enabled"]').prop("checked", model.variables[i].enabled);
}
template.find('input[name="key"]').val(model.variables[i].key);
template.find('input[name="value"]').val(model.variables[i].value);
template.find('button').attr("data-id", i);
template.find('input[name="enabled"]').on('change', function () {
model.variables[i].enabled = $(this).prop('checked');
});
template.find('input[name="key"]').on('input', function () {
model.variables[i].key = $(this).val();
});
template.find('input[name="value"]').on('input', function () {
model.variables[i].value = $(this).val();
});
target.find('.variables').append(template);
}
},
renderHeaders: function (tabIdx) {
let target = $('#api_request_' + tabIdx);
target.find('.request_headers').empty();
@@ -437,9 +478,11 @@
template.attr('class', 'tab-pane fade mt-1' + (idx === this.currentIdx ? ' show active' : ''));
template.find('.btn_request_param_tab').attr('data-bs-target', "#requestParamTab_" + idx);
template.find('.btn_request_header_tab').attr('data-bs-target', "#requestHeaderTab_" + idx);
template.find('.btn_variables_tab').attr('data-bs-target', "#variablesTab_" + idx);
template.find('.btn_request_body_tab').attr('data-bs-target', "#requestBodyTab_" + idx);
template.find('.request_header_tab').attr('id', "requestHeaderTab_" + idx);
template.find('.variables_tab').attr('id', "variablesTab_" + idx);
template.find('.request_param_tab').attr('id', "requestParamTab_" + idx);
template.find('.request_body_tab').attr('id', "requestBodyTab_" + idx);
template.find('.api_name').text(model.name);
@@ -455,6 +498,10 @@
controller.addQueryParam(idx, '', '');
});
target.find('.btn_add_variables').click(function () {
controller.addVariable(idx, '', '');
});
target.find('input[name="basePath"]').val(target.find('select[name="server"] option:selected').data('path'));
target.find('select[name="server"]').val(model.server);
target.find('select[name="server"]').on('change', function () {
@@ -479,6 +526,12 @@
}
});
target.find('.variables').on('click', 'button', function () {
if ($(this).hasClass('btn_remove')) {
controller.removeVariable(idx, $(this).data("id"));
}
});
target.find('.path').on('input', function () {
model.path = $(this).val();
model.queryParams = [];
@@ -536,6 +589,7 @@
target.find('.method').val(model.method);
this.renderParam(idx);
this.renderHeaders(idx);
this.renderVariables(idx);
this.openTab(view.currentIdx);
},
render: function () {
@@ -681,6 +735,20 @@
});
view.renderParam(tabIdx);
},
addVariable: function (tabIdx, key, value) {
let model = apiRequests[tabIdx];
model.variables.push({
key: key,
value: value
});
view.renderVariables(tabIdx);
},
removeVariable: function (tabIdx, idx) {
let model = apiRequests[tabIdx];
model.variables.splice(idx, 1);
this.buildPath(tabIdx);
view.renderVariables(tabIdx);
},
buildPath: function (tabIdx) {
let model = apiRequests[tabIdx];
let queryString = model.path.split('?')[0];
@@ -861,6 +929,7 @@
path: '',
headers: [],
queryParams: [],
variables: [],
requestBody: '',
collectionId : collectionId,
responseModel: {