diff --git a/ApiTestManager/src/APITester.js b/ApiTestManager/src/APITester.js index 188d6e6..d94c9d8 100644 --- a/ApiTestManager/src/APITester.js +++ b/ApiTestManager/src/APITester.js @@ -5,6 +5,7 @@ import VariableSnippet from './components/VariableSnippet'; import HTTPClientView from './components/HTTPClientView'; import TCPClientView from './components/TCPClientView'; import ServerManager from './components/ServerManager'; +import HTTPFlatClientView from './components/HTTPFlatClientView'; class APITester { @@ -398,9 +399,15 @@ class APITester { requestBodyEditor = monaco.editor.create(target.find('.request_body')[0], { value: model.requestBody, language: 'json', automaticLayout: true }); - } - - if (model.type === 'TCP') { + } else if (model.type === 'HTTPFlat') { + let httpFlatClientView = new HTTPFlatClientView(me, this.serverManager, apiRequestTabContent, model); + httpFlatClientView.init(); + target = this.openTab(model.id); + requestBodyEditor = monaco.editor.create(target.find('.request_body')[0], { + value: model.requestBody, language: 'json', automaticLayout: true + }); + httpFlatClientView.requestBodyEditor = requestBodyEditor; + } else if (model.type === 'TCP') { const tcpClientView = new TCPClientView(me, this.serverManager, apiRequestTabContent, model); tcpClientView.init(); target = this.openTab(model.id); @@ -408,7 +415,6 @@ class APITester { requestBodyEditor = monaco.editor.create(target.find('.request_body')[0], { value: model.requestBody, language: 'text', automaticLayout: true }); - tcpClientView.requestBodyEditor = requestBodyEditor; } @@ -832,7 +838,7 @@ class APITester { }; newApi.collectionName = collection.name; - if (newApiType === 'TCP') { + if (newApiType === 'TCP' || newApiType === 'HTTPFlat') { newApi.layout = { layoutItems : []}; } diff --git a/ApiTestManager/src/components/EditableInput.js b/ApiTestManager/src/components/EditableInput.js index 06283c4..b3c0efd 100644 --- a/ApiTestManager/src/components/EditableInput.js +++ b/ApiTestManager/src/components/EditableInput.js @@ -54,22 +54,6 @@ class EditableInputView { this.controller.additionalCallback(this.model.getValue()); } }); - // this.editableDiv.addEventListener('paste', (event) => { - // event.preventDefault(); - // if (navigator.clipboard && navigator.clipboard.readText) { - // navigator.clipboard.readText().then(text => { - // this.insertTextAtCursor(text); - // }).catch(err => { - // console.error('Failed to read clipboard contents: ', err); - // // Fallback to using clipboardData from the paste event - // const text = (event.clipboardData || window.clipboardData).getData('text'); - // this.insertTextAtCursor(text); - // }); - // } else { - // const text = (event.clipboardData || window.clipboardData).getData('text'); - // this.insertTextAtCursor(text); - // } - // }); this.editableDiv.addEventListener('paste', (event) => { event.preventDefault(); let text = (event.clipboardData || window.clipboardData).getData('text/plain'); // Using 'text/plain' for compatibility diff --git a/ApiTestManager/src/components/HTTPClientView.js b/ApiTestManager/src/components/HTTPClientView.js index 86501de..2b659f7 100644 --- a/ApiTestManager/src/components/HTTPClientView.js +++ b/ApiTestManager/src/components/HTTPClientView.js @@ -13,7 +13,7 @@ class HTTPClientView { init() { let me = this; this.apiRequestTabContent.append(this.httpApiRequestTemplate(this.model, this.index)); - let editableInput = new EditableInput({ + let pathInput = new EditableInput({ name: 'path', value: this.model.path }); @@ -28,7 +28,7 @@ class HTTPClientView { }); this.apiRequestTabContent.find(`#api_request_${this.model.id}`).find('.api_request_info').find('.path').each(function() { - editableInput.init(this, (value) => { + pathInput.init(this, (value) => { me.model.path = value; me.model.queryParams = me.parent.parseParam(me.model.path); queryParamTable.setProperties(me.model.queryParams); @@ -36,8 +36,8 @@ class HTTPClientView { $('span.variable').off('mouseout'); document.querySelectorAll('span.variable').forEach(element => { let text = element.innerText; - element.addEventListener('mouseover', () => this.parent.showPopper(element, text)); - element.addEventListener('mouseout', () => this.parent.hidePopper(element)); + element.addEventListener('mouseover', () => me.parent.showPopper(element, text)); + element.addEventListener('mouseout', () => me.parent.hidePopper(element)); }); }); @@ -51,7 +51,7 @@ class HTTPClientView { this.model.queryParams = properties; console.log(this.model.queryParams); this.buildPath(this.model); - editableInput.setValue(this.model.path); + pathInput.setValue(this.model.path); $('span.variable').off('mouseover'); $('span.variable').off('mouseout'); document.querySelectorAll('span.variable').forEach(element => { diff --git a/ApiTestManager/src/components/HTTPFlatClientView.js b/ApiTestManager/src/components/HTTPFlatClientView.js new file mode 100644 index 0000000..99f66b4 --- /dev/null +++ b/ApiTestManager/src/components/HTTPFlatClientView.js @@ -0,0 +1,390 @@ +import LayoutGrid from './LayoutGrid'; +import LayoutTreeGrid from './LayoutTreeGrid'; +import LayoutTreeDataGrid from './LayoutTreeDataGrid'; +import * as monaco from 'monaco-editor'; +import ApiLayoutItemTreeBuilder from './ApiLayoutItemTreeBuilder'; +import ApiLayoutItemTreeDataBuilder from './ApiLayoutItemTreeDataBuilder'; +import EditableInput from './EditableInput'; +import PropertyTable from './PropertyTable'; + +class HTTPFlatClientView { + + constructor(parent, serverManager, apiRequestTabContent, model, index) { + this.parent = parent; + this.serverManager = serverManager; + this.apiRequestTabContent = apiRequestTabContent; + this.model = model; + this.index = index; + this.server = this.serverManager.getServer(this.model.server); + + } + + init() { + console.log('HTTPFlatClientView init'); + this.apiRequestTabContent.append(this.httpFlatRequestTemplate(this.model, this.index)); + + let pathInput = new EditableInput({ + name: 'path', + value: this.model.path + }); + let queryParamTable = new PropertyTable({ + propertyName: 'queryParams', + properties: this.model.queryParams + }); + + let headerTable = new PropertyTable({ + propertyName: 'headers', + properties: this.model.headers + }); + let me = this; + + this.apiRequestTabContent.find(`#api_request_${this.model.id}`).find('.api_request_info').find('.path').each(function() { + pathInput.init(this, (value) => { + me.model.path = value; + me.model.queryParams = me.parent.parseParam(me.model.path); + queryParamTable.setProperties(me.model.queryParams); + $('span.variable').off('mouseover'); + $('span.variable').off('mouseout'); + document.querySelectorAll('span.variable').forEach(element => { + let text = element.innerText; + element.addEventListener('mouseover', () => me.parent.showPopper(element, text)); + element.addEventListener('mouseout', () => me.parent.hidePopper(element)); + }); + }); + $(this).append(``); + $('span.variable').off('mouseover'); + $('span.variable').off('mouseout'); + }); + + queryParamTable.init(this.apiRequestTabContent.find(`#api_request_${this.model.id}`).find('.query_params')[0], (properties) => { + this.model.queryParams = properties; + console.log(this.model.queryParams); + this.buildPath(this.model); + pathInput.setValue(this.model.path); + $('span.variable').off('mouseover'); + $('span.variable').off('mouseout'); + document.querySelectorAll('span.variable').forEach(element => { + let text = element.innerText; + element.addEventListener('mouseover', () => this.parent.showPopper(element, text)); + element.addEventListener('mouseout', () => this.parent.hidePopper(element)); + }); + }); + + headerTable.init(this.apiRequestTabContent.find(`#api_request_${this.model.id}`).find('.request_headers')[0], (properties) => { + this.model.headers = properties; + $('span.variable').off('mouseover'); + $('span.variable').off('mouseout'); + document.querySelectorAll('span.variable').forEach(element => { + let text = element.innerText; + element.addEventListener('mouseover', () => this.parent.showPopper(element, text)); + element.addEventListener('mouseout', () => this.parent.hidePopper(element)); + }); + }); + + if (this.server && this.server.headers === undefined) { + this.server.headers = []; + } + + let layoutJson = JSON.stringify(this.model.layout, null, 2); + this.layoutJsonEditor = monaco.editor.create(this.apiRequestTabContent.find(`#api_request_${this.model.id}`).find('.layout-json')[0], { + value: layoutJson, language: 'json', automaticLayout: true, quickSuggestions: false + }); + + const layoutDataTreeOptions = [ + {name: 'loutItemSerno', width: 100, isFirst: true, label: '#'}, + {name: 'loutItemName', width: 100, align: 'left', label: '항목명(영문)'}, + {name: 'loutItemDesc', width: 100, align: 'left', label: '항목설명'}, + {name: 'loutItemDepth', width: 60, align: 'center', label: '깊이'}, + {name: 'loutItemType', width: 100, align: 'center', label: '아이템 유형'}, + {name: 'loutItemOccCnt', width: 100, align: 'center', label: '반복 횟수'}, + {name: 'loutItemOccRef', width: 120, align: 'center', label: '반복 참조 필드'}, + {name: 'loutItemLength', width: 100, align: 'center', label: '데이터 길이'}, + {name: 'value', width: 200, align: 'left', label: '값', type: 'textfield'} + ]; + + let layoutRoot = ApiLayoutItemTreeBuilder.createTree(this.model.layout.layoutItems); + let replicator = new ApiLayoutItemTreeDataBuilder(10000); + let layoutDataRoot = replicator.createReplicatedTree(layoutRoot); + + let layoutTreeDataGrid = new LayoutTreeDataGrid(this.apiRequestTabContent.find(`#api_request_${this.model.id}`).find('.request_layout_data_tree'), layoutDataRoot, this.model, layoutDataTreeOptions, this.apiRequestTabContent.find(`#api_request_${this.model.id}`).find('.total_message_length')); + layoutTreeDataGrid.init( async (finalMessage, layoutDataTree) => { + const prefix = (this.server && this.server.serverOptions) ? await this.serverManager.renderServerPrefix(this.server.serverOptions, this.model) : ''; + + this.requestBodyEditor.setValue(prefix + finalMessage); + this.model.layout.computedLayoutItemRoot = layoutDataTree; + $('span.variable').off('mouseover'); + $('span.variable').off('mouseout'); + document.querySelectorAll('span.variable').forEach(element => { + let text = element.innerText; + element.addEventListener('mouseover', () => this.parent.showPopper(element, text)); + element.addEventListener('mouseout', () => this.parent.hidePopper(element)); + }); + }); + + let layoutTreeGrid = new LayoutTreeGrid(this.apiRequestTabContent.find(`#api_request_${this.model.id}`).find('.request_layout_tree'), layoutRoot, layoutDataTreeOptions, (root) => { + let replicator = new ApiLayoutItemTreeDataBuilder(10000); + let layoutDataRoot = replicator.createReplicatedTree(root); + layoutTreeDataGrid.rebuild(layoutDataRoot); + $('span.variable').off('mouseover'); + $('span.variable').off('mouseout'); + document.querySelectorAll('span.variable').forEach(element => { + let text = element.innerText; + element.addEventListener('mouseover', () => this.parent.showPopper(element, text)); + element.addEventListener('mouseout', () => this.parent.hidePopper(element)); + }); + }); + + let layoutGrid = new LayoutGrid(this.apiRequestTabContent.find(`#api_request_${this.model.id}`).find('.request_layout'), this.model.layout, (model) => { + model.items.forEach((item) => { + item.children = []; + }); + let updatedRoot = ApiLayoutItemTreeBuilder.createTree(model.items); + layoutTreeGrid.rebuild(updatedRoot, (root) => { + let replicator = new ApiLayoutItemTreeDataBuilder(10000); + let layoutDataRoot = replicator.createReplicatedTree(root); + layoutTreeDataGrid.rebuild(layoutDataRoot); + }); + this.layoutJsonEditor.setValue(JSON.stringify(this.model.layout, null, 2)); + + $('span.variable').off('mouseover'); + $('span.variable').off('mouseout'); + document.querySelectorAll('span.variable').forEach(element => { + let text = element.innerText; + element.addEventListener('mouseover', () => this.parent.showPopper(element, text)); + element.addEventListener('mouseout', () => this.parent.hidePopper(element)); + }); + }); + + this.apiRequestTabContent.find(`#api_request_${this.model.id}`).find('.load_layout').on('click', () => { + try { + this.model.layout = JSON.parse(this.layoutJsonEditor.getValue()); + this.model.layout.layoutItems.forEach((item) => { + if (item.value === undefined){ + item.value = ''; + } + }); + console.log(this.model); + layoutGrid.rebuild(this.model.layout); + } catch (e) { + console.error(e); + alert(e); + } + }); + + window.addEventListener('resize', this.resizeEditor.bind(this)); + + } + + buildPath(model) { + let queryString = model.path.split('?')[0]; + if (model.queryParams.length > 0) { + queryString += '?'; + } + for (let i = 0; i < model.queryParams.length; i++) { + if (!model.queryParams[i].enabled) { + continue; + } + queryString += model.queryParams[i].key + '=' + model.queryParams[i].value; + if (i < model.queryParams.length - 1) { + queryString += '&'; + } + } + model.path = queryString; + } + + resizeEditor() { + this.requestBodyEditor.layout({ + width: 0 + }); + this.requestBodyEditor.layout({}); + this.layoutJsonEditor.layout({ + width: 0 + }); + this.layoutJsonEditor.layout({}); + } + + httpFlatRequestTemplate(apiRequest, index) { + return ` +