stac-fastapi-opensearch 4.0.0a1__py3-none-any.whl → 4.0.0a2__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.
@@ -87,7 +87,7 @@ post_request_model = create_post_request_model(search_extensions)
87
87
  api = StacApi(
88
88
  title=os.getenv("STAC_FASTAPI_TITLE", "stac-fastapi-opensearch"),
89
89
  description=os.getenv("STAC_FASTAPI_DESCRIPTION", "stac-fastapi-opensearch"),
90
- api_version=os.getenv("STAC_FASTAPI_VERSION", "2.1"),
90
+ api_version=os.getenv("STAC_FASTAPI_VERSION", "4.0.0a2"),
91
91
  settings=settings,
92
92
  extensions=extensions,
93
93
  client=CoreClient(
@@ -100,6 +100,7 @@ api = StacApi(
100
100
  app = api.app
101
101
  app.root_path = os.getenv("STAC_FASTAPI_ROOT_PATH", "")
102
102
 
103
+
103
104
  # Add rate limit
104
105
  setup_rate_limit(app, rate_limit=os.getenv("STAC_FASTAPI_RATE_LIMIT"))
105
106
 
@@ -1,4 +1,5 @@
1
1
  """API configuration."""
2
+ import logging
2
3
  import os
3
4
  import ssl
4
5
  from typing import Any, Dict, Set
@@ -7,12 +8,13 @@ import certifi
7
8
  from opensearchpy import AsyncOpenSearch, OpenSearch
8
9
 
9
10
  from stac_fastapi.core.base_settings import ApiBaseSettings
11
+ from stac_fastapi.core.utilities import get_bool_env
10
12
  from stac_fastapi.types.config import ApiSettings
11
13
 
12
14
 
13
15
  def _es_config() -> Dict[str, Any]:
14
16
  # Determine the scheme (http or https)
15
- use_ssl = os.getenv("ES_USE_SSL", "true").lower() == "true"
17
+ use_ssl = get_bool_env("ES_USE_SSL", default=True)
16
18
  scheme = "https" if use_ssl else "http"
17
19
 
18
20
  # Configure the hosts parameter with the correct scheme
@@ -33,7 +35,7 @@ def _es_config() -> Dict[str, Any]:
33
35
  "headers": {"accept": "application/json", "Content-Type": "application/json"},
34
36
  }
35
37
 
36
- http_compress = os.getenv("ES_HTTP_COMPRESS", "true").lower() == "true"
38
+ http_compress = get_bool_env("ES_HTTP_COMPRESS", default=True)
37
39
  if http_compress:
38
40
  config["http_compress"] = True
39
41
 
@@ -42,8 +44,8 @@ def _es_config() -> Dict[str, Any]:
42
44
  return config
43
45
 
44
46
  # Include SSL settings if using https
45
- config["ssl_version"] = ssl.PROTOCOL_SSLv23 # type: ignore
46
- config["verify_certs"] = os.getenv("ES_VERIFY_CERTS", "true").lower() != "false" # type: ignore
47
+ config["ssl_version"] = ssl.PROTOCOL_SSLv23
48
+ config["verify_certs"] = get_bool_env("ES_VERIFY_CERTS", default=True)
47
49
 
48
50
  # Include CA Certificates if verifying certs
49
51
  if config["verify_certs"]:
@@ -69,11 +71,18 @@ _forbidden_fields: Set[str] = {"type"}
69
71
 
70
72
 
71
73
  class OpensearchSettings(ApiSettings, ApiBaseSettings):
72
- """API settings."""
74
+ """
75
+ API settings.
76
+
77
+ Set enable_direct_response via the ENABLE_DIRECT_RESPONSE environment variable.
78
+ If enabled, all API routes use direct response for maximum performance, but ALL FastAPI dependencies (including authentication, custom status codes, and validation) are disabled.
79
+ Default is False for safety.
80
+ """
73
81
 
74
- # Fields which are defined by STAC but not included in the database model
75
82
  forbidden_fields: Set[str] = _forbidden_fields
76
83
  indexed_fields: Set[str] = {"datetime"}
84
+ enable_response_models: bool = False
85
+ enable_direct_response: bool = get_bool_env("ENABLE_DIRECT_RESPONSE", default=False)
77
86
 
78
87
  @property
79
88
  def create_client(self):
@@ -82,13 +91,31 @@ class OpensearchSettings(ApiSettings, ApiBaseSettings):
82
91
 
83
92
 
84
93
  class AsyncOpensearchSettings(ApiSettings, ApiBaseSettings):
85
- """API settings."""
94
+ """
95
+ API settings.
96
+
97
+ Set enable_direct_response via the ENABLE_DIRECT_RESPONSE environment variable.
98
+ If enabled, all API routes use direct response for maximum performance, but ALL FastAPI dependencies (including authentication, custom status codes, and validation) are disabled.
99
+ Default is False for safety.
100
+ """
86
101
 
87
- # Fields which are defined by STAC but not included in the database model
88
102
  forbidden_fields: Set[str] = _forbidden_fields
89
103
  indexed_fields: Set[str] = {"datetime"}
104
+ enable_response_models: bool = False
105
+ enable_direct_response: bool = get_bool_env("ENABLE_DIRECT_RESPONSE", default=False)
90
106
 
91
107
  @property
92
108
  def create_client(self):
93
109
  """Create async elasticsearch client."""
94
110
  return AsyncOpenSearch(**_es_config())
111
+
112
+
113
+ # Warn at import if direct response is enabled (applies to either settings class)
114
+ if (
115
+ OpensearchSettings().enable_direct_response
116
+ or AsyncOpensearchSettings().enable_direct_response
117
+ ):
118
+ logging.basicConfig(level=logging.WARNING)
119
+ logging.warning(
120
+ "ENABLE_DIRECT_RESPONSE is True: All FastAPI dependencies (including authentication) are DISABLED for all routes!"
121
+ )
@@ -9,7 +9,6 @@ from typing import Any, Dict, Iterable, List, Optional, Tuple, Type
9
9
 
10
10
  import attr
11
11
  from opensearchpy import exceptions, helpers
12
- from opensearchpy.exceptions import TransportError
13
12
  from opensearchpy.helpers.query import Q
14
13
  from opensearchpy.helpers.search import Search
15
14
  from starlette.requests import Request
@@ -80,24 +79,21 @@ async def create_collection_index() -> None:
80
79
  """
81
80
  client = AsyncSearchSettings().create_client
82
81
 
83
- search_body: Dict[str, Any] = {
84
- "aliases": {COLLECTIONS_INDEX: {}},
85
- }
86
-
87
82
  index = f"{COLLECTIONS_INDEX}-000001"
88
83
 
89
- try:
90
- await client.indices.create(index=index, body=search_body)
91
- except TransportError as e:
92
- if e.status_code == 400:
93
- pass # Ignore 400 status codes
94
- else:
95
- raise e
96
-
84
+ exists = await client.indices.exists(index=index)
85
+ if not exists:
86
+ await client.indices.create(
87
+ index=index,
88
+ body={
89
+ "aliases": {COLLECTIONS_INDEX: {}},
90
+ "mappings": ES_COLLECTIONS_MAPPINGS,
91
+ },
92
+ )
97
93
  await client.close()
98
94
 
99
95
 
100
- async def create_item_index(collection_id: str):
96
+ async def create_item_index(collection_id: str) -> None:
101
97
  """
102
98
  Create the index for Items. The settings of the index template will be used implicitly.
103
99
 
@@ -109,24 +105,22 @@ async def create_item_index(collection_id: str):
109
105
 
110
106
  """
111
107
  client = AsyncSearchSettings().create_client
112
- search_body: Dict[str, Any] = {
113
- "aliases": {index_alias_by_collection_id(collection_id): {}},
114
- }
115
108
 
116
- try:
109
+ index_name = f"{index_by_collection_id(collection_id)}-000001"
110
+ exists = await client.indices.exists(index=index_name)
111
+ if not exists:
117
112
  await client.indices.create(
118
- index=f"{index_by_collection_id(collection_id)}-000001", body=search_body
113
+ index=index_name,
114
+ body={
115
+ "aliases": {index_alias_by_collection_id(collection_id): {}},
116
+ "mappings": ES_ITEMS_MAPPINGS,
117
+ "settings": ES_ITEMS_SETTINGS,
118
+ },
119
119
  )
120
- except TransportError as e:
121
- if e.status_code == 400:
122
- pass # Ignore 400 status codes
123
- else:
124
- raise e
125
-
126
120
  await client.close()
127
121
 
128
122
 
129
- async def delete_item_index(collection_id: str):
123
+ async def delete_item_index(collection_id: str) -> None:
130
124
  """Delete the index for items in a collection.
131
125
 
132
126
  Args:
@@ -1,2 +1,2 @@
1
1
  """library version."""
2
- __version__ = "4.0.0a1"
2
+ __version__ = "4.0.0a2"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: stac-fastapi-opensearch
3
- Version: 4.0.0a1
3
+ Version: 4.0.0a2
4
4
  Summary: Opensearch stac-fastapi backend.
5
5
  Home-page: https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch
6
6
  License: MIT
@@ -15,7 +15,7 @@ Classifier: Programming Language :: Python :: 3.13
15
15
  Classifier: License :: OSI Approved :: MIT License
16
16
  Requires-Python: >=3.9
17
17
  Description-Content-Type: text/markdown
18
- Requires-Dist: stac-fastapi-core==4.0.0a1
18
+ Requires-Dist: stac-fastapi-core==4.0.0a2
19
19
  Requires-Dist: opensearch-py~=2.8.0
20
20
  Requires-Dist: opensearch-py[async]~=2.8.0
21
21
  Requires-Dist: uvicorn~=0.23.0
@@ -66,8 +66,18 @@ Requires-Dist: uvicorn[standard]~=0.23.0; extra == "server"
66
66
  - There is [Postman](https://documenter.getpostman.com/view/12888943/2s8ZDSdRHA) documentation here for examples on how to run some of the API routes locally - after starting the elasticsearch backend via the compose.yml file.
67
67
  - The `/examples` folder shows an example of running stac-fastapi-elasticsearch from PyPI in docker without needing any code from the repository. There is also a Postman collection here that you can load into Postman for testing the API routes.
68
68
 
69
- - For changes, see the [Changelog](CHANGELOG.md)
70
- - We are always welcoming contributions. For the development notes: [Contributing](CONTRIBUTING.md)
69
+
70
+ ### Performance Note
71
+
72
+ The `enable_direct_response` option is provided by the stac-fastapi core library (introduced in stac-fastapi 5.2.0) and is available in this project starting from v4.0.0.
73
+
74
+ **You can now control this setting via the `ENABLE_DIRECT_RESPONSE` environment variable.**
75
+
76
+ When enabled (`ENABLE_DIRECT_RESPONSE=true`), endpoints return Starlette Response objects directly, bypassing FastAPI's default serialization for improved performance. **However, all FastAPI dependencies (including authentication, custom status codes, and validation) are disabled for all routes.**
77
+
78
+ This mode is best suited for public or read-only APIs where authentication and custom logic are not required. Default is `false` for safety.
79
+
80
+ See: [issue #347](https://github.com/stac-utils/stac-fastapi-elasticsearch-opensearch/issues/347)
71
81
 
72
82
 
73
83
  ### To install from PyPI:
@@ -111,8 +121,9 @@ If you wish to use a different version, put the following in a
111
121
  file named `.env` in the same directory you run Docker Compose from:
112
122
 
113
123
  ```shell
114
- ELASTICSEARCH_VERSION=7.17.1
115
- OPENSEARCH_VERSION=2.11.0
124
+ ELASTICSEARCH_VERSION=8.11.0
125
+ OPENSEARCH_VERSION=2.11.1
126
+ ENABLE_DIRECT_RESPONSE=false
116
127
  ```
117
128
  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.
118
129
 
@@ -137,8 +148,9 @@ You can customize additional settings in your `.env` file:
137
148
  | `RELOAD` | Enable auto-reload for development. | `true` | Optional |
138
149
  | `STAC_FASTAPI_RATE_LIMIT` | API rate limit per client. | `200/minute` | Optional |
139
150
  | `BACKEND` | Tests-related variable | `elasticsearch` or `opensearch` based on the backend | Optional |
140
- | `ELASTICSEARCH_VERSION` | ElasticSearch version | `7.17.1` | Optional |
141
- | `OPENSEARCH_VERSION` | OpenSearch version | `2.11.0` | Optional |
151
+ | `ELASTICSEARCH_VERSION` | Version of Elasticsearch to use. | `8.11.0` | Optional |
152
+ | `ENABLE_DIRECT_RESPONSE` | Enable direct response for maximum performance (disables all FastAPI dependencies, including authentication, custom status codes, and validation) | `false` | Optional |
153
+ | `OPENSEARCH_VERSION` | OpenSearch version | `2.11.1` | Optional |
142
154
 
143
155
  > [!NOTE]
144
156
  > 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.
@@ -0,0 +1,10 @@
1
+ stac_fastapi/opensearch/__init__.py,sha256=iJWMUgn7mUvmuPQSO_FlyhJ5eDdbbfmGv1qnFOX5-qk,28
2
+ stac_fastapi/opensearch/app.py,sha256=QwOOoTmQ5NN1ELnsKKISFb_qZyUd72c_mqHpXImuu3g,4163
3
+ stac_fastapi/opensearch/config.py,sha256=pTLzw9xt7rBcQCMAcPSrQ4Ty_oQDZmcM7DGZmD5x4uY,4077
4
+ stac_fastapi/opensearch/database_logic.py,sha256=ib43q2BHJXJZ7_oUhEdBAjLfsf87sQazbJzdiJaeCoc,35559
5
+ stac_fastapi/opensearch/version.py,sha256=GsmpaQvFvJbtYx8dw8vx3_8haCi-G94TpDhDpEjX-8E,47
6
+ stac_fastapi_opensearch-4.0.0a2.dist-info/METADATA,sha256=XSjUPKm9EksyYYFUaFfpgsbV2XXDZmMIKeaWhAtV9xo,20443
7
+ stac_fastapi_opensearch-4.0.0a2.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
8
+ stac_fastapi_opensearch-4.0.0a2.dist-info/entry_points.txt,sha256=zjZ0Xsr9BUNJqMkdPpl6zEIUykv1uFdJtNELFRChp0w,76
9
+ stac_fastapi_opensearch-4.0.0a2.dist-info/top_level.txt,sha256=vqn-D9-HsRPTTxy0Vk_KkDmTiMES4owwBQ3ydSZYb2s,13
10
+ stac_fastapi_opensearch-4.0.0a2.dist-info/RECORD,,
@@ -1,10 +0,0 @@
1
- stac_fastapi/opensearch/__init__.py,sha256=iJWMUgn7mUvmuPQSO_FlyhJ5eDdbbfmGv1qnFOX5-qk,28
2
- stac_fastapi/opensearch/app.py,sha256=NgoH3mkNPhDlrX4wdtNqGTwelzMMnNwIwOHGqUdu9D8,4158
3
- stac_fastapi/opensearch/config.py,sha256=tr-j9cLib-FYSSDkiOr8qJawICIVeRgcbzA54Z8A-rI,2950
4
- stac_fastapi/opensearch/database_logic.py,sha256=SQ7ebK5V3EH8scH9PAq9rgFZk_uZ24ElwooANQZTC00,35587
5
- stac_fastapi/opensearch/version.py,sha256=I6yN3SJF9c8gQmc5k4THOt7Vh8jd_Q50-0Zl7daL29k,47
6
- stac_fastapi_opensearch-4.0.0a1.dist-info/METADATA,sha256=H_GcFxnJ90DcTQ1gNBY_ptEcYosxBt28PjhcoygiZHo,19416
7
- stac_fastapi_opensearch-4.0.0a1.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
8
- stac_fastapi_opensearch-4.0.0a1.dist-info/entry_points.txt,sha256=zjZ0Xsr9BUNJqMkdPpl6zEIUykv1uFdJtNELFRChp0w,76
9
- stac_fastapi_opensearch-4.0.0a1.dist-info/top_level.txt,sha256=vqn-D9-HsRPTTxy0Vk_KkDmTiMES4owwBQ3ydSZYb2s,13
10
- stac_fastapi_opensearch-4.0.0a1.dist-info/RECORD,,