Repository: stac-utils/stac-fastapi (language: python)

Issue:
pin fastapi to previous version
first reported in https://github.com/developmentseed/geojson-pydantic/issues/42

Here is what we get when requesting `/docs`

```
...
  File "/usr/local/lib/python3.8/site-packages/fastapi/openapi/utils.py", line 410, in get_openapi
    return jsonable_encoder(OpenAPI(**output), by_alias=True, exclude_none=True)  # type: ignore
  File "pydantic/main.py", line 406, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 21 validation errors for OpenAPI
components -> schemas -> LineString -> properties -> coordinates -> items -> anyOf -> 0 -> items
  value is not a valid dict (type=type_error.dict)
components -> schemas -> LineString -> properties -> coordinates -> items -> anyOf -> 1 -> items
  value is not a valid dict (type=type_error.dict)
components -> schemas -> LineString -> $ref
  field required (type=value_error.missing)
components -> schemas -> MultiLineString -> properties -> coordinates -> items -> items -> anyOf -> 0 -> items
  value is not a valid dict (type=type_error.dict)
components -> schemas -> MultiLineString -> properties -> coordinates -> items -> items -> anyOf -> 1 -> items
  value is not a valid dict (type=type_error.dict)
components -> schemas -> MultiLineString -> $ref
  field required (type=value_error.missing)
components -> schemas -> MultiPoint -> properties -> coordinates -> items -> anyOf -> 0 -> items
  value is not a valid dict (type=type_error.dict)
components -> schemas -> MultiPoint -> properties -> coordinates -> items -> anyOf -> 1 -> items
  value is not a valid dict (type=type_error.dict)
components -> schemas -> MultiPoint -> $ref
  field required (type=value_error.missing)
components -> schemas -> MultiPolygon -> properties -> coordinates -> items -> items -> items -> anyOf -> 0 -> items
  value is not a valid dict (type=type_error.dict)
components -> schemas -> MultiPolygon -> properties -> coordinates -> items -> items -> items -> anyOf -> 1 -> items
  value is not a valid dict (type=type_error.dict)
components -> schemas -> MultiPolygon -> $ref
  field required (type=value_error.missing)
components -> schemas -> PgstacSearch -> properties -> bbox -> anyOf -> 0 -> items
  value is not a valid dict (type=type_error.dict)
components -> schemas -> PgstacSearch -> properties -> bbox -> anyOf -> 1 -> items
  value is not a valid dict (type=type_error.dict)
components -> schemas -> PgstacSearch -> $ref
  field required (type=value_error.missing)
components -> schemas -> Point -> properties -> coordinates -> anyOf -> 0 -> items
  value is not a valid dict (type=type_error.dict)
components -> schemas -> Point -> properties -> coordinates -> anyOf -> 1 -> items
  value is not a valid dict (type=type_error.dict)
components -> schemas -> Point -> $ref
  field required (type=value_error.missing)
components -> schemas -> Polygon -> properties -> coordinates -> items -> items -> anyOf -> 0 -> items
  value is not a valid dict (type=type_error.dict)
components -> schemas -> Polygon -> properties -> coordinates -> items -> items -> anyOf -> 1 -> items
  value is not a valid dict (type=type_error.dict)
components -> schemas -> Polygon -> $ref
  field required (type=value_error.missing)
...
```

As mentioned in the geojson-pydantic issue, the latest version of fastapi doesn't support Tuple with fixed size (work is ongoing to change this but not yet merged). While it worked with earlier fastapi version (**0.65.1**), the schema is not well representer (e.g for `center: Optional[Tuple[float, float, int]]` the openapi schema cannot render it but at least docs are shown 🤷‍♂️)

I'm not 100% sure which is the best version to use so it will need a closer look



Discussion / hints:

Fix patch:
diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml
index 2f037cce0..3606505ba 100644
--- a/.github/workflows/cicd.yaml
+++ b/.github/workflows/cicd.yaml
@@ -47,17 +47,17 @@ jobs:
         run: |
           python -m pip install --upgrade pipenv wheel
 
-      - name: Install core api
-        run: |
-          pip install ./stac_fastapi/api[dev]
-
       - name: Install types
         run: |
           pip install ./stac_fastapi/types[dev]
 
+      - name: Install core api
+        run: |
+          pip install ./stac_fastapi/api[dev]
+
       - name: Install Extensions
         run: |
-          pip install ./stac_fastapi/extensions[dev,tiles]
+          pip install ./stac_fastapi/extensions[dev]
 
       - name: Install sqlalchemy stac-fastapi
         run: |
diff --git a/CHANGES.md b/CHANGES.md
index b43755f18..3e10cd524 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -10,6 +10,8 @@
 
 ### Fixed
 
