From af0506d1cd67eb61f6d6adc6cb5382a1bf42db66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dion=20H=C3=A4fner?= Date: Mon, 15 May 2023 10:14:26 +0200 Subject: [PATCH 1/4] fix explicit cmap example in apidoc (#306) --- terracotta/server/singleband.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terracotta/server/singleband.py b/terracotta/server/singleband.py index ca62470b..1aeb5670 100644 --- a/terracotta/server/singleband.py +++ b/terracotta/server/singleband.py @@ -52,7 +52,7 @@ class Meta: explicit_color_map = fields.Dict( keys=fields.Number(), values=fields.List(fields.Number, validate=validate.Length(min=3, max=4)), - example="{0: (255, 255, 255)}", + example='{"0": [255, 255, 255]}', description="Explicit value-color mapping to use, encoded as JSON object. " "Must be given together with `colormap=explicit`. Color values can be " "specified either as RGB or RGBA tuple (in the range of [0, 255]), or as " From a2e1ddb56086ae4e17fa80734cb4f4cf28348095 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dion=20H=C3=A4fner?= Date: Mon, 15 May 2023 11:26:10 +0200 Subject: [PATCH 2/4] render apidoc in online docs --- docs/.gitignore | 2 ++ docs/_static/generated/EMPTY | 0 docs/apidoc.rst | 4 ++++ docs/conf.py | 40 +++++++++++++++++++++++++++++++++--- docs/index.rst | 1 + 5 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 docs/.gitignore create mode 100644 docs/_static/generated/EMPTY create mode 100644 docs/apidoc.rst diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 00000000..60af7f70 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,2 @@ +_static/generated/* +!_static/generated/EMPTY \ No newline at end of file diff --git a/docs/_static/generated/EMPTY b/docs/_static/generated/EMPTY new file mode 100644 index 00000000..e69de29b diff --git a/docs/apidoc.rst b/docs/apidoc.rst new file mode 100644 index 00000000..4e96092e --- /dev/null +++ b/docs/apidoc.rst @@ -0,0 +1,4 @@ +Server API +========== + +This file is intentionally left blank diff --git a/docs/conf.py b/docs/conf.py index 98309c6d..0227961d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -63,7 +63,7 @@ # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = None +language = 'en' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. @@ -81,6 +81,40 @@ # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] +# -- Render apidoc ------------------------------------------------------------- + +# Get swagger.json from terracotta server +from terracotta.server.app import app as terracotta_app + +with terracotta_app.test_client() as client: + swagger_json = client.get('/swagger.json').data.decode('utf-8') + +with open('_static/generated/swagger.json', 'w') as f: + f.write(swagger_json) + +templates_path.append('../terracotta/server/templates') +html_static_path.append('../terracotta/server/static') + +html_additional_pages = { + 'apidoc': 'apidoc.html', +} + + +def _url_for(endpoint, filename=None): + if filename is not None: + assert endpoint == 'static' + return f'_static/{filename}' + + assert endpoint == '.specification' + return '_static/generated/swagger.json' + + +# Inject Jinja variables defined by Flask +html_context = { + 'url_for': _url_for +} + + # -- Extension settings -------------------------------------------------------- intersphinx_mapping = { @@ -108,12 +142,12 @@ 'body_text': '#000', 'sidebar_header': '#4B4032', 'sidebar_text': '#49443E', - 'github_banner': 'true', + 'github_banner': 'false', 'github_user': 'DHI-GRAS', 'github_repo': 'terracotta', 'github_button': 'true', 'github_type': 'star', - 'travis_button': 'true', + 'travis_button': 'false', 'codecov_button': 'true', 'logo': 'logo.svg' } diff --git a/docs/index.rst b/docs/index.rst index 50a1c346..df48c67b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -70,6 +70,7 @@ Contents settings cli api + apidoc tutorial reference issues From 34248b7b47641955731701ceb4fb1da46ba5d915 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dion=20H=C3=A4fner?= Date: Mon, 15 May 2023 11:48:06 +0200 Subject: [PATCH 3/4] fix tests --- tests/conftest.py | 4 +-- tests/scripts/test_migrate.py | 46 +++++++++++++++++++++++++++++----- tests/server/test_flask_api.py | 19 +++++--------- 3 files changed, 48 insertions(+), 21 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 3ef14404..cdd3448e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -359,9 +359,9 @@ def testdb(raster_file, tmpdir_factory): return dbpath -@pytest.fixture(scope="session") +@pytest.fixture() def v07_db(tmpdir_factory): - """A read-only, pre-populated test database""" + """A read-only, pre-populated test database at terracotta v0.7""" import shutil dbpath = tmpdir_factory.mktemp("db").join("db-outdated.sqlite") diff --git a/tests/scripts/test_migrate.py b/tests/scripts/test_migrate.py index cd6630b3..7a137944 100644 --- a/tests/scripts/test_migrate.py +++ b/tests/scripts/test_migrate.py @@ -1,3 +1,4 @@ +import pytest from click.testing import CliRunner @@ -9,6 +10,38 @@ def parse_version(verstr): def test_migrate(v07_db, monkeypatch, force_reload): + """Test database migration to this major version.""" + import terracotta + from terracotta import get_driver + from terracotta.scripts import cli + + current_version = parse_version(terracotta.__version__) + + # run migration + runner = CliRunner() + result = runner.invoke( + cli.cli, + [ + "migrate", + str(v07_db), + "--from", + "v0.7", + "--to", + f"v{current_version[0]}.{current_version[1]}", + "--yes", + ], + ) + + assert result.exit_code == 0 + assert "Upgrade path found" in result.output + + driver_updated = get_driver(str(v07_db), provider="sqlite") + + # key_names did not exist in v0.7 + assert driver_updated.key_names == ("key1", "akey", "key2") + + +def test_migrate_next(v07_db, monkeypatch, force_reload): """Test database migration to next major version if one is available.""" with monkeypatch.context() as m: # pretend we are at the next major version @@ -18,22 +51,23 @@ def test_migrate(v07_db, monkeypatch, force_reload): next_major_version = (current_version[0], current_version[1] + 1, 0) m.setattr(terracotta, "__version__", ".".join(map(str, next_major_version))) - # run migration from terracotta import get_driver from terracotta.scripts import cli from terracotta.migrations import MIGRATIONS + if next_major_version[:2] not in [m.up_version for m in MIGRATIONS.values()]: + pytest.skip("No migration available for next major version") + + # run migration runner = CliRunner() result = runner.invoke( cli.cli, ["migrate", str(v07_db), "--from", "v0.7", "--yes"] ) - assert result.exit_code == 0 - - if next_major_version[:2] not in [m.up_version for m in MIGRATIONS.values()]: - assert "Unknown target version" in result.output - return + assert result.exit_code == 0 assert "Upgrade path found" in result.output driver_updated = get_driver(str(v07_db), provider="sqlite") + + # key_names did not exist in v0.7 assert driver_updated.key_names == ("key1", "akey", "key2") diff --git a/tests/server/test_flask_api.py b/tests/server/test_flask_api.py index 3e386653..6a216ad2 100644 --- a/tests/server/test_flask_api.py +++ b/tests/server/test_flask_api.py @@ -1,7 +1,6 @@ from io import BytesIO import json import urllib.parse -from collections import OrderedDict from PIL import Image import numpy as np @@ -94,40 +93,34 @@ def test_get_metadata_nonexisting(client, use_testdb): def test_get_datasets(client, use_testdb): rv = client.get("/datasets") assert rv.status_code == 200 - datasets = json.loads(rv.data, object_pairs_hook=OrderedDict)["datasets"] + datasets = json.loads(rv.data)["datasets"] assert len(datasets) == 4 - assert ( - OrderedDict([("key1", "val11"), ("akey", "x"), ("key2", "val12")]) in datasets - ) + assert dict([("key1", "val11"), ("akey", "x"), ("key2", "val12")]) in datasets def test_get_datasets_pagination(client, use_testdb): # no page (implicit 0) rv = client.get("/datasets?limit=2") assert rv.status_code == 200 - response = json.loads(rv.data, object_pairs_hook=OrderedDict) + response = json.loads(rv.data) assert response["limit"] == 2 assert response["page"] == 0 first_datasets = response["datasets"] assert len(first_datasets) == 2 - assert ( - OrderedDict([("key1", "val11"), ("akey", "x"), ("key2", "val12")]) - in first_datasets - ) + assert dict([("key1", "val11"), ("akey", "x"), ("key2", "val12")]) in first_datasets # second page rv = client.get("/datasets?limit=2&page=1") assert rv.status_code == 200 - response = json.loads(rv.data, object_pairs_hook=OrderedDict) + response = json.loads(rv.data) assert response["limit"] == 2 assert response["page"] == 1 last_datasets = response["datasets"] assert len(last_datasets) == 2 assert ( - OrderedDict([("key1", "val11"), ("akey", "x"), ("key2", "val12")]) - not in last_datasets + dict([("key1", "val11"), ("akey", "x"), ("key2", "val12")]) not in last_datasets ) # page out of range From c94cb2a2bb6c6a8db0238f416260140104496c90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dion=20H=C3=A4fner?= Date: Mon, 15 May 2023 11:48:20 +0200 Subject: [PATCH 4/4] apidoc is now in docs --- docs/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index df48c67b..21b3a04f 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -56,7 +56,7 @@ respectively. This is the best way to find out which API *your* deployment of Terracotta uses. To catch a first glance, -`feel free to explore the API of our demo server `__. +:doc:`feel free to explore the API of this Terracotta version `. Contents