From 50c59b274bbec7e18d42079d8d760de58f0d280c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=98=84=EC=84=B1=ED=95=84?= Date: Tue, 17 Oct 2023 14:05:35 +0900 Subject: [PATCH] =?UTF-8?q?APITester=20=EC=9E=AC=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/static/js/APITester.js | 1080 +++++++++++++++++ .../templates/page/tester/apiCollection.html | 18 +- .../templates/page/tester/apiTester.html | 1009 +-------------- 3 files changed, 1117 insertions(+), 990 deletions(-) create mode 100644 src/main/resources/static/js/APITester.js diff --git a/src/main/resources/static/js/APITester.js b/src/main/resources/static/js/APITester.js new file mode 100644 index 0000000..28bfc8b --- /dev/null +++ b/src/main/resources/static/js/APITester.js @@ -0,0 +1,1080 @@ +require.config({paths: {'vs': '/plugins/vs'}}); + + +class APITester { + + + constructor() { + this.servers = []; + this.apiTabTitleTarget = '#apiRequestTabTitle'; + this.apiTabContentTarget = '#apiRequestTabContent'; + this.collectionTarget = '#side_menubar'; + this.currentIndex = 0; + this.apiRequests = []; //탭 열린 API + this.collections = []; //API 컬렉션 + this.requestEditors = []; //API request body editor + this.responseEditors = []; //API response body editor + + } + + renderServers(selectedServer) { + const optionsHtml = this.servers.map(server => ` + + `).join(''); + + return ``; + } + + basePath(serverId) { + if (serverId === undefined || serverId === null) { + return this.servers[0].basePath; + } + return this.servers.filter(server => server.id == serverId)[0].basePath; // == : 타입 비교 없이 값만 비교 + } + + apiRequestTemplate(apiRequest, index) { + return ` +
+
${apiRequest.collectionName} > ${apiRequest.name}
+
+
+ + +
+
+ ${this.renderServers(apiRequest.server)} + +
+
+
+ + +
+
+
+
+ + +
+ +
+ +
+ +
+
+ + + + + + + + + + + ${this.renderParams(apiRequest.queryParams)} + +
#이름 + +
+
+
+ + + + + + + + + + + ${this.renderHeaders(apiRequest.headers)} + +
#이름 + +
+
+
+ + + + + + + + + + + ${this.renderVariables(apiRequest.variables)} + +
# + +
+
+
+ +
+
+
+
+ Response + + + +
+ +
+
+
+
+
+ + + + + + + + + +
이름
+
+
+
+ `; + } + + queryParamTemplate(queryParam, index) { + return ` + + +
+ +
+ + + + + + + + + + + + `; + } + + variableTemplate(variable, index) { + return ` + + +
+ +
+ + + + + + + + + + + + `; + } + + headerTemplate(header, index) { + return ` + + +
+ +
+ + + + + + + + + + + + `; + } + + responseHeaderTemplate(header, index) { + return ` + + + + + + + + + `; + } + + collectionTemplate(collection, index) { + return ` +
  • +
    + +
    + + +
    +
    +
    +
      + ${collection.apis.map((api, apiIndex) => this.collectionApiTemplate(api, apiIndex, collection.id)).join('')} +
    +
    +
  • + `; + } + + collectionApiTemplate(api, apiIndex, collectionId) { + return ` +
  • + ${api.name} +
    + + +
    +
  • + `; + } + + init(servers) { + if (servers) { + this.servers = servers; + } + this.bindEvents(); + this.loadCollections(); + } + + bindEvents() { + const events = [ + {selector: '.btn_add_header', action: this.addHeader.bind(this)}, + {selector: '.btn_remove_header', action: this.removeHeader.bind(this)}, + {selector: '.btn_add_query', action: this.addQueryParam.bind(this)}, + {selector: '.btn_remove_query', action: this.removeQueryParam.bind(this)}, + {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: '.save', action: this.saveAPIRequest.bind(this)}, + {selector: '.save_collection', action: this.addCollection.bind(this)}, + {selector: '#cancel-ajax', action: this.cancelAjaxRequest.bind(this)}, + {selector: '#new_collection', action: this.showNewCollectionModal.bind(this)}, + {selector: '#new_request', action: this.showNewApiRequestModal.bind(this)}, + {selector: '.edit_collection', action: this.showCollectionEditModal.bind(this)}, + {selector: '.delete_collection', action: this.showCollectionDeleteModal.bind(this)}, + {selector: '.confirm_delete', action: this.removeCollection.bind(this)}, + {selector: '.add_new_request', action: this.addAPIRequest.bind(this)}, + {selector: '.edit_api_request', action: this.showEditNameModal.bind(this)}, + {selector: '.delete_api_request', action: this.showDeleteApiRequestModal.bind(this)}, + {selector: '.open_api_request', action: this.openApiRequest.bind(this)}, + {selector: '.confirm_delete_api', action: this.deleteApiRequest.bind(this)}, + {selector: '.close_tab', action: this.closeTab.bind(this)} + + ]; + + for (let event of events) { + $(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('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('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)); + + + } + + showCollectionDeleteModal(event) { + const collectionId = $(event.currentTarget).closest('button').data('collection'); + $('#confirm_delete_modal').find('.confirm_delete').attr('data-id', collectionId); + $('#confirm_delete_modal').modal('show'); + } + + showCollectionEditModal(event) { + const me = this; + const collectionId = $(event.currentTarget).closest('button').data('collection'); + const collection = this.collections.filter(function (collection) { + return collection.id === collectionId; + })[0]; + + $('#edit_name_modal').find('input').val(''); + $('#edit_name_modal').modal('show'); + + let saveBtn = $('#edit_name_modal').find('button.change_name'); + saveBtn.off('click'); + + saveBtn.on('click', function () { + collection.name = $('#edit_name_modal').find('input').val(); + me.saveCollection(collection.id, collection.name); + me.renderCollections(); + $('#edit_name_modal').modal('hide'); + }); + } + + showDeleteApiRequestModal(event) { + let collectionId = $(event.currentTarget).closest('li').data('collection'); + let apiId = $(event.currentTarget).closest('li').data('id'); + + $('#confirm_delete_api_modal').find('.confirm_delete_api').attr('data-id', apiId); + $('#confirm_delete_api_modal').find('.confirm_delete_api').attr('data-collection', collectionId); + $('#confirm_delete_api_modal').modal('show'); + } + + showNewApiRequestModal(event) { + if (this.collections.length == 0) { + alert('새로운 컬렉션을 추가해 주세요.'); + return; + } + $('#new_api_request_modal').modal('show'); + } + + showNewCollectionModal(event) { + $('#new_collection_modal').find('input').val(''); + $('#new_collection_modal').modal('show'); + } + + showEditNameModal(event) { + const me = this; + const apiId = $(event.currentTarget).closest('li').data('id'); + const collectionId = $(event.currentTarget).closest('li').data('collection'); + + const collection = this.collections.filter(function (collection) { + return collection.id === collectionId; + })[0]; + + let apiRequest = collection.apis.filter(function (api) { + return api.id === apiId; + })[0]; + + apiRequest.collectionId = collectionId; + + let saveBtn = $('#edit_name_modal').find('button.change_name'); + saveBtn.off('click'); + saveBtn.on('click', function () { + apiRequest.name = $('#edit_name_modal').find('input').val(); + me.changeAPIName(apiRequest); + me.renderCollections(); + $('#edit_name_modal').modal('hide'); + + //탭이 열려 있으면, 닫았다가 다시 열어야 함. + me.renderTabHeader(); + me.renderTabContent(apiRequest.index); + + }); + $('#edit_name_modal').find('input').val(''); + $('#edit_name_modal').modal('show'); + } + + getFieldValue(element) { + if ($(element).is('select') || $(element).is('input[type="text"]')) { + return $(element).val(); + } + if ($(element).is('input[type="checkbox"]')) { + return $(element).is(':checked'); + } + } + + handleUIUpdate(event) { + if (event) { + event.preventDefault(); + const element = event.currentTarget; + const fieldName = $(element).attr('name'); + //console.log(fieldName); + const newValue = this.getFieldValue(element); + //console.log(newValue); + + const tabIndex = $(event.currentTarget).closest('.api_request').data('index'); + let model = this.apiRequests[tabIndex]; + + if (fieldName !== '') { + _.set(model, fieldName, newValue); + } + //console.log(model); + } + } + + handleUpdateServer(event) { + const tabIndex = $(event.currentTarget).closest('.api_request').data('index'); + let model = this.apiRequests[tabIndex]; + let target = $('#api_request_' + model.id); + //console.log('model.server: ' + model.server); + target.find('input[name="basePath"]').val(this.basePath(model.server)); + } + + renderParams(params) { + return params.map((param, index) => this.queryParamTemplate(param, index)).join(''); + } + + renderVariables(variables) { + return variables.map((variable, index) => this.variableTemplate(variable, index)).join(''); + } + + renderHeaders(headers) { + return headers.map((header, index) => this.headerTemplate(header, index)).join(''); + } + + renderTabHeader() { + //console.log('renderTabHeader'); + let apiRequestTabTitle = $('#apiRequestTabTitle'); + apiRequestTabTitle.empty(); + + for (let idx = 0; idx < this.apiRequests.length; idx++) { + let li = ``; + apiRequestTabTitle.append(li); + } + } + + renderTabContent(index) { + var me = this; + //console.log('renderTabContent: ' + index); + let apiRequestTabContent = $(this.apiTabContentTarget); + let model = this.apiRequests[index]; + + apiRequestTabContent.append(this.apiRequestTemplate(model, index)); + + this.openTab(model.id); + let target = $('#api_request_' + model.id); + + require(['vs/editor/editor.main'], function () { + let requestBodyEditor = monaco.editor.create(target.find(".request_body")[0], { + value: model.requestBody, + language: 'json', + theme: 'vs-light', + automaticLayout: true + }); + me.requestEditors.push(requestBodyEditor); + target.find(".request_body")[0].addEventListener('shown.bs.tab', function () { + me.requestEditors[index].layout(); + }); + + me.requestEditors[index].onDidChangeModelContent(function (e) { + model.requestBody = requestBodyEditor.getValue(); + }); + + target.find('.request_body_type').on('change', function () { + let model = requestBodyEditor.getModel(); + monaco.editor.setModelLanguage(model, $(this).val()); + }); + + let responseBodyEditor = monaco.editor.create(target.find(".response_body")[0], { + value: '', + language: 'json', + theme: 'vs-light', + automaticLayout: true + }); + me.responseEditors.push(responseBodyEditor); + }); + } + + 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) { + this.responseEditors[tabIndex].setValue(model.responseModel.body); + } + 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)); + } + } + + 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() { + let select = $('#modal_collection'); + let sidemenu = $(this.collectionTarget); + select.empty(); + sidemenu.empty(); + + for (let i = 0; i < this.collections.length; i++) { + sidemenu.append(this.renderCollection(this.collections[i], i)); + let option = $('