stac-fastapi.core 3.1.0__tar.gz → 3.2.1__tar.gz

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.
Files changed (30) hide show
  1. {stac_fastapi_core-3.1.0 → stac-fastapi.core-3.2.1}/PKG-INFO +45 -120
  2. {stac_fastapi_core-3.1.0 → stac-fastapi.core-3.2.1}/README.md +44 -106
  3. {stac_fastapi_core-3.1.0 → stac-fastapi.core-3.2.1}/setup.py +1 -0
  4. {stac_fastapi_core-3.1.0 → stac-fastapi.core-3.2.1}/stac_fastapi/core/extensions/aggregation.py +36 -1
  5. {stac_fastapi_core-3.1.0 → stac-fastapi.core-3.2.1}/stac_fastapi/core/models/links.py +8 -0
  6. stac-fastapi.core-3.2.1/stac_fastapi/core/rate_limit.py +44 -0
  7. {stac_fastapi_core-3.1.0 → stac-fastapi.core-3.2.1}/stac_fastapi/core/version.py +1 -1
  8. {stac_fastapi_core-3.1.0 → stac-fastapi.core-3.2.1}/stac_fastapi.core.egg-info/PKG-INFO +45 -120
  9. {stac_fastapi_core-3.1.0 → stac-fastapi.core-3.2.1}/stac_fastapi.core.egg-info/SOURCES.txt +1 -0
  10. {stac_fastapi_core-3.1.0 → stac-fastapi.core-3.2.1}/stac_fastapi.core.egg-info/requires.txt +1 -0
  11. {stac_fastapi_core-3.1.0 → stac-fastapi.core-3.2.1}/stac_fastapi.core.egg-info/top_level.txt +0 -1
  12. {stac_fastapi_core-3.1.0 → stac-fastapi.core-3.2.1}/setup.cfg +0 -0
  13. {stac_fastapi_core-3.1.0 → stac-fastapi.core-3.2.1}/stac_fastapi/core/__init__.py +0 -0
  14. {stac_fastapi_core-3.1.0 → stac-fastapi.core-3.2.1}/stac_fastapi/core/base_database_logic.py +0 -0
  15. {stac_fastapi_core-3.1.0 → stac-fastapi.core-3.2.1}/stac_fastapi/core/base_settings.py +0 -0
  16. {stac_fastapi_core-3.1.0 → stac-fastapi.core-3.2.1}/stac_fastapi/core/basic_auth.py +0 -0
  17. {stac_fastapi_core-3.1.0 → stac-fastapi.core-3.2.1}/stac_fastapi/core/core.py +0 -0
  18. {stac_fastapi_core-3.1.0 → stac-fastapi.core-3.2.1}/stac_fastapi/core/datetime_utils.py +0 -0
  19. {stac_fastapi_core-3.1.0 → stac-fastapi.core-3.2.1}/stac_fastapi/core/extensions/__init__.py +0 -0
  20. {stac_fastapi_core-3.1.0 → stac-fastapi.core-3.2.1}/stac_fastapi/core/extensions/fields.py +0 -0
  21. {stac_fastapi_core-3.1.0 → stac-fastapi.core-3.2.1}/stac_fastapi/core/extensions/filter.py +0 -0
  22. {stac_fastapi_core-3.1.0 → stac-fastapi.core-3.2.1}/stac_fastapi/core/extensions/query.py +0 -0
  23. {stac_fastapi_core-3.1.0 → stac-fastapi.core-3.2.1}/stac_fastapi/core/models/__init__.py +0 -0
  24. {stac_fastapi_core-3.1.0 → stac-fastapi.core-3.2.1}/stac_fastapi/core/models/search.py +0 -0
  25. {stac_fastapi_core-3.1.0 → stac-fastapi.core-3.2.1}/stac_fastapi/core/route_dependencies.py +0 -0
  26. {stac_fastapi_core-3.1.0 → stac-fastapi.core-3.2.1}/stac_fastapi/core/serializers.py +0 -0
  27. {stac_fastapi_core-3.1.0 → stac-fastapi.core-3.2.1}/stac_fastapi/core/session.py +0 -0
  28. {stac_fastapi_core-3.1.0 → stac-fastapi.core-3.2.1}/stac_fastapi/core/utilities.py +0 -0
  29. {stac_fastapi_core-3.1.0 → stac-fastapi.core-3.2.1}/stac_fastapi.core.egg-info/dependency_links.txt +0 -0
  30. {stac_fastapi_core-3.1.0 → stac-fastapi.core-3.2.1}/stac_fastapi.core.egg-info/not-zip-safe +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: stac-fastapi.core
3
- Version: 3.1.0
3
+ Version: 3.2.1
4
4
  Summary: Core library for the Elasticsearch and Opensearch stac-fastapi backends.
5
5
  Home-page: https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch
6
6
  License: MIT
@@ -15,19 +15,6 @@ Classifier: Programming Language :: Python :: 3.12
15
15
  Classifier: License :: OSI Approved :: MIT License
16
16
  Requires-Python: >=3.8
17
17
  Description-Content-Type: text/markdown
18
- Requires-Dist: fastapi-slim
19
- Requires-Dist: attrs>=23.2.0
20
- Requires-Dist: pydantic[dotenv]
21
- Requires-Dist: stac_pydantic>=3
22
- Requires-Dist: stac-fastapi.types==3.0.0
23
- Requires-Dist: stac-fastapi.api==3.0.0
24
- Requires-Dist: stac-fastapi.extensions==3.0.0
25
- Requires-Dist: orjson
26
- Requires-Dist: overrides
27
- Requires-Dist: geojson-pydantic
28
- Requires-Dist: pygeofilter==0.2.1
29
- Requires-Dist: typing_extensions==4.8.0
30
- Requires-Dist: jsonschema
31
18
 
32
19
  # stac-fastapi-elasticsearch-opensearch (sfeos)
