eoapi-cdk 5.0.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/.devcontainer/devcontainer.json +4 -0
- package/.github/workflows/build.yaml +73 -0
- package/.github/workflows/conventional-pr.yaml +26 -0
- package/.github/workflows/distribute.yaml +45 -0
- package/.github/workflows/docs.yaml +26 -0
- package/.github/workflows/test.yaml +13 -0
- package/.github/workflows/tox.yaml +24 -0
- package/.jsii +5058 -0
- package/.nvmrc +1 -0
- package/CHANGELOG.md +195 -0
- package/README.md +50 -0
- package/diagrams/bastion_diagram.excalidraw +1416 -0
- package/diagrams/bastion_diagram.png +0 -0
- package/diagrams/ingestor_diagram.excalidraw +2274 -0
- package/diagrams/ingestor_diagram.png +0 -0
- package/lib/bastion-host/index.d.ts +117 -0
- package/lib/bastion-host/index.js +162 -0
- package/lib/bootstrapper/index.d.ts +57 -0
- package/lib/bootstrapper/index.js +73 -0
- package/lib/bootstrapper/runtime/Dockerfile +18 -0
- package/lib/bootstrapper/runtime/handler.py +235 -0
- package/lib/database/index.d.ts +60 -0
- package/lib/database/index.js +84 -0
- package/lib/database/instance-memory.json +525 -0
- package/lib/index.d.ts +6 -0
- package/lib/index.js +19 -0
- package/lib/ingestor-api/index.d.ts +54 -0
- package/lib/ingestor-api/index.js +147 -0
- package/lib/ingestor-api/runtime/dev_requirements.txt +2 -0
- package/lib/ingestor-api/runtime/requirements.txt +12 -0
- package/lib/ingestor-api/runtime/src/__init__.py +0 -0
- package/lib/ingestor-api/runtime/src/collection.py +36 -0
- package/lib/ingestor-api/runtime/src/config.py +46 -0
- package/lib/ingestor-api/runtime/src/dependencies.py +94 -0
- package/lib/ingestor-api/runtime/src/handler.py +9 -0
- package/lib/ingestor-api/runtime/src/ingestor.py +82 -0
- package/lib/ingestor-api/runtime/src/loader.py +21 -0
- package/lib/ingestor-api/runtime/src/main.py +125 -0
- package/lib/ingestor-api/runtime/src/schemas.py +148 -0
- package/lib/ingestor-api/runtime/src/services.py +44 -0
- package/lib/ingestor-api/runtime/src/utils.py +52 -0
- package/lib/ingestor-api/runtime/src/validators.py +72 -0
- package/lib/ingestor-api/runtime/tests/__init__.py +0 -0
- package/lib/ingestor-api/runtime/tests/conftest.py +271 -0
- package/lib/ingestor-api/runtime/tests/test_collection.py +35 -0
- package/lib/ingestor-api/runtime/tests/test_collection_endpoint.py +41 -0
- package/lib/ingestor-api/runtime/tests/test_ingestor.py +60 -0
- package/lib/ingestor-api/runtime/tests/test_registration.py +198 -0
- package/lib/ingestor-api/runtime/tests/test_utils.py +35 -0
- package/lib/stac-api/index.d.ts +50 -0
- package/lib/stac-api/index.js +60 -0
- package/lib/stac-api/runtime/requirements.txt +8 -0
- package/lib/stac-api/runtime/src/__init__.py +0 -0
- package/lib/stac-api/runtime/src/app.py +58 -0
- package/lib/stac-api/runtime/src/config.py +96 -0
- package/lib/stac-api/runtime/src/handler.py +9 -0
- package/lib/titiler-pgstac-api/index.d.ts +33 -0
- package/lib/titiler-pgstac-api/index.js +67 -0
- package/lib/titiler-pgstac-api/runtime/Dockerfile +20 -0
- package/lib/titiler-pgstac-api/runtime/dev_requirements.txt +1 -0
- package/lib/titiler-pgstac-api/runtime/requirements.txt +3 -0
- package/lib/titiler-pgstac-api/runtime/src/__init__.py +3 -0
- package/lib/titiler-pgstac-api/runtime/src/handler.py +23 -0
- package/lib/titiler-pgstac-api/runtime/src/utils.py +26 -0
- package/package.json +81 -0
- package/tox.ini +52 -0
- package/tsconfig.tsbuildinfo +18116 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from unittest.mock import patch
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@pytest.fixture()
|
|
7
|
+
def dynamodb_stream_event():
|
|
8
|
+
return {"Records": None}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.fixture()
|
|
12
|
+
def get_queued_ingestions(example_ingestion):
|
|
13
|
+
with patch(
|
|
14
|
+
"src.ingestor.get_queued_ingestions",
|
|
15
|
+
return_value=iter([example_ingestion]),
|
|
16
|
+
autospec=True,
|
|
17
|
+
) as m:
|
|
18
|
+
yield m
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@pytest.fixture()
|
|
22
|
+
def get_db_credentials():
|
|
23
|
+
with patch("src.ingestor.get_db_credentials", return_value="", autospec=True) as m:
|
|
24
|
+
yield m
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@pytest.fixture()
|
|
28
|
+
def load_items():
|
|
29
|
+
with patch("src.ingestor.load_items", return_value=0, autospec=True) as m:
|
|
30
|
+
yield m
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@pytest.fixture()
|
|
34
|
+
def get_table(mock_table):
|
|
35
|
+
with patch("src.ingestor.get_table", return_value=mock_table, autospec=True) as m:
|
|
36
|
+
yield m
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_handler(
|
|
40
|
+
monkeypatch,
|
|
41
|
+
test_environ,
|
|
42
|
+
dynamodb_stream_event,
|
|
43
|
+
example_ingestion,
|
|
44
|
+
get_queued_ingestions,
|
|
45
|
+
get_db_credentials,
|
|
46
|
+
load_items,
|
|
47
|
+
get_table,
|
|
48
|
+
mock_table,
|
|
49
|
+
):
|
|
50
|
+
import src.ingestor as ingestor
|
|
51
|
+
|
|
52
|
+
ingestor.handler(dynamodb_stream_event, {})
|
|
53
|
+
load_items.assert_called_once_with(
|
|
54
|
+
creds="",
|
|
55
|
+
ingestions=list([example_ingestion]),
|
|
56
|
+
)
|
|
57
|
+
response = mock_table.get_item(
|
|
58
|
+
Key={"created_by": example_ingestion.created_by, "id": example_ingestion.id}
|
|
59
|
+
)
|
|
60
|
+
assert response["Item"]["status"] == "succeeded"
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import json
|
|
3
|
+
from datetime import timedelta
|
|
4
|
+
from typing import TYPE_CHECKING, List
|
|
5
|
+
from unittest.mock import call, patch
|
|
6
|
+
|
|
7
|
+
from fastapi.encoders import jsonable_encoder
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from fastapi.testclient import TestClient
|
|
13
|
+
from src import schemas, services
|
|
14
|
+
|
|
15
|
+
ingestion_endpoint = "/ingestions"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@pytest.fixture()
|
|
19
|
+
def collection_exists():
|
|
20
|
+
with patch("src.validators.collection_exists", return_value=True) as m:
|
|
21
|
+
yield m
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@pytest.fixture()
|
|
25
|
+
def collection_missing():
|
|
26
|
+
def bad_collection(collection_id: str):
|
|
27
|
+
raise ValueError("MOCKED MISSING COLLECTION ERROR")
|
|
28
|
+
|
|
29
|
+
with patch("src.validators.collection_exists", side_effect=bad_collection) as m:
|
|
30
|
+
yield m
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@pytest.fixture()
|
|
34
|
+
def asset_exists():
|
|
35
|
+
with patch("src.validators.url_is_accessible", return_value=True) as m:
|
|
36
|
+
yield m
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@pytest.fixture()
|
|
40
|
+
def asset_missing():
|
|
41
|
+
def bad_asset_url(href: str):
|
|
42
|
+
raise ValueError("MOCKED INACCESSIBLE URL ERROR")
|
|
43
|
+
|
|
44
|
+
with patch("src.validators.url_is_accessible", side_effect=bad_asset_url) as m:
|
|
45
|
+
yield m
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class TestCreate:
|
|
49
|
+
@pytest.fixture(autouse=True)
|
|
50
|
+
def setup(
|
|
51
|
+
self,
|
|
52
|
+
api_client: "TestClient",
|
|
53
|
+
mock_table: "services.Table",
|
|
54
|
+
example_ingestion: "schemas.Ingestion",
|
|
55
|
+
):
|
|
56
|
+
from src import services
|
|
57
|
+
|
|
58
|
+
self.api_client = api_client
|
|
59
|
+
self.mock_table = mock_table
|
|
60
|
+
self.db = services.Database(self.mock_table)
|
|
61
|
+
self.example_ingestion = example_ingestion
|
|
62
|
+
|
|
63
|
+
def test_unauthenticated_create(self):
|
|
64
|
+
response = self.api_client.post(
|
|
65
|
+
ingestion_endpoint,
|
|
66
|
+
json=jsonable_encoder(self.example_ingestion.item),
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
assert response.status_code == 403
|
|
70
|
+
|
|
71
|
+
def test_create(self, client_authenticated, collection_exists, asset_exists):
|
|
72
|
+
response = self.api_client.post(
|
|
73
|
+
ingestion_endpoint,
|
|
74
|
+
json=jsonable_encoder(self.example_ingestion.item),
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
assert response.status_code == 201
|
|
78
|
+
assert collection_exists.called_once_with(
|
|
79
|
+
self.example_ingestion.item.collection
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
stored_data = self.db.fetch_many(status="queued")["items"]
|
|
83
|
+
assert len(stored_data) == 1
|
|
84
|
+
assert json.loads(stored_data[0].json(by_alias=True)) == response.json()
|
|
85
|
+
|
|
86
|
+
def test_validates_missing_collection(
|
|
87
|
+
self, client_authenticated, collection_missing, asset_exists
|
|
88
|
+
):
|
|
89
|
+
response = self.api_client.post(
|
|
90
|
+
ingestion_endpoint,
|
|
91
|
+
json=jsonable_encoder(self.example_ingestion.item),
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
collection_missing.assert_called_once_with(
|
|
95
|
+
collection_id=self.example_ingestion.item.collection
|
|
96
|
+
)
|
|
97
|
+
assert response.status_code == 422, "should get validation error"
|
|
98
|
+
assert (
|
|
99
|
+
len(self.db.fetch_many(status="queued")["items"]) == 0
|
|
100
|
+
), "data should not be stored in DB"
|
|
101
|
+
|
|
102
|
+
def test_validates_missing_assets(
|
|
103
|
+
self, client_authenticated, collection_exists, asset_missing
|
|
104
|
+
):
|
|
105
|
+
response = self.api_client.post(
|
|
106
|
+
ingestion_endpoint,
|
|
107
|
+
json=jsonable_encoder(self.example_ingestion.item),
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
collection_exists.assert_called_once_with(
|
|
111
|
+
collection_id=self.example_ingestion.item.collection
|
|
112
|
+
)
|
|
113
|
+
asset_missing.assert_has_calls(
|
|
114
|
+
[
|
|
115
|
+
call(href=asset.href)
|
|
116
|
+
for asset in self.example_ingestion.item.assets.values()
|
|
117
|
+
],
|
|
118
|
+
any_order=True,
|
|
119
|
+
)
|
|
120
|
+
assert response.status_code == 422, "should get validation error"
|
|
121
|
+
for asset_type in self.example_ingestion.item.assets.keys():
|
|
122
|
+
assert any(
|
|
123
|
+
[
|
|
124
|
+
err["loc"] == ["body", "assets", asset_type, "href"]
|
|
125
|
+
for err in response.json()["detail"]
|
|
126
|
+
]
|
|
127
|
+
), "should reference asset type in validation error response"
|
|
128
|
+
assert (
|
|
129
|
+
len(self.db.fetch_many(status="queued")["items"]) == 0
|
|
130
|
+
), "data should not be stored in DB"
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class TestList:
|
|
134
|
+
@pytest.fixture(autouse=True)
|
|
135
|
+
def setup(
|
|
136
|
+
self,
|
|
137
|
+
api_client: "TestClient",
|
|
138
|
+
mock_table: "services.Table",
|
|
139
|
+
example_ingestion: "schemas.Ingestion",
|
|
140
|
+
):
|
|
141
|
+
self.api_client = api_client
|
|
142
|
+
self.mock_table = mock_table
|
|
143
|
+
self.example_ingestion = example_ingestion
|
|
144
|
+
|
|
145
|
+
def populate_table(self, count=100) -> List["schemas.Ingestion"]:
|
|
146
|
+
example_ingestions = []
|
|
147
|
+
for i in range(count):
|
|
148
|
+
ingestion = self.example_ingestion.copy()
|
|
149
|
+
ingestion.id = str(i)
|
|
150
|
+
ingestion.created_at = ingestion.created_at + timedelta(hours=i)
|
|
151
|
+
self.mock_table.put_item(Item=ingestion.dynamodb_dict())
|
|
152
|
+
example_ingestions.append(ingestion)
|
|
153
|
+
return example_ingestions
|
|
154
|
+
|
|
155
|
+
def test_simple_lookup(self):
|
|
156
|
+
self.mock_table.put_item(Item=self.example_ingestion.dynamodb_dict())
|
|
157
|
+
ingestion = jsonable_encoder(self.example_ingestion)
|
|
158
|
+
response = self.api_client.get(ingestion_endpoint)
|
|
159
|
+
assert response.status_code == 200
|
|
160
|
+
assert response.json() == {
|
|
161
|
+
"items": [ingestion],
|
|
162
|
+
"next": None,
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
def test_next_response(self):
|
|
166
|
+
example_ingestions = self.populate_table(100)
|
|
167
|
+
|
|
168
|
+
limit = 25
|
|
169
|
+
expected_next = json.loads(
|
|
170
|
+
example_ingestions[limit - 1].json(
|
|
171
|
+
include={"created_by", "id", "status", "created_at"}
|
|
172
|
+
)
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
response = self.api_client.get(ingestion_endpoint, params={"limit": limit})
|
|
176
|
+
assert response.status_code == 200
|
|
177
|
+
assert json.loads(base64.b64decode(response.json()["next"])) == expected_next
|
|
178
|
+
assert response.json()["items"] == jsonable_encoder(example_ingestions[:limit])
|
|
179
|
+
|
|
180
|
+
@pytest.mark.skip(reason="Test is currently broken")
|
|
181
|
+
def test_get_next_page(self):
|
|
182
|
+
example_ingestions = self.populate_table(100)
|
|
183
|
+
|
|
184
|
+
limit = 25
|
|
185
|
+
next_param = base64.b64encode(
|
|
186
|
+
example_ingestions[limit - 1]
|
|
187
|
+
.json(include={"created_by", "id", "status", "created_at"})
|
|
188
|
+
.encode()
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
response = self.api_client.get(
|
|
192
|
+
ingestion_endpoint, params={"limit": limit, "next": next_param}
|
|
193
|
+
)
|
|
194
|
+
assert response.status_code == 200
|
|
195
|
+
assert response.json()["items"] == [
|
|
196
|
+
json.loads(ingestion.json(by_alias=True))
|
|
197
|
+
for ingestion in example_ingestions[limit : limit * 2]
|
|
198
|
+
]
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from unittest.mock import Mock, patch
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from pypgstac.load import Methods
|
|
5
|
+
from fastapi.encoders import jsonable_encoder
|
|
6
|
+
from src.utils import DbCreds
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@pytest.fixture()
|
|
10
|
+
def loader():
|
|
11
|
+
with patch("src.utils.Loader", autospec=True) as m:
|
|
12
|
+
yield m
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@pytest.fixture()
|
|
16
|
+
def pgstacdb():
|
|
17
|
+
with patch("src.utils.PgstacDB", autospec=True) as m:
|
|
18
|
+
m.return_value.__enter__.return_value = Mock()
|
|
19
|
+
yield m
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@pytest.fixture()
|
|
23
|
+
def dbcreds():
|
|
24
|
+
dbcreds = DbCreds(username="", password="", host="", port=1, dbname="", engine="")
|
|
25
|
+
return dbcreds
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_load_items(loader, pgstacdb, example_ingestion, dbcreds):
|
|
29
|
+
import src.utils as utils
|
|
30
|
+
|
|
31
|
+
utils.load_items(dbcreds, list([example_ingestion]))
|
|
32
|
+
loader.return_value.load_items.assert_called_once_with(
|
|
33
|
+
file=jsonable_encoder([example_ingestion.item]),
|
|
34
|
+
insert_mode=Methods.upsert,
|
|
35
|
+
)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { aws_ec2 as ec2, aws_rds as rds, aws_secretsmanager as secretsmanager } from "aws-cdk-lib";
|
|
2
|
+
import { PythonFunction, PythonFunctionProps } from "@aws-cdk/aws-lambda-python-alpha";
|
|
3
|
+
import { Construct } from "constructs";
|
|
4
|
+
export declare class PgStacApiLambda extends Construct {
|
|
5
|
+
readonly url: string;
|
|
6
|
+
stacApiLambdaFunction: PythonFunction;
|
|
7
|
+
constructor(scope: Construct, id: string, props: PgStacApiLambdaProps);
|
|
8
|
+
}
|
|
9
|
+
export interface PgStacApiLambdaProps {
|
|
10
|
+
/**
|
|
11
|
+
* VPC into which the lambda should be deployed.
|
|
12
|
+
*/
|
|
13
|
+
readonly vpc: ec2.IVpc;
|
|
14
|
+
/**
|
|
15
|
+
* RDS Instance with installed pgSTAC.
|
|
16
|
+
*/
|
|
17
|
+
readonly db: rds.IDatabaseInstance;
|
|
18
|
+
/**
|
|
19
|
+
* Subnet into which the lambda should be deployed.
|
|
20
|
+
*/
|
|
21
|
+
readonly subnetSelection: ec2.SubnetSelection;
|
|
22
|
+
/**
|
|
23
|
+
* Secret containing connection information for pgSTAC database.
|
|
24
|
+
*/
|
|
25
|
+
readonly dbSecret: secretsmanager.ISecret;
|
|
26
|
+
/**
|
|
27
|
+
* Custom code to run for fastapi-pgstac.
|
|
28
|
+
*
|
|
29
|
+
* @default - simplified version of fastapi-pgstac
|
|
30
|
+
*/
|
|
31
|
+
readonly apiCode?: ApiEntrypoint;
|
|
32
|
+
/**
|
|
33
|
+
* Customized environment variables to send to fastapi-pgstac runtime.
|
|
34
|
+
*/
|
|
35
|
+
readonly apiEnv?: Record<string, string>;
|
|
36
|
+
}
|
|
37
|
+
export interface ApiEntrypoint {
|
|
38
|
+
/**
|
|
39
|
+
* Path to the source of the function or the location for dependencies.
|
|
40
|
+
*/
|
|
41
|
+
readonly entry: PythonFunctionProps["entry"];
|
|
42
|
+
/**
|
|
43
|
+
* The path (relative to entry) to the index file containing the exported handler.
|
|
44
|
+
*/
|
|
45
|
+
readonly index: PythonFunctionProps["index"];
|
|
46
|
+
/**
|
|
47
|
+
* The name of the exported handler in the index file.
|
|
48
|
+
*/
|
|
49
|
+
readonly handler: PythonFunctionProps["handler"];
|
|
50
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var _a;
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.PgStacApiLambda = void 0;
|
|
5
|
+
const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti");
|
|
6
|
+
const aws_cdk_lib_1 = require("aws-cdk-lib");
|
|
7
|
+
const aws_lambda_python_alpha_1 = require("@aws-cdk/aws-lambda-python-alpha");
|
|
8
|
+
const aws_apigatewayv2_alpha_1 = require("@aws-cdk/aws-apigatewayv2-alpha");
|
|
9
|
+
const aws_apigatewayv2_integrations_alpha_1 = require("@aws-cdk/aws-apigatewayv2-integrations-alpha");
|
|
10
|
+
const constructs_1 = require("constructs");
|
|
11
|
+
class PgStacApiLambda extends constructs_1.Construct {
|
|
12
|
+
constructor(scope, id, props) {
|
|
13
|
+
super(scope, id);
|
|
14
|
+
const apiCode = props.apiCode || {
|
|
15
|
+
entry: `${__dirname}/runtime`,
|
|
16
|
+
index: "src/handler.py",
|
|
17
|
+
handler: "handler",
|
|
18
|
+
};
|
|
19
|
+
this.stacApiLambdaFunction = new aws_lambda_python_alpha_1.PythonFunction(this, "stac-api", {
|
|
20
|
+
...apiCode,
|
|
21
|
+
/**
|
|
22
|
+
* NOTE: Unable to use Py3.9, due to issues with hashes:
|
|
23
|
+
*
|
|
24
|
+
* ERROR: Hashes are required in --require-hashes mode, but they are missing
|
|
25
|
+
* from some requirements. Here is a list of those requirements along with the
|
|
26
|
+
* hashes their downloaded archives actually had. Add lines like these to your
|
|
27
|
+
* requirements files to prevent tampering. (If you did not enable
|
|
28
|
+
* --require-hashes manually, note that it turns on automatically when any
|
|
29
|
+
* package has a hash.)
|
|
30
|
+
* anyio==3.6.1 --hash=sha256:cb29b9c70620506a9a8f87a309591713446953302d7d995344d0d7c6c0c9a7be
|
|
31
|
+
* */
|
|
32
|
+
runtime: aws_cdk_lib_1.aws_lambda.Runtime.PYTHON_3_8,
|
|
33
|
+
architecture: aws_cdk_lib_1.aws_lambda.Architecture.X86_64,
|
|
34
|
+
environment: {
|
|
35
|
+
PGSTAC_SECRET_ARN: props.dbSecret.secretArn,
|
|
36
|
+
DB_MIN_CONN_SIZE: "0",
|
|
37
|
+
DB_MAX_CONN_SIZE: "1",
|
|
38
|
+
...props.apiEnv,
|
|
39
|
+
},
|
|
40
|
+
vpc: props.vpc,
|
|
41
|
+
vpcSubnets: props.subnetSelection,
|
|
42
|
+
allowPublicSubnet: true,
|
|
43
|
+
memorySize: 8192,
|
|
44
|
+
});
|
|
45
|
+
props.dbSecret.grantRead(this.stacApiLambdaFunction);
|
|
46
|
+
this.stacApiLambdaFunction.connections.allowTo(props.db, aws_cdk_lib_1.aws_ec2.Port.tcp(5432));
|
|
47
|
+
const stacApi = new aws_apigatewayv2_alpha_1.HttpApi(this, `${aws_cdk_lib_1.Stack.of(this).stackName}-stac-api`, {
|
|
48
|
+
defaultIntegration: new aws_apigatewayv2_integrations_alpha_1.HttpLambdaIntegration("integration", this.stacApiLambdaFunction),
|
|
49
|
+
});
|
|
50
|
+
this.url = stacApi.url;
|
|
51
|
+
new aws_cdk_lib_1.CfnOutput(this, "stac-api-output", {
|
|
52
|
+
exportName: `${aws_cdk_lib_1.Stack.of(this).stackName}-url`,
|
|
53
|
+
value: this.url,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
exports.PgStacApiLambda = PgStacApiLambda;
|
|
58
|
+
_a = JSII_RTTI_SYMBOL_1;
|
|
59
|
+
PgStacApiLambda[_a] = { fqn: "eoapi-cdk.PgStacApiLambda", version: "5.0.0" };
|
|
60
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUFBLDZDQU9xQjtBQUNyQiw4RUFHMEM7QUFDMUMsNEVBQTBEO0FBQzFELHNHQUFxRjtBQUNyRiwyQ0FBdUM7QUFFdkMsTUFBYSxlQUFnQixTQUFRLHNCQUFTO0lBSTVDLFlBQVksS0FBZ0IsRUFBRSxFQUFVLEVBQUUsS0FBMkI7UUFDbkUsS0FBSyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsQ0FBQztRQUVqQixNQUFNLE9BQU8sR0FBRyxLQUFLLENBQUMsT0FBTyxJQUFJO1lBQy9CLEtBQUssRUFBRSxHQUFHLFNBQVMsVUFBVTtZQUM3QixLQUFLLEVBQUUsZ0JBQWdCO1lBQ3ZCLE9BQU8sRUFBRSxTQUFTO1NBQ25CLENBQUM7UUFFRixJQUFJLENBQUMscUJBQXFCLEdBQUcsSUFBSSx3Q0FBYyxDQUFDLElBQUksRUFBRSxVQUFVLEVBQUU7WUFDaEUsR0FBRyxPQUFPO1lBQ1Y7Ozs7Ozs7Ozs7aUJBVUs7WUFDTCxPQUFPLEVBQUUsd0JBQU0sQ0FBQyxPQUFPLENBQUMsVUFBVTtZQUNsQyxZQUFZLEVBQUUsd0JBQU0sQ0FBQyxZQUFZLENBQUMsTUFBTTtZQUN4QyxXQUFXLEVBQUU7Z0JBQ1gsaUJBQWlCLEVBQUUsS0FBSyxDQUFDLFFBQVEsQ0FBQyxTQUFTO2dCQUMzQyxnQkFBZ0IsRUFBRSxHQUFHO2dCQUNyQixnQkFBZ0IsRUFBRSxHQUFHO2dCQUNyQixHQUFHLEtBQUssQ0FBQyxNQUFNO2FBQ2hCO1lBQ0QsR0FBRyxFQUFFLEtBQUssQ0FBQyxHQUFHO1lBQ2QsVUFBVSxFQUFFLEtBQUssQ0FBQyxlQUFlO1lBQ2pDLGlCQUFpQixFQUFFLElBQUk7WUFDdkIsVUFBVSxFQUFFLElBQUk7U0FDakIsQ0FBQyxDQUFDO1FBRUgsS0FBSyxDQUFDLFFBQVEsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLHFCQUFxQixDQUFDLENBQUM7UUFDckQsSUFBSSxDQUFDLHFCQUFxQixDQUFDLFdBQVcsQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLEVBQUUsRUFBRSxxQkFBRyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztRQUU3RSxNQUFNLE9BQU8sR0FBRyxJQUFJLGdDQUFPLENBQUMsSUFBSSxFQUFFLEdBQUcsbUJBQUssQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLENBQUMsU0FBUyxXQUFXLEVBQUU7WUFDeEUsa0JBQWtCLEVBQUUsSUFBSSwyREFBcUIsQ0FBQyxhQUFhLEVBQUUsSUFBSSxDQUFDLHFCQUFxQixDQUFDO1NBQ3pGLENBQUMsQ0FBQztRQUVILElBQUksQ0FBQyxHQUFHLEdBQUcsT0FBTyxDQUFDLEdBQUksQ0FBQztRQUV4QixJQUFJLHVCQUFTLENBQUMsSUFBSSxFQUFFLGlCQUFpQixFQUFFO1lBQ3JDLFVBQVUsRUFBRSxHQUFHLG1CQUFLLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxDQUFDLFNBQVMsTUFBTTtZQUM3QyxLQUFLLEVBQUUsSUFBSSxDQUFDLEdBQUc7U0FDaEIsQ0FBQyxDQUFDO0lBQ0wsQ0FBQzs7QUFyREgsMENBc0RDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgU3RhY2ssXG4gIGF3c19lYzIgYXMgZWMyLFxuICBhd3NfcmRzIGFzIHJkcyxcbiAgYXdzX2xhbWJkYSBhcyBsYW1iZGEsXG4gIGF3c19zZWNyZXRzbWFuYWdlciBhcyBzZWNyZXRzbWFuYWdlcixcbiAgQ2ZuT3V0cHV0LFxufSBmcm9tIFwiYXdzLWNkay1saWJcIjtcbmltcG9ydCB7XG4gIFB5dGhvbkZ1bmN0aW9uLFxuICBQeXRob25GdW5jdGlvblByb3BzLFxufSBmcm9tIFwiQGF3cy1jZGsvYXdzLWxhbWJkYS1weXRob24tYWxwaGFcIjtcbmltcG9ydCB7IEh0dHBBcGkgfSBmcm9tIFwiQGF3cy1jZGsvYXdzLWFwaWdhdGV3YXl2Mi1hbHBoYVwiO1xuaW1wb3J0IHsgSHR0cExhbWJkYUludGVncmF0aW9uIH0gZnJvbSBcIkBhd3MtY2RrL2F3cy1hcGlnYXRld2F5djItaW50ZWdyYXRpb25zLWFscGhhXCI7XG5pbXBvcnQgeyBDb25zdHJ1Y3QgfSBmcm9tIFwiY29uc3RydWN0c1wiO1xuXG5leHBvcnQgY2xhc3MgUGdTdGFjQXBpTGFtYmRhIGV4dGVuZHMgQ29uc3RydWN0IHtcbiAgcmVhZG9ubHkgdXJsOiBzdHJpbmc7XG4gIHB1YmxpYyBzdGFjQXBpTGFtYmRhRnVuY3Rpb246IFB5dGhvbkZ1bmN0aW9uO1xuXG4gIGNvbnN0cnVjdG9yKHNjb3BlOiBDb25zdHJ1Y3QsIGlkOiBzdHJpbmcsIHByb3BzOiBQZ1N0YWNBcGlMYW1iZGFQcm9wcykge1xuICAgIHN1cGVyKHNjb3BlLCBpZCk7XG5cbiAgICBjb25zdCBhcGlDb2RlID0gcHJvcHMuYXBpQ29kZSB8fCB7XG4gICAgICBlbnRyeTogYCR7X19kaXJuYW1lfS9ydW50aW1lYCxcbiAgICAgIGluZGV4OiBcInNyYy9oYW5kbGVyLnB5XCIsXG4gICAgICBoYW5kbGVyOiBcImhhbmRsZXJcIixcbiAgICB9O1xuXG4gICAgdGhpcy5zdGFjQXBpTGFtYmRhRnVuY3Rpb24gPSBuZXcgUHl0aG9uRnVuY3Rpb24odGhpcywgXCJzdGFjLWFwaVwiLCB7XG4gICAgICAuLi5hcGlDb2RlLFxuICAgICAgLyoqXG4gICAgICAgKiBOT1RFOiBVbmFibGUgdG8gdXNlIFB5My45LCBkdWUgdG8gaXNzdWVzIHdpdGggaGFzaGVzOlxuICAgICAgICpcbiAgICAgICAqICAgIEVSUk9SOiBIYXNoZXMgYXJlIHJlcXVpcmVkIGluIC0tcmVxdWlyZS1oYXNoZXMgbW9kZSwgYnV0IHRoZXkgYXJlIG1pc3NpbmdcbiAgICAgICAqICAgIGZyb20gc29tZSByZXF1aXJlbWVudHMuIEhlcmUgaXMgYSBsaXN0IG9mIHRob3NlIHJlcXVpcmVtZW50cyBhbG9uZyB3aXRoIHRoZVxuICAgICAgICogICAgaGFzaGVzIHRoZWlyIGRvd25sb2FkZWQgYXJjaGl2ZXMgYWN0dWFsbHkgaGFkLiBBZGQgbGluZXMgbGlrZSB0aGVzZSB0byB5b3VyXG4gICAgICAgKiAgICByZXF1aXJlbWVudHMgZmlsZXMgdG8gcHJldmVudCB0YW1wZXJpbmcuIChJZiB5b3UgZGlkIG5vdCBlbmFibGVcbiAgICAgICAqICAgIC0tcmVxdWlyZS1oYXNoZXMgbWFudWFsbHksIG5vdGUgdGhhdCBpdCB0dXJucyBvbiBhdXRvbWF0aWNhbGx5IHdoZW4gYW55XG4gICAgICAgKiAgICBwYWNrYWdlIGhhcyBhIGhhc2guKVxuICAgICAgICogICAgICAgIGFueWlvPT0zLjYuMSAtLWhhc2g9c2hhMjU2OmNiMjliOWM3MDYyMDUwNmE5YThmODdhMzA5NTkxNzEzNDQ2OTUzMzAyZDdkOTk1MzQ0ZDBkN2M2YzBjOWE3YmVcbiAgICAgICAqICovXG4gICAgICBydW50aW1lOiBsYW1iZGEuUnVudGltZS5QWVRIT05fM184LFxuICAgICAgYXJjaGl0ZWN0dXJlOiBsYW1iZGEuQXJjaGl0ZWN0dXJlLlg4Nl82NCxcbiAgICAgIGVudmlyb25tZW50OiB7XG4gICAgICAgIFBHU1RBQ19TRUNSRVRfQVJOOiBwcm9wcy5kYlNlY3JldC5zZWNyZXRBcm4sXG4gICAgICAgIERCX01JTl9DT05OX1NJWkU6IFwiMFwiLFxuICAgICAgICBEQl9NQVhfQ09OTl9TSVpFOiBcIjFcIixcbiAgICAgICAgLi4ucHJvcHMuYXBpRW52LFxuICAgICAgfSxcbiAgICAgIHZwYzogcHJvcHMudnBjLFxuICAgICAgdnBjU3VibmV0czogcHJvcHMuc3VibmV0U2VsZWN0aW9uLFxuICAgICAgYWxsb3dQdWJsaWNTdWJuZXQ6IHRydWUsXG4gICAgICBtZW1vcnlTaXplOiA4MTkyLFxuICAgIH0pO1xuXG4gICAgcHJvcHMuZGJTZWNyZXQuZ3JhbnRSZWFkKHRoaXMuc3RhY0FwaUxhbWJkYUZ1bmN0aW9uKTtcbiAgICB0aGlzLnN0YWNBcGlMYW1iZGFGdW5jdGlvbi5jb25uZWN0aW9ucy5hbGxvd1RvKHByb3BzLmRiLCBlYzIuUG9ydC50Y3AoNTQzMikpO1xuXG4gICAgY29uc3Qgc3RhY0FwaSA9IG5ldyBIdHRwQXBpKHRoaXMsIGAke1N0YWNrLm9mKHRoaXMpLnN0YWNrTmFtZX0tc3RhYy1hcGlgLCB7XG4gICAgICBkZWZhdWx0SW50ZWdyYXRpb246IG5ldyBIdHRwTGFtYmRhSW50ZWdyYXRpb24oXCJpbnRlZ3JhdGlvblwiLCB0aGlzLnN0YWNBcGlMYW1iZGFGdW5jdGlvbiksXG4gICAgfSk7XG5cbiAgICB0aGlzLnVybCA9IHN0YWNBcGkudXJsITtcblxuICAgIG5ldyBDZm5PdXRwdXQodGhpcywgXCJzdGFjLWFwaS1vdXRwdXRcIiwge1xuICAgICAgZXhwb3J0TmFtZTogYCR7U3RhY2sub2YodGhpcykuc3RhY2tOYW1lfS11cmxgLFxuICAgICAgdmFsdWU6IHRoaXMudXJsLFxuICAgIH0pO1xuICB9XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUGdTdGFjQXBpTGFtYmRhUHJvcHMge1xuICAvKipcbiAgICogVlBDIGludG8gd2hpY2ggdGhlIGxhbWJkYSBzaG91bGQgYmUgZGVwbG95ZWQuXG4gICAqL1xuICByZWFkb25seSB2cGM6IGVjMi5JVnBjO1xuXG4gIC8qKlxuICAgKiBSRFMgSW5zdGFuY2Ugd2l0aCBpbnN0YWxsZWQgcGdTVEFDLlxuICAgKi9cbiAgcmVhZG9ubHkgZGI6IHJkcy5JRGF0YWJhc2VJbnN0YW5jZTtcblxuICAvKipcbiAgICogU3VibmV0IGludG8gd2hpY2ggdGhlIGxhbWJkYSBzaG91bGQgYmUgZGVwbG95ZWQuXG4gICAqL1xuICByZWFkb25seSBzdWJuZXRTZWxlY3Rpb246IGVjMi5TdWJuZXRTZWxlY3Rpb247XG5cbiAgLyoqXG4gICAqIFNlY3JldCBjb250YWluaW5nIGNvbm5lY3Rpb24gaW5mb3JtYXRpb24gZm9yIHBnU1RBQyBkYXRhYmFzZS5cbiAgICovXG4gIHJlYWRvbmx5IGRiU2VjcmV0OiBzZWNyZXRzbWFuYWdlci5JU2VjcmV0O1xuXG4gIC8qKlxuICAgKiBDdXN0b20gY29kZSB0byBydW4gZm9yIGZhc3RhcGktcGdzdGFjLlxuICAgKlxuICAgKiBAZGVmYXVsdCAtIHNpbXBsaWZpZWQgdmVyc2lvbiBvZiBmYXN0YXBpLXBnc3RhY1xuICAgKi9cbiAgcmVhZG9ubHkgYXBpQ29kZT86IEFwaUVudHJ5cG9pbnQ7XG5cbiAgLyoqXG4gICAqIEN1c3RvbWl6ZWQgZW52aXJvbm1lbnQgdmFyaWFibGVzIHRvIHNlbmQgdG8gZmFzdGFwaS1wZ3N0YWMgcnVudGltZS5cbiAgICovXG4gIHJlYWRvbmx5IGFwaUVudj86IFJlY29yZDxzdHJpbmcsIHN0cmluZz47XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgQXBpRW50cnlwb2ludCB7XG4gIC8qKlxuICAgKiBQYXRoIHRvIHRoZSBzb3VyY2Ugb2YgdGhlIGZ1bmN0aW9uIG9yIHRoZSBsb2NhdGlvbiBmb3IgZGVwZW5kZW5jaWVzLlxuICAgKi9cbiAgcmVhZG9ubHkgZW50cnk6IFB5dGhvbkZ1bmN0aW9uUHJvcHNbXCJlbnRyeVwiXTtcbiAgLyoqXG4gICAqIFRoZSBwYXRoIChyZWxhdGl2ZSB0byBlbnRyeSkgdG8gdGhlIGluZGV4IGZpbGUgY29udGFpbmluZyB0aGUgZXhwb3J0ZWQgaGFuZGxlci5cbiAgICovXG4gIHJlYWRvbmx5IGluZGV4OiBQeXRob25GdW5jdGlvblByb3BzW1wiaW5kZXhcIl07XG4gIC8qKlxuICAgKiBUaGUgbmFtZSBvZiB0aGUgZXhwb3J0ZWQgaGFuZGxlciBpbiB0aGUgaW5kZXggZmlsZS5cbiAgICovXG4gIHJlYWRvbmx5IGhhbmRsZXI6IFB5dGhvbkZ1bmN0aW9uUHJvcHNbXCJoYW5kbGVyXCJdO1xufVxuIl19
|
|
File without changes
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""
|
|
2
|
+
FastAPI application using PGStac.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
6
|
+
from fastapi.responses import ORJSONResponse
|
|
7
|
+
from stac_fastapi.api.app import StacApi
|
|
8
|
+
from stac_fastapi.pgstac.core import CoreCrudClient
|
|
9
|
+
from stac_fastapi.pgstac.db import close_db_connection, connect_to_db
|
|
10
|
+
from starlette_cramjam.middleware import CompressionMiddleware
|
|
11
|
+
|
|
12
|
+
from .config import ApiSettings
|
|
13
|
+
from .config import extensions as PgStacExtensions
|
|
14
|
+
from .config import get_request_model as GETModel
|
|
15
|
+
from .config import post_request_model as POSTModel
|
|
16
|
+
|
|
17
|
+
api_settings = ApiSettings()
|
|
18
|
+
|
|
19
|
+
api = StacApi(
|
|
20
|
+
title=api_settings.name,
|
|
21
|
+
api_version=api_settings.version,
|
|
22
|
+
description=api_settings.description or api_settings.name,
|
|
23
|
+
settings=api_settings.load_postgres_settings(),
|
|
24
|
+
extensions=PgStacExtensions,
|
|
25
|
+
client=CoreCrudClient(post_request_model=POSTModel),
|
|
26
|
+
search_get_request_model=GETModel,
|
|
27
|
+
search_post_request_model=POSTModel,
|
|
28
|
+
response_class=ORJSONResponse,
|
|
29
|
+
middlewares=[CompressionMiddleware],
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
app = api.app
|
|
33
|
+
|
|
34
|
+
# Set all CORS enabled origins
|
|
35
|
+
if api_settings.cors_origins:
|
|
36
|
+
app.add_middleware(
|
|
37
|
+
CORSMiddleware,
|
|
38
|
+
allow_origins=api_settings.cors_origins,
|
|
39
|
+
allow_credentials=True,
|
|
40
|
+
allow_methods=["GET", "POST", "OPTIONS"],
|
|
41
|
+
allow_headers=["*"],
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@app.on_event("startup")
|
|
46
|
+
async def startup_event():
|
|
47
|
+
"""Connect to database on startup."""
|
|
48
|
+
print("Setting up DB connection...")
|
|
49
|
+
await connect_to_db(app)
|
|
50
|
+
print("DB connection setup.")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@app.on_event("shutdown")
|
|
54
|
+
async def shutdown_event():
|
|
55
|
+
"""Close database connection."""
|
|
56
|
+
print("Closing up DB connection...")
|
|
57
|
+
await close_db_connection(app)
|
|
58
|
+
print("DB connection closed.")
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""API settings.
|
|
2
|
+
Based on https://github.com/developmentseed/eoAPI/tree/master/src/eoapi/stac"""
|
|
3
|
+
import base64
|
|
4
|
+
import json
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
import boto3
|
|
8
|
+
import pydantic
|
|
9
|
+
from stac_fastapi.api.models import create_get_request_model, create_post_request_model
|
|
10
|
+
|
|
11
|
+
# from stac_fastapi.pgstac.extensions import QueryExtension
|
|
12
|
+
from stac_fastapi.extensions.core import (
|
|
13
|
+
ContextExtension,
|
|
14
|
+
FieldsExtension,
|
|
15
|
+
FilterExtension,
|
|
16
|
+
QueryExtension,
|
|
17
|
+
SortExtension,
|
|
18
|
+
TokenPaginationExtension,
|
|
19
|
+
)
|
|
20
|
+
from stac_fastapi.pgstac.config import Settings
|
|
21
|
+
from stac_fastapi.pgstac.types.search import PgstacSearch
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def get_secret_dict(secret_name: str):
|
|
25
|
+
"""Retrieve secrets from AWS Secrets Manager
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
secret_name (str): name of aws secrets manager secret containing database connection secrets
|
|
29
|
+
profile_name (str, optional): optional name of aws profile for use in debugger only
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
secrets (dict): decrypted secrets in dict
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
# Create a Secrets Manager client
|
|
36
|
+
session = boto3.session.Session()
|
|
37
|
+
client = session.client(service_name="secretsmanager")
|
|
38
|
+
|
|
39
|
+
get_secret_value_response = client.get_secret_value(SecretId=secret_name)
|
|
40
|
+
|
|
41
|
+
if "SecretString" in get_secret_value_response:
|
|
42
|
+
return json.loads(get_secret_value_response["SecretString"])
|
|
43
|
+
else:
|
|
44
|
+
return json.loads(base64.b64decode(get_secret_value_response["SecretBinary"]))
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class ApiSettings(pydantic.BaseSettings):
|
|
48
|
+
"""API settings"""
|
|
49
|
+
|
|
50
|
+
name: str = "asdi-stac-api"
|
|
51
|
+
version: str = "0.1"
|
|
52
|
+
description: Optional[str] = None
|
|
53
|
+
cors_origins: str = "*"
|
|
54
|
+
cachecontrol: str = "public, max-age=3600"
|
|
55
|
+
debug: bool = False
|
|
56
|
+
|
|
57
|
+
pgstac_secret_arn: Optional[str]
|
|
58
|
+
|
|
59
|
+
@pydantic.validator("cors_origins")
|
|
60
|
+
def parse_cors_origin(cls, v):
|
|
61
|
+
"""Parse CORS origins."""
|
|
62
|
+
return [origin.strip() for origin in v.split(",")]
|
|
63
|
+
|
|
64
|
+
def load_postgres_settings(self) -> "Settings":
|
|
65
|
+
"""Load postgres connection params from AWS secret"""
|
|
66
|
+
|
|
67
|
+
if self.pgstac_secret_arn:
|
|
68
|
+
secret = get_secret_dict(self.pgstac_secret_arn)
|
|
69
|
+
|
|
70
|
+
return Settings(
|
|
71
|
+
postgres_host_reader=secret["host"],
|
|
72
|
+
postgres_host_writer=secret["host"],
|
|
73
|
+
postgres_dbname=secret["dbname"],
|
|
74
|
+
postgres_user=secret["username"],
|
|
75
|
+
postgres_pass=secret["password"],
|
|
76
|
+
postgres_port=secret["port"],
|
|
77
|
+
)
|
|
78
|
+
else:
|
|
79
|
+
return Settings()
|
|
80
|
+
|
|
81
|
+
class Config:
|
|
82
|
+
"""model config"""
|
|
83
|
+
|
|
84
|
+
env_file = ".env"
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
extensions = [
|
|
88
|
+
FilterExtension(),
|
|
89
|
+
QueryExtension(),
|
|
90
|
+
SortExtension(),
|
|
91
|
+
FieldsExtension(),
|
|
92
|
+
TokenPaginationExtension(),
|
|
93
|
+
ContextExtension(),
|
|
94
|
+
]
|
|
95
|
+
post_request_model = create_post_request_model(extensions, base_model=PgstacSearch)
|
|
96
|
+
get_request_model = create_get_request_model(extensions)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { aws_ec2 as ec2, aws_rds as rds, aws_lambda as lambda, aws_secretsmanager as secretsmanager } from "aws-cdk-lib";
|
|
2
|
+
import { Construct } from "constructs";
|
|
3
|
+
export declare class TitilerPgstacApiLambda extends Construct {
|
|
4
|
+
readonly url: string;
|
|
5
|
+
titilerPgstacLambdaFunction: lambda.Function;
|
|
6
|
+
constructor(scope: Construct, id: string, props: TitilerPgStacApiLambdaProps);
|
|
7
|
+
}
|
|
8
|
+
export interface TitilerPgStacApiLambdaProps {
|
|
9
|
+
/**
|
|
10
|
+
* VPC into which the lambda should be deployed.
|
|
11
|
+
*/
|
|
12
|
+
readonly vpc: ec2.IVpc;
|
|
13
|
+
/**
|
|
14
|
+
* RDS Instance with installed pgSTAC.
|
|
15
|
+
*/
|
|
16
|
+
readonly db: rds.IDatabaseInstance;
|
|
17
|
+
/**
|
|
18
|
+
* Subnet into which the lambda should be deployed.
|
|
19
|
+
*/
|
|
20
|
+
readonly subnetSelection: ec2.SubnetSelection;
|
|
21
|
+
/**
|
|
22
|
+
* Secret containing connection information for pgSTAC database.
|
|
23
|
+
*/
|
|
24
|
+
readonly dbSecret: secretsmanager.ISecret;
|
|
25
|
+
/**
|
|
26
|
+
* Customized environment variables to send to titiler-pgstac runtime.
|
|
27
|
+
*/
|
|
28
|
+
readonly apiEnv?: Record<string, string>;
|
|
29
|
+
/**
|
|
30
|
+
* list of buckets the lambda will be granted access to.
|
|
31
|
+
*/
|
|
32
|
+
readonly buckets?: string[];
|
|
33
|
+
}
|