Spaces:
Running
Running
Update flare-ui/src/app/dialogs/api-edit-dialog/api-edit-dialog.component.ts
Browse files
flare-ui/src/app/dialogs/api-edit-dialog/api-edit-dialog.component.ts
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
import { Component, Inject, OnInit } from '@angular/core';
|
2 |
import { CommonModule } from '@angular/common';
|
3 |
import { FormBuilder, FormGroup, Validators, ReactiveFormsModule, FormArray } from '@angular/forms';
|
4 |
-
import { FormsModule } from '@angular/forms';
|
5 |
import { MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
|
6 |
import { MatTabsModule } from '@angular/material/tabs';
|
7 |
import { MatFormFieldModule } from '@angular/material/form-field';
|
@@ -22,7 +22,7 @@ import { ApiService } from '../../services/api.service';
|
|
22 |
imports: [
|
23 |
CommonModule,
|
24 |
ReactiveFormsModule,
|
25 |
-
FormsModule,
|
26 |
MatDialogModule,
|
27 |
MatTabsModule,
|
28 |
MatFormFieldModule,
|
@@ -45,15 +45,14 @@ export default class ApiEditDialogComponent implements OnInit {
|
|
45 |
testing = false;
|
46 |
testResult: any = null;
|
47 |
testRequestJson = '{}';
|
48 |
-
allIntentParameters:
|
49 |
-
responseMappingVariables:
|
50 |
-
activeTabIndex = 0;
|
51 |
|
52 |
httpMethods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'];
|
53 |
retryStrategies = ['static', 'exponential'];
|
54 |
variableTypes = ['str', 'int', 'float', 'bool', 'date'];
|
55 |
|
56 |
-
// ✅ Cursor position tracking
|
57 |
private cursorPositions: { [key: string]: number } = {};
|
58 |
|
59 |
constructor(
|
@@ -95,69 +94,6 @@ export default class ApiEditDialogComponent implements OnInit {
|
|
95 |
});
|
96 |
}
|
97 |
|
98 |
-
populateForm(api: any) {
|
99 |
-
console.log('Populating form with API:', api); // Debug için
|
100 |
-
|
101 |
-
// Convert headers object to FormArray
|
102 |
-
const headersArray = this.form.get('headers') as FormArray;
|
103 |
-
headersArray.clear();
|
104 |
-
|
105 |
-
if (api.headers) {
|
106 |
-
// ✅ Array veya Object olabilir, kontrol edelim
|
107 |
-
if (Array.isArray(api.headers)) {
|
108 |
-
// Eğer array ise direkt kullan
|
109 |
-
api.headers.forEach((header: any) => {
|
110 |
-
headersArray.push(this.createHeaderFormGroup(header.key || '', header.value || ''));
|
111 |
-
});
|
112 |
-
} else if (typeof api.headers === 'object') {
|
113 |
-
// Eğer object ise entries ile dönüştür
|
114 |
-
Object.entries(api.headers).forEach(([key, value]) => {
|
115 |
-
headersArray.push(this.createHeaderFormGroup(key, value as string));
|
116 |
-
});
|
117 |
-
}
|
118 |
-
}
|
119 |
-
|
120 |
-
// Convert response_mappings to FormArray
|
121 |
-
const responseMappingsArray = this.form.get('response_mappings') as FormArray;
|
122 |
-
responseMappingsArray.clear();
|
123 |
-
|
124 |
-
if (api.response_mappings && Array.isArray(api.response_mappings)) {
|
125 |
-
api.response_mappings.forEach((mapping: any) => {
|
126 |
-
responseMappingsArray.push(this.createResponseMappingFormGroup(mapping));
|
127 |
-
});
|
128 |
-
}
|
129 |
-
|
130 |
-
// Convert body_template to JSON string if it's an object
|
131 |
-
if (api.body_template && typeof api.body_template === 'object') {
|
132 |
-
api.body_template = JSON.stringify(api.body_template, null, 2);
|
133 |
-
}
|
134 |
-
|
135 |
-
// Convert auth bodies to JSON strings
|
136 |
-
if (api.auth) {
|
137 |
-
if (api.auth.token_request_body && typeof api.auth.token_request_body === 'object') {
|
138 |
-
api.auth.token_request_body = JSON.stringify(api.auth.token_request_body, null, 2);
|
139 |
-
}
|
140 |
-
if (api.auth.token_refresh_body && typeof api.auth.token_refresh_body === 'object') {
|
141 |
-
api.auth.token_refresh_body = JSON.stringify(api.auth.token_refresh_body, null, 2);
|
142 |
-
}
|
143 |
-
}
|
144 |
-
|
145 |
-
// ✅ patchValue'dan önce form değerlerini düzelt
|
146 |
-
const formData = { ...api };
|
147 |
-
|
148 |
-
// headers array'ini kaldır çünkü zaten FormArray'e ekledik
|
149 |
-
delete formData.headers;
|
150 |
-
delete formData.response_mappings;
|
151 |
-
|
152 |
-
// Patch form values
|
153 |
-
this.form.patchValue(formData);
|
154 |
-
|
155 |
-
// Disable name field if editing or testing
|
156 |
-
if (this.data.mode === 'edit' || this.data.mode === 'test') {
|
157 |
-
this.form.get('name')?.disable();
|
158 |
-
}
|
159 |
-
}
|
160 |
-
|
161 |
initializeForm() {
|
162 |
this.form = this.fb.group({
|
163 |
// General Tab
|
@@ -212,21 +148,18 @@ export default class ApiEditDialogComponent implements OnInit {
|
|
212 |
}
|
213 |
|
214 |
populateForm(api: any) {
|
215 |
-
console.log('Populating form with API:', api);
|
216 |
|
217 |
// Convert headers object to FormArray
|
218 |
const headersArray = this.form.get('headers') as FormArray;
|
219 |
headersArray.clear();
|
220 |
|
221 |
if (api.headers) {
|
222 |
-
// ✅ Array veya Object olabilir, kontrol edelim
|
223 |
if (Array.isArray(api.headers)) {
|
224 |
-
// Eğer array ise direkt kullan
|
225 |
api.headers.forEach((header: any) => {
|
226 |
headersArray.push(this.createHeaderFormGroup(header.key || '', header.value || ''));
|
227 |
});
|
228 |
} else if (typeof api.headers === 'object') {
|
229 |
-
// Eğer object ise entries ile dönüştür
|
230 |
Object.entries(api.headers).forEach(([key, value]) => {
|
231 |
headersArray.push(this.createHeaderFormGroup(key, value as string));
|
232 |
});
|
@@ -258,7 +191,6 @@ export default class ApiEditDialogComponent implements OnInit {
|
|
258 |
}
|
259 |
}
|
260 |
|
261 |
-
// ✅ patchValue'dan önce form değerlerini düzelt
|
262 |
const formData = { ...api };
|
263 |
|
264 |
// headers array'ini kaldır çünkü zaten FormArray'e ekledik
|
@@ -268,8 +200,8 @@ export default class ApiEditDialogComponent implements OnInit {
|
|
268 |
// Patch form values
|
269 |
this.form.patchValue(formData);
|
270 |
|
271 |
-
// Disable name field if editing
|
272 |
-
if (this.data.mode === 'edit') {
|
273 |
this.form.get('name')?.disable();
|
274 |
}
|
275 |
}
|
@@ -294,7 +226,7 @@ export default class ApiEditDialogComponent implements OnInit {
|
|
294 |
variable_name: [data.variable_name || '', [Validators.required, Validators.pattern(/^[a-z_][a-z0-9_]*$/)]],
|
295 |
type: [data.type || 'str', Validators.required],
|
296 |
json_path: [data.json_path || '', Validators.required],
|
297 |
-
caption: [data.caption || '', Validators.required]
|
298 |
});
|
299 |
}
|
300 |
|
@@ -328,27 +260,27 @@ export default class ApiEditDialogComponent implements OnInit {
|
|
328 |
|
329 |
getTemplateVariables(includeResponseMappings = true): string[] {
|
330 |
const variables = new Set<string>();
|
331 |
-
|
332 |
// Intent parameters
|
333 |
this.allIntentParameters.forEach(param => {
|
334 |
-
variables.add(`variables.${param
|
335 |
});
|
336 |
-
|
337 |
// Auth tokens
|
338 |
const apiName = this.form.get('name')?.value || 'api_name';
|
339 |
variables.add(`auth_tokens.${apiName}.token`);
|
340 |
-
|
341 |
// Response mappings
|
342 |
if (includeResponseMappings) {
|
343 |
this.responseMappingVariables.forEach(varName => {
|
344 |
variables.add(`variables.${varName}`);
|
345 |
});
|
346 |
}
|
347 |
-
|
348 |
// Config variables
|
349 |
variables.add('config.work_mode');
|
350 |
variables.add('config.cloud_token');
|
351 |
-
|
352 |
return Array.from(variables).sort();
|
353 |
}
|
354 |
|
@@ -356,11 +288,8 @@ export default class ApiEditDialogComponent implements OnInit {
|
|
356 |
this.responseMappingVariables = [];
|
357 |
const mappings = this.responseMappings.value;
|
358 |
mappings.forEach((mapping: any) => {
|
359 |
-
if (mapping.variable_name
|
360 |
-
this.responseMappingVariables.push(
|
361 |
-
name: mapping.variable_name,
|
362 |
-
type: mapping.type
|
363 |
-
});
|
364 |
}
|
365 |
});
|
366 |
}
|
@@ -368,24 +297,21 @@ export default class ApiEditDialogComponent implements OnInit {
|
|
368 |
async loadIntentParameters() {
|
369 |
try {
|
370 |
const projects = await this.apiService.getProjects(false).toPromise();
|
371 |
-
const params = new
|
372 |
-
|
373 |
projects?.forEach(project => {
|
374 |
project.versions?.forEach(version => {
|
375 |
version.intents?.forEach(intent => {
|
376 |
intent.parameters?.forEach((param: any) => {
|
377 |
-
if (param.variable_name
|
378 |
-
params.
|
379 |
}
|
380 |
});
|
381 |
});
|
382 |
});
|
383 |
});
|
384 |
-
|
385 |
-
this.allIntentParameters = Array.from(params.
|
386 |
-
.map(([name, type]) => ({ name, type }))
|
387 |
-
.sort((a, b) => a.name.localeCompare(b.name));
|
388 |
-
|
389 |
} catch (error) {
|
390 |
console.error('Failed to load intent parameters:', error);
|
391 |
}
|
@@ -394,59 +320,13 @@ export default class ApiEditDialogComponent implements OnInit {
|
|
394 |
validateJSON(field: string): boolean {
|
395 |
const control = this.form.get(field);
|
396 |
if (!control || !control.value) return true;
|
397 |
-
|
398 |
try {
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
const path = variablePath.trim();
|
403 |
-
|
404 |
-
if (path.startsWith('variables.')) {
|
405 |
-
const varName = path.split('.')[1];
|
406 |
-
|
407 |
-
// Önce intent parametrelerinde ara
|
408 |
-
const param = this.allIntentParameters.find(p => p.name === varName);
|
409 |
-
if (param) {
|
410 |
-
switch (param.type) {
|
411 |
-
case 'int': return '123';
|
412 |
-
case 'float': return '123.45';
|
413 |
-
case 'bool': return 'true';
|
414 |
-
case 'date': return '"2025-01-01"';
|
415 |
-
default: return '"placeholder"';
|
416 |
-
}
|
417 |
-
}
|
418 |
-
|
419 |
-
// Response mapping'lerde ara
|
420 |
-
const responseVar = this.responseMappingVariables.find(v => v.name === varName);
|
421 |
-
if (responseVar) {
|
422 |
-
switch (responseVar.type) {
|
423 |
-
case 'int': return '123';
|
424 |
-
case 'float': return '123.45';
|
425 |
-
case 'bool': return 'true';
|
426 |
-
case 'date': return '"2025-01-01"';
|
427 |
-
default: return '"placeholder"';
|
428 |
-
}
|
429 |
-
}
|
430 |
-
|
431 |
-
// Tanımsız değişken
|
432 |
-
return '"UNDEFINED_VARIABLE"';
|
433 |
-
}
|
434 |
-
|
435 |
-
if (path.startsWith('auth_tokens.')) {
|
436 |
-
return '"token123"';
|
437 |
-
}
|
438 |
-
|
439 |
-
if (path.startsWith('config.')) {
|
440 |
-
return '"configvalue"';
|
441 |
-
}
|
442 |
-
|
443 |
-
return '"unknown"';
|
444 |
-
});
|
445 |
-
|
446 |
-
JSON.parse(jsonStr);
|
447 |
return true;
|
448 |
-
|
449 |
-
} catch (e) {
|
450 |
return false;
|
451 |
}
|
452 |
}
|
@@ -454,13 +334,10 @@ export default class ApiEditDialogComponent implements OnInit {
|
|
454 |
replaceVariablesForValidation(jsonStr: string): string {
|
455 |
let processed = jsonStr;
|
456 |
|
457 |
-
// Tüm {{...}} template değişkenlerini uygun placeholder ile değiştir
|
458 |
processed = processed.replace(/\{\{([^}]+)\}\}/g, (match, variablePath) => {
|
459 |
-
// Değişken tipini tahmin et
|
460 |
if (variablePath.includes('variables.')) {
|
461 |
const varName = variablePath.split('.').pop()?.toLowerCase() || '';
|
462 |
|
463 |
-
// Bilinen sayısal değişkenler
|
464 |
const numericVars = ['count', 'passenger_count', 'timeout_seconds', 'retry_count', 'amount', 'price', 'quantity', 'age', 'id'];
|
465 |
const booleanVars = ['enabled', 'published', 'is_active', 'confirmed', 'canceled', 'deleted', 'required'];
|
466 |
|
@@ -469,12 +346,10 @@ export default class ApiEditDialogComponent implements OnInit {
|
|
469 |
} else if (booleanVars.some(v => varName.includes(v))) {
|
470 |
return 'true';
|
471 |
} else {
|
472 |
-
// String olarak ele al
|
473 |
return '"placeholder"';
|
474 |
}
|
475 |
}
|
476 |
|
477 |
-
// Auth token'lar ve config değerleri her zaman string
|
478 |
return '"placeholder"';
|
479 |
});
|
480 |
|
@@ -482,7 +357,6 @@ export default class ApiEditDialogComponent implements OnInit {
|
|
482 |
}
|
483 |
|
484 |
async testAPI() {
|
485 |
-
// Validate form first
|
486 |
const generalValid = this.form.get('url')?.valid && this.form.get('method')?.valid;
|
487 |
if (!generalValid) {
|
488 |
this.snackBar.open('Please fill in required fields first', 'Close', { duration: 3000 });
|
@@ -495,7 +369,6 @@ export default class ApiEditDialogComponent implements OnInit {
|
|
495 |
try {
|
496 |
const testData = this.prepareAPIData();
|
497 |
|
498 |
-
// Parse test request JSON
|
499 |
let testRequestData = {};
|
500 |
try {
|
501 |
testRequestData = JSON.parse(this.testRequestJson);
|
@@ -508,13 +381,11 @@ export default class ApiEditDialogComponent implements OnInit {
|
|
508 |
return;
|
509 |
}
|
510 |
|
511 |
-
// Add test request to the data
|
512 |
testData.test_request = testRequestData;
|
513 |
|
514 |
const result = await this.apiService.testAPI(testData).toPromise();
|
515 |
this.testResult = result;
|
516 |
|
517 |
-
// Show appropriate message based on actual success
|
518 |
if (result.success) {
|
519 |
this.snackBar.open(`API test successful! (${result.status_code})`, 'Close', {
|
520 |
duration: 3000
|
@@ -559,17 +430,14 @@ export default class ApiEditDialogComponent implements OnInit {
|
|
559 |
bodyTemplate = {};
|
560 |
}
|
561 |
|
562 |
-
// Placeholder değerlerle doldur
|
563 |
const testData = this.replacePlaceholdersForTest(bodyTemplate);
|
564 |
this.testRequestJson = JSON.stringify(testData, null, 2);
|
565 |
}
|
566 |
|
567 |
replacePlaceholdersForTest(obj: any): any {
|
568 |
if (typeof obj === 'string') {
|
569 |
-
// Template değişkenlerini test değerleriyle değiştir
|
570 |
let result = obj;
|
571 |
|
572 |
-
// Intent parameters
|
573 |
result = result.replace(/\{\{variables\.origin\}\}/g, 'Istanbul');
|
574 |
result = result.replace(/\{\{variables\.destination\}\}/g, 'Ankara');
|
575 |
result = result.replace(/\{\{variables\.flight_date\}\}/g, '2025-06-15');
|
@@ -578,13 +446,10 @@ export default class ApiEditDialogComponent implements OnInit {
|
|
578 |
result = result.replace(/\{\{variables\.pnr\}\}/g, 'ABC12');
|
579 |
result = result.replace(/\{\{variables\.surname\}\}/g, 'Test');
|
580 |
|
581 |
-
// Auth tokens
|
582 |
result = result.replace(/\{\{auth_tokens\.[^}]+\.token\}\}/g, 'test_token_123');
|
583 |
|
584 |
-
// Config
|
585 |
result = result.replace(/\{\{config\.work_mode\}\}/g, 'hfcloud');
|
586 |
|
587 |
-
// Diğer değişkenler
|
588 |
result = result.replace(/\{\{[^}]+\}\}/g, 'test_value');
|
589 |
|
590 |
return result;
|
@@ -601,7 +466,6 @@ export default class ApiEditDialogComponent implements OnInit {
|
|
601 |
prepareAPIData(): any {
|
602 |
const formValue = this.form.getRawValue();
|
603 |
|
604 |
-
// Convert headers array back to object
|
605 |
const headers: any = {};
|
606 |
formValue.headers.forEach((h: any) => {
|
607 |
if (h.key && h.value) {
|
@@ -609,7 +473,6 @@ export default class ApiEditDialogComponent implements OnInit {
|
|
609 |
}
|
610 |
});
|
611 |
|
612 |
-
// Parse JSON fields
|
613 |
let body_template = {};
|
614 |
let auth_token_request_body = {};
|
615 |
let auth_token_refresh_body = {};
|
@@ -632,7 +495,6 @@ export default class ApiEditDialogComponent implements OnInit {
|
|
632 |
console.error('Invalid auth token_refresh_body JSON:', e);
|
633 |
}
|
634 |
|
635 |
-
// Prepare final data
|
636 |
const apiData: any = {
|
637 |
name: formValue.name,
|
638 |
url: formValue.url,
|
@@ -645,12 +507,10 @@ export default class ApiEditDialogComponent implements OnInit {
|
|
645 |
response_mappings: formValue.response_mappings || []
|
646 |
};
|
647 |
|
648 |
-
// Add proxy if specified
|
649 |
if (formValue.proxy) {
|
650 |
apiData.proxy = formValue.proxy;
|
651 |
}
|
652 |
|
653 |
-
// Add auth if enabled
|
654 |
if (formValue.auth.enabled) {
|
655 |
apiData.auth = {
|
656 |
enabled: true,
|
@@ -665,7 +525,6 @@ export default class ApiEditDialogComponent implements OnInit {
|
|
665 |
}
|
666 |
}
|
667 |
|
668 |
-
// Add last_update_date for edit mode
|
669 |
if (this.data.mode === 'edit' && formValue.last_update_date) {
|
670 |
apiData.last_update_date = formValue.last_update_date;
|
671 |
}
|
@@ -678,14 +537,12 @@ export default class ApiEditDialogComponent implements OnInit {
|
|
678 |
this.cancel();
|
679 |
return;
|
680 |
}
|
681 |
-
|
682 |
if (this.form.invalid) {
|
683 |
-
// Mark all fields as touched to show validation errors
|
684 |
Object.keys(this.form.controls).forEach(key => {
|
685 |
this.form.get(key)?.markAsTouched();
|
686 |
});
|
687 |
|
688 |
-
// Check specific JSON fields
|
689 |
const jsonFields = ['body_template', 'auth.token_request_body', 'auth.token_refresh_body'];
|
690 |
for (const field of jsonFields) {
|
691 |
if (!this.validateJSON(field)) {
|
@@ -745,7 +602,6 @@ export default class ApiEditDialogComponent implements OnInit {
|
|
745 |
const variableText = `{{${variable}}}`;
|
746 |
const cursorPos = this.cursorPositions[field] || currentValue.length;
|
747 |
|
748 |
-
// Değişkeni cursor pozisyonuna ekle
|
749 |
const newValue =
|
750 |
currentValue.slice(0, cursorPos) +
|
751 |
variableText +
|
@@ -753,9 +609,7 @@ export default class ApiEditDialogComponent implements OnInit {
|
|
753 |
|
754 |
control.setValue(newValue);
|
755 |
|
756 |
-
// Cursor pozisyonunu güncelle (değişken sonrası)
|
757 |
setTimeout(() => {
|
758 |
-
// ✅ Auth field'ları için özel selector
|
759 |
let selector = `textarea[formControlName="${field}"]`;
|
760 |
if (field === 'auth.token_request_body') {
|
761 |
selector = 'div[formGroupName="auth"] textarea[formControlName="token_request_body"]';
|
|
|
1 |
import { Component, Inject, OnInit } from '@angular/core';
|
2 |
import { CommonModule } from '@angular/common';
|
3 |
import { FormBuilder, FormGroup, Validators, ReactiveFormsModule, FormArray } from '@angular/forms';
|
4 |
+
import { FormsModule } from '@angular/forms';
|
5 |
import { MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
|
6 |
import { MatTabsModule } from '@angular/material/tabs';
|
7 |
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
|
22 |
imports: [
|
23 |
CommonModule,
|
24 |
ReactiveFormsModule,
|
25 |
+
FormsModule,
|
26 |
MatDialogModule,
|
27 |
MatTabsModule,
|
28 |
MatFormFieldModule,
|
|
|
45 |
testing = false;
|
46 |
testResult: any = null;
|
47 |
testRequestJson = '{}';
|
48 |
+
allIntentParameters: string[] = [];
|
49 |
+
responseMappingVariables: string[] = [];
|
50 |
+
activeTabIndex = 0;
|
51 |
|
52 |
httpMethods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'];
|
53 |
retryStrategies = ['static', 'exponential'];
|
54 |
variableTypes = ['str', 'int', 'float', 'bool', 'date'];
|
55 |
|
|
|
56 |
private cursorPositions: { [key: string]: number } = {};
|
57 |
|
58 |
constructor(
|
|
|
94 |
});
|
95 |
}
|
96 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
97 |
initializeForm() {
|
98 |
this.form = this.fb.group({
|
99 |
// General Tab
|
|
|
148 |
}
|
149 |
|
150 |
populateForm(api: any) {
|
151 |
+
console.log('Populating form with API:', api);
|
152 |
|
153 |
// Convert headers object to FormArray
|
154 |
const headersArray = this.form.get('headers') as FormArray;
|
155 |
headersArray.clear();
|
156 |
|
157 |
if (api.headers) {
|
|
|
158 |
if (Array.isArray(api.headers)) {
|
|
|
159 |
api.headers.forEach((header: any) => {
|
160 |
headersArray.push(this.createHeaderFormGroup(header.key || '', header.value || ''));
|
161 |
});
|
162 |
} else if (typeof api.headers === 'object') {
|
|
|
163 |
Object.entries(api.headers).forEach(([key, value]) => {
|
164 |
headersArray.push(this.createHeaderFormGroup(key, value as string));
|
165 |
});
|
|
|
191 |
}
|
192 |
}
|
193 |
|
|
|
194 |
const formData = { ...api };
|
195 |
|
196 |
// headers array'ini kaldır çünkü zaten FormArray'e ekledik
|
|
|
200 |
// Patch form values
|
201 |
this.form.patchValue(formData);
|
202 |
|
203 |
+
// Disable name field if editing or testing
|
204 |
+
if (this.data.mode === 'edit' || this.data.mode === 'test') {
|
205 |
this.form.get('name')?.disable();
|
206 |
}
|
207 |
}
|
|
|
226 |
variable_name: [data.variable_name || '', [Validators.required, Validators.pattern(/^[a-z_][a-z0-9_]*$/)]],
|
227 |
type: [data.type || 'str', Validators.required],
|
228 |
json_path: [data.json_path || '', Validators.required],
|
229 |
+
caption: [data.caption || '', Validators.required]
|
230 |
});
|
231 |
}
|
232 |
|
|
|
260 |
|
261 |
getTemplateVariables(includeResponseMappings = true): string[] {
|
262 |
const variables = new Set<string>();
|
263 |
+
|
264 |
// Intent parameters
|
265 |
this.allIntentParameters.forEach(param => {
|
266 |
+
variables.add(`variables.${param}`);
|
267 |
});
|
268 |
+
|
269 |
// Auth tokens
|
270 |
const apiName = this.form.get('name')?.value || 'api_name';
|
271 |
variables.add(`auth_tokens.${apiName}.token`);
|
272 |
+
|
273 |
// Response mappings
|
274 |
if (includeResponseMappings) {
|
275 |
this.responseMappingVariables.forEach(varName => {
|
276 |
variables.add(`variables.${varName}`);
|
277 |
});
|
278 |
}
|
279 |
+
|
280 |
// Config variables
|
281 |
variables.add('config.work_mode');
|
282 |
variables.add('config.cloud_token');
|
283 |
+
|
284 |
return Array.from(variables).sort();
|
285 |
}
|
286 |
|
|
|
288 |
this.responseMappingVariables = [];
|
289 |
const mappings = this.responseMappings.value;
|
290 |
mappings.forEach((mapping: any) => {
|
291 |
+
if (mapping.variable_name) {
|
292 |
+
this.responseMappingVariables.push(mapping.variable_name);
|
|
|
|
|
|
|
293 |
}
|
294 |
});
|
295 |
}
|
|
|
297 |
async loadIntentParameters() {
|
298 |
try {
|
299 |
const projects = await this.apiService.getProjects(false).toPromise();
|
300 |
+
const params = new Set<string>();
|
301 |
+
|
302 |
projects?.forEach(project => {
|
303 |
project.versions?.forEach(version => {
|
304 |
version.intents?.forEach(intent => {
|
305 |
intent.parameters?.forEach((param: any) => {
|
306 |
+
if (param.variable_name) {
|
307 |
+
params.add(param.variable_name);
|
308 |
}
|
309 |
});
|
310 |
});
|
311 |
});
|
312 |
});
|
313 |
+
|
314 |
+
this.allIntentParameters = Array.from(params).sort();
|
|
|
|
|
|
|
315 |
} catch (error) {
|
316 |
console.error('Failed to load intent parameters:', error);
|
317 |
}
|
|
|
320 |
validateJSON(field: string): boolean {
|
321 |
const control = this.form.get(field);
|
322 |
if (!control || !control.value) return true;
|
323 |
+
|
324 |
try {
|
325 |
+
const jsonStr = control.value;
|
326 |
+
const processedJson = this.replaceVariablesForValidation(jsonStr);
|
327 |
+
JSON.parse(processedJson);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
328 |
return true;
|
329 |
+
} catch {
|
|
|
330 |
return false;
|
331 |
}
|
332 |
}
|
|
|
334 |
replaceVariablesForValidation(jsonStr: string): string {
|
335 |
let processed = jsonStr;
|
336 |
|
|
|
337 |
processed = processed.replace(/\{\{([^}]+)\}\}/g, (match, variablePath) => {
|
|
|
338 |
if (variablePath.includes('variables.')) {
|
339 |
const varName = variablePath.split('.').pop()?.toLowerCase() || '';
|
340 |
|
|
|
341 |
const numericVars = ['count', 'passenger_count', 'timeout_seconds', 'retry_count', 'amount', 'price', 'quantity', 'age', 'id'];
|
342 |
const booleanVars = ['enabled', 'published', 'is_active', 'confirmed', 'canceled', 'deleted', 'required'];
|
343 |
|
|
|
346 |
} else if (booleanVars.some(v => varName.includes(v))) {
|
347 |
return 'true';
|
348 |
} else {
|
|
|
349 |
return '"placeholder"';
|
350 |
}
|
351 |
}
|
352 |
|
|
|
353 |
return '"placeholder"';
|
354 |
});
|
355 |
|
|
|
357 |
}
|
358 |
|
359 |
async testAPI() {
|
|
|
360 |
const generalValid = this.form.get('url')?.valid && this.form.get('method')?.valid;
|
361 |
if (!generalValid) {
|
362 |
this.snackBar.open('Please fill in required fields first', 'Close', { duration: 3000 });
|
|
|
369 |
try {
|
370 |
const testData = this.prepareAPIData();
|
371 |
|
|
|
372 |
let testRequestData = {};
|
373 |
try {
|
374 |
testRequestData = JSON.parse(this.testRequestJson);
|
|
|
381 |
return;
|
382 |
}
|
383 |
|
|
|
384 |
testData.test_request = testRequestData;
|
385 |
|
386 |
const result = await this.apiService.testAPI(testData).toPromise();
|
387 |
this.testResult = result;
|
388 |
|
|
|
389 |
if (result.success) {
|
390 |
this.snackBar.open(`API test successful! (${result.status_code})`, 'Close', {
|
391 |
duration: 3000
|
|
|
430 |
bodyTemplate = {};
|
431 |
}
|
432 |
|
|
|
433 |
const testData = this.replacePlaceholdersForTest(bodyTemplate);
|
434 |
this.testRequestJson = JSON.stringify(testData, null, 2);
|
435 |
}
|
436 |
|
437 |
replacePlaceholdersForTest(obj: any): any {
|
438 |
if (typeof obj === 'string') {
|
|
|
439 |
let result = obj;
|
440 |
|
|
|
441 |
result = result.replace(/\{\{variables\.origin\}\}/g, 'Istanbul');
|
442 |
result = result.replace(/\{\{variables\.destination\}\}/g, 'Ankara');
|
443 |
result = result.replace(/\{\{variables\.flight_date\}\}/g, '2025-06-15');
|
|
|
446 |
result = result.replace(/\{\{variables\.pnr\}\}/g, 'ABC12');
|
447 |
result = result.replace(/\{\{variables\.surname\}\}/g, 'Test');
|
448 |
|
|
|
449 |
result = result.replace(/\{\{auth_tokens\.[^}]+\.token\}\}/g, 'test_token_123');
|
450 |
|
|
|
451 |
result = result.replace(/\{\{config\.work_mode\}\}/g, 'hfcloud');
|
452 |
|
|
|
453 |
result = result.replace(/\{\{[^}]+\}\}/g, 'test_value');
|
454 |
|
455 |
return result;
|
|
|
466 |
prepareAPIData(): any {
|
467 |
const formValue = this.form.getRawValue();
|
468 |
|
|
|
469 |
const headers: any = {};
|
470 |
formValue.headers.forEach((h: any) => {
|
471 |
if (h.key && h.value) {
|
|
|
473 |
}
|
474 |
});
|
475 |
|
|
|
476 |
let body_template = {};
|
477 |
let auth_token_request_body = {};
|
478 |
let auth_token_refresh_body = {};
|
|
|
495 |
console.error('Invalid auth token_refresh_body JSON:', e);
|
496 |
}
|
497 |
|
|
|
498 |
const apiData: any = {
|
499 |
name: formValue.name,
|
500 |
url: formValue.url,
|
|
|
507 |
response_mappings: formValue.response_mappings || []
|
508 |
};
|
509 |
|
|
|
510 |
if (formValue.proxy) {
|
511 |
apiData.proxy = formValue.proxy;
|
512 |
}
|
513 |
|
|
|
514 |
if (formValue.auth.enabled) {
|
515 |
apiData.auth = {
|
516 |
enabled: true,
|
|
|
525 |
}
|
526 |
}
|
527 |
|
|
|
528 |
if (this.data.mode === 'edit' && formValue.last_update_date) {
|
529 |
apiData.last_update_date = formValue.last_update_date;
|
530 |
}
|
|
|
537 |
this.cancel();
|
538 |
return;
|
539 |
}
|
540 |
+
|
541 |
if (this.form.invalid) {
|
|
|
542 |
Object.keys(this.form.controls).forEach(key => {
|
543 |
this.form.get(key)?.markAsTouched();
|
544 |
});
|
545 |
|
|
|
546 |
const jsonFields = ['body_template', 'auth.token_request_body', 'auth.token_refresh_body'];
|
547 |
for (const field of jsonFields) {
|
548 |
if (!this.validateJSON(field)) {
|
|
|
602 |
const variableText = `{{${variable}}}`;
|
603 |
const cursorPos = this.cursorPositions[field] || currentValue.length;
|
604 |
|
|
|
605 |
const newValue =
|
606 |
currentValue.slice(0, cursorPos) +
|
607 |
variableText +
|
|
|
609 |
|
610 |
control.setValue(newValue);
|
611 |
|
|
|
612 |
setTimeout(() => {
|
|
|
613 |
let selector = `textarea[formControlName="${field}"]`;
|
614 |
if (field === 'auth.token_request_body') {
|
615 |
selector = 'div[formGroupName="auth"] textarea[formControlName="token_request_body"]';
|