Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#13] Add prettyfier based on content-type #17

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 53 additions & 33 deletions log_outgoing_requests/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from django import forms
from django.contrib import admin
from django.utils.html import mark_safe
from django.utils.translation import gettext as _

from solo.admin import SingletonModelAdmin
Expand Down Expand Up @@ -30,44 +31,12 @@ class OutgoingRequestsLogAdmin(admin.ModelAdmin):
search_fields = ("url", "params", "hostname")
date_hierarchy = "timestamp"
show_full_result_count = False

fieldsets = (
(
_("Request"),
{
"fields": (
"method",
"url",
"timestamp",
"query_params",
"params",
"req_headers",
"req_content_type",
"req_body_encoding",
"request_body",
)
},
),
(
_("Response"),
{
"fields": (
"status_code",
"response_ms",
"res_headers",
"res_content_type",
"res_body_encoding",
"response_body",
)
},
),
(_("Extra"), {"fields": ("trace",)}),
)
readonly_fields = (
"url",
"timestamp",
"method",
"query_params",
"prettify_body_response",
danielmursa-dev marked this conversation as resolved.
Show resolved Hide resolved
"params",
"req_headers",
"req_content_type",
Expand All @@ -80,10 +49,50 @@ class OutgoingRequestsLogAdmin(admin.ModelAdmin):
"trace",
)

def get_fieldsets(self, request, obj=None):
fieldsets = [
(
_("Request"),
{
"fields": (
"method",
"url",
"timestamp",
"query_params",
"params",
"req_headers",
"req_content_type",
"req_body_encoding",
"request_body",
)
},
),
(
_("Response"),
{
"fields": (
"status_code",
"response_ms",
"res_headers",
"res_content_type",
"res_body_encoding",
"response_body",
)
},
),
(_("Extra"), {"fields": ("trace",)}),
]

if obj and obj.supports_xml_or_json:
fieldsets[1][1]["fields"] += ("prettify_body_response",)

return fieldsets

class Media:
css = {
"all": ("log_outgoing_requests/css/admin.css",),
}
js = ("log_outgoing_requests/js/admin.js",)

def has_add_permission(self, request):
return False
Expand All @@ -100,6 +109,17 @@ def request_body(self, obj) -> str:
def response_body(self, obj) -> str:
return obj.response_body_decoded or "-"

def prettify_body_response(self, obj):
body_response = ""
if obj.supports_xml_or_json:
body_response = mark_safe(
'<a href="#" class="prettify-toggle-link">Prettify</a><br>\n'
'<textarea readonly class="prettify-output" style="display:none;"\n'
f" rows='15' cols='60' content-type='{obj.res_content_type}'>\n"
f"{obj.response_body_decoded}</textarea>"
)
return body_response

def truncated_url(self, obj):
parsed_url = urlparse(obj.url)
path = parsed_url.path
Expand Down
4 changes: 4 additions & 0 deletions log_outgoing_requests/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ def __str__(self):
def url_parsed(self):
return urlparse(self.url)

@cached_property
def supports_xml_or_json(self):
return "xml" in self.res_content_type or "json" in self.res_content_type

@property
def query_params(self):
return self.url_parsed.query
Expand Down
53 changes: 53 additions & 0 deletions log_outgoing_requests/static/log_outgoing_requests/js/admin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
document.addEventListener("DOMContentLoaded", function () {
const link = document.querySelector(".prettify-toggle-link");
if (link) {
link.addEventListener("click", function (event) {
event.preventDefault();
const textArea = document.querySelector(".prettify-output");
const contentType = textArea.getAttribute("content-type");
const responseBody = textArea.value;
if (textArea.style.display === "none") {
let outputValue = '';
if (contentType.includes('json')) {
try {
outputValue = JSON.stringify(JSON.parse(responseBody), null, 3);
} catch (error) {
outputValue = "Invalid JSON format";
}
} else if (contentType.includes('xml')) {
try {
outputValue = prettifyXml(responseBody);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem to work for me locally (e.g. if I make a request to https://www.w3schools.com/xml/note.xml)

image

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mmmm, weird, I don't know, I tried too and it works.
image

} catch (xmlError) {
outputValue = "Invalid XML format";
}
}
textArea.value = outputValue;
textArea.style.display = "inline";
} else {
textArea.style.display = "none";
}
});
}
});

var prettifyXml = function(sourceXml) {
var xmlDoc = new DOMParser().parseFromString(sourceXml, 'application/xml');
var xsltDoc = new DOMParser().parseFromString([
'<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">',
' <xsl:strip-space elements="*"/>',
' <xsl:template match="node()|@*">',
' <xsl:copy>',
' <xsl:apply-templates select="node()|@*"/>',
' </xsl:copy>',
' </xsl:template>',
' <xsl:output method="xml" indent="yes"/>',
'</xsl:stylesheet>',
].join('\n'), 'application/xml');

var xsltProcessor = new XSLTProcessor();
xsltProcessor.importStylesheet(xsltDoc);
var resultDoc = xsltProcessor.transformToDocument(xmlDoc);
var resultXml = new XMLSerializer().serializeToString(resultDoc);
return resultXml;
};

Loading