Skip to content

Commit 6078e54

Browse files
add operationIds (#1145)
1 parent dc9209a commit 6078e54

File tree

11 files changed

+152
-36
lines changed

11 files changed

+152
-36
lines changed

CHANGES.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
### Misc
66

77
* rename `/map` endpoint to `/map.html` **breaking change**
8+
* add `name` attribute to `BaseFactory` to define endpoint's `operationId`
9+
* add `operationId` on all endpoints
810

911
### titiler.application
1012

@@ -129,6 +131,13 @@
129131
* deprecate `VariablesExtension` extension
130132
* add `DatasetMetadataExtension` extension (`/dataset/keys`, `/dataset/` and `/dataset/dict` endpoints)
131133

134+
### titiler.mosaic
135+
136+
* add `/bbox` prefix to `/{minx},{miny},{maxx},{maxy}/assets` endpoint -> `/bbox/{minx},{miny},{maxx},{maxy}/assets` **breaking change**
137+
* add `/point` prefix to `{lon},{lat}/assets` endpoint -> `/point/{lon},{lat}/assets` **breaking change**
138+
* add `/tiles` prefix to `/{tileMatrixSetId}/{z}/{x}/{y}/assets` endpoint -> `/tiles/{tileMatrixSetId}/{z}/{x}/{y}/assets` **breaking change**
139+
140+
132141
## 0.21.1 (2025-01-29)
133142

134143
### titiler.core

docs/src/advanced/endpoints_factories.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ Most **Factories** are built from this [abstract based class](https://docs.pytho
2121
- **router_prefix**: Set prefix to all factory's endpoint. Defaults to `""`.
2222
- **route_dependencies**: Additional routes dependencies to add after routes creations. Defaults to `[]`.
2323
- **extension**: TiTiler extensions to register after endpoints creations. Defaults to `[]`.
24+
- **name**: Name of the Endpoints group. Defaults to `None`.
25+
- **operation_prefix** (*private*): Endpoint's `operationId` prefix. Defined by `self.name` or `self.router_prefix.replace("/", ".")`.
2426

2527
#### Methods
2628

src/titiler/core/titiler/core/factory.py

Lines changed: 71 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,15 @@ class BaseFactory(metaclass=abc.ABCMeta):
146146

147147
extensions: List[FactoryExtension] = field(factory=list)
148148

149+
name: Optional[str] = field(default=None)
150+
operation_prefix: str = field(init=False, default="")
151+
149152
def __attrs_post_init__(self):
150153
"""Post Init: register route and configure specific options."""
154+
# prefix for endpoint's operationId
155+
name = self.name or self.router_prefix.replace("/", ".")
156+
self.operation_prefix = f"{name}." if name else ""
157+
151158
# Register endpoints
152159
self.register_routes()
153160

@@ -334,6 +341,7 @@ def bounds(self):
334341
"/bounds",
335342
response_model=Bounds,
336343
responses={200: {"description": "Return dataset's bounds."}},
344+
operation_id=f"{self.operation_prefix}getBounds",
337345
)
338346
def bounds(
339347
src_path=Depends(self.path_dependency),
@@ -362,6 +370,7 @@ def info(self):
362370
response_model_exclude_none=True,
363371
response_class=JSONResponse,
364372
responses={200: {"description": "Return dataset's basic info."}},
373+
operation_id=f"{self.operation_prefix}getInfo",
365374
)
366375
def info(
367376
src_path=Depends(self.path_dependency),
@@ -384,6 +393,7 @@ def info(
384393
"description": "Return dataset's basic info as a GeoJSON feature.",
385394
}
386395
},
396+
operation_id=f"{self.operation_prefix}getInfoGeoJSON",
387397
)
388398
def info_geojson(
389399
src_path=Depends(self.path_dependency),
@@ -421,6 +431,7 @@ def statistics(self):
421431
"description": "Return dataset's statistics.",
422432
}
423433
},
434+
operation_id=f"{self.operation_prefix}getStatistics",
424435
)
425436
def statistics(
426437
src_path=Depends(self.path_dependency),
@@ -462,6 +473,7 @@ def statistics(
462473
"description": "Return dataset's statistics from feature or featureCollection.",
463474
}
464475
},
476+
operation_id=f"{self.operation_prefix}postStatisticsForGeoJSON",
465477
)
466478
def geojson_statistics(
467479
geojson: Annotated[
@@ -538,6 +550,7 @@ def tilesets(self):
538550
}
539551
},
540552
summary="Retrieve a list of available raster tilesets for the specified dataset.",
553+
operation_id=f"{self.operation_prefix}getTileSetList",
541554
)
542555
async def tileset_list(
543556
request: Request,
@@ -623,6 +636,7 @@ async def tileset_list(
623636
response_model_exclude_none=True,
624637
responses={200: {"content": {"application/json": {}}}},
625638
summary="Retrieve the raster tileset metadata for the specified dataset and tiling scheme (tile matrix set).",
639+
operation_id=f"{self.operation_prefix}getTileSet",
626640
)
627641
async def tileset(
628642
request: Request,
@@ -751,15 +765,24 @@ async def tileset(
751765
def tile(self): # noqa: C901
752766
"""Register /tiles endpoint."""
753767

754-
@self.router.get(r"/tiles/{tileMatrixSetId}/{z}/{x}/{y}", **img_endpoint_params)
755768
@self.router.get(
756-
r"/tiles/{tileMatrixSetId}/{z}/{x}/{y}.{format}", **img_endpoint_params
769+
"/tiles/{tileMatrixSetId}/{z}/{x}/{y}",
770+
operation_id=f"{self.operation_prefix}getTile",
771+
**img_endpoint_params,
772+
)
773+
@self.router.get(
774+
"/tiles/{tileMatrixSetId}/{z}/{x}/{y}.{format}",
775+
operation_id=f"{self.operation_prefix}getTileWithFormat",
776+
**img_endpoint_params,
757777
)
758778
@self.router.get(
759-
r"/tiles/{tileMatrixSetId}/{z}/{x}/{y}@{scale}x", **img_endpoint_params
779+
"/tiles/{tileMatrixSetId}/{z}/{x}/{y}@{scale}x",
780+
operation_id=f"{self.operation_prefix}getTileWithScale",
781+
**img_endpoint_params,
760782
)
761783
@self.router.get(
762-
r"/tiles/{tileMatrixSetId}/{z}/{x}/{y}@{scale}x.{format}",
784+
"/tiles/{tileMatrixSetId}/{z}/{x}/{y}@{scale}x.{format}",
785+
operation_id=f"{self.operation_prefix}getTileWithFormatAndScale",
763786
**img_endpoint_params,
764787
)
765788
def tile(
@@ -844,6 +867,7 @@ def tilejson(self): # noqa: C901
844867
response_model=TileJSON,
845868
responses={200: {"description": "Return a tilejson"}},
846869
response_model_exclude_none=True,
870+
operation_id=f"{self.operation_prefix}getTileJSON",
847871
)
848872
def tilejson(
849873
request: Request,
@@ -927,7 +951,11 @@ def tilejson(
927951
def map_viewer(self): # noqa: C901
928952
"""Register /map.html endpoint."""
929953

930-
@self.router.get("/{tileMatrixSetId}/map.html", response_class=HTMLResponse)
954+
@self.router.get(
955+
"/{tileMatrixSetId}/map.html",
956+
response_class=HTMLResponse,
957+
operation_id=f"{self.operation_prefix}getMapViewer",
958+
)
931959
def map_viewer(
932960
request: Request,
933961
tileMatrixSetId: Annotated[
@@ -993,7 +1021,9 @@ def wmts(self): # noqa: C901
9931021
"""Register /wmts endpoint."""
9941022

9951023
@self.router.get(
996-
"/{tileMatrixSetId}/WMTSCapabilities.xml", response_class=XMLResponse
1024+
"/{tileMatrixSetId}/WMTSCapabilities.xml",
1025+
response_class=XMLResponse,
1026+
operation_id=f"{self.operation_prefix}getWMTS",
9971027
)
9981028
def wmts(
9991029
request: Request,
@@ -1135,10 +1165,11 @@ def point(self):
11351165
"""Register /point endpoints."""
11361166

11371167
@self.router.get(
1138-
r"/point/{lon},{lat}",
1168+
"/point/{lon},{lat}",
11391169
response_model=Point,
11401170
response_class=JSONResponse,
11411171
responses={200: {"description": "Return a value for a point"}},
1172+
operation_id=f"{self.operation_prefix}getDataForPoint",
11421173
)
11431174
def point(
11441175
lon: Annotated[float, Path(description="Longitude")],
@@ -1151,7 +1182,6 @@ def point(
11511182
env=Depends(self.environment_dependency),
11521183
):
11531184
"""Get Point value for a dataset."""
1154-
11551185
with rasterio.Env(**env):
11561186
with self.reader(src_path, **reader_params.as_dict()) as src_dst:
11571187
pts = src_dst.point(
@@ -1174,8 +1204,16 @@ def point(
11741204
def preview(self):
11751205
"""Register /preview endpoint."""
11761206

1177-
@self.router.get(r"/preview", **img_endpoint_params)
1178-
@self.router.get(r"/preview.{format}", **img_endpoint_params)
1207+
@self.router.get(
1208+
"/preview",
1209+
operation_id=f"{self.operation_prefix}getPreview",
1210+
**img_endpoint_params,
1211+
)
1212+
@self.router.get(
1213+
"/preview.{format}",
1214+
operation_id=f"{self.operation_prefix}getPreviewWithFormat",
1215+
**img_endpoint_params,
1216+
)
11791217
def preview(
11801218
format: Annotated[
11811219
ImageType,
@@ -1224,10 +1262,12 @@ def part(self): # noqa: C901
12241262
# GET endpoints
12251263
@self.router.get(
12261264
"/bbox/{minx},{miny},{maxx},{maxy}.{format}",
1265+
operation_id=f"{self.operation_prefix}getDataForBoundingBox",
12271266
**img_endpoint_params,
12281267
)
12291268
@self.router.get(
12301269
"/bbox/{minx},{miny},{maxx},{maxy}/{width}x{height}.{format}",
1270+
operation_id=f"{self.operation_prefix}getDataForBoundingBoxWithSizesAndFormat",
12311271
**img_endpoint_params,
12321272
)
12331273
def bbox_image(
@@ -1279,14 +1319,17 @@ def bbox_image(
12791319
# POST endpoints
12801320
@self.router.post(
12811321
"/feature",
1322+
operation_id=f"{self.operation_prefix}postDataForGeoJSON",
12821323
**img_endpoint_params,
12831324
)
12841325
@self.router.post(
12851326
"/feature.{format}",
1327+
operation_id=f"{self.operation_prefix}postDataForGeoJSONWithFormat",
12861328
**img_endpoint_params,
12871329
)
12881330
@self.router.post(
12891331
"/feature/{width}x{height}.{format}",
1332+
operation_id=f"{self.operation_prefix}postDataForGeoJSONWithSizesAndFormat",
12901333
**img_endpoint_params,
12911334
)
12921335
def feature_image(
@@ -1370,6 +1413,7 @@ def info(self):
13701413
"description": "Return dataset's basic info or the list of available assets."
13711414
}
13721415
},
1416+
operation_id=f"{self.operation_prefix}getInfo",
13731417
)
13741418
def info(
13751419
src_path=Depends(self.path_dependency),
@@ -1393,6 +1437,7 @@ def info(
13931437
"description": "Return dataset's basic info as a GeoJSON feature.",
13941438
}
13951439
},
1440+
operation_id=f"{self.operation_prefix}getInfoGeoJSON",
13961441
)
13971442
def info_geojson(
13981443
src_path=Depends(self.path_dependency),
@@ -1418,6 +1463,7 @@ def info_geojson(
14181463
"/assets",
14191464
response_model=List[str],
14201465
responses={200: {"description": "Return a list of supported assets."}},
1466+
operation_id=f"{self.operation_prefix}getAssets",
14211467
)
14221468
def available_assets(
14231469
src_path=Depends(self.path_dependency),
@@ -1445,6 +1491,7 @@ def statistics(self): # noqa: C901
14451491
"description": "Return dataset's statistics.",
14461492
}
14471493
},
1494+
operation_id=f"{self.operation_prefix}getAssetsStatistics",
14481495
)
14491496
def asset_statistics(
14501497
src_path=Depends(self.path_dependency),
@@ -1480,6 +1527,7 @@ def asset_statistics(
14801527
"description": "Return dataset's statistics.",
14811528
}
14821529
},
1530+
operation_id=f"{self.operation_prefix}getStatistics",
14831531
)
14841532
def statistics(
14851533
src_path=Depends(self.path_dependency),
@@ -1525,6 +1573,7 @@ def statistics(
15251573
"description": "Return dataset's statistics from feature or featureCollection.",
15261574
}
15271575
},
1576+
operation_id=f"{self.operation_prefix}postStatisticsForGeoJSON",
15281577
)
15291578
def geojson_statistics(
15301579
geojson: Annotated[
@@ -1615,6 +1664,7 @@ def info(self):
16151664
response_model_exclude_none=True,
16161665
response_class=JSONResponse,
16171666
responses={200: {"description": "Return dataset's basic info."}},
1667+
operation_id=f"{self.operation_prefix}getInfo",
16181668
)
16191669
def info(
16201670
src_path=Depends(self.path_dependency),
@@ -1638,6 +1688,7 @@ def info(
16381688
"description": "Return dataset's basic info as a GeoJSON feature.",
16391689
}
16401690
},
1691+
operation_id=f"{self.operation_prefix}getInfoGeoJSON",
16411692
)
16421693
def info_geojson(
16431694
src_path=Depends(self.path_dependency),
@@ -1663,6 +1714,7 @@ def info_geojson(
16631714
"/bands",
16641715
response_model=List[str],
16651716
responses={200: {"description": "Return a list of supported bands."}},
1717+
operation_id=f"{self.operation_prefix}getBands",
16661718
)
16671719
def available_bands(
16681720
src_path=Depends(self.path_dependency),
@@ -1689,6 +1741,7 @@ def statistics(self): # noqa: C901
16891741
"description": "Return dataset's statistics.",
16901742
}
16911743
},
1744+
operation_id=f"{self.operation_prefix}getStatistics",
16921745
)
16931746
def statistics(
16941747
src_path=Depends(self.path_dependency),
@@ -1734,6 +1787,7 @@ def statistics(
17341787
"description": "Return dataset's statistics from feature or featureCollection.",
17351788
}
17361789
},
1790+
operation_id=f"{self.operation_prefix}postStatisticsForGeoJSON",
17371791
)
17381792
def geojson_statistics(
17391793
geojson: Annotated[
@@ -1798,11 +1852,11 @@ def register_routes(self):
17981852
"""Register TMS endpoint routes."""
17991853

18001854
@self.router.get(
1801-
r"/tileMatrixSets",
1855+
"/tileMatrixSets",
18021856
response_model=TileMatrixSetList,
18031857
response_model_exclude_none=True,
18041858
summary="Retrieve the list of available tiling schemes (tile matrix sets).",
1805-
operation_id="getTileMatrixSetsList",
1859+
operation_id=f"{self.operation_prefix}getTileMatrixSetsList",
18061860
responses={
18071861
200: {
18081862
"content": {
@@ -1843,7 +1897,7 @@ async def tilematrixsets(request: Request):
18431897
response_model=TileMatrixSet,
18441898
response_model_exclude_none=True,
18451899
summary="Retrieve the definition of the specified tiling scheme (tile matrix set).",
1846-
operation_id="getTileMatrixSet",
1900+
operation_id=f"{self.operation_prefix}getTileMatrixSet",
18471901
responses={
18481902
200: {
18491903
"content": {
@@ -1923,7 +1977,7 @@ def metadata(algorithm: BaseAlgorithm) -> AlgorithmMetadata:
19231977
"/algorithms",
19241978
response_model=Dict[str, AlgorithmMetadata],
19251979
summary="Retrieve the list of available Algorithms.",
1926-
operation_id="getAlgorithms",
1980+
operation_id=f"{self.operation_prefix}getAlgorithmList",
19271981
)
19281982
def available_algorithms(request: Request):
19291983
"""Retrieve the list of available Algorithms."""
@@ -1933,7 +1987,7 @@ def available_algorithms(request: Request):
19331987
"/algorithms/{algorithmId}",
19341988
response_model=AlgorithmMetadata,
19351989
summary="Retrieve the metadata of the specified algorithm.",
1936-
operation_id="getAlgorithm",
1990+
operation_id=f"{self.operation_prefix}getAlgorithm",
19371991
)
19381992
def algorithm_metadata(
19391993
algorithm: Annotated[
@@ -1960,7 +2014,7 @@ def register_routes(self): # noqa: C901
19602014
response_model=ColorMapsList,
19612015
response_model_exclude_none=True,
19622016
summary="Retrieve the list of available colormaps.",
1963-
operation_id="getColorMaps",
2017+
operation_id=f"{self.operation_prefix}getColorMapList",
19642018
)
19652019
def available_colormaps(request: Request):
19662020
"""Retrieve the list of available colormaps."""
@@ -2002,7 +2056,7 @@ def available_colormaps(request: Request):
20022056
"/colorMaps/{colorMapId}",
20032057
response_model=ColorMapType,
20042058
summary="Retrieve the colorMap metadata or image.",
2005-
operation_id="getColorMap",
2059+
operation_id=f"{self.operation_prefix}getColorMap",
20062060
responses={
20072061
200: {
20082062
"content": {

src/titiler/extensions/titiler/extensions/cogeo.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ def register(self, factory: TilerFactory):
3030
"/validate",
3131
response_model=Info,
3232
response_class=JSONResponse,
33+
operation_id=f"{factory.name}validate",
3334
)
3435
def validate(
3536
src_path=Depends(factory.path_dependency),

0 commit comments

Comments
 (0)