sdlc-hub/src/jira_hub/core/registry.py
Andrey Epifancev 9534e1385a Task 3: author в PATCH, ответ POST по спеке, проверка editable-полей (#91)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 01:33:03 +04:00

69 lines
3.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Реестр модулей хаба. Список собирается лениво: ядро не импортирует модули при импорте."""
from .modules import Module, ProjectField
_cache: list[Module] | None = None
def modules() -> list[Module]:
global _cache
if _cache is None:
from ..modules import jira # внутри функции: ядро не зависит от модулей при импорте
from . import settings
mods = [jira.MODULE]
# маршруты ядра проверяет core/api.py при сборке ROUTES: здесь их ещё нет
check(mods, [], settings.CORE_DEFAULTS)
_cache = mods
return _cache
DEFAULT_AGENT = "jira" # агент по умолчанию: GET /agent без module, agent publish без --module
def agent_modules() -> list[Module]:
"""Модули с командами: у каждого свой агент (имя агента = имя модуля), в порядке реестра."""
return [m for m in modules() if m.commands]
CORE_PROJECT_FIELDS = ("label", "name", "created_at", "author") # author — служебный ключ тела POST/PATCH
def project_fields() -> dict[str, tuple[Module, ProjectField]]:
"""Поля проектов всех модулей: имя → (модуль, поле)."""
return {f.name: (m, f) for m in modules() for f in m.project_fields}
def module(name: str) -> Module | None:
return next((m for m in modules() if m.name == name), None)
def module_of(ctype: str) -> Module | None:
"""Модуль, владеющий типом команды."""
return next((m for m in modules() if m.commands and ctype in m.commands.types), None)
def check(mods: list[Module], core_routes, core_settings: dict) -> None:
"""Проверить, что модули не делят тип команды, ключ настроек и маршрут между собой и с ядром."""
types: dict[str, str] = {}
keys: dict[str, str] = {key: "ядро" for key in core_settings}
routes: dict[tuple[str, str], str] = {(r[0], r[1]): "ядро" for r in core_routes}
fields: dict[str, str] = {name: "ядро" for name in CORE_PROJECT_FIELDS}
for m in mods:
for ctype in sorted(m.commands.types) if m.commands else ():
if ctype in types:
raise RuntimeError(f"тип {ctype}: модули {types[ctype]} и {m.name}")
types[ctype] = m.name
for key in m.settings_defaults:
if key in keys:
raise RuntimeError(f"ключ настроек {key}: {keys[key]} и модуль {m.name}")
keys[key] = m.name
for f in m.project_fields:
if f.name in fields:
raise RuntimeError(f"поле проекта {f.name}: {fields[f.name]} и {m.name}")
fields[f.name] = m.name
if f.editable and m.on_project_updated is None:
raise RuntimeError(f"поле проекта {f.name}: модуль {m.name} объявил его изменяемым без on_project_updated")
for r in m.routes:
if (r[0], r[1]) in routes:
raise RuntimeError(f"маршрут {r[0]} {r[1]}: {routes[(r[0], r[1])]} и модуль {m.name}")
routes[(r[0], r[1])] = m.name