523 lines
22 KiB
HTML
523 lines
22 KiB
HTML
{% extends "base.html" %}
|
|
{% load static %}
|
|
|
|
{% block title %}API Endpoint Details - Integration{% endblock %}
|
|
|
|
{% block content %}
|
|
<!-- BEGIN breadcrumb -->
|
|
<ol class="breadcrumb float-xl-end">
|
|
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">Dashboard</a></li>
|
|
<li class="breadcrumb-item"><a href="{% url 'integration:dashboard' %}">Integration</a></li>
|
|
<li class="breadcrumb-item"><a href="{% url 'integration:api_endpoint_list' %}">API Endpoints</a></li>
|
|
<li class="breadcrumb-item active">{{ object.name }}</li>
|
|
</ol>
|
|
<!-- END breadcrumb -->
|
|
|
|
<!-- BEGIN page-header -->
|
|
<h1 class="page-header">
|
|
{{ object.name }}
|
|
<small>API Endpoint Details</small>
|
|
</h1>
|
|
<!-- END page-header -->
|
|
|
|
<div class="row">
|
|
<div class="col-xl-8">
|
|
<!-- BEGIN panel -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Endpoint Information</h4>
|
|
<div class="panel-heading-btn">
|
|
<a href="{% url 'integration:api_endpoint_update' object.pk %}" class="btn btn-xs btn-primary me-2">
|
|
<i class="fa fa-edit"></i> Edit
|
|
</a>
|
|
<button class="btn btn-xs btn-success me-2" onclick="testEndpoint()">
|
|
<i class="fa fa-play"></i> Test
|
|
</button>
|
|
<a href="javascript:;" class="btn btn-xs btn-icon btn-default" data-toggle="panel-expand"><i class="fa fa-expand"></i></a>
|
|
</div>
|
|
</div>
|
|
<div class="panel-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<table class="table table-borderless">
|
|
<tr>
|
|
<td class="fw-bold" width="150">Name:</td>
|
|
<td>{{ object.name }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">External System:</td>
|
|
<td>
|
|
<a href="{% url 'integration:external_system_detail' object.external_system.pk %}" class="text-decoration-none">
|
|
{{ object.external_system.name }}
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">URL:</td>
|
|
<td><code>{{ object.url }}</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Method:</td>
|
|
<td>
|
|
<span class="badge bg-{% if object.method == 'GET' %}primary{% elif object.method == 'POST' %}success{% elif object.method == 'PUT' %}warning{% elif object.method == 'DELETE' %}danger{% else %}secondary{% endif %}">
|
|
{{ object.method }}
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Status:</td>
|
|
<td>
|
|
<span class="badge bg-{% if object.status == 'ACTIVE' %}success{% elif object.status == 'INACTIVE' %}secondary{% elif object.status == 'TESTING' %}warning{% else %}danger{% endif %}">
|
|
{{ object.get_status_display }}
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<table class="table table-borderless">
|
|
<tr>
|
|
<td class="fw-bold" width="150">Health Status:</td>
|
|
<td>
|
|
{% if object.health_status %}
|
|
<span class="badge bg-{% if object.health_status == 'HEALTHY' %}success{% elif object.health_status == 'WARNING' %}warning{% else %}danger{% endif %}">
|
|
{{ object.health_status }}
|
|
</span>
|
|
{% else %}
|
|
<span class="text-muted">Not tested</span>
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Last Tested:</td>
|
|
<td>{{ object.last_tested|date:"M d, Y H:i"|default:"Never" }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Response Time:</td>
|
|
<td>{{ object.last_response_time|default:"--" }}ms</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Success Rate:</td>
|
|
<td>{{ object.success_rate|floatformat:1 }}%</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Created:</td>
|
|
<td>{{ object.created_at|date:"M d, Y H:i" }}</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
{% if object.description %}
|
|
<div class="mt-4">
|
|
<h6>Description</h6>
|
|
<div class="bg-light p-3 rounded">
|
|
{{ object.description|linebreaks }}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<!-- END panel -->
|
|
|
|
<!-- BEGIN panel -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Configuration</h4>
|
|
</div>
|
|
<div class="panel-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<h6>Request Configuration</h6>
|
|
<table class="table table-sm table-bordered">
|
|
<tr>
|
|
<td class="fw-bold" width="120">Timeout:</td>
|
|
<td>{{ object.timeout|default:"30" }} seconds</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Retry Count:</td>
|
|
<td>{{ object.retry_count|default:"3" }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Content Type:</td>
|
|
<td><code>{{ object.content_type|default:"application/json" }}</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Authentication:</td>
|
|
<td>{{ object.auth_type|default:"None" }}</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<h6>Response Configuration</h6>
|
|
<table class="table table-sm table-bordered">
|
|
<tr>
|
|
<td class="fw-bold" width="120">Expected Status:</td>
|
|
<td>{{ object.expected_status_code|default:"200" }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Response Format:</td>
|
|
<td>{{ object.response_format|default:"JSON" }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Validation:</td>
|
|
<td>{{ object.validate_response|yesno:"Enabled,Disabled" }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Logging:</td>
|
|
<td>{{ object.log_requests|yesno:"Enabled,Disabled" }}</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
{% if object.headers %}
|
|
<div class="mt-4">
|
|
<h6>Headers</h6>
|
|
<div class="table-responsive">
|
|
<table class="table table-sm table-striped">
|
|
<thead>
|
|
<tr>
|
|
<th>Header</th>
|
|
<th>Value</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for header, value in object.headers.items %}
|
|
<tr>
|
|
<td><code>{{ header }}</code></td>
|
|
<td><code>{{ value }}</code></td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if object.parameters %}
|
|
<div class="mt-4">
|
|
<h6>Parameters</h6>
|
|
<div class="table-responsive">
|
|
<table class="table table-sm table-striped">
|
|
<thead>
|
|
<tr>
|
|
<th>Parameter</th>
|
|
<th>Type</th>
|
|
<th>Required</th>
|
|
<th>Default Value</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for param in object.parameters %}
|
|
<tr>
|
|
<td><code>{{ param.name }}</code></td>
|
|
<td>{{ param.type }}</td>
|
|
<td>{{ param.required|yesno:"Yes,No" }}</td>
|
|
<td><code>{{ param.default_value|default:"--" }}</code></td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<!-- END panel -->
|
|
|
|
<!-- BEGIN panel -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Recent Test Results</h4>
|
|
<div class="panel-heading-btn">
|
|
<button class="btn btn-xs btn-success" onclick="testEndpoint()">
|
|
<i class="fa fa-play"></i> Run Test
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="panel-body">
|
|
{% if recent_tests %}
|
|
<div class="table-responsive">
|
|
<table class="table table-striped">
|
|
<thead>
|
|
<tr>
|
|
<th>Test Date</th>
|
|
<th>Status</th>
|
|
<th>Response Time</th>
|
|
<th>Status Code</th>
|
|
<th>Result</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for test in recent_tests %}
|
|
<tr>
|
|
<td>{{ test.tested_at|date:"M d, Y H:i" }}</td>
|
|
<td>
|
|
<span class="badge bg-{% if test.success %}success{% else %}danger{% endif %}">
|
|
{{ test.success|yesno:"Success,Failed" }}
|
|
</span>
|
|
</td>
|
|
<td>{{ test.response_time }}ms</td>
|
|
<td>{{ test.status_code }}</td>
|
|
<td>
|
|
{% if test.error_message %}
|
|
<span class="text-danger">{{ test.error_message|truncatechars:50 }}</span>
|
|
{% else %}
|
|
<span class="text-success">OK</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
<button class="btn btn-sm btn-outline-primary" onclick="viewTestDetails({{ test.id }})">
|
|
<i class="fa fa-eye"></i>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% else %}
|
|
<div class="text-center text-muted py-3">
|
|
<i class="fa fa-flask fa-2x mb-2"></i>
|
|
<div>No test results available</div>
|
|
<button class="btn btn-primary mt-2" onclick="testEndpoint()">
|
|
<i class="fa fa-play me-2"></i>Run First Test
|
|
</button>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<!-- END panel -->
|
|
</div>
|
|
|
|
<div class="col-xl-4">
|
|
<!-- BEGIN panel -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Quick Actions</h4>
|
|
</div>
|
|
<div class="panel-body">
|
|
<div class="d-grid gap-2">
|
|
<button class="btn btn-success" onclick="testEndpoint()">
|
|
<i class="fa fa-play me-2"></i>Test Endpoint
|
|
</button>
|
|
|
|
<a href="{% url 'integration:api_endpoint_update' object.pk %}" class="btn btn-primary">
|
|
<i class="fa fa-edit me-2"></i>Edit Configuration
|
|
</a>
|
|
|
|
{% if object.status == 'INACTIVE' %}
|
|
<button class="btn btn-warning" onclick="activateEndpoint()">
|
|
<i class="fa fa-check me-2"></i>Activate Endpoint
|
|
</button>
|
|
{% else %}
|
|
<button class="btn btn-secondary" onclick="deactivateEndpoint()">
|
|
<i class="fa fa-pause me-2"></i>Deactivate Endpoint
|
|
</button>
|
|
{% endif %}
|
|
|
|
<button class="btn btn-info" onclick="duplicateEndpoint()">
|
|
<i class="fa fa-copy me-2"></i>Duplicate Endpoint
|
|
</button>
|
|
|
|
<button class="btn btn-outline-primary" onclick="exportConfiguration()">
|
|
<i class="fa fa-download me-2"></i>Export Config
|
|
</button>
|
|
|
|
<hr>
|
|
|
|
<a href="{% url 'integration:api_endpoint_delete' object.pk %}" class="btn btn-danger">
|
|
<i class="fa fa-trash me-2"></i>Delete Endpoint
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- END panel -->
|
|
|
|
<!-- BEGIN panel -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Statistics</h4>
|
|
</div>
|
|
<div class="panel-body">
|
|
<div class="row text-center">
|
|
<div class="col-6">
|
|
<div class="fs-20px fw-bold text-primary">{{ object.total_calls }}</div>
|
|
<div class="small text-muted">Total Calls</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="fs-20px fw-bold text-success">{{ object.successful_calls }}</div>
|
|
<div class="small text-muted">Successful</div>
|
|
</div>
|
|
</div>
|
|
<hr>
|
|
<div class="row text-center">
|
|
<div class="col-6">
|
|
<div class="fs-20px fw-bold text-danger">{{ object.failed_calls }}</div>
|
|
<div class="small text-muted">Failed</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="fs-20px fw-bold text-info">{{ object.avg_response_time }}ms</div>
|
|
<div class="small text-muted">Avg Response</div>
|
|
</div>
|
|
</div>
|
|
<hr>
|
|
<div class="text-center">
|
|
<div class="fs-20px fw-bold text-warning">{{ object.success_rate|floatformat:1 }}%</div>
|
|
<div class="small text-muted">Success Rate</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- END panel -->
|
|
|
|
<!-- BEGIN panel -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">External System</h4>
|
|
</div>
|
|
<div class="panel-body">
|
|
<div class="d-flex align-items-center mb-3">
|
|
<div class="flex-fill">
|
|
<div class="fw-bold">{{ object.external_system.name }}</div>
|
|
<div class="small text-muted">{{ object.external_system.system_type }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<table class="table table-borderless table-sm">
|
|
<tr>
|
|
<td class="fw-bold" width="80">Status:</td>
|
|
<td>
|
|
<span class="badge bg-{% if object.external_system.status == 'ACTIVE' %}success{% else %}secondary{% endif %}">
|
|
{{ object.external_system.get_status_display }}
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Version:</td>
|
|
<td>{{ object.external_system.version|default:"Unknown" }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Base URL:</td>
|
|
<td><code class="small">{{ object.external_system.base_url }}</code></td>
|
|
</tr>
|
|
</table>
|
|
|
|
<div class="d-grid">
|
|
<a href="{% url 'integration:external_system_detail' object.external_system.pk %}" class="btn btn-outline-primary btn-sm">
|
|
<i class="fa fa-external-link-alt me-2"></i>View System Details
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- END panel -->
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block js %}
|
|
<script>
|
|
function testEndpoint() {
|
|
$.ajax({
|
|
url: '{% url "integration:api_endpoint_test" object.pk %}',
|
|
method: 'POST',
|
|
data: {
|
|
'csrfmiddlewaretoken': '{{ csrf_token }}'
|
|
},
|
|
beforeSend: function() {
|
|
toastr.info('Testing endpoint...');
|
|
},
|
|
success: function(response) {
|
|
if (response.success) {
|
|
toastr.success('Endpoint test successful');
|
|
location.reload();
|
|
} else {
|
|
toastr.error('Endpoint test failed: ' + response.error);
|
|
}
|
|
},
|
|
error: function() {
|
|
toastr.error('Failed to test endpoint');
|
|
}
|
|
});
|
|
}
|
|
|
|
function activateEndpoint() {
|
|
$.ajax({
|
|
url: '{% url "integration:api_endpoint_activate" object.pk %}',
|
|
method: 'POST',
|
|
data: {
|
|
'csrfmiddlewaretoken': '{{ csrf_token }}'
|
|
},
|
|
success: function(response) {
|
|
if (response.success) {
|
|
toastr.success('Endpoint activated successfully');
|
|
location.reload();
|
|
} else {
|
|
toastr.error('Failed to activate endpoint');
|
|
}
|
|
},
|
|
error: function() {
|
|
toastr.error('An error occurred while activating the endpoint');
|
|
}
|
|
});
|
|
}
|
|
|
|
function deactivateEndpoint() {
|
|
if (confirm('Deactivate this endpoint?')) {
|
|
$.ajax({
|
|
url: '{% url "integration:api_endpoint_deactivate" object.pk %}',
|
|
method: 'POST',
|
|
data: {
|
|
'csrfmiddlewaretoken': '{{ csrf_token }}'
|
|
},
|
|
success: function(response) {
|
|
if (response.success) {
|
|
toastr.success('Endpoint deactivated successfully');
|
|
location.reload();
|
|
} else {
|
|
toastr.error('Failed to deactivate endpoint');
|
|
}
|
|
},
|
|
error: function() {
|
|
toastr.error('An error occurred while deactivating the endpoint');
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function duplicateEndpoint() {
|
|
if (confirm('Create a copy of this endpoint?')) {
|
|
$.ajax({
|
|
url: '{% url "integration:api_endpoint_duplicate" object.pk %}',
|
|
method: 'POST',
|
|
data: {
|
|
'csrfmiddlewaretoken': '{{ csrf_token }}'
|
|
},
|
|
success: function(response) {
|
|
if (response.success) {
|
|
toastr.success('Endpoint duplicated successfully');
|
|
window.location.href = response.redirect_url;
|
|
} else {
|
|
toastr.error('Failed to duplicate endpoint');
|
|
}
|
|
},
|
|
error: function() {
|
|
toastr.error('An error occurred while duplicating the endpoint');
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function exportConfiguration() {
|
|
window.open('{% url "integration:api_endpoint_export_config" object.pk %}');
|
|
}
|
|
|
|
function viewTestDetails(testId) {
|
|
// Open test details in modal or new page
|
|
window.open('{% url "integration:api_endpoint_test_detail" 0 %}'.replace('0', testId));
|
|
}
|
|
</script>
|
|
{% endblock %}
|
|
|