From b903632bf3daffd69404dd26e5181e2fb93b706a Mon Sep 17 00:00:00 2001 From: ajnisbet Date: Mon, 19 Feb 2024 15:01:04 -0800 Subject: [PATCH] Support preflight requests --- docs/changelog.md | 5 ++++- opentopodata/api.py | 34 +++++++++++++++++++++++----------- tests/test_api.py | 9 +++++++++ 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index b0d8cf9..ae5ac32 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -3,7 +3,10 @@ This is a list of changes to Open Topo Data between each release. -## Version 1.8.4 (18 Aug 2023) +## Version 1.8.4 (19 Feb 2024) +* Dependency upgrades +* Fix handling of preflight requests ([#93](https://github.com/ajnisbet/opentopodata/issues/93)) + ## Version 1.8.3 (7 Feb 2023) diff --git a/opentopodata/api.py b/opentopodata/api.py index a7e1f5e..7f85f8c 100644 --- a/opentopodata/api.py +++ b/opentopodata/api.py @@ -1,7 +1,7 @@ import logging import os -from flask import Flask, jsonify, request +from flask import Flask, jsonify, request, Response from flask_caching import Cache import polyline @@ -64,6 +64,18 @@ def _load_config_memcache(): return config.load_config() +@app.before_request +def handle_preflight(): + # If before_request returns a non-none value, the regular view isn't run. + # after_request() does still run though, so the CORS header and OTD version + # will be set correctly there. + if request.method == "OPTIONS": + response = Response(status=204) + response.headers["access-control-allow-methods"] = "GET,POST,OPTIONS,HEAD" + response.headers["access-control-allow-headers"] = "content-type,x-api-key" + return response + + @app.after_request def apply_cors(response): """Set CORs header. @@ -84,6 +96,16 @@ def apply_cors(response): return response +@app.after_request +def add_version(response): + if "version" not in _SIMPLE_CACHE: + with open(VERSION_PATH) as f: + version = f.read().strip() + _SIMPLE_CACHE["version"] = version + response.headers["x-opentopodata-version"] = _SIMPLE_CACHE["version"] + return response + + class ClientError(ValueError): """Invalid input data. @@ -543,13 +565,3 @@ def get_elevation(dataset_name): app.logger.error(e) msg = "Unhandled server error, see server logs for details." return jsonify({"status": "SERVER_ERROR", "error": msg}), 500 - - -@app.after_request -def add_version(response): - if "version" not in _SIMPLE_CACHE: - with open(VERSION_PATH) as f: - version = f.read().strip() - _SIMPLE_CACHE["version"] = version - response.headers["x-opentopodata-version"] = _SIMPLE_CACHE["version"] - return response diff --git a/tests/test_api.py b/tests/test_api.py index d363504..9a2d90f 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -41,6 +41,15 @@ def test_no_cors(self, patch_config): response = test_api.get(url) assert response.headers.get("access-control-allow-origin") is None + def test_options(self): + test_api = api.app.test_client() + url = "/" + response = test_api.options(url) + assert response.status_code == 204 + assert "x-opentopodata-version" in response.headers + assert "access-control-allow-methods" in response.headers + assert response.headers.get("access-control-allow-origin") == "*" + class TestFindRequestAgument: def test_no_argument(self, patch_config):