from __future__ import annotations from pathlib import Path from govoplan_core.core.institutional import ( CAPABILITY_SERVICE_AVAILABILITY, CAPABILITY_SERVICE_DEFINITIONS, ) from govoplan_core.core.module_guards import drop_table_retirement_provider, persistent_table_uninstall_guard from govoplan_core.core.modules import ( CapabilityDocumentation, DocumentationLink, DocumentationTopic, MigrationSpec, ModuleContext, ModuleInterfaceProvider, ModuleManifest, PermissionDefinition, RoleTemplate, ) from govoplan_core.core.provider_governance import declared_module_architecture from govoplan_core.db.base import Base from govoplan_services.backend.db import models as service_models from govoplan_services.backend.service import RegistryServiceAvailabilityEvaluator, SqlServiceDefinitionProvider MODULE_ID = "services" MODULE_NAME = "Services" MODULE_VERSION = "0.1.16" READ_SCOPE = "services:definition:read" WRITE_SCOPE = "services:definition:write" ADMIN_SCOPE = "services:definition:admin" def _permission(scope: str, label: str, description: str) -> PermissionDefinition: module_id, resource, action = scope.split(":", 2) return PermissionDefinition( scope=scope, label=label, description=description, category="Services", level="tenant", module_id=module_id, resource=resource, action=action, ) def _router(context: ModuleContext): from govoplan_services.backend.router import configure_registry, router configure_registry(context.registry) return router def _definitions(_context: ModuleContext) -> SqlServiceDefinitionProvider: return SqlServiceDefinitionProvider() def _availability(context: ModuleContext) -> RegistryServiceAvailabilityEvaluator: return RegistryServiceAvailabilityEvaluator(context.registry) manifest = ModuleManifest( id=MODULE_ID, name=MODULE_NAME, version=MODULE_VERSION, optional_dependencies=("organizations", "mandates", "policy", "portal", "cases", "forms_runtime", "workflow_engine"), provides_interfaces=( ModuleInterfaceProvider(name="services.definition", version="0.1.0"), ModuleInterfaceProvider(name="services.availability", version="0.1.0"), ), permissions=( _permission(READ_SCOPE, "View service definitions", "View service definitions, bindings, and availability constraints."), _permission(WRITE_SCOPE, "Manage service definitions", "Create and revise service definitions."), _permission(ADMIN_SCOPE, "Administer services", "Administer service publication and recovery."), ), role_templates=( RoleTemplate(slug="service_manager", name="Service manager", description="Manage the institutional service catalogue.", permissions=(READ_SCOPE, WRITE_SCOPE)), RoleTemplate(slug="service_reader", name="Service reader", description="Inspect service definitions.", permissions=(READ_SCOPE,)), ), route_factory=_router, capability_factories={ CAPABILITY_SERVICE_DEFINITIONS: _definitions, CAPABILITY_SERVICE_AVAILABILITY: _availability, }, capability_documentation={ CAPABILITY_SERVICE_DEFINITIONS: CapabilityDocumentation(label="Service definitions", summary="Provides exact versioned institutional Service definitions.", contract_version="0.1.0"), CAPABILITY_SERVICE_AVAILABILITY: CapabilityDocumentation(label="Service availability", summary="Evaluates provider-known runtime availability requirements without widening constraints.", contract_version="0.1.0"), }, migration_spec=MigrationSpec( module_id=MODULE_ID, metadata=Base.metadata, script_location=str(Path(__file__).with_name("migrations") / "versions"), retirement_supported=True, retirement_provider=drop_table_retirement_provider(service_models.ServiceDefinitionRevision, label="Services"), retirement_notes="Destructive retirement requires a database snapshot and removes service-definition history.", ), uninstall_guard_providers=(persistent_table_uninstall_guard(service_models.ServiceDefinitionRevision, label="Services"),), documentation=( DocumentationTopic( id="services.catalogue", title="Institutional service catalogue", summary="Manage versioned service promises independently from presentation and case handling.", body="Definitions retain audiences, prerequisites, evidence, responsibility, bindings, publication, and explainable availability requirements. Catalogue and unversioned detail reads follow the titlebar valid-time and recorded-time selection. Portal and Cases consume exact revisions through capabilities, and current authorization is unchanged.", layer="configured", documentation_types=("admin", "user"), audience=("user", "operator", "module_admin", "product_owner"), links=(DocumentationLink(label="Services domain and recovery", href="govoplan-services/docs/SERVICES_DOMAIN.md", kind="repository"),), ), ), architecture=declared_module_architecture( layer="human_work_procedure", kind="domain", maturity="vertical_slice", documentation_ref="docs/SERVICES_DOMAIN.md", test_ref="tests/test_services.py", known_limits=("The catalogue is API-first; Portal remains the presentation surface.",), supported_authority_modes=("native_authoritative",), owned_concepts=("service definition", "service publication", "service availability requirement"), non_owned_concepts=("portal presentation", "case lifecycle", "form definition", "workflow definition"), reference_packages=("product.service-to-decision",), migration_docs=("docs/SERVICES_DOMAIN.md",), recovery_docs=("docs/SERVICES_DOMAIN.md",), security_docs=("docs/SERVICES_DOMAIN.md",), operations_docs=("docs/SERVICES_DOMAIN.md",), ), ) def get_manifest() -> ModuleManifest: return manifest