Skip to content

Commit d2c8d91

Browse files
authored
chore: rename filter generator to filter factory
"generator" is a bit of a loaded term in Python
1 parent c28c9b1 commit d2c8d91

File tree

6 files changed

+39
-39
lines changed

6 files changed

+39
-39
lines changed

docs/user-guide/configuration.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -200,23 +200,23 @@ The application is configurable via environment variables.
200200

201201
### `ITEMS_FILTER_CLS`
202202

203-
: CQL2 expression generator for item-level filtering
203+
: CQL2 expression factor for item-level filtering
204204

205205
- **Type:** JSON object with class configuration
206206
- **Required:** No, defaults to `null` (disabled)
207207
- **Example:** `stac_auth_proxy.filters:Opa`, `stac_auth_proxy.filters:Template`, `my_package:OrganizationFilter`
208208

209209
### `ITEMS_FILTER_ARGS`
210210

211-
: Positional arguments for CQL2 expression generator
211+
: Positional arguments for CQL2 expression factor
212212

213213
- **Type:** List of positional arguments used to initialize the class
214214
- **Required:** No, defaults to `[]`
215215
- **Example:** `["org1"]`
216216

217217
### `ITEMS_FILTER_KWARGS`
218218

219-
: Keyword arguments for CQL2 expression generator
219+
: Keyword arguments for CQL2 expression factor
220220

221221
- **Type:** Dictionary of keyword arguments used to initialize the class
222222
- **Required:** No, defaults to `{}`
@@ -232,23 +232,23 @@ The application is configurable via environment variables.
232232

233233
### `COLLECTIONS_FILTER_CLS`
234234

235-
: CQL2 expression generator for collection-level filtering
235+
: CQL2 expression factor for collection-level filtering
236236

237237
- **Type:** JSON object with class configuration
238238
- **Required:** No, defaults to `null` (disabled)
239239
- **Example:** `stac_auth_proxy.filters:Opa`, `stac_auth_proxy.filters:Template`, `my_package:OrganizationFilter`
240240

241241
### `COLLECTIONS_FILTER_ARGS`
242242

243-
: Positional arguments for CQL2 expression generator
243+
: Positional arguments for CQL2 expression factor
244244

245245
- **Type:** List of positional arguments used to initialize the class
246246
- **Required:** No, defaults to `[]`
247247
- **Example:** `["org1"]`
248248

249249
### `COLLECTIONS_FILTER_KWARGS`
250250

251-
: Keyword arguments for CQL2 expression generator
251+
: Keyword arguments for CQL2 expression factor
252252

253253
- **Type:** Dictionary of keyword arguments used to initialize the class
254254
- **Required:** No, defaults to `{}`

