Skip to content
This repository was archived by the owner on Feb 18, 2026. It is now read-only.

Commit 36a6401

Browse files
committed
Add tests for ObfuscateTags plugin and remove aiohttp legacy code
- Add 35 unit tests for ObfuscateTags plugin covering: - Helper functions (has_no_obfuscation_group, obfuscate_private_tags, etc.) - before_request skip/apply conditions - before_response obfuscation for Entity, AddressTags, NeighborEntities - tagpack_uri_rule config option - Remove aiohttp legacy support from plugin system (now FastAPI-only) - Simplify gsrest/plugins/__init__.py (removed dual-framework logic) - Update docstring in gsrest/routes/base.py
1 parent 056d927 commit 36a6401

4 files changed

Lines changed: 244 additions & 90 deletions

File tree

gsrest/builtin/plugins/obfuscate_tags/obfuscate_tags.py

Lines changed: 14 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from functools import partial
33
from typing import Any
44

5+
from fastapi import Request
56
from graphsenselib.tagstore.algorithms.obfuscate import (
67
obfuscate_entity_actor,
78
obfuscate_tag_if_not_public,
@@ -12,7 +13,6 @@
1213
get_request_header,
1314
get_request_path,
1415
get_request_query_string,
15-
is_fastapi_request,
1616
)
1717
from openapi_server.models.address_tags import AddressTags
1818
from openapi_server.models.entity import Entity
@@ -25,14 +25,6 @@
2525
from openapi_server.models.search_result_level5 import SearchResultLevel5
2626
from openapi_server.models.search_result_level6 import SearchResultLevel6
2727

28-
# Import aiohttp only for legacy support
29-
try:
30-
from aiohttp import web
31-
from multidict import CIMultiDict
32-
except ImportError:
33-
web = None
34-
CIMultiDict = None
35-
3628
GROUPS_HEADER_NAME = "X-Consumer-Groups"
3729
NO_OBFUSCATION_MARKER_PATTERN = re.compile(r"(private|tags-private)")
3830
OBFUSCATION_MARKER_GROUP = "obfuscate"
@@ -73,7 +65,7 @@ def obfuscate_private_tags(tags):
7365

