Add ability to test http functions with postman like interface
This commit is contained in:
14
app.py
14
app.py
@@ -5,7 +5,7 @@ from jinja2_fragments import render_block
|
||||
from flask_htmx import HTMX
|
||||
import requests
|
||||
from db import DataBase
|
||||
from services import create_http_functions_view_model
|
||||
from services import create_http_function_view_model, create_http_functions_view_model
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config.from_pyfile('config.py')
|
||||
@@ -32,6 +32,7 @@ DEFAULT_SCRIPT = """async (req) => {
|
||||
<td>${value}</td>
|
||||
</tr>`)}
|
||||
</table>
|
||||
<pre>${JSON.stringify(req.body, null, 2)}</pre>
|
||||
</div>`)
|
||||
}"""
|
||||
|
||||
@@ -59,9 +60,14 @@ def map_isolator_response_to_flask_response(response):
|
||||
def home():
|
||||
return render_template("home.html", script=DEFAULT_SCRIPT)
|
||||
|
||||
@ app.route("/client", methods=["GET"])
|
||||
def client():
|
||||
return render_template("client.html")
|
||||
@ app.route("/client/<function>", methods=["GET"])
|
||||
def client(function):
|
||||
http_function = db.get_http_function(function)
|
||||
if not http_function:
|
||||
return jsonify({'error': 'Function not found'}), 404
|
||||
|
||||
http_function = create_http_function_view_model(http_function)
|
||||
return render_template("client.html", **http_function)
|
||||
|
||||
@ app.route("/dashboard", methods=["GET"])
|
||||
def dashboard():
|
||||
|
||||
25
services.py
25
services.py
@@ -1,18 +1,25 @@
|
||||
def create_http_function_view_model(http_function):
|
||||
# Base URL for the function invocation
|
||||
base_url = "https://function.peterstockings.com/f/"
|
||||
|
||||
name = http_function['name']
|
||||
function_view_model = {
|
||||
"id": http_function['id'],
|
||||
"name": name,
|
||||
"script_content": http_function['script_content'],
|
||||
"url": f"{base_url}{name}",
|
||||
"invoke_count": http_function['invoked_count']
|
||||
}
|
||||
|
||||
return function_view_model
|
||||
|
||||
def create_http_functions_view_model(http_functions):
|
||||
# Base URL for the function invocation
|
||||
base_url = "https://function.peterstockings.com/f/"
|
||||
|
||||
view_model = []
|
||||
|
||||
for function in http_functions:
|
||||
name = function['name']
|
||||
function_view_model = {
|
||||
"id": function['id'],
|
||||
"name": name,
|
||||
"script_content": function['script_content'],
|
||||
"url": f"{base_url}{name}",
|
||||
"invoke_count": function['invoked_count']
|
||||
}
|
||||
function_view_model = create_http_function_view_model(function)
|
||||
view_model.append(function_view_model)
|
||||
|
||||
return view_model
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
|
||||
<div class="mx-auto w-full">
|
||||
<form class="flex"><select
|
||||
class="px-4 py-2 border rounded-md border-gray-300 hover:border-orange-500 focus:outline-none bg-gray-100">
|
||||
class="px-4 py-2 border rounded-md border-gray-300 hover:border-orange-500 focus:outline-none bg-gray-100"
|
||||
name="method">
|
||||
<option value="GET">GET</option>
|
||||
<option value="POST">POST</option>
|
||||
<option value="PUT">PUT</option>
|
||||
@@ -12,22 +13,22 @@
|
||||
<option value="DELETE">DELETE</option>
|
||||
</select><input
|
||||
class="ml-3 w-full px-4 py-2 border rounded-md border-gray-300 hover:border-orange-500 focus:outline-orange-500"
|
||||
value="https://function.peterstockings.com/f/helloworld"><button
|
||||
class="ml-3 px-6 py-2 rounded-md font-semibold text-white bg-orange-500 hover:bg-orange-600"
|
||||
type="button">Send</button></form>
|
||||
name="url" value="{{ url }}"><button
|
||||
class="ml-3 px-6 py-2 rounded-md font-semibold text-white bg-orange-500 hover:bg-orange-600" type="button"
|
||||
id="send-request">Send</button></form>
|
||||
<div class="react-tabs" data-rttabs="true">
|
||||
<ul class="flex mt-5 border border-gray-300 rounded-t-lg" role="tablist">
|
||||
<li class="mr-3 py-2 px-4 border-orange-400 focus:outline-none
|
||||
hover:text-orange-500 cursor-pointer border-b-2 text-orange-600" role="tab" id="tab:r0:0" aria-selected="true"
|
||||
aria-disabled="false" aria-controls="panel:r0:0" tabindex="0" data-rttab="true">Query Params</li>
|
||||
hover:text-orange-500 cursor-pointer" role="tab" id="tab:r0:0" aria-selected="true" aria-disabled="false"
|
||||
aria-controls="panel:r0:0" tabindex="0" data-rttab="true">Query Params</li>
|
||||
<li class="mr-3 py-2 px-4 border-orange-400 focus:outline-none
|
||||
hover:text-orange-500 cursor-pointer" role="tab" id="tab:r0:1" aria-selected="false" aria-disabled="false"
|
||||
aria-controls="panel:r0:1" data-rttab="true">Headers</li>
|
||||
<li class="mr-3 py-2 px-4 border-orange-400 focus:outline-none
|
||||
hover:text-orange-500 cursor-pointer" role="tab" id="tab:r0:2" aria-selected="false" aria-disabled="false"
|
||||
aria-controls="panel:r0:2" data-rttab="true">Body</li>
|
||||
hover:text-orange-500 cursor-pointer border-b-2 text-orange-600" role="tab" id="tab:r0:2"
|
||||
aria-selected="false" aria-disabled="false" aria-controls="panel:r0:2" data-rttab="true">Body</li>
|
||||
</ul>
|
||||
<div class="react-tabs__tab-panel px-4 py-4 rounded-b-lg border border-t-0 border-gray-300 react-tabs__tab-panel--selected"
|
||||
<div class="react-tabs__tab-panel px-4 py-4 rounded-b-lg border border-t-0 border-gray-300 react-tabs__tab-panel--selected hidden"
|
||||
role="tabpanel" id="panel:r0:0" aria-labelledby="tab:r0:0">
|
||||
<div class="">
|
||||
<div class="flex mb-3"><input
|
||||
@@ -40,8 +41,8 @@
|
||||
class="px-6 py-1 rounded-md text-orange-600 border border-orange-400 hover:bg-orange-100">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="react-tabs__tab-panel px-4 py-4 rounded-b-lg border border-t-0 border-gray-300" role="tabpanel"
|
||||
id="panel:r0:1" aria-labelledby="tab:r0:1">
|
||||
<div class="react-tabs__tab-panel px-4 py-4 rounded-b-lg border border-t-0 border-gray-300 hidden"
|
||||
role="tabpanel" id="panel:r0:1" aria-labelledby="tab:r0:1">
|
||||
<div class="">
|
||||
<div class="flex mb-3"><input
|
||||
class="px-4 py-1.5 w-full border border-gray-300 rounded-md hover:border-orange-500 focus:outline-orange-500"
|
||||
@@ -56,37 +57,10 @@
|
||||
<div class="react-tabs__tab-panel px-4 py-4 rounded-b-lg border border-t-0 border-gray-300" role="tabpanel"
|
||||
id="panel:r0:2" aria-labelledby="tab:r0:2">
|
||||
<div>
|
||||
<div class="cm-editor cm-wrap ͼ1 ͼ2 ͼ4">
|
||||
<div aria-live="polite" style="position: absolute; top: -10000px;"></div>
|
||||
<div tabindex="-1" class="cm-scroller">
|
||||
<div class="cm-gutters" aria-hidden="true" style="position: sticky; min-height: 42px;">
|
||||
<div class="cm-gutter cm-lineNumbers">
|
||||
<div class="cm-gutterElement"
|
||||
style="height: 0px; visibility: hidden; pointer-events: none;">9</div>
|
||||
<div class="cm-gutterElement cm-activeLineGutter" style="height: 14px;">1</div>
|
||||
<div class="cm-gutterElement" style="height: 14px;">2</div>
|
||||
<div class="cm-gutterElement" style="height: 14px;">3</div>
|
||||
</div>
|
||||
<div class="cm-gutter cm-foldGutter">
|
||||
<div class="cm-gutterElement"
|
||||
style="height: 0px; visibility: hidden; pointer-events: none;"><span
|
||||
title="Unfold line">›</span></div>
|
||||
<div class="cm-gutterElement cm-activeLineGutter" style="height: 14px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div spellcheck="false" autocorrect="off" autocapitalize="off" contenteditable="true"
|
||||
class="cm-content" style="tab-size: 2" role="textbox" aria-multiline="true"
|
||||
aria-autocomplete="list">
|
||||
<div class="cm-activeLine cm-line">{</div>
|
||||
<div class="cm-line"> </div>
|
||||
<div class="cm-line">}</div>
|
||||
</div>
|
||||
<div class="cm-selectionLayer" aria-hidden="true"></div>
|
||||
<div class="cm-cursorLayer" aria-hidden="true" style="animation-duration: 1200ms;">
|
||||
<div class="cm-cursor cm-cursor-primary" style="left: 0px; top: 0px; height: 0px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<textarea id="request-body" rows="4"
|
||||
class="block p-2.5 w-full text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
||||
placeholder="Request body"></textarea>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -103,41 +77,9 @@
|
||||
<div class="px-4 py-4 rounded-b-lg border border-t-0 border-gray-300">
|
||||
<div class="react-tabs__tab-panel react-tabs__tab-panel--selected" role="tabpanel" id="panel:r1:0"
|
||||
aria-labelledby="tab:r1:0">
|
||||
<div>
|
||||
<div class="cm-editor cm-wrap ͼ1 ͼ2 ͼ4">
|
||||
<div aria-live="polite" style="position: absolute; top: -10000px;"></div>
|
||||
<div tabindex="-1" class="cm-scroller">
|
||||
<div class="cm-gutters" aria-hidden="true"
|
||||
style="position: sticky; min-height: 26.1979px;">
|
||||
<div class="cm-gutter cm-lineNumbers">
|
||||
<div class="cm-gutterElement"
|
||||
style="height: 0px; visibility: hidden; pointer-events: none;">9</div>
|
||||
<div class="cm-gutterElement cm-activeLineGutter"
|
||||
style="height: 18.1979px; margin-top: 4px;">1</div>
|
||||
</div>
|
||||
<div class="cm-gutter cm-foldGutter">
|
||||
<div class="cm-gutterElement"
|
||||
style="height: 0px; visibility: hidden; pointer-events: none;"><span
|
||||
title="Unfold line">›</span></div>
|
||||
<div class="cm-gutterElement cm-activeLineGutter"
|
||||
style="height: 18.1979px; margin-top: 4px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div spellcheck="false" autocorrect="off" autocapitalize="off" contenteditable="false"
|
||||
class="cm-content" style="tab-size: 2" role="textbox" aria-multiline="true"
|
||||
aria-autocomplete="list">
|
||||
<div class="cm-activeLine cm-line">{}</div>
|
||||
</div>
|
||||
<div class="cm-selectionLayer" aria-hidden="true"></div>
|
||||
<div class="cm-cursorLayer" aria-hidden="true" style="animation-duration: 1200ms;">
|
||||
<div class="cm-cursor cm-cursor-primary"
|
||||
style="left: 33.8229px; top: 5.33334px; height: 15.3333px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<code id="response"></code>
|
||||
</div>
|
||||
<div class="react-tabs__tab-panel" role="tabpanel" id="panel:r1:1" aria-labelledby="tab:r1:1">
|
||||
<div class="react-tabs__tab-panel hidden" role="tabpanel" id="panel:r1:1" aria-labelledby="tab:r1:1">
|
||||
<table class="text-left">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -153,4 +95,34 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
const send_request_btn = document.getElementById('send-request');
|
||||
const response = document.getElementById('response');
|
||||
|
||||
send_request_btn.addEventListener('click', () => {
|
||||
const url = document.querySelector('input[name="url"]').value;
|
||||
const method = document.querySelector('select[name="method"]').value;
|
||||
let body = document.getElementById('request-body').value;
|
||||
// If get or head then dont add body
|
||||
if (method === 'GET' || method === 'HEAD') {
|
||||
body = null;
|
||||
}
|
||||
|
||||
fetch(url, {
|
||||
method,
|
||||
body,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
}).then(response => {
|
||||
response.text().then(text => {
|
||||
response.innerHTML = text;
|
||||
});
|
||||
});
|
||||
|
||||
})
|
||||
})();
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,8 +1,7 @@
|
||||
<div class="flex items-center" data-id="51">
|
||||
<h1 class="font-semibold text-lg md:text-2xl" data-id="52">HTTP functions</h1>
|
||||
<button
|
||||
class="inline-flex items-center justify-center text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-primary text-primary-foreground hover:bg-primary/90 h-9 rounded-md px-3 ml-auto"
|
||||
hx-get="{{ url_for('client') }}" hx-target="#container" hx-swap="innerHTML">
|
||||
class="inline-flex items-center justify-center text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-primary text-primary-foreground hover:bg-primary/90 h-9 rounded-md px-3 ml-auto">
|
||||
Add Function
|
||||
</button>
|
||||
</div>
|
||||
@@ -41,10 +40,17 @@
|
||||
</td>
|
||||
<td class="p-4 align-middle [&:has([role=checkbox])]:pr-0 hidden md:table-cell" data-id="68">
|
||||
<button
|
||||
class="inline-flex items-center justify-center text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-primary text-primary-foreground hover:bg-primary/90 h-9 rounded-md px-3 mr-2"
|
||||
data-id="69">Edit</button><button
|
||||
class="inline-flex items-center justify-center text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 border border-input bg-background hover:bg-accent hover:text-accent-foreground h-9 rounded-md px-3"
|
||||
data-id="70">Delete</button>
|
||||
data-id="69" hx-get="{{ url_for('client', function=function.name) }}" hx-target="#container"
|
||||
hx-swap="innerHTML">
|
||||
Try
|
||||
</button>
|
||||
<button
|
||||
class="inline-flex items-center justify-center text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 border border-input bg-background hover:bg-accent hover:text-accent-foreground h-9 rounded-md px-3"
|
||||
data-id="69" hx-get="{{ url_for('client', function=function.name) }}" hx-target="#container"
|
||||
hx-swap="innerHTML">
|
||||
Edit
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<div class="flex items-center" data-id="51">
|
||||
<h1 class="font-semibold text-lg md:text-2xl" data-id="52">Timer functions</h1>
|
||||
<button
|
||||
class="inline-flex items-center justify-center text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-primary text-primary-foreground hover:bg-primary/90 h-9 rounded-md px-3 ml-auto"
|
||||
hx-get="{{ url_for('client') }}" hx-target="#container" hx-swap="innerHTML">
|
||||
class="inline-flex items-center justify-center text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-primary text-primary-foreground hover:bg-primary/90 h-9 rounded-md px-3 ml-auto">
|
||||
Add Function
|
||||
</button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user