Add typescript support and automatic openapi client generation from ninja

This commit is contained in:
2024-10-09 16:28:54 +02:00
parent 6f4e93bb76
commit a71ca60270
10 changed files with 786 additions and 17 deletions

View File

@ -7,7 +7,7 @@ from django.contrib.staticfiles.management.commands.collectstatic import (
)
from staticfiles.apps import GENERATED_ROOT, IGNORE_PATTERNS_SCSS
from staticfiles.processors import Scss, Webpack
from staticfiles.processors import OpenApi, Scss, Webpack
class Command(CollectStatic):
@ -50,6 +50,7 @@ class Command(CollectStatic):
return Path(location)
Scss.compile(self.collect_scss())
OpenApi.compile() # This needs to be prior to webpack
Webpack.compile()
collected = super().collect()

View File

@ -6,13 +6,15 @@ from django.contrib.staticfiles.management.commands.runserver import (
)
from django.utils.autoreload import DJANGO_AUTORELOAD_ENV
from staticfiles.processors import Webpack
from staticfiles.processors import OpenApi, Webpack
class Command(Runserver):
"""Light wrapper around the statics runserver that integrates webpack auto bundling"""
def run(self, **options):
# OpenApi generation needs to be before webpack
OpenApi.compile()
# Only run webpack server when debug is enabled
# Also protects from re-launching the server if django reloads it
if os.environ.get(DJANGO_AUTORELOAD_ENV) is None and settings.DEBUG:

View File

@ -1,6 +1,7 @@
import logging
import subprocess
from dataclasses import dataclass
from hashlib import sha1
from pathlib import Path
from typing import Iterable
@ -8,6 +9,7 @@ import rjsmin
import sass
from django.conf import settings
from sith.urls import api
from staticfiles.apps import GENERATED_ROOT
@ -71,3 +73,38 @@ class JS:
minified = rjsmin.jsmin(p.read_text())
p.write_text(minified)
logging.getLogger("main").info(f"Minified {path}")
class OpenApi:
OPENAPI_DIR = GENERATED_ROOT / "openapi"
@classmethod
def compile(cls):
"""Compile a typescript client for the sith API. Only generates it if it changed"""
logging.getLogger("django").info("Compiling open api typescript client")
out = cls.OPENAPI_DIR / "schema.json"
cls.OPENAPI_DIR.mkdir(parents=True, exist_ok=True)
old_hash = ""
if out.exists():
with open(out, "rb") as f:
old_hash = sha1(f.read()).hexdigest()
schema = api.get_openapi_schema()
# Remove hash from operationIds
# This is done for cache invalidation but this is too aggressive
for path in schema["paths"].values():
for action, desc in path.items():
path[action]["operationId"] = "_".join(
desc["operationId"].split("_")[:-1]
)
schema = str(schema)
if old_hash == sha1(schema.encode("utf-8")).hexdigest():
logging.getLogger("django").info("✨ Api did not change, nothing to do ✨")
return
with open(out, "w") as f:
_ = f.write(schema)
subprocess.run(["npx", "openapi-ts"]).check_returncode()

View File

@ -12,7 +12,7 @@ from staticfiles.processors import JS, Scss
class ManifestPostProcessingStorage(ManifestStaticFilesStorage):
def url(self, name: str, *, force: bool = False) -> str:
"""Get the URL for a file, convert .scss calls to .css ones"""
"""Get the URL for a file, convert .scss calls to .css ones and .ts to .js"""
# This name swap has to be done here
# Otherwise, the manifest isn't aware of the file and can't work properly
path = Path(name)
@ -27,6 +27,9 @@ class ManifestPostProcessingStorage(ManifestStaticFilesStorage):
)
name = str(path.with_suffix(".css"))
if path.suffix == ".ts":
name = str(path.with_suffix(".js"))
return super().url(name, force=force)
def post_process(