diff --git a/spp_dci_server/README.rst b/spp_dci_server/README.rst index bc1b589d5..639118900 100644 --- a/spp_dci_server/README.rst +++ b/spp_dci_server/README.rst @@ -159,6 +159,13 @@ Dependencies Changelog ========= +19.0.2.0.4 +~~~~~~~~~~ + +- Return signed DCI ``on-search`` envelopes from registry alias stubs + (disability, crvs, farmer) instead of HTTP 501; per-item ``rjct`` with + ``ACTION_NOT_SUPPORTED``. + 19.0.2.0.0 ~~~~~~~~~~ diff --git a/spp_dci_server/__manifest__.py b/spp_dci_server/__manifest__.py index ebfd938c2..e07533adc 100644 --- a/spp_dci_server/__manifest__.py +++ b/spp_dci_server/__manifest__.py @@ -1,7 +1,7 @@ { # pylint: disable=pointless-statement "name": "OpenSPP DCI Server", "summary": "DCI API server infrastructure with FastAPI routers", - "version": "19.0.2.0.3", + "version": "19.0.2.0.4", "category": "OpenSPP/Integration", "author": "OpenSPP.org", "website": "https://github.com/OpenSPP/OpenSPP2", diff --git a/spp_dci_server/readme/HISTORY.md b/spp_dci_server/readme/HISTORY.md index 4aaf9afef..a2439ea8c 100644 --- a/spp_dci_server/readme/HISTORY.md +++ b/spp_dci_server/readme/HISTORY.md @@ -1,3 +1,7 @@ +### 19.0.2.0.4 + +- Return signed DCI ``on-search`` envelopes from registry alias stubs (disability, crvs, farmer) instead of HTTP 501; per-item ``rjct`` with ``ACTION_NOT_SUPPORTED``. + ### 19.0.2.0.0 - Initial migration to OpenSPP2 diff --git a/spp_dci_server/routers/registry_aliases.py b/spp_dci_server/routers/registry_aliases.py index f0e21fc98..5b2780bc0 100644 --- a/spp_dci_server/routers/registry_aliases.py +++ b/spp_dci_server/routers/registry_aliases.py @@ -11,37 +11,88 @@ """ import logging -from datetime import datetime +import uuid +from datetime import UTC, datetime from typing import Annotated from odoo.api import Environment from odoo.addons.fastapi.dependencies import odoo_env +from odoo.addons.spp_dci.schemas import DCIEnvelope from odoo.addons.spp_dci.schemas.constants import MsgHeaderStatusReasonCode from odoo.addons.spp_dci.schemas.search import ( SearchRequest, SearchResponse, SearchResponseItem, ) +from odoo.addons.spp_dci.services import build_signed_envelope -from fastapi import APIRouter, Depends, status +from fastapi import APIRouter, Depends, HTTPException, status from ..middleware.signature import verify_bearer_token _logger = logging.getLogger(__name__) + +def _build_stub_search_envelope( + request_envelope: DCIEnvelope, + env: Environment, + not_implemented_message: str, +) -> DCIEnvelope: + """Return a signed DCI on-search envelope with per-item rjct stubs.""" + try: + search_request = SearchRequest.model_validate(request_envelope.message) + except Exception as e: + _logger.error("Invalid SearchRequest message: %s", str(e)) + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=f"Invalid search request message: {str(e)}", + ) from e + + response_items = [] + for req_item in search_request.search_request: + response_items.append( + SearchResponseItem( + reference_id=req_item.reference_id, + timestamp=datetime.now(UTC), + status="rjct", + status_reason_code=MsgHeaderStatusReasonCode.ACTION_NOT_SUPPORTED.value, + status_reason_message=not_implemented_message, + data=None, + pagination=None, + locale=req_item.locale, + ) + ) + + search_response = SearchResponse( + transaction_id=search_request.transaction_id, + correlation_id=str(uuid.uuid4()), + search_response=response_items, + ) + response_message = search_response.model_dump(mode="json", exclude_none=True) + + return build_signed_envelope( + env, + request_envelope.header, + response_message, + status_code="rjct", + status_reason_code=MsgHeaderStatusReasonCode.ACTION_NOT_SUPPORTED.value, + status_reason_message=not_implemented_message, + completed_count=0, + ) + + # Disability Registry router disability_router = APIRouter(tags=["Disability Registry"], prefix="/disability/registry") @disability_router.post( "/sync/search", - response_model=SearchResponse, + response_model=DCIEnvelope, response_model_exclude_none=True, - status_code=status.HTTP_501_NOT_IMPLEMENTED, ) async def disability_sync_search( - request: SearchRequest, + request_envelope: DCIEnvelope, env: Annotated[Environment, Depends(odoo_env)], _bearer_token: Annotated[str, Depends(verify_bearer_token)], ): @@ -54,29 +105,10 @@ async def disability_sync_search( For now, returns "not implemented" response for SPDCI compliance testing. """ _logger.warning("Disability Registry search endpoint called but not yet implemented") - - # Build response items with "not implemented" status - response_items = [] - for req_item in request.search_request: - response_items.append( - SearchResponseItem( - reference_id=req_item.reference_id, - timestamp=datetime.utcnow(), - status="rjct", - status_reason_code=MsgHeaderStatusReasonCode.ACTION_NOT_SUPPORTED.value, - status_reason_message=( - "Disability Registry not yet implemented. Will be available in spp_dci_server_disability module." - ), - data=None, - pagination=None, - locale=req_item.locale, - ) - ) - - return SearchResponse( - transaction_id=request.transaction_id, - correlation_id=None, - search_response=response_items, + return _build_stub_search_envelope( + request_envelope, + env, + "Disability Registry not yet implemented. Will be available in spp_dci_server_disability module.", ) @@ -99,7 +131,7 @@ async def disability_sync_notify( return { "status": "rjct", "message": "Disability Registry not yet implemented", - "timestamp": datetime.utcnow().isoformat(), + "timestamp": datetime.now(UTC).isoformat(), } @@ -109,12 +141,11 @@ async def disability_sync_notify( @crvs_router.post( "/sync/search", - response_model=SearchResponse, + response_model=DCIEnvelope, response_model_exclude_none=True, - status_code=status.HTTP_501_NOT_IMPLEMENTED, ) async def crvs_sync_search( - request: SearchRequest, + request_envelope: DCIEnvelope, env: Annotated[Environment, Depends(odoo_env)], _bearer_token: Annotated[str, Depends(verify_bearer_token)], ): @@ -127,29 +158,10 @@ async def crvs_sync_search( For now, returns "not implemented" response for SPDCI compliance testing. """ _logger.warning("Civil Registry search endpoint called but not yet implemented") - - # Build response items with "not implemented" status - response_items = [] - for req_item in request.search_request: - response_items.append( - SearchResponseItem( - reference_id=req_item.reference_id, - timestamp=datetime.utcnow(), - status="rjct", - status_reason_code=MsgHeaderStatusReasonCode.ACTION_NOT_SUPPORTED.value, - status_reason_message=( - "Civil Registry not yet implemented. Will be available in spp_dci_server_crvs module." - ), - data=None, - pagination=None, - locale=req_item.locale, - ) - ) - - return SearchResponse( - transaction_id=request.transaction_id, - correlation_id=None, - search_response=response_items, + return _build_stub_search_envelope( + request_envelope, + env, + "Civil Registry not yet implemented. Will be available in spp_dci_server_crvs module.", ) @@ -172,7 +184,7 @@ async def crvs_sync_notify( return { "status": "rjct", "message": "Civil Registry not yet implemented", - "timestamp": datetime.utcnow().isoformat(), + "timestamp": datetime.now(UTC).isoformat(), } @@ -182,12 +194,11 @@ async def crvs_sync_notify( @farmer_router.post( "/sync/search", - response_model=SearchResponse, + response_model=DCIEnvelope, response_model_exclude_none=True, - status_code=status.HTTP_501_NOT_IMPLEMENTED, ) async def farmer_sync_search( - request: SearchRequest, + request_envelope: DCIEnvelope, env: Annotated[Environment, Depends(odoo_env)], _bearer_token: Annotated[str, Depends(verify_bearer_token)], ): @@ -200,29 +211,10 @@ async def farmer_sync_search( For now, returns "not implemented" response for SPDCI compliance testing. """ _logger.warning("Farmer Registry search endpoint called but not yet implemented") - - # Build response items with "not implemented" status - response_items = [] - for req_item in request.search_request: - response_items.append( - SearchResponseItem( - reference_id=req_item.reference_id, - timestamp=datetime.utcnow(), - status="rjct", - status_reason_code=MsgHeaderStatusReasonCode.ACTION_NOT_SUPPORTED.value, - status_reason_message=( - "Farmer Registry not yet implemented. Will be available in spp_dci_server_farmer module." - ), - data=None, - pagination=None, - locale=req_item.locale, - ) - ) - - return SearchResponse( - transaction_id=request.transaction_id, - correlation_id=None, - search_response=response_items, + return _build_stub_search_envelope( + request_envelope, + env, + "Farmer Registry not yet implemented. Will be available in spp_dci_server_farmer module.", ) @@ -245,5 +237,5 @@ async def farmer_sync_notify( return { "status": "rjct", "message": "Farmer Registry not yet implemented", - "timestamp": datetime.utcnow().isoformat(), + "timestamp": datetime.now(UTC).isoformat(), } diff --git a/spp_dci_server/static/description/index.html b/spp_dci_server/static/description/index.html index 980e54294..e013654e5 100644 --- a/spp_dci_server/static/description/index.html +++ b/spp_dci_server/static/description/index.html @@ -534,6 +534,14 @@