플랫 전분 기본 처리 반영
This commit is contained in:
@@ -35,7 +35,7 @@
|
||||
"dropzone": "^6.0.0-beta.2",
|
||||
"golden-layout": "^2.6.0",
|
||||
"jqueryui": "^1.11.1",
|
||||
"monaco-editor": "^0.45.0",
|
||||
"monaco-editor": "^0.47.0",
|
||||
"monaco-editor-workers": "^0.45.0",
|
||||
"sockjs-client": "^1.6.1",
|
||||
"split-grid": "^1.0.11",
|
||||
|
||||
@@ -92,6 +92,7 @@ class APIScenario {
|
||||
openScenario(event) {
|
||||
event.preventDefault();
|
||||
let scenarioId = $(event.target).closest('li').data('id');
|
||||
this.stopScenario();
|
||||
this.openScenarioById(scenarioId);
|
||||
}
|
||||
|
||||
@@ -308,6 +309,7 @@ class APIScenario {
|
||||
stopScenario() {
|
||||
if (this.executor) {
|
||||
this.executor.reset();
|
||||
this.executor = null;
|
||||
}
|
||||
this.outputEditor.setValue('');
|
||||
}
|
||||
@@ -353,6 +355,7 @@ class APIScenario {
|
||||
const y = ui.offset.top - $(this).offset().top;
|
||||
|
||||
const dataset = ui.draggable[0].dataset;
|
||||
console.log(dataset);
|
||||
var node = `<div><div class="title-box">API</div><div class="box">${dataset.name}</div></div>`;
|
||||
|
||||
me.editor.addNode(dataset.name, 1, 1, x, y, dataset.nodeType, dataset, node);
|
||||
|
||||
@@ -66,14 +66,27 @@ class APIScenarioExecutor {
|
||||
let model = JSON.parse(JSON.stringify(this.apiTester.getApiRequestModel(currentNode.data.id)));
|
||||
model.responseModel = {};
|
||||
this.appendLog(`===============================================================\nExecuting API [${model.name}]`);
|
||||
this.apiClient.sendAPIRequest(model, this.logHttpRequest.bind(this)).then(response => {
|
||||
let request = null;
|
||||
if (model.type === 'TCP') {
|
||||
request = this.logTcpRequest;
|
||||
}else {
|
||||
request = this.logHttpRequest;
|
||||
}
|
||||
|
||||
this.apiClient.sendAPIRequest(model, request.bind(this)).then(response => {
|
||||
this.isRunning = false;
|
||||
if (model.type === 'TCP') {
|
||||
this.logTcpResponse(response);
|
||||
nodeElement.classList.add('result-success');
|
||||
} else {
|
||||
this.logHttpResponse(response);
|
||||
if (response.status === 200 || response.status === 201) {
|
||||
nodeElement.classList.add('result-success');
|
||||
} else {
|
||||
nodeElement.classList.add('result-error');
|
||||
}
|
||||
}
|
||||
|
||||
resolve();
|
||||
}).catch(error => {
|
||||
nodeElement.classList.add('result-abort');
|
||||
@@ -105,7 +118,13 @@ class APIScenarioExecutor {
|
||||
|
||||
logHttpRequest(model) {
|
||||
console.log(model);
|
||||
let log = `[API Request]\n${model.method} ${model.path} HTTP/1.1\n[Request Headers]\n${this.parseRequestHeaders(model.headers)}\n[Request Body]\n${model.requestBody}`;
|
||||
let log = `[HTTP API Request]\n${model.method} ${model.path} HTTP/1.1\n[Request Headers]\n${this.parseRequestHeaders(model.headers)}\n[Request Body]\n${model.requestBody}`;
|
||||
this.appendLog(log);
|
||||
}
|
||||
|
||||
logTcpRequest(model) {
|
||||
console.log(model);
|
||||
let log = `[TCP API Request]\n[Request Body]\n${model.requestBody}`;
|
||||
this.appendLog(log);
|
||||
}
|
||||
|
||||
@@ -114,6 +133,11 @@ class APIScenarioExecutor {
|
||||
this.appendLog(log);
|
||||
}
|
||||
|
||||
logTcpResponse(response) {
|
||||
let log = `[Response]\n${response.body}\n`;
|
||||
this.appendLog(log);
|
||||
}
|
||||
|
||||
parseRequestHeaders(headers) {
|
||||
let parsedHeaders = '';
|
||||
headers.forEach(header => {
|
||||
|
||||
@@ -175,6 +175,7 @@ class APITester {
|
||||
width: 0
|
||||
});
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.responseEditors.length; i++) {
|
||||
this.responseEditors[i].layout({
|
||||
width: 0
|
||||
@@ -387,14 +388,13 @@ class APITester {
|
||||
});
|
||||
let language = model.type === 'HTTP' ? 'json' : 'text';
|
||||
|
||||
let target = null;
|
||||
let requestBodyEditor = null;
|
||||
let target = null;
|
||||
|
||||
if (model.type === 'HTTP') {
|
||||
let httpClientView = new HTTPClientView(me, this.serverManager, apiRequestTabContent, model);
|
||||
httpClientView.init();
|
||||
this.openTab(model.id);
|
||||
target = $('#api_request_' + model.id);
|
||||
target = this.openTab(model.id);
|
||||
requestBodyEditor = monaco.editor.create(target.find('.request_body')[0], {
|
||||
value: model.requestBody, language: 'json', automaticLayout: true
|
||||
});
|
||||
@@ -403,12 +403,13 @@ class APITester {
|
||||
if (model.type === 'TCP') {
|
||||
const tcpClientView = new TCPClientView(me, this.serverManager, apiRequestTabContent, model);
|
||||
tcpClientView.init();
|
||||
this.openTab(model.id);
|
||||
target = $('#api_request_' + model.id);
|
||||
target = this.openTab(model.id);
|
||||
|
||||
requestBodyEditor = monaco.editor.create(target.find('.request_body')[0], {
|
||||
value: model.requestBody, language: 'text', automaticLayout: false
|
||||
value: model.requestBody, language: 'text', automaticLayout: true
|
||||
});
|
||||
tcpClientView.setRequestBodyEditor(requestBodyEditor);
|
||||
|
||||
tcpClientView.requestBodyEditor = requestBodyEditor;
|
||||
}
|
||||
|
||||
requestBodyEditor.onMouseMove((e) => {
|
||||
@@ -428,7 +429,8 @@ class APITester {
|
||||
});
|
||||
|
||||
me.requestEditors.push(requestBodyEditor);
|
||||
target.find('.request_body')[0].addEventListener('shown.bs.tab', () => {
|
||||
|
||||
target.find('.request_body_tab')[0].addEventListener('shown.bs.tab', () => {
|
||||
me.requestEditors[index].layout();
|
||||
});
|
||||
|
||||
@@ -553,6 +555,7 @@ class APITester {
|
||||
}
|
||||
|
||||
openTab(id) {
|
||||
const target = $(`#api_request_${id}`);
|
||||
try {
|
||||
$(`#api_request_tab_${id}`).tab('show'); // API request 탭 활성화
|
||||
} catch (e) {
|
||||
@@ -562,6 +565,7 @@ class APITester {
|
||||
$(`#api_request_${id}`).find('.btn_request_body_tab').tab('show'); // Body 탭 활성화
|
||||
} catch (e) {
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
addHeader(event) {
|
||||
@@ -601,7 +605,6 @@ class APITester {
|
||||
});
|
||||
}
|
||||
}
|
||||
console.log(queryParams);
|
||||
return queryParams;
|
||||
}
|
||||
|
||||
@@ -791,18 +794,32 @@ class APITester {
|
||||
})[0];
|
||||
let me = this;
|
||||
let newApi = {
|
||||
id: '', server: null, method: 'GET', name: newApiName, path: '', type: newApiType, headers: [], queryParams: [], requestBody: '', preRequestScript: '', postRequestScript: '', collectionId: collectionId, responseModel: {
|
||||
status: 0, time: 0, size: 0, headers: [], ody: ''
|
||||
id: '',
|
||||
server: null,
|
||||
method: 'GET',
|
||||
name: newApiName,
|
||||
path: '',
|
||||
type: newApiType,
|
||||
headers: [],
|
||||
queryParams: [],
|
||||
requestBody: '',
|
||||
preRequestScript: '',
|
||||
postRequestScript: '',
|
||||
collectionId: collectionId,
|
||||
responseModel: {
|
||||
status: 0,
|
||||
time: 0,
|
||||
size: 0,
|
||||
headers: [],
|
||||
body: ''
|
||||
}
|
||||
};
|
||||
newApi.collectionName = collection.name;
|
||||
|
||||
if (newApiType === 'TCP') {
|
||||
newApi.layout = [];
|
||||
newApi.layout.push({layoutItems: []});
|
||||
newApi.layout = { layoutItems : []};
|
||||
}
|
||||
|
||||
|
||||
this.currentAjaxRequest = $.ajax({
|
||||
url: this.getAPISaveURL(collectionId), type: 'POST', contentType: 'application/json', data: JSON.stringify(newApi), success: function(data) {
|
||||
newApi.id = data;
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
class ApiLayoutItemTreeBuilder {
|
||||
static createTree(layoutItems) {
|
||||
layoutItems.sort((a, b) => a.loutItemSerno - b.loutItemSerno);
|
||||
const rootId = layoutItems.length === 0 ? 0 : parseInt(layoutItems[0].parent);
|
||||
const root = {id: rootId, children: [], loutItemSerno: 0, loutItemName: 'Root', loutItemDesc: 'Root', loutItemType: '', loutItemDepth: 0, parent: null};
|
||||
|
||||
const nodeMap = new Map();
|
||||
nodeMap.set(rootId, root);
|
||||
|
||||
for (const item of layoutItems) {
|
||||
if (['Grid', 'Group'].includes(item.loutItemType)) {
|
||||
if (item.loutItemOccCnt !== null) {
|
||||
if (item.loutItemOccCnt === '*') {
|
||||
let refItem = layoutItems.find(p => p.loutItemName === item.loutItemOccRef);
|
||||
if (refItem) {
|
||||
item.childOccCnt = parseInt(refItem.value, 10);
|
||||
}
|
||||
} else {
|
||||
item.childOccCnt = parseInt(item.loutItemOccCnt, 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nodeMap.has(parseInt(item.parent))) {
|
||||
nodeMap.get(parseInt(item.parent)).children.push(item);
|
||||
console.log(item.id + ' to ' + item.parent);
|
||||
} else {
|
||||
console.log('Parent not found: ' + item.parent);
|
||||
}
|
||||
|
||||
nodeMap.set(item.id, item);
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
static moveNode(root, node, moveUp) {
|
||||
const parent = this.getParentNode(root, node);
|
||||
if (parent === null) {
|
||||
// Node is the root node, cannot be moved
|
||||
return;
|
||||
}
|
||||
|
||||
const siblings = parent.children;
|
||||
const currentIndex = siblings.indexOf(node);
|
||||
if (currentIndex === -1) {
|
||||
// Node not found in parent's children list
|
||||
return;
|
||||
}
|
||||
|
||||
const newIndex = moveUp ? currentIndex - 1 : currentIndex + 1;
|
||||
if (newIndex >= 0 && newIndex < siblings.length) {
|
||||
// Swap the positions of the current node and the node at the new index
|
||||
[siblings[currentIndex], siblings[newIndex]] = [siblings[newIndex], siblings[currentIndex]];
|
||||
}
|
||||
}
|
||||
|
||||
static getParentNode(root, node) {
|
||||
if (node.parent === null) {
|
||||
return null; // Node is the root node
|
||||
}
|
||||
|
||||
return this.getNodeById(root, node.parent);
|
||||
}
|
||||
|
||||
static getNodeIndex(parent, node) {
|
||||
return parent.children.indexOf(node);
|
||||
}
|
||||
|
||||
static getNodeById(node, id) {
|
||||
if (node.id == id) {
|
||||
return node;
|
||||
}
|
||||
|
||||
for (const child of node.children) {
|
||||
const found = this.getNodeById(child, id);
|
||||
if (found !== null) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static findParent(root, node) {
|
||||
if (root === null || node === null) {
|
||||
return null; // Invalid input
|
||||
}
|
||||
|
||||
if (root.children.includes(node)) {
|
||||
return root; // The root is the parent of the node
|
||||
}
|
||||
|
||||
for (const child of root.children) {
|
||||
const parent = this.findParent(child, node);
|
||||
if (parent !== null) {
|
||||
return parent;
|
||||
}
|
||||
}
|
||||
|
||||
return null; // Node not found in the tree
|
||||
}
|
||||
|
||||
static printTree(root) {
|
||||
console.log('====================================');
|
||||
this.printNode(root, 0);
|
||||
console.log('====================================');
|
||||
}
|
||||
|
||||
static printNode(node, level) {
|
||||
if (node === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Print indentation based on the level
|
||||
let indent = '';
|
||||
for (let i = 0; i < level; i++) {
|
||||
indent += ' ';
|
||||
}
|
||||
|
||||
// Print the node information
|
||||
console.log(`${indent}${node.id}: ${node.loutName}[${node.loutItemType}, Level: ${node.loutItemDepth}, Serno: ${node.loutItemSerno}]`);
|
||||
|
||||
// Recursively print the children
|
||||
if (node.children.length > 0) {
|
||||
for (const child of node.children) {
|
||||
this.printNode(child, level + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static moveNodeDepth(root, node, newDepth) {
|
||||
if (node === null || root === null || newDepth < 0) {
|
||||
// Invalid input
|
||||
return;
|
||||
}
|
||||
|
||||
const parent = this.getParentNode(root, node);
|
||||
if (parent === null) {
|
||||
// Node is the root node, cannot be moved to a different level
|
||||
return;
|
||||
}
|
||||
|
||||
const currentDepth = node.loutItemDepth;
|
||||
const nodeIndex = this.getNodeIndex(parent, node);
|
||||
const nextSibling = this.findClosestSibling(parent.children, nodeIndex);
|
||||
parent.children.splice(nodeIndex, 1);
|
||||
|
||||
if (newDepth > currentDepth) {
|
||||
// Moving to a higher depth
|
||||
if (nextSibling !== null) {
|
||||
nextSibling.children.push(node);
|
||||
node.parent = nextSibling.id;
|
||||
node.loutItemDepth = newDepth;
|
||||
this.adjustChildrenDepth(node, newDepth + 1);
|
||||
} else {
|
||||
// No next sibling found, add the node back to the original parent
|
||||
parent.children.push(node);
|
||||
}
|
||||
} else {
|
||||
// Moving to a lower depth
|
||||
const newParent = this.findParent(root, parent);
|
||||
if (newParent !== null) {
|
||||
newParent.children.push(node);
|
||||
node.parent = newParent.id;
|
||||
node.loutItemDepth = newDepth;
|
||||
this.adjustChildrenDepth(node, newDepth + 1);
|
||||
} else {
|
||||
// No parent found at the desired level, add the node back to the original parent
|
||||
parent.children.push(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static adjustChildrenDepth(node, depth) {
|
||||
for (const child of node.children) {
|
||||
child.loutItemDepth = depth;
|
||||
this.adjustChildrenDepth(child, depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
static findNextSibling(siblings) {
|
||||
if (siblings === null || siblings.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return siblings[0];
|
||||
}
|
||||
|
||||
static findClosestSibling(siblings, nodeIndex) {
|
||||
if (siblings === null || siblings.length === 0) {
|
||||
return null;
|
||||
}
|
||||
if (nodeIndex < 1 || nodeIndex >= siblings.length) {
|
||||
return this.findNextSibling(siblings);
|
||||
}
|
||||
return siblings[nodeIndex - 1];
|
||||
}
|
||||
}
|
||||
|
||||
export default ApiLayoutItemTreeBuilder;
|
||||
@@ -0,0 +1,51 @@
|
||||
class ApiLayoutItemTreeDataBuilder {
|
||||
constructor(startId = 10000) {
|
||||
this.idCounter = this.generateId(startId);
|
||||
this.nodeMap = new Map();
|
||||
}
|
||||
|
||||
* generateId(start) {
|
||||
let id = start;
|
||||
while (true) {
|
||||
yield id++;
|
||||
}
|
||||
}
|
||||
|
||||
cloneNode(node, parent) {
|
||||
|
||||
// Create a shallow copy of the node, except for the children array
|
||||
let newNode = {
|
||||
...node,
|
||||
id: this.idCounter.next().value, // Update with a new unique ID
|
||||
parent: parent ? parent.id : undefined // Set the new parent's ID if parent exists
|
||||
};
|
||||
|
||||
// Remove children property to handle it separately
|
||||
delete newNode.children;
|
||||
this.nodeMap.set(newNode.id, newNode);
|
||||
return newNode;
|
||||
}
|
||||
|
||||
replicateTree(node, parent = null) {
|
||||
const newNode = this.cloneNode(node, parent);
|
||||
newNode.children = []; // Initialize children array
|
||||
if (node.children && node.children.length > 0) {
|
||||
node.children.forEach(child => {
|
||||
const childCount = node.childOccCnt || 1;
|
||||
for (let i = 0; i < childCount; i++) {
|
||||
const childReplicas = this.replicateTree(child, newNode);
|
||||
newNode.children = newNode.children.concat(childReplicas);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return [newNode]; // Return an array containing only the new singular parent node with replicated children
|
||||
}
|
||||
|
||||
createReplicatedTree(originalTree) {
|
||||
// console.log({originalTree});
|
||||
return this.replicateTree(originalTree)[0]; // Return the first element to maintain the tree structure
|
||||
}
|
||||
}
|
||||
|
||||
export default ApiLayoutItemTreeDataBuilder;
|
||||
@@ -16,9 +16,10 @@ class EditableInputModel {
|
||||
}
|
||||
|
||||
class EditableInputView {
|
||||
constructor(model, controller) {
|
||||
constructor(model, controller, options) {
|
||||
this.model = model;
|
||||
this.controller = controller;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
render(element) {
|
||||
@@ -30,8 +31,17 @@ class EditableInputView {
|
||||
|
||||
generateInputHtml() {
|
||||
let contentHtml = this.model.getValue() ? this.renderContent(this.parseContent(this.model.getValue())) : ZERO_WIDTH_SPACE;
|
||||
|
||||
let styleString = '';
|
||||
if (this.options.style && typeof this.options.style === 'object') {
|
||||
const styles = Object.entries(this.options.style);
|
||||
styleString = styles.map(([prop, value]) => `${prop}: ${value};`).join(' ');
|
||||
}
|
||||
|
||||
let style = styleString ? `style="${styleString}"` : '';
|
||||
|
||||
return `
|
||||
<div contenteditable="true" class="editableInputContent form-control" data-value="${this.model.getValue()}">
|
||||
<div contenteditable="true" ${style} class="editableInputContent form-control" data-value="${this.model.getValue()}">
|
||||
${contentHtml}
|
||||
</div>
|
||||
`;
|
||||
@@ -132,9 +142,9 @@ class EditableInputView {
|
||||
|
||||
|
||||
class EditableInputController {
|
||||
constructor(model) {
|
||||
constructor(model, options) {
|
||||
this.model = model;
|
||||
this.view = new EditableInputView(this.model, this);
|
||||
this.view = new EditableInputView(this.model, this, options);
|
||||
}
|
||||
|
||||
init(element, callback) {
|
||||
@@ -251,7 +261,7 @@ class EditableInput {
|
||||
constructor(options = {}) {
|
||||
// Initialize the model with the value
|
||||
this.model = new EditableInputModel(options.value || '');
|
||||
this.controller = new EditableInputController(this.model);
|
||||
this.controller = new EditableInputController(this.model, options);
|
||||
|
||||
// Additional properties as required
|
||||
this.name = options.name || '';
|
||||
|
||||
@@ -0,0 +1,287 @@
|
||||
import EditableInput from './EditableInput';
|
||||
|
||||
class LayoutGridModel {
|
||||
constructor(items) {
|
||||
this.items = items;
|
||||
}
|
||||
}
|
||||
|
||||
class LayoutGridView {
|
||||
|
||||
constructor(model, controller) {
|
||||
this.model = model;
|
||||
this.controller = controller;
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
this.renderApiLayout();
|
||||
}
|
||||
|
||||
renderApiLayout() {
|
||||
const requestContainer = this.targetContent.find('.request_layout');
|
||||
requestContainer.empty();
|
||||
|
||||
const headerHtml = `
|
||||
<table class="layout_table" style="border-collapse: collapse; border: 1px gray solid;">
|
||||
<colgroup>
|
||||
<col style="width: 50px;">
|
||||
<col style="width: 80px;">
|
||||
<col style="width: 80px;">
|
||||
<col style="width: 120px;">
|
||||
<col style="width: 150px;">
|
||||
<col style="width: 60px;">
|
||||
<col style="width: 110px;">
|
||||
<col style="width: 80px;">
|
||||
<col style="width: 100px;">
|
||||
<col style="width: 80px;">
|
||||
<col style="width: 120px;">
|
||||
<col style="width: 120px;">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" style="border: 1px gray solid; text-align: center;width: 80px;">#</th>
|
||||
<th scope="col" style="border: 1px gray solid; text-align: center;width: 40px;">ID</th>
|
||||
<th scope="col" style="border: 1px gray solid; text-align: center;width: 40px;">Parent</th>
|
||||
<th scope="col" style="border: 1px gray solid; text-align: center;width: 120px;">항목명(영문)</th>
|
||||
<th scope="col" style="border: 1px gray solid; text-align: center;width: 150px;">항목설명</th>
|
||||
<th scope="col" style="border: 1px gray solid; text-align: center;width: 60px;">깊이</th>
|
||||
<th scope="col" style="border: 1px gray solid; text-align: center;width: 110px;">아이템 유형</th>
|
||||
<th scope="col" style="border: 1px gray solid; text-align: center;width: 80px;">반복 횟수 </th>
|
||||
<th scope="col" style="border: 1px gray solid; text-align: center;width: 100px;">반복 참조 필드</th>
|
||||
<th scope="col" style="border: 1px gray solid; text-align: center;width: 80px;">데이터 길이</th>
|
||||
<th scope="col" style="border: 1px gray solid; text-align: center;width: 120px;">값</th>
|
||||
<th scope="col" style="border: 1px gray solid; text-align: center;width: 120px;">비고 <button type="button" class="btn btn-sm btn-outline-secondary add-layout-item"><i class="fas fa-plus"></i></button></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
`;
|
||||
|
||||
const footerHtml = `
|
||||
</tbody>
|
||||
</table>
|
||||
`;
|
||||
|
||||
const fieldsHtml = this.model.items ? this.model.items.map((item, index) => {
|
||||
return this.renderApiLayoutItem(item, index);
|
||||
}).join('') : '';
|
||||
|
||||
requestContainer.append(headerHtml + fieldsHtml + footerHtml);
|
||||
|
||||
requestContainer.find('.editable').each((index, editableElement) => {
|
||||
const editableInput = new EditableInput({
|
||||
name: editableElement.dataset.name,
|
||||
value: editableElement.dataset.value,
|
||||
style : {width : '120px'}
|
||||
});
|
||||
|
||||
editableInput.init(editableElement, async (value) => {
|
||||
_.set(this.model, editableElement.dataset.name, value);
|
||||
await this.controller.additionalCallback(this.model);
|
||||
});
|
||||
});
|
||||
|
||||
let totalLength = 0;
|
||||
this.model.items.forEach(item => {
|
||||
totalLength += parseInt(item.loutItemLength);
|
||||
});
|
||||
|
||||
|
||||
this.setupListeners();
|
||||
}
|
||||
|
||||
getFieldValue(element) {
|
||||
if ($(element).is('select') || $(element).is('input[type="text"]') || $(element).is('input[type="number"]')) {
|
||||
return $(element).val();
|
||||
}
|
||||
if ($(element).is('input[type="checkbox"]')) {
|
||||
return $(element).is(':checked');
|
||||
}
|
||||
}
|
||||
|
||||
recalculateSerno() {
|
||||
// Reassign loutItemSerno sequentially based on the current order
|
||||
this.model.items.forEach((item, index) => {
|
||||
item.loutItemSerno = index + 1; // Assuming you want to start numbering from 1
|
||||
});
|
||||
}
|
||||
|
||||
async handleUIUpdate(event) {
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
const element = event.currentTarget;
|
||||
const fieldName = $(element).attr('name');
|
||||
const newValue = this.getFieldValue(element);
|
||||
if (fieldName !== '') {
|
||||
_.set(this.model, fieldName, newValue);
|
||||
await this.controller.additionalCallback(this.model);
|
||||
}
|
||||
}
|
||||
let totalLength = 0;
|
||||
this.model.items.forEach(item => {
|
||||
totalLength += parseInt(item.loutItemLength);
|
||||
});
|
||||
}
|
||||
|
||||
getLastId (){
|
||||
let lastId = 0;
|
||||
this.model.items.forEach(item => {
|
||||
if (item.id > lastId) {
|
||||
lastId = item.id;
|
||||
}
|
||||
});
|
||||
return lastId + 1;
|
||||
}
|
||||
|
||||
setupListeners() {
|
||||
const table = this.targetContent.find('.layout_table');
|
||||
table.on('change', 'select.field, input.field', this.handleUIUpdate.bind(this));
|
||||
|
||||
table.on('click', '.add-layout-item', async(event) => {
|
||||
let item = {
|
||||
'id' : this.getLastId(),
|
||||
'parent': 0,
|
||||
'level': 1,
|
||||
'loutName': '',
|
||||
'loutItemName': '',
|
||||
'loutItemDesc': '',
|
||||
'loutItemDepth': 1,
|
||||
'loutItemType': 'Field',
|
||||
'loutItemOccCnt': '',
|
||||
'loutItemOccRef': '',
|
||||
'loutItemDataType': 'String',
|
||||
'loutItemLength': 1,
|
||||
'loutItemDecimal': 0,
|
||||
'loutItemDefault': '',
|
||||
'loutItemMaskYn': 'N',
|
||||
'loutItemMaskOffset': 0,
|
||||
'loutItemMaskLength': 0,
|
||||
'parentLoutItemIndex': 0,
|
||||
'expanded': true,
|
||||
'isLeaf': true,
|
||||
'LOUTITEMPATH': '',
|
||||
'value': '',
|
||||
'loutItemSerno': 0
|
||||
};
|
||||
this.model.items.push(item);
|
||||
this.recalculateSerno();
|
||||
this.renderApiLayout();
|
||||
await this.controller.additionalCallback(this.model);
|
||||
});
|
||||
|
||||
table.on('click', '.move-up', async (event) => {
|
||||
const index = $(event.currentTarget).data('index');
|
||||
if (index > 0) {
|
||||
const temp = this.model.items[index];
|
||||
this.model.items[index] = this.model.items[index - 1];
|
||||
this.model.items[index - 1] = temp;
|
||||
this.recalculateSerno();
|
||||
this.renderApiLayout();
|
||||
await this.controller.additionalCallback(this.model);
|
||||
}
|
||||
});
|
||||
|
||||
table.on('click', '.move-down', async (event) => {
|
||||
const index = $(event.currentTarget).data('index');
|
||||
if (index < this.model.items.length - 1) {
|
||||
const temp = this.model.items[index];
|
||||
this.model.items[index] = this.model.items[index + 1];
|
||||
this.model.items[index + 1] = temp;
|
||||
this.recalculateSerno();
|
||||
this.renderApiLayout();
|
||||
await this.controller.additionalCallback(this.model);
|
||||
}
|
||||
});
|
||||
|
||||
table.on('click', '.delete', async (event) => {
|
||||
const index = $(event.currentTarget).data('index');
|
||||
this.model.items.splice(index, 1);
|
||||
this.recalculateSerno();
|
||||
this.renderApiLayout();
|
||||
await this.controller.additionalCallback(this.model);
|
||||
});
|
||||
}
|
||||
|
||||
renderApiLayoutItem(item, index) {
|
||||
if (item.value === null) {
|
||||
item.value = '';
|
||||
}
|
||||
|
||||
return `
|
||||
<tr class="layout-grid-row">
|
||||
<td class="layout-grid-cell" style="width: 80px; text-align: center;">
|
||||
<span style="width: 80px; text-align: center;">${item.loutItemSerno}</span>
|
||||
</td>
|
||||
<td class="layout-grid-cell">
|
||||
<input type="text" class="form-control" name="items[${index}].id" value="${item.id}">
|
||||
</td>
|
||||
<td class="layout-grid-cell">
|
||||
<input type="text" class="form-control" name="items[${index}].parent" value="${item.parent}">
|
||||
</td>
|
||||
<td class="layout-grid-cell">
|
||||
<input type="text" class="form-control" name="items[${index}].loutItemName" value="${item.loutItemName}">
|
||||
</td>
|
||||
<td class="layout-grid-cell">
|
||||
<input type="text" class="form-control" name="items[${index}].loutItemDesc" value="${item.loutItemDesc}">
|
||||
</td>
|
||||
<td class="layout-grid-cell">
|
||||
<input type="number" class="form-control" style="width: 60px;" name="items[${index}].loutItemDepth" min="1" value="${item.loutItemDepth}">
|
||||
</td>
|
||||
<td class="layout-grid-cell">
|
||||
<select name="items[${index}].loutItemType" class="form-select">
|
||||
<option value="Field" ${item.loutItemType === 'Field' ? 'selected' : ''}>Field</option>
|
||||
<option value="Grid" ${item.loutItemType === 'Grid' ? 'selected' : ''}>Grid</option>
|
||||
<option value="Group" ${item.loutItemType === 'Group' ? 'selected' : ''}>Group</option>
|
||||
<option value="Attr" ${item.loutItemType === 'Attr' ? 'selected' : ''}>Attr</option>
|
||||
</select>
|
||||
</td>
|
||||
<td class="layout-grid-cell">
|
||||
<input type="text" class="form-control" style="width: 80px;" name="items[${index}].loutItemOccCnt" min="0" value="${item.loutItemOccCnt}">
|
||||
</td>
|
||||
<td class="layout-grid-cell">
|
||||
<input type="text" class="form-control" name="items[${index}].loutItemOccRef" value="${item.loutItemOccRef}"></td>
|
||||
<td class="layout-grid-cell">
|
||||
<input type="number" class="form-control" style="width: 80px;" name="items[${index}].loutItemLength" min="1" value="${item.loutItemLength}">
|
||||
</td>
|
||||
<td class="layout-grid-cell">
|
||||
${ item.loutItemType ==='Field'? `<div class="editable" data-name="items[${index}].value" data-value="${item.value}"></div>`:''}
|
||||
</td>
|
||||
<td class="layout-grid-cell" style="width: 120px; text-align: center;">
|
||||
<div style="width: 120px !important;">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary move-up" data-index="${index}"><i class="fas fa-arrow-up"></i></button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary move-down" data-index="${index}"><i class="fas fa-arrow-down"></i></button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary delete" data-index="${index}"><i class="fas fa-trash-alt"></i></button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class LayoutGridController {
|
||||
constructor(model) {
|
||||
this.model = model;
|
||||
this.view = new LayoutGridView(this.model, this);
|
||||
}
|
||||
|
||||
init(targetContent, callback) {
|
||||
this.additionalCallback = callback;
|
||||
this.view.targetContent = targetContent;
|
||||
this.view.render();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class LayoutGrid {
|
||||
constructor(layout) {
|
||||
this.model = new LayoutGridModel(layout.layoutItems);
|
||||
this.controller = new LayoutGridController(this.model);
|
||||
}
|
||||
|
||||
init(targetContent, callback) {
|
||||
this.controller.init(targetContent, callback);
|
||||
}
|
||||
}
|
||||
|
||||
export default LayoutGrid;
|
||||
@@ -0,0 +1,281 @@
|
||||
import EditableInput from './EditableInput';
|
||||
import APIClient from '../APIClient';
|
||||
|
||||
class LayoutTreeDataGridModel {
|
||||
constructor(root, apiRequest) {
|
||||
this.dataRoot = root;
|
||||
this.apiRequest = apiRequest;
|
||||
console.log('LayoutTreeDataGridModel', this.dataRoot);
|
||||
}
|
||||
|
||||
getNodePath(node, targetId, path = '') {
|
||||
// Check if the current node is the target node
|
||||
if (node.id === targetId) {
|
||||
return path;
|
||||
}
|
||||
|
||||
// If the current node has children, iterate over them
|
||||
if (node.children) {
|
||||
for (let i = 0; i < node.children.length; i++) {
|
||||
// Construct the path to the current child
|
||||
const currentPath = path ? `${path}.children[${i}]` : `children[${i}]`;
|
||||
|
||||
// Recursively call findPath on the child
|
||||
const resultPath = this.getNodePath(node.children[i], targetId, currentPath);
|
||||
|
||||
// If resultPath is not null, the target has been found in the subtree
|
||||
if (resultPath) {
|
||||
return resultPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return null if the target is not found in this subtree
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class LayoutTreeDataGridView {
|
||||
constructor(selector) {
|
||||
console.log({selector});
|
||||
this.table = $('<table>').appendTo($(selector));
|
||||
this.table.addClass('layout_table');
|
||||
this.table.css({'width': 'inherit', 'border-collapse': 'collapse', 'border': '1px gray solid'});
|
||||
console.log(this.table);
|
||||
}
|
||||
|
||||
addHeaders(fields) {
|
||||
const thead = $('<thead>').appendTo(this.table);
|
||||
const tr = $('<tr>').appendTo(thead);
|
||||
tr.addClass('layout-grid-row');
|
||||
|
||||
fields.forEach(field => {
|
||||
const th = $('<th>').html(field.label).appendTo(tr);
|
||||
th.css({'width': field.width + 'px', 'border': '1px gray solid', 'text-align': 'center'});
|
||||
});
|
||||
}
|
||||
|
||||
toggleChildrenVisibility(nodeId, isExpanded) {
|
||||
const selector = `.treegrid-parent-${nodeId}`;
|
||||
$(selector).each((_, el) => {
|
||||
$(el).toggle(!isExpanded);
|
||||
|
||||
if (isExpanded) { // If we are hiding the children
|
||||
const childNodeId = $(el).data('nodeId'); // Assume data-nodeId is set when rows are created
|
||||
this.toggleChildrenVisibility(childNodeId, true); // Recursively hide all descendants
|
||||
const expander = $(`.treegrid-${nodeId} .treegrid-expander`);
|
||||
expander.html(isExpanded ? '+' : '-');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setupExpander(cell, callback) {
|
||||
const expander = $('<span>').addClass('treegrid-expander').html('-').appendTo(cell);
|
||||
expander.click(callback);
|
||||
}
|
||||
}
|
||||
|
||||
class LayoutTreeGridController {
|
||||
constructor(model, view, options, totalMessageLengthSelector) {
|
||||
this.model = model;
|
||||
this.view = view;
|
||||
this.options = options;
|
||||
this.apiClient = new APIClient();
|
||||
this.totalMessageLengthSelector = totalMessageLengthSelector;
|
||||
}
|
||||
|
||||
init(callback) {
|
||||
this.view.addHeaders(this.options);
|
||||
this.processNode(this.model.dataRoot, 0);
|
||||
this.setupEditableFields();
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
async rebuild(dataRoot) {
|
||||
this.model.dataRoot = dataRoot;
|
||||
this.view.table.children().remove();
|
||||
this.view.addHeaders(this.options);
|
||||
this.processNode(this.model.dataRoot, 0);
|
||||
this.setupEditableFields();
|
||||
this.callback(await this.concatenateFieldValues(this.model.dataRoot));
|
||||
}
|
||||
|
||||
getIndent(depth) {
|
||||
return new Array(depth + 1).join('<span class="treegrid-indent"></span>');
|
||||
}
|
||||
|
||||
async fillPadding(value, dataLength) {
|
||||
let paddedValue = await this.apiClient.processPreScriptAndVariables(this.model.apiRequest, value.toString());
|
||||
|
||||
while (paddedValue.length < dataLength) {
|
||||
paddedValue = ' ' + paddedValue; // Prepend a space character to the string
|
||||
}
|
||||
|
||||
return paddedValue;
|
||||
}
|
||||
|
||||
async concatenateFieldValues(node) {
|
||||
let concatenatedValues = '';
|
||||
|
||||
// Process the current node if it is of type 'Field'
|
||||
if (node.loutItemType === 'Field') {
|
||||
concatenatedValues += await this.fillPadding(node.value, node.loutItemLength) || '';
|
||||
}
|
||||
|
||||
// Recursively process each child
|
||||
if (node.children && node.children.length > 0) {
|
||||
for (const child of node.children) {
|
||||
concatenatedValues += await this.concatenateFieldValues(child);
|
||||
}
|
||||
}
|
||||
|
||||
return concatenatedValues;
|
||||
}
|
||||
|
||||
sumFieldLoutItemLengths() {
|
||||
const queue = [this.model.dataRoot];
|
||||
let sum = 0;
|
||||
|
||||
while (queue.length > 0) {
|
||||
const node = queue.shift();
|
||||
|
||||
if (node.loutItemType === 'Field') {
|
||||
sum += parseInt(node.loutItemLength) || 0; // Add the loutItemLength of the node, or 0 if undefined
|
||||
}
|
||||
|
||||
if (node.children && node.children.length > 0) {
|
||||
for (const child of node.children) {
|
||||
queue.push(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
setupEditableFields() {
|
||||
this.view.table.find('.editable').each((index, editableElement) => {
|
||||
const editableInput = new EditableInput({
|
||||
name: editableElement.dataset.name,
|
||||
value: editableElement.dataset.value,
|
||||
path: editableElement.dataset.path
|
||||
});
|
||||
|
||||
editableInput.init(editableElement, async (value) => {
|
||||
console.log(editableElement.dataset.path);
|
||||
console.log(value);
|
||||
_.set(this.model.dataRoot, editableElement.dataset.path, value);
|
||||
console.log(this.model.dataRoot);
|
||||
this.callback(await this.concatenateFieldValues(this.model.dataRoot));
|
||||
});
|
||||
});
|
||||
|
||||
this.totalMessageLengthSelector.text(this.sumFieldLoutItemLengths());
|
||||
}
|
||||
|
||||
addCell(row, node, fieldInfo, depth) {
|
||||
const td = $('<td>').appendTo(row);
|
||||
const padding = fieldInfo.isFirst ? this.getIndent(depth) : '';
|
||||
const nodePath = this.model.getNodePath(this.model.dataRoot, node.id) + '.value';
|
||||
|
||||
if (node.loutItemType === 'Field' && fieldInfo.type === 'textfield') {
|
||||
const div = $('<div>');
|
||||
div.addClass('editable');
|
||||
div.appendTo(td);
|
||||
div.attr('data-name', fieldInfo.name);
|
||||
div.attr('data-value', node[fieldInfo.name]);
|
||||
div.attr('data-path', nodePath);
|
||||
} else {
|
||||
td.html(padding); // Default to just displaying the content
|
||||
}
|
||||
|
||||
if (fieldInfo.width) {
|
||||
td.css({'width': fieldInfo.width + 'px', 'border': '1px gray solid'});
|
||||
}
|
||||
|
||||
if (fieldInfo.align) {
|
||||
td.css({'text-align': fieldInfo.align});
|
||||
}
|
||||
|
||||
return td;
|
||||
}
|
||||
|
||||
addRow(node, depth) {
|
||||
const tr = $('<tr>').addClass('treegrid-' + node.id).data('nodeId', node.id); // Storing node ID for easy access later
|
||||
|
||||
if (depth === 0) {
|
||||
tr.css({'display': 'none'});
|
||||
}
|
||||
|
||||
if (node.parent) {
|
||||
const parentSelector = `.treegrid-${node.parent}`;
|
||||
const lastChildSelector = `.treegrid-parent-${node.parent}`;
|
||||
|
||||
if ($(this.view.table).find(lastChildSelector).length > 0) {
|
||||
const lastChild = $(this.view.table).find(lastChildSelector).last();
|
||||
tr.insertAfter(lastChild);
|
||||
} else {
|
||||
tr.insertAfter($(this.view.table).find(parentSelector));
|
||||
}
|
||||
tr.addClass('treegrid-parent-' + node.parent);
|
||||
} else {
|
||||
tr.appendTo(this.view.table);
|
||||
}
|
||||
return tr;
|
||||
}
|
||||
|
||||
//processNode in bfs
|
||||
processNode(node, depth) {
|
||||
const queue = [{node, depth}];
|
||||
while (queue.length > 0) {
|
||||
const {node, depth} = queue.shift();
|
||||
const tr = this.addRow(node, depth);
|
||||
|
||||
this.options.forEach((fieldInfo) => {
|
||||
const content = node[fieldInfo.name];
|
||||
const td = this.addCell(tr, node, fieldInfo, depth);
|
||||
|
||||
if (fieldInfo.isFirst) {
|
||||
if (node.children && node.children.length > 0) {
|
||||
this.view.setupExpander($(tr.children()[0]), () => this.handleExpanderClick(node.id));
|
||||
}
|
||||
}
|
||||
|
||||
if (fieldInfo.type !== 'textfield') {
|
||||
td.append(content);
|
||||
}
|
||||
});
|
||||
|
||||
if (node.children && node.children.length > 0) {
|
||||
for (const child of node.children) {
|
||||
queue.push({node: child, depth: depth + 1});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleExpanderClick(nodeId) {
|
||||
const expander = $(`.treegrid-${nodeId} .treegrid-expander`);
|
||||
const isExpanded = expander.html() === '-';
|
||||
expander.html(isExpanded ? '+' : '-');
|
||||
this.view.toggleChildrenVisibility(nodeId, isExpanded);
|
||||
}
|
||||
}
|
||||
|
||||
class LayoutTreeDataGrid {
|
||||
constructor(selector, root, apiRequest, options, totalMessageLengthSelector) {
|
||||
this.model = new LayoutTreeDataGridModel(root, apiRequest);
|
||||
this.view = new LayoutTreeDataGridView(selector);
|
||||
this.controller = new LayoutTreeGridController(this.model, this.view, options, totalMessageLengthSelector);
|
||||
}
|
||||
|
||||
init(callback) {
|
||||
this.controller.init(callback);
|
||||
}
|
||||
|
||||
rebuild(dataRoot) {
|
||||
this.controller.rebuild(dataRoot);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default LayoutTreeDataGrid;
|
||||
@@ -0,0 +1,231 @@
|
||||
import EditableInput from './EditableInput';
|
||||
|
||||
class LayoutTreeGridModel {
|
||||
constructor(root) {
|
||||
this.root = root;
|
||||
this.rootId = root.id;
|
||||
}
|
||||
|
||||
setRoot(newRoot) {
|
||||
this.root = newRoot;
|
||||
}
|
||||
|
||||
getNodePath(node, targetId, path = "") {
|
||||
// Check if the current node is the target node
|
||||
if (node.id === targetId) {
|
||||
return path;
|
||||
}
|
||||
|
||||
// If the current node has children, iterate over them
|
||||
if (node.children) {
|
||||
for (let i = 0; i < node.children.length; i++) {
|
||||
// Construct the path to the current child
|
||||
const currentPath = path ? `${path}.children[${i}]` : `children[${i}]`;
|
||||
|
||||
// Recursively call findPath on the child
|
||||
const resultPath = this.getNodePath(node.children[i], targetId, currentPath);
|
||||
|
||||
// If resultPath is not null, the target has been found in the subtree
|
||||
if (resultPath) {
|
||||
return resultPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return null if the target is not found in this subtree
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class LayoutTreeGridView {
|
||||
constructor(selector) {
|
||||
this.selector = selector;
|
||||
this.table = $('<table>').appendTo($(this.selector));
|
||||
this.table.addClass('layout_table');
|
||||
this.table.css({'width': 'inherit', 'border-collapse': 'collapse', 'border': '1px gray solid'});
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
this.table.children().remove();
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.init();
|
||||
}
|
||||
|
||||
addHeaders(fields) {
|
||||
const thead = $('<thead>').appendTo(this.table);
|
||||
const tr = $('<tr>').appendTo(thead);
|
||||
tr.addClass('layout-grid-row');
|
||||
|
||||
fields.forEach(field => {
|
||||
const th = $('<th>').html(field.label).appendTo(tr);
|
||||
th.css({'width': field.width + 'px', 'border': '1px gray solid', 'text-align': 'center'});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
toggleChildrenVisibility(nodeId, isExpanded) {
|
||||
const selector = `.treegrid-parent-${nodeId}`;
|
||||
$(selector).each((_, el) => {
|
||||
$(el).toggle(!isExpanded);
|
||||
|
||||
if (isExpanded) { // If we are hiding the children
|
||||
const childNodeId = $(el).data('nodeId'); // Assume data-nodeId is set when rows are created
|
||||
this.toggleChildrenVisibility(childNodeId, true); // Recursively hide all descendants
|
||||
const expander = $(`.treegrid-${nodeId} .treegrid-expander`);
|
||||
expander.html(isExpanded ? '+' : '-');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setupExpander(cell, callback) {
|
||||
const expander = $('<span>').addClass('treegrid-expander').html('-').appendTo(cell);
|
||||
expander.click(callback);
|
||||
}
|
||||
}
|
||||
|
||||
class LayoutTreeGridController {
|
||||
constructor(model, view, options, callback) {
|
||||
this.model = model;
|
||||
this.view = view;
|
||||
this.options = options;
|
||||
this.callback = callback;
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
this.view.addHeaders(this.options);
|
||||
this.processNode(this.model.root, 0);
|
||||
this.view.table.find('.editable').each((index, editableElement) => {
|
||||
const editableInput = new EditableInput({
|
||||
name: editableElement.dataset.name,
|
||||
value: editableElement.dataset.value,
|
||||
path: editableElement.dataset.path
|
||||
});
|
||||
|
||||
editableInput.init(editableElement, async(value) => {
|
||||
_.set(this.model.root, editableElement.dataset.path, value);
|
||||
this.callback(this.model.root);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getIndent(depth) {
|
||||
return new Array(depth + 1).join('<span class="treegrid-indent"></span>');
|
||||
}
|
||||
|
||||
addRow(node, depth) {
|
||||
const tr = $('<tr>').addClass('treegrid-' + node.id).data('nodeId', node.id); // Storing node ID for easy access later
|
||||
|
||||
if (depth === 0) {
|
||||
tr.css({'display': 'none'});
|
||||
}
|
||||
|
||||
if (node.parent || (node.parent !== null && parseInt(node.parent) !== this.model.rootId)) {
|
||||
const parentSelector = `.treegrid-${node.parent}`;
|
||||
const lastChildSelector = `.treegrid-parent-${node.parent}`;
|
||||
|
||||
if ($(this.view.table).find(lastChildSelector).length > 0) {
|
||||
const lastChild = $(this.view.table).find(lastChildSelector).last();
|
||||
console.log('lastChild: ', lastChild);
|
||||
tr.insertAfter(lastChild);
|
||||
} else {
|
||||
console.log('parentSelector: ', parentSelector);
|
||||
tr.insertAfter($(this.view.table).find(parentSelector));
|
||||
}
|
||||
tr.addClass('treegrid-parent-' + node.parent);
|
||||
} else {
|
||||
console.log('no parent');
|
||||
tr.appendTo(this.view.table);
|
||||
}
|
||||
console.log('addRow, node: ', node.id, ' parent: ', node.parent);
|
||||
console.log('addRow tr: ', tr);
|
||||
return tr;
|
||||
}
|
||||
|
||||
addCell(row, node, fieldInfo, depth) {
|
||||
const td = $('<td>').appendTo(row);
|
||||
const padding = fieldInfo.isFirst ? this.getIndent(depth) : '';
|
||||
const nodePath = this.model.getNodePath(this.model.root, node.id) + ".value";
|
||||
|
||||
if (node.loutItemType === 'Field' && fieldInfo.type === 'textfield') {
|
||||
const div = $('<div>');
|
||||
div.addClass('editable');
|
||||
div.appendTo(td);
|
||||
div.attr('data-name', fieldInfo.name);
|
||||
div.attr('data-value', node[fieldInfo.name]);
|
||||
div.attr('data-path', nodePath);
|
||||
|
||||
} else {
|
||||
td.html(padding); // Default to just displaying the content
|
||||
}
|
||||
|
||||
if (fieldInfo.width) {
|
||||
td.css({'width': fieldInfo.width + 'px', 'border': '1px gray solid'});
|
||||
}
|
||||
|
||||
if (fieldInfo.align) {
|
||||
td.css({'text-align': fieldInfo.align});
|
||||
}
|
||||
|
||||
return td;
|
||||
}
|
||||
|
||||
processNode(node, depth) {
|
||||
console.log('node: ', node.id);
|
||||
const tr = this.addRow(node, depth);
|
||||
|
||||
this.options.forEach((fieldInfo) => {
|
||||
const content = node[fieldInfo.name];
|
||||
const td = this.addCell(tr, node, fieldInfo, depth);
|
||||
|
||||
if (fieldInfo.isFirst) {
|
||||
if (node.children && node.children.length > 0) {
|
||||
this.view.setupExpander($(tr.children()[0]), () => this.handleExpanderClick(node.id));
|
||||
}
|
||||
}
|
||||
|
||||
if (fieldInfo.type !== 'textfield') {
|
||||
td.append(content);
|
||||
}
|
||||
});
|
||||
|
||||
// Recursively process each child node if any
|
||||
if (node.children && node.children.length > 0) {
|
||||
node.children.forEach(child => {
|
||||
this.processNode(child, depth + 1); // Increment depth for each child
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
handleExpanderClick(nodeId) {
|
||||
const expander = $(`.treegrid-${nodeId} .treegrid-expander`);
|
||||
const isExpanded = expander.html() === '-';
|
||||
expander.html(isExpanded ? '+' : '-');
|
||||
this.view.toggleChildrenVisibility(nodeId, isExpanded);
|
||||
}
|
||||
|
||||
rebuild(newRoot) {
|
||||
this.model.setRoot(newRoot);
|
||||
this.view.clear();
|
||||
this.init();
|
||||
}
|
||||
}
|
||||
|
||||
class LayoutTreeGrid {
|
||||
constructor(selector, root, options, callback) {
|
||||
this.model = new LayoutTreeGridModel(root);
|
||||
this.view = new LayoutTreeGridView(selector);
|
||||
this.controller = new LayoutTreeGridController(this.model, this.view, options, callback);
|
||||
}
|
||||
|
||||
rebuild(root, callback) {
|
||||
this.controller.rebuild(root);
|
||||
callback(root);
|
||||
}
|
||||
}
|
||||
|
||||
export default LayoutTreeGrid;
|
||||
@@ -33,10 +33,18 @@ class LoadTestResultDetailController {
|
||||
|
||||
showDetail(result) {
|
||||
this.view.outputEditor.setValue('');
|
||||
console.log(result);
|
||||
if (result.request.type === 'TCP') {
|
||||
this.logTcpRequest(result.request);
|
||||
this.logTcpResponse(result.response);
|
||||
|
||||
} else {
|
||||
this.logHttpRequest(result.request);
|
||||
this.logHttpResponse(result.response);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
reset(){
|
||||
this.view.outputEditor.setValue('');
|
||||
}
|
||||
@@ -48,18 +56,32 @@ class LoadTestResultDetailController {
|
||||
this.view.outputEditor.executeEdits('my-source', [op]);
|
||||
}
|
||||
|
||||
|
||||
logHttpRequest(request) {
|
||||
console.log({request});
|
||||
let log = `[API Request]\n${request.method} ${request.path} HTTP/1.1\n[Request Headers]\n${this.parseRequestHeaders(request.headers)}\n[Request Body]\n${request.requestBody}`;
|
||||
this.appendLog(log);
|
||||
}
|
||||
|
||||
logTcpRequest(model) {
|
||||
console.log(model);
|
||||
let log = `[TCP API Request]\n[Request Body]\n${model.requestBody}`;
|
||||
this.appendLog(log);
|
||||
}
|
||||
|
||||
|
||||
logHttpResponse(response) {
|
||||
console.log({response});
|
||||
let log = `[Response]\nHTTP/1.1 ${response.status}\n[Response Headers]\n${this.parseHeaders(response.headers)}\n[Response Body]\n${response.body}\n`;
|
||||
this.appendLog(log);
|
||||
}
|
||||
|
||||
logTcpResponse(response) {
|
||||
console.log({response});
|
||||
let log = `[Response]\n${response.body}\n`;
|
||||
this.appendLog(log);
|
||||
}
|
||||
|
||||
parseRequestHeaders(headers) {
|
||||
let parsedHeaders = '';
|
||||
headers.forEach(header => {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import EditableInput from './EditableInput';
|
||||
|
||||
class ServerManager {
|
||||
constructor(servers) {
|
||||
this.servers = servers || [];
|
||||
@@ -23,17 +25,93 @@ class ServerManager {
|
||||
}
|
||||
|
||||
renderTcpServers(selectedServer) {
|
||||
const optionsHtml = this.servers.filter( server => server.scheme === 'tcp')
|
||||
.map(server => `<option value="${server.id}" ${selectedServer == server.id ? 'selected' : ''}>${server.name}</option>`).join('');
|
||||
const optionsHtml = this.servers.filter(server => server.scheme === 'tcp').map(server => `<option value="${server.id}" ${selectedServer == server.id ? 'selected' : ''}>${server.name}</option>`).join('');
|
||||
|
||||
return `<select class="form-select server" name="server" style="width: 180px; border-radius: 0;" required>
|
||||
<option value="">서버 선택</option>
|
||||
${optionsHtml}</select>`;
|
||||
}
|
||||
|
||||
|
||||
getHeader(headers, key) {
|
||||
if (headers === undefined || headers === null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Filter the headers to find the matching key
|
||||
const filteredHeaders = headers.filter(header => header.key === key);
|
||||
|
||||
// Check if any header was found that matches the key
|
||||
if (filteredHeaders.length === 0) {
|
||||
console.log('new header');
|
||||
let newHeader = { key: key, value: '', enabled : true };
|
||||
headers.push(newHeader);
|
||||
return newHeader;
|
||||
}
|
||||
// Return the value of the first matching header
|
||||
return filteredHeaders[0];
|
||||
}
|
||||
|
||||
renderTcpServerOptions(target, serverOptions, headers, parent) {
|
||||
console.log(headers);
|
||||
|
||||
let tableHTML = `
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Option Key</th>
|
||||
<th>Option Name</th>
|
||||
<th>Offset</th>
|
||||
<th>Length</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${serverOptions.map(option => `
|
||||
<tr>
|
||||
<td><span>${option.optionKey}</span></td>
|
||||
<td><span>${option.optionName}</span></td>
|
||||
<td><span>${option.offset}</span></td>
|
||||
<td><span>${option.length}</span></td>
|
||||
<td>
|
||||
<div class="editable" data-name="${option.optionKey}" data-value="${this.getHeader(headers, option.optionKey).value}"></div>
|
||||
</td>
|
||||
</tr>
|
||||
`).join('')}
|
||||
</tbody>
|
||||
</table>
|
||||
`;
|
||||
|
||||
const container = document.querySelector(target);
|
||||
container.innerHTML += tableHTML;
|
||||
|
||||
container.querySelectorAll('.editable').forEach(editableElement => {
|
||||
const editableInput = new EditableInput({
|
||||
name: editableElement.dataset.name,
|
||||
value: editableElement.dataset.value
|
||||
});
|
||||
|
||||
editableInput.init(editableElement, (value) => {
|
||||
let header = this.getHeader(headers, editableElement.dataset.name);
|
||||
header.value = value;
|
||||
console.log({value});
|
||||
console.log(header);
|
||||
|
||||
$('span.variable').off('mouseover');
|
||||
$('span.variable').off('mouseout');
|
||||
document.querySelectorAll('span.variable').forEach(element => {
|
||||
let text = element.innerText;
|
||||
element.addEventListener('mouseover', () => parent.showPopper(element, text));
|
||||
element.addEventListener('mouseout', () => parent.hidePopper(element));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
renderHttpServers(selectedServer) {
|
||||
const optionsHtml = this.servers.filter( server => server.scheme !== 'tcp')
|
||||
.map(server => `<option value="${server.id}" ${selectedServer === server.id ? 'selected' : ''} data-path="${server.basePath}">${server.name}</option>`).join('');
|
||||
const optionsHtml = this.servers.filter(server => server.scheme !== 'tcp').
|
||||
map(server => `<option value="${server.id}" ${selectedServer === server.id ? 'selected' : ''} data-path="${server.basePath}">${server.name}</option>`).
|
||||
join('');
|
||||
|
||||
return `<select class="form-select server" name="server" style="width: 180px; border-radius: 0;" required>
|
||||
<option value="">서버 선택</option>
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import EditableInput from './EditableInput';
|
||||
import APIClient from '../APIClient';
|
||||
|
||||
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';
|
||||
|
||||
class TCPClientView {
|
||||
|
||||
@@ -11,255 +14,93 @@ class TCPClientView {
|
||||
this.model = model;
|
||||
this.index = index;
|
||||
this.server = this.serverManager.getServer(this.model.server);
|
||||
this.apiClient = new APIClient();
|
||||
|
||||
}
|
||||
|
||||
init() {
|
||||
console.log('TCPClientView init');
|
||||
this.apiRequestTabContent.append(this.tcpApiRequestTemplate(this.model, this.index));
|
||||
this.renderApiLayout(this.model.layout);
|
||||
if (this.server && this.server.headers === undefined) {
|
||||
this.server.headers = [];
|
||||
}
|
||||
if (this.server) {
|
||||
this.serverManager.renderTcpServerOptions('.server_options', this.server.serverOptions, this.model.headers, this.parent);
|
||||
}
|
||||
|
||||
setRequestBodyEditor(editor) {
|
||||
this.requestBodyEditor = editor;
|
||||
}
|
||||
|
||||
renderApiLayout(layout) {
|
||||
const requestContainer = this.apiRequestTabContent.find('.request_layout');
|
||||
requestContainer.empty();
|
||||
|
||||
const headerHtml = `
|
||||
<table class="table layout_table">
|
||||
<colgroup>
|
||||
<col style="width: 80px;">
|
||||
<col style="width: 120px;">
|
||||
<col style="width: 150px;">
|
||||
<col style="width: 80px;">
|
||||
<col style="width: 110px;">
|
||||
<col style="width: 80px;">
|
||||
<col style="width: 100px;">
|
||||
<col style="width: 120px;">
|
||||
<col style="width: 80px;">
|
||||
<col>
|
||||
<col style="width: 120px;">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" style="text-align: center;width: 80px;">#</th>
|
||||
<th scope="col" style="text-align: center;width: 120px;">항목명(영문)</th>
|
||||
<th scope="col" style="text-align: center;width: 150px;">항목설명</th>
|
||||
<th scope="col" style="text-align: center;width: 80px;">깊이</th>
|
||||
<th scope="col" style="text-align: center;width: 110px;">아이템 유형</th>
|
||||
<th scope="col" style="text-align: center;width: 80px;">반복 횟수 </th>
|
||||
<th scope="col" style="text-align: center;width: 100px;">반복 참조 필드</th>
|
||||
<th scope="col" style="text-align: center;width: 120px;">데이터 타입</th>
|
||||
<th scope="col" style="text-align: center;width: 80px;">데이터 길이</th>
|
||||
<th scope="col" style="text-align: center;">값</th>
|
||||
<th scope="col" style="text-align: center;width: 120px;">비고 <button type="button" class="btn btn-sm btn-outline-secondary add-layout-item"><i class="fas fa-plus"></i></button></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
`;
|
||||
|
||||
const footerHtml = `
|
||||
</tbody>
|
||||
</table>
|
||||
`;
|
||||
|
||||
const fieldsHtml = layout.layoutItems ? layout.layoutItems.map((item, index) => {
|
||||
return this.renderApiLayoutItem(item, index);
|
||||
}).join('') : '';
|
||||
|
||||
requestContainer.append(headerHtml + fieldsHtml + footerHtml);
|
||||
|
||||
requestContainer.find('.editable').each((index, editableElement) => {
|
||||
const editableInput = new EditableInput({
|
||||
name: editableElement.dataset.name,
|
||||
value: editableElement.dataset.value
|
||||
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: 'center', label: '항목명(영문)'},
|
||||
{name: 'loutItemDesc', width: 100, align: 'center', 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);
|
||||
|
||||
console.log(this.apiRequestTabContent.find(`#api_request_${this.model.id}`).find('.request_layout_data_tree'));
|
||||
console.log(this.apiRequestTabContent.find(`#api_request_${this.model.id}`).find('.request_layout_tree'));
|
||||
console.log(this.apiRequestTabContent.find(`#api_request_${this.model.id}`).find('.total_message_length'));
|
||||
|
||||
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) => {
|
||||
this.requestBodyEditor.setValue(finalMessage);
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
let layoutGrid = new LayoutGrid(this.model.layout);
|
||||
layoutGrid.init(this.apiRequestTabContent.find(`#api_request_${this.model.id}`), (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);
|
||||
});
|
||||
|
||||
editableInput.init(editableElement, (value) => {
|
||||
_.set(this.model.layout, editableElement.dataset.name, value);
|
||||
this.buildMessage();
|
||||
$('span.variable').off('mouseover');
|
||||
$('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));
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener('resize', this.resizeEditor.bind(this));
|
||||
|
||||
}
|
||||
|
||||
resizeEditor() {
|
||||
this.requestBodyEditor.layout({
|
||||
width: 0
|
||||
});
|
||||
|
||||
let totalLength = 0;
|
||||
this.model.layout.layoutItems.forEach(item => {
|
||||
totalLength += parseInt(item.loutItemLength);
|
||||
this.requestBodyEditor.layout({});
|
||||
this.layoutJsonEditor.layout({
|
||||
width: 0
|
||||
});
|
||||
this.apiRequestTabContent.find('.total_message_length').text(totalLength);
|
||||
this.setupListeners();
|
||||
}
|
||||
|
||||
|
||||
handleUIUpdate(event) {
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
const element = event.currentTarget;
|
||||
const fieldName = $(element).attr('name');
|
||||
const newValue = this.getFieldValue(element);
|
||||
if (fieldName !== '') {
|
||||
_.set(this.model.layout, fieldName, newValue);
|
||||
this.buildMessage();
|
||||
}
|
||||
}
|
||||
let totalLength = 0;
|
||||
this.model.layout.layoutItems.forEach(item => {
|
||||
totalLength += parseInt(item.loutItemLength);
|
||||
});
|
||||
this.apiRequestTabContent.find('.total_message_length').text(totalLength);
|
||||
}
|
||||
|
||||
getFieldValue(element) {
|
||||
if ($(element).is('select') || $(element).is('input[type="text"]') || $(element).is('input[type="number"]')) {
|
||||
return $(element).val();
|
||||
}
|
||||
if ($(element).is('input[type="checkbox"]')) {
|
||||
return $(element).is(':checked');
|
||||
}
|
||||
}
|
||||
|
||||
renderApiLayoutItem(item, index) {
|
||||
if (item.value === null)
|
||||
item.value = '';
|
||||
|
||||
return `
|
||||
<tr>
|
||||
<td style="width: 80px; text-align: center;"><span style="width: 80px; text-align: center;">${item.loutItemSerno}</span></td>
|
||||
<td><input type="text" class="field" name="layoutItems[${index}].loutItemName" value="${item.loutItemName}"></td>
|
||||
<td><input type="text" class="field" name="layoutItems[${index}].loutItemDesc" value="${item.loutItemDesc}"></td>
|
||||
<td><input type="number" class="field" style="width: 80px;" name="layoutItems[${index}].loutItemDepth" min="1" value="${item.loutItemDepth}"></td>
|
||||
<td>
|
||||
<select name="layoutItems[${index}].loutItemType" class="field" id="loutItemType${index}">
|
||||
<option value="Field" ${item.loutItemType === 'Field' ? 'selected' : ''}>Field</option>
|
||||
<option value="Grid" ${item.loutItemType === 'Grid' ? 'selected' : ''}>Grid</option>
|
||||
<option value="Group" ${item.loutItemType === 'Group' ? 'selected' : ''}>Group</option>
|
||||
<option value="Attr" ${item.loutItemType === 'Attr' ? 'selected' : ''}>Attr</option>
|
||||
</select>
|
||||
</td>
|
||||
<td><input type="number" class="field" style="width: 80px;" name="layoutItems[${index}].loutItemOccCnt" min="0" value="${item.loutItemOccCnt}"></td>
|
||||
<td><input type="text" class="field" name="layoutItems[${index}].loutItemOccRef" value="${item.loutItemOccRef}"></td>
|
||||
<td>
|
||||
<select name="layoutItems[${index}].loutItemDataType" class="field" id="loutItemDataType${index}">
|
||||
<option value="BigDecimal" ${item.loutItemDataType === 'BigDecimal' ? 'selected' : ''}>BigDecimal</option>
|
||||
<option value="Int" ${item.loutItemDataType === 'Int' ? 'selected' : ''}>Int</option>
|
||||
<option value="Long" ${item.loutItemDataType === 'Long' ? 'selected' : ''}>Long</option>
|
||||
<option value="String" ${item.loutItemDataType === 'String' ? 'selected' : ''}>String</option>
|
||||
</select>
|
||||
</td>
|
||||
<td><input type="number" class="field" style="width: 80px;" name="layoutItems[${index}].loutItemLength" min="1" value="${item.loutItemLength}"></td>
|
||||
<td><!-- input type="text" class="field" name="layoutItems[${index}].value" value="${item.value !== undefined ? item.value : ''}" -->
|
||||
<div class="editable" data-name="layoutItems[${index}].value" data-value="${item.value !== undefined ? item.value : ''}"></div>
|
||||
</td>
|
||||
<td style="width: 120px; text-align: center;">
|
||||
<div style="width: 120px !important;">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary move-up" data-index="${index}"><i class="fas fa-arrow-up"></i></button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary move-down" data-index="${index}"><i class="fas fa-arrow-down"></i></button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary delete" data-index="${index}"><i class="fas fa-trash-alt"></i></button>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
|
||||
}
|
||||
|
||||
recalculateSerno() {
|
||||
// Reassign loutItemSerno sequentially based on the current order
|
||||
this.model.layout.layoutItems.forEach((item, index) => {
|
||||
item.loutItemSerno = index + 1; // Assuming you want to start numbering from 1
|
||||
});
|
||||
}
|
||||
|
||||
setupListeners() {
|
||||
const table = this.apiRequestTabContent.find('.layout_table');
|
||||
table.on('change', 'select.field, input.field', this.handleUIUpdate.bind(this));
|
||||
|
||||
table.on('click', '.add-layout-item', event => {
|
||||
let item = {
|
||||
'parent': 0,
|
||||
'level': 1,
|
||||
'loutName': '',
|
||||
'loutItemName': '',
|
||||
'loutItemDesc': '',
|
||||
'loutItemDepth': 1,
|
||||
'loutItemType': 'Field',
|
||||
'loutItemOccCnt': '',
|
||||
'loutItemOccRef': '',
|
||||
'loutItemDataType': 'String',
|
||||
'loutItemLength': 1,
|
||||
'loutItemDecimal': 0,
|
||||
'loutItemDefault': '',
|
||||
'loutItemMaskYn': 'N',
|
||||
'loutItemMaskOffset': 0,
|
||||
'loutItemMaskLength': 0,
|
||||
'parentLoutItemIndex': 0,
|
||||
'expanded': true,
|
||||
'isLeaf': true,
|
||||
'LOUTITEMPATH': '',
|
||||
'value' : ''
|
||||
};
|
||||
this.model.layout.layoutItems.push(item);
|
||||
this.recalculateSerno();
|
||||
this.renderApiLayout(this.model.layout);
|
||||
});
|
||||
|
||||
table.on('click', '.move-up', event => {
|
||||
const index = $(event.currentTarget).data('index');
|
||||
if (index > 0) {
|
||||
const temp = this.model.layout.layoutItems[index];
|
||||
this.model.layout.layoutItems[index] = this.model.layout.layoutItems[index - 1];
|
||||
this.model.layout.layoutItems[index - 1] = temp;
|
||||
this.recalculateSerno();
|
||||
this.renderApiLayout(this.model.layout);
|
||||
}
|
||||
});
|
||||
|
||||
table.on('click', '.move-down', event => {
|
||||
const index = $(event.currentTarget).data('index');
|
||||
if (index < this.model.layout.layoutItems.length - 1) {
|
||||
const temp = this.model.layout.layoutItems[index];
|
||||
this.model.layout.layoutItems[index] = this.model.layout.layoutItems[index + 1];
|
||||
this.model.layout.layoutItems[index + 1] = temp;
|
||||
this.recalculateSerno();
|
||||
this.renderApiLayout(this.model.layout);
|
||||
}
|
||||
});
|
||||
|
||||
table.on('click', '.delete', event => {
|
||||
const index = $(event.currentTarget).data('index');
|
||||
this.model.layout.layoutItems.splice(index, 1);
|
||||
this.recalculateSerno();
|
||||
this.renderApiLayout(this.model.layout);
|
||||
});
|
||||
}
|
||||
|
||||
async buildMessage() {
|
||||
let message = '';
|
||||
this.requestBodyEditor.setValue(message);
|
||||
|
||||
for (const item of this.model.layout.layoutItems) {
|
||||
let duplicated = item.value;
|
||||
const value = await this.apiClient.processPreScriptAndVariables(this.model, duplicated, null);
|
||||
message += this.fillPadding(item.loutItemLength, value);
|
||||
}
|
||||
|
||||
this.requestBodyEditor.setValue(message);
|
||||
}
|
||||
|
||||
fillPadding(dataLength, value) {
|
||||
value = String(value);
|
||||
return value.padStart(dataLength, ' ');
|
||||
this.layoutJsonEditor.layout({});
|
||||
}
|
||||
|
||||
tcpApiRequestTemplate(apiRequest, index) {
|
||||
console.log('tcpApiRequestTemplate');
|
||||
return `
|
||||
<div class="tab-pane fade mt-1 api_request" id="api_request_${apiRequest.id}" style="height: 100%;">
|
||||
<div><span class="collection_name">${apiRequest.collectionName}</span> > <span class="api_name">${apiRequest.name}</span></div>
|
||||
@@ -268,41 +109,41 @@ class TCPClientView {
|
||||
${this.serverManager.renderTcpServers(apiRequest.server)}
|
||||
<label class="form-label must">Server</label>
|
||||
</div>
|
||||
<div class="form-floating me-1 ${(this.server && this.server.lengthFieldInclude) ? 'show':'hide'}">
|
||||
<input type="number" style="width: 120px" value="${this.server.lengthFieldOffset}" name="lengthFieldOffset" class="form-control" placeholder="Length Field Offset" readonly>
|
||||
<label>길이 필드 위치</label>
|
||||
</div>
|
||||
<div class="form-floating me-1 ${(this.server && this.server.lengthFieldInclude) ? 'show':'hide'}">
|
||||
<input type="number" style="width: 120px" value="${this.server.lengthFieldLength}" name="lengthFieldLength" class="form-control" placeholder="Length Field Length" readonly>
|
||||
<label>길이 필드 길이</label>
|
||||
</div>
|
||||
<div class="form-floating me-1 ${(this.server && this.server.messageKeyInclude) ? 'show':'hide'}">
|
||||
<input type="number" style="width: 120px" value="${this.server.messageKeyOffset}" name="messageKeyOffset" class="form-control" placeholder="Message Key Offset" readonly>
|
||||
<label>메시지 키 위치</label>
|
||||
</div>
|
||||
<div class="form-floating me-1 ${(this.server && this.server.messageKeyInclude) ? 'show':'hide'}">
|
||||
<input type="number" style="width: 120px" value="${this.server.messageKeyLength}" name="messageKeyLength" class="form-control" placeholder="Message Key Length" readonly>
|
||||
<label>메시지 키 길이</label>
|
||||
</div>
|
||||
<div class="form-floating me-1 path ${(this.server && this.server.messageKeyInclude) ? 'show':'hide'}" data-name="path" data-label="Key">
|
||||
<input type="text" name="path" class="form-control" style="width: 180px; border-radius: 0" value="${apiRequest.path}">
|
||||
<label>메시지 키</label>
|
||||
</div>
|
||||
<button class="btn btn-primary send me-1">Send</button>
|
||||
<button class="btn btn-primary save" data-index="${index}" style="height: 58px;">저장</button>
|
||||
</div>
|
||||
<div class="d-flex mt-2 server_options">
|
||||
</div>
|
||||
<div class="api_request d-flex flex-column" style="height:100%;">
|
||||
<div class="api_request_body">
|
||||
<ul class="nav nav-tabs mt-1" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link btn_request_body_tab" data-bs-toggle="tab"
|
||||
type="button" role="tab" aria-controls="requestBody" data-bs-target="#requestBodyTab_${apiRequest.id}"
|
||||
aria-selected="false">Body
|
||||
aria-selected="false">Layout
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link btn_layout_json_tab" data-bs-toggle="tab"
|
||||
type="button" role="tab" aria-controls="layoutJson" data-bs-target="#requestLayoutJson_${apiRequest.id}"
|
||||
aria-selected="false">Layout JSON
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link btn_layout_tree_tab" data-bs-toggle="tab"
|
||||
type="button" role="tab" aria-controls="layoutTree" data-bs-target="#requestLayoutTreeTab_${apiRequest.id}"
|
||||
aria-selected="false">Layout Tree
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link btn_request_body_data_tab" data-bs-toggle="tab"
|
||||
type="button" role="tab" aria-controls="requestBodyData" data-bs-target="#requestBodyDataTab_${apiRequest.id}"
|
||||
aria-selected="false">Layout Data
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link btn_prerequest_tab" data-bs-toggle="tab"
|
||||
type="button" role="tab" aria-controls="preScrit" data-bs-target="#preRequestTab_${apiRequest.id}"
|
||||
type="button" role="tab" aria-controls="preScript" data-bs-target="#preRequestTab_${apiRequest.id}"
|
||||
aria-selected="false">Pre-Request
|
||||
</button>
|
||||
</li>
|
||||
@@ -314,13 +155,30 @@ class TCPClientView {
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content" style="height: 100%;">
|
||||
<div class="tab-pane fade request_body_tab" role="tabpanel" id="requestBodyTab_${apiRequest.id}" aria-labelledby="requestBody-tab">
|
||||
<div class="tab-pane fade request_layout_json_tab" role="tabpanel" id="requestLayoutJson_${apiRequest.id}" aria-labelledby="requestLayoutJson-tab">
|
||||
<div class="d-flex flex-row">
|
||||
<div style="width:calc(100%);">
|
||||
<div class="layout-json mt-1" style="height:300px;border:1px solid grey;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane show fade request_body_tab" role="tabpanel" id="requestBodyTab_${apiRequest.id}" aria-labelledby="requestBody-tab">
|
||||
<div class="d-flex flex-column">
|
||||
<div class="request_layout" style="width:100%; border:1px solid grey; overflow: scroll"></div>
|
||||
<div class="request_body" style="width:100%;height: 150px; border:1px solid grey;"></div>
|
||||
<div class="request_layout" style="border:1px solid grey; overflow: scroll"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane fade pre_script_tab" role="tabpanel" id="preRequestTab_${apiRequest.id}" aria-labelledby="preScrit-tab">
|
||||
<div class="tab-pane show fade request_layout_tree_tab" role="tabpanel" id="requestLayoutTreeTab_${apiRequest.id}" aria-labelledby="requestLayoutTree-tab">
|
||||
<div class="d-flex flex-column">
|
||||
<div class="request_layout_tree" style="border:1px solid grey; overflow: scroll"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane show fade request_body_data_tab" role="tabpanel" id="requestBodyDataTab_${apiRequest.id}" aria-labelledby="requestBodyData-tab">
|
||||
<div class="d-flex flex-column">
|
||||
<div class="request_layout_data_tree" style="border:1px solid grey; overflow: scroll"></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane fade pre_script_tab" role="tabpanel" id="preRequestTab_${apiRequest.id}" aria-labelledby="preScript-tab">
|
||||
<div class="d-flex flex-row">
|
||||
<div style="width:calc(100% - 200px);">
|
||||
<div class="pre-request mt-1" style="height:300px;border:1px solid grey;"></div>
|
||||
@@ -345,6 +203,7 @@ class TCPClientView {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="request_body mt-1" style="height: 150px; border:1px solid grey;"></div>
|
||||
<div class="api_request_info" >
|
||||
<span>전체 메시지 길이: </span>
|
||||
<span class="total_message_length"></span>
|
||||
|
||||
@@ -30,22 +30,30 @@ module.exports = {
|
||||
'less-loader' // Compiles Less to CSS
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.(woff|woff2|eot|ttf|otf)$/i,
|
||||
type: 'asset/resource',
|
||||
generator: {
|
||||
filename: 'fonts/[name][ext]',
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: ['style-loader', 'css-loader']
|
||||
},
|
||||
{
|
||||
test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
|
||||
use: [
|
||||
{
|
||||
loader: 'file-loader',
|
||||
options: {
|
||||
name: '[name].[ext]',
|
||||
outputPath: 'fonts/',
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
//,
|
||||
// {
|
||||
// test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
|
||||
// use: [
|
||||
// {
|
||||
// loader: 'file-loader',
|
||||
// options: {
|
||||
// name: '[name].[ext]',
|
||||
// outputPath: 'fonts/',
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
// }
|
||||
]
|
||||
},
|
||||
plugins: [new MonacoWebpackPlugin()],
|
||||
|
||||
+2
-2
@@ -10,8 +10,8 @@ jar.enabled = false
|
||||
|
||||
group = 'com.eactive'
|
||||
version = '2.0.0'
|
||||
sourceCompatibility = '11'
|
||||
targetCompatibility = 11
|
||||
sourceCompatibility = '1.8'
|
||||
targetCompatibility = '1.8'
|
||||
|
||||
configurations {
|
||||
annotationProcessor
|
||||
|
||||
+22
File diff suppressed because one or more lines are too long
+22
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user