Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

init #1

Merged
merged 4 commits into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
51 changes: 51 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Test and Deploy

# Triggers on pushes to main, dev and tags.
on:
workflow_dispatch:
push:
branches:
- main
- develop
tags:
- '*'
paths:
# Only run test and docker publish if some code have changed
- 'pyproject.toml'
- 'titiler/**'
- '.pre-commit-config.yaml'
- '.github/workflows/ci.yml'

# Run tests on pull requests.
pull_request:
env:
LATEST_PY_VERSION: '3.12'


jobs:
tests:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']

steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -e .["test"]

- name: run pre-commit
if: ${{ matrix.python-version == env.LATEST_PY_VERSION }}
run: |
python -m pip install pre-commit
pre-commit run --all-files

- name: Run tests
run: python -m pytest --cov titiler.stacapi --cov-report term-missing -s -vv
34 changes: 34 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
repos:
- repo: https://github.com/abravalheri/validate-pyproject
rev: v0.12.1
hooks:
- id: validate-pyproject

- repo: https://github.com/psf/black
rev: 22.12.0
hooks:
- id: black
language_version: python

- repo: https://github.com/PyCQA/isort
rev: 5.12.0
hooks:
- id: isort
language_version: python

- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.290
hooks:
- id: ruff
args: ["--fix"]

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.5.1
hooks:
- id: mypy
language_version: python
exclude: tests/.*
additional_dependencies:
- types-cachetools
- types-attrs
- pydantic>=2.4
Empty file added CHANGELOG.md
Empty file.
24 changes: 24 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Development - Contributing

Issues and pull requests are more than welcome: https://github.com/developmentseed/titiler-stacapi/issues

**dev install**

```bash
git clone https://github.com/developmentseed/titiler-stacapi.git
cd titiler-stacapi

python -m pip install pre-commit -e .["dev,test"]
```

You can then run the tests with the following command:

```sh
python -m pytest --cov titiler.stacapi --cov-report term-missing
```

This repo is set to use `pre-commit` to run *isort*, *flake8*, *pydocstring*, *black* ("uncompromising Python code formatter") and mypy when committing new code.

```bash
$ pre-commit install
```
79 changes: 78 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,79 @@
# titiler-stacapi
Connect titiler to STAC APIs

<p align="center">
<p align="center">Connect titiler to STAC APIs</p>
</p>

<p align="center">
<a href="https://github.com/developmentseed/titiler-stacapi/actions?query=workflow%3ACI" target="_blank">
<img src="https://github.com/developmentseed/titiler-stacapi/workflows/CI/badge.svg" alt="Test">
</a>
<a href="https://codecov.io/gh/developmentseed/titiler-stacapi" target="_blank">
<img src="https://codecov.io/gh/developmentseed/titiler-stacapi/branch/main/graph/badge.svg" alt="Coverage">
</a>
<a href="https://github.com/developmentseed/titiler-stacapi/blob/main/LICENSE" target="_blank">
<img src="https://img.shields.io/github/license/developmentseed/titiler-stacapi.svg" alt="License">
</a>
</p>

---

**Documentation**: <a href="https://developmentseed.org/titiler-stacapi/" target="_blank">https://developmentseed.org/titiler-stacapi/</a>

**Source Code**: <a href="https://github.com/developmentseed/titiler-stacapi" target="_blank">https://github.com/developmentseed/titiler-stacapi</a>

---

## Installation

Install from sources and run for development:

```
$ git clone https://github.com/developmentseed/titiler-stacapi.git
$ cd titiler-stacapi
$ python -m pip install -e .
```

## Launch

You'll need to have `TITILER_STACAPI_STAC_API_URL` variables set in your environment pointing to your STAC API service.

```
export TITILER_STACAPI_STAC_API_URL=https://api.stac
```

```
python -m pip install uvicorn

uvicorn titiler.stacapi.main:app --port 8000
```

### Using Docker

```
$ git clone https://github.com/developmentseed/titiler-stacapi.git
$ cd titiler-stacapi
$ docker-compose up --build api
```

It runs `titiler.stacapi` using Gunicorn web server. To run Uvicorn based version:

```
$ docker-compose up --build api-uvicorn
```

## Contribution & Development

