컬렉션, 요청 삭제

This commit is contained in:
현성필
2023-05-15 17:24:57 +09:00
parent 78277d271d
commit 239a562d6f
5 changed files with 167 additions and 56 deletions
@@ -30,6 +30,11 @@ public class ApiRequestMgmtService {
}
public void deleteApiCollection(String id) {
ApiCollection collection = apiCollectionRepository.findById(id).orElseThrow(() -> new NotFoundException("api collection[" + id + "] not found"));
if (collection.getApis().size()>0){
throw new RuntimeException("api collection[" + id + "] is not empty");
}
apiCollectionRepository.deleteById(id);
}
@@ -21,4 +21,13 @@ public class CustomGlobalExceptionHandler extends ResponseEntityExceptionHandler
return new ResponseEntity<>(errors, HttpStatus.NOT_FOUND);
}
@ExceptionHandler(RuntimeException.class)
public ResponseEntity<CustomErrorResponse> customHandleRuntime(Exception ex, WebRequest request) {
CustomErrorResponse errors = new CustomErrorResponse();
errors.setTimestamp(LocalDateTime.now());
errors.setError(ex.getMessage());
errors.setStatus(HttpStatus.BAD_REQUEST.value());
return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST);
}
}
+16 -1
View File
@@ -1547,4 +1547,19 @@ figure div span {
.prod {
background-color : #49cc90 !important;
color : white !important;
}
}
.reset-button {
/* Reset button styles */
background: none;
border: none;
color: inherit;
cursor: pointer;
padding: 0;
font: inherit;
outline: inherit;
/* Style the icon */
display: inline-block;
margin-right: 5px; /* Adjust this value as per your requirement */
}
@@ -40,6 +40,23 @@
</div>
</div>
</div>
<div class="modal fade" id="confirm_delete_modal" tabindex="-1" aria-labelledby="confirm_delete_modal" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">컬렉션 삭제</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div>삭제 하시겠습니까?</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">닫기</button>
<button type="button" class="btn btn-primary confirm_delete">삭제</button>
</div>
</div>
</div>
</div>
</aside>
<!-- menu script -->
+120 -55
View File
@@ -5,7 +5,7 @@
<section layout:fragment="contentFragment">
<script>
require.config({ paths: { 'vs': '/plugins/vs' }});
require.config({paths: {'vs': '/plugins/vs'}});
// require(['vs/editor/editor.main'], function(){});
</script>
<div id="loading-overlay" style="display: none;">
@@ -25,7 +25,8 @@
<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 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">
@@ -61,7 +62,8 @@
</div>
<div class="form-floating col-md-1 me-1">
<select class="form-select server" name="server">
<option th:each="server : ${servers}" th:value="${server.id}" th:text="${server.name}" th:data-path="${server.basePath}"></option>
<option th:each="server : ${servers}" th:value="${server.id}" th:text="${server.name}"
th:data-path="${server.basePath}"></option>
</select>
<label class="form-label must">Server</label>
</div>
@@ -71,7 +73,7 @@
<label class="must">base path</label>
</div>
</div>
<div class="input-group col-md">
<div class="input-group col-md me-1">
<div class="form-floating col-md">
<input type="text" name="path" class="form-control path" placeholder="호출할 경로 (/부터 입력)"
required>
@@ -79,6 +81,7 @@
</div>
<button class="btn btn-primary send">Send</button>
</div>
<button class="btn btn-primary save">저장</button>
</div>
<ul class="nav nav-tabs mt-1" role="tablist">
<li class="nav-item" role="presentation">
@@ -228,10 +231,14 @@
<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"
<button class="btn d-inline-flex align-items-center rounded new_collection normal"
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 class="d-flex justify-content-between">
<button class="p-1 reset-button edit_collection"><i class="fa fa-sharp fa-file-edit"></i></button>
<button class="p-1 reset-button delete_collection"><i class="fa fa-sharp fa-trash-can"></i></button>
</div>
</div>
<div class="collapse">
<ul class="list-unstyled fw-normal pb-1 small">
@@ -242,7 +249,7 @@
<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>
<button class="p-1 reset-button delete_api_request"><i class="fa fa-trash-can"></i></button>
</li>
</ul>
</section>
@@ -269,18 +276,25 @@
});
$('#new_request').on('click', function () {
if (collections.length == 0) {
alert('새로운 컬렉션을 추가해 주세요.');
return;
}
$('#new_api_request_modal').modal('show');
});
$('#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());
});
$('#confirm_delete_modal').find('button.confirm_delete').on('click', function () {
controller.removeCollection($(this).data('id'));
});
controller.loadCollections();
},
renderParam: function (tabIdx) {
@@ -293,8 +307,8 @@
template.find('input[name="key"]').val(model.queryParams[i].key);
template.find('input[name="value"]').val(model.queryParams[i].value);
if(model.queryParams[i].enabled){
template.find('input[name="enabled"]').prop("checked",model.queryParams[i].enabled);
if (model.queryParams[i].enabled) {
template.find('input[name="enabled"]').prop("checked", model.queryParams[i].enabled);
}
template.find('button').attr("data-id", i);
@@ -325,8 +339,8 @@
let model = apiRequests[tabIdx];
for (let i = 0; i < model.headers.length; i++) {
let template = $('#inputTemplate').children().find('tr').clone();
if(model.headers[i].enabled){
template.find('input[name="enabled"]').prop("checked",model.headers[i].enabled);
if (model.headers[i].enabled) {
template.find('input[name="enabled"]').prop("checked", model.headers[i].enabled);
}
template.find('input[name="key"]').val(model.headers[i].key);
template.find('input[name="value"]').val(model.headers[i].value);
@@ -345,14 +359,14 @@
target.find('.request_headers').append(template);
}
},
renderTabHeader : function(){
renderTabHeader: function () {
let apiRequestTabTitle = $('#apiRequestTabTitle');
apiRequestTabTitle.empty();
for (let idx = 0; idx < apiRequests.length; idx++) {
let $span = $('<span>')
.html(' <i class="fa fa-light fa-close" data-id="'+ idx +'"></i>');
.html(' <i class="fa fa-light fa-close" data-id="' + idx + '"></i>');
$span.on('click', function(event) {
$span.on('click', function (event) {
event.stopPropagation();
controller.removeAPIRequest(idx);
});
@@ -374,7 +388,7 @@
apiRequestTabTitle.append(li);
}
},
renderTabContent: function(idx){
renderTabContent: function (idx) {
let apiRequestTabContent = $('#apiRequestTabContent');
let model = apiRequests[idx];
@@ -384,13 +398,13 @@
template.attr('id', 'api_request_' + idx);
template.attr("data-id", idx);
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_request_body_tab').attr('data-bs-target', "#requestBodyTab_"+ idx);
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_request_body_tab').attr('data-bs-target', "#requestBodyTab_" + idx);
template.find('.request_param_tab').attr('id', "requestParamTab_"+ idx);
template.find('.request_header_tab').attr('id', "requestHeaderTab_"+ idx);
template.find('.request_body_tab').attr('id', "requestBodyTab_"+ idx);
template.find('.request_param_tab').attr('id', "requestParamTab_" + idx);
template.find('.request_header_tab').attr('id', "requestHeaderTab_" + idx);
template.find('.request_body_tab').attr('id', "requestBodyTab_" + idx);
apiRequestTabContent.append(template);
let target = $('#api_request_' + idx);
@@ -435,7 +449,7 @@
let queryParamArray = queryString.split('&');
for (let i = 0; i < queryParamArray.length; i++) {
let queryParam = queryParamArray[i].split('=');
controller.addQueryParam(idx, queryParam[0], queryParam[1]===undefined?'' : queryParam[1]);
controller.addQueryParam(idx, queryParam[0], queryParam[1] === undefined ? '' : queryParam[1]);
}
}
});
@@ -445,7 +459,7 @@
});
let me = this;
require(['vs/editor/editor.main'], function() {
require(['vs/editor/editor.main'], function () {
let requestBodyEditor = monaco.editor.create(target.find(".request_body")[0], {
value: '',
language: 'json',
@@ -503,8 +517,13 @@
target.find('.response_headers').empty();
target.find(".response_status").empty();
this.responses[tabIdx].setValue(model.responseModel.body);
try {
const parsedJson = JSON.parse(model.responseModel.body);
const formattedJson = JSON.stringify(parsedJson, null, 2);
this.responses[tabIdx].setValue(formattedJson);
}catch(e){
this.responses[tabIdx].setValue(model.responseModel.body);
}
for (let i = 0; i < model.responseModel.headers.length; i++) {
let template = $('#responseHeaderTemplate').children().find('tr').clone();
template.find('input[name="key"]').val(model.responseModel.headers[i].key);
@@ -512,31 +531,43 @@
target.find('.response_headers').append(template);
}
//show response status
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(){
renderCollections: function () {
let select = $('#modal_collection');
let sidemenu = $('#side_menubar');
let sidemenu = $('#side_menubar');
select.empty();
sidemenu.empty();
for (let i = 0; i < collections.length; i++) {
let template = $('#collectionTemplate').children().clone();
template.attr('id', 'collection_' + i);
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);
template.find('button.new_collection').attr('data-bs-target', '#btn_collection_' + i);
template.find('button.delete_collection').attr('data-id', collections[i].id);
template.find('button.delete_collection').on('click', function () {
$('#confirm_delete_modal').find('.confirm_delete').attr('data-id', $(this).attr('data-id'));
$('#confirm_delete_modal').modal('show');
});
template.find('.collapse').attr('id', 'btn_collection_' + i);
for (let j = 0; j < collections[i].apis.length; j++) {
let collectionApiTemplate = $('#collectionApiTemplate').children().clone();
let link = collectionApiTemplate.find('a');
let btn = collectionApiTemplate.find('button.delete_api_request');
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'));
link.on('click', function () {
controller.openApiRequest($(this).attr('data-collection'), $(this).attr('data-id'));
})
btn.attr('data-collection', collections[i].id);
btn.attr('data-id', collections[i].apis[j].id);
btn.on('click', function () {
controller.deleteApiRequest($(this).attr('data-collection'), $(this).attr('data-id'));
})
template.find('.collapse ul').append(collectionApiTemplate);
}
@@ -586,7 +617,7 @@
queryString += '?';
}
for (let i = 0; i < model.queryParams.length; i++) {
if(!model.queryParams[i].enabled){
if (!model.queryParams[i].enabled) {
continue;
}
queryString += model.queryParams[i].key + '=' + model.queryParams[i].value;
@@ -609,18 +640,28 @@
hideLoadingOverlay: function () {
$("#loading-overlay").hide();
},
openApiRequest: function(collectionId, apiId) {
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){
if (selectedApi !== undefined) {
let tab = apiRequests.filter(function (api) {
return api.id === selectedApi.id;
})[0];
if (tab === undefined){
if (selectedApi.responseModel === undefined) {
selectedApi.responseModel = {
status: 0,
time: 0,
size: 0,
headers: [],
body: ''
};
}
if (tab === undefined) {
apiRequests.push(selectedApi);
view.currentIdx = apiRequests.length - 1;
view.renderTabHeader();
@@ -628,6 +669,32 @@
}
}
},
deleteApiRequest: function (collectionId, apiId) {
this.currentAjaxRequest = $.ajax({
url: '/mgmt/collections/{id}/apis/delete.do'.replace('{id}', collectionId),
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({id: apiId}),
success: function (data) {
},
error: function (response, textStatus, errorThrown) {
controller.hideLoadingOverlay();
},
complete: function () {
controller.hideLoadingOverlay();
//get the api with apiIdx index from apiRequests
let apiIdx = apiRequests.findIndex(function (api) {
return api.id === apiId;
});
apiRequests.splice(apiIdx, 1);
view.render();
$('#apiRequestTabTitle li:eq(0) button').tab('show');
controller.loadCollections();
}
});
},
send: function (tabIdx) {
let model = apiRequests[tabIdx];
let target = $('#api_request_' + tabIdx);
@@ -648,8 +715,7 @@
model.responseModel.headers = data.headers;
model.responseModel.size = data.size;
},
error: function (jqXHR, textStatus, errorThrown) {
console.error(textStatus, errorThrown);
error: function (response, textStatus, errorThrown) {
controller.hideLoadingOverlay();
},
complete: function () {
@@ -661,7 +727,7 @@
});
}
},
loadCollections: function(){
loadCollections: function () {
controller.showLoadingOverlay();
this.currentAjaxRequest = $.ajax({
url: '/mgmt/collections/list.do',
@@ -670,8 +736,7 @@
success: function (data) {
collections = data;
},
error: function (jqXHR, textStatus, errorThrown) {
console.error(textStatus, errorThrown);
error: function (response, textStatus, errorThrown) {
controller.hideLoadingOverlay();
},
complete: function () {
@@ -688,15 +753,15 @@
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);
error: function (response, textStatus, errorThrown) {
controller.hideLoadingOverlay();
},
complete: function () {
controller.loadCollections();
controller.hideLoadingOverlay();
$('#new_collection_modal').modal('hide');
}
});
},
@@ -709,20 +774,23 @@
data: JSON.stringify({id: id}),
success: function () {
},
error: function (jqXHR, textStatus, errorThrown) {
console.error(textStatus, errorThrown);
error: function (response, textStatus, errorThrown) {
let error = response.responseJSON;
controller.hideLoadingOverlay();
alert(error.error);
},
complete: function () {
controller.loadCollections();
controller.hideLoadingOverlay();
$('#confirm_delete_modal').modal('hide');
}
});
},
addAPIRequest: function (collectionId) {
let name = 'New Request';
let newApi = {
id : '',
id: '',
server: null,
method: 'GET',
name: name,
@@ -745,24 +813,21 @@
contentType: 'application/json',
data: JSON.stringify(newApi),
success: function (data) {
console.log(data);
newApi.id = data.id;
newApi.id = data;
},
error: function (jqXHR, textStatus, errorThrown) {
console.error(textStatus, errorThrown);
error: function (response, textStatus, errorThrown) {
controller.hideLoadingOverlay();
},
complete: function () {
controller.loadCollections();
controller.hideLoadingOverlay();
apiRequests.push(newApi);
view.currentIdx = apiRequests.length - 1;
view.renderTabHeader();
view.renderTabContent(view.currentIdx);
controller.hideLoadingOverlay();
$('#new_api_request_modal').modal('hide');
controller.loadCollections();
}
});
},