Skip to content

Commit 2ab3a83

Browse files
Merge pull request #227 from developmentseed/fix/tileset-rel
fix rel for tiling-scheme and openapi media-type
2 parents 1aee146 + 2f38aa7 commit 2ab3a83

File tree

5 files changed

+76
-7
lines changed

5 files changed

+76
-7
lines changed

tests/routes/test_endpoints.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,10 @@ def test_docs(app):
6363
"""Test /api endpoint."""
6464
response = app.get("/api")
6565
assert response.status_code == 200
66-
assert response.headers["content-type"] == "application/json"
66+
assert (
67+
response.headers["content-type"]
68+
== "application/vnd.oai.openapi+json;version=3.0"
69+
)
6770
body = response.json()
6871
assert body["openapi"]
6972

tipg/factory.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1254,7 +1254,7 @@ def links(self, request: Request) -> List[model.Link]:
12541254
"tilematrixsets",
12551255
),
12561256
type=MediaType.json,
1257-
rel="data",
1257+
rel="http://www.opengis.net/def/rel/ogc/1.0/tiling-schemes",
12581258
),
12591259
model.Link(
12601260
title="TileMatrixSet (Template URL)",
@@ -1315,8 +1315,6 @@ async def tilematrixsets(
13151315
"tilematrixset",
13161316
tileMatrixSetId=tms_id,
13171317
),
1318-
rel="http://www.opengis.net/def/rel/ogc/1.0/tiling-schemes",
1319-
type=MediaType.json,
13201318
)
13211319
],
13221320
)
@@ -1435,7 +1433,7 @@ async def collection_tileset_list(
14351433
"tilematrixset",
14361434
tileMatrixSetId=tms,
14371435
),
1438-
"rel": "http://www.opengis.net/def/rel/ogc/1.0/tiling-schemes",
1436+
"rel": "http://www.opengis.net/def/rel/ogc/1.0/tiling-scheme",
14391437
"type": "application/json",
14401438
"title": f"Definition of '{tms}' tileMatrixSet",
14411439
},
@@ -1546,7 +1544,7 @@ async def collection_tileset(
15461544
"tilematrixset",
15471545
tileMatrixSetId=tileMatrixSetId,
15481546
),
1549-
"rel": "http://www.opengis.net/def/rel/ogc/1.0/tiling-schemes",
1547+
"rel": "http://www.opengis.net/def/rel/ogc/1.0/tiling-scheme",
15501548
"type": "application/json",
15511549
"title": f"Definition of '{tileMatrixSetId}' tileMatrixSet",
15521550
},

tipg/main.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from tipg.errors import DEFAULT_STATUS_CODES, add_exception_handlers
1212
from tipg.factory import Endpoints
1313
from tipg.middleware import CacheControlMiddleware, CatalogUpdateMiddleware
14+
from tipg.openapi import _update_openapi
1415
from tipg.settings import APISettings, CustomSQLSettings, DatabaseSettings
1516

1617
from fastapi import FastAPI, Request
@@ -53,6 +54,9 @@ async def lifespan(app: FastAPI):
5354
root_path=settings.root_path,
5455
)
5556

57+
# Fix OpenAPI response header for OGC Common compatibility
58+
_update_openapi(app)
59+
5660
# custom template directory
5761
templates_location: List[Any] = (
5862
[jinja2.FileSystemLoader(settings.template_directory)]

tipg/model.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ class TileMatrixSetLink(BaseModel):
250250
"""
251251

252252
href: str
253-
rel: str = "http://www.opengis.net/def/rel/ogc/1.0/tiling-schemes"
253+
rel: str = "http://www.opengis.net/def/rel/ogc/1.0/tiling-scheme"
254254
type: MediaType = MediaType.json
255255

256256
model_config = {"use_enum_values": True}

tipg/openapi.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
"""TiPG openapi tools."""
2+
3+
from fastapi import FastAPI
4+
5+
from starlette.requests import Request
6+
from starlette.responses import Response
7+
from starlette.routing import Route, request_response
8+
9+
10+
def _update_openapi(app: FastAPI) -> FastAPI:
11+
"""Update OpenAPI response content-type.
12+
13+
This function modifies the openapi route to comply with the STAC API spec's required
14+
content-type response header.
15+
16+
Copied from https://github.com/stac-utils/stac-fastapi/blob/main/stac_fastapi/api/stac_fastapi/api/openapi.py
17+
18+
MIT License
19+
20+
Copyright (c) 2020 Arturo AI
21+
22+
Permission is hereby granted, free of charge, to any person obtaining a copy
23+
of this software and associated documentation files (the "Software"), to deal
24+
in the Software without restriction, including without limitation the rights
25+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
26+
copies of the Software, and to permit persons to whom the Software is
27+
furnished to do so, subject to the following conditions:
28+
29+
The above copyright notice and this permission notice shall be included in all
30+
copies or substantial portions of the Software.
31+
32+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
37+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
38+
SOFTWARE.
39+
"""
40+
# Find the route for the openapi_url in the app
41+
openapi_route: Route = next(
42+
route for route in app.router.routes if route.path == app.openapi_url
43+
)
44+
# Store the old endpoint function so we can call it from the patched function
45+
old_endpoint = openapi_route.endpoint
46+
47+
# Create a patched endpoint function that modifies the content type of the response
48+
async def patched_openapi_endpoint(req: Request) -> Response:
49+
# Get the response from the old endpoint function
50+
response = await old_endpoint(req)
51+
# Update the content type header in place
52+
response.headers["content-type"] = (
53+
"application/vnd.oai.openapi+json;version=3.0"
54+
)
55+
# Return the updated response
56+
return response
57+
58+
# When a Route is accessed the `handle` function calls `self.app`. Which is
59+
# the endpoint function wrapped with `request_response`. So we need to wrap
60+
# our patched function and replace the existing app with it.
61+
openapi_route.app = request_response(patched_openapi_endpoint)
62+
63+
# return the patched app
64+
return app

0 commit comments

Comments
 (0)