시나리오 관리 추가

This commit is contained in:
현성필
2024-01-07 17:34:19 +09:00
parent e65a55693f
commit b4c43a2138
31 changed files with 25435 additions and 203 deletions
+133 -172
View File
@@ -3,7 +3,6 @@ require.config({paths: {'vs': '/plugins/vs'}});
class APITester {
constructor() {
this.servers = [];
this.apiTabTitleTarget = '#apiRequestTabTitle';
@@ -16,9 +15,9 @@ class APITester {
this.responseEditors = []; //API response body editor
this.preRequestEditors = [];
this.postRequestEditors = [];
window.globals = {};
this.globalVariables = window.globals;
this.variables = {};
this.apiClient = new APIClient();
}
renderServers(selectedServer) {
@@ -304,7 +303,7 @@ class APITester {
collectionTemplate(collection, index) {
return `
<li id="collection_${index}" class="collection" data-index="${index}">
<li id="collection_${index}" class="collection api_scenario_item" data-index="${index}">
<div class="d-flex justify-content-between">
<button class="btn d-inline-flex align-items-center rounded normal"
data-bs-toggle="collapse"
@@ -326,8 +325,8 @@ class APITester {
collectionApiTemplate(api, apiIndex, collectionId) {
return `
<li class="d-flex justify-content-between " data-collection="${collectionId}" data-id="${api.id}" data-index="${apiIndex}">
<a href="#" class="d-inline-flex align-items-center rounded open_api_request">${api.name}</a>
<li class="d-flex justify-content-between api_request_sortable " draggable="true" data-collection="${collectionId}" data-id="${api.id}" data-index="${apiIndex}" data-node-type="api" data-name="${api.name}">
<a href="#" class="d-inline-flex align-items-center rounded open_api_request" data-node-type="api">${api.name}</a>
<div class="d-flex justify-content-between">
<button class="p-1 reset-button edit_api_request"><i class="fa fa-sharp fa-file-edit"></i></button>
<button class="p-1 reset-button delete_api_request"><i class="fa fa-trash-can"></i></button>
@@ -339,7 +338,7 @@ class APITester {
init(servers) {
if (servers) {
this.servers = servers;
if (this.servers.length === 0){
if (this.servers.length === 0) {
$('.toast-body').text('서버관리에서 서버를 등록해주세요.');
$('.toast').toast('show');
}
@@ -357,7 +356,7 @@ class APITester {
{selector: '.btn_add_variables', action: this.addVariable.bind(this)},
{selector: '.btn_remove_variable', action: this.removeVariable.bind(this)},
{selector: '.send', action: this.sendApiRequest.bind(this)},
{selector: '.send', action: this.handleSendAPIRequest.bind(this)},
{selector: '.save', action: this.saveAPIRequest.bind(this)},
{selector: '.save_collection', action: this.addCollection.bind(this)},
{selector: '#cancel-ajax', action: this.cancelAjaxRequest.bind(this)},
@@ -379,16 +378,57 @@ class APITester {
$(document).on('click', event.selector, event.action);
}
$(document).on('input', '.query_params input', this.handleUIUpdate.bind(this));
$(document).on('input', '.query_params input', this.buildPath.bind(this));
$(document).on('input', '.query_params input', this.handleUIUpdate.bind(this));
$(document).on('input', '.query_params input', this.buildPath.bind(this));
$(document).on('change', '.query_params input', this.handleUIUpdate.bind(this));
$(document).on('change', '.query_params input', this.buildPath.bind(this));
$(document).on('input', '.path', this.parseParam.bind(this));
$(document).on('input', '.path', this.parseParam.bind(this));
$(document).on('change', '.request_headers input', this.handleUIUpdate.bind(this));
$(document).on('change', '.variables input', this.handleUIUpdate.bind(this));
$(document).on('change', '.api_request_info select, .api_request_info input', this.handleUIUpdate.bind(this));
$(document).on('change', '.api_request_info select', this.handleUpdateServer.bind(this));
window.addEventListener('resize', this.resizeEditor.bind(this));
}
resizeEditor() {
//FIXME: 창 크기 변경시 자동으로 editor 크기가 조정되지 않아서, 0으로 강제 변경후 다시 layout() 호출
for (let i = 0; i < this.requestEditors.length; i++) {
this.requestEditors[i].layout({
width: 0
});
}
for (let i = 0; i < this.responseEditors.length; i++) {
this.responseEditors[i].layout({
width: 0
});
}
for (let i = 0; i < this.preRequestEditors.length; i++) {
this.preRequestEditors[i].layout({
width: 0
});
}
for (let i = 0; i < this.postRequestEditors.length; i++) {
this.postRequestEditors[i].layout({
width: 0
});
}
for (let i = 0; i < this.requestEditors.length; i++) {
this.requestEditors[i].layout({});
}
for (let i = 0; i < this.responseEditors.length; i++) {
this.responseEditors[i].layout({});
}
for (let i = 0; i < this.preRequestEditors.length; i++) {
this.preRequestEditors[i].layout({});
}
for (let i = 0; i < this.postRequestEditors.length; i++) {
this.postRequestEditors[i].layout({});
}
}
@@ -547,7 +587,7 @@ class APITester {
this.openTab(model.id);
let target = $('#api_request_' + model.id);
require(['vs/editor/editor.main'], () => {
require(['vs/editor/editor.main'], () => {
let requestBodyEditor = monaco.editor.create(target.find(".request_body")[0], {
value: model.requestBody,
language: 'json',
@@ -555,15 +595,15 @@ class APITester {
automaticLayout: true
});
me.requestEditors.push(requestBodyEditor);
target.find(".request_body")[0].addEventListener('shown.bs.tab', () => {
target.find(".request_body")[0].addEventListener('shown.bs.tab', () => {
me.requestEditors[index].layout();
});
me.requestEditors[index].onDidChangeModelContent( (e) => {
me.requestEditors[index].onDidChangeModelContent((e) => {
model.requestBody = requestBodyEditor.getValue();
});
target.find('.request_body_type').on('change', () => {
target.find('.request_body_type').on('change', () => {
let model = requestBodyEditor.getModel();
monaco.editor.setModelLanguage(model, $(this).val());
});
@@ -583,11 +623,11 @@ class APITester {
automaticLayout: true
});
me.preRequestEditors.push(preRequestEditor);
target.find(".pre-request")[0].addEventListener('shown.bs.tab', () => {
target.find(".pre-request")[0].addEventListener('shown.bs.tab', () => {
me.preRequestEditors[index].layout();
});
me.preRequestEditors[index].onDidChangeModelContent( (e) => {
me.preRequestEditors[index].onDidChangeModelContent((e) => {
model.preRequestScript = preRequestEditor.getValue();
});
@@ -598,37 +638,32 @@ class APITester {
automaticLayout: true
});
me.postRequestEditors.push(postRequestEditor);
target.find(".post-request")[0].addEventListener('shown.bs.tab', () => {
target.find(".post-request")[0].addEventListener('shown.bs.tab', () => {
me.postRequestEditors[index].layout();
});
me.postRequestEditors[index].onDidChangeModelContent( (e) => {
me.postRequestEditors[index].onDidChangeModelContent((e) => {
model.postRequestScript = postRequestEditor.getValue();
});
});
}
renderResponse(tabIndex) {
//console.log('renderResponse: ' + tabIndex);
console.log('renderResponse: ' + tabIndex);
let model = this.apiRequests[tabIndex];
let target = $('#api_request_' + model.id);
target.find('.response_headers').empty();
target.find(".response_status").empty();
//console.log(model.responseModel);
try {
const parsedJson = JSON.parse(model.responseModel.body);
const formattedJson = JSON.stringify(parsedJson, null, 2);
this.responseEditors[tabIndex].setValue(formattedJson);
} catch(e) {
} catch (e) {
this.responseEditors[tabIndex].setValue(model.responseModel.body);
}
if(model.responseModel && model.responseModel.headers) {
if (model.responseModel && model.responseModel.headers) {
for (let i = 0; i < model.responseModel.headers.length; i++) {
target.find('.response_headers').append(this.responseHeaderTemplate(model.responseModel.headers[i], i));
}
@@ -640,6 +675,14 @@ class APITester {
}
customHelper(event) {
// Create and return the custom helper element
let name = $(event.target).closest('.api_request_sortable').find('.open_api_request').text().trim();
let apiId = $(event.target).closest('.api_request_sortable').data('id');
return `<div style="display: flex; flex-direction: column; flex: 1; border: 2px dashed #333; padding: 10px;" data-id="${apiId}">
<div>${name}</div>
</div>`;
}
renderCollections() {
let select = $('#modal_collection');
@@ -654,6 +697,25 @@ class APITester {
option.text(this.collections[i].name);
select.append(option);
}
$("li .api_request_sortable").draggable({
helper: this.customHelper,
start: function (event, ui) {
// You can add any additional data or styles you want when dragging starts
$(this).addClass('dragging');
const apiDetails = {
name: 'API 이름',
method: 'POST',
url: 'http://localhost:8080/api/test'
};
},
stop: function (event, ui) {
// Clean up, e.g., remove styles or data
$(this).removeClass('dragging');
}
});
}
renderCollection(collection, index) {
@@ -661,7 +723,6 @@ class APITester {
}
openTab(id) {
//console.log('openTab: ' + id);
try {
$(`#api_request_tab_${id}`).tab('show'); // API request 탭 활성화
} catch (e) {
@@ -763,6 +824,7 @@ class APITester {
target.find('.variables').empty();
target.find('.variables').append(this.renderVariables(model.variables));
}
parseParam(event) {
const tabIndex = $('#apiRequestTabContent').children().index($(event.currentTarget).closest('.api_request'));
let model = this.apiRequests[tabIndex];
@@ -785,6 +847,7 @@ class APITester {
this.renderQueryParamsView(tabIndex);
}
}
buildPath(event) {
const tabIndex = $('#apiRequestTabContent').children().index($(event.currentTarget).closest('.api_request'));
let model = this.apiRequests[tabIndex];
@@ -813,14 +876,13 @@ class APITester {
$("#loading-overlay").hide();
}
sendApiRequest(event) {
handleSendAPIRequest(event) {
const tabIndex = $('#apiRequestTabContent').children().index($(event.currentTarget).closest('.api_request'));
console.log(tabIndex);
const me = this;
let model = this.apiRequests[tabIndex];
if (!model.server){
if (!model.server) {
$('.toast-body').text('서버를 등록해주세요.');
$('.toast').toast('show');
return;
@@ -829,53 +891,42 @@ class APITester {
let target = $('#api_request_' + model.id);
target.find('.response_headers').empty();
this.responseEditors[tabIndex].setValue('');
let startTime = new Date().getTime();
this.showLoadingOverlay();
model.responseModel = {};
this.showLoadingOverlay();
let processedRequest = this.executePreRequestScript(model, tabIndex,() => {
processedRequest = this.replacePlaceholdersWithVariables(processedRequest, tabIndex);
this.currentAjaxRequest = $.ajax({
url: '/mgmt/api/test.do',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(processedRequest),
success: (data) => {
model.responseModel.status = data.status;
model.responseModel.body = data.body;
model.responseModel.headers = data.headers;
model.responseModel.size = data.size;
let startTime = new Date().getTime();
this.currentAjaxRequest = this.apiClient.sendAPIRequest(model)
.then(response => {
if (response) {
model.responseModel = response;
let endTime = new Date().getTime();
model.responseModel.time = endTime - startTime;
} else {
model.responseModel = {
status: 0,
time: 0,
size: 0,
headers: [],
body: ''
};
}
let response = {};
response.body = data.body;
response.status = data.status;
response.headers = {};
response.size = data.size;
_.forEach(data.headers, (header) => {
response.headers[header.key] = header.value;
});
this.executePostRequestScript(model.postRequestScript, tabIndex, response, ()=> {
let endTime = new Date().getTime();
model.responseModel.time = endTime - startTime;
me.renderResponse(tabIndex);
me.hideLoadingOverlay();
});
},
error: (response, textStatus, errorThrown) => {
me.hideLoadingOverlay();
$('.toast-body').text(response.responseJSON.error);
this.renderResponse(tabIndex);
this.hideLoadingOverlay();
})
.catch(error => {
console.log(error.name)
if (error.name === 'AbortError') {
console.log('Fetch aborted');
} else {
console.log('error3')
$('.toast-body').text(error);
$('.toast').toast('show');
}
this.hideLoadingOverlay();
});
});
}
loadCollections() {
const me = this;
this.currentAjaxRequest = $.ajax({
@@ -911,9 +962,6 @@ class APITester {
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({name: name}),
success: function (data) {
},
error: function (response, textStatus, errorThrown) {
me.hideLoadingOverlay();
$('.toast-body').text(response.responseJSON.error);
@@ -1046,8 +1094,8 @@ class APITester {
queryParams: [],
variables: [],
requestBody: '',
preRequestScript :'',
postRequestScript :'',
preRequestScript: '',
postRequestScript: '',
collectionId: collectionId,
responseModel: {
status: 0,
@@ -1088,9 +1136,14 @@ class APITester {
cancelAjaxRequest() {
if (this.currentAjaxRequest) {
this.currentAjaxRequest.abort();
this.hideLoadingOverlay();
try {
this.currentAjaxRequest.abort();
} catch (e) {
this.apiClient.abort();
}
}
this.hideLoadingOverlay();
}
openApiRequest(event) {
@@ -1147,7 +1200,7 @@ class APITester {
return api.id === apiId;
});
console.log(apiIndex);
if(apiIndex >= 0){
if (apiIndex >= 0) {
this.closeTabWithApiIndex(apiIndex);
}
const me = this;
@@ -1172,7 +1225,7 @@ class APITester {
closeTabWithApiIndex(index) {
console.log(index);
if (index !== undefined){
if (index !== undefined) {
//console.log('closeTabWithApiIndex: ' + index);
const model = this.apiRequests[index];
console.log(model);
@@ -1186,103 +1239,11 @@ class APITester {
$(`#apiRequestTabTitle li:eq(0) button`).tab('show');
}
}
closeTab(event) {
const index = $('#apiRequestTabTitle').children().index($(event.currentTarget).closest('.nav-item'));
this.closeTabWithApiIndex(index);
}
replacePlaceholdersWithVariables(original, index) {
let request = JSON.parse(JSON.stringify(original));
let mergedVariables = {};
_.forEach(this.globalVariables, (value, key) => {
mergedVariables[key] = value;
});
if (this.variables[index]) {
_.forEach(this.variables[index], (value, key) => {
mergedVariables[key] = value;
});
}
_.forEach(mergedVariables, (value, key) => {
const placeholder = `{{${key}}}`;
// Replace in path
request.path = request.path.replace(new RegExp(placeholder, 'g'), value);
// Replace in requestBody
if (request.requestBody) {
request.requestBody = request.requestBody.replace(new RegExp(placeholder, 'g'), value);
}
// Replace in headers
if (request.headers) {
request.headers = request.headers.map(header => {
return {
...header,
key: header.key.replace(new RegExp(placeholder, 'g'), value),
value: header.value.replace(new RegExp(placeholder, 'g'), value)
};
});
}
// Replace in queryParams
if (request.queryParams) {
request.queryParams = request.queryParams.map(queryParam => {
return {
...queryParam,
key: queryParam.key.replace(new RegExp(placeholder, 'g'), value),
value: queryParam.value.replace(new RegExp(placeholder, 'g'), value)
};
});
}
});
return request;
}
executePreRequestScript(model, index, callback) {
let script = model.preRequestScript;
let resultFrame = document.getElementById('preRequest');
let {headers, method, path, queryParams, requestBody} = model;
window.request = {headers, method, path, queryParams, requestBody};
this.variables[index] = {};
model.variables.forEach((item) =>{
if (item.enabled) {
this.variables[index][item.key] = item.value;
}
});
window.preRequestComplete = function(updatedModel) {
callback(updatedModel);
};
window.temp_variable = this.variables[index];
resultFrame.srcdoc = `<script>
window.globals = window.parent.globals;
window.variables = window.parent.temp_variable;
window.request = window.parent.request;
${script}
window.parent.preRequestComplete(window.request);
</script>`;
return model;
}
executePostRequestScript(script, index, response, callback) {
let resultFrame = document.getElementById('postRequest');
window.response = response;
window.parent.temp_variable = this.variables[index];
window.postRequestComplete = function(updatedModel) {
callback(updatedModel);
};
window.temp_variable = this.variables[index];
resultFrame.srcdoc = `<script>
window.globals = window.parent.globals;
window.variables = window.parent.temp_variable;
window.response = window.parent.response;
${script}
window.parent.postRequestComplete(window.request);
</script>`;
}
}