diff --git a/CHANGES.rst b/CHANGES.rst index e236fe4..5369dd3 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,11 @@ Changes Unreleased ==================================================================================================================== +Changes: + +* Allow request hooks to return a response which is returned immediately instead + of getting the response from upstream. + 0.11.1 (2026-01-09) ==================================================================================================================== diff --git a/tests/functional/test_adapter.py b/tests/functional/test_adapter.py index c0e9304..178fa29 100644 --- a/tests/functional/test_adapter.py +++ b/tests/functional/test_adapter.py @@ -1,5 +1,7 @@ +from contextlib import contextmanager import json import mock +from pyramid.response import Response from twitcher.adapter.default import DefaultAdapter from twitcher.owssecurity import OWSSecurityInterface @@ -29,18 +31,25 @@ def response_hook(self, response, service): return response -class TestAdapterWithHooks(FunctionalTest): +class AdapterWithRequestReturnResponseHook(AdapterWithHooks): + def request_hook(self, request, service): + return Response(body="response test", status=203) + + +class AdapterTestWithHooks(FunctionalTest): + adapter: DefaultAdapter + @property def settings(self): - adapter_name = '{}.{}'.format(AdapterWithHooks.__module__, AdapterWithHooks.__name__) - settings = super(TestAdapterWithHooks, self).settings.copy() + adapter_name = '{}.{}'.format(self.adapter.__module__, self.adapter.__name__) + settings = super(AdapterTestWithHooks, self).settings.copy() settings.update({ 'twitcher.adapter': adapter_name }) return settings def setUp(self): - super(TestAdapterWithHooks, self).setUp() + super(AdapterTestWithHooks, self).setUp() self.init_database() service_store = ServiceStore(dummy_request(dbsession=self.session)) self.reg = OWSRegistry(servicestore=service_store) @@ -60,7 +69,8 @@ def setUp(self): self.config.include('twitcher.owsproxy') self.app = self.get_test_app() - def test_request_response_hooks(self): + @contextmanager + def patched_request(self): test_request_handle = [] def mocked_request(method, url, data, headers, **_): @@ -79,13 +89,30 @@ def mocked_request(method, url, data, headers, **_): return _resp with mock.patch("requests.request", side_effect=mocked_request): + yield test_request_handle + + +class TestAdapterWithHooks(AdapterTestWithHooks): + adapter = AdapterWithHooks + + def test_request_response_hooks(self): + with self.patched_request() as test_request_handle: resp = self.app.get(f'/ows/proxy/{self.test_service_name}?service=wps&request=getcapabilities') assert resp.status_code == 200 assert resp.content_type == "application/json" - # check added header by request hook assert test_request_handle assert test_request_handle[0].headers.get("X-Hook-Test-Service") == self.test_service_name # check added body content by response hook assert resp.json == {"response": "ok", "Hook-Test-Service": self.test_service_name} + + +class TestAdapterWithRequestResponseHooks(AdapterTestWithHooks): + adapter = AdapterWithRequestReturnResponseHook + + def test_request_hook_returns_response(self): + with self.patched_request(): + resp = self.app.get(f'/ows/proxy/{self.test_service_name}?service=wps&request=getcapabilities') + assert resp.status_code == 203 + assert resp.body == b"response test" diff --git a/twitcher/adapter/base.py b/twitcher/adapter/base.py index 0d67950..f1b15ff 100644 --- a/twitcher/adapter/base.py +++ b/twitcher/adapter/base.py @@ -1,3 +1,5 @@ +from typing import Union + from pyramid.config import Configurator from pyramid.request import Request from pyramid.response import Response @@ -49,7 +51,7 @@ def owsproxy_config(self, container: AnySettingsContainer) -> None: """ raise NotImplementedError - def request_hook(self, request: Request, service: ServiceConfig) -> Request: + def request_hook(self, request: Request, service: ServiceConfig) -> Union[Request | Response]: """ Apply modifications onto the request before sending it. diff --git a/twitcher/owsproxy.py b/twitcher/owsproxy.py index 0f7ffbb..7253216 100644 --- a/twitcher/owsproxy.py +++ b/twitcher/owsproxy.py @@ -171,6 +171,8 @@ def owsproxy_view(request: Request) -> Response: # in order to ensure both request/response operations are handled by the same logic adapter = request.adapter request = adapter.request_hook(request, service) + if isinstance(request, Response): + return request response = adapter.send_request(request, service) response = adapter.response_hook(response, service) return response