Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ COPY scripts/wait-for-it.sh scripts/wait-for-it.sh
COPY pyproject.toml pyproject.toml
COPY README.md README.md

RUN python -m pip install .[server]
RUN rm -rf stac_fastapi .toml README.md
RUN python -m pip install -e .[server,catalogs]

RUN groupadd -g 1000 user && \
useradd -u 1000 -g user -s /bin/bash -m user
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile.tests
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ USER newuser
WORKDIR /app
COPY . /app

RUN python -m pip install . --user --group dev
RUN python -m pip install .[catalogs] --user --group dev
8 changes: 6 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ run = docker compose run --rm \
-e APP_PORT=${APP_PORT} \
app

runtests = docker compose run --rm tests
runtests = docker compose -f compose-tests.yml run --rm tests

.PHONY: image
image:
Expand All @@ -22,7 +22,7 @@ docker-run: image

.PHONY: docker-run-nginx-proxy
docker-run-nginx-proxy:
docker compose -f docker-compose.yml -f docker-compose.nginx.yml up
docker compose -f compose.yml -f docker-compose.nginx.yml up

.PHONY: docker-shell
docker-shell:
Expand All @@ -32,6 +32,10 @@ docker-shell:
test:
$(runtests) /bin/bash -c 'export && python -m pytest /app/tests/ --log-cli-level $(LOG_LEVEL)'

.PHONY: test-catalogs
test-catalogs:
$(runtests) /bin/bash -c 'export && python -m pytest /app/tests/test_catalogs.py -v --log-cli-level $(LOG_LEVEL)'

.PHONY: run-database
run-database:
docker compose run --rm database
Expand Down
14 changes: 10 additions & 4 deletions docker-compose.yml → compose-tests.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
services:
app:
image: stac-utils/stac-fastapi-pgstac
restart: always
build: .
environment:
- APP_HOST=0.0.0.0
Expand All @@ -20,15 +21,19 @@ services:
- DB_MAX_CONN_SIZE=1
- USE_API_HYDRATE=${USE_API_HYDRATE:-false}
- ENABLE_TRANSACTIONS_EXTENSIONS=TRUE
ports:
- "8082:8082"
- ENABLE_CATALOGS_ROUTE=TRUE
# ports:
# - "8082:8082"
depends_on:
- database
command: bash -c "scripts/wait-for-it.sh database:5432 && python -m stac_fastapi.pgstac.app"
command: bash -c "scripts/wait-for-it.sh database:5432 && uvicorn stac_fastapi.pgstac.app:app --host 0.0.0.0 --port 8082 --reload"
develop:
watch:
- action: rebuild
- action: sync
path: ./stac_fastapi/pgstac
target: /app/stac_fastapi/pgstac
- action: rebuild
path: ./setup.py

tests:
image: stac-utils/stac-fastapi-pgstac-test
Expand All @@ -40,6 +45,7 @@ services:
- DB_MIN_CONN_SIZE=1
- DB_MAX_CONN_SIZE=1
- USE_API_HYDRATE=${USE_API_HYDRATE:-false}
- ENABLE_CATALOGS_ROUTE=TRUE
command: bash -c "python -m pytest -s -vv"

database:
Expand Down
91 changes: 91 additions & 0 deletions compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
services:
app:
image: stac-utils/stac-fastapi-pgstac
restart: always
build: .
environment:
- APP_HOST=0.0.0.0
- APP_PORT=8082
- RELOAD=true
- ENVIRONMENT=local
- PGUSER=username
- PGPASSWORD=password
- PGDATABASE=postgis
- PGHOST=database
- PGPORT=5432
- WEB_CONCURRENCY=10
- VSI_CACHE=TRUE
- GDAL_HTTP_MERGE_CONSECUTIVE_RANGES=YES
- GDAL_DISABLE_READDIR_ON_OPEN=EMPTY_DIR
- DB_MIN_CONN_SIZE=1
- DB_MAX_CONN_SIZE=1
- USE_API_HYDRATE=${USE_API_HYDRATE:-false}
- ENABLE_TRANSACTIONS_EXTENSIONS=TRUE
- ENABLE_CATALOGS_ROUTE=TRUE
# ports:
# - "8082:8082"
depends_on:
- database
command: bash -c "scripts/wait-for-it.sh database:5432 && uvicorn stac_fastapi.pgstac.app:app --host 0.0.0.0 --port 8082 --reload"
develop:
watch:
- action: sync
path: ./stac_fastapi/pgstac
target: /app/stac_fastapi/pgstac
- action: rebuild
path: ./setup.py

database:
image: ghcr.io/stac-utils/pgstac:v0.9.8
environment:
- POSTGRES_USER=username
- POSTGRES_PASSWORD=password
- POSTGRES_DB=postgis
- PGUSER=username
- PGPASSWORD=password
- PGDATABASE=postgis
ports:
- "5439:5432"
command: postgres -N 500

# Load joplin demo dataset into the PGStac Application
loadjoplin:
image: stac-utils/stac-fastapi-pgstac
environment:
- ENVIRONMENT=development
volumes:
- ./testdata:/tmp/testdata
- ./scripts:/tmp/scripts
command: >
/bin/sh -c "
scripts/wait-for-it.sh -t 60 app:8082 &&
python -m pip install pip -U &&
python -m pip install requests &&
python /tmp/scripts/ingest_joplin.py http://app:8082
"
depends_on:
- database
- app

