Skip to content

Commit af6e2a9

Browse files
authored
Merge pull request #1 from Healy-Hyperspatial/add-tests
Add tests, clean up, v0.1.1
2 parents 9df68c0 + 234918e commit af6e2a9

File tree

7 files changed

+465
-118
lines changed

7 files changed

+465
-118
lines changed

.github/workflows/cicd.yml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
name: CI/CD
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
branches:
9+
- main
10+
- features/**
11+
12+
jobs:
13+
test:
14+
runs-on: ubuntu-latest
15+
strategy:
16+
matrix:
17+
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14']
18+
steps:
19+
- uses: actions/checkout@v4
20+
- name: Set up Python ${{ matrix.python-version }}
21+
uses: actions/setup-python@v4
22+
with:
23+
python-version: ${{ matrix.python-version }}
24+
- name: Install dependencies
25+
run: |
26+
python -m pip install --upgrade pip
27+
pip install --no-cache-dir -e ".[dev]"
28+
- name: Run tests
29+
run: pytest tests/ -v --cov=sfeos_tools --cov-report=xml
30+
- name: Upload coverage to Codecov
31+
uses: codecov/codecov-action@v3
32+
with:
33+
files: ./coverage.xml
34+
fail_ci_if_error: false
35+
36+
lint:
37+
runs-on: ubuntu-latest
38+
steps:
39+
- uses: actions/checkout@v4
40+
- name: Set up Python
41+
uses: actions/setup-python@v4
42+
with:
43+
python-version: '3.12'
44+
- uses: pre-commit/[email protected]

CHANGELOG.md

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,26 @@ The format is (loosely) based on [Keep a Changelog](http://keepachangelog.com/)
66

77
## [Unreleased]
88

9-
## [v0.1.0] - 2025-10-15
9+
## [v0.1.1] - 2025-10-21
10+
11+
### Changed
12+
13+
- Moved `add_bbox_shape` logic from `cli.py` to new `bbox_shape.py` module for better code organization [#1](https://github.com/healy-hyperspatial/sfeos-tools/pull/1)
14+
15+
### Added
16+
17+
- Comprehensive test suite for `bbox_shape` module with 9 test cases covering both Elasticsearch and OpenSearch backends [#1](https://github.com/healy-hyperspatial/sfeos-tools/pull/1)
18+
19+
### Removed
20+
21+
- Python 3.8 support [#1](https://github.com/healy-hyperspatial/sfeos-tools/pull/1)
22+
23+
## [v0.1.0] - 2025-10-15 [#1](https://github.com/healy-hyperspatial/sfeos-tools/pull/1)
1024

1125
### Added
1226

1327
- Initial release
1428

15-
[Unreleased]: https://github.com/healy-hyperspatial/sfeos-tools/compare/v0.1.0..main
16-
[v0.1.0]: https://github.com/healy-hyperspatial/sfeos-tools/releases/tag/v0.1.0
29+
[Unreleased]: https://github.com/healy-hyperspatial/sfeos-tools/compare/v0.1.1..main
30+
[v0.1.1]: https://github.com/healy-hyperspatial/sfeos-tools/compare/v0.1.0...v0.1.1
31+
[v0.1.0]: https://github.com/healy-hyperspatial/sfeos-tools/compare/v0.1.0

pyproject.toml

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "sfeos-tools"
7-
version = "0.1.0"
7+
version = "0.1.1"
88
description = "CLI tools for managing stac-fastapi-elasticsearch-opensearch deployments"
99
readme = "README.md"
1010
authors = [
@@ -21,11 +21,12 @@ classifiers = [
2121
"Intended Audience :: Developers",
2222
"Intended Audience :: Science/Research",
2323
"License :: OSI Approved :: MIT License",
24-
"Programming Language :: Python :: 3.8",
2524
"Programming Language :: Python :: 3.9",
2625
"Programming Language :: Python :: 3.10",
2726
"Programming Language :: Python :: 3.11",
2827
"Programming Language :: Python :: 3.12",
28+
"Programming Language :: Python :: 3.13",
29+
"Programming Language :: Python :: 3.14",
2930
"Topic :: Scientific/Engineering",
3031
"Topic :: Scientific/Engineering :: GIS",
3132
"Topic :: Software Development :: Libraries :: Python Modules"
@@ -42,16 +43,17 @@ Source = "https://github.com/Healy-Hyperspatial/sfeos-tools"
4243

4344
[project.optional-dependencies]
4445
elasticsearch = [
45-
"stac-fastapi-elasticsearch",
46+
"stac-fastapi-elasticsearch>=6.6.0",
4647
]
4748
opensearch = [
48-
"stac-fastapi-opensearch",
49+
"stac-fastapi-opensearch>=6.6.0",
4950
]
5051
dev = [
51-
"stac-fastapi-elasticsearch",
52-
"stac-fastapi-opensearch",
52+
"stac-fastapi-elasticsearch>=6.6.0",
53+
"stac-fastapi-opensearch>=6.6.0",
5354
# Development tools
5455
"pytest>=6.0",
56+
"pytest-asyncio>=0.21.0",
5557
"pytest-cov>=2.0",
5658
"black>=21.0",
5759
"isort>=5.0",
@@ -79,7 +81,11 @@ line_length = 88
7981
[tool.pytest.ini_options]
8082
testpaths = ["tests"]
8183
python_files = "test_*.py"
82-
addopts = "-v --cov=sfeos_tool --cov-report=term-missing"
84+
addopts = "-v --cov=sfeos_tools --cov-report=term-missing"
85+
asyncio_mode = "auto"
86+
filterwarnings = [
87+
"ignore::DeprecationWarning:pydantic._internal._config",
88+
]
8389

8490
[tool.coverage.report]
8591
exclude_lines = [

sfeos_tools/bbox_shape.py

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
"""Bbox shape migration utilities for SFEOS collections."""
2+
3+
import logging
4+
5+
from stac_fastapi.sfeos_helpers.database import add_bbox_shape_to_collection
6+
from stac_fastapi.sfeos_helpers.mappings import COLLECTIONS_INDEX
7+
8+
logger = logging.getLogger(__name__)
9+
10+
11+
async def process_collection_bbox_shape(client, collection_doc, backend):
12+
"""Process a single collection document to add bbox_shape field.
13+
14+
Args:
15+
client: Elasticsearch/OpenSearch client
16+
collection_doc: Collection document from database
17+
backend: Backend type ('elasticsearch' or 'opensearch')
18+
19+
Returns:
20+
bool: True if collection was updated, False if no update was needed
21+
"""
22+
collection = collection_doc["_source"]
23+
collection_id = collection.get("id", collection_doc["_id"])
24+
25+
# Use the shared function to add bbox_shape
26+
was_added = add_bbox_shape_to_collection(collection)
27+
28+
if not was_added:
29+
return False
30+
31+
# Update the collection in the database
32+
if backend == "elasticsearch":
33+
await client.index(
34+
index=COLLECTIONS_INDEX,
35+
id=collection_id,
36+
document=collection,
37+
refresh=True,
38+
)
39+
else: # opensearch
40+
await client.index(
41+
index=COLLECTIONS_INDEX,
42+
id=collection_id,
43+
body=collection,
44+
refresh=True,
45+
)
46+
47+
logger.info(f"Collection '{collection_id}': Added bbox_shape field")
48+
return True
49+
50+
51+
async def run_add_bbox_shape(backend):
52+
"""Add bbox_shape field to all existing collections.
53+
54+
Args:
55+
backend: Backend type ('elasticsearch' or 'opensearch')
56+
"""
57+
import os
58+
59+
logger.info(
60+
f"Starting migration: Adding bbox_shape to existing collections ({backend})"
61+
)
62+
63+
# Log connection info (showing what will be used by the client)
64+
es_host = os.getenv("ES_HOST", "localhost")
65+
es_port = os.getenv(
66+
"ES_PORT", "9200"
67+
) # Both backends default to 9200 in their config
68+
es_use_ssl = os.getenv("ES_USE_SSL", "true")
69+
logger.info(f"Connecting to {backend} at {es_host}:{es_port} (SSL: {es_use_ssl})")
70+
71+
# Create client based on backend
72+
if backend == "elasticsearch":
73+
from stac_fastapi.elasticsearch.config import AsyncElasticsearchSettings
74+
75+
settings = AsyncElasticsearchSettings()
76+
else: # opensearch
77+
from stac_fastapi.opensearch.config import AsyncOpensearchSettings
78+
79+
settings = AsyncOpensearchSettings()
80+
81+
client = settings.create_client
82+
83+
try:
84+
# Get all collections
85+
response = await client.search(
86+
index=COLLECTIONS_INDEX,
87+
body={
88+
"query": {"match_all": {}},
89+
"size": 10000,
90+
}, # Adjust size if you have more collections
91+
)
92+
93+
total_collections = response["hits"]["total"]["value"]
94+
logger.info(f"Found {total_collections} collections to process")
95+
96+
updated_count = 0
97+
skipped_count = 0
98+
99+
for hit in response["hits"]["hits"]:
100+
was_updated = await process_collection_bbox_shape(client, hit, backend)
101+
if was_updated:
102+
updated_count += 1
103+
else:
104+
skipped_count += 1
105+
106+
logger.info(
107+
f"Migration complete: {updated_count} collections updated, {skipped_count} skipped"
108+
)
109+
110+
except Exception as e:
111+
logger.error(f"Migration failed with error: {e}")
112+
raise
113+
finally:
114+
await client.close()

sfeos_tools/cli.py

Lines changed: 1 addition & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -13,121 +13,14 @@
1313
import sys
1414

1515
import click
16-
from stac_fastapi.sfeos_helpers.database import add_bbox_shape_to_collection
17-
from stac_fastapi.sfeos_helpers.mappings import COLLECTIONS_INDEX
1816

17+
from .bbox_shape import run_add_bbox_shape
1918
from .reindex import run as unified_reindex_run
2019

2120
logging.basicConfig(level=logging.INFO)
2221
logger = logging.getLogger(__name__)
2322

2423

25-
async def process_collection_bbox_shape(client, collection_doc, backend):
26-
"""Process a single collection document to add bbox_shape field.
27-
28-
Args:
29-
client: Elasticsearch/OpenSearch client
30-
collection_doc: Collection document from database
31-
backend: Backend type ('elasticsearch' or 'opensearch')
32-
33-
Returns:
34-
bool: True if collection was updated, False if no update was needed
35-
"""
36-
collection = collection_doc["_source"]
37-
collection_id = collection.get("id", collection_doc["_id"])
38-
39-
# Use the shared function to add bbox_shape
40-
was_added = add_bbox_shape_to_collection(collection)
41-
42-
if not was_added:
43-
return False
44-
45-
# Update the collection in the database
46-
if backend == "elasticsearch":
47-
await client.index(
48-
index=COLLECTIONS_INDEX,
49-
id=collection_id,
50-
document=collection,
51-
refresh=True,
52-
)
53-
else: # opensearch
54-
await client.index(
55-
index=COLLECTIONS_INDEX,
56-
id=collection_id,
57-
body=collection,
58-
refresh=True,
59-
)
60-
61-
logger.info(f"Collection '{collection_id}': Added bbox_shape field")
62-
return True
63-
64-
65-
async def run_add_bbox_shape(backend):
66-
"""Add bbox_shape field to all existing collections.
67-
68-
Args:
69-
backend: Backend type ('elasticsearch' or 'opensearch')
70-
"""
71-
import os
72-
73-
logger.info(
74-
f"Starting migration: Adding bbox_shape to existing collections ({backend})"
75-
)
76-
77-
# Log connection info (showing what will be used by the client)
78-
es_host = os.getenv("ES_HOST", "localhost")
79-
es_port = os.getenv(
80-
"ES_PORT", "9200"
81-
) # Both backends default to 9200 in their config
82-
es_use_ssl = os.getenv("ES_USE_SSL", "true")
83-
logger.info(f"Connecting to {backend} at {es_host}:{es_port} (SSL: {es_use_ssl})")
84-
85-
# Create client based on backend
86-
if backend == "elasticsearch":
87-
from stac_fastapi.elasticsearch.config import AsyncElasticsearchSettings
88-
89-
settings = AsyncElasticsearchSettings()
90-
else: # opensearch
91-
from stac_fastapi.opensearch.config import AsyncOpensearchSettings
92-
93-
settings = AsyncOpensearchSettings()
94-
95-
client = settings.create_client
96-
97-
try:
98-
# Get all collections
99-
response = await client.search(
100-
index=COLLECTIONS_INDEX,
101-
body={
102-
"query": {"match_all": {}},
103-
"size": 10000,
104-
}, # Adjust size if you have more collections
105-
)
106-
107-
total_collections = response["hits"]["total"]["value"]
108-
logger.info(f"Found {total_collections} collections to process")
109-
110-
updated_count = 0
111-
skipped_count = 0
112-
113-
for hit in response["hits"]["hits"]:
114-
was_updated = await process_collection_bbox_shape(client, hit, backend)
115-
if was_updated:
116-
updated_count += 1
117-
else:
118-
skipped_count += 1
119-
120-
logger.info(
121-
f"Migration complete: {updated_count} collections updated, {skipped_count} skipped"
122-
)
123-
124-
except Exception as e:
125-
logger.error(f"Migration failed with error: {e}")
126-
raise
127-
finally:
128-
await client.close()
129-
130-
13124
@click.group()
13225
@click.version_option(version="0.1.0", prog_name="sfeos-tools")
13326
def cli():

tests/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)