33
20
 
@@ -74,17 +61,24 @@ or
74
61
  pip install stac_fastapi.opensearch
75
62
  ```
76
63
 
77
- ## Build Elasticsearch API backend
64
+ ### To install and run via pre-built Docker Images
65
+
66
+ We provide ready-to-use Docker images through GitHub Container Registry ([ElasticSearch](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pkgs/container/stac-fastapi-es) and [OpenSearch](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pkgs/container/stac-fastapi-os) backends). You can easily pull and run these images:
78
67
 
79
68
  ```shell
80
- docker-compose up elasticsearch
81
- docker-compose build app-elasticsearch
69
+ # For Elasticsearch backend
70
+ docker pull ghcr.io/stac-utils/stac-fastapi-es:latest
71
+
72
+ # For OpenSearch backend
73
+ docker pull ghcr.io/stac-utils/stac-fastapi-os:latest
82
74
  ```
83
75
 
84
- ## Running Elasticsearch API on localhost:8080
76
+ ## Run Elasticsearch API backend on localhost:8080
77
+
78
+ You need to ensure [**Docker Compose**](https://docs.docker.com/compose/install/) or [**Podman Compose**](https://podman-desktop.io/docs/compose) installed and running on your machine. In the follwoing command instead of `docker-compose` you can use `podman-compose` as well.
85
79
 
86
80
  ```shell
87
- docker-compose up app-elasticsearch
81
+ docker-compose up elasticsearch app-elasticsearch
88
82
  ```
89
83
 
90
84
  By default, docker-compose uses Elasticsearch 8.x and OpenSearch 2.11.1.
@@ -97,6 +91,35 @@ OPENSEARCH_VERSION=2.11.0
97
91
  ```
