diff --git a/stac_fastapi/api/stac_fastapi/api/app.py b/stac_fastapi/api/stac_fastapi/api/app.py index 72ddf19a9..b59978e3a 100644 --- a/stac_fastapi/api/stac_fastapi/api/app.py +++ b/stac_fastapi/api/stac_fastapi/api/app.py @@ -6,8 +6,8 @@ from fastapi import APIRouter, FastAPI from fastapi.openapi.utils import get_openapi from stac_pydantic import Collection, Item, ItemCollection -from stac_pydantic.api.collections import Collections from stac_pydantic.api import ConformanceClasses, LandingPage +from stac_pydantic.api.collections import Collections from stac_pydantic.version import STAC_VERSION from stac_fastapi.api.errors import DEFAULT_STATUS_CODES, add_exception_handlers diff --git a/stac_fastapi/api/stac_fastapi/api/routes.py b/stac_fastapi/api/stac_fastapi/api/routes.py index b01aa3f1a..39a32c373 100644 --- a/stac_fastapi/api/stac_fastapi/api/routes.py +++ b/stac_fastapi/api/stac_fastapi/api/routes.py @@ -49,8 +49,7 @@ def _endpoint( else: def _endpoint( - request: Request, - request_data: request_model, # type:ignore + request: Request, request_data: request_model, # type:ignore ): """Endpoint.""" resp = func(request_data, request=request) @@ -73,8 +72,7 @@ async def _endpoint( else: async def _endpoint( - request: Request, - request_data: request_model, # type:ignore + request: Request, request_data: request_model, # type:ignore ): """Endpoint.""" resp = await func(request_data, request=request) diff --git a/stac_fastapi/extensions/stac_fastapi/extensions/third_party/tiles.py b/stac_fastapi/extensions/stac_fastapi/extensions/third_party/tiles.py index 7bb3a3607..6856181a3 100644 --- a/stac_fastapi/extensions/stac_fastapi/extensions/third_party/tiles.py +++ b/stac_fastapi/extensions/stac_fastapi/extensions/third_party/tiles.py @@ -7,8 +7,8 @@ from fastapi import FastAPI from pydantic import BaseModel from stac_pydantic.collection import SpatialExtent -from stac_pydantic.shared import MimeTypes from stac_pydantic.links import Link, Relations +from stac_pydantic.shared import MimeTypes from starlette.requests import Request from starlette.responses import HTMLResponse, RedirectResponse @@ -45,8 +45,7 @@ class TileLinks: def __attrs_post_init__(self): """Post init handler.""" self.item_uri = urljoin( - self.base_url, - f"collections/{self.collection_id}/items/{self.item_id}", + self.base_url, f"collections/{self.collection_id}/items/{self.item_id}", ) def tiles(self) -> OGCTileLink: @@ -66,8 +65,7 @@ def viewer(self) -> OGCTileLink: """Create viewer link.""" return OGCTileLink( href=urljoin( - self.base_url, - f"{self.route_prefix}/viewer?url={self.item_uri}", + self.base_url, f"{self.route_prefix}/viewer?url={self.item_uri}", ), rel=Relations.alternate, type=MimeTypes.html, @@ -78,8 +76,7 @@ def tilejson(self) -> OGCTileLink: """Create tilejson link.""" return OGCTileLink( href=urljoin( - self.base_url, - f"{self.route_prefix}/tilejson.json?url={self.item_uri}", + self.base_url, f"{self.route_prefix}/tilejson.json?url={self.item_uri}", ), rel=Relations.alternate, type=MimeTypes.json, diff --git a/stac_fastapi/pgstac/stac_fastapi/pgstac/core.py b/stac_fastapi/pgstac/stac_fastapi/pgstac/core.py index 7f386c88a..e2a8d9dd5 100644 --- a/stac_fastapi/pgstac/stac_fastapi/pgstac/core.py +++ b/stac_fastapi/pgstac/stac_fastapi/pgstac/core.py @@ -10,8 +10,9 @@ from fastapi.responses import ORJSONResponse from stac_pydantic import Collection, Item, ItemCollection from stac_pydantic.api import ConformanceClasses, LandingPage -from stac_pydantic.shared import MimeTypes +from stac_pydantic.api.collections import Collections from stac_pydantic.links import Link, Relations +from stac_pydantic.shared import MimeTypes from stac_fastapi.pgstac.models.links import CollectionLinks, ItemLinks, PagingLinks from stac_fastapi.pgstac.types.search import PgstacSearch @@ -39,11 +40,7 @@ async def landing_page(self, **kwargs) -> ORJSONResponse: title="Arturo STAC API", description="Arturo raster datastore", links=[ - Link( - rel=Relations.self, - type=MimeTypes.json, - href=str(base_url), - ), + Link(rel=Relations.self, type=MimeTypes.json, href=str(base_url),), Link( rel=Relations.docs, type=MimeTypes.html, @@ -112,10 +109,23 @@ async def _all_collections_func(self, **kwargs) -> List[Collection]: async def all_collections(self, **kwargs) -> ORJSONResponse: """Get all collections.""" + request = kwargs["request"] + base_url = str(request.base_url) + url = str(request.url) collections = await self._all_collections_func(**kwargs) - if collections is None or len(collections) < 1: - return ORJSONResponse([]) - return ORJSONResponse([c.dict(exclude_none=True) for c in collections]) + links = [ + Link(rel=Relations.self, type=MimeTypes.json, href=url), + Link(rel=Relations.parent, type=MimeTypes.json, href=base_url), + Link(rel=Relations.root, type=MimeTypes.json, href=base_url), + ] + if collections is None: + return ORJSONResponse(Collections(collections=[], links=links)) + return ORJSONResponse( + Collections( + collections=[c.dict(exclude_none=True) for c in collections], + links=links, + ) + ) async def get_collection(self, id: str, **kwargs) -> ORJSONResponse: """Get collection by id. @@ -195,15 +205,11 @@ async def _search_base( include = search_request.fields.include if len(include) == 0: include = None - feature = feature.dict( - exclude_none=True, - ) + feature = feature.dict(exclude_none=True,) cleaned_features.append(feature) collection.features = cleaned_features collection.links = await PagingLinks( - request=request, - next=next, - prev=prev, + request=request, next=next, prev=prev, ).get_links() return collection diff --git a/stac_fastapi/pgstac/stac_fastapi/pgstac/db.py b/stac_fastapi/pgstac/stac_fastapi/pgstac/db.py index 63d7f915a..1c6bccd54 100644 --- a/stac_fastapi/pgstac/stac_fastapi/pgstac/db.py +++ b/stac_fastapi/pgstac/stac_fastapi/pgstac/db.py @@ -19,16 +19,10 @@ async def con_init(conn): """Use orjson for json returns.""" await conn.set_type_codec( - "json", - encoder=orjson.dumps, - decoder=orjson.loads, - schema="pg_catalog", + "json", encoder=orjson.dumps, decoder=orjson.loads, schema="pg_catalog", ) await conn.set_type_codec( - "jsonb", - encoder=orjson.dumps, - decoder=orjson.loads, - schema="pg_catalog", + "jsonb", encoder=orjson.dumps, decoder=orjson.loads, schema="pg_catalog", ) diff --git a/stac_fastapi/pgstac/stac_fastapi/pgstac/models/links.py b/stac_fastapi/pgstac/stac_fastapi/pgstac/models/links.py index 431d4af2f..fd8a120d4 100644 --- a/stac_fastapi/pgstac/stac_fastapi/pgstac/models/links.py +++ b/stac_fastapi/pgstac/stac_fastapi/pgstac/models/links.py @@ -5,8 +5,8 @@ import attr from stac_pydantic.api.extensions.paging import PaginationLink -from stac_pydantic.shared import MimeTypes from stac_pydantic.links import Link, Relations +from stac_pydantic.shared import MimeTypes from starlette.requests import Request from stac_fastapi.extensions.third_party.tiles import OGCTileLink @@ -113,10 +113,7 @@ def link_next(self) -> PaginationLink: if method == "GET": href = merge_params(self.url, {"token": f"next:{self.next}"}) link = PaginationLink( - rel=Relations.next, - type=MimeTypes.json, - method=method, - href=href, + rel=Relations.next, type=MimeTypes.json, method=method, href=href, ) return link if method == "POST": @@ -179,11 +176,7 @@ def link_self(self) -> Link: def link_parent(self) -> Link: """Create the `parent` link.""" - return Link( - rel=Relations.parent, - type=MimeTypes.json, - href=self.base_url, - ) + return Link(rel=Relations.parent, type=MimeTypes.json, href=self.base_url,) def link_items(self) -> Link: """Create the `item` link.""" @@ -241,8 +234,7 @@ class TileLinks: def __post_init__(self): """Post init handler.""" self.item_uri = urljoin( - self.base_url, - f"/collections/{self.collection_id}/items/{self.item_id}", + self.base_url, f"/collections/{self.collection_id}/items/{self.item_id}", ) def link_tiles(self) -> OGCTileLink: @@ -280,8 +272,7 @@ def link_wmts(self) -> OGCTileLink: """Create wmts capabilities link.""" return OGCTileLink( href=urljoin( - self.base_url, - f"/titiler/WMTSCapabilities.xml?url={self.item_uri}", + self.base_url, f"/titiler/WMTSCapabilities.xml?url={self.item_uri}", ), rel=Relations.alternate, type=MimeTypes.xml, diff --git a/stac_fastapi/pgstac/stac_fastapi/pgstac/models/schemas.py b/stac_fastapi/pgstac/stac_fastapi/pgstac/models/schemas.py index ebbadaa81..4b3e00642 100644 --- a/stac_fastapi/pgstac/stac_fastapi/pgstac/models/schemas.py +++ b/stac_fastapi/pgstac/stac_fastapi/pgstac/models/schemas.py @@ -7,8 +7,8 @@ from pydantic import BaseModel from stac_pydantic import Collection as CollectionBase from stac_pydantic import Item as ItemBase -from stac_pydantic.shared import DATETIME_RFC339 from stac_pydantic.links import Link +from stac_pydantic.shared import DATETIME_RFC339 # Be careful: https://github.com/samuelcolvin/pydantic/issues/1423#issuecomment-642797287 NumType = Union[float, int] diff --git a/stac_fastapi/pgstac/tests/clients/test_postgres.py b/stac_fastapi/pgstac/tests/clients/test_postgres.py index 009872585..3015bcfb4 100644 --- a/stac_fastapi/pgstac/tests/clients/test_postgres.py +++ b/stac_fastapi/pgstac/tests/clients/test_postgres.py @@ -11,10 +11,7 @@ async def test_create_collection(app_client, load_test_data: Callable): in_json = load_test_data("test_collection.json") in_coll = Collection.parse_obj(in_json) - resp = await app_client.post( - "/collections", - json=in_json, - ) + resp = await app_client.post("/collections", json=in_json,) assert resp.status_code == 200 post_coll = Collection.parse_obj(resp.json()) assert in_coll.dict(exclude={"links"}) == post_coll.dict(exclude={"links"}) @@ -57,10 +54,7 @@ async def test_create_item(app_client, load_test_data: Callable, load_test_colle in_json = load_test_data("test_item.json") in_item = Item.parse_obj(in_json) - resp = await app_client.post( - "/collections/{coll.id}/items", - json=in_json, - ) + resp = await app_client.post("/collections/{coll.id}/items", json=in_json,) assert resp.status_code == 200 post_item = Item.parse_obj(resp.json()) @@ -111,15 +105,10 @@ async def test_get_collection_items(app_client, load_test_collection, load_test_ for _ in range(4): item.id = str(uuid.uuid4()) - resp = await app_client.post( - f"/collections/{coll.id}/items", - json=item.dict(), - ) + resp = await app_client.post(f"/collections/{coll.id}/items", json=item.dict(),) assert resp.status_code == 200 - resp = await app_client.get( - f"/collections/{coll.id}/items", - ) + resp = await app_client.get(f"/collections/{coll.id}/items",) assert resp.status_code == 200 fc = resp.json() assert "features" in fc diff --git a/stac_fastapi/pgstac/tests/conftest.py b/stac_fastapi/pgstac/tests/conftest.py index 0a899a33f..f8d87258d 100644 --- a/stac_fastapi/pgstac/tests/conftest.py +++ b/stac_fastapi/pgstac/tests/conftest.py @@ -127,10 +127,7 @@ def load_file(filename: str) -> Dict: @pytest.fixture async def load_test_collection(app_client, load_test_data): data = load_test_data("test_collection.json") - resp = await app_client.post( - "/collections", - json=data, - ) + resp = await app_client.post("/collections", json=data,) assert resp.status_code == 200 return Collection.parse_obj(resp.json()) @@ -138,9 +135,6 @@ async def load_test_collection(app_client, load_test_data): @pytest.fixture async def load_test_item(app_client, load_test_data, load_test_collection): data = load_test_data("test_item.json") - resp = await app_client.post( - "/collections/{coll.id}/items", - json=data, - ) + resp = await app_client.post("/collections/{coll.id}/items", json=data,) assert resp.status_code == 200 return Item.parse_obj(resp.json()) diff --git a/stac_fastapi/pgstac/tests/resources/test_collection.py b/stac_fastapi/pgstac/tests/resources/test_collection.py index 66c594d15..ffd1f87e8 100644 --- a/stac_fastapi/pgstac/tests/resources/test_collection.py +++ b/stac_fastapi/pgstac/tests/resources/test_collection.py @@ -8,10 +8,7 @@ async def test_create_collection(app_client, load_test_data: Callable): in_json = load_test_data("test_collection.json") in_coll = Collection.parse_obj(in_json) - resp = await app_client.post( - "/collections", - json=in_json, - ) + resp = await app_client.post("/collections", json=in_json,) assert resp.status_code == 200 post_coll = Collection.parse_obj(resp.json()) assert in_coll.dict(exclude={"links"}) == post_coll.dict(exclude={"links"}) @@ -54,23 +51,15 @@ async def test_delete_collection( async def test_create_collection_conflict(app_client, load_test_data: Callable): in_json = load_test_data("test_collection.json") Collection.parse_obj(in_json) - resp = await app_client.post( - "/collections", - json=in_json, - ) + resp = await app_client.post("/collections", json=in_json,) assert resp.status_code == 200 Collection.parse_obj(resp.json()) - resp = await app_client.post( - "/collections", - json=in_json, - ) + resp = await app_client.post("/collections", json=in_json,) assert resp.status_code == 409 @pytest.mark.asyncio -async def test_delete_missing_collection( - app_client, -): +async def test_delete_missing_collection(app_client,): resp = await app_client.delete("/collections") assert resp.status_code == 405 @@ -85,8 +74,6 @@ async def test_update_new_collection(app_client, load_test_collection): @pytest.mark.asyncio -async def test_nocollections( - app_client, -): +async def test_nocollections(app_client,): resp = await app_client.get("/collections") assert resp.status_code == 200 diff --git a/stac_fastapi/pgstac/tests/resources/test_item.py b/stac_fastapi/pgstac/tests/resources/test_item.py index e2bf7c755..bc978e16c 100644 --- a/stac_fastapi/pgstac/tests/resources/test_item.py +++ b/stac_fastapi/pgstac/tests/resources/test_item.py @@ -14,10 +14,7 @@ async def test_create_collection(app_client, load_test_data: Callable): in_json = load_test_data("test_collection.json") in_coll = Collection.parse_obj(in_json) - resp = await app_client.post( - "/collections", - json=in_json, - ) + resp = await app_client.post("/collections", json=in_json,) assert resp.status_code == 200 post_coll = Collection.parse_obj(resp.json()) assert in_coll.dict(exclude={"links"}) == post_coll.dict(exclude={"links"}) @@ -62,10 +59,7 @@ async def test_create_item(app_client, load_test_data: Callable, load_test_colle in_json = load_test_data("test_item.json") in_item = Item.parse_obj(in_json) - resp = await app_client.post( - "/collections/{coll.id}/items", - json=in_json, - ) + resp = await app_client.post("/collections/{coll.id}/items", json=in_json,) assert resp.status_code == 200 post_item = Item.parse_obj(resp.json()) @@ -120,15 +114,10 @@ async def test_get_collection_items(app_client, load_test_collection, load_test_ for _ in range(4): item.id = str(uuid.uuid4()) - resp = await app_client.post( - f"/collections/{coll.id}/items", - json=item.dict(), - ) + resp = await app_client.post(f"/collections/{coll.id}/items", json=item.dict(),) assert resp.status_code == 200 - resp = await app_client.get( - f"/collections/{coll.id}/items", - ) + resp = await app_client.get(f"/collections/{coll.id}/items",) assert resp.status_code == 200 fc = resp.json() assert "features" in fc @@ -143,16 +132,10 @@ async def test_create_item_conflict( in_json = load_test_data("test_item.json") Item.parse_obj(in_json) - resp = await app_client.post( - "/collections/{coll.id}/items", - json=in_json, - ) + resp = await app_client.post("/collections/{coll.id}/items", json=in_json,) assert resp.status_code == 200 - resp = await app_client.post( - "/collections/{coll.id}/items", - json=in_json, - ) + resp = await app_client.post("/collections/{coll.id}/items", json=in_json,) assert resp.status_code == 409 diff --git a/stac_fastapi/sqlalchemy/alembic/env.py b/stac_fastapi/sqlalchemy/alembic/env.py index 20af555b0..3e360c14b 100644 --- a/stac_fastapi/sqlalchemy/alembic/env.py +++ b/stac_fastapi/sqlalchemy/alembic/env.py @@ -72,9 +72,7 @@ def run_migrations_online(): configuration = config.get_section(config.config_ini_section) configuration["sqlalchemy.url"] = get_connection_url() connectable = engine_from_config( - configuration, - prefix="sqlalchemy.", - poolclass=pool.NullPool, + configuration, prefix="sqlalchemy.", poolclass=pool.NullPool, ) with connectable.connect() as connection: diff --git a/stac_fastapi/sqlalchemy/stac_fastapi/sqlalchemy/models/schemas.py b/stac_fastapi/sqlalchemy/stac_fastapi/sqlalchemy/models/schemas.py index 6e9a50c3c..daecdcab8 100644 --- a/stac_fastapi/sqlalchemy/stac_fastapi/sqlalchemy/models/schemas.py +++ b/stac_fastapi/sqlalchemy/stac_fastapi/sqlalchemy/models/schemas.py @@ -7,8 +7,8 @@ from pydantic import BaseModel from stac_pydantic import Collection as CollectionBase from stac_pydantic import Item as ItemBase -from stac_pydantic.shared import DATETIME_RFC339 from stac_pydantic.links import Link +from stac_pydantic.shared import DATETIME_RFC339 from stac_fastapi.sqlalchemy.models.decompose import CollectionGetter, ItemGetter diff --git a/stac_fastapi/types/stac_fastapi/types/core.py b/stac_fastapi/types/stac_fastapi/types/core.py index 87091b754..70650cce7 100644 --- a/stac_fastapi/types/stac_fastapi/types/core.py +++ b/stac_fastapi/types/stac_fastapi/types/core.py @@ -7,8 +7,8 @@ import attr from stac_pydantic import Collection, Item, ItemCollection from stac_pydantic.api import ConformanceClasses, LandingPage, Search -from stac_pydantic.shared import MimeTypes from stac_pydantic.links import Link, Relations +from stac_pydantic.shared import MimeTypes from stac_pydantic.version import STAC_VERSION from stac_fastapi.types.extension import ApiExtension diff --git a/stac_fastapi/types/stac_fastapi/types/links.py b/stac_fastapi/types/stac_fastapi/types/links.py index 9ee26a119..05dae706e 100644 --- a/stac_fastapi/types/stac_fastapi/types/links.py +++ b/stac_fastapi/types/stac_fastapi/types/links.py @@ -4,8 +4,8 @@ from urllib.parse import urljoin import attr -from stac_pydantic.shared import MimeTypes from stac_pydantic.links import Link, Relations +from stac_pydantic.shared import MimeTypes # These can be inferred from the item/collection so they aren't included in the database # Instead they are dynamically generated when querying the database using the classes defined below