stac-fastapi-opensearch 4.0.0a1__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 @@
1
+ """opensearch submodule."""
@@ -0,0 +1,143 @@
1
+ """FastAPI application."""
2
+
3
+ import os
4
+
5
+ from stac_fastapi.api.app import StacApi
6
+ from stac_fastapi.api.models import create_get_request_model, create_post_request_model
7
+ from stac_fastapi.core.core import (
8
+ BulkTransactionsClient,
9
+ CoreClient,
10
+ EsAsyncBaseFiltersClient,
11
+ TransactionsClient,
12
+ )
13
+ from stac_fastapi.core.extensions import QueryExtension
14
+ from stac_fastapi.core.extensions.aggregation import (
15
+ EsAggregationExtensionGetRequest,
16
+ EsAggregationExtensionPostRequest,
17
+ EsAsyncAggregationClient,
18
+ )
19
+ from stac_fastapi.core.extensions.fields import FieldsExtension
20
+ from stac_fastapi.core.rate_limit import setup_rate_limit
21
+ from stac_fastapi.core.route_dependencies import get_route_dependencies
22
+ from stac_fastapi.core.session import Session
23
+ from stac_fastapi.extensions.core import (
24
+ AggregationExtension,
25
+ FilterExtension,
26
+ FreeTextExtension,
27
+ SortExtension,
28
+ TokenPaginationExtension,
29
+ TransactionExtension,
30
+ )
31
+ from stac_fastapi.extensions.third_party import BulkTransactionExtension
32
+ from stac_fastapi.opensearch.config import OpensearchSettings
33
+ from stac_fastapi.opensearch.database_logic import (
34
+ DatabaseLogic,
35
+ create_collection_index,
36
+ create_index_templates,
37
+ )
38
+
39
+ settings = OpensearchSettings()
40
+ session = Session.create_from_settings(settings)
41
+
42
+ database_logic = DatabaseLogic()
43
+
44
+ filter_extension = FilterExtension(
45
+ client=EsAsyncBaseFiltersClient(database=database_logic)
46
+ )
47
+ filter_extension.conformance_classes.append(
48
+ "http://www.opengis.net/spec/cql2/1.0/conf/advanced-comparison-operators"
49
+ )
50
+
51
+ aggregation_extension = AggregationExtension(
52
+ client=EsAsyncAggregationClient(
53
+ database=database_logic, session=session, settings=settings
54
+ )
55
+ )
56
+ aggregation_extension.POST = EsAggregationExtensionPostRequest
57
+ aggregation_extension.GET = EsAggregationExtensionGetRequest
58
+
59
+ search_extensions = [
60
+ TransactionExtension(
61
+ client=TransactionsClient(
62
+ database=database_logic, session=session, settings=settings
63
+ ),
64
+ settings=settings,
65
+ ),
66
+ BulkTransactionExtension(
67
+ client=BulkTransactionsClient(
68
+ database=database_logic,
69
+ session=session,
70
+ settings=settings,
71
+ )
72
+ ),
73
+ FieldsExtension(),
74
+ QueryExtension(),
75
+ SortExtension(),
76
+ TokenPaginationExtension(),
77
+ filter_extension,
78
+ FreeTextExtension(),
79
+ ]
80
+
81
+ extensions = [aggregation_extension] + search_extensions
82
+
83
+ database_logic.extensions = [type(ext).__name__ for ext in extensions]
84
+
85
+ post_request_model = create_post_request_model(search_extensions)
86
+
87
+ api = StacApi(
88
+ title=os.getenv("STAC_FASTAPI_TITLE", "stac-fastapi-opensearch"),
89
+ description=os.getenv("STAC_FASTAPI_DESCRIPTION", "stac-fastapi-opensearch"),
90
+ api_version=os.getenv("STAC_FASTAPI_VERSION", "2.1"),
91
+ settings=settings,
92
+ extensions=extensions,
93
+ client=CoreClient(
94
+ database=database_logic, session=session, post_request_model=post_request_model
95
+ ),
96
+ search_get_request_model=create_get_request_model(search_extensions),
97
+ search_post_request_model=post_request_model,
98
+ route_dependencies=get_route_dependencies(),
99
+ )
100
+ app = api.app
101
+ app.root_path = os.getenv("STAC_FASTAPI_ROOT_PATH", "")
102
+
103
+ # Add rate limit
104
+ setup_rate_limit(app, rate_limit=os.getenv("STAC_FASTAPI_RATE_LIMIT"))
105
+
106
+
107
+ @app.on_event("startup")
108
+ async def _startup_event() -> None:
109
+ await create_index_templates()
110
+ await create_collection_index()
111
+
112
+
113
+ def run() -> None:
114
+ """Run app from command line using uvicorn if available."""
115
+ try:
116
+ import uvicorn
117
+
118
+ uvicorn.run(
119
+ "stac_fastapi.opensearch.app:app",
120
+ host=settings.app_host,
121
+ port=settings.app_port,
122
+ log_level="info",
123
+ reload=settings.reload,
124
+ )
125
+ except ImportError:
126
+ raise RuntimeError("Uvicorn must be installed in order to use command")
127
+
128
+
129
+ if __name__ == "__main__":
130
+ run()
131
+
132
+
133
+ def create_handler(app):
134
+ """Create a handler to use with AWS Lambda if mangum available."""
135
+ try:
136
+ from mangum import Mangum
137
+
138
+ return Mangum(app)
139
+ except ImportError:
140
+ return None
141
+
142
+
143
+ handler = create_handler(app)
@@ -0,0 +1,94 @@
1
+ """API configuration."""
2
+ import os
3
+ import ssl
4
+ from typing import Any, Dict, Set
5
+
6
+ import certifi
7
+ from opensearchpy import AsyncOpenSearch, OpenSearch
8
+
9
+ from stac_fastapi.core.base_settings import ApiBaseSettings
10
+ from stac_fastapi.types.config import ApiSettings
11
+
12
+
13
+ def _es_config() -> Dict[str, Any]:
14
+ # Determine the scheme (http or https)
15
+ use_ssl = os.getenv("ES_USE_SSL", "true").lower() == "true"
16
+ scheme = "https" if use_ssl else "http"
17
+
18
+ # Configure the hosts parameter with the correct scheme
19
+ es_hosts = os.getenv(
20
+ "ES_HOST", "localhost"
21
+ ).strip() # Default to localhost if ES_HOST is not set
22
+ es_port = os.getenv("ES_PORT", "9200") # Default to 9200 if ES_PORT is not set
23
+
24
+ # Validate ES_HOST
25
+ if not es_hosts:
26
+ raise ValueError("ES_HOST environment variable is empty or invalid.")
27
+
28
+ hosts = [f"{scheme}://{host.strip()}:{es_port}" for host in es_hosts.split(",")]
29
+
30
+ # Initialize the configuration dictionary
31
+ config: Dict[str, Any] = {
32
+ "hosts": hosts,
33
+ "headers": {"accept": "application/json", "Content-Type": "application/json"},
34
+ }
35
+
36
+ http_compress = os.getenv("ES_HTTP_COMPRESS", "true").lower() == "true"
37
+ if http_compress:
38
+ config["http_compress"] = True
39
+
40
+ # Explicitly exclude SSL settings when not using SSL
41
+ if not use_ssl:
42
+ return config
43
+
44
+ # 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
+
48
+ # Include CA Certificates if verifying certs
49
+ if config["verify_certs"]:
50
+ config["ca_certs"] = os.getenv("CURL_CA_BUNDLE", certifi.where())
51
+
52
+ # Handle authentication
53
+ if (u := os.getenv("ES_USER")) and (p := os.getenv("ES_PASS")):
54
+ config["http_auth"] = (u, p)
55
+
56
+ if api_key := os.getenv("ES_API_KEY"):
57
+ if isinstance(config["headers"], dict):
58
+ headers = {**config["headers"], "x-api-key": api_key}
59
+
60
+ else:
61
+ config["headers"] = {"x-api-key": api_key}
62
+
63
+ config["headers"] = headers
64
+
65
+ return config
66
+
67
+
68
+ _forbidden_fields: Set[str] = {"type"}
69
+
70
+
71
+ class OpensearchSettings(ApiSettings, ApiBaseSettings):
72
+ """API settings."""
73
+
74
+ # Fields which are defined by STAC but not included in the database model
75
+ forbidden_fields: Set[str] = _forbidden_fields
76
+ indexed_fields: Set[str] = {"datetime"}
77
+
78
+ @property
79
+ def create_client(self):
80
+ """Create es client."""
81
+ return OpenSearch(**_es_config())
82
+
83
+
84
+ class AsyncOpensearchSettings(ApiSettings, ApiBaseSettings):
85
+ """API settings."""
86
+
87
+ # Fields which are defined by STAC but not included in the database model
88
+ forbidden_fields: Set[str] = _forbidden_fields
89
+ indexed_fields: Set[str] = {"datetime"}
90
+
91
+ @property
92
+ def create_client(self):
93
+ """Create async elasticsearch client."""
94
+ return AsyncOpenSearch(**_es_config())