Skip to content

Commit db9d81c

Browse files
disable transaction and bulk_transactions extensions by default (#236)
* disable transaction and bulk_transactions extensions by default resolves #231 * enable all extensions in cicd * apply suggestions from review Co-authored-by: Vincent Sarago <vincent@developmentseed.org> * refactor fixtures * remove unused * use specific ENV ENABLE_TRANSACTIONS_EXTENSIONS for transactions (#239) * use specific ENV ENABLE_TRANSACTIONS_EXTENSIONS for transactions * Update docker-compose.yml Co-authored-by: Henry Rodman <henry.rodman@gmail.com> --------- Co-authored-by: Henry Rodman <henry.rodman@gmail.com> * update changelog --------- Co-authored-by: Vincent Sarago <vincent@developmentseed.org> Co-authored-by: vincentsarago <vincent.sarago@gmail.com>
1 parent 476796f commit db9d81c

File tree

6 files changed

+148
-41
lines changed

6 files changed

+148
-41
lines changed

.github/workflows/cicd.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ jobs:
101101
PGDATABASE: postgis
102102
APP_HOST: 0.0.0.0
103103
APP_PORT: 8080
104+
ENABLE_TRANSACTIONS_EXTENSIONS: TRUE
104105

105106
test-docs:
106107
runs-on: ubuntu-latest

CHANGES.md

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
### Changed
66

7+
- add `ENABLE_TRANSACTIONS_EXTENSIONS` env variable to enable `transaction` extensions
8+
- disable transaction and bulk_transactions extensions by default **breaking change**
79
- update `stac-fastapi-*` version requirements to `>=5.2,<6.0`
810
- add pgstac health-check in `/_mgmt/health`
911

docker-compose.yml

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ services:
2121
- DB_MIN_CONN_SIZE=1
2222
- DB_MAX_CONN_SIZE=1
2323
- USE_API_HYDRATE=${USE_API_HYDRATE:-false}
24+
- ENABLE_TRANSACTIONS_EXTENSIONS=TRUE
2425
ports:
2526
- "8082:8082"
2627
volumes:

stac_fastapi/pgstac/app.py

+17-22
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,6 @@
4949

5050
settings = Settings()
5151

52-
# application extensions
53-
application_extensions_map = {
54-
"transaction": TransactionExtension(
55-
client=TransactionsClient(),
56-
settings=settings,
57-
response_class=JSONResponse,
58-
),
59-
"bulk_transactions": BulkTransactionExtension(client=BulkTransactionsClient()),
60-
}
61-
6252
# search extensions
6353
search_extensions_map = {
6454
"query": QueryExtension(),
@@ -93,25 +83,30 @@
9383
"pagination": TokenPaginationExtension(),
9484
}
9585

96-
known_extensions = {
97-
*application_extensions_map.keys(),
86+
enabled_extensions = {
9887
*search_extensions_map.keys(),
9988
*cs_extensions_map.keys(),
10089
*itm_col_extensions_map.keys(),
10190
"collection_search",
10291
}
10392

104-
enabled_extensions = (
105-
os.environ["ENABLED_EXTENSIONS"].split(",")
106-
if "ENABLED_EXTENSIONS" in os.environ
107-
else known_extensions
108-
)
93+
if ext := os.environ.get("ENABLED_EXTENSIONS"):
94+
enabled_extensions = set(ext.split(","))
10995

110-
application_extensions = [
111-
extension
112-
for key, extension in application_extensions_map.items()
113-
if key in enabled_extensions
114-
]
96+
application_extensions = []
97+
98+
if os.environ.get("ENABLE_TRANSACTIONS_EXTENSIONS", "").lower() in ["yes", "true", "1"]:
99+
application_extensions.append(
100+
TransactionExtension(
101+
client=TransactionsClient(),
102+
settings=settings,
103+
response_class=JSONResponse,
104+
),
105+
)
106+
107+
application_extensions.append(
108+
BulkTransactionExtension(client=BulkTransactionsClient()),
109+
)
115110

116111
# /search models
117112
search_extensions = [

tests/api/test_api.py

+50
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,19 @@
6868
)
6969

7070

71+
async def test_default_app_no_transactions(
72+
app_client_no_transaction, load_test_data, load_test_collection
73+
):
74+
coll = load_test_collection
75+
item = load_test_data("test_item.json")
76+
resp = await app_client_no_transaction.post(
77+
f"/collections/{coll['id']}/items", json=item
78+
)
79+
80+
# the default application does not have the transaction extensions enabled!
81+
assert resp.status_code == 405
82+
83+
7184
async def test_post_search_content_type(app_client):
7285
params = {"limit": 1}
7386
resp = await app_client.post("search", json=params)
@@ -892,3 +905,40 @@ async def test_no_extension(
892905

893906
finally:
894907
await close_db_connection(app)
908+
909+
910+
async def test_default_app(default_client, default_app, load_test_data):
911+
api_routes = {
912+
f"{list(route.methods)[0]} {route.path}" for route in default_app.routes
913+
}
914+
assert set(STAC_CORE_ROUTES).issubset(api_routes)
915+
assert set(STAC_TRANSACTION_ROUTES).issubset(api_routes)
916+
917+
# Load collections
918+
col = load_test_data("test_collection.json")
919+
resp = await default_client.post("/collections", json=col)
920+
assert resp.status_code == 201
921+
922+
# Load items
923+
item = load_test_data("test_item.json")
924+
resp = await default_client.post(f"/collections/{col['id']}/items", json=item)
925+
assert resp.status_code == 201
926+
927+
resp = await default_client.get("/conformance")
928+
assert resp.status_code == 200
929+
conf = resp.json()["conformsTo"]
930+
assert (
931+
"https://api.stacspec.org/v1.0.0/ogcapi-features/extensions/transaction" in conf
932+
)
933+
assert "https://api.stacspec.org/v1.0.0/collections/extensions/transaction" in conf
934+
assert "http://www.opengis.net/spec/cql2/1.0/conf/basic-cql2" in conf
935+
assert "http://www.opengis.net/spec/ogcapi-common-2/1.0/conf/simple-query" in conf
936+
assert "http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/core" in conf
937+
assert (
938+
"http://www.opengis.net/spec/ogcapi-features-3/1.0/conf/features-filter" in conf
939+
)
940+
assert "http://www.opengis.net/spec/ogcapi-features-3/1.0/conf/filter" in conf
941+
assert "https://api.stacspec.org/v1.0.0-rc.1/collection-search" in conf
942+
assert "https://api.stacspec.org/v1.0.0/collections" in conf
943+
assert "https://api.stacspec.org/v1.0.0/ogcapi-features#query" in conf
944+
assert "https://api.stacspec.org/v1.0.0/ogcapi-features#sort" in conf

tests/conftest.py

+77-19
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import asyncpg
1010
import pytest
1111
from fastapi import APIRouter
12-
from fastapi.responses import ORJSONResponse
1312
from httpx import ASGITransport, AsyncClient
1413
from pypgstac import __version__ as pgstac_version
1514
from pypgstac.db import PgstacDB
@@ -18,6 +17,7 @@
1817
from stac_fastapi.api.app import StacApi
1918
from stac_fastapi.api.models import (
2019
ItemCollectionUri,
20+
JSONResponse,
2121
create_get_request_model,
2222
create_post_request_model,
2323
create_request_model,
@@ -189,7 +189,7 @@ def api_client(request):
189189
search_get_request_model=search_get_request_model,
190190
search_post_request_model=search_post_request_model,
191191
collections_get_request_model=collection_search_extension.GET,
192-
response_class=ORJSONResponse,
192+
response_class=JSONResponse,
193193
router=APIRouter(prefix=prefix),
194194
health_check=health_check,
195195
)
@@ -290,14 +290,11 @@ async def load_test2_item(app_client, load_test_data, load_test2_collection):
290290
return Item.model_validate(resp.json())
291291

292292

293-
@pytest.fixture(
294-
scope="session",
295-
)
296-
def api_client_no_ext():
297-
api_settings = Settings(
298-
testing=True,
299-
)
300-
return StacApi(
293+
@pytest.fixture(scope="function")
294+
async def app_no_ext(database):
295+
"""Default stac-fastapi-pgstac application without only the transaction extensions."""
296+
api_settings = Settings(testing=True)
297+
api_client_no_ext = StacApi(
301298
settings=api_settings,
302299
extensions=[
303300
TransactionExtension(client=TransactionsClient(), settings=api_settings)
@@ -306,9 +303,6 @@ def api_client_no_ext():
306303
health_check=health_check,
307304
)
308305

309-
310-
@pytest.fixture(scope="function")
311-
async def app_no_ext(api_client_no_ext, database):
312306
postgres_settings = PostgresSettings(
313307
postgres_user=database.user,
314308
postgres_pass=database.password,
@@ -319,12 +313,9 @@ async def app_no_ext(api_client_no_ext, database):
319313
)
320314
logger.info("Creating app Fixture")
321315
time.time()
322-
app = api_client_no_ext.app
323-
await connect_to_db(app, postgres_settings=postgres_settings)
324-
325-
yield app
326-
327-
await close_db_connection(app)
316+
await connect_to_db(api_client_no_ext.app, postgres_settings=postgres_settings)
317+
yield api_client_no_ext.app
318+
await close_db_connection(api_client_no_ext.app)
328319

329320
logger.info("Closed Pools.")
330321

@@ -336,3 +327,70 @@ async def app_client_no_ext(app_no_ext):
336327
transport=ASGITransport(app=app_no_ext), base_url="http://test"
337328
) as c:
338329
yield c
330+
331+
332+
@pytest.fixture(scope="function")
333+
async def app_no_transaction(database):
334+
"""Default stac-fastapi-pgstac application without any extensions."""
335+
api_settings = Settings(testing=True)
336+
api = StacApi(
337+
settings=api_settings,
338+
extensions=[],
339+
client=CoreCrudClient(),
340+
health_check=health_check,
341+
)
342+
343+
postgres_settings = PostgresSettings(
344+
postgres_user=database.user,
345+
postgres_pass=database.password,
346+
postgres_host_reader=database.host,
347+
postgres_host_writer=database.host,
348+
postgres_port=database.port,
349+
postgres_dbname=database.dbname,
350+
)
351+
logger.info("Creating app Fixture")
352+
time.time()
353+
await connect_to_db(api.app, postgres_settings=postgres_settings)
354+
yield api.app
355+
await close_db_connection(api.app)
356+
357+
logger.info("Closed Pools.")
358+
359+
360+
@pytest.fixture(scope="function")
361+
async def app_client_no_transaction(app_no_transaction):
362+
logger.info("creating app_client")
363+
async with AsyncClient(
364+
transport=ASGITransport(app=app_no_transaction), base_url="http://test"
365+
) as c:
366+
yield c
367+
368+
369+
@pytest.fixture(scope="function")
370+
async def default_app(database, monkeypatch):
371+
"""Test default stac-fastapi-pgstac application."""
372+
monkeypatch.setenv("POSTGRES_USER", database.user)
373+
monkeypatch.setenv("POSTGRES_PASS", database.password)
374+
monkeypatch.setenv("POSTGRES_HOST_READER", database.host)
375+
monkeypatch.setenv("POSTGRES_HOST_WRITER", database.host)
376+
monkeypatch.setenv("POSTGRES_PORT", str(database.port))
377+
monkeypatch.setenv("POSTGRES_DBNAME", database.dbname)
378+
monkeypatch.delenv("ENABLED_EXTENSIONS", raising=False)
379+
380+
monkeypatch.setenv("ENABLE_TRANSACTIONS_EXTENSIONS", "TRUE")
381+
monkeypatch.setenv("USE_API_HYDRATE", "TRUE")
382+
monkeypatch.setenv("ENABLE_RESPONSE_MODELS", "TRUE")
383+
384+
from stac_fastapi.pgstac.app import app
385+
386+
await connect_to_db(app)
387+
yield app
388+
await close_db_connection(app)
389+
390+
391+
@pytest.fixture(scope="function")
392+
async def default_client(default_app):
393+
async with AsyncClient(
394+
transport=ASGITransport(app=default_app), base_url="http://test"
395+
) as c:
396+
yield c

0 commit comments

Comments
 (0)