Skip to content

Commit

Permalink
remove pagination extension dependency and add request model attribut…
Browse files Browse the repository at this point in the history
…es (#718)

* remove pagination extension dependency and add request model attributes

* add migration guide

---------

Co-authored-by: Jonathan Healy <[email protected]>
  • Loading branch information
vincentsarago and jonhealy1 authored Jul 1, 2024
1 parent 270e03d commit 1062ac4
Show file tree
Hide file tree
Showing 6 changed files with 277 additions and 19 deletions.
16 changes: 15 additions & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,23 @@

## [Unreleased] - TBD

### Added

* Add attributes to `stac_fastapi.api.app.StacApi` to enable customization of request model for:
- `/collections`: **collections_get_request_model**, default to `EmptyRequest`
- `/collections/{collection_id}`: **collection_get_request_model**, default to `CollectionUri`
- `/collections/{collection_id}/items`: **items_get_request_model**, default to `ItemCollectionUri`
- `/collections/{collection_id}/items/{item_id}`: **item_get_request_model**, default to `ItemUri`

### Removed

* Removed the Filter Extension dependency from `AggregationExtensionPostRequest` and `AggregationExtensionGetRequest` [#716](https://github.com/stac-utils/stac-fastapi/pull/716)
* Removed `pagination_extension` attribute in `stac_fastapi.api.app.StacApi`
* Removed use of `pagination_extension` in `register_get_item_collection` function (User now need to construct the request model and pass it using `items_get_request_model` attribute)

### Changed

* moved `GETPagination`, `POSTPagination`, `GETTokenPagination` and `POSTTokenPagination` to `stac_fastapi.extensions.core.pagination.request` submodule [#717](https://github.com/stac-utils/stac-fastapi/pull/717)
* Moved `GETPagination`, `POSTPagination`, `GETTokenPagination` and `POSTTokenPagination` to `stac_fastapi.extensions.core.pagination.request` submodule [#717](https://github.com/stac-utils/stac-fastapi/pull/717)

## [3.0.0a4] - 2024-06-27

Expand Down
2 changes: 2 additions & 0 deletions docs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ nav:
- search: api/stac_fastapi/types/search.md
- stac: api/stac_fastapi/types/stac.md
- version: api/stac_fastapi/types/version.md
- Migration Guides:
- v2.5 -> v3.0: migrations/v3.0.0.md
- Performance Benchmarks: benchmarks.html
- Development - Contributing: "contributing.md"
- Release Notes: "release-notes.md"
Expand Down
163 changes: 163 additions & 0 deletions docs/src/migrations/v3.0.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@

# stac-fastapi v3.0 Migration Guide

This document aims to help you update your application from **stac-fastapi** 2.5 to 3.0.0.

## Dependencies

- **pydantic~=2.0**
- **fastapi>=0.111**
- **stac-pydantic~=3.1**

Most of the **stac-fastapi's** dependencies have been upgraded. Moving from pydantic v1 to v2 is mostly the one update bringing most breaking changes (see https://docs.pydantic.dev/latest/migration/).

In addition to pydantic v2 update, `stac-pydantic` has been updated to better match the STAC and STAC-API specifications (see https://github.com/stac-utils/stac-pydantic/blob/main/CHANGELOG.md#310-2024-05-21)


## Deprecation

* the `ContextExtension` have been removed (see https://github.com/stac-utils/stac-pydantic/pull/138) and was replaced by optional `NumberMatched` and `NumberReturned` attributes, defined by the OGC features specification.

* `stac_fastapi.api.config_openapi` method was removed (see https://github.com/stac-utils/stac-fastapi/pull/523)

* passing `response_class` in `stac_fastapi.api.routes.create_async_endpoint` is now deprecated. The response class now has to be set when registering the endpoint to the application (see https://github.com/stac-utils/stac-fastapi/issues/461)

* `PostFieldsExtension.filter_fields` property has been removed.

## Middlewares configuration

The `StacApi.middlewares` attribute has been updated to accept a list of `starlette.middleware.Middleware`. This enables dynamic configuration of middlewares (see https://github.com/stac-utils/stac-fastapi/pull/442).

```python
# before
class myMiddleware(mainMiddleware):
option1 = option1
option2 = option2

stac = StacApi(
middlewares=[
myMiddleware,
]
)

# now
stac = StacApi(
middlewares=[
Middleware(myMiddleware, option1, option2),
]
)
```

## Request Models

In stac-fastapi v2.0, users could already customize both GET/POST search request models. For v3.0, we've added more attributes to enable other endpoints customization:

- `collections_get_request_model`: GET request model for the `/collections` endpoint (default to `EmptyRequest`)
- `collection_get_request_model`: GET request model for the `/collections/{collection_id}` endpoint (default to `stac_fastapi.api.models.CollectionUri`)
- `items_get_request_model`: GET request model for the `/collections/{collection_id}/items` endpoint (default to `stac_fastapi.api.models.ItemCollectionUri`)
- `item_get_request_model`: GET request model for the `/collections/{collection_id}/items/{item_id}` endpoint (default to `stac_fastapi.api.models.ItemUri`)

```python
# before
getSearchModel = create_request_model(
model_name="SearchGetRequest",
base_model=BaseSearchGetRequest
extensions=[...],
request_type="GET"
)
stac = StacApi(
search_get_request_model=getSearchModel,
search_post_request_model=...,
)

# now
@dataclass
class CollectionsRequest(APIRequest):
user: str = Query(...)

stac = StacApi(
search_get_request_model=getSearchModel,
search_post_request_model=postSearchModel,
collections_get_request_model=CollectionsRequest,
collection_get_request_model=...,
items_get_request_model=...,
item_get_request_model=...,
)
```

## Filter extension

`default_includes` attribute has been removed from the `ApiSettings` object. If you need `defaults` includes you can overwrite the `FieldExtension` models (see https://github.com/stac-utils/stac-fastapi/pull/706).

```python
# before
stac = StacApi(
extensions=[
FieldsExtension()
]
)

# now
class PostFieldsExtension(requests.PostFieldsExtension):
include: Optional[Set[str]] = Field(
default_factory=lambda: {
"id",
"type",
"stac_version",
"geometry",
"bbox",
"links",
"assets",
"properties.datetime",
"collection",
}
)
exclude: Optional[Set[str]] = set()


class FieldsExtensionPostRequest(BaseModel):
"""Additional fields and schema for the POST request."""

fields: Optional[PostFieldsExtension] = Field(PostFieldsExtension())


class FieldsExtension(FieldsExtensionBase):
"""Override the POST model"""

POST = FieldsExtensionPostRequest


from stac_fastapi.api.app import StacApi

stac = StacApi(
extensions=[
FieldsExtension()
]
)
```

## Pagination extension

In stac-fastapi v3.0, we removed the `pagination_extension` attribute in `stac_fastapi.api.app.StacApi`. This attribute was used within the `register_get_item_collection` to update the request model for the `/collections/{collection_id}/items` endpoint.

It's now up to the user to create the request model and use the `items_get_request_model=` attribute in the StacApi object.

```python
# before
stac=StacApi(
pagination_extension=TokenPaginationExtension,
extension=[TokenPaginationExtension]
)

# now
items_get_request_model = create_request_model(
"ItemCollectionURI",
base_model=ItemCollectionUri,
mixins=[TokenPaginationExtension().GET],
)

stac=StacApi(
extension=[TokenPaginationExtension],
items_get_request_model=items_get_request_model,
)
```
35 changes: 18 additions & 17 deletions stac_fastapi/api/stac_fastapi/api/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,18 @@
from stac_fastapi.api.errors import DEFAULT_STATUS_CODES, add_exception_handlers
from stac_fastapi.api.middleware import CORSMiddleware, ProxyHeaderMiddleware
from stac_fastapi.api.models import (
APIRequest,
CollectionUri,
EmptyRequest,
GeoJSONResponse,
ItemCollectionUri,
ItemUri,
create_request_model,
)
from stac_fastapi.api.openapi import update_openapi
from stac_fastapi.api.routes import Scope, add_route_dependencies, create_async_endpoint

# TODO: make this module not depend on `stac_fastapi.extensions`
from stac_fastapi.extensions.core import FieldsExtension, TokenPaginationExtension
from stac_fastapi.extensions.core import FieldsExtension
from stac_fastapi.types.config import ApiSettings, Settings
from stac_fastapi.types.core import AsyncBaseCoreClient, BaseCoreClient
from stac_fastapi.types.extension import ApiExtension
Expand Down Expand Up @@ -108,7 +108,10 @@ class StacApi:
search_post_request_model: Type[BaseSearchPostRequest] = attr.ib(
default=BaseSearchPostRequest
)
pagination_extension = attr.ib(default=TokenPaginationExtension)
collections_get_request_model: Type[APIRequest] = attr.ib(default=EmptyRequest)
collection_get_request_model: Type[APIRequest] = attr.ib(default=CollectionUri)
items_get_request_model: Type[APIRequest] = attr.ib(default=ItemCollectionUri)
item_get_request_model: Type[APIRequest] = attr.ib(default=ItemUri)
response_class: Type[Response] = attr.ib(default=JSONResponse)
middlewares: List[Middleware] = attr.ib(
default=attr.Factory(
Expand Down Expand Up @@ -211,7 +214,9 @@ def register_get_item(self):
response_model_exclude_unset=True,
response_model_exclude_none=True,
methods=["GET"],
endpoint=create_async_endpoint(self.client.get_item, ItemUri),
endpoint=create_async_endpoint(
self.client.get_item, self.item_get_request_model
),
)

def register_post_search(self):
Expand Down Expand Up @@ -302,7 +307,9 @@ def register_get_collections(self):
response_model_exclude_unset=True,
response_model_exclude_none=True,
methods=["GET"],
endpoint=create_async_endpoint(self.client.all_collections, EmptyRequest),
endpoint=create_async_endpoint(
self.client.all_collections, self.collections_get_request_model
),
)

def register_get_collection(self):
Expand All @@ -329,7 +336,9 @@ def register_get_collection(self):
response_model_exclude_unset=True,
response_model_exclude_none=True,
methods=["GET"],
endpoint=create_async_endpoint(self.client.get_collection, CollectionUri),
endpoint=create_async_endpoint(
self.client.get_collection, self.collection_get_request_model
),
)

def register_get_item_collection(self):
Expand All @@ -338,16 +347,6 @@ def register_get_item_collection(self):
Returns:
None
"""
pagination_extension = self.get_extension(self.pagination_extension)
if pagination_extension is not None:
mixins = [pagination_extension.GET]
else:
mixins = None
request_model = create_request_model(
"ItemCollectionURI",
base_model=ItemCollectionUri,
mixins=mixins,
)
self.router.add_api_route(
name="Get ItemCollection",
path="/collections/{collection_id}/items",
Expand All @@ -366,7 +365,9 @@ def register_get_item_collection(self):
response_model_exclude_unset=True,
response_model_exclude_none=True,
methods=["GET"],
endpoint=create_async_endpoint(self.client.item_collection, request_model),
endpoint=create_async_endpoint(
self.client.item_collection, self.items_get_request_model
),
)

def register_core(self):
Expand Down
9 changes: 9 additions & 0 deletions stac_fastapi/api/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from starlette.testclient import TestClient

from stac_fastapi.api.app import StacApi
from stac_fastapi.api.models import ItemCollectionUri, create_request_model
from stac_fastapi.extensions.core import (
TokenPaginationExtension,
TransactionExtension,
Expand All @@ -13,6 +14,13 @@ class TestRouteDependencies:
@staticmethod
def _build_api(**overrides):
settings = config.ApiSettings()

items_get_request_model = create_request_model(
"ItemCollectionURI",
base_model=ItemCollectionUri,
mixins=[TokenPaginationExtension().GET],
)

return StacApi(
**{
"settings": settings,
Expand All @@ -23,6 +31,7 @@ def _build_api(**overrides):
),
TokenPaginationExtension(),
],
"items_get_request_model": items_get_request_model,
**overrides,
}
)
Expand Down
Loading

0 comments on commit 1062ac4

Please sign in to comment.