Skip to content
This repository was archived by the owner on Mar 18, 2026. It is now read-only.

Commit 4b0ef78

Browse files
authored
Merge pull request #264 from X2bee/main
feat: Add html_parser parameter to API tools and update ToolStorageUp…
2 parents 810b6d9 + 2e58840 commit 4b0ef78

2 files changed

Lines changed: 75 additions & 7 deletions

File tree

src/app/_common/api/toolsAPI.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ export const listToolsDetail = async () => {
6666
* @param {number} content.api_timeout - API 타임아웃 (초)
6767
* @param {boolean} content.is_query_string - Query String 사용 여부
6868
* @param {boolean} content.response_filter - 응답 필터 사용 여부
69+
* @param {boolean} content.html_parser - HTML Parser 사용 여부
6970
* @param {string} content.response_filter_path - 응답 필터 경로
7071
* @param {string} content.response_filter_field - 응답 필터 필드
7172
* @param {string} content.status - 도구 상태
@@ -453,6 +454,7 @@ export const rateToolStore = async (storeToolId, userId, functionUploadId, ratin
453454
* @param {Object} testRequest.static_body - 정적 바디 파라미터 (옵션)
454455
* @param {string} testRequest.body_type - 바디 타입 (application/json, application/xml, application/x-www-form-urlencoded, multipart/form-data, text/plain, text/html, text/csv, url-params, 기본값: application/json)
455456
* @param {boolean} testRequest.is_query_string - Query String 사용 여부 (옵션)
457+
* @param {boolean} testRequest.html_parser - HTML Parser 사용 여부 (옵션)
456458
* @param {number} testRequest.api_timeout - 타임아웃 (초, 기본값: 30)
457459
* @returns {Promise<Object>} API 테스트 결과를 포함하는 프로미스
458460
* @returns {boolean} result.success - 요청 성공 여부
@@ -475,6 +477,7 @@ export const testApiEndpoint = async (testRequest) => {
475477
devLog.log('- has_static_body:', !!testRequest.static_body && Object.keys(testRequest.static_body).length > 0);
476478
devLog.log('- body_type:', testRequest.body_type);
477479
devLog.log('- is_query_string:', testRequest.is_query_string);
480+
devLog.log('- html_parser:', testRequest.html_parser);
478481

479482
// 필수 필드 검증
480483
if (!testRequest.api_url || !testRequest.api_url.trim()) {
@@ -491,6 +494,7 @@ export const testApiEndpoint = async (testRequest) => {
491494
static_body: testRequest.static_body || {},
492495
body_type: testRequest.body_type || 'application/json',
493496
is_query_string: testRequest.is_query_string || false,
497+
html_parser: testRequest.html_parser || false,
494498
api_timeout: testRequest.api_timeout || 30,
495499
}),
496500
});

src/app/main/workflowSection/components/ToolStorageUpload.tsx

Lines changed: 71 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ const ToolStorageUpload: React.FC<ToolStorageUploadProps> = ({ onBack, editMode
4949
body_type: initialData?.body_type || 'application/json', // application/json, application/xml, application/x-www-form-urlencoded, multipart/form-data, text/plain, text/html, text/csv, url-params
5050
is_query_string: initialData?.is_query_string || false,
5151
response_filter: initialData?.response_filter || false,
52+
html_parser: initialData?.html_parser || false,
5253
response_filter_path: initialData?.response_filter_path || '',
5354
response_filter_field: initialData?.response_filter_field || '',
5455
});
@@ -361,12 +362,47 @@ const ToolStorageUpload: React.FC<ToolStorageUploadProps> = ({ onBack, editMode
361362
const pathKeys = formData.response_filter_path.split('.');
362363

363364
for (const key of pathKeys) {
364-
if (extractedData && typeof extractedData === 'object' && key in extractedData) {
365-
extractedData = extractedData[key];
366-
} else {
367-
// 경로를 찾지 못하면 원본 반환
365+
if (!extractedData) {
368366
return displayData;
369367
}
368+
369+
// 배열 인덱스 처리: key[0], key[1] 등의 형식 지원
370+
const arrayMatch = key.match(/^(.+?)\[(\d+)\]$/);
371+
372+
if (arrayMatch) {
373+
// 배열 접근: key[index] 형식
374+
const arrayKey = arrayMatch[1];
375+
const arrayIndex = parseInt(arrayMatch[2], 10);
376+
377+
if (typeof extractedData === 'object' && arrayKey in extractedData) {
378+
extractedData = extractedData[arrayKey];
379+
if (Array.isArray(extractedData) && arrayIndex >= 0 && arrayIndex < extractedData.length) {
380+
extractedData = extractedData[arrayIndex];
381+
} else {
382+
// 배열이 아니거나 인덱스가 범위를 벗어나면 원본 반환
383+
return displayData;
384+
}
385+
} else {
386+
// key를 찾지 못하면 원본 반환
387+
return displayData;
388+
}
389+
} else if (/^\d+$/.test(key)) {
390+
// 순수 숫자인 경우 (배열 인덱스만)
391+
const index = parseInt(key, 10);
392+
if (Array.isArray(extractedData) && index >= 0 && index < extractedData.length) {
393+
extractedData = extractedData[index];
394+
} else {
395+
return displayData;
396+
}
397+
} else {
398+
// 일반 객체 키 접근
399+
if (typeof extractedData === 'object' && key in extractedData) {
400+
extractedData = extractedData[key];
401+
} else {
402+
// 경로를 찾지 못하면 원본 반환
403+
return displayData;
404+
}
405+
}
370406
}
371407
}
372408

@@ -472,7 +508,8 @@ const ToolStorageUpload: React.FC<ToolStorageUploadProps> = ({ onBack, editMode
472508
body: formData.api_method !== 'GET' ? body : undefined,
473509
staticBody: formData.api_method !== 'GET' ? staticBody : undefined,
474510
bodyType: formData.body_type,
475-
isQueryString: formData.is_query_string
511+
isQueryString: formData.is_query_string,
512+
htmlParser: formData.html_parser
476513
});
477514

478515
// 백엔드 프록시를 통해 API 테스트
@@ -484,6 +521,7 @@ const ToolStorageUpload: React.FC<ToolStorageUploadProps> = ({ onBack, editMode
484521
static_body: staticBody,
485522
body_type: formData.body_type,
486523
is_query_string: formData.is_query_string,
524+
html_parser: formData.html_parser,
487525
api_timeout: formData.api_timeout || 30
488526
} as any);
489527

@@ -575,6 +613,7 @@ const ToolStorageUpload: React.FC<ToolStorageUploadProps> = ({ onBack, editMode
575613
api_timeout: formData.api_timeout,
576614
is_query_string: formData.is_query_string,
577615
response_filter: formData.response_filter,
616+
html_parser: formData.html_parser,
578617
response_filter_path: formData.response_filter_path,
579618
response_filter_field: formData.response_filter_field,
580619
status: apiTestSuccess ? 'active' : 'inactive',
@@ -1239,13 +1278,38 @@ const ToolStorageUpload: React.FC<ToolStorageUploadProps> = ({ onBack, editMode
12391278

12401279
{formData.response_filter && (
12411280
<>
1281+
<div className={styles.formRow}>
1282+
<div>
1283+
<div className={styles.fieldLabel}>
1284+
HTML Parser 사용
1285+
</div>
1286+
<div className={styles.fieldDescription}>
1287+
HTML 응답을 파싱하여 텍스트만 추출합니다.
1288+
</div>
1289+
</div>
1290+
<div className={styles.fieldInput}>
1291+
<label className={styles.checkboxLabel}>
1292+
<input
1293+
type="checkbox"
1294+
name="html_parser"
1295+
checked={formData.html_parser}
1296+
onChange={handleInputChange}
1297+
className={styles.checkbox}
1298+
disabled={loading}
1299+
/>
1300+
<span>HTML Parser 활성화</span>
1301+
</label>
1302+
</div>
1303+
</div>
1304+
12421305
<div className={styles.formRow}>
12431306
<div>
12441307
<div className={styles.fieldLabel}>
12451308
필터 경로
12461309
</div>
12471310
<div className={styles.fieldDescription}>
1248-
응답 객체에서 데이터를 추출할 경로를 지정하세요. <br></br>(예: data.results)
1311+
응답 객체에서 데이터를 추출할 경로를 지정하세요. <br></br>
1312+
(예: data.results, html.body.div[1], items[0].content)
12491313
</div>
12501314
</div>
12511315
<div className={styles.fieldInput}>
@@ -1256,7 +1320,7 @@ const ToolStorageUpload: React.FC<ToolStorageUploadProps> = ({ onBack, editMode
12561320
value={formData.response_filter_path}
12571321
onChange={handleInputChange}
12581322
className={styles.input}
1259-
placeholder="data.weather.current"
1323+
placeholder="data.weather.current 또는 html.body.div[1]"
12601324
disabled={loading}
12611325
/>
12621326
</div>

0 commit comments

Comments
 (0)