mirror of https://github.com/interlegis/sapl.git
Browse Source
Remove a página de documentação customizada (/api/doc/) e adiciona card de atalho para Swagger UI e ReDoc na tela inicial do administrador. Corrige ReDoc em branco fixando versão estável do CDN.pull/3858/head
5 changed files with 19 additions and 581 deletions
@ -1,514 +0,0 @@ |
|||||
{% extends "base.html" %} |
|
||||
{% load i18n %} |
|
||||
|
|
||||
{% block head_title %}API - Documentacao{% endblock %} |
|
||||
|
|
||||
{% block base_content %} |
|
||||
<style> |
|
||||
.api-doc { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; } |
|
||||
.api-doc h2 { border-bottom: 2px solid #dee2e6; padding-bottom: .5rem; } |
|
||||
.api-app-section { margin-bottom: 1.5rem; } |
|
||||
.api-app-section h4 { cursor: pointer; padding: .5rem .75rem; background: #f8f9fa; border-radius: .25rem; margin-bottom: 0; } |
|
||||
.api-app-section h4:hover { background: #e9ecef; } |
|
||||
.api-app-section h4 .badge { font-size: .75rem; } |
|
||||
.api-endpoint-list { display: none; } |
|
||||
.api-endpoint-list.show { display: block; } |
|
||||
.api-endpoint { border: 1px solid #dee2e6; border-radius: .25rem; margin: .5rem 0; } |
|
||||
.api-endpoint-header { padding: .5rem .75rem; cursor: pointer; display: flex; align-items: center; gap: .5rem; background: #fff; } |
|
||||
.api-endpoint-header:hover { background: #f8f9fa; } |
|
||||
.api-endpoint-body { display: none; padding: 1rem; border-top: 1px solid #dee2e6; background: #fdfdfe; } |
|
||||
.api-endpoint-body.show { display: block; } |
|
||||
.method-badge { display: inline-block; font-size: .7rem; font-weight: 700; padding: .15rem .4rem; border-radius: .2rem; color: #fff; min-width: 50px; text-align: center; } |
|
||||
.method-get { background: #28a745; } |
|
||||
.method-post { background: #007bff; } |
|
||||
.method-put { background: #fd7e14; } |
|
||||
.method-patch { background: #6f42c1; } |
|
||||
.method-delete { background: #dc3545; } |
|
||||
.method-head, .method-options { background: #6c757d; } |
|
||||
.endpoint-url { font-family: SFMono-Regular, Menlo, Monaco, monospace; font-size: .85rem; color: #333; } |
|
||||
.response-area { background: #1e1e1e; color: #d4d4d4; border-radius: .25rem; padding: 1rem; font-family: SFMono-Regular, Menlo, Monaco, monospace; font-size: .8rem; max-height: 400px; overflow: auto; white-space: pre-wrap; word-break: break-all; } |
|
||||
.param-input { font-size: .85rem; } |
|
||||
.auth-panel { background: #f0f7ff; border: 1px solid #b8daff; border-radius: .25rem; padding: 1rem; margin-bottom: 1.5rem; } |
|
||||
.filter-input { margin-bottom: 1rem; } |
|
||||
.btn-send { min-width: 100px; } |
|
||||
.status-code { font-weight: 700; } |
|
||||
.status-code.s2xx { color: #28a745; } |
|
||||
.status-code.s4xx { color: #fd7e14; } |
|
||||
.status-code.s5xx { color: #dc3545; } |
|
||||
.spinner-border-sm { width: 1rem; height: 1rem; border-width: .15em; } |
|
||||
.readonly-methods .method-badge { opacity: .5; font-size: .6rem; } |
|
||||
.tab-pane { padding-top: 1rem; } |
|
||||
.json-editor { width: 100%; min-height: 120px; font-family: SFMono-Regular, Menlo, Monaco, monospace; font-size: .8rem; border: 1px solid #ced4da; border-radius: .25rem; padding: .5rem; } |
|
||||
.quick-stats { display: flex; gap: 1rem; flex-wrap: wrap; margin-bottom: 1rem; } |
|
||||
.quick-stats .card { min-width: 120px; text-align: center; } |
|
||||
.quick-stats .card-body { padding: .75rem; } |
|
||||
.quick-stats .display-6 { font-size: 1.5rem; font-weight: 700; } |
|
||||
</style> |
|
||||
|
|
||||
<div class="api-doc"> |
|
||||
<div class="d-flex justify-content-between align-items-center mb-3"> |
|
||||
<h2 class="mb-0">API REST - Documentacao</h2> |
|
||||
<div> |
|
||||
<a href="{% url 'sapl.api:swagger_ui_schema_api' %}" class="btn btn-sm btn-outline-secondary" target="_blank">Swagger UI</a> |
|
||||
<a href="{% url 'sapl.api:redoc_schema_api' %}" class="btn btn-sm btn-outline-secondary" target="_blank">ReDoc</a> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<div class="quick-stats"> |
|
||||
<div class="card"> |
|
||||
<div class="card-body"> |
|
||||
<div class="display-6" id="stat-apps">-</div> |
|
||||
<small class="text-muted">Apps</small> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="card"> |
|
||||
<div class="card-body"> |
|
||||
<div class="display-6" id="stat-models">-</div> |
|
||||
<small class="text-muted">Models</small> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="card"> |
|
||||
<div class="card-body"> |
|
||||
<div class="display-6" id="stat-endpoints">-</div> |
|
||||
<small class="text-muted">Endpoints</small> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<!-- Auth Panel --> |
|
||||
<div class="auth-panel"> |
|
||||
<h5>Autenticacao</h5> |
|
||||
<div class="row"> |
|
||||
<div class="col-md-6"> |
|
||||
<div class="form-group"> |
|
||||
<label for="auth-token"><strong>Token</strong> <small class="text-muted">(Authorization: Token xxx)</small></label> |
|
||||
<div class="input-group"> |
|
||||
<input type="text" id="auth-token" class="form-control form-control-sm" placeholder="Cole seu token aqui..."> |
|
||||
<div class="input-group-append"> |
|
||||
<button class="btn btn-sm btn-outline-secondary" onclick="clearToken()">Limpar</button> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="col-md-6"> |
|
||||
<div class="form-group"> |
|
||||
<label><strong>Obter token</strong></label> |
|
||||
<div class="input-group"> |
|
||||
<input type="text" id="auth-username" class="form-control form-control-sm" placeholder="Usuario"> |
|
||||
<input type="password" id="auth-password" class="form-control form-control-sm" placeholder="Senha"> |
|
||||
<div class="input-group-append"> |
|
||||
<button class="btn btn-sm btn-primary" onclick="obtainToken()">Obter Token</button> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div id="auth-message" class="mt-2" style="display:none;"></div> |
|
||||
</div> |
|
||||
|
|
||||
<!-- Search --> |
|
||||
<div class="filter-input"> |
|
||||
<input type="text" id="search-endpoints" class="form-control" placeholder="Buscar endpoints... (ex: parlamentar, sessao, materia)"> |
|
||||
</div> |
|
||||
|
|
||||
<!-- Endpoints --> |
|
||||
<div id="api-endpoints-container"> |
|
||||
<div class="text-center py-5"> |
|
||||
<div class="spinner-border" role="status"></div> |
|
||||
<p class="mt-2 text-muted">Carregando endpoints...</p> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<script> |
|
||||
document.addEventListener('DOMContentLoaded', function() { |
|
||||
fetch('{% url "sapl.api:api_doc_data" %}') |
|
||||
.then(r => r.json()) |
|
||||
.then(data => renderEndpoints(data)) |
|
||||
.catch(err => { |
|
||||
document.getElementById('api-endpoints-container').innerHTML = |
|
||||
'<div class="alert alert-danger">Erro ao carregar endpoints: ' + err.message + '</div>'; |
|
||||
}); |
|
||||
|
|
||||
document.getElementById('search-endpoints').addEventListener('input', function() { |
|
||||
filterEndpoints(this.value.toLowerCase()); |
|
||||
}); |
|
||||
}); |
|
||||
|
|
||||
function getBaseUrl() { |
|
||||
return window.location.origin; |
|
||||
} |
|
||||
|
|
||||
function getAuthHeaders() { |
|
||||
var headers = {'Content-Type': 'application/json'}; |
|
||||
var token = document.getElementById('auth-token').value.trim(); |
|
||||
if (token) { |
|
||||
headers['Authorization'] = 'Token ' + token; |
|
||||
} |
|
||||
return headers; |
|
||||
} |
|
||||
|
|
||||
function obtainToken() { |
|
||||
var username = document.getElementById('auth-username').value; |
|
||||
var password = document.getElementById('auth-password').value; |
|
||||
var msgEl = document.getElementById('auth-message'); |
|
||||
|
|
||||
fetch(getBaseUrl() + '/api/auth/token', { |
|
||||
method: 'POST', |
|
||||
headers: {'Content-Type': 'application/json'}, |
|
||||
body: JSON.stringify({username: username, password: password}) |
|
||||
}) |
|
||||
.then(r => r.json().then(data => ({status: r.status, data: data}))) |
|
||||
.then(result => { |
|
||||
if (result.data.token) { |
|
||||
document.getElementById('auth-token').value = result.data.token; |
|
||||
msgEl.className = 'mt-2 alert alert-success'; |
|
||||
msgEl.textContent = 'Token obtido com sucesso!'; |
|
||||
} else { |
|
||||
msgEl.className = 'mt-2 alert alert-danger'; |
|
||||
msgEl.textContent = 'Erro: ' + JSON.stringify(result.data); |
|
||||
} |
|
||||
msgEl.style.display = 'block'; |
|
||||
setTimeout(function() { msgEl.style.display = 'none'; }, 5000); |
|
||||
}) |
|
||||
.catch(err => { |
|
||||
msgEl.className = 'mt-2 alert alert-danger'; |
|
||||
msgEl.textContent = 'Erro de conexao: ' + err.message; |
|
||||
msgEl.style.display = 'block'; |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
function clearToken() { |
|
||||
document.getElementById('auth-token').value = ''; |
|
||||
} |
|
||||
|
|
||||
function renderEndpoints(data) { |
|
||||
var container = document.getElementById('api-endpoints-container'); |
|
||||
var html = ''; |
|
||||
var totalModels = 0; |
|
||||
var totalEndpoints = 0; |
|
||||
var appCount = 0; |
|
||||
|
|
||||
var apps = data.apps; |
|
||||
var appNames = Object.keys(apps).sort(); |
|
||||
|
|
||||
appNames.forEach(function(appName) { |
|
||||
appCount++; |
|
||||
var models = apps[appName]; |
|
||||
var modelNames = Object.keys(models).sort(); |
|
||||
var modelCount = modelNames.length; |
|
||||
totalModels += modelCount; |
|
||||
|
|
||||
html += '<div class="api-app-section" data-app="' + appName + '">'; |
|
||||
html += '<h4 onclick="toggleApp(this)">'; |
|
||||
html += '<i class="fa fa-chevron-right mr-2"></i>'; |
|
||||
html += appName; |
|
||||
html += ' <span class="badge badge-secondary">' + modelCount + ' models</span>'; |
|
||||
html += '</h4>'; |
|
||||
html += '<div class="api-endpoint-list">'; |
|
||||
|
|
||||
modelNames.forEach(function(modelName) { |
|
||||
var info = models[modelName]; |
|
||||
var methods = info.methods || ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']; |
|
||||
var url = info.url; |
|
||||
var actions = info.actions || []; |
|
||||
totalEndpoints++; |
|
||||
|
|
||||
html += '<div class="api-endpoint" data-search="' + appName + ' ' + modelName + ' ' + url + '">'; |
|
||||
|
|
||||
// Header |
|
||||
html += '<div class="api-endpoint-header" onclick="toggleEndpoint(this)">'; |
|
||||
methods.forEach(function(m) { |
|
||||
html += '<span class="method-badge method-' + m.toLowerCase() + '">' + m + '</span>'; |
|
||||
}); |
|
||||
html += '<span class="endpoint-url">/api/' + url + '/</span>'; |
|
||||
html += '<span class="text-muted ml-auto" style="font-size:.8rem;">' + modelName + '</span>'; |
|
||||
html += '</div>'; |
|
||||
|
|
||||
// Body |
|
||||
html += '<div class="api-endpoint-body">'; |
|
||||
html += buildEndpointTester(appName, modelName, url, methods, actions); |
|
||||
html += '</div>'; |
|
||||
|
|
||||
html += '</div>'; |
|
||||
|
|
||||
// Custom actions |
|
||||
actions.forEach(function(act) { |
|
||||
totalEndpoints++; |
|
||||
var actMethods = act.methods || ['GET']; |
|
||||
var actUrl = act.url; |
|
||||
|
|
||||
html += '<div class="api-endpoint" data-search="' + appName + ' ' + modelName + ' ' + actUrl + ' ' + act.name + '">'; |
|
||||
html += '<div class="api-endpoint-header" onclick="toggleEndpoint(this)">'; |
|
||||
actMethods.forEach(function(m) { |
|
||||
html += '<span class="method-badge method-' + m.toLowerCase() + '">' + m + '</span>'; |
|
||||
}); |
|
||||
html += '<span class="endpoint-url">/api/' + actUrl + '</span>'; |
|
||||
html += '<span class="badge badge-info ml-2">action</span>'; |
|
||||
html += '<span class="text-muted ml-auto" style="font-size:.8rem;">' + act.name + '</span>'; |
|
||||
html += '</div>'; |
|
||||
|
|
||||
html += '<div class="api-endpoint-body">'; |
|
||||
html += buildActionTester(actUrl, actMethods); |
|
||||
html += '</div>'; |
|
||||
|
|
||||
html += '</div>'; |
|
||||
}); |
|
||||
}); |
|
||||
|
|
||||
html += '</div></div>'; |
|
||||
}); |
|
||||
|
|
||||
container.innerHTML = html; |
|
||||
|
|
||||
document.getElementById('stat-apps').textContent = appCount; |
|
||||
document.getElementById('stat-models').textContent = totalModels; |
|
||||
document.getElementById('stat-endpoints').textContent = totalEndpoints; |
|
||||
} |
|
||||
|
|
||||
function buildEndpointTester(appName, modelName, url, methods, actions) { |
|
||||
var id = appName + '_' + modelName; |
|
||||
var fullUrl = '/api/' + url + '/'; |
|
||||
|
|
||||
var h = ''; |
|
||||
h += '<ul class="nav nav-tabs" role="tablist">'; |
|
||||
h += '<li class="nav-item"><a class="nav-link active" data-toggle="tab" href="#tab-list-' + id + '">Listar (GET)</a></li>'; |
|
||||
if (methods.indexOf('POST') !== -1) { |
|
||||
h += '<li class="nav-item"><a class="nav-link" data-toggle="tab" href="#tab-create-' + id + '">Criar (POST)</a></li>'; |
|
||||
} |
|
||||
if (methods.indexOf('PUT') !== -1) { |
|
||||
h += '<li class="nav-item"><a class="nav-link" data-toggle="tab" href="#tab-update-' + id + '">Atualizar (PUT)</a></li>'; |
|
||||
} |
|
||||
if (methods.indexOf('DELETE') !== -1) { |
|
||||
h += '<li class="nav-item"><a class="nav-link" data-toggle="tab" href="#tab-delete-' + id + '">Deletar (DELETE)</a></li>'; |
|
||||
} |
|
||||
h += '</ul>'; |
|
||||
|
|
||||
h += '<div class="tab-content">'; |
|
||||
|
|
||||
// GET tab |
|
||||
h += '<div class="tab-pane active" id="tab-list-' + id + '">'; |
|
||||
h += '<div class="form-row align-items-end mb-2">'; |
|
||||
h += '<div class="col-md-2"><label class="mb-0"><small>ID (opcional)</small></label>'; |
|
||||
h += '<input type="text" class="form-control form-control-sm param-input" id="pk-' + id + '" placeholder="pk"></div>'; |
|
||||
h += '<div class="col-md-3"><label class="mb-0"><small>Filtros</small></label>'; |
|
||||
h += '<input type="text" class="form-control form-control-sm param-input" id="filter-' + id + '" placeholder="campo=valor&campo2=valor2"></div>'; |
|
||||
h += '<div class="col-md-2"><label class="mb-0"><small>Pagina</small></label>'; |
|
||||
h += '<input type="number" class="form-control form-control-sm param-input" id="page-' + id + '" placeholder="1" value="1" min="1"></div>'; |
|
||||
h += '<div class="col-md-2"><label class="mb-0"><small>Itens/pag</small></label>'; |
|
||||
h += '<input type="number" class="form-control form-control-sm param-input" id="psize-' + id + '" value="10" min="1" max="100"></div>'; |
|
||||
h += '<div class="col-md-3">'; |
|
||||
h += '<button class="btn btn-sm btn-success btn-send" onclick="sendGet(\'' + id + '\', \'' + fullUrl + '\')">Enviar GET</button></div>'; |
|
||||
h += '</div>'; |
|
||||
h += '<div class="response-area" id="resp-list-' + id + '">Clique em "Enviar GET" para testar...</div>'; |
|
||||
h += '</div>'; |
|
||||
|
|
||||
// POST tab |
|
||||
if (methods.indexOf('POST') !== -1) { |
|
||||
h += '<div class="tab-pane" id="tab-create-' + id + '">'; |
|
||||
h += '<p class="text-muted"><small>Envie um JSON com os campos do model. Os campos obrigatorios variam por model.</small></p>'; |
|
||||
h += '<textarea class="json-editor" id="body-create-' + id + '" placeholder=\'{"campo": "valor"}\'></textarea>'; |
|
||||
h += '<button class="btn btn-sm btn-primary btn-send mt-2" onclick="sendPost(\'' + id + '\', \'' + fullUrl + '\')">Enviar POST</button>'; |
|
||||
h += '<div class="response-area mt-2" id="resp-create-' + id + '">...</div>'; |
|
||||
h += '</div>'; |
|
||||
} |
|
||||
|
|
||||
// PUT tab |
|
||||
if (methods.indexOf('PUT') !== -1) { |
|
||||
h += '<div class="tab-pane" id="tab-update-' + id + '">'; |
|
||||
h += '<div class="form-row mb-2">'; |
|
||||
h += '<div class="col-md-2"><label class="mb-0"><small>ID (obrigatorio)</small></label>'; |
|
||||
h += '<input type="text" class="form-control form-control-sm" id="pk-update-' + id + '" placeholder="pk"></div>'; |
|
||||
h += '</div>'; |
|
||||
h += '<textarea class="json-editor" id="body-update-' + id + '" placeholder=\'{"campo": "novo_valor"}\'></textarea>'; |
|
||||
h += '<button class="btn btn-sm btn-warning btn-send mt-2" onclick="sendPut(\'' + id + '\', \'' + fullUrl + '\')">Enviar PUT</button>'; |
|
||||
h += '<button class="btn btn-sm btn-outline-secondary btn-send mt-2 ml-1" onclick="sendPatch(\'' + id + '\', \'' + fullUrl + '\')">PATCH</button>'; |
|
||||
h += '<div class="response-area mt-2" id="resp-update-' + id + '">...</div>'; |
|
||||
h += '</div>'; |
|
||||
} |
|
||||
|
|
||||
// DELETE tab |
|
||||
if (methods.indexOf('DELETE') !== -1) { |
|
||||
h += '<div class="tab-pane" id="tab-delete-' + id + '">'; |
|
||||
h += '<div class="form-row align-items-end mb-2">'; |
|
||||
h += '<div class="col-md-3"><label class="mb-0"><small>ID (obrigatorio)</small></label>'; |
|
||||
h += '<input type="text" class="form-control form-control-sm" id="pk-delete-' + id + '" placeholder="pk"></div>'; |
|
||||
h += '<div class="col-md-3">'; |
|
||||
h += '<button class="btn btn-sm btn-danger btn-send" onclick="sendDelete(\'' + id + '\', \'' + fullUrl + '\')">Enviar DELETE</button></div>'; |
|
||||
h += '</div>'; |
|
||||
h += '<div class="response-area mt-2" id="resp-delete-' + id + '">...</div>'; |
|
||||
h += '</div>'; |
|
||||
} |
|
||||
|
|
||||
h += '</div>'; |
|
||||
return h; |
|
||||
} |
|
||||
|
|
||||
function buildActionTester(actUrl, methods) { |
|
||||
var id = actUrl.replace(/[^a-zA-Z0-9]/g, '_'); |
|
||||
var fullUrl = '/api/' + actUrl; |
|
||||
var method = methods[0] || 'GET'; |
|
||||
|
|
||||
var h = ''; |
|
||||
h += '<div class="form-row align-items-end mb-2">'; |
|
||||
h += '<div class="col-md-6"><label class="mb-0"><small>URL completa</small></label>'; |
|
||||
h += '<input type="text" class="form-control form-control-sm" id="url-' + id + '" value="' + fullUrl + '"></div>'; |
|
||||
h += '<div class="col-md-3"><label class="mb-0"><small>Filtros (query string)</small></label>'; |
|
||||
h += '<input type="text" class="form-control form-control-sm" id="qs-' + id + '" placeholder="param=valor"></div>'; |
|
||||
h += '<div class="col-md-3">'; |
|
||||
|
|
||||
if (method === 'GET') { |
|
||||
h += '<button class="btn btn-sm btn-success btn-send" onclick="sendAction(\'' + id + '\', \'GET\')">GET</button>'; |
|
||||
} else { |
|
||||
h += '<button class="btn btn-sm btn-primary btn-send" onclick="sendAction(\'' + id + '\', \'' + method + '\')"> ' + method + '</button>'; |
|
||||
} |
|
||||
h += '</div></div>'; |
|
||||
|
|
||||
if (method !== 'GET') { |
|
||||
h += '<textarea class="json-editor" id="body-' + id + '" placeholder=\'{"campo": "valor"}\'></textarea>'; |
|
||||
} |
|
||||
|
|
||||
h += '<div class="response-area mt-2" id="resp-' + id + '">...</div>'; |
|
||||
return h; |
|
||||
} |
|
||||
|
|
||||
// API call functions |
|
||||
function sendGet(id, url) { |
|
||||
var pk = document.getElementById('pk-' + id).value.trim(); |
|
||||
var filter = document.getElementById('filter-' + id).value.trim(); |
|
||||
var page = document.getElementById('page-' + id).value || '1'; |
|
||||
var psize = document.getElementById('psize-' + id).value || '10'; |
|
||||
|
|
||||
var fullUrl = getBaseUrl() + url; |
|
||||
if (pk) fullUrl += pk + '/'; |
|
||||
|
|
||||
var params = 'page=' + page + '&page_size=' + psize; |
|
||||
if (filter) params += '&' + filter; |
|
||||
if (!pk) fullUrl += '?' + params; |
|
||||
|
|
||||
doFetch('resp-list-' + id, fullUrl, 'GET'); |
|
||||
} |
|
||||
|
|
||||
function sendPost(id, url) { |
|
||||
var body = document.getElementById('body-create-' + id).value; |
|
||||
doFetch('resp-create-' + id, getBaseUrl() + url, 'POST', body); |
|
||||
} |
|
||||
|
|
||||
function sendPut(id, url) { |
|
||||
var pk = document.getElementById('pk-update-' + id).value.trim(); |
|
||||
if (!pk) { alert('Informe o ID para atualizar.'); return; } |
|
||||
var body = document.getElementById('body-update-' + id).value; |
|
||||
doFetch('resp-update-' + id, getBaseUrl() + url + pk + '/', 'PUT', body); |
|
||||
} |
|
||||
|
|
||||
function sendPatch(id, url) { |
|
||||
var pk = document.getElementById('pk-update-' + id).value.trim(); |
|
||||
if (!pk) { alert('Informe o ID para atualizar.'); return; } |
|
||||
var body = document.getElementById('body-update-' + id).value; |
|
||||
doFetch('resp-update-' + id, getBaseUrl() + url + pk + '/', 'PATCH', body); |
|
||||
} |
|
||||
|
|
||||
function sendDelete(id, url) { |
|
||||
var pk = document.getElementById('pk-delete-' + id).value.trim(); |
|
||||
if (!pk) { alert('Informe o ID para deletar.'); return; } |
|
||||
if (!confirm('Confirma exclusao do registro #' + pk + '?')) return; |
|
||||
doFetch('resp-delete-' + id, getBaseUrl() + url + pk + '/', 'DELETE'); |
|
||||
} |
|
||||
|
|
||||
function sendAction(id, method) { |
|
||||
var url = document.getElementById('url-' + id).value.trim(); |
|
||||
var qs = document.getElementById('qs-' + id).value.trim(); |
|
||||
var fullUrl = getBaseUrl() + url; |
|
||||
if (qs) fullUrl += (fullUrl.indexOf('?') === -1 ? '?' : '&') + qs; |
|
||||
|
|
||||
var body = null; |
|
||||
var bodyEl = document.getElementById('body-' + id); |
|
||||
if (bodyEl) body = bodyEl.value; |
|
||||
|
|
||||
doFetch('resp-' + id, fullUrl, method, body); |
|
||||
} |
|
||||
|
|
||||
function doFetch(respId, url, method, body) { |
|
||||
var respEl = document.getElementById(respId); |
|
||||
respEl.innerHTML = '<span class="spinner-border spinner-border-sm"></span> Enviando...'; |
|
||||
|
|
||||
var opts = { |
|
||||
method: method, |
|
||||
headers: getAuthHeaders() |
|
||||
}; |
|
||||
|
|
||||
if (body && method !== 'GET' && method !== 'DELETE') { |
|
||||
try { |
|
||||
opts.body = JSON.stringify(JSON.parse(body)); |
|
||||
} catch(e) { |
|
||||
opts.body = body; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
var startTime = Date.now(); |
|
||||
|
|
||||
fetch(url, opts) |
|
||||
.then(function(r) { |
|
||||
var elapsed = Date.now() - startTime; |
|
||||
var statusClass = r.status < 300 ? 's2xx' : (r.status < 500 ? 's4xx' : 's5xx'); |
|
||||
|
|
||||
return r.text().then(function(text) { |
|
||||
var formatted; |
|
||||
try { |
|
||||
formatted = JSON.stringify(JSON.parse(text), null, 2); |
|
||||
} catch(e) { |
|
||||
formatted = text; |
|
||||
} |
|
||||
|
|
||||
respEl.innerHTML = '<span class="status-code ' + statusClass + '">' + |
|
||||
method + ' ' + r.status + ' ' + r.statusText + '</span>' + |
|
||||
' <span class="text-muted">(' + elapsed + 'ms)</span>\n' + |
|
||||
'<span class="text-muted">' + url + '</span>\n\n' + formatted; |
|
||||
}); |
|
||||
}) |
|
||||
.catch(function(err) { |
|
||||
respEl.innerHTML = '<span class="status-code s5xx">ERRO</span> ' + err.message; |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
function toggleApp(el) { |
|
||||
var list = el.nextElementSibling; |
|
||||
var icon = el.querySelector('i'); |
|
||||
list.classList.toggle('show'); |
|
||||
icon.classList.toggle('fa-chevron-right'); |
|
||||
icon.classList.toggle('fa-chevron-down'); |
|
||||
} |
|
||||
|
|
||||
function toggleEndpoint(el) { |
|
||||
el.nextElementSibling.classList.toggle('show'); |
|
||||
} |
|
||||
|
|
||||
function filterEndpoints(query) { |
|
||||
if (!query) { |
|
||||
document.querySelectorAll('.api-app-section').forEach(function(s) { s.style.display = ''; }); |
|
||||
document.querySelectorAll('.api-endpoint').forEach(function(e) { e.style.display = ''; }); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
document.querySelectorAll('.api-app-section').forEach(function(section) { |
|
||||
var hasMatch = false; |
|
||||
section.querySelectorAll('.api-endpoint').forEach(function(ep) { |
|
||||
var searchText = ep.getAttribute('data-search') || ''; |
|
||||
if (searchText.toLowerCase().indexOf(query) !== -1) { |
|
||||
ep.style.display = ''; |
|
||||
hasMatch = true; |
|
||||
} else { |
|
||||
ep.style.display = 'none'; |
|
||||
} |
|
||||
}); |
|
||||
section.style.display = hasMatch ? '' : 'none'; |
|
||||
if (hasMatch) { |
|
||||
section.querySelector('.api-endpoint-list').classList.add('show'); |
|
||||
var icon = section.querySelector('h4 i'); |
|
||||
icon.classList.remove('fa-chevron-right'); |
|
||||
icon.classList.add('fa-chevron-down'); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
</script> |
|
||||
{% endblock base_content %} |
|
||||
Loading…
Reference in new issue