feat(identity): define the search provider contract
This commit is contained in:
@@ -126,6 +126,15 @@ Feature modules should prefer these capabilities over direct reads of
|
||||
access/tenant ORM models when they need labels, group membership, default
|
||||
access provisioning, counts, audit actor labels, or tenant metadata.
|
||||
|
||||
Other stable runtime capabilities currently include:
|
||||
|
||||
- `identity.directory` and `identity.search`
|
||||
- `organizations.directory`
|
||||
- `idm.directory`
|
||||
- `calendar.outbox` and `calendar.scheduling`
|
||||
- `poll.scheduling`
|
||||
- `notifications.dispatch`
|
||||
|
||||
### Named Interface Contracts
|
||||
|
||||
Capabilities are runtime objects. Named interface contracts are compatibility
|
||||
@@ -147,15 +156,25 @@ intended for SemVer major-version lines. Missing optional interfaces are
|
||||
allowed, but an installed provider with an incompatible version blocks
|
||||
activation because the integration would otherwise bind to an unsafe API.
|
||||
|
||||
Current named interfaces:
|
||||
Current named interfaces, generated from the source manifests by the workspace
|
||||
contract checks, are:
|
||||
|
||||
- `files.campaign_attachments`
|
||||
- `addresses.contact_writer`, `addresses.lookup`, `addresses.recipient_source`
|
||||
- `calendar.outbox`, `calendar.scheduling`
|
||||
- `campaigns.access`, `campaigns.delivery_tasks`,
|
||||
`campaigns.mail_policy_context`, `campaigns.policy_context`,
|
||||
`campaigns.retention`
|
||||
- `dist_lists.expand`, `dist_lists.source`, `dist_lists.writer`
|
||||
- `evaluation.feedback`, `evaluation.result_aggregation`, `evaluation.scoring`
|
||||
- `files.access`, `files.campaign_attachments`
|
||||
- `mail.campaign_delivery`
|
||||
- `campaigns.access`
|
||||
- `campaigns.delivery_tasks`
|
||||
- `campaigns.mail_policy_context`
|
||||
- `campaigns.policy_context`
|
||||
- `campaigns.retention`
|
||||
- `notifications.dispatch`
|
||||
- `poll.availability_matrix`, `poll.option_selection`,
|
||||
`poll.response_collection`, `poll.signed_participation`,
|
||||
`poll.workflow_context`
|
||||
- `rest.function_publication`
|
||||
- `scheduling.candidate_slots`, `scheduling.decision_handoff`
|
||||
- `soap.operation_publication`
|
||||
|
||||
Core validates named interface contracts in three places:
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ from typing import Literal, Protocol, runtime_checkable
|
||||
|
||||
IDENTITY_MODULE_ID = "identity"
|
||||
CAPABILITY_IDENTITY_DIRECTORY = "identity.directory"
|
||||
CAPABILITY_IDENTITY_SEARCH = "identity.search"
|
||||
|
||||
IdentityStatus = Literal["active", "inactive", "suspended"]
|
||||
|
||||
@@ -44,3 +45,15 @@ class IdentityDirectory(Protocol):
|
||||
|
||||
def accounts_for_identity(self, identity_id: str) -> Sequence[IdentityAccountLinkRef]:
|
||||
...
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
class IdentitySearchProvider(Protocol):
|
||||
def search_identities(
|
||||
self,
|
||||
query: str | None = None,
|
||||
*,
|
||||
include_inactive: bool = False,
|
||||
limit: int = 25,
|
||||
) -> Sequence[IdentityRef]:
|
||||
...
|
||||
|
||||
@@ -396,8 +396,11 @@ class IdentityOrganizationContractTests(unittest.TestCase):
|
||||
from govoplan_organizations.backend.directory import SqlOrganizationDirectory
|
||||
|
||||
identity_directory = SqlIdentityDirectory()
|
||||
idm_directory = SqlIdmDirectory()
|
||||
organization_directory = SqlOrganizationDirectory()
|
||||
idm_directory = SqlIdmDirectory(
|
||||
identities=identity_directory,
|
||||
organizations=organization_directory,
|
||||
)
|
||||
|
||||
self.assertEqual("Directory Person", identity_directory.get_identity("identity-directory").display_name) # type: ignore[union-attr]
|
||||
self.assertEqual("identity-directory", identity_directory.identity_for_account("account-directory").id) # type: ignore[union-attr]
|
||||
|
||||
@@ -12,11 +12,18 @@ from govoplan_access.backend.db.models import Account, User
|
||||
from govoplan_core.auth import ApiPrincipal, get_api_principal
|
||||
from govoplan_core.core.access import PrincipalRef
|
||||
from govoplan_core.core.configuration_control import configuration_control_snapshot, create_configuration_change_request
|
||||
from govoplan_core.core.identity import CAPABILITY_IDENTITY_DIRECTORY, CAPABILITY_IDENTITY_SEARCH
|
||||
from govoplan_core.core.modules import ModuleContext, ModuleManifest
|
||||
from govoplan_core.core.organizations import CAPABILITY_ORGANIZATION_DIRECTORY
|
||||
from govoplan_core.core.registry import PlatformRegistry
|
||||
from govoplan_core.core.runtime import clear_runtime, configure_runtime
|
||||
from govoplan_core.db.base import Base
|
||||
from govoplan_identity.backend.directory import SqlIdentityDirectory
|
||||
from govoplan_identity.backend.db.models import Identity, IdentityAccountLink
|
||||
from govoplan_idm.backend.db.models import IdmOrganizationFunctionAssignment, IdmTenantSettings
|
||||
from govoplan_idm.backend.api.v1.routes import router as idm_router
|
||||
from govoplan_idm.backend.manifest import get_manifest
|
||||
from govoplan_organizations.backend.directory import SqlOrganizationDirectory
|
||||
from govoplan_organizations.backend.db.models import OrganizationFunction, OrganizationUnit
|
||||
from tests.db_isolation import temporary_database
|
||||
|
||||
@@ -50,7 +57,37 @@ class IdmApiTests(unittest.TestCase):
|
||||
)
|
||||
|
||||
def _app(self, scopes: set[str]) -> FastAPI:
|
||||
identity_directory = SqlIdentityDirectory()
|
||||
organization_directory = SqlOrganizationDirectory()
|
||||
registry = PlatformRegistry()
|
||||
registry.register(
|
||||
ModuleManifest(
|
||||
id="identity",
|
||||
name="Identity",
|
||||
version="test",
|
||||
capability_factories={
|
||||
CAPABILITY_IDENTITY_DIRECTORY: lambda context: identity_directory,
|
||||
CAPABILITY_IDENTITY_SEARCH: lambda context: identity_directory,
|
||||
},
|
||||
)
|
||||
)
|
||||
registry.register(
|
||||
ModuleManifest(
|
||||
id="organizations",
|
||||
name="Organizations",
|
||||
version="test",
|
||||
capability_factories={
|
||||
CAPABILITY_ORGANIZATION_DIRECTORY: lambda context: organization_directory,
|
||||
},
|
||||
)
|
||||
)
|
||||
context = ModuleContext(registry=registry, settings=object())
|
||||
registry.configure_capability_context(context)
|
||||
configure_runtime(context)
|
||||
self.addCleanup(clear_runtime)
|
||||
|
||||
app = FastAPI()
|
||||
app.state.govoplan_registry = registry
|
||||
app.include_router(idm_router, prefix="/api/v1")
|
||||
app.dependency_overrides[get_api_principal] = lambda: self._principal(scopes)
|
||||
return app
|
||||
|
||||
Reference in New Issue
Block a user