See [CONTRIBUTING.md](https://github.com//developmentseed/titiler-stacapi/blob/main/CONTRIBUTING.md)

## License

See [LICENSE](https://github.com//developmentseed/titiler-stacapi/blob/main/LICENSE)

## Authors

See [contributors](https://github.com/developmentseed/titiler-stacapi/graphs/contributors) for a listing of individual contributors.

## Changes

See [CHANGES.md](https://github.com/developmentseed/titiler-stacapi/blob/main/CHANGES.md).
107 changes: 107 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
[project]
name = "titiler.stacapi"
description = "Connect titiler to STAC APIs."
readme = "README.md"
requires-python = ">=3.8"
license = {file = "LICENSE"}
authors = [
{name = "Vincent Sarago", email = "[email protected]"},
]
keywords = [
"TiTiler",
"STAC API",
]
classifiers = [
"Intended Audience :: Information Technology",
"Intended Audience :: Science/Research",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Scientific/Engineering :: GIS",
]
dependencies = [
"orjson",
"titiler.core>=0.17.0,<0.18",
"titiler.mosaic>=0.17.0,<0.18",
"pystac-client",
"pydantic>=2.4,<3.0",
"pydantic-settings~=2.0",
]
dynamic = ["version"]

[project.optional-dependencies]
dev = [
"pre-commit",
]
test = [
"pytest",
"pytest-cov",
"pytest-asyncio",
"httpx",
]

[project.urls]
Homepage = 'https://developmentseed.org/titiler-stacapi/'
Documentation = "https://developmentseed.org/titiler-stacapi/"
Issues = "https://github.com/developmentseed/titiler-stacapi/issues"
Source = "https://github.com/developmentseed/titiler-stacapi"
Changelog = "https://developmentseed.org/titiler-stacapi/release-notes/"

[build-system]
requires = ["pdm-pep517"]
build-backend = "pdm.pep517.api"

[tool.pdm.version]
source = "file"
path = "titiler/stacapi/__init__.py"

[tool.pdm.build]
includes = ["titiler/stacapi"]
excludes = ["tests/", "**/.mypy_cache", "**/.DS_Store"]

[tool.coverage.run]
branch = true
parallel = true

[tool.coverage.report]
exclude_lines = [
"no cov",
"if __name__ == .__main__.:",
"if TYPE_CHECKING:",
]

[tool.isort]
profile = "black"
known_first_party = ["titiler"]
known_third_party = [
"rasterio",
"morecantile",
"rio_tiler",
"cogeo_mosaic",
"geojson_pydantic",
]
default_section = "THIRDPARTY"

[tool.ruff]
select = [
"D1", # pydocstyle errors
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # flake8
"C", # flake8-comprehensions
"B", # flake8-bugbear
]
ignore = [
"E501", # line too long, handled by black
"B008", # do not perform function calls in argument defaults
"B905", # ignore zip() without an explicit strict= parameter, only support with python >3.10
]

[tool.mypy]
no_implicit_optional = true
strict_optional = true
namespace_packages = true
explicit_package_bases = true
15 changes: 15 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"""titiler.stacapi tests configuration."""

import pytest
from fastapi.testclient import TestClient


@pytest.fixture
def app(monkeypatch):
"""App fixture."""
monkeypatch.setenv("TITILER_STACAPI_STAC_API_URL", "http://something.stac")

from titiler.stacapi.main import app

with TestClient(app) as client:
yield client
72 changes: 72 additions & 0 deletions tests/test_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
"""test titiler-stacapi app."""


def test_landing(app):
"""Test / endpoint."""
response = app.get("/")
assert response.status_code == 200
assert response.headers["content-type"] == "application/json"
body = response.json()
assert body["title"] == "titiler-stacapi"
assert body["links"]

response = app.get("/?f=html")
assert response.status_code == 200
assert "text/html" in response.headers["content-type"]
assert "titiler-stacapi" in response.text

# Check accept headers
response = app.get("/", headers={"accept": "text/html"})
assert response.status_code == 200
assert "text/html" in response.headers["content-type"]
assert "titiler-stacapi" in response.text

# accept quality
response = app.get(
"/", headers={"accept": "application/json;q=0.9, text/html;q=1.0"}
)
assert response.status_code == 200
assert "text/html" in response.headers["content-type"]
assert "titiler-stacapi" in response.text

# accept quality but only json is available
response = app.get("/", headers={"accept": "text/csv;q=1.0, application/json"})
assert response.status_code == 200
assert response.headers["content-type"] == "application/json"
body = response.json()
assert body["title"] == "titiler-stacapi"

# accept quality but only json is available
response = app.get("/", headers={"accept": "text/csv;q=1.0, */*"})
assert response.status_code == 200
assert response.headers["content-type"] == "application/json"
body = response.json()
assert body["title"] == "titiler-stacapi"

# Invalid accept, return default
response = app.get("/", headers={"accept": "text/htm"})
assert response.status_code == 200
assert response.headers["content-type"] == "application/json"
body = response.json()
assert body["title"] == "titiler-stacapi"
assert body["links"]

# make sure `?f=` has priority over headers
response = app.get("/?f=json", headers={"accept": "text/html"})
assert response.status_code == 200
assert response.headers["content-type"] == "application/json"
body = response.json()
assert body["title"] == "titiler-stacapi"


def test_docs(app):
"""Test /api endpoint."""
response = app.get("/api")
assert response.status_code == 200
assert response.headers["content-type"] == "application/json"
body = response.json()
assert body["openapi"]

response = app.get("/api.html")
assert response.status_code == 200
assert "text/html" in response.headers["content-type"]
Loading