stac-fastapi.eodag 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,18 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright 2023, CS GROUP - France, https://www.cs-soprasteria.com
3
+ #
4
+ # This file is part of stac-fastapi-eodag project
5
+ # https://www.github.com/CS-SI/stac-fastapi-eodag
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ """stac_fastapi.eodag module."""
@@ -0,0 +1,58 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright 2024, CS GROUP - France, https://www.cs-soprasteria.com
3
+ #
4
+ # This file is part of stac-fastapi-eodag project
5
+ # https://www.github.com/CS-SI/stac-fastapi-eodag
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ """override stac api for download handlers."""
19
+
20
+ from __future__ import annotations
21
+
22
+ from typing import TYPE_CHECKING
23
+
24
+ import attr
25
+ from stac_fastapi.api.app import StacApi
26
+
27
+ from stac_fastapi.eodag.models.stac_metadata import CommonStacMetadata
28
+
29
+ if TYPE_CHECKING:
30
+ from pydantic import BaseModel
31
+
32
+
33
+ @attr.s
34
+ class EodagStacApi(StacApi):
35
+ """Override default API to include download endpoints handlers."""
36
+
37
+ item_properties_model: type[BaseModel] = attr.ib(default=CommonStacMetadata)
38
+
39
+ # def register_download_item(self) -> None:
40
+ # """Register download item endpoint (GET /collections/{collection_id}/items/{item_id}/download).
41
+
42
+ # Returns:
43
+ # None
44
+ # """
45
+ # self.router.add_api_route(
46
+ # name="Download Item",
47
+ # path="/collections/{collection_id}/items/{item_id}/download",
48
+ # response_class=StreamingResponse,
49
+ # methods=["GET"],
50
+ # endpoint=create_async_endpoint(
51
+ # self.client.download_item, ItemUri, StreamingResponse
52
+ # ),
53
+ # )
54
+
55
+ # def register_core(self) -> None:
56
+ # """Register endpoints."""
57
+ # super().register_core()
58
+ # self.register_download_item()
@@ -0,0 +1,248 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright 2024, CS GROUP - France, https://www.cs-soprasteria.com
3
+ #
4
+ # This file is part of stac-fastapi-eodag project
5
+ # https://www.github.com/CS-SI/stac-fastapi-eodag
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ """FastAPI application using EODAG."""
19
+
20
+ from __future__ import annotations
21
+
22
+ import os
23
+ from typing import TYPE_CHECKING
24
+
25
+ import stac_fastapi.api.errors
26
+ from brotli_asgi import BrotliMiddleware # type: ignore[import-untyped]
27
+ from fastapi import FastAPI
28
+ from fastapi.concurrency import asynccontextmanager
29
+ from fastapi.middleware import Middleware
30
+ from fastapi.responses import ORJSONResponse
31
+ from stac_fastapi.api.app import StacApi
32
+ from stac_fastapi.api.middleware import CORSMiddleware
33
+ from stac_fastapi.api.models import (
34
+ EmptyRequest,
35
+ ItemCollectionUri,
36
+ create_get_request_model,
37
+ create_post_request_model,
38
+ create_request_model,
39
+ )
40
+ from stac_fastapi.extensions.core import (
41
+ CollectionSearchExtension,
42
+ FilterExtension,
43
+ FreeTextExtension,
44
+ QueryExtension,
45
+ SortExtension,
46
+ )
47
+ from stac_fastapi.extensions.core.free_text import FreeTextConformanceClasses
48
+ from stac_fastapi.extensions.core.query import QueryConformanceClasses
49
+ from stac_fastapi.extensions.core.sort import SortConformanceClasses
50
+
51
+ from stac_fastapi.eodag.config import get_settings
52
+ from stac_fastapi.eodag.core import EodagCoreClient
53
+ from stac_fastapi.eodag.dag import init_dag
54
+ from stac_fastapi.eodag.errors import add_exception_handlers, exception_handler_factory
55
+ from stac_fastapi.eodag.extensions.collection_order import (
56
+ BaseCollectionOrderClient,
57
+ CollectionOrderExtension,
58
+ )
59
+ from stac_fastapi.eodag.extensions.data_download import DataDownload
60
+ from stac_fastapi.eodag.extensions.ecmwf import EcmwfExtension
61
+ from stac_fastapi.eodag.extensions.filter import FiltersClient
62
+ from stac_fastapi.eodag.extensions.offset_pagination import OffsetPaginationExtension
63
+ from stac_fastapi.eodag.extensions.pagination import PaginationExtension
64
+ from stac_fastapi.eodag.extensions.stac import (
65
+ ElectroOpticalExtension,
66
+ FederationExtension,
67
+ OrderExtension,
68
+ ProcessingExtension,
69
+ ProductExtension,
70
+ SarExtension,
71
+ SatelliteExtension,
72
+ ScientificCitationExtension,
73
+ StorageExtension,
74
+ TimestampExtension,
75
+ ViewGeometryExtension,
76
+ )
77
+ from stac_fastapi.eodag.logs import RequestIDMiddleware, init_logging
78
+ from stac_fastapi.eodag.middlewares import ProxyHeaderMiddleware
79
+ from stac_fastapi.eodag.models.stac_metadata import create_stac_metadata_model
80
+
81
+ if TYPE_CHECKING:
82
+ from typing import AsyncGenerator
83
+
84
+ init_logging()
85
+
86
+ settings = get_settings()
87
+
88
+ stac_metadata_model = create_stac_metadata_model(
89
+ extensions=[
90
+ SarExtension(),
91
+ SatelliteExtension(),
92
+ TimestampExtension(),
93
+ ProcessingExtension(),
94
+ ViewGeometryExtension(),
95
+ ElectroOpticalExtension(),
96
+ FederationExtension(),
97
+ ScientificCitationExtension(),
98
+ ProductExtension(),
99
+ StorageExtension(),
100
+ OrderExtension(),
101
+ EcmwfExtension(),
102
+ ]
103
+ )
104
+
105
+ # search extensions
106
+ search_extensions_map = {
107
+ "query": QueryExtension(),
108
+ "sort": SortExtension(),
109
+ "filter": FilterExtension(client=FiltersClient(stac_metadata_model=stac_metadata_model)),
110
+ "pagination": PaginationExtension(),
111
+ }
112
+
113
+ # collection_search extensions
114
+ cs_extensions_map = {
115
+ "query": QueryExtension(conformance_classes=[QueryConformanceClasses.COLLECTIONS]),
116
+ "offset-pagination": OffsetPaginationExtension(),
117
+ "collection-search": CollectionSearchExtension(),
118
+ "free-text": FreeTextExtension(conformance_classes=[FreeTextConformanceClasses.COLLECTIONS]),
119
+ }
120
+
121
+ # item_collection extensions
122
+ itm_col_extensions_map = {
123
+ "pagination": PaginationExtension(),
124
+ "sort": SortExtension(conformance_classes=[SortConformanceClasses.ITEMS]),
125
+ }
126
+
127
+ all_extensions = {
128
+ **search_extensions_map,
129
+ **cs_extensions_map,
130
+ **itm_col_extensions_map,
131
+ **{
132
+ "data-download": DataDownload(),
133
+ "collection-order": CollectionOrderExtension(
134
+ client=BaseCollectionOrderClient(stac_metadata_model=stac_metadata_model)
135
+ ),
136
+ },
137
+ }
138
+
139
+
140
+ def get_enabled_extensions(specif_extensions: dict):
141
+ """
142
+ Retrieve the list of enabled extensions based on the environment variable `ENABLED_EXTENSIONS`.
143
+
144
+ :param specif_extensions: A dictionary mapping extension names to their corresponding objects.
145
+ :returns: A list of enabled extension objects. If `ENABLED_EXTENSIONS` is not set, all extensions
146
+ from `specif_extensions` are returned.
147
+ """
148
+ if enabled := os.getenv("ENABLED_EXTENSIONS"):
149
+ return [specif_extensions[name] for name in enabled.split(",") if name in specif_extensions]
150
+ return list(specif_extensions.values())
151
+
152
+
153
+ extensions = get_enabled_extensions(all_extensions)
154
+
155
+ for e in extensions:
156
+ if isinstance(e, CollectionOrderExtension):
157
+ e.client.extensions = extensions
158
+
159
+
160
+ @asynccontextmanager
161
+ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
162
+ """API init and tear-down"""
163
+ init_dag(app)
164
+ if os.getenv("OTEL_EXPORTER_OTLP_ENDPOINT", ""):
165
+ from stac_fastapi.eodag.telemetry import instrument_eodag
166
+
167
+ instrument_eodag(app.state.dag)
168
+ # init_cache(app)
169
+ app.state.stac_metadata_model = stac_metadata_model
170
+ yield
171
+
172
+
173
+ app = FastAPI(
174
+ openapi_url=settings.openapi_url,
175
+ docs_url=settings.docs_url,
176
+ redoc_url=None,
177
+ lifespan=lifespan,
178
+ )
179
+
180
+ if os.getenv("OTEL_EXPORTER_OTLP_ENDPOINT", ""):
181
+ from stac_fastapi.eodag.telemetry import instrument_fastapi
182
+
183
+ instrument_fastapi(app)
184
+
185
+ add_exception_handlers(app)
186
+ app.add_middleware(RequestIDMiddleware)
187
+
188
+ search_extensions = get_enabled_extensions(search_extensions_map)
189
+
190
+ search_post_model = create_post_request_model(search_extensions)
191
+ search_get_model = create_get_request_model(search_extensions)
192
+
193
+ collections_model = create_request_model(
194
+ "CollectionsRequest",
195
+ base_model=EmptyRequest,
196
+ extensions=get_enabled_extensions(cs_extensions_map),
197
+ request_type="GET",
198
+ )
199
+
200
+ item_collection_model = create_request_model(
201
+ "ItemsRequest",
202
+ base_model=ItemCollectionUri,
203
+ extensions=get_enabled_extensions(itm_col_extensions_map),
204
+ request_type="GET",
205
+ )
206
+
207
+
208
+ client = EodagCoreClient(post_request_model=search_post_model, stac_metadata_model=stac_metadata_model)
209
+
210
+ stac_fastapi.api.errors.exception_handler_factory = exception_handler_factory
211
+
212
+ api = StacApi(
213
+ app=app,
214
+ settings=settings,
215
+ extensions=extensions,
216
+ client=client,
217
+ response_class=ORJSONResponse,
218
+ search_get_request_model=search_get_model,
219
+ search_post_request_model=search_post_model,
220
+ collections_get_request_model=collections_model,
221
+ items_get_request_model=item_collection_model,
222
+ middlewares=[
223
+ Middleware(BrotliMiddleware),
224
+ Middleware(CORSMiddleware),
225
+ Middleware(ProxyHeaderMiddleware),
226
+ ],
227
+ )
228
+
229
+
230
+ def run():
231
+ """Run app from command line using uvicorn if available."""
232
+ try:
233
+ import uvicorn
234
+
235
+ uvicorn.run(
236
+ "stac_fastapi.eodag.app:app",
237
+ host=settings.app_host,
238
+ port=settings.app_port,
239
+ log_level="info",
240
+ reload=settings.reload,
241
+ root_path=os.getenv("UVICORN_ROOT_PATH", ""),
242
+ )
243
+ except ImportError as e:
244
+ raise RuntimeError("Uvicorn must be installed in order to use command") from e
245
+
246
+
247
+ if __name__ == "__main__":
248
+ run()
@@ -0,0 +1,58 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright 2024, CS GROUP - France, https://www.cs-soprasteria.com
3
+ #
4
+ # This file is part of stac-fastapi-eodag project
5
+ # https://www.github.com/CS-SI/stac-fastapi-eodag
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ """Initialize landing page"""
19
+
20
+ from urllib.parse import urljoin
21
+
22
+ from stac_fastapi.types import stac
23
+ from stac_fastapi.types.core import AsyncBaseCoreClient
24
+ from stac_fastapi.types.requests import get_base_url
25
+
26
+ from stac_fastapi.eodag.config import get_settings
27
+ from stac_fastapi.eodag.models.stac_metadata import get_federation_backend_dict
28
+
29
+
30
+ class CustomCoreClient(AsyncBaseCoreClient):
31
+ """Override the base method to generate a STAC landing page (GET /)."""
32
+
33
+ async def landing_page(self, **kwargs) -> stac.LandingPage:
34
+ """Generate a STAC landing page (GET /)."""
35
+ landing_page = await super().landing_page(**kwargs)
36
+
37
+ request = kwargs["request"]
38
+ base_url = get_base_url(request)
39
+
40
+ # Modify each link to add a title if absent
41
+ stac_fastapi_title = get_settings().stac_fastapi_title
42
+ if "links" in landing_page and isinstance(landing_page["links"], list):
43
+ collections_url = urljoin(base_url, "collections")
44
+ for link in landing_page["links"]:
45
+ href = link.get("href", "")
46
+ if href == base_url:
47
+ link["title"] = f"{stac_fastapi_title}"
48
+ if href == collections_url:
49
+ link["title"] = "Collections"
50
+
51
+ # add federation backends infos
52
+ federation_backends = request.app.state.dag.available_providers()
53
+ federation_dict = {fb: get_federation_backend_dict(request, fb) for fb in federation_backends}
54
+ landing_page["federation"] = federation_dict
55
+
56
+ landing_page["stac_extensions"].append(self.stac_metadata_model._conformance_classes["FederationExtension"])
57
+
58
+ return landing_page
@@ -0,0 +1,72 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright 2024, CS GROUP - France, https://www.cs-soprasteria.com
3
+ #
4
+ # This file is part of stac-fastapi-eodag project
5
+ # https://www.github.com/CS-SI/stac-fastapi-eodag
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ """EODAG STAC API configuration"""
19
+
20
+ from __future__ import annotations
21
+
22
+ from functools import lru_cache
23
+ from typing import Annotated, Union
24
+
25
+ from pydantic import Field
26
+ from pydantic.functional_validators import BeforeValidator
27
+ from stac_fastapi.types.config import ApiSettings
28
+
29
+ from stac_fastapi.eodag.utils import str2liststr
30
+
31
+
32
+ class Settings(ApiSettings):
33
+ """EODAG Server config"""
34
+
35
+ debug: bool = False
36
+
37
+ keep_origin_url: bool = Field(
38
+ default=False,
39
+ description=("Keep origin as alternate URL when data-download extension is enabled."),
40
+ )
41
+
42
+ origin_url_blacklist: Annotated[Union[str, list[str]], BeforeValidator(str2liststr)] = Field(
43
+ default=[],
44
+ description=("Hide from clients items assets' origin URLs starting with URLs from the list."),
45
+ )
46
+
47
+ auto_order_whitelist: Annotated[Union[str, list[str]], BeforeValidator(str2liststr)] = Field(
48
+ default=[
49
+ "wekeo_main",
50
+ ],
51
+ description=("List of providers for which the order should be done at the same time as the download."),
52
+ )
53
+
54
+ fetch_providers: bool = Field(default=False, description="Fetch additional collections from all providers.")
55
+
56
+ count: bool = Field(
57
+ default=False,
58
+ description=("Whether to run a query with a count request or not"),
59
+ )
60
+
61
+ download_base_url: str = Field(
62
+ default="",
63
+ description="base url to be used for download link",
64
+ pattern=r"(http|https):\/\/[\w:.-]+\/",
65
+ validate_default=False,
66
+ )
67
+
68
+
69
+ @lru_cache(maxsize=1)
70
+ def get_settings() -> Settings:
71
+ """Get settings"""
72
+ return Settings()
@@ -0,0 +1,43 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright 2024, CS GROUP - France, https://www.cs-soprasteria.com
3
+ #
4
+ # This file is part of stac-fastapi-eodag project
5
+ # https://www.github.com/CS-SI/stac-fastapi-eodag
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ """Constants"""
19
+
20
+ ITEM_PROPERTIES_EXCLUDE = {
21
+ "_id": True,
22
+ "productType": True,
23
+ "downloadLink": True,
24
+ "orderLink": True,
25
+ "orderStatus": True,
26
+ "orderStatusLink": True,
27
+ "searchLink": True,
28
+ "missionStartDate": True,
29
+ "missionEndDate": True,
30
+ "keywords": True,
31
+ "_date": True,
32
+ "_dc_qs": True,
33
+ "qs": True,
34
+ "defaultGeometry": True,
35
+ }
36
+
37
+ CACHE_KEY_COLLECTIONS = "collections"
38
+ CACHE_KEY_COLLECTION = "collection"
39
+ CACHE_KEY_SEARCH = "search"
40
+ CACHE_KEY_QUERYABLES = "queryables"
41
+
42
+ #: default number of items per page from stac-fastapi
43
+ DEFAULT_ITEMS_PER_PAGE = 10