+* Pin FastAPI to 0.67 to avoid issues with rendering OpenAPI documentation ([#246](https://github.com/stac-utils/stac-fastapi/pull/246))
+
 ## [2.1.0]
 
 ### Added
diff --git a/Dockerfile b/Dockerfile
index f1d6b9da1..5c8f46199 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -17,8 +17,8 @@ COPY . /app
 ENV PATH=$PATH:/install/bin
 
 RUN mkdir -p /install && \
-    pip install -e ./stac_fastapi/api[dev] && \
     pip install -e ./stac_fastapi/types[dev] && \
-    pip install -e ./stac_fastapi/extensions[dev,tiles] && \
+    pip install -e ./stac_fastapi/api[dev] && \
+    pip install -e ./stac_fastapi/extensions[dev] && \
     pip install -e ./stac_fastapi/sqlalchemy[dev,server] && \
     pip install -e ./stac_fastapi/pgstac[dev,server]
diff --git a/stac_fastapi/api/setup.py b/stac_fastapi/api/setup.py
index a0abd9230..6de3585d1 100644
--- a/stac_fastapi/api/setup.py
+++ b/stac_fastapi/api/setup.py
@@ -6,11 +6,11 @@
     desc = f.read()
 
 install_requires = [
-    "fastapi",
     "attrs",
     "pydantic[dotenv]",
     "stac_pydantic==2.0.*",
     "brotli_asgi",
+    "stac-fastapi.types",
 ]
 
 extra_reqs = {
diff --git a/stac_fastapi/extensions/setup.py b/stac_fastapi/extensions/setup.py
index 315e1d94a..7e61906ea 100644
--- a/stac_fastapi/extensions/setup.py
+++ b/stac_fastapi/extensions/setup.py
@@ -6,7 +6,6 @@
     desc = f.read()
 
 install_requires = [
-    "fastapi",
     "attrs",
     "pydantic[dotenv]",
     "stac_pydantic==2.0.*",
diff --git a/stac_fastapi/pgstac/setup.py b/stac_fastapi/pgstac/setup.py
index f5146ceae..62c6ab3cb 100644
--- a/stac_fastapi/pgstac/setup.py
+++ b/stac_fastapi/pgstac/setup.py
@@ -6,7 +6,6 @@
     desc = f.read()
 
 install_requires = [
-    "fastapi",
     "attrs",
     "orjson",
     "pydantic[dotenv]",
diff --git a/stac_fastapi/sqlalchemy/setup.py b/stac_fastapi/sqlalchemy/setup.py
index 5f8d2f5d0..52628ebee 100644
--- a/stac_fastapi/sqlalchemy/setup.py
+++ b/stac_fastapi/sqlalchemy/setup.py
@@ -6,7 +6,6 @@
     desc = f.read()
 
 install_requires = [
-    "fastapi",
     "attrs",
     "pydantic[dotenv]",
     "stac_pydantic==2.0.*",
diff --git a/stac_fastapi/types/setup.py b/stac_fastapi/types/setup.py
index ecf602a94..714466fb6 100644
--- a/stac_fastapi/types/setup.py
+++ b/stac_fastapi/types/setup.py
@@ -6,7 +6,9 @@
     desc = f.read()
 
 install_requires = [
-    "fastapi",
+    # We need to pin fastapi for the openapi schema
+    # See https://github.com/stac-utils/stac-fastapi/issues/242
+    "fastapi==0.67.*",
     "attrs",
     "pydantic[dotenv]",
     "stac_pydantic==2.0.*",


Test patch:
diff --git a/stac_fastapi/sqlalchemy/tests/features/test_tiles_extension.py b/stac_fastapi/sqlalchemy/tests/features/test_tiles_extension.py
deleted file mode 100644
index 8a1de9136..000000000
--- a/stac_fastapi/sqlalchemy/tests/features/test_tiles_extension.py
+++ /dev/null
@@ -1,75 +0,0 @@
-from urllib.parse import urlsplit
-
-import pytest
-from starlette.testclient import TestClient
-
-from stac_fastapi.api.app import StacApi
-from stac_fastapi.extensions.third_party.tiles import TilesClient, TilesExtension
-from stac_fastapi.sqlalchemy.config import SqlalchemySettings
-
-from ..conftest import MockStarletteRequest
-
-
-@pytest.fixture
-def tiles_extension_app(postgres_core, postgres_transactions, load_test_data):
-    # Ingest test data for testing
-    coll = load_test_data("test_collection.json")
-    postgres_transactions.create_collection(coll, request=MockStarletteRequest)
-
-    item = load_test_data("test_item.json")
-    postgres_transactions.create_item(item, request=MockStarletteRequest)
-
-    settings = SqlalchemySettings()
-    api = StacApi(
-        settings=settings,
-        client=postgres_core,
-        extensions=[TilesExtension(TilesClient(postgres_core))],
-    )
-    with TestClient(api.app) as test_app:
-        yield test_app
-
-    # Cleanup test data
-    postgres_transactions.delete_item(
-        item["id"], item["collection"], request=MockStarletteRequest
-    )
-    postgres_transactions.delete_collection(coll["id"], request=MockStarletteRequest)
-
-
-def test_tiles_extension(tiles_extension_app, load_test_data):
-    item = load_test_data("test_item.json")
-
-    # Fetch the item
-    resp = tiles_extension_app.get(
-        f"/collections/{item['collection']}/items/{item['id']}"
-    )
-    resp_json = resp.json()
-    assert resp.status_code == 200
-
-    # Find the OGC tiles link
-    link = None
-    for link in resp_json["links"]:
-        if link.get("title") == "tiles":
-            break
-    assert link
-
-    # Request the TileSet resource
-    tiles_path = urlsplit(link["href"]).path
-    resp = tiles_extension_app.get(tiles_path)
-    assert resp.status_code == 200
-    tileset = resp.json()
-
-    assert tileset["extent"]["bbox"][0] == item["bbox"]
-
-    # We expect the tileset to have certain links
-    link_titles = [link["title"] for link in tileset["links"]]
-    assert "tiles" in link_titles
-    assert "tilejson" in link_titles
-    assert "viewer" in link_titles
-
-    # Confirm templated links are actually templates
-    for link in tileset["links"]:
-        if link.get("templated"):
-            # Since this is the `tile` extension checking against zoom seems reliable
-            assert "{z}" in link["href"]
-        else:
-            assert "{z}" not in link["href"]