7466
class ObfuscateTags(Plugin):
7567
@classmethod
76-
def before_request(cls, context: dict, request: Any) -> Any:
68+
def before_request(cls, context: dict, request: Request) -> dict | None:
7769
groups = [
7870
x.strip()
7971
for x in get_request_header(request, GROUPS_HEADER_NAME, "").split(",")
@@ -83,47 +75,27 @@ def before_request(cls, context: dict, request: Any) -> Any:
8375
query_string = get_request_query_string(request)
8476

8577
if has_no_obfuscation_group(groups):
86-
return request if not is_fastapi_request(request) else None
78+
return None
8779
if "include_labels=true" in query_string.lower():
88-
return request if not is_fastapi_request(request) else None
80+
return None
8981
if "/search" == path:
90-
return request if not is_fastapi_request(request) else None
82+
return None
9183
if "/bulk" in path:
92-
return request if not is_fastapi_request(request) else None
84+
return None
9385
if re.match(re.compile("/tags"), path):
94-
return request if not is_fastapi_request(request) else None
86+
return None
9587
if re.match(re.compile("/[a-z]{3}/addresses/[^/]+$"), path):
9688
# to avoid loading actors for address
97-
return request if not is_fastapi_request(request) else None
98-
99-
# For FastAPI, return header modifications dict
100-
if is_fastapi_request(request):
101-
return {GROUPS_HEADER_NAME: OBFUSCATION_MARKER_GROUP}
102-
103-
# For aiohttp, clone request with modified headers
104-
if web is not None and CIMultiDict is not None:
105-
headers = dict(request.headers)
106-
headers[GROUPS_HEADER_NAME] = OBFUSCATION_MARKER_GROUP
107-
headers = CIMultiDict(**headers)
108-
return request.clone(headers=headers)
89+
return None
10990

110-
return request
91+
return {GROUPS_HEADER_NAME: OBFUSCATION_MARKER_GROUP}
11192

11293
@classmethod
113-
def before_response(cls, context: dict, request: Any, result: Any) -> None:
114-
# Get groups from headers (check for FastAPI header modifications first)
115-
if is_fastapi_request(request):
116-
# For FastAPI, check request.state for header modifications
117-
header_mods = getattr(request.state, "header_modifications", {})
118-
if GROUPS_HEADER_NAME in header_mods:
119-
groups = [header_mods[GROUPS_HEADER_NAME]]
120-
else:
121-
groups = [
122-
x.strip()
123-
for x in get_request_header(request, GROUPS_HEADER_NAME, "").split(
124-
","
125-
)
126-
]
94+
def before_response(cls, context: dict, request: Request, result: Any) -> None:
95+
# Get groups from headers (check for header modifications first)
96+
header_mods = getattr(request.state, "header_modifications", {})
97+
if GROUPS_HEADER_NAME in header_mods:
98+
groups = [header_mods[GROUPS_HEADER_NAME]]
12799
else:
128100
groups = [
129101
x.strip()

gsrest/plugins/__init__.py

Lines changed: 15 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2,42 +2,28 @@
22
import inspect
33
from typing import Any
44

5-
# Support both aiohttp and FastAPI request types
6-
try:
7-
from aiohttp import web as aiohttp_web
8-
9-
AioHTTPRequest = aiohttp_web.Request
10-
except ImportError:
11-
AioHTTPRequest = None
12-
13-
try:
14-
from fastapi import Request as FastAPIRequest
15-
except ImportError:
16-
FastAPIRequest = None
5+
from fastapi import Request
176

187

198
class Plugin(abc.ABC):
209
"""Base class for plugins.
2110
2211
Plugins can implement before_request and before_response hooks to modify
23-
request handling and responses. The interface is designed to work with
24-
both aiohttp (legacy) and FastAPI (new) request objects.
12+
request handling and responses.
2513
"""
2614

2715
@classmethod
2816
@abc.abstractmethod
29-
def before_request(cls, context: dict, request: Any) -> Any:
17+
def before_request(cls, context: dict, request: Request) -> dict | None:
3018
"""Called before request processing.
3119
32-
For aiohttp: Can return a modified request or the original.
33-
For FastAPI: Should modify request.state and return None or a dict
34-
of header modifications.
20+
Returns None to skip modifications, or a dict of header modifications.
3521
"""
36-
return request
22+
return None
3723

3824
@classmethod
3925
@abc.abstractmethod
40-
def before_response(cls, context: dict, request: Any, result: Any) -> None:
26+
def before_response(cls, context: dict, request: Request, result: Any) -> None:
4127
"""Called after response is prepared but before serialization.
4228
4329
Can modify the result object in place.
@@ -52,37 +38,19 @@ def get_subclass(module):
5238
continue
5339
if issubclass(kls, Plugin):
5440
return kls
55-
raise TypeError(f"{module.__name__} does not implement " "gsrest.plugins.Plugin")
56-
57-
58-
def is_fastapi_request(request: Any) -> bool:
59-
"""Check if request is a FastAPI Request object."""
60-
if FastAPIRequest is not None:
61-
return isinstance(request, FastAPIRequest)
62-
return False
63-
64-
65-
def is_aiohttp_request(request: Any) -> bool:
66-
"""Check if request is an aiohttp Request object."""
67-
if AioHTTPRequest is not None:
68-
return isinstance(request, AioHTTPRequest)
69-
return False
41+
raise TypeError(f"{module.__name__} does not implement gsrest.plugins.Plugin")
7042

7143

72-
def get_request_path(request: Any) -> str:
73-
"""Get the request path from either request type."""
74-
if is_fastapi_request(request):
75-
return request.url.path
76-
return request.path
44+
def get_request_path(request: Request) -> str:
45+
"""Get the request path."""
46+
return request.url.path
7747

7848

79-
def get_request_query_string(request: Any) -> str:
80-
"""Get the query string from either request type."""
81-
if is_fastapi_request(request):
82-
return str(request.query_params)
83-
return request.query_string
49+
def get_request_query_string(request: Request) -> str:
50+
"""Get the query string."""
51+
return str(request.query_params)
8452

8553

86-
def get_request_header(request: Any, name: str, default: str = "") -> str:
87-
"""Get a header value from either request type."""
54+
def get_request_header(request: Request, name: str, default: str = "") -> str:
55+
"""Get a header value."""
8856
return request.headers.get(name, default)

gsrest/routes/base.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ class RequestAdapter:
1818
"""Adapter to make FastAPI Request compatible with existing service layer.
1919
2020
This adapter provides a unified interface that the service layer expects,
21-
bridging FastAPI's Request object with the legacy aiohttp-style access patterns.
21+
bridging FastAPI's Request object with the dict-style access patterns
22+
used by the service layer.
2223
"""
2324

2425
def __init__(

0 commit comments

Comments
 (0)