docs/user-guide/record-level-auth.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,20 +58,20 @@ The [`ITEMS_FILTER_CLS`](configuration.md#items_filter_cls) applies filters to t
5858

5959
## Filter Contract
6060

61-
A filter generator implements the following contract:
61+
A filter factory implements the following contract:
6262

6363
- A class or function that may take initialization arguments
64-
- Once initialized, the generator is a callable with the following behavior:
64+
- Once initialized, the factory is a callable with the following behavior:
6565
- **Input**: A context dictionary containing request and user information
6666
- **Output**: A valid CQL2 expression (as a string or dict) that filters the data
6767

6868
In Python typing syntax, it conforms to:
6969

7070
```py
71-
FilterGenerator = Callable[..., Callable[[dict[str, Any]], Awaitable[str | dict[str, Any]]]]
71+
FilterFactory = Callable[..., Callable[[dict[str, Any]], Awaitable[str | dict[str, Any]]]]
7272
```
7373

74-
### Example Filter Generator
74+
### Example Filter Factory
7575

7676
```py
7777
import dataclasses
@@ -87,7 +87,7 @@ class ExampleFilter:
8787
```
8888

8989
> [!TIP]
90-
> Despite being referred to as a _class_, a filter generator could be written as a function.
90+
> Despite being referred to as a _class_ in the settings, a filter factory could be written as a function.
9191
>
9292
> <details>
9393
>
@@ -149,7 +149,7 @@ ITEMS_FILTER_KWARGS='{"cache_ttl": 30.0}'
149149
- `{FILTER_TYPE}_FILTER_ARGS`: Positional arguments (comma-separated)
150150
- `{FILTER_TYPE}_FILTER_KWARGS`: Keyword arguments (comma-separated key=value pairs)
151151

152-
## Built-in Filter Generators
152+
## Built-in Filter Factorys
153153

154154
### Template Filter
155155

@@ -195,14 +195,14 @@ items_cql2 := "true" if {
195195
}
196196
```
197197

198-
## Custom Filter Generators
198+
## Custom Filter Factories
199199

200200
> [!TIP]
201201
> An example integration can be found in [`examples/custom-integration`](https://github.com/developmentseed/stac-auth-proxy/blob/main/examples/custom-integration).
202202
203-
### Complex Filter Generator
203+
### Complex Filter Factory
204204

205-
An example of a more complex filter generator where the filter is generated based on the response of an external API:
205+
An example of a more complex filter factory where the filter is generated based on the response of an external API:
206206

207207
```py
208208
import dataclasses

examples/custom-integration/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Custom Integration Example
22

3-
This example demonstrates how to integrate with a custom filter generator.
3+
This example demonstrates how to integrate with a custom filter factory.
44

55
## Running the Example
66

helm/values.schema.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,13 +92,13 @@ properties:
9292
# Filtering
9393
ITEMS_FILTER_CLS:
9494
type: ["string", "null"]
95-
description: "CQL2 expression generator for item-level filtering"
95+
description: "CQL2 expression factor for item-level filtering"
9696
ITEMS_FILTER_ARGS:
9797
type: ["array", "string"]
98-
description: "Positional arguments for CQL2 expression generator"
98+
description: "Positional arguments for CQL2 expression factor"
9999
ITEMS_FILTER_KWARGS:
100100
type: ["object", "string"]
101-
description: "Keyword arguments for CQL2 expression generator"
101+
description: "Keyword arguments for CQL2 expression factor"
102102
required:
103103
- UPSTREAM_URL
104104
- OIDC_DISCOVERY_URL

src/stac_auth_proxy/filters/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""CQL2 filter generators."""
1+
"""CQL2 filter factories."""
22

33
from .opa import Opa
44
from .template import Template

tests/test_filters_opa.py

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010

1111
@pytest.fixture
12-
def opa_filter_generator():
12+
def opa_filter_factory():
1313
"""Create an OPA instance for testing."""
1414
return Opa(host="http://localhost:8181", decision="stac/filter")
1515

@@ -24,72 +24,72 @@ def mock_opa_response():
2424

2525

2626
@pytest.mark.asyncio
27-
async def test_opa_initialization(opa_filter_generator):
27+
async def test_opa_initialization(opa_filter_factory):
2828
"""Test OPA initialization."""
29-
assert opa_filter_generator.host == "http://localhost:8181"
30-
assert opa_filter_generator.decision == "stac/filter"
31-
assert opa_filter_generator.cache_key == "req.headers.authorization"
32-
assert opa_filter_generator.cache_ttl == 5.0
33-
assert isinstance(opa_filter_generator.client, AsyncClient)
34-
assert opa_filter_generator.cache is not None
29+
assert opa_filter_factory.host == "http://localhost:8181"
30+
assert opa_filter_factory.decision == "stac/filter"
31+
assert opa_filter_factory.cache_key == "req.headers.authorization"
32+
assert opa_filter_factory.cache_ttl == 5.0
33+
assert isinstance(opa_filter_factory.client, AsyncClient)
34+
assert opa_filter_factory.cache is not None
3535

3636

3737
@pytest.mark.asyncio
38-
async def test_opa_cache_hit(opa_filter_generator, mock_opa_response):
38+
async def test_opa_cache_hit(opa_filter_factory, mock_opa_response):
3939
"""Test OPA cache hit behavior."""
4040
context = {"req": {"headers": {"authorization": "test-token"}}}
4141

4242
# Mock the OPA response
4343
with patch.object(
44-
opa_filter_generator.client, "post", new_callable=AsyncMock
44+
opa_filter_factory.client, "post", new_callable=AsyncMock
4545
) as mock_post:
4646
mock_post.return_value = mock_opa_response
4747

4848
# First call should hit OPA
49-
result = await opa_filter_generator(context)
49+
result = await opa_filter_factory(context)
5050
assert result == "collection = 'test'"
5151
assert mock_post.call_count == 1
5252

5353
# Second call should use cache
54-
result = await opa_filter_generator(context)
54+
result = await opa_filter_factory(context)
5555
assert result == "collection = 'test'"
5656
assert mock_post.call_count == 1 # Still 1, no new call made
5757

5858

5959
@pytest.mark.asyncio
60-
async def test_opa_cache_miss(opa_filter_generator, mock_opa_response):
60+
async def test_opa_cache_miss(opa_filter_factory, mock_opa_response):
6161
"""Test OPA cache miss behavior."""
6262
context = {"req": {"headers": {"authorization": "test-token"}}}
6363

6464
with patch.object(
65-
opa_filter_generator.client, "post", new_callable=AsyncMock
65+
opa_filter_factory.client, "post", new_callable=AsyncMock
6666
) as mock_post:
6767
mock_post.return_value = mock_opa_response
6868

6969
# First call with token1
70-
result = await opa_filter_generator(context)
70+
result = await opa_filter_factory(context)
7171
assert result == "collection = 'test'"
7272
assert mock_post.call_count == 1
7373

7474
# Call with different token should miss cache
7575
context["req"]["headers"]["authorization"] = "different-token"
76-
result = await opa_filter_generator(context)
76+
result = await opa_filter_factory(context)
7777
assert result == "collection = 'test'"
7878
assert mock_post.call_count == 2 # New call made
7979

8080

8181
@pytest.mark.asyncio
82-
async def test_opa_error_handling(opa_filter_generator):
82+
async def test_opa_error_handling(opa_filter_factory):
8383
"""Test OPA error handling."""
8484
context = {"req": {"headers": {"authorization": "test-token"}}}
8585

8686
with patch.object(
87-
opa_filter_generator.client, "post", new_callable=AsyncMock
87+
opa_filter_factory.client, "post", new_callable=AsyncMock
8888
) as mock_post:
8989
# Create a mock response that raises an exception on raise_for_status
9090
error_response = MagicMock(spec=Response)
9191
error_response.raise_for_status.side_effect = Exception("Internal server error")
9292
mock_post.return_value = error_response
9393

9494
with pytest.raises(Exception):
95-
await opa_filter_generator(context)
95+
await opa_filter_factory(context)

0 commit comments

Comments
 (0)