Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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)
====================================================================================================================

Expand Down
39 changes: 33 additions & 6 deletions tests/functional/test_adapter.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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, **_):
Expand All @@ -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"
4 changes: 3 additions & 1 deletion twitcher/adapter/base.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Union

from pyramid.config import Configurator
from pyramid.request import Request
from pyramid.response import Response
Expand Down Expand Up @@ -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.

Expand Down
2 changes: 2 additions & 0 deletions twitcher/owsproxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading