refactor(extensions): add Pydantic validation for extension configuration (#36767)

This commit is contained in:
Michael S. Molina
2025-12-19 13:33:10 -03:00
committed by GitHub
parent fb6f3fbb4d
commit 5920cb57ea
8 changed files with 517 additions and 116 deletions

View File

@@ -23,12 +23,17 @@ import sys
import time
import zipfile
from pathlib import Path
from typing import Any, Callable, cast
from typing import Any, Callable
import click
import semver
from jinja2 import Environment, FileSystemLoader
from superset_core.extensions.types import Manifest, Metadata
from superset_core.extensions.types import (
ExtensionConfig,
Manifest,
ManifestBackend,
ManifestFrontend,
)
from watchdog.events import FileSystemEventHandler
from watchdog.observers import Observer
@@ -125,40 +130,40 @@ def clean_dist_frontend(cwd: Path) -> None:
def build_manifest(cwd: Path, remote_entry: str | None) -> Manifest:
extension: Metadata = cast(Metadata, read_json(cwd / "extension.json"))
if not extension:
extension_data = read_json(cwd / "extension.json")
if not extension_data:
click.secho("❌ extension.json not found.", err=True, fg="red")
sys.exit(1)
manifest: Manifest = {
"id": extension["id"],
"name": extension["name"],
"version": extension["version"],
"permissions": extension["permissions"],
"dependencies": extension.get("dependencies", []),
}
if (
(frontend := extension.get("frontend"))
and (contributions := frontend.get("contributions"))
and (module_federation := frontend.get("moduleFederation"))
and remote_entry
):
manifest["frontend"] = {
"contributions": contributions,
"moduleFederation": module_federation,
"remoteEntry": remote_entry,
}
extension = ExtensionConfig.model_validate(extension_data)
if entry_points := extension.get("backend", {}).get("entryPoints"):
manifest["backend"] = {"entryPoints": entry_points}
frontend: ManifestFrontend | None = None
if extension.frontend and remote_entry:
frontend = ManifestFrontend(
contributions=extension.frontend.contributions,
moduleFederation=extension.frontend.moduleFederation,
remoteEntry=remote_entry,
)
return manifest
backend: ManifestBackend | None = None
if extension.backend and extension.backend.entryPoints:
backend = ManifestBackend(entryPoints=extension.backend.entryPoints)
return Manifest(
id=extension.id,
name=extension.name,
version=extension.version,
permissions=extension.permissions,
dependencies=extension.dependencies,
frontend=frontend,
backend=backend,
)
def write_manifest(cwd: Path, manifest: Manifest) -> None:
dist_dir = cwd / "dist"
(dist_dir / "manifest.json").write_text(
json.dumps(manifest, indent=2, sort_keys=True)
manifest.model_dump_json(indent=2, exclude_none=True, by_alias=True)
)
click.secho("✅ Manifest updated", fg="green")