ciyidogan commited on
Commit
c82ea0a
·
verified ·
1 Parent(s): 809e803

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'; // ✅ FormsModule eklendi
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, // ✅ FormsModule imports'a eklendi
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: { name: string; type: string }[] = [];
49
- responseMappingVariables: { name: string; type: 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
- // ✅ 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); // Debug için
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] // Yeni alan
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.name}`); // param artık object
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 && mapping.type) {
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 Map<string, string>();
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 && param.type) {
378
- params.set(param.variable_name, param.type);
379
  }
380
  });
381
  });
382
  });
383
  });
384
-
385
- this.allIntentParameters = Array.from(params.entries())
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
- let jsonStr = control.value;
400
-
401
- jsonStr = jsonStr.replace(/\{\{([^}]+)\}\}/g, (match: string, variablePath: string) => {
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"]';