98
92
  The most recent Elasticsearch 7.x versions should also work. See the [opensearch-py docs](https://github.com/opensearch-project/opensearch-py/blob/main/COMPATIBILITY.md) for compatibility information.
99
93
 
94
+ #### **Configuration reference keys:**
95
+
96
+ You can customize additional settings in your `.env` file:
97
+ ###### Key variables to configure:
98
+
99
+ | Variable | Description | Default | Required |
100
+ |------------------------------|--------------------------------------------------------------------------------------|--------------------------|---------------------------------------------------------------------------------------------|
101
+ | `ES_HOST` | Hostname for external Elasticsearch/OpenSearch. | `localhost` | Optional |
102
+ | `ES_PORT` | Port for Elasticsearch/OpenSearch. | `9200` (ES) / `9202` (OS)| Optional |
103
+ | `ES_USE_SSL` | Use SSL for connecting to Elasticsearch/OpenSearch. | `false` | Optional |
104
+ | `ES_VERIFY_CERTS` | Verify SSL certificates when connecting. | `false` | Optional |
105
+ | `STAC_FASTAPI_TITLE` | Title of the API in the documentation. | `stac-fastapi-elasticsearch` or `stac-fastapi-opensearch` | Optional |
106
+ | `STAC_FASTAPI_DESCRIPTION` | Description of the API in the documentation. | N/A | Optional |
107
+ | `STAC_FASTAPI_VERSION` | API version. | `2.1` | Optional |
108
+ | `APP_HOST` | Server bind address. | `0.0.0.0` | Optional |
109
+ | `APP_PORT` | Server port. | `8080` | Optional |
110
+ | `ENVIRONMENT` | Runtime environment. | `local` | Optional |
111
+ | `WEB_CONCURRENCY` | Number of worker processes. | `10` | Optional |
112
+ | `RELOAD` | Enable auto-reload for development. | `true` | Optional |
113
+ | `STAC_FASTAPI_RATE_LIMIT` | API rate limit per client. | `200/minute` | Optional |
114
+ | `BACKEND` | Tests-related variable | `elasticsearch` or `opensearch` based on the backend | Optional |
115
+ | `ELASTICSEARCH_VERSION` | ElasticSearch version | `7.17.1` | Optional |
116
+ | `OPENSEARCH_VERSION` | OpenSearch version | `2.11.0` | Optional |
117
+
118
+ > [!NOTE]
119
+ > The variables `ES_HOST`, `ES_PORT`, `ES_USE_SSL`, and `ES_VERIFY_CERTS` apply to both Elasticsearch and OpenSearch backends, so there is no need to rename the key names to `OS_` even if you're using OpenSearch.
120
+
121
+ ## Interacting with the API
122
+
100
123
  To create a new Collection:
101
124
 
102
125
  ```shell
@@ -310,108 +333,10 @@ The modified Items with lowercase identifiers will now be visible to users acces
310
333
 
311
334
  Authentication is an optional feature that can be enabled through `Route Dependencies` examples can be found and a more detailed explanation in [examples/auth](examples/auth).
312
335
 
313
-
314
336
  ## Aggregation
315
337
 
316
- Sfeos supports the STAC API [Aggregation Extension](https://github.com/stac-api-extensions/aggregation). This enables geospatial aggregation of points and geometries, as well as frequency distribution aggregation of any other property including dates. Aggregations can be defined at the root Catalog level (`/aggregations`) and at the Collection level (`/<collection_id>/aggregations`). The `/aggregate` route also fully supports base search and the STAC API [Filter Extension](https://github.com/stac-api-extensions/filter). Any query made with `/search` may also be executed with `/aggregate`, provided that the relevant aggregation fields are available,
317
-
338
+ Aggregation of points and geometries, as well as frequency distribution aggregation of any other property including dates is supported in stac-fatsapi-elasticsearch-opensearch. Aggregations can be defined at the root Catalog level (`/aggregations`) and at the Collection level (`/<collection_id>/aggregations`). Details for supported aggregations can be found at [./docs/src/aggregation.md](./docs/src/aggregation.md)
318
339
 
319
- A field named `aggregations` should be added to the Collection object for the collection for which the aggregations are available, for example:
340
+ ## Rate Limiting
320
341
 
321
- ```json
322
- "aggregations": [
323
- {
324
- "name": "total_count",
325
- "data_type": "integer"
326
- },
327
- {
328
- "name": "datetime_max",
329
- "data_type": "datetime"
330
- },
331
- {
332
- "name": "datetime_min",
333
- "data_type": "datetime"
334
- },
335
- {
336
- "name": "datetime_frequency",
337
- "data_type": "frequency_distribution",
338
- "frequency_distribution_data_type": "datetime"
339
- },
340
- {
341
- "name": "sun_elevation_frequency",
342
- "data_type": "frequency_distribution",
343
- "frequency_distribution_data_type": "numeric"
344
- },
345
- {
346
- "name": "platform_frequency",
347
- "data_type": "frequency_distribution",
348
- "frequency_distribution_data_type": "string"
349
- },
350
- {
351
- "name": "sun_azimuth_frequency",
352
- "data_type": "frequency_distribution",
353
- "frequency_distribution_data_type": "numeric"
354
- },
355
- {
356
- "name": "off_nadir_frequency",
357
- "data_type": "frequency_distribution",
358
- "frequency_distribution_data_type": "numeric"
359
- },
360
- {
361
- "name": "cloud_cover_frequency",
362
- "data_type": "frequency_distribution",
363
- "frequency_distribution_data_type": "numeric"
364
- },
365
- {
366
- "name": "grid_code_frequency",
367
- "data_type": "frequency_distribution",
368
- "frequency_distribution_data_type": "string"
369
- },
370
- {
371
- "name": "centroid_geohash_grid_frequency",
372
- "data_type": "frequency_distribution",
373
- "frequency_distribution_data_type": "string"
374
- },
375
- {
376
- "name": "centroid_geohex_grid_frequency",
377
- "data_type": "frequency_distribution",
378
- "frequency_distribution_data_type": "string"
379
- },
380
- {
381
- "name": "centroid_geotile_grid_frequency",
382
- "data_type": "frequency_distribution",
383
- "frequency_distribution_data_type": "string"
384
- },
385
- {
386
- "name": "geometry_geohash_grid_frequency",
387
- "data_type": "frequency_distribution",
388
- "frequency_distribution_data_type": "numeric"
389
- },
390
- {
391
- "name": "geometry_geotile_grid_frequency",
392
- "data_type": "frequency_distribution",
393
- "frequency_distribution_data_type": "string"
394
- }
395
- ]
396
- ```
397
-
398
- Available aggregations are:
399
-
400
- - total_count (count of total items)
401
- - collection_frequency (Item `collection` field)
402
- - platform_frequency (Item.Properties.platform)
403
- - cloud_cover_frequency (Item.Properties.eo:cloud_cover)
404
- - datetime_frequency (Item.Properties.datetime, monthly interval)
405
- - datetime_min (earliest Item.Properties.datetime)
406
- - datetime_max (latest Item.Properties.datetime)
407
- - sun_elevation_frequency (Item.Properties.view:sun_elevation)
408
- - sun_azimuth_frequency (Item.Properties.view:sun_azimuth)
409
- - off_nadir_frequency (Item.Properties.view:off_nadir)
410
- - grid_code_frequency (Item.Properties.grid:code)
411
- - centroid_geohash_grid_frequency ([geohash grid](https://opensearch.org/docs/latest/aggregations/bucket/geohash-grid/) on Item.Properties.proj:centroid)
412
- - centroid_geohex_grid_frequency ([geohex grid](https://opensearch.org/docs/latest/aggregations/bucket/geohex-grid/) on Item.Properties.proj:centroid)
413
- - centroid_geotile_grid_frequency (geotile on Item.Properties.proj:centroid)
414
- - geometry_geohash_grid_frequency ([geohash grid](https://opensearch.org/docs/latest/aggregations/bucket/geohash-grid/) on Item.geometry)
415
- - geometry_geotile_grid_frequency ([geotile grid](https://opensearch.org/docs/latest/aggregations/bucket/geotile-grid/) on Item.geometry)
416
-
417
- Support for additional fields and new aggregations can be added in the associated `database_logic.py` file.
342
+ Rate limiting is an optional security feature that controls API request frequency on a remote address basis. It's enabled by setting the `STAC_FASTAPI_RATE_LIMIT` environment variable, e.g., `500/minute`. This limits each client to 500 requests per minute, helping prevent abuse and maintain API stability. Implementation examples are available in the [examples/rate_limit](examples/rate_limit) directory.
@@ -43,17 +43,24 @@ or
43
43
  pip install stac_fastapi.opensearch
44
44
  ```
45
45
 
46
- ## Build Elasticsearch API backend
46
+ ### To install and run via pre-built Docker Images
47
+
48
+ We provide ready-to-use Docker images through GitHub Container Registry ([ElasticSearch](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pkgs/container/stac-fastapi-es) and [OpenSearch](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pkgs/container/stac-fastapi-os) backends). You can easily pull and run these images:
47
49
 
48
50
  ```shell
49
- docker-compose up elasticsearch
50
- docker-compose build app-elasticsearch
51
+ # For Elasticsearch backend
52
+ docker pull ghcr.io/stac-utils/stac-fastapi-es:latest
53
+
54
+ # For OpenSearch backend
55
+ docker pull ghcr.io/stac-utils/stac-fastapi-os:latest
51
56
  ```
52
57
 
53
- ## Running Elasticsearch API on localhost:8080
58
+ ## Run Elasticsearch API backend on localhost:8080
59
+
60
+ You need to ensure [**Docker Compose**](https://docs.docker.com/compose/install/) or [**Podman Compose**](https://podman-desktop.io/docs/compose) installed and running on your machine. In the follwoing command instead of `docker-compose` you can use `podman-compose` as well.
54
61
 
55
62
  ```shell
56
- docker-compose up app-elasticsearch
63
+ docker-compose up elasticsearch app-elasticsearch
57
64
  ```
58
65
 
59
66
  By default, docker-compose uses Elasticsearch 8.x and OpenSearch 2.11.1.
@@ -66,6 +73,35 @@ OPENSEARCH_VERSION=2.11.0
66
73
  ```
67
74
  The most recent Elasticsearch 7.x versions should also work. See the [opensearch-py docs](https://github.com/opensearch-project/opensearch-py/blob/main/COMPATIBILITY.md) for compatibility information.
68
75
 
76
+ #### **Configuration reference keys:**
77
+
78
+ You can customize additional settings in your `.env` file:
79
+ ###### Key variables to configure:
80
+
81
+ | Variable | Description | Default | Required |
82
+ |------------------------------|--------------------------------------------------------------------------------------|--------------------------|---------------------------------------------------------------------------------------------|
83
+ | `ES_HOST` | Hostname for external Elasticsearch/OpenSearch. | `localhost` | Optional |
84
+ | `ES_PORT` | Port for Elasticsearch/OpenSearch. | `9200` (ES) / `9202` (OS)| Optional |
85
+ | `ES_USE_SSL` | Use SSL for connecting to Elasticsearch/OpenSearch. | `false` | Optional |
86
+ | `ES_VERIFY_CERTS` | Verify SSL certificates when connecting. | `false` | Optional |
87
+ | `STAC_FASTAPI_TITLE` | Title of the API in the documentation. | `stac-fastapi-elasticsearch` or `stac-fastapi-opensearch` | Optional |
88
+ | `STAC_FASTAPI_DESCRIPTION` | Description of the API in the documentation. | N/A | Optional |
89
+ | `STAC_FASTAPI_VERSION` | API version. | `2.1` | Optional |
90
+ | `APP_HOST` | Server bind address. | `0.0.0.0` | Optional |
91
+ | `APP_PORT` | Server port. | `8080` | Optional |
92
+ | `ENVIRONMENT` | Runtime environment. | `local` | Optional |
93
+ | `WEB_CONCURRENCY` | Number of worker processes. | `10` | Optional |
94
+ | `RELOAD` | Enable auto-reload for development. | `true` | Optional |
95
+ | `STAC_FASTAPI_RATE_LIMIT` | API rate limit per client. | `200/minute` | Optional |
96
+ | `BACKEND` | Tests-related variable | `elasticsearch` or `opensearch` based on the backend | Optional |
97
+ | `ELASTICSEARCH_VERSION` | ElasticSearch version | `7.17.1` | Optional |
98
+ | `OPENSEARCH_VERSION` | OpenSearch version | `2.11.0` | Optional |
99
+
100
+ > [!NOTE]
101
+ > The variables `ES_HOST`, `ES_PORT`, `ES_USE_SSL`, and `ES_VERIFY_CERTS` apply to both Elasticsearch and OpenSearch backends, so there is no need to rename the key names to `OS_` even if you're using OpenSearch.
102
+
103
+ ## Interacting with the API
104
+
69
105
  To create a new Collection:
70
106
 
71
107
  ```shell
@@ -279,108 +315,10 @@ The modified Items with lowercase identifiers will now be visible to users acces
279
315
 
280
316
  Authentication is an optional feature that can be enabled through `Route Dependencies` examples can be found and a more detailed explanation in [examples/auth](examples/auth).
281
317
 
282
-
283
318
  ## Aggregation
284
319
 
285
- Sfeos supports the STAC API [Aggregation Extension](https://github.com/stac-api-extensions/aggregation). This enables geospatial aggregation of points and geometries, as well as frequency distribution aggregation of any other property including dates. Aggregations can be defined at the root Catalog level (`/aggregations`) and at the Collection level (`/<collection_id>/aggregations`). The `/aggregate` route also fully supports base search and the STAC API [Filter Extension](https://github.com/stac-api-extensions/filter). Any query made with `/search` may also be executed with `/aggregate`, provided that the relevant aggregation fields are available,
286
-
320
+ Aggregation of points and geometries, as well as frequency distribution aggregation of any other property including dates is supported in stac-fatsapi-elasticsearch-opensearch. Aggregations can be defined at the root Catalog level (`/aggregations`) and at the Collection level (`/<collection_id>/aggregations`). Details for supported aggregations can be found at [./docs/src/aggregation.md](./docs/src/aggregation.md)
287
321
 
288
- A field named `aggregations` should be added to the Collection object for the collection for which the aggregations are available, for example:
322
+ ## Rate Limiting
289
323
 
290
- ```json
291
- "aggregations": [
292
- {
293
- "name": "total_count",
294
- "data_type": "integer"
295
- },
296
- {
297
- "name": "datetime_max",
298
- "data_type": "datetime"
299
- },
300
- {
301
- "name": "datetime_min",
302
- "data_type": "datetime"
303
- },
304
- {
305
- "name": "datetime_frequency",
306
- "data_type": "frequency_distribution",
307
- "frequency_distribution_data_type": "datetime"
308
- },
309
- {
310
- "name": "sun_elevation_frequency",
311
- "data_type": "frequency_distribution",
312
- "frequency_distribution_data_type": "numeric"
313
- },
314
- {
315
- "name": "platform_frequency",
316
- "data_type": "frequency_distribution",
317
- "frequency_distribution_data_type": "string"
318
- },
319
- {
320
- "name": "sun_azimuth_frequency",
321
- "data_type": "frequency_distribution",
322
- "frequency_distribution_data_type": "numeric"
323
- },
324
- {
325
- "name": "off_nadir_frequency",
326
- "data_type": "frequency_distribution",
327
- "frequency_distribution_data_type": "numeric"
328
- },
329
- {
330
- "name": "cloud_cover_frequency",
331
- "data_type": "frequency_distribution",
332
- "frequency_distribution_data_type": "numeric"
333
- },
334
- {
335
- "name": "grid_code_frequency",
336
- "data_type": "frequency_distribution",
337
- "frequency_distribution_data_type": "string"
338
- },
339
- {
340
- "name": "centroid_geohash_grid_frequency",
341
- "data_type": "frequency_distribution",
342
- "frequency_distribution_data_type": "string"
343
- },
344
- {
345
- "name": "centroid_geohex_grid_frequency",
346
- "data_type": "frequency_distribution",
347
- "frequency_distribution_data_type": "string"
348
- },
349
- {
350
- "name": "centroid_geotile_grid_frequency",
351
- "data_type": "frequency_distribution",
352
- "frequency_distribution_data_type": "string"
353
- },
354
- {
355
- "name": "geometry_geohash_grid_frequency",
356
- "data_type": "frequency_distribution",
357
- "frequency_distribution_data_type": "numeric"
358
- },
359
- {
360
- "name": "geometry_geotile_grid_frequency",
361
- "data_type": "frequency_distribution",
362
- "frequency_distribution_data_type": "string"
363
- }
364
- ]
365
- ```
366
-
367
- Available aggregations are:
368
-
369
- - total_count (count of total items)
370
- - collection_frequency (Item `collection` field)
371
- - platform_frequency (Item.Properties.platform)
372
- - cloud_cover_frequency (Item.Properties.eo:cloud_cover)
373
- - datetime_frequency (Item.Properties.datetime, monthly interval)
374
- - datetime_min (earliest Item.Properties.datetime)
375
- - datetime_max (latest Item.Properties.datetime)
376
- - sun_elevation_frequency (Item.Properties.view:sun_elevation)
377
- - sun_azimuth_frequency (Item.Properties.view:sun_azimuth)
378
- - off_nadir_frequency (Item.Properties.view:off_nadir)
379
- - grid_code_frequency (Item.Properties.grid:code)
380
- - centroid_geohash_grid_frequency ([geohash grid](https://opensearch.org/docs/latest/aggregations/bucket/geohash-grid/) on Item.Properties.proj:centroid)
381
- - centroid_geohex_grid_frequency ([geohex grid](https://opensearch.org/docs/latest/aggregations/bucket/geohex-grid/) on Item.Properties.proj:centroid)
382
- - centroid_geotile_grid_frequency (geotile on Item.Properties.proj:centroid)
383
- - geometry_geohash_grid_frequency ([geohash grid](https://opensearch.org/docs/latest/aggregations/bucket/geohash-grid/) on Item.geometry)
384
- - geometry_geotile_grid_frequency ([geotile grid](https://opensearch.org/docs/latest/aggregations/bucket/geotile-grid/) on Item.geometry)
385
-
386
- Support for additional fields and new aggregations can be added in the associated `database_logic.py` file.
324
+ Rate limiting is an optional security feature that controls API request frequency on a remote address basis. It's enabled by setting the `STAC_FASTAPI_RATE_LIMIT` environment variable, e.g., `500/minute`. This limits each client to 500 requests per minute, helping prevent abuse and maintain API stability. Implementation examples are available in the [examples/rate_limit](examples/rate_limit) directory.
@@ -19,6 +19,7 @@ install_requires = [
19
19
  "pygeofilter==0.2.1",
20
20
  "typing_extensions==4.8.0",
21
21
  "jsonschema",
22
+ "slowapi==0.1.9",
22
23
  ]
23
24
 
24
25
  setup(
@@ -50,6 +50,7 @@ class EsAggregationExtensionGetRequest(
50
50
  centroid_geotile_grid_frequency_precision: Optional[int] = attr.ib(default=None)
51
51
  geometry_geohash_grid_frequency_precision: Optional[int] = attr.ib(default=None)
52
52
  geometry_geotile_grid_frequency_precision: Optional[int] = attr.ib(default=None)
53
+ datetime_frequency_interval: Optional[str] = attr.ib(default=None)
53
54
 
54
55
 
55
56
  class EsAggregationExtensionPostRequest(
@@ -62,6 +63,7 @@ class EsAggregationExtensionPostRequest(
62
63
  centroid_geotile_grid_frequency_precision: Optional[int] = None
63
64
  geometry_geohash_grid_frequency_precision: Optional[int] = None
64
65
  geometry_geotile_grid_frequency_precision: Optional[int] = None
66
+ datetime_frequency_interval: Optional[str] = None
65
67
 
66
68
 
67
69
  @attr.s
@@ -124,6 +126,8 @@ class EsAsyncAggregationClient(AsyncBaseAggregationClient):
124
126
  MAX_GEOHASH_PRECISION = 12
125
127
  MAX_GEOHEX_PRECISION = 15
126
128
  MAX_GEOTILE_PRECISION = 29
129
+ SUPPORTED_DATETIME_INTERVAL = {"day", "month", "year"}
130
+ DEFAULT_DATETIME_INTERVAL = "month"
127
131
 
128
132
  async def get_aggregations(self, collection_id: Optional[str] = None, **kwargs):
129
133
  """Get the available aggregations for a catalog or collection defined in the STAC JSON. If no aggregations, default aggregations are used."""
@@ -143,7 +147,7 @@ class EsAsyncAggregationClient(AsyncBaseAggregationClient):
143
147
  {
144
148
  "rel": "self",
145
149
  "type": "application/json",
146
- "href": urljoin(collection_endpoint, "aggregations"),
150
+ "href": urljoin(collection_endpoint + "/", "aggregations"),
147
151
  },
148
152
  ]
149
153
  )
@@ -182,6 +186,30 @@ class EsAsyncAggregationClient(AsyncBaseAggregationClient):
182
186
  else:
183
187
  return min_value
184
188
 
189
+ def extract_date_histogram_interval(self, value: Optional[str]) -> str:
190
+ """
191
+ Ensure that the interval for the date histogram is valid. If no value is provided, the default will be returned.
192
+
193
+ Args:
194
+ value: value entered by the user
195
+
196
+ Returns:
197
+ string containing the date histogram interval to use.
198
+
199
+ Raises:
200
+ HTTPException: if the supplied value is not in the supported intervals
201
+ """
202
+ if value is not None:
203
+ if value not in self.SUPPORTED_DATETIME_INTERVAL:
204
+ raise HTTPException(
205
+ status_code=400,
206
+ detail=f"Invalid datetime interval. Must be one of {self.SUPPORTED_DATETIME_INTERVAL}",
207
+ )
208
+ else:
209
+ return value
210
+ else:
211
+ return self.DEFAULT_DATETIME_INTERVAL
212
+
185
213
  @staticmethod
186
214
  def _return_date(
187
215
  interval: Optional[Union[DateTimeType, str]]
@@ -319,6 +347,7 @@ class EsAsyncAggregationClient(AsyncBaseAggregationClient):
319
347
  centroid_geotile_grid_frequency_precision: Optional[int] = None,
320
348
  geometry_geohash_grid_frequency_precision: Optional[int] = None,
321
349
  geometry_geotile_grid_frequency_precision: Optional[int] = None,
350
+ datetime_frequency_interval: Optional[str] = None,
322
351
  **kwargs,
323
352
  ) -> Union[Dict, Exception]:
324
353
  """Get aggregations from the database."""
@@ -339,6 +368,7 @@ class EsAsyncAggregationClient(AsyncBaseAggregationClient):
339
368
  "centroid_geotile_grid_frequency_precision": centroid_geotile_grid_frequency_precision,
340
369
  "geometry_geohash_grid_frequency_precision": geometry_geohash_grid_frequency_precision,
341
370
  "geometry_geotile_grid_frequency_precision": geometry_geotile_grid_frequency_precision,
371
+ "datetime_frequency_interval": datetime_frequency_interval,
342
372
  }
343
373
 
344
374
  if collection_id:
@@ -475,6 +505,10 @@ class EsAsyncAggregationClient(AsyncBaseAggregationClient):
475
505
  self.MAX_GEOTILE_PRECISION,
476
506
  )
477
507
 
508
+ datetime_frequency_interval = self.extract_date_histogram_interval(
509
+ aggregate_request.datetime_frequency_interval,
510
+ )
511
+
478
512
  try:
479
513
  db_response = await self.database.aggregate(
480
514
  collections,
@@ -485,6 +519,7 @@ class EsAsyncAggregationClient(AsyncBaseAggregationClient):
485
519
  centroid_geotile_grid_precision,
486
520
  geometry_geohash_grid_precision,
487
521
  geometry_geotile_grid_precision,
522
+ datetime_frequency_interval,
488
523
  )
489
524
  except Exception as error:
490
525
  if not isinstance(error, IndexError):
@@ -114,6 +114,14 @@ class CollectionLinks(BaseLinks):
114
114
  collection_id: str = attr.ib()
115
115
  extensions: List[str] = attr.ib(default=attr.Factory(list))
116
116
 
117
+ def link_self(self) -> Dict:
118
+ """Return the self link."""
119
+ return dict(
120
+ rel=Relations.self.value,
121
+ type=MimeTypes.json.value,
122
+ href=urljoin(self.base_url, f"collections/{self.collection_id}"),
123
+ )
124
+
117
125
  def link_parent(self) -> Dict[str, Any]:
118
126
  """Create the `parent` link."""
119
127
  return dict(rel=Relations.parent, type=MimeTypes.json.value, href=self.base_url)
@@ -0,0 +1,44 @@
1
+ """Rate limiting middleware."""
2
+
3
+ import logging
4
+ import os
5
+ from typing import Optional
6
+
7
+ from fastapi import FastAPI, Request
8
+ from slowapi import Limiter, _rate_limit_exceeded_handler
9
+ from slowapi.errors import RateLimitExceeded
10
+ from slowapi.middleware import SlowAPIMiddleware
11
+ from slowapi.util import get_remote_address
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ def get_limiter(key_func=get_remote_address):
17
+ """Create and return a Limiter instance for rate limiting."""
18
+ return Limiter(key_func=key_func)
19
+
20
+
21
+ def setup_rate_limit(
22
+ app: FastAPI, rate_limit: Optional[str] = None, key_func=get_remote_address
23
+ ):
24
+ """Set up rate limiting middleware."""
25
+ RATE_LIMIT = rate_limit or os.getenv("STAC_FASTAPI_RATE_LIMIT")
26
+
27
+ if not RATE_LIMIT:
28
+ logger.info("Rate limiting is disabled")
29
+ return
30
+
31
+ logger.info(f"Setting up rate limit with RATE_LIMIT={RATE_LIMIT}")
32
+
33
+ limiter = get_limiter(key_func)
34
+ app.state.limiter = limiter
35
+ app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
36
+ app.add_middleware(SlowAPIMiddleware)
37
+
38
+ @app.middleware("http")
39
+ @limiter.limit(RATE_LIMIT)
40
+ async def rate_limit_middleware(request: Request, call_next):
41
+ response = await call_next(request)
42
+ return response
43
+
44
+ logger.info("Rate limit setup complete")
@@ -1,2 +1,2 @@
1
1
  """library version."""
2
- __version__ = "3.1.0"
2
+ __version__ = "3.2.1"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: stac-fastapi.core
3
- Version: 3.1.0
3
+ Version: 3.2.1
4
4
  Summary: Core library for the Elasticsearch and Opensearch stac-fastapi backends.
5
5
  Home-page: https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch
6
6
  License: MIT
@@ -15,19 +15,6 @@ Classifier: Programming Language :: Python :: 3.12
15
15
  Classifier: License :: OSI Approved :: MIT License
16
16
  Requires-Python: >=3.8
17
17
  Description-Content-Type: text/markdown
18
- Requires-Dist: fastapi-slim
19
- Requires-Dist: attrs>=23.2.0
20
- Requires-Dist: pydantic[dotenv]
21
- Requires-Dist: stac_pydantic>=3
22
- Requires-Dist: stac-fastapi.types==3.0.0
23
- Requires-Dist: stac-fastapi.api==3.0.0
24
- Requires-Dist: stac-fastapi.extensions==3.0.0
25
- Requires-Dist: orjson
26
- Requires-Dist: overrides
27
- Requires-Dist: geojson-pydantic
28
- Requires-Dist: pygeofilter==0.2.1
29
- Requires-Dist: typing_extensions==4.8.0
30
- Requires-Dist: jsonschema
31
18
 
32
19
  # stac-fastapi-elasticsearch-opensearch (sfeos)
33
20
 
@@ -74,17 +61,24 @@ or
74
61
  pip install stac_fastapi.opensearch
75
62
  ```
76
63
 
77
- ## Build Elasticsearch API backend
64
+ ### To install and run via pre-built Docker Images
65
+
66
+ We provide ready-to-use Docker images through GitHub Container Registry ([ElasticSearch](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pkgs/container/stac-fastapi-es) and [OpenSearch](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/pkgs/container/stac-fastapi-os) backends). You can easily pull and run these images:
78
67
 
79
68
  ```shell
80
- docker-compose up elasticsearch
81
- docker-compose build app-elasticsearch
69
+ # For Elasticsearch backend
70
+ docker pull ghcr.io/stac-utils/stac-fastapi-es:latest
71
+
72
+ # For OpenSearch backend
73
+ docker pull ghcr.io/stac-utils/stac-fastapi-os:latest
82
74
  ```
83
75
 
84
- ## Running Elasticsearch API on localhost:8080
76
+ ## Run Elasticsearch API backend on localhost:8080
77
+
78
+ You need to ensure [**Docker Compose**](https://docs.docker.com/compose/install/) or [**Podman Compose**](https://podman-desktop.io/docs/compose) installed and running on your machine. In the follwoing command instead of `docker-compose` you can use `podman-compose` as well.
85
79
 
86
80
  ```shell
87
- docker-compose up app-elasticsearch
81
+ docker-compose up elasticsearch app-elasticsearch
88
82
  ```
89
83
 
90
84
  By default, docker-compose uses Elasticsearch 8.x and OpenSearch 2.11.1.
@@ -97,6 +91,35 @@ OPENSEARCH_VERSION=2.11.0
97
91
  ```
98
92
  The most recent Elasticsearch 7.x versions should also work. See the [opensearch-py docs](https://github.com/opensearch-project/opensearch-py/blob/main/COMPATIBILITY.md) for compatibility information.
99
93
 
94
+ #### **Configuration reference keys:**
95
+
96
+ You can customize additional settings in your `.env` file:
97
+ ###### Key variables to configure:
98
+
99
+ | Variable | Description | Default | Required |
100
+ |------------------------------|--------------------------------------------------------------------------------------|--------------------------|---------------------------------------------------------------------------------------------|
101
+ | `ES_HOST` | Hostname for external Elasticsearch/OpenSearch. | `localhost` | Optional |
102
+ | `ES_PORT` | Port for Elasticsearch/OpenSearch. | `9200` (ES) / `9202` (OS)| Optional |
103
+ | `ES_USE_SSL` | Use SSL for connecting to Elasticsearch/OpenSearch. | `false` | Optional |
104
+ | `ES_VERIFY_CERTS` | Verify SSL certificates when connecting. | `false` | Optional |
105
+ | `STAC_FASTAPI_TITLE` | Title of the API in the documentation. | `stac-fastapi-elasticsearch` or `stac-fastapi-opensearch` | Optional |
106
+ | `STAC_FASTAPI_DESCRIPTION` | Description of the API in the documentation. | N/A | Optional |
107
+ | `STAC_FASTAPI_VERSION` | API version. | `2.1` | Optional |
108
+ | `APP_HOST` | Server bind address. | `0.0.0.0` | Optional |
109
+ | `APP_PORT` | Server port. | `8080` | Optional |
110
+ | `ENVIRONMENT` | Runtime environment. | `local` | Optional |
111
+ | `WEB_CONCURRENCY` | Number of worker processes. | `10` | Optional |
112
+ | `RELOAD` | Enable auto-reload for development. | `true` | Optional |
113
+ | `STAC_FASTAPI_RATE_LIMIT` | API rate limit per client. | `200/minute` | Optional |
114
+ | `BACKEND` | Tests-related variable | `elasticsearch` or `opensearch` based on the backend | Optional |
115
+ | `ELASTICSEARCH_VERSION` | ElasticSearch version | `7.17.1` | Optional |
116
+ | `OPENSEARCH_VERSION` | OpenSearch version | `2.11.0` | Optional |
117
+
118
+ > [!NOTE]
119
+ > The variables `ES_HOST`, `ES_PORT`, `ES_USE_SSL`, and `ES_VERIFY_CERTS` apply to both Elasticsearch and OpenSearch backends, so there is no need to rename the key names to `OS_` even if you're using OpenSearch.
120
+
121
+ ## Interacting with the API
122
+
100
123
  To create a new Collection:
101
124
 
102
125
  ```shell
@@ -310,108 +333,10 @@ The modified Items with lowercase identifiers will now be visible to users acces
310
333
 
311
334
  Authentication is an optional feature that can be enabled through `Route Dependencies` examples can be found and a more detailed explanation in [examples/auth](examples/auth).
312
335
 
313
-
314
336
  ## Aggregation
315
337
 
316
- Sfeos supports the STAC API [Aggregation Extension](https://github.com/stac-api-extensions/aggregation). This enables geospatial aggregation of points and geometries, as well as frequency distribution aggregation of any other property including dates. Aggregations can be defined at the root Catalog level (`/aggregations`) and at the Collection level (`/<collection_id>/aggregations`). The `/aggregate` route also fully supports base search and the STAC API [Filter Extension](https://github.com/stac-api-extensions/filter). Any query made with `/search` may also be executed with `/aggregate`, provided that the relevant aggregation fields are available,
317
-
338
+ Aggregation of points and geometries, as well as frequency distribution aggregation of any other property including dates is supported in stac-fatsapi-elasticsearch-opensearch. Aggregations can be defined at the root Catalog level (`/aggregations`) and at the Collection level (`/<collection_id>/aggregations`). Details for supported aggregations can be found at [./docs/src/aggregation.md](./docs/src/aggregation.md)
318
339
 
319
- A field named `aggregations` should be added to the Collection object for the collection for which the aggregations are available, for example:
340
+ ## Rate Limiting
320
341
 
321
- ```json
322
- "aggregations": [
323
- {
324
- "name": "total_count",
325
- "data_type": "integer"
326
- },
327
- {
328
- "name": "datetime_max",
329
- "data_type": "datetime"
330
- },
331
- {
332
- "name": "datetime_min",
333
- "data_type": "datetime"
334
- },
335
- {
336
- "name": "datetime_frequency",
337
- "data_type": "frequency_distribution",
338
- "frequency_distribution_data_type": "datetime"
339
- },
340
- {
341
- "name": "sun_elevation_frequency",
342
- "data_type": "frequency_distribution",
343
- "frequency_distribution_data_type": "numeric"
344
- },
345
- {
346
- "name": "platform_frequency",
347
- "data_type": "frequency_distribution",
348
- "frequency_distribution_data_type": "string"
349
- },
350
- {
351
- "name": "sun_azimuth_frequency",
352
- "data_type": "frequency_distribution",
353
- "frequency_distribution_data_type": "numeric"
354
- },
355
- {
356
- "name": "off_nadir_frequency",
357
- "data_type": "frequency_distribution",
358
- "frequency_distribution_data_type": "numeric"
359
- },
360
- {
361
- "name": "cloud_cover_frequency",
362
- "data_type": "frequency_distribution",
363
- "frequency_distribution_data_type": "numeric"
364
- },
365
- {
366
- "name": "grid_code_frequency",
367
- "data_type": "frequency_distribution",
368
- "frequency_distribution_data_type": "string"
369
- },
370
- {
371
- "name": "centroid_geohash_grid_frequency",
372
- "data_type": "frequency_distribution",
373
- "frequency_distribution_data_type": "string"
374
- },
375
- {
376
- "name": "centroid_geohex_grid_frequency",
377
- "data_type": "frequency_distribution",
378
- "frequency_distribution_data_type": "string"
379
- },
380
- {
381
- "name": "centroid_geotile_grid_frequency",
382
- "data_type": "frequency_distribution",
383
- "frequency_distribution_data_type": "string"
384
- },
385
- {
386
- "name": "geometry_geohash_grid_frequency",
387
- "data_type": "frequency_distribution",
388
- "frequency_distribution_data_type": "numeric"
389
- },
390
- {
391
- "name": "geometry_geotile_grid_frequency",
392
- "data_type": "frequency_distribution",
393
- "frequency_distribution_data_type": "string"
394
- }
395
- ]
396
- ```
397
-
398
- Available aggregations are:
399
-
400
- - total_count (count of total items)
401
- - collection_frequency (Item `collection` field)
402
- - platform_frequency (Item.Properties.platform)
403
- - cloud_cover_frequency (Item.Properties.eo:cloud_cover)
404
- - datetime_frequency (Item.Properties.datetime, monthly interval)
405
- - datetime_min (earliest Item.Properties.datetime)
406
- - datetime_max (latest Item.Properties.datetime)
407
- - sun_elevation_frequency (Item.Properties.view:sun_elevation)
408
- - sun_azimuth_frequency (Item.Properties.view:sun_azimuth)
409
- - off_nadir_frequency (Item.Properties.view:off_nadir)
410
- - grid_code_frequency (Item.Properties.grid:code)
411
- - centroid_geohash_grid_frequency ([geohash grid](https://opensearch.org/docs/latest/aggregations/bucket/geohash-grid/) on Item.Properties.proj:centroid)
412
- - centroid_geohex_grid_frequency ([geohex grid](https://opensearch.org/docs/latest/aggregations/bucket/geohex-grid/) on Item.Properties.proj:centroid)
413
- - centroid_geotile_grid_frequency (geotile on Item.Properties.proj:centroid)
414
- - geometry_geohash_grid_frequency ([geohash grid](https://opensearch.org/docs/latest/aggregations/bucket/geohash-grid/) on Item.geometry)
415
- - geometry_geotile_grid_frequency ([geotile grid](https://opensearch.org/docs/latest/aggregations/bucket/geotile-grid/) on Item.geometry)
416
-
417
- Support for additional fields and new aggregations can be added in the associated `database_logic.py` file.
342
+ Rate limiting is an optional security feature that controls API request frequency on a remote address basis. It's enabled by setting the `STAC_FASTAPI_RATE_LIMIT` environment variable, e.g., `500/minute`. This limits each client to 500 requests per minute, helping prevent abuse and maintain API stability. Implementation examples are available in the [examples/rate_limit](examples/rate_limit) directory.
@@ -13,6 +13,7 @@ stac_fastapi/core/base_settings.py
13
13
  stac_fastapi/core/basic_auth.py
14
14
  stac_fastapi/core/core.py
15
15
  stac_fastapi/core/datetime_utils.py
16
+ stac_fastapi/core/rate_limit.py
16
17
  stac_fastapi/core/route_dependencies.py
17
18
  stac_fastapi/core/serializers.py
18
19
  stac_fastapi/core/session.py
@@ -11,3 +11,4 @@ geojson-pydantic
11
11
  pygeofilter==0.2.1
12
12
  typing_extensions==4.8.0
13
13
  jsonschema
14
+ slowapi==0.1.9