From 95f905fb4e12d491603102928319d79e957ef3d2 Mon Sep 17 00:00:00 2001 From: Nathan Zimmerman Date: Wed, 11 Aug 2021 09:43:05 -0500 Subject: [PATCH] Add base and extension conformance distinction --- stac_fastapi/types/stac_fastapi/types/core.py | 61 +++++++++++++++---- .../types/stac_fastapi/types/extension.py | 3 + 2 files changed, 51 insertions(+), 13 deletions(-) diff --git a/stac_fastapi/types/stac_fastapi/types/core.py b/stac_fastapi/types/stac_fastapi/types/core.py index 6b91fa6c6..e4e931ab4 100644 --- a/stac_fastapi/types/stac_fastapi/types/core.py +++ b/stac_fastapi/types/stac_fastapi/types/core.py @@ -22,6 +22,7 @@ class BaseTransactionsClient(abc.ABC): """Defines a pattern for implementing the STAC transaction extension.""" + @abc.abstractmethod def create_item(self, item: stac_types.Item, **kwargs) -> stac_types.Item: """Create a new item. @@ -231,22 +232,16 @@ class LandingPageMixin: landing_page_id: str = attr.ib(default="stac-fastapi") title: str = attr.ib(default="stac-fastapi") description: str = attr.ib(default="stac-fastapi") - conformance_classes: List[str] = attr.ib( - factory=lambda: [ - "https://api.stacspec.org/v1.0.0-beta.2/core", - "https://api.stacspec.org/v1.0.0-beta.2/ogcapi-features", - "https://api.stacspec.org/v1.0.0-beta.2/item-search", - ] - ) - def _landing_page(self, base_url: str) -> stac_types.LandingPage: + + def _landing_page(self, base_url: str, conformance_classes: List[str]) -> stac_types.LandingPage: landing_page = stac_types.LandingPage( type="Catalog", id=self.landing_page_id, title=self.title, description=self.description, stac_version=self.stac_version, - conformsTo=self.conformance_classes, + conformsTo=conformance_classes, links=[ { "rel": Relations.self.value, @@ -290,6 +285,16 @@ class BaseCoreClient(LandingPageMixin, abc.ABC): extensions: list of registered api extensions. """ + base_conformance_classes: List[str] = attr.ib( + factory=lambda: [ + "https://api.stacspec.org/v1.0.0-beta.2/core", + "https://api.stacspec.org/v1.0.0-beta.2/ogcapi-features", + "https://api.stacspec.org/v1.0.0-beta.2/item-search", + "http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/core", + "http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/oas30", + "http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/geojson", + ] + ) extensions: List[ApiExtension] = attr.ib(default=attr.Factory(list)) conformance_classes: List[str] = attr.ib( factory=lambda: [ @@ -298,6 +303,16 @@ class BaseCoreClient(LandingPageMixin, abc.ABC): ] ) + def conformance_classes(self) -> List[str]: + """Generate conformance classes by adding extension conformance to base conformance classes""" + base_conformance_classes = self.base_conformance_classes.copy() + + for extension in self.extensions: + extension_classes = getattr(extension, "conformance_classes", []) + base_conformance_classes.extend(extension_classes) + + return list(set(base_conformance_classes)) + def extension_is_enabled(self, extension: Type[ApiExtension]) -> bool: """Check if an api extension is enabled.""" return any([isinstance(ext, extension) for ext in self.extensions]) @@ -311,7 +326,7 @@ def landing_page(self, **kwargs) -> stac_types.LandingPage: API landing page, serving as an entry point to the API. """ base_url = str(kwargs["request"].base_url) - landing_page = self._landing_page(base_url=base_url) + landing_page = self._landing_page(base_url=base_url, conformance_classes=self.conformance_classes()) collections = self.all_collections(request=kwargs["request"]) for collection in collections: landing_page["links"].append( @@ -332,7 +347,7 @@ def conformance(self, **kwargs) -> stac_types.Conformance: Returns: Conformance classes which the server conforms to. """ - return Conformance(conformsTo=self.conformance_classes) + return Conformance(conformsTo=self.conformance_classes()) @abc.abstractmethod def post_search( @@ -440,6 +455,16 @@ class AsyncBaseCoreClient(LandingPageMixin, abc.ABC): extensions: list of registered api extensions. """ + base_conformance_classes: List[str] = attr.ib( + factory=lambda: [ + "https://api.stacspec.org/v1.0.0-beta.2/core", + "https://api.stacspec.org/v1.0.0-beta.2/ogcapi-features", + "https://api.stacspec.org/v1.0.0-beta.2/item-search", + "http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/core", + "http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/oas30", + "http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/geojson", + ] + ) extensions: List[ApiExtension] = attr.ib(default=attr.Factory(list)) conformance_classes: List[str] = attr.ib( factory=lambda: [ @@ -448,6 +473,16 @@ class AsyncBaseCoreClient(LandingPageMixin, abc.ABC): ] ) + def conformance_classes(self) -> List[str]: + """Generate conformance classes by adding extension conformance to base conformance classes""" + conformance_classes = self.base_conformance_classes.copy() + + for extension in self.extensions: + extension_classes = getattr(extension, "conformance_classes", []) + conformance_classes.extend(extension_classes) + + return list(set(conformance_classes)) + def extension_is_enabled(self, extension: Type[ApiExtension]) -> bool: """Check if an api extension is enabled.""" return any([isinstance(ext, extension) for ext in self.extensions]) @@ -461,7 +496,7 @@ async def landing_page(self, **kwargs) -> stac_types.LandingPage: API landing page, serving as an entry point to the API. """ base_url = str(kwargs["request"].base_url) - landing_page = self._landing_page(base_url=base_url) + landing_page = self._landing_page(base_url=base_url, conformance_classes=self.conformance_classes()) collections = await self.all_collections(request=kwargs["request"]) for collection in collections: landing_page["links"].append( @@ -482,7 +517,7 @@ async def conformance(self, **kwargs) -> stac_types.Conformance: Returns: Conformance classes which the server conforms to. """ - return Conformance(conformsTo=self.conformance_classes) + return Conformance(conformsTo=self.conformance_classes()) @abc.abstractmethod async def post_search( diff --git a/stac_fastapi/types/stac_fastapi/types/extension.py b/stac_fastapi/types/stac_fastapi/types/extension.py index 144790999..8b6a95729 100644 --- a/stac_fastapi/types/stac_fastapi/types/extension.py +++ b/stac_fastapi/types/stac_fastapi/types/extension.py @@ -1,5 +1,6 @@ """base api extension.""" import abc +from typing import List import attr from fastapi import FastAPI @@ -9,6 +10,8 @@ class ApiExtension(abc.ABC): """Abstract base class for defining API extensions.""" + conformance_classes: List[str] = attr.ib(factory=list) + @abc.abstractmethod def register(self, app: FastAPI) -> None: """Register the extension with a FastAPI application.