eoapi-cdk 8.1.1 → 8.2.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.
Files changed (62) hide show
  1. package/.jsii +594 -26
  2. package/lib/bastion-host/index.js +1 -1
  3. package/lib/database/index.d.ts +1 -0
  4. package/lib/database/index.js +5 -5
  5. package/lib/index.d.ts +2 -0
  6. package/lib/index.js +3 -1
  7. package/lib/ingestor-api/index.js +1 -1
  8. package/lib/stac-api/index.js +1 -1
  9. package/lib/stac-browser/index.js +1 -1
  10. package/lib/stac-item-loader/index.d.ts +322 -0
  11. package/lib/stac-item-loader/index.js +251 -0
  12. package/lib/stac-item-loader/runtime/Dockerfile +18 -0
  13. package/lib/stac-item-loader/runtime/pyproject.toml +17 -0
  14. package/lib/stac-item-loader/runtime/src/stac_item_loader/handler.py +241 -0
  15. package/lib/stactools-item-generator/index.d.ts +243 -0
  16. package/lib/stactools-item-generator/index.js +204 -0
  17. package/lib/stactools-item-generator/runtime/Dockerfile +20 -0
  18. package/lib/stactools-item-generator/runtime/pyproject.toml +16 -0
  19. package/lib/stactools-item-generator/runtime/src/stactools_item_generator/__init__.py +2 -0
  20. package/lib/stactools-item-generator/runtime/src/stactools_item_generator/handler.py +176 -0
  21. package/lib/stactools-item-generator/runtime/src/stactools_item_generator/item.py +77 -0
  22. package/lib/tipg-api/index.js +1 -1
  23. package/lib/titiler-pgstac-api/index.js +1 -1
  24. package/package.json +1 -1
  25. package/pyproject.toml +45 -0
  26. package/uv.lock +1065 -0
  27. package/.devcontainer/devcontainer.json +0 -4
  28. package/.github/pull_request_template.md +0 -4
  29. package/.github/workflows/build.yaml +0 -73
  30. package/.github/workflows/build_and_release.yaml +0 -13
  31. package/.github/workflows/conventional-pr.yaml +0 -26
  32. package/.github/workflows/deploy.yaml +0 -84
  33. package/.github/workflows/distribute.yaml +0 -46
  34. package/.github/workflows/docs.yaml +0 -26
  35. package/.github/workflows/lint.yaml +0 -26
  36. package/.github/workflows/tox.yaml +0 -26
  37. package/.nvmrc +0 -1
  38. package/.pre-commit-config.yaml +0 -23
  39. package/CHANGELOG.md +0 -471
  40. package/diagrams/bastion_diagram.excalidraw +0 -1416
  41. package/diagrams/bastion_diagram.png +0 -0
  42. package/diagrams/ingestor_diagram.excalidraw +0 -2274
  43. package/diagrams/ingestor_diagram.png +0 -0
  44. package/integration_tests/cdk/README.md +0 -55
  45. package/integration_tests/cdk/app.py +0 -186
  46. package/integration_tests/cdk/cdk.json +0 -32
  47. package/integration_tests/cdk/config.py +0 -52
  48. package/integration_tests/cdk/package-lock.json +0 -42
  49. package/integration_tests/cdk/package.json +0 -7
  50. package/integration_tests/cdk/requirements.txt +0 -7
  51. package/lib/database/lambda/package-lock.json +0 -1324
  52. package/lib/ingestor-api/runtime/tests/conftest.py +0 -270
  53. package/lib/ingestor-api/runtime/tests/test_collection.py +0 -87
  54. package/lib/ingestor-api/runtime/tests/test_collection_endpoint.py +0 -41
  55. package/lib/ingestor-api/runtime/tests/test_ingestor.py +0 -60
  56. package/lib/ingestor-api/runtime/tests/test_registration.py +0 -207
  57. package/lib/ingestor-api/runtime/tests/test_utils.py +0 -35
  58. package/lib/ingestor-api/runtime/tests/test_validators.py +0 -164
  59. package/ruff.toml +0 -23
  60. package/tox.ini +0 -16
  61. package/tsconfig.tsbuildinfo +0 -1
  62. /package/lib/{ingestor-api/runtime/tests → stac-item-loader/runtime/src/stac_item_loader}/__init__.py +0 -0
