flare / flare-ui /src /app /dialogs /api-edit-dialog /api-edit-dialog.component.html
ciyidogan's picture
Upload 118 files
9f79da5 verified
<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>