nginx:
image: nginx
ports:
- ${STAC_FASTAPI_NGINX_PORT:-8080}:80
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- app-nginx
command: [ "nginx-debug", "-g", "daemon off;" ]

app-nginx:
extends:
service: app
command: >
bash -c "
scripts/wait-for-it.sh database:5432 &&
uvicorn stac_fastapi.pgstac.app:app --host 0.0.0.0 --port 8082 --proxy-headers --forwarded-allow-ips=* --root-path=/api/v1/pgstac
"

networks:
default:
name: stac-fastapi-network
5 changes: 4 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "stac-fastapi-pgstac"
description = "An implementation of STAC API based on the FastAPI framework and using the pgstac backend."
readme = "README.md"
requires-python = ">=3.11"
requires-python = ">=3.12"
license = "MIT"
authors = [
{ name = "David Bitner", email = "david@developmentseed.org" },
Expand Down Expand Up @@ -55,6 +55,9 @@ validation = [
server = [
"uvicorn[standard]==0.38.0"
]
catalogs = [
"stac-fastapi-catalogs-extension>=0.1.2",
]

[dependency-groups]
dev = [
Expand Down
52 changes: 46 additions & 6 deletions stac_fastapi/pgstac/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
If the variable is not set, enables all extensions.
"""

import logging
import os
from contextlib import asynccontextmanager
from typing import cast
Expand Down Expand Up @@ -45,13 +46,35 @@
from stac_fastapi.pgstac.config import Settings
from stac_fastapi.pgstac.core import CoreCrudClient, health_check
from stac_fastapi.pgstac.db import close_db_connection, connect_to_db
from stac_fastapi.pgstac.extensions import FreeTextExtension, QueryExtension
from stac_fastapi.pgstac.extensions import (
DatabaseLogic,
FreeTextExtension,
QueryExtension,
)
from stac_fastapi.pgstac.extensions.catalogs.catalogs_client import CatalogsClient
from stac_fastapi.pgstac.extensions.filter import FiltersClient
from stac_fastapi.pgstac.transactions import BulkTransactionsClient, TransactionsClient
from stac_fastapi.pgstac.types.search import PgstacSearch

logger = logging.getLogger(__name__)

# Optional catalogs extension (optional dependency)
try:
from stac_fastapi_catalogs_extension import CatalogsExtension
except ImportError:
CatalogsExtension = None

settings = Settings()


def _is_env_flag_enabled(name: str) -> bool:
"""Return True if the given env var is enabled.

Accepts common truthy values ("yes", "true", "1") case-insensitively.
"""
return os.environ.get(name, "").lower() in ("yes", "true", "1")


# search extensions
search_extensions_map: dict[str, ApiExtension] = {
"query": QueryExtension(),
Expand Down Expand Up @@ -98,11 +121,7 @@

application_extensions: list[ApiExtension] = []

with_transactions = os.environ.get("ENABLE_TRANSACTIONS_EXTENSIONS", "").lower() in [
"yes",
"true",
"1",
]
with_transactions = _is_env_flag_enabled("ENABLE_TRANSACTIONS_EXTENSIONS")
if with_transactions:
application_extensions.append(
TransactionExtension(
Expand Down Expand Up @@ -158,6 +177,27 @@
collections_get_request_model = collection_search_extension.GET
application_extensions.append(collection_search_extension)

# Optional catalogs route
ENABLE_CATALOGS_ROUTE = _is_env_flag_enabled("ENABLE_CATALOGS_ROUTE")
logger.info("ENABLE_CATALOGS_ROUTE is set to %s", ENABLE_CATALOGS_ROUTE)

if ENABLE_CATALOGS_ROUTE:
if CatalogsExtension is None:
logger.warning(
"ENABLE_CATALOGS_ROUTE is set to true, but the catalogs extension is not installed. "
"Please install it with: pip install stac-fastapi-core[catalogs].",
)
else:
try:
catalogs_extension = CatalogsExtension(
client=CatalogsClient(database=DatabaseLogic()),
enable_transactions=with_transactions,
)
application_extensions.append(catalogs_extension)
print("CatalogsExtension enabled successfully.")
except Exception as e: # pragma: no cover - defensive
logger.warning("Failed to initialize CatalogsExtension: %s", e)


@asynccontextmanager
async def lifespan(app: FastAPI):
Expand Down
10 changes: 9 additions & 1 deletion stac_fastapi/pgstac/extensions/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
"""pgstac extension customisations."""

from .catalogs.catalogs_client import CatalogsClient
from .catalogs.catalogs_database_logic import DatabaseLogic
from .filter import FiltersClient
from .free_text import FreeTextExtension
from .query import QueryExtension

__all__ = ["QueryExtension", "FiltersClient", "FreeTextExtension"]
__all__ = [
"QueryExtension",
"FiltersClient",
"FreeTextExtension",
"CatalogsClient",
"DatabaseLogic",
]
Loading
Loading