@@ -1,270 +0,0 @@
1
- import boto3
2
- import pytest
3
- from fastapi.testclient import TestClient
4
- from moto import mock_dynamodb, mock_ssm
5
- from stac_pydantic import Item
6
-
7
-
8
- @pytest.fixture
9
- def test_environ(monkeypatch):
10
- # Mocked AWS Credentials for moto
11
- monkeypatch.setenv("AWS_ACCESS_KEY_ID", "testing")
12
- monkeypatch.setenv("AWS_SECRET_ACCESS_KEY", "testing")
13
- monkeypatch.setenv("AWS_SECURITY_TOKEN", "testing")
14
- monkeypatch.setenv("AWS_SESSION_TOKEN", "testing")
15
-
16
- # Config mocks
17
- monkeypatch.setenv("DYNAMODB_TABLE", "test_table")
18
- monkeypatch.setenv("JWKS_URL", "https://test-jwks.url")
19
- monkeypatch.setenv("STAC_URL", "https://test-stac.url")
20
- monkeypatch.setenv("DATA_ACCESS_ROLE", "arn:aws:iam::123456789012:role/test-role")
21
- monkeypatch.setenv("DB_SECRET_ARN", "testing")
22
- monkeypatch.setenv("ROOT_PATH", "testing")
23
-
24
-
25
- @pytest.fixture
26
- def mock_ssm_parameter_store():
27
- with mock_ssm():
28
- yield boto3.client("ssm")
29
-
30
-
31
- @pytest.fixture
32
- def app(test_environ, mock_ssm_parameter_store):
33
- from src.main import app
34
-
35
- return app
36
-
37
-
38
- @pytest.fixture
39
- def api_client(app):
40
- return TestClient(app)
41
-
42
-
43
- @pytest.fixture
44
- def mock_table(app, test_environ):
45
- from src import dependencies
46
- from src.config import settings
47
-
48
- with mock_dynamodb():
49
- client = boto3.resource("dynamodb")
50
- mock_table = client.create_table(
51
- TableName=settings.dynamodb_table,
52
- AttributeDefinitions=[
53
- {"AttributeName": "created_by", "AttributeType": "S"},
54
- {"AttributeName": "id", "AttributeType": "S"},
55
- {"AttributeName": "status", "AttributeType": "S"},
56
- {"AttributeName": "created_at", "AttributeType": "S"},
57
- ],
58
- KeySchema=[
59
- {"AttributeName": "created_by", "KeyType": "HASH"},
60
- {"AttributeName": "id", "KeyType": "RANGE"},
61
- ],
62
- BillingMode="PAY_PER_REQUEST",
63
- GlobalSecondaryIndexes=[
64
- {
65
- "IndexName": "status",
66
- "KeySchema": [
67
- {"AttributeName": "status", "KeyType": "HASH"},
68
- {"AttributeName": "created_at", "KeyType": "RANGE"},
69
- ],
70
- "Projection": {"ProjectionType": "ALL"},
71
- }
72
- ],
73
- )
74
- app.dependency_overrides[dependencies.get_table] = lambda: mock_table
75
- yield mock_table
76
- app.dependency_overrides.pop(dependencies.get_table)
77
-
78
-
79
- @pytest.fixture
80
- def example_stac_item():
81
- return {
82
- "stac_version": "1.0.0",
83
- "stac_extensions": [],
84
- "type": "Feature",
85
- "id": "20201211_223832_CS2",
86
- "bbox": [
87
- 172.91173669923782,
88
- 1.3438851951615003,
89
- 172.95469614953714,
90
- 1.3690476620161975,
91
- ],
92
- "geometry": {
93
- "type": "Polygon",
94
- "coordinates": [
95
- [
96
- [172.91173669923782, 1.3438851951615003],
97
- [172.95469614953714, 1.3438851951615003],
98
- [172.95469614953714, 1.3690476620161975],
99
- [172.91173669923782, 1.3690476620161975],
100
- [172.91173669923782, 1.3438851951615003],
101
- ]
102
- ],
103
- },
104
- "properties": {
105
- "datetime": "2020-12-11T22:38:32.125000Z",
106
- "eo:cloud_cover": 1,
107
- },
108
- "collection": "simple-collection",
109
- "links": [
110
- {
111
- "rel": "collection",
112
- "href": "./collection.json",
113
- "type": "application/json",
114
- "title": "Simple Example Collection",
115
- },
116
- {
117
- "rel": "root",
118
- "href": "./collection.json",
119
- "type": "application/json",
120
- "title": "Simple Example Collection",
121
- },
122
- {
123
- "rel": "parent",
124
- "href": "./collection.json",
125
- "type": "application/json",
126
- "title": "Simple Example Collection",
127
- },
128
- ],
129
- "assets": {
130
- "visual": {
131
- "href": "https://TEST_API.com/open-cogs/stac-examples/20201211_223832_CS2.tif", # noqa
132
- "type": "image/tiff; application=geotiff; profile=cloud-optimized",
133
- "title": "3-Band Visual",
134
- "roles": ["visual"],
135
- },
136
- "thumbnail": {
137
- "href": "https://TEST_API.com/open-cogs/stac-examples/20201211_223832_CS2.jpg", # noqa
138
- "title": "Thumbnail",
139
- "type": "image/jpeg",
140
- "roles": ["thumbnail"],
141
- },
142
- },
143
- }
144
-
145
-
146
- @pytest.fixture
147
- def example_stac_collection():
148
- return {
149
- "id": "simple-collection",
150
- "type": "Collection",
151
- "stac_extensions": [
152
- "https://stac-extensions.github.io/eo/v1.0.0/schema.json",
153
- "https://stac-extensions.github.io/projection/v1.0.0/schema.json",
154
- "https://stac-extensions.github.io/view/v1.0.0/schema.json",
155
- ],
156
- "item_assets": {
157
- "data": {
158
- "type": "image/tiff; application=geotiff; profile=cloud-optimized",
159
- "roles": ["data"],
160
- }
161
- },
162
- "stac_version": "1.0.0",
163
- "description": "A simple collection demonstrating core catalog fields with links to a couple of items",
164
- "title": "Simple Example Collection",
165
- "providers": [
166
- {
167
- "name": "Remote Data, Inc",
168
- "description": "Producers of awesome spatiotemporal assets",
169
- "roles": ["producer", "processor"],
170
- "url": "http://remotedata.io",
171
- }
172
- ],
173
- "extent": {
174
- "spatial": {
175
- "bbox": [
176
- [
177
- 172.91173669923782,
178
- 1.3438851951615003,
179
- 172.95469614953714,
180
- 1.3690476620161975,
181
- ]
182
- ]
183
- },
184
- "temporal": {
185
- "interval": [["2020-12-11T22:38:32.125Z", "2020-12-14T18:02:31.437Z"]]
186
- },
187
- },
188
- "license": "CC-BY-4.0",
189
- "summaries": {
190
- "platform": ["cool_sat1", "cool_sat2"],
191
- "constellation": ["ion"],
192
- "instruments": ["cool_sensor_v1", "cool_sensor_v2"],
193
- "gsd": {"minimum": 0.512, "maximum": 0.66},
194
- "eo:cloud_cover": {"minimum": 1.2, "maximum": 1.2},
195
- "proj:epsg": {"minimum": 32659, "maximum": 32659},
196
- "view:sun_elevation": {"minimum": 54.9, "maximum": 54.9},
197
- "view:off_nadir": {"minimum": 3.8, "maximum": 3.8},
198
- "view:sun_azimuth": {"minimum": 135.7, "maximum": 135.7},
199
- },
200
- "links": [
201
- {
202
- "rel": "root",
203
- "href": "./collection.json",
204
- "type": "application/json",
205
- "title": "Simple Example Collection",
206
- },
207
- {
208
- "rel": "item",
209
- "href": "./simple-item.json",
210
- "type": "application/geo+json",
211
- "title": "Simple Item",
212
- },
213
- {
214
- "rel": "item",
215
- "href": "./core-item.json",
216
- "type": "application/geo+json",
217
- "title": "Core Item",
218
- },
219
- {
220
- "rel": "item",
221
- "href": "./extended-item.json",
222
- "type": "application/geo+json",
223
- "title": "Extended Item",
224
- },
225
- {
226
- "rel": "self",
227
- "href": "https://raw.githubusercontent.com/radiantearth/stac-spec/v1.0.0/examples/collection.json",
228
- "type": "application/json",
229
- },
230
- ],
231
- }
232
-
233
-
234
- @pytest.fixture
235
- def client(app):
236
- """
237
- Return an API Client
238
- """
239
- app.dependency_overrides = {}
240
- return TestClient(app)
241
-
242
-
243
- @pytest.fixture
244
- def client_authenticated(app):
245
- """
246
- Returns an API client which skips the authentication
247
- """
248
- from src.dependencies import get_username
249
-
250
- app.dependency_overrides[get_username] = lambda: "test_user"
251
- return TestClient(app)
252
-
253
-
254
- @pytest.fixture
255
- def stac_collection(example_stac_collection):
256
- from src import schemas
257
-
258
- return schemas.StacCollection(**example_stac_collection)
259
-
260
-
261
- @pytest.fixture(scope="function")
262
- def example_ingestion(example_stac_item):
263
- from src import schemas
264
-
265
- return schemas.Ingestion(
266
- id=example_stac_item["id"],
267
- created_by="test-user",
268
- status=schemas.Status.queued,
269
- item=Item.model_validate(example_stac_item),
270
- )
@@ -1,87 +0,0 @@
1
- import os
2
- from unittest.mock import Mock, patch
3
-
4
- import pytest
5
- import src.collection as collection
6
- from fastapi import HTTPException
7
- from pypgstac.load import Methods
8
- from src.utils import DbCreds
9
-
10
-
11
- @pytest.fixture()
12
- def loader():
13
- with patch("src.collection.Loader", autospec=True) as m:
14
- yield m
15
-
16
-
17
- @pytest.fixture()
18
- def pgstacdb():
19
- with patch("src.collection.PgstacDB", autospec=True) as m:
20
- m.return_value.__enter__.return_value = Mock()
21
- yield m
22
-
23
-
24
- def test_load_collections(stac_collection, loader, pgstacdb):
25
- with patch(
26
- "src.collection.get_db_credentials",
27
- return_value=DbCreds(
28
- username="", password="", host="", port=1, dbname="", engine=""
29
- ),
30
- ):
31
- os.environ["DB_SECRET_ARN"] = ""
32
- collection.ingest(stac_collection)
33
-
34
- loader.return_value.load_collections.assert_called_once_with(
35
- file=[stac_collection.model_dump(mode="json")],
36
- insert_mode=Methods.upsert,
37
- )
38
-
39
-
40
- def test_ingest_loader_error(stac_collection, pgstacdb):
41
- """Test handling of errors during the loading process."""
42
- with patch(
43
- "src.collection.get_db_credentials",
44
- return_value=DbCreds(
45
- username="", password="", host="", port=1, dbname="", engine=""
46
- ),
47
- ):
48
- os.environ["DB_SECRET_ARN"] = ""
49
-
50
- with patch("src.collection.Loader") as mock_loader:
51
- mock_loader_instance = Mock()
52
- mock_loader.return_value = mock_loader_instance
53
- mock_loader_instance.load_collections.side_effect = Exception(
54
- "Invalid collection data"
55
- )
56
-
57
- with pytest.raises(HTTPException) as excinfo:
58
- collection.ingest(stac_collection)
59
-
60
- assert excinfo.value.status_code == 500
61
- assert "Invalid collection data" in str(excinfo.value.detail)
62
-
63
-
64
- def test_ingest_missing_environment_variable(stac_collection):
65
- """Test handling when the required environment variable is missing."""
66
- if "DB_SECRET_ARN" in os.environ:
67
- del os.environ["DB_SECRET_ARN"]
68
-
69
- with pytest.raises(HTTPException) as excinfo:
70
- collection.ingest(stac_collection)
71
-
72
- assert "DB_SECRET_ARN" in str(excinfo.value)
73
-
74
-
75
- def test_ingest_credentials_error(stac_collection):
76
- """Test handling of errors during credential retrieval."""
77
- with patch(
78
- "src.collection.get_db_credentials",
79
- side_effect=Exception("Failed to retrieve credentials"),
80
- ):
81
- os.environ["DB_SECRET_ARN"] = ""
82
-
83
- with pytest.raises(HTTPException) as excinfo:
84
- collection.ingest(stac_collection)
85
-
86
- assert excinfo.value.status_code == 500
87
- assert "Failed to retrieve credentials" in str(excinfo.value.detail)
@@ -1,41 +0,0 @@
1
- from unittest.mock import patch
2
-
3
- publish_collections_endpoint = "/collections"
4
- delete_collection_endpoint = "/collections/{collection_id}"
5
-
6
-
7
- @patch("src.collection.ingest")
8
- def test_auth_publish_collection(
9
- ingest, stac_collection, example_stac_collection, client_authenticated
10
- ):
11
- token = "token"
12
- response = client_authenticated.post(
13
- publish_collections_endpoint,
14
- headers={"Authorization": f"bearer {token}"},
15
- json=example_stac_collection,
16
- )
17
- ingest.assert_called_once_with(stac_collection)
18
- assert response.status_code == 201
19
-
20
-
21
- def test_unauth_publish_collection(client, example_stac_collection):
22
- response = client.post(publish_collections_endpoint, json=example_stac_collection)
23
- assert response.status_code == 403
24
-
25
-
26
- @patch("src.collection.delete")
27
- def test_auth_delete_collection(delete, example_stac_collection, client_authenticated):
28
- token = "token"
29
- response = client_authenticated.delete(
30
- delete_collection_endpoint.format(collection_id=example_stac_collection["id"]),
31
- headers={"Authorization": f"bearer {token}"},
32
- )
33
- delete.assert_called_once_with(collection_id=example_stac_collection["id"])
34
- assert response.status_code == 200
35
-
36
-
37
- def test_unauth_delete_collection(client, example_stac_collection):
38
- response = client.delete(
39
- delete_collection_endpoint.format(collection_id=example_stac_collection["id"]),
40
- )
41
- assert response.status_code == 403
@@ -1,60 +0,0 @@
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=[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"
@@ -1,207 +0,0 @@
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
- import pytest
8
- from fastapi.encoders import jsonable_encoder
9
-
10
- if TYPE_CHECKING:
11
- from fastapi.testclient import TestClient
12
- from src import schemas, services
13
-
14
- ingestion_endpoint = "/ingestions"
15
-
16
-
17
- @pytest.fixture()
18
- def collection_exists():
19
- with patch("src.validators.collection_exists", return_value=True) as m:
20
- yield m
21
-
22
-
23
- @pytest.fixture()
24
- def collection_missing():
25
- def bad_collection(collection_id: str):
26
- raise ValueError("MOCKED MISSING COLLECTION ERROR")
27
-
28
- with patch("src.validators.collection_exists", side_effect=bad_collection) as m:
29
- yield m
30
-
31
-
32
- @pytest.fixture()
33
- def asset_exists():
34
- with patch("src.validators.url_is_accessible", return_value=True) as m:
35
- yield m
36
-
37
-
38
- @pytest.fixture()
39
- def asset_missing():
40
- def bad_asset_url(href: str):
41
- raise ValueError("MOCKED INACCESSIBLE URL ERROR")
42
-
43
- with patch("src.validators.url_is_accessible", side_effect=bad_asset_url) as m:
44
- yield m
45
-
46
-
47
- class TestCreate:
48
- @pytest.fixture(autouse=True)
49
- def setup(
50
- self,
51
- api_client: "TestClient",
52
- mock_table: "services.Table",
53
- example_ingestion: "schemas.Ingestion",
54
- ):
55
- from src import services
56
-
57
- self.api_client = api_client
58
- self.mock_table = mock_table
59
- self.db = services.Database(self.mock_table)
60
- self.example_ingestion = example_ingestion
61
-
62
- def test_unauthenticated_create(self):
63
- response = self.api_client.post(
64
- ingestion_endpoint,
65
- json=jsonable_encoder(self.example_ingestion.item),
66
- )
67
-
68
- assert response.status_code == 403
69
-
70
- def test_create(self, client_authenticated, collection_exists, asset_exists):
71
- response = self.api_client.post(
72
- ingestion_endpoint,
73
- json=jsonable_encoder(self.example_ingestion.item),
74
- )
75
-
76
- assert response.status_code == 201
77
- collection_exists.assert_called_once_with(
78
- collection_id=self.example_ingestion.item.collection
79
- )
80
-
81
- stored_data = self.db.fetch_many(status="queued")["items"]
82
- assert len(stored_data) == 1
83
- assert json.loads(stored_data[0].json(by_alias=True)) == response.json()
84
-
85
- def test_validates_missing_collection(
86
- self, client_authenticated, collection_missing, asset_exists
87
- ):
88
- response = self.api_client.post(
89
- ingestion_endpoint,
90
- json=jsonable_encoder(self.example_ingestion.item),
91
- )
92
- collection_missing.assert_called_once_with(
93
- collection_id=self.example_ingestion.item.collection
94
- )
95
- assert response.status_code == 422, "should get validation error"
96
- assert (
97
- len(self.db.fetch_many(status="queued")["items"]) == 0
98
- ), "data should not be stored in DB"
99
-
100
- def test_validates_no_collection_id(self, asset_exists):
101
- item_sans_id = self.example_ingestion.item.model_copy()
102
- item_sans_id.collection = None
103
-
104
- response = self.api_client.post(
105
- ingestion_endpoint,
106
- json=item_sans_id.model_dump(mode="json"),
107
- )
108
- assert response.status_code == 422, "should get validation error"
109
- assert (
110
- len(self.db.fetch_many(status="queued")["items"]) == 0
111
- ), "data should not be stored in DB"
112
-
113
- def test_validates_missing_assets(
114
- self, client_authenticated, collection_exists, asset_missing
115
- ):
116
- response = self.api_client.post(
117
- ingestion_endpoint,
118
- json=jsonable_encoder(self.example_ingestion.item),
119
- )
120
-
121
- collection_exists.assert_called_once_with(
122
- collection_id=self.example_ingestion.item.collection
123
- )
124
- asset_missing.assert_has_calls(
125
- [
126
- call(href=asset.href)
127
- for asset in self.example_ingestion.item.assets.values()
128
- ],
129
- any_order=True,
130
- )
131
- assert response.status_code == 422, "should get validation error"
132
- for asset_type in self.example_ingestion.item.assets.keys():
133
- assert any(
134
- err["loc"] == ["body", "assets", asset_type, "href"]
135
- for err in response.json()["detail"]
136
- ), "should reference asset type in validation error response"
137
- assert (
138
- len(self.db.fetch_many(status="queued")["items"]) == 0
139
- ), "data should not be stored in DB"
140
-
141
-
142
- class TestList:
143
- @pytest.fixture(autouse=True)
144
- def setup(
145
- self,
146
- api_client: "TestClient",
147
- mock_table: "services.Table",
148
- example_ingestion: "schemas.Ingestion",
149
- ):
150
- self.api_client = api_client
151
- self.mock_table = mock_table
152
- self.example_ingestion = example_ingestion
153
-
154
- def populate_table(self, count=100) -> List["schemas.Ingestion"]:
155
- example_ingestions = []
156
- for i in range(count):
157
- ingestion = self.example_ingestion.model_copy()
158
- ingestion.id = str(i)
159
- ingestion.created_at = ingestion.created_at + timedelta(hours=i)
160
- self.mock_table.put_item(Item=ingestion.dynamodb_dict())
161
- example_ingestions.append(ingestion)
162
- return example_ingestions
163
-
164
- def test_simple_lookup(self):
165
- self.mock_table.put_item(Item=self.example_ingestion.dynamodb_dict())
166
- ingestion = jsonable_encoder(self.example_ingestion)
167
- response = self.api_client.get(ingestion_endpoint)
168
- assert response.status_code == 200
169
- assert response.json() == {
170
- "items": [ingestion],
171
- "next": None,
172
- }
173
-
174
- def test_next_response(self):
175
- example_ingestions = self.populate_table(100)
176
-
177
- limit = 25
178
- expected_next = json.loads(
179
- example_ingestions[limit - 1].json(
180
- include={"created_by", "id", "status", "created_at"}
181
- )
182
- )
183
-
184
- response = self.api_client.get(ingestion_endpoint, params={"limit": limit})
185
- assert response.status_code == 200
186
- assert json.loads(base64.b64decode(response.json()["next"])) == expected_next
187
- assert response.json()["items"] == jsonable_encoder(example_ingestions[:limit])
188
-
189
- @pytest.mark.skip(reason="Test is currently broken")
190
- def test_get_next_page(self):
191
- example_ingestions = self.populate_table(100)
192
-
193
- limit = 25
194
- next_param = base64.b64encode(
195
- example_ingestions[limit - 1]
196
- .json(include={"created_by", "id", "status", "created_at"})
197
- .encode()
198
- )
199
-
200
- response = self.api_client.get(
201
- ingestion_endpoint, params={"limit": limit, "next": next_param}
202
- )
203
- assert response.status_code == 200
204
- assert response.json()["items"] == [
205
- json.loads(ingestion.json(by_alias=True))
206
- for ingestion in example_ingestions[limit : limit * 2]
207
- ]
@@ -1,35 +0,0 @@
1
- from unittest.mock import Mock, patch
2
-
3
- import pytest
4
- from pypgstac.load import Methods
5
- from src.utils import DbCreds
6
-
7
-
8
- @pytest.fixture()
9
- def loader():
10
- with patch("src.utils.Loader", autospec=True) as m:
11
- yield m
12
-
13
-
14
- @pytest.fixture()
15
- def pgstacdb():
16
- with patch("src.utils.PgstacDB", autospec=True) as m:
17
- m.return_value.__enter__.return_value = Mock()
18
- yield m
19
-
20
-
21
- @pytest.fixture()
22
- def dbcreds():
23
- dbcreds = DbCreds(username="", password="", host="", port=1, dbname="", engine="")
24
- return dbcreds
25
-
26
-
27
- def test_load_items(loader, pgstacdb, example_ingestion, dbcreds):
28
- import src.utils as utils
29
-
30
- ingestions = [example_ingestion]
31
- utils.load_items(dbcreds, ingestions)
32
- loader.return_value.load_items.assert_called_once_with(
33
- file=[i.item.model_dump(mode="json") for i in ingestions],
34
- insert_mode=Methods.upsert,
35
- )