Skip to content

Commit

Permalink
api: use custom json renderer for speed
Browse files Browse the repository at this point in the history
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
  • Loading branch information
rissson committed Jun 28, 2024
1 parent 8915904 commit 8021768
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 3 deletions.
64 changes: 64 additions & 0 deletions authentik/api/renderers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import contextlib
from datetime import timedelta
from decimal import Decimal
from typing import Any

import orjson
from django.db.models.query import QuerySet
from django.utils.encoding import force_str
from django.utils.functional import Promise
from rest_framework.compat import coreapi
from rest_framework.renderers import JSONRenderer as BaseJSONRenderer


def default(obj: Any) -> Any:
"""
Render object to JSON.
Adaptation of https://github.com/encode/django-rest-framework/blob/master/rest_framework/utils/encoders.py
but without the overrides for types natively supported by orjson
"""
if isinstance(obj, Promise):
return force_str(obj)
elif isinstance(obj, timedelta):
return str(obj.total_seconds())

Check warning on line 24 in authentik/api/renderers.py

View check run for this annotation

Codecov / codecov/patch

authentik/api/renderers.py#L24

Added line #L24 was not covered by tests
elif isinstance(obj, Decimal):
return float(obj)

Check warning on line 26 in authentik/api/renderers.py

View check run for this annotation

Codecov / codecov/patch

authentik/api/renderers.py#L26

Added line #L26 was not covered by tests
elif isinstance(obj, QuerySet):
return tuple(obj)

Check warning on line 28 in authentik/api/renderers.py

View check run for this annotation

Codecov / codecov/patch

authentik/api/renderers.py#L28

Added line #L28 was not covered by tests
elif hasattr(obj, "tolist"):
return obj.tolist()

Check warning on line 30 in authentik/api/renderers.py

View check run for this annotation

Codecov / codecov/patch

authentik/api/renderers.py#L30

Added line #L30 was not covered by tests
elif (coreapi is not None) and isinstance(obj, coreapi.Document | coreapi.Error):
raise RuntimeError(

Check warning on line 32 in authentik/api/renderers.py

View check run for this annotation

Codecov / codecov/patch

authentik/api/renderers.py#L32

Added line #L32 was not covered by tests
"Cannot return a coreapi object from a JSON view. "
"You should be using a schema renderer instead for this view."
)
elif hasattr(obj, "__getitem__"):
cls = list if isinstance(obj, list | tuple) else dict
with contextlib.suppress(Exception):
return cls(obj)

Check warning on line 39 in authentik/api/renderers.py

View check run for this annotation

Codecov / codecov/patch

authentik/api/renderers.py#L37-L39

Added lines #L37 - L39 were not covered by tests
elif hasattr(obj, "__iter__"):
return tuple(item for item in obj)
return obj

Check warning on line 42 in authentik/api/renderers.py

View check run for this annotation

Codecov / codecov/patch

authentik/api/renderers.py#L42

Added line #L42 was not covered by tests


class JSONRenderer(BaseJSONRenderer):
"""
Renderer which serializes to JSON, using orjson.
"""

def render(self, data, accepted_media_type=None, renderer_context=None):
"""
Render `data` into JSON, returning a bytestring.
"""
if data is None:
return b""

renderer_context = renderer_context or {}
indent = self.get_indent(accepted_media_type, renderer_context)
options = orjson.OPT_NON_STR_KEYS | orjson.OPT_UTC_Z
if indent is not None:
# No other indentation is supported
options |= orjson.OPT_INDENT_2

Check warning on line 62 in authentik/api/renderers.py

View check run for this annotation

Codecov / codecov/patch

authentik/api/renderers.py#L62

Added line #L62 was not covered by tests

return orjson.dumps(data, default=default, option=options)
2 changes: 1 addition & 1 deletion authentik/root/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@
"rest_framework.authentication.SessionAuthentication",
),
"DEFAULT_RENDERER_CLASSES": [
"rest_framework.renderers.JSONRenderer",
"authentik.api.renderers.JSONRenderer",
],
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
"TEST_REQUEST_DEFAULT_FORMAT": "json",
Expand Down
59 changes: 57 additions & 2 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ ldap3 = "*"
lxml = "*"
msgraph-sdk = "*"
opencontainers = { extras = ["reggie"], version = "*" }
orjson = "*"
packaging = "*"
paramiko = "*"
psycopg = { extras = ["c"], version = "*" }
Expand Down

0 comments on commit 8021768

Please sign in to comment.