149 lines
5.2 KiB
Python
149 lines
5.2 KiB
Python
from __future__ import annotations
|
|
|
|
import logging
|
|
|
|
from pydantic import BaseModel, ValidationError
|
|
|
|
from core.base import BaseRenderer, BaseTransport
|
|
from core.plugin_types import PluginLoadError, ensure_plugin_config_model
|
|
from core.schemas.config import Config, ResolvedConfig, ResolvedPluginConfig, ResolvedReportOutput
|
|
from core.schemas.modules import LoadedModule
|
|
from reporting.models import PluginRuntimeError
|
|
from reporting.registry import RendererRegistry
|
|
from reporting.transport_registry import TransportRegistry
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def resolve_config(
|
|
config: Config,
|
|
loaded_modules: list[LoadedModule],
|
|
renderer_registry: RendererRegistry,
|
|
transport_registry: TransportRegistry,
|
|
plugin_errors: list[PluginLoadError] | None = None,
|
|
) -> tuple[ResolvedConfig, list[PluginRuntimeError]]:
|
|
runtime_errors = [_plugin_load_error_to_runtime_error(error) for error in plugin_errors or ()]
|
|
resolved_modules: dict[str, BaseModel] = {}
|
|
seen_module_ids: set[str] = set()
|
|
|
|
for module in loaded_modules:
|
|
if module.manifest.identifier in seen_module_ids:
|
|
runtime_errors.append(
|
|
PluginRuntimeError(
|
|
plugin_kind="module",
|
|
plugin_name=module.manifest.identifier,
|
|
stage="discovery",
|
|
message="duplicate module identifier",
|
|
)
|
|
)
|
|
continue
|
|
seen_module_ids.add(module.manifest.identifier)
|
|
|
|
raw_module_config = config.modules.get(module.manifest.identifier, {})
|
|
try:
|
|
resolved_modules[module.manifest.identifier] = module.config_model.model_validate(
|
|
raw_module_config
|
|
)
|
|
except ValidationError as exc:
|
|
runtime_errors.append(
|
|
PluginRuntimeError(
|
|
plugin_kind="module",
|
|
plugin_name=module.manifest.identifier,
|
|
stage="config_validation",
|
|
message=str(exc),
|
|
)
|
|
)
|
|
|
|
resolved_outputs: list[ResolvedReportOutput] = []
|
|
for output in config.reports.outputs:
|
|
try:
|
|
renderer = renderer_registry.get(output.renderer.type)
|
|
transport = transport_registry.get(output.transport.type)
|
|
resolved_outputs.append(
|
|
ResolvedReportOutput(
|
|
renderer=_resolve_plugin_config(
|
|
output.renderer.type, output.renderer.payload(), renderer
|
|
),
|
|
transport=_resolve_plugin_config(
|
|
output.transport.type, output.transport.payload(), transport
|
|
),
|
|
)
|
|
)
|
|
except (ValidationError, ValueError, TypeError) as exc:
|
|
runtime_errors.append(
|
|
PluginRuntimeError(
|
|
plugin_kind="report_output",
|
|
plugin_name=f"{output.renderer.type}->{output.transport.type}",
|
|
stage="config_validation",
|
|
message=str(exc),
|
|
)
|
|
)
|
|
|
|
resolved_config = ResolvedConfig(
|
|
exclude_categories=config.exclude_categories,
|
|
allow_failures_below=config.allow_failures_below,
|
|
strict_mode=config.strict_mode,
|
|
fail_fast=config.fail_fast,
|
|
dry_run=config.dry_run,
|
|
max_concurrency=config.max_concurrency,
|
|
logging=config.logging,
|
|
plugin_packages=config.plugin_packages,
|
|
reports=resolved_outputs,
|
|
modules=resolved_modules,
|
|
)
|
|
return resolved_config, runtime_errors
|
|
|
|
|
|
def validate_reporting_plugins(
|
|
renderer_registry: RendererRegistry,
|
|
transport_registry: TransportRegistry,
|
|
) -> list[PluginRuntimeError]:
|
|
errors: list[PluginRuntimeError] = []
|
|
|
|
errors.extend(_validate_registry_plugins("renderer", renderer_registry._renderers.values()))
|
|
errors.extend(_validate_registry_plugins("transport", transport_registry._transports.values()))
|
|
|
|
return errors
|
|
|
|
|
|
def _validate_registry_plugins(plugin_kind: str, plugins: object) -> list[PluginRuntimeError]:
|
|
seen: set[type[object]] = set()
|
|
errors: list[PluginRuntimeError] = []
|
|
|
|
for plugin in plugins:
|
|
plugin_type = type(plugin)
|
|
if plugin_type in seen:
|
|
continue
|
|
seen.add(plugin_type)
|
|
try:
|
|
ensure_plugin_config_model(plugin)
|
|
except TypeError as exc:
|
|
errors.append(
|
|
PluginRuntimeError(
|
|
plugin_kind=plugin_kind,
|
|
plugin_name=plugin_type.__name__,
|
|
stage="plugin_validation",
|
|
message=str(exc),
|
|
)
|
|
)
|
|
|
|
return errors
|
|
|
|
|
|
def _resolve_plugin_config(
|
|
plugin_type: str,
|
|
payload: dict[str, object],
|
|
plugin: BaseRenderer | BaseTransport,
|
|
) -> ResolvedPluginConfig:
|
|
config_model = ensure_plugin_config_model(plugin)
|
|
return ResolvedPluginConfig(type=plugin_type, config=config_model.model_validate(payload))
|
|
|
|
|
|
def _plugin_load_error_to_runtime_error(error: PluginLoadError) -> PluginRuntimeError:
|
|
return PluginRuntimeError(
|
|
plugin_kind=error.plugin_kind,
|
|
plugin_name=error.source,
|
|
stage="discovery",
|
|
message=error.message,
|
|
)
|