Spaces:
Paused
Paused
| <h2 mat-dialog-title> | |
| @if (data.mode === 'create') { | |
| Create New API | |
| } @else if (data.mode === 'duplicate') { | |
| Duplicate API | |
| } @else if (data.mode === 'test') { | |
| Test API: {{ data.api.name }} | |
| } @else { | |
| Edit API: {{ data.api.name }} | |
| } | |
| </h2> | |
| <mat-dialog-content> | |
| <mat-tab-group [(selectedIndex)]="activeTabIndex"> | |
| <!-- General Tab --> | |
| <mat-tab label="General"> | |
| <div class="tab-content"> | |
| <mat-form-field appearance="outline"> | |
| <mat-label>Name</mat-label> | |
| <input matInput [formControl]="$any(form.get('name'))" placeholder="e.g., get_flights"> | |
| <mat-hint>Unique identifier for this API</mat-hint> | |
| @if (form.get('name')?.hasError('required') && form.get('name')?.touched) { | |
| <mat-error>Name is required</mat-error> | |
| } | |
| @if (form.get('name')?.hasError('pattern') && form.get('name')?.touched) { | |
| <mat-error>Only alphanumeric and underscore allowed</mat-error> | |
| } | |
| </mat-form-field> | |
| <mat-form-field appearance="outline"> | |
| <mat-label>URL</mat-label> | |
| <input matInput [formControl]="$any(form.get('url'))" placeholder="https://api.example.com/endpoint"> | |
| <mat-hint>Full URL including protocol</mat-hint> | |
| @if (form.get('url')?.hasError('required') && form.get('url')?.touched) { | |
| <mat-error>URL is required</mat-error> | |
| } | |
| @if (form.get('url')?.hasError('pattern') && form.get('url')?.touched) { | |
| <mat-error>Invalid URL format</mat-error> | |
| } | |
| </mat-form-field> | |
| <div class="row"> | |
| <mat-form-field appearance="outline" class="method-field"> | |
| <mat-label>Method</mat-label> | |
| <mat-select [formControl]="$any(form.get('method'))"> | |
| @for (method of httpMethods; track method) { | |
| <mat-option [value]="method">{{ method }}</mat-option> | |
| } | |
| </mat-select> | |
| </mat-form-field> | |
| <mat-form-field appearance="outline" class="timeout-field"> | |
| <mat-label>Timeout (seconds)</mat-label> | |
| <input matInput type="number" [formControl]="$any(form.get('timeout_seconds'))"> | |
| <mat-hint>Request timeout in seconds</mat-hint> | |
| @if (form.get('timeout_seconds')?.hasError('min')) { | |
| <mat-error>Minimum 1 second</mat-error> | |
| } | |
| @if (form.get('timeout_seconds')?.hasError('max')) { | |
| <mat-error>Maximum 300 seconds</mat-error> | |
| } | |
| </mat-form-field> | |
| </div> | |
| <app-json-editor | |
| [formControl]="$any(form.get('body_template'))" | |
| label="Body Template" | |
| placeholder='{"key": "value"}' | |
| hint="JSON template with template variable support" | |
| [rows]="8" | |
| [availableVariables]="getTemplateVariables(false)" | |
| [variableReplacer]="replaceVariablesForValidation"> | |
| </app-json-editor> | |
| <mat-form-field appearance="outline"> | |
| <mat-label>Proxy URL (Optional)</mat-label> | |
| <input matInput [formControl]="$any(form.get('proxy'))" placeholder="http://proxy.example.com:8080"> | |
| <mat-hint>HTTP proxy for this API call</mat-hint> | |
| </mat-form-field> | |
| </div> | |
| </mat-tab> | |
| <!-- Headers Tab --> | |
| <mat-tab label="Headers"> | |
| <div class="tab-content"> | |
| <div class="array-section"> | |
| <div class="section-header"> | |
| <h3>Request Headers</h3> | |
| <button mat-button color="primary" (click)="addHeader()"> | |
| <mat-icon>add</mat-icon> | |
| Add Header | |
| </button> | |
| </div> | |
| @if (headers.length === 0) { | |
| <p class="empty-message">No headers configured. Click "Add Header" to add one.</p> | |
| } | |
| @for (header of headers.controls; track header; let i = $index) { | |
| <div class="array-item" [formGroup]="$any(header)"> | |
| <mat-form-field appearance="outline" class="key-field"> | |
| <mat-label>Header Name</mat-label> | |
| <input matInput formControlName="key" placeholder="Content-Type"> | |
| </mat-form-field> | |
| <mat-form-field appearance="outline" class="value-field"> | |
| <mat-label>Header Value</mat-label> | |
| <input matInput formControlName="value" placeholder="application/json"> | |
| <button mat-icon-button matSuffix [matMenuTriggerFor]="headerMenu"> | |
| <mat-icon>code</mat-icon> | |
| </button> | |
| <mat-menu #headerMenu="matMenu"> | |
| @for (variable of getTemplateVariables(); track variable) { | |
| <button mat-menu-item (click)="insertHeaderValue(i, variable)"> | |
| {{ variable }} | |
| </button> | |
| } | |
| </mat-menu> | |
| </mat-form-field> | |
| <button mat-icon-button color="warn" (click)="removeHeader(i)"> | |
| <mat-icon>delete</mat-icon> | |
| </button> | |
| </div> | |
| } | |
| </div> | |
| </div> | |
| </mat-tab> | |
| <!-- Auth Tab --> | |
| <mat-tab label="Authentication"> | |
| <div class="tab-content" [formGroup]="$any(form.get('auth'))"> | |
| <mat-checkbox formControlName="enabled"> | |
| Enable Authentication | |
| </mat-checkbox> | |
| @if (form.get('auth.enabled')?.value) { | |
| <mat-divider></mat-divider> | |
| <div class="auth-section"> | |
| <h3>Token Configuration</h3> | |
| <mat-form-field appearance="outline"> | |
| <mat-label>Token Endpoint</mat-label> | |
| <input matInput formControlName="token_endpoint" placeholder="https://api.example.com/auth/token"> | |
| <mat-hint>URL to obtain authentication token</mat-hint> | |
| @if (form.get('auth.token_endpoint')?.hasError('required') && form.get('auth.token_endpoint')?.touched) { | |
| <mat-error>Token endpoint is required when auth is enabled</mat-error> | |
| } | |
| </mat-form-field> | |
| <mat-form-field appearance="outline"> | |
| <mat-label>Token Response Path</mat-label> | |
| <input matInput formControlName="response_token_path" placeholder="token"> | |
| <mat-hint>JSON path to extract token from response</mat-hint> | |
| @if (form.get('auth.response_token_path')?.hasError('required') && form.get('auth.response_token_path')?.touched) { | |
| <mat-error>Token path is required when auth is enabled</mat-error> | |
| } | |
| </mat-form-field> | |
| <app-json-editor | |
| formControlName="token_request_body" | |
| label="Token Request Body" | |
| placeholder='{"username": "api_user", "password": "api_pass"}' | |
| hint="JSON body for token request" | |
| [rows]="6" | |
| [availableVariables]="getTemplateVariables()" | |
| [variableReplacer]="replaceVariablesForValidation"> | |
| </app-json-editor> | |
| <mat-divider></mat-divider> | |
| <h3>Token Refresh (Optional)</h3> | |
| <mat-form-field appearance="outline"> | |
| <mat-label>Refresh Endpoint</mat-label> | |
| <input matInput formControlName="token_refresh_endpoint" placeholder="https://api.example.com/auth/refresh"> | |
| <mat-hint>URL to refresh expired token</mat-hint> | |
| </mat-form-field> | |
| <app-json-editor | |
| formControlName="token_refresh_body" | |
| label="Refresh Request Body" | |
| placeholder='{"refresh_token": "your_refresh_token"}' | |
| hint="JSON body for refresh request" | |
| [rows]="4" | |
| [availableVariables]="getTemplateVariables()" | |
| [variableReplacer]="replaceVariablesForValidation"> | |
| </app-json-editor> | |
| </div> | |
| } | |
| </div> | |
| </mat-tab> | |
| <!-- Response Tab --> | |
| <mat-tab label="Response"> | |
| <div class="tab-content"> | |
| <mat-form-field appearance="outline" class="full-width"> | |
| <mat-label>Response Prompt</mat-label> | |
| <textarea matInput | |
| [formControl]="$any(form.get('response_prompt'))" | |
| rows="4" | |
| placeholder="Optional instructions for processing the response"></textarea> | |
| <mat-hint>Instructions for AI to process the response (optional)</mat-hint> | |
| </mat-form-field> | |
| <mat-divider></mat-divider> | |
| <div class="array-section"> | |
| <div class="section-header"> | |
| <h3>Response Mappings</h3> | |
| <button mat-button color="primary" (click)="addResponseMapping()"> | |
| <mat-icon>add</mat-icon> | |
| Add Mapping | |
| </button> | |
| </div> | |
| @if (responseMappings.length === 0) { | |
| <p class="empty-message">No response mappings configured.</p> | |
| } | |
| @for (mapping of responseMappings.controls; track mapping; let i = $index) { | |
| <mat-expansion-panel [formGroup]="$any(mapping)"> | |
| <mat-expansion-panel-header> | |
| <mat-panel-title> | |
| {{ mapping.get('variable_name')?.value || 'New Mapping' }} | |
| </mat-panel-title> | |
| <mat-panel-description> | |
| {{ mapping.get('json_path')?.value || 'Configure mapping' }} | |
| </mat-panel-description> | |
| </mat-expansion-panel-header> | |
| <div class="mapping-content"> | |
| <mat-form-field appearance="outline"> | |
| <mat-label>Variable Name</mat-label> | |
| <input matInput formControlName="variable_name" placeholder="booking_ref"> | |
| <mat-hint>Name to store the extracted value</mat-hint> | |
| @if (mapping.get('variable_name')?.hasError('pattern')) { | |
| <mat-error>Lowercase letters, numbers and underscore only</mat-error> | |
| } | |
| </mat-form-field> | |
| <mat-form-field appearance="outline"> | |
| <mat-label>Caption</mat-label> | |
| <input matInput formControlName="caption" placeholder="Booking Reference"> | |
| <mat-hint>Human-readable description</mat-hint> | |
| </mat-form-field> | |
| <div class="row"> | |
| <mat-form-field appearance="outline" class="type-field"> | |
| <mat-label>Type</mat-label> | |
| <mat-select formControlName="type"> | |
| @for (type of variableTypes; track type) { | |
| <mat-option [value]="type">{{ type }}</mat-option> | |
| } | |
| </mat-select> | |
| </mat-form-field> | |
| <mat-form-field appearance="outline" class="path-field"> | |
| <mat-label>JSON Path</mat-label> | |
| <input matInput formControlName="json_path" placeholder="$.data.bookingReference"> | |
| <mat-hint>JSONPath expression to extract value</mat-hint> | |
| </mat-form-field> | |
| </div> | |
| <button mat-button color="warn" (click)="removeResponseMapping(i)"> | |
| <mat-icon>delete</mat-icon> | |
| Remove Mapping | |
| </button> | |
| </div> | |
| </mat-expansion-panel> | |
| } | |
| </div> | |
| <!-- Retry Settings --> | |
| <mat-divider></mat-divider> | |
| <div class="retry-section" [formGroup]="$any(form.get('retry'))"> | |
| <h3>Retry Settings</h3> | |
| <div class="row"> | |
| <mat-form-field appearance="outline"> | |
| <mat-label>Retry Count</mat-label> | |
| <input matInput type="number" formControlName="retry_count"> | |
| <mat-hint>Number of retry attempts</mat-hint> | |
| </mat-form-field> | |
| <mat-form-field appearance="outline"> | |
| <mat-label>Backoff (seconds)</mat-label> | |
| <input matInput type="number" formControlName="backoff_seconds"> | |
| <mat-hint>Delay between retries</mat-hint> | |
| </mat-form-field> | |
| <mat-form-field appearance="outline"> | |
| <mat-label>Strategy</mat-label> | |
| <mat-select formControlName="strategy"> | |
| @for (strategy of retryStrategies; track strategy) { | |
| <mat-option [value]="strategy">{{ strategy }}</mat-option> | |
| } | |
| </mat-select> | |
| </mat-form-field> | |
| </div> | |
| </div> | |
| </div> | |
| </mat-tab> | |
| <!-- Test Tab --> | |
| <mat-tab label="Test"> | |
| <div class="tab-content"> | |
| <div class="test-section"> | |
| <h3>Test API Call</h3> | |
| <div class="test-controls"> | |
| <button mat-raised-button color="primary" | |
| (click)="testAPI()" | |
| [disabled]="testing || !form.get('url')?.valid || !form.get('method')?.valid"> | |
| @if (testing) { | |
| <ng-container> | |
| <mat-icon class="spin">sync</mat-icon> | |
| Testing... | |
| </ng-container> | |
| } @else { | |
| <ng-container> | |
| <mat-icon>play_arrow</mat-icon> | |
| Test API | |
| </ng-container> | |
| } | |
| </button> | |
| <button mat-button (click)="updateTestRequestJson()"> | |
| <mat-icon>refresh</mat-icon> | |
| Generate Test Data | |
| </button> | |
| </div> | |
| <app-json-editor | |
| [(ngModel)]="testRequestJson" | |
| label="Test Request Body" | |
| placeholder="Enter test request JSON here" | |
| hint="Variables will be replaced with test values" | |
| [rows]="10"> | |
| </app-json-editor> | |
| @if (testResult) { | |
| <mat-divider></mat-divider> | |
| <div class="test-result" [class.success]="testResult.success" [class.error]="!testResult.success"> | |
| <h4>Test Result</h4> | |
| @if (testResult.success) { | |
| <div class="result-status"> | |
| <mat-icon>check_circle</mat-icon> | |
| <span>Success ({{ testResult.status_code }})</span> | |
| </div> | |
| } @else { | |
| <div class="result-status"> | |
| <mat-icon>error</mat-icon> | |
| <span>Failed: {{ testResult.error }}</span> | |
| </div> | |
| } | |
| @if (testResult.response_time) { | |
| <p><strong>Response Time:</strong> {{ testResult.response_time }}ms</p> | |
| } | |
| @if (testResult.response_headers) { | |
| <mat-expansion-panel> | |
| <mat-expansion-panel-header> | |
| <mat-panel-title>Response Headers</mat-panel-title> | |
| </mat-expansion-panel-header> | |
| <mat-form-field appearance="outline" class="full-width"> | |
| <mat-label>Headers</mat-label> | |
| <textarea matInput | |
| [value]="testResult.response_headers | json" | |
| rows="6" | |
| readonly></textarea> | |
| </mat-form-field> | |
| </mat-expansion-panel> | |
| } | |
| @if (testResult.response_body) { | |
| <mat-expansion-panel [expanded]="true"> | |
| <mat-expansion-panel-header> | |
| <mat-panel-title>Response Body</mat-panel-title> | |
| </mat-expansion-panel-header> | |
| <mat-form-field appearance="outline" class="full-width"> | |
| <mat-label>Response</mat-label> | |
| <textarea matInput | |
| [value]="testResult.response_body | json" | |
| rows="12" | |
| readonly></textarea> | |
| </mat-form-field> | |
| </mat-expansion-panel> | |
| } | |
| @if (testResult.request_body) { | |
| <mat-expansion-panel> | |
| <mat-expansion-panel-header> | |
| <mat-panel-title>Request Details</mat-panel-title> | |
| </mat-expansion-panel-header> | |
| <mat-form-field appearance="outline" class="full-width"> | |
| <mat-label>Actual Request Sent</mat-label> | |
| <textarea matInput | |
| [value]="testResult.request_body | json" | |
| rows="8" | |
| readonly></textarea> | |
| </mat-form-field> | |
| @if (testResult.request_headers) { | |
| <mat-form-field appearance="outline" class="full-width"> | |
| <mat-label>Request Headers</mat-label> | |
| <textarea matInput | |
| [value]="testResult.request_headers | json" | |
| rows="6" | |
| readonly></textarea> | |
| </mat-form-field> | |
| } | |
| </mat-expansion-panel> | |
| } | |
| @if (testResult.extracted_values && testResult.extracted_values.length > 0) { | |
| <mat-expansion-panel> | |
| <mat-expansion-panel-header> | |
| <mat-panel-title>Extracted Values</mat-panel-title> | |
| </mat-expansion-panel-header> | |
| <table mat-table [dataSource]="testResult.extracted_values" class="full-width"> | |
| <ng-container matColumnDef="variable"> | |
| <th mat-header-cell *matHeaderCellDef>Variable</th> | |
| <td mat-cell *matCellDef="let element">{{ element.variable_name }}</td> | |
| </ng-container> | |
| <ng-container matColumnDef="value"> | |
| <th mat-header-cell *matHeaderCellDef>Value</th> | |
| <td mat-cell *matCellDef="let element">{{ element.value }}</td> | |
| </ng-container> | |
| <ng-container matColumnDef="type"> | |
| <th mat-header-cell *matHeaderCellDef>Type</th> | |
| <td mat-cell *matCellDef="let element">{{ element.type }}</td> | |
| </ng-container> | |
| <tr mat-header-row *matHeaderRowDef="['variable', 'value', 'type']"></tr> | |
| <tr mat-row *matRowDef="let row; columns: ['variable', 'value', 'type'];"></tr> | |
| </table> | |
| </mat-expansion-panel> | |
| } | |
| </div> | |
| } | |
| </div> | |
| </div> | |
| </mat-tab> | |
| </mat-tab-group> | |
| </mat-dialog-content> | |
| <mat-dialog-actions align="end"> | |
| <button mat-button (click)="cancel()"> | |
| @if (data.mode === 'test') { | |
| Close | |
| } @else { | |
| Cancel | |
| } | |
| </button> | |
| @if (data.mode !== 'test') { | |
| <button mat-raised-button color="primary" | |
| (click)="save()" | |
| [disabled]="saving || form.invalid"> | |
| @if (saving) { | |
| <ng-container> | |
| <mat-icon class="spin">sync</mat-icon> | |
| Saving... | |
| </ng-container> | |
| } @else { | |
| @if (data.mode === 'create' || data.mode === 'duplicate') { | |
| Create | |
| } @else { | |
| Update | |
| } | |
| } | |
| </button> | |
| } | |
| </mat-dialog-actions> |