eoapi-cdk 11.4.1 → 11.6.0
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.
- package/.jsii +38 -23
- package/README.md +2 -0
- package/lib/bastion-host/index.js +1 -1
- package/lib/database/index.js +1 -1
- package/lib/ingestor-api/index.d.ts +4 -0
- package/lib/ingestor-api/index.js +8 -3
- package/lib/ingestor-api/runtime/Dockerfile +13 -9
- package/lib/ingestor-api/runtime/pyproject.toml +37 -0
- package/lib/ingestor-api/runtime/src/config.py +9 -0
- package/lib/ingestor-api/runtime/src/handler.py +27 -4
- package/lib/ingestor-api/runtime/src/ingestor.py +1 -1
- package/lib/ingestor-api/runtime/src/schemas.py +11 -19
- package/lib/ingestor-api/runtime/src/services.py +4 -2
- package/lib/ingestor-api/runtime/uv.lock +1313 -0
- package/lib/lambda-api-gateway/index.js +1 -1
- package/lib/lambda-api-gateway-private/index.js +1 -1
- package/lib/stac-api/index.js +2 -2
- package/lib/stac-api/runtime/README.md +36 -0
- package/lib/stac-api/runtime/pyproject.toml +2 -2
- package/lib/stac-api/runtime/src/stac_api/handler.py +82 -86
- package/lib/stac-api/runtime/uv.lock +206 -182
- package/lib/stac-auth-proxy/index.js +2 -2
- package/lib/stac-auth-proxy/runtime/pyproject.toml +2 -2
- package/lib/stac-auth-proxy/runtime/src/stac_auth_proxy_api/handler.py +25 -2
- package/lib/stac-auth-proxy/runtime/uv.lock +174 -248
- package/lib/stac-browser/index.js +1 -1
- package/lib/stac-loader/index.js +2 -2
- package/lib/stac-loader/runtime/Dockerfile +1 -0
- package/lib/stac-loader/runtime/src/stac_loader/handler.py +2 -25
- package/lib/stac-loader/runtime/uv.lock +251 -252
- package/lib/stactools-item-generator/index.js +1 -1
- package/lib/stactools-item-generator/runtime/uv.lock +79 -75
- package/lib/tipg-api/index.js +2 -2
- package/lib/tipg-api/runtime/pyproject.toml +1 -1
- package/lib/tipg-api/runtime/src/tipg_api/handler.py +85 -75
- package/lib/tipg-api/runtime/uv.lock +248 -243
- package/lib/titiler-pgstac-api/index.js +2 -2
- package/lib/titiler-pgstac-api/runtime/pyproject.toml +2 -2
- package/lib/titiler-pgstac-api/runtime/src/titiler_pgstac_api/handler.py +79 -50
- package/lib/titiler-pgstac-api/runtime/uv.lock +319 -280
- package/lib/utils/utils.py +49 -15
- package/package.json +4 -4
- package/pyproject.toml +4 -0
- package/uv.lock +475 -73
- package/lib/ingestor-api/runtime/dev_requirements.txt +0 -4
- package/lib/ingestor-api/runtime/requirements.txt +0 -10
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Handler for AWS Lambda.
|
|
3
|
-
"""
|
|
1
|
+
"""Handler for AWS Lambda."""
|
|
4
2
|
|
|
5
|
-
import
|
|
3
|
+
import logging
|
|
6
4
|
import os
|
|
5
|
+
from collections.abc import AsyncIterator, Mapping
|
|
6
|
+
from contextlib import asynccontextmanager
|
|
7
|
+
from typing import Any
|
|
7
8
|
|
|
8
9
|
from mangum import Mangum
|
|
9
10
|
from snapshot_restore_py import register_after_restore, register_before_snapshot
|
|
@@ -15,12 +16,15 @@ from tipg.settings import (
|
|
|
15
16
|
DatabaseSettings,
|
|
16
17
|
PostgresSettings,
|
|
17
18
|
)
|
|
18
|
-
from utils import get_secret_dict
|
|
19
|
+
from utils import ensure_event_loop, get_secret_dict, run_async
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
19
22
|
|
|
20
23
|
db_settings = DatabaseSettings()
|
|
21
24
|
custom_sql_settings = CustomSQLSettings()
|
|
22
25
|
|
|
23
26
|
_connection_initialized = False
|
|
27
|
+
_original_lifespan = app.router.lifespan_context
|
|
24
28
|
|
|
25
29
|
|
|
26
30
|
def _build_postgres_settings() -> PostgresSettings:
|
|
@@ -35,95 +39,101 @@ def _build_postgres_settings() -> PostgresSettings:
|
|
|
35
39
|
)
|
|
36
40
|
|
|
37
41
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"""
|
|
41
|
-
Runtime hook called by Lambda before taking a snapshot.
|
|
42
|
-
We close database connections that shouldn't be in the snapshot.
|
|
43
|
-
"""
|
|
44
|
-
|
|
45
|
-
# Close any existing database connections before the snapshot is taken
|
|
42
|
+
def _close_db_pool() -> None:
|
|
43
|
+
"""Close the current database pool if one exists."""
|
|
46
44
|
if hasattr(app, "state") and hasattr(app.state, "pool") and app.state.pool:
|
|
47
45
|
try:
|
|
48
46
|
app.state.pool.close()
|
|
47
|
+
except Exception:
|
|
48
|
+
logger.exception("SnapStart: error closing database pool")
|
|
49
|
+
finally:
|
|
49
50
|
app.state.pool = None
|
|
50
|
-
except Exception as e:
|
|
51
|
-
print(f"SnapStart: Error closing database pool: {e}")
|
|
52
|
-
|
|
53
|
-
return {"statusCode": 200}
|
|
54
51
|
|
|
55
52
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
"""
|
|
59
|
-
Runtime hook called by Lambda after restoring from a snapshot.
|
|
60
|
-
We recreate database connections that were closed before the snapshot.
|
|
61
|
-
"""
|
|
53
|
+
async def _initialize_connection() -> None:
|
|
54
|
+
"""Create a fresh database connection pool and register collections."""
|
|
62
55
|
global _connection_initialized
|
|
63
56
|
|
|
64
|
-
|
|
65
|
-
# Get the event loop or create a new one
|
|
66
|
-
try:
|
|
67
|
-
loop = asyncio.get_running_loop()
|
|
68
|
-
except RuntimeError:
|
|
69
|
-
loop = asyncio.new_event_loop()
|
|
70
|
-
asyncio.set_event_loop(loop)
|
|
71
|
-
|
|
72
|
-
# Close any existing pool (from snapshot)
|
|
73
|
-
if hasattr(app.state, "pool") and app.state.pool:
|
|
74
|
-
try:
|
|
75
|
-
app.state.pool.close()
|
|
76
|
-
except Exception as e:
|
|
77
|
-
print(f"SnapStart: Error closing stale pool: {e}")
|
|
78
|
-
app.state.pool = None
|
|
79
|
-
|
|
80
|
-
# Create fresh connection pool
|
|
81
|
-
postgres_settings = _build_postgres_settings()
|
|
82
|
-
loop.run_until_complete(
|
|
83
|
-
connect_to_db(
|
|
84
|
-
app,
|
|
85
|
-
schemas=db_settings.schemas,
|
|
86
|
-
tipg_schema=db_settings.tipg_schema,
|
|
87
|
-
user_sql_files=custom_sql_settings.sql_files,
|
|
88
|
-
settings=postgres_settings,
|
|
89
|
-
)
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
loop.run_until_complete(
|
|
93
|
-
register_collection_catalog(
|
|
94
|
-
app,
|
|
95
|
-
db_settings=db_settings,
|
|
96
|
-
)
|
|
97
|
-
)
|
|
98
|
-
|
|
99
|
-
_connection_initialized = True
|
|
100
|
-
|
|
101
|
-
except Exception as e:
|
|
102
|
-
print(f"SnapStart: Failed to initialize database connection: {e}")
|
|
103
|
-
raise
|
|
104
|
-
|
|
105
|
-
return {"statusCode": 200}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
@app.on_event("startup")
|
|
109
|
-
async def startup_event() -> None:
|
|
110
|
-
"""Connect to database on startup."""
|
|
111
|
-
postgres_settings = _build_postgres_settings()
|
|
57
|
+
_close_db_pool()
|
|
112
58
|
await connect_to_db(
|
|
113
59
|
app,
|
|
114
60
|
schemas=db_settings.schemas,
|
|
115
61
|
tipg_schema=db_settings.tipg_schema,
|
|
116
62
|
user_sql_files=custom_sql_settings.sql_files,
|
|
117
|
-
settings=
|
|
63
|
+
settings=_build_postgres_settings(),
|
|
118
64
|
)
|
|
119
65
|
await register_collection_catalog(
|
|
120
66
|
app,
|
|
121
67
|
db_settings=db_settings,
|
|
122
68
|
)
|
|
69
|
+
_connection_initialized = True
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
async def _shutdown_connection() -> None:
|
|
73
|
+
"""Close the current database pool if it exists."""
|
|
74
|
+
global _connection_initialized
|
|
75
|
+
|
|
76
|
+
_close_db_pool()
|
|
77
|
+
_connection_initialized = False
|
|
123
78
|
|
|
124
79
|
|
|
125
|
-
|
|
80
|
+
@register_before_snapshot
|
|
81
|
+
def on_snapshot() -> dict[str, int]:
|
|
82
|
+
"""Close database connections before Lambda SnapStart takes a snapshot."""
|
|
83
|
+
_close_db_pool()
|
|
84
|
+
return {"statusCode": 200}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@register_after_restore
|
|
88
|
+
def on_snap_restore() -> dict[str, int]:
|
|
89
|
+
"""Recreate database connections after Lambda SnapStart restores a snapshot."""
|
|
90
|
+
try:
|
|
91
|
+
run_async(_initialize_connection())
|
|
92
|
+
except Exception:
|
|
93
|
+
logger.exception("SnapStart: failed to initialize database connection")
|
|
94
|
+
raise
|
|
95
|
+
|
|
96
|
+
return {"statusCode": 200}
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@asynccontextmanager
|
|
100
|
+
async def lifespan(app_instance) -> AsyncIterator[Mapping[str, Any] | None]:
|
|
101
|
+
"""Wrap the upstream lifespan with database setup and teardown.
|
|
102
|
+
|
|
103
|
+
We keep the app's lifespan wiring intact for non-Lambda contexts, but the
|
|
104
|
+
Lambda runtime below uses ``Mangum(..., lifespan="off")`` and performs
|
|
105
|
+
connection setup explicitly. In sandbox testing, Mangum lifespan handling
|
|
106
|
+
was not a drop-in replacement for Lambda-container-scoped pool reuse.
|
|
107
|
+
"""
|
|
108
|
+
async with _original_lifespan(app_instance) as state:
|
|
109
|
+
await _initialize_connection()
|
|
110
|
+
try:
|
|
111
|
+
yield state
|
|
112
|
+
finally:
|
|
113
|
+
await _shutdown_connection()
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
app.router.lifespan_context = lifespan
|
|
117
|
+
|
|
118
|
+
# The Lambda runtime initializes long-lived async resources on an installed
|
|
119
|
+
# reusable loop, then hands request execution to Mangum. ``ensure_event_loop``
|
|
120
|
+
# is a defensive step for those synchronous initialization paths. It is not here
|
|
121
|
+
# because normal FastAPI route execution cannot run without it.
|
|
122
|
+
_asgi_handler = Mangum(app, lifespan="off")
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def handler(event: Any, context: Any) -> dict[str, Any]:
|
|
126
|
+
"""Handle AWS Lambda events with a reusable installed event loop.
|
|
127
|
+
|
|
128
|
+
This supports synchronous Lambda-side async setup such as cold-start and
|
|
129
|
+
SnapStart restore initialization before control passes to Mangum.
|
|
130
|
+
"""
|
|
131
|
+
ensure_event_loop()
|
|
132
|
+
return _asgi_handler(event, context)
|
|
133
|
+
|
|
126
134
|
|
|
127
135
|
if "AWS_EXECUTION_ENV" in os.environ:
|
|
128
|
-
|
|
129
|
-
loop
|
|
136
|
+
# Avoid ``asyncio.run(...)`` here. It would create the pool on a temporary
|
|
137
|
+
# loop and then close it, which is a poor fit for container-scoped async
|
|
138
|
+
# resources that should live on the installed reusable loop.
|
|
139
|
+
run_async(_initialize_connection())
|