From 7bf8ff8adf5ea055d3fd5ece23e169647320d029 Mon Sep 17 00:00:00 2001 From: doublebyte1 Date: Thu, 12 Sep 2024 16:46:51 +0100 Subject: [PATCH 1/2] - initial boiler plate for Styles API --- pygeoapi/api/__init__.py | 5 +- pygeoapi/api/styles.py | 112 +++++++++++++++++++++++++++++++++++++++ pygeoapi/flask_app.py | 9 ++++ 3 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 pygeoapi/api/styles.py diff --git a/pygeoapi/api/__init__.py b/pygeoapi/api/__init__.py index b47541f23..ee4f2a788 100644 --- a/pygeoapi/api/__init__.py +++ b/pygeoapi/api/__init__.py @@ -133,7 +133,7 @@ def all_apis() -> dict: """ from . import (coverages, environmental_data_retrieval, itemtypes, maps, - processes, tiles, stac) + processes, styles, tiles, stac) return { 'coverage': coverages, @@ -141,8 +141,9 @@ def all_apis() -> dict: 'itemtypes': itemtypes, 'map': maps, 'process': processes, + 'style': styles, 'tile': tiles, - 'stac': stac + 'stac': stac, } diff --git a/pygeoapi/api/styles.py b/pygeoapi/api/styles.py new file mode 100644 index 000000000..cfce4bf0c --- /dev/null +++ b/pygeoapi/api/styles.py @@ -0,0 +1,112 @@ +# ================================================================= + +# Authors: Joana Simoes +# +# Copyright (c) 2024 Joana Simoes +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, +# copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following +# conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +# ================================================================= + + +import logging +from http import HTTPStatus +from typing import Tuple + +from pygeoapi.util import to_json + +from . import APIRequest, API, F_JSON, SYSTEM_LOCALE + +LOGGER = logging.getLogger(__name__) + +CONFORMANCE_CLASSES = [ + 'http://www.opengis.net/spec/ogcapi-styles-1/0.0/conf/core', + 'http://www.opengis.net/spec/ogcapi-styles-1/0.0/conf/html', + 'http://www.opengis.net/spec/ogcapi-styles-1/0.0/conf/mapbox-style' +] + + +def get_styles(api: API, request: APIRequest) -> Tuple[dict, int, str]: + """ + Fetches the set of styles available. + For each style it returns the id, a title, links to the stylesheet of the style in each supported encoding, + and the link to the metadata. + + :param request: A request object + + :returns: tuple of headers, status code, content + """ + + format_ = request.format or F_JSON + + # Force response content type and language (en-US only) headers + headers = request.get_response_headers(SYSTEM_LOCALE, **api.api_headers) + + # TODO: implement this + + data = '{"styles": [{"title": "night", "id": "night"}]}' + + if format_ == F_JSON: + headers['Content-Type'] = 'application/json' + return headers, HTTPStatus.OK, to_json(data, api.pretty_print) + else: + return api.get_format_exception(request) + + +def get_oas_30(cfg: dict, locale: str) -> tuple[list[dict[str, str]], dict[str, dict]]: # noqa + """ + Get OpenAPI fragments + + :param cfg: `dict` of configuration + :param locale: `str` of locale + + :returns: `tuple` of `list` of tag objects, and `dict` of path objects + """ + + from pygeoapi.openapi import OPENAPI_YAML + + paths = {} + + paths['/styles'] = { + 'get': { + 'summary': 'lists the available styles', + 'description': 'This operation fetches the set of styles available.', + 'tags': ['Discover and fetch styles'], + 'operationId': 'getStyles', + 'externalDocs': { + 'description': 'The specification that describes this operation: OGC API - Styles (DRAFT)', + 'url': 'https://docs.ogc.org/DRAFTS/20-009.html' + }, + 'parameters': [ + {'$ref': '#/components/parameters/access_token'}, + {'$ref': '#/components/parameters/fStyles'} + ], + 'responses': { + '200': {'$ref': f"{OPENAPI_YAML['oapif-1']}#/components/responses/Features"}, # noqa + '400': {'$ref': f"{OPENAPI_YAML['oapif-1']}#/components/responses/InvalidParameter"}, # noqa + # TODO: add 406 + '500': {'$ref': f"{OPENAPI_YAML['oapif-1']}#/components/responses/ServerError"} # noqa + } + } + } + + return [{'name': 'styles'}], {'paths': paths} \ No newline at end of file diff --git a/pygeoapi/flask_app.py b/pygeoapi/flask_app.py index 4e4f0097e..118d748f6 100644 --- a/pygeoapi/flask_app.py +++ b/pygeoapi/flask_app.py @@ -45,6 +45,7 @@ import pygeoapi.api.processes as processes_api import pygeoapi.api.stac as stac_api import pygeoapi.api.tiles as tiles_api +import pygeoapi.api.styles as styles_api from pygeoapi.openapi import load_openapi_document from pygeoapi.config import get_config from pygeoapi.util import get_mimetype, get_api_rules @@ -522,6 +523,14 @@ def get_collection_edr_query(collection_id, instance_id=None, skip_valid_check=True, ) +@BLUEPRINT.route('/styles') +def styles_api_my_function(): + """ + Get Styles endpoint + :returns: HTTP response + """ + + return execute_from_flask(styles_api.get_styles, request) @BLUEPRINT.route('/stac') def stac_catalog_root(): From 091d62b952874121ba9f412e57a30a69cad42fb9 Mon Sep 17 00:00:00 2001 From: doublebyte1 Date: Thu, 12 Sep 2024 17:07:16 +0100 Subject: [PATCH 2/2] - fixed flake errors --- pygeoapi/api/__init__.py | 2 +- pygeoapi/api/styles.py | 16 ++++++++-------- pygeoapi/flask_app.py | 2 ++ 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/pygeoapi/api/__init__.py b/pygeoapi/api/__init__.py index ee4f2a788..7c56dbf0a 100644 --- a/pygeoapi/api/__init__.py +++ b/pygeoapi/api/__init__.py @@ -143,7 +143,7 @@ def all_apis() -> dict: 'process': processes, 'style': styles, 'tile': tiles, - 'stac': stac, + 'stac': stac, } diff --git a/pygeoapi/api/styles.py b/pygeoapi/api/styles.py index cfce4bf0c..d52223371 100644 --- a/pygeoapi/api/styles.py +++ b/pygeoapi/api/styles.py @@ -47,9 +47,9 @@ def get_styles(api: API, request: APIRequest) -> Tuple[dict, int, str]: """ - Fetches the set of styles available. - For each style it returns the id, a title, links to the stylesheet of the style in each supported encoding, - and the link to the metadata. + Fetches the set of styles available. + For each style it returns the id, a title, links to the stylesheet of the style # noqa + in each supported encoding, and the link to the metadata. :param request: A request object @@ -89,12 +89,12 @@ def get_oas_30(cfg: dict, locale: str) -> tuple[list[dict[str, str]], dict[str, paths['/styles'] = { 'get': { 'summary': 'lists the available styles', - 'description': 'This operation fetches the set of styles available.', + 'description': 'This operation fetches the set of styles available.', # noqa 'tags': ['Discover and fetch styles'], 'operationId': 'getStyles', - 'externalDocs': { - 'description': 'The specification that describes this operation: OGC API - Styles (DRAFT)', - 'url': 'https://docs.ogc.org/DRAFTS/20-009.html' + 'externalDocs': { + 'description': 'The specification that describes this operation: OGC API - Styles (DRAFT)', # noqa + 'url': 'https://docs.ogc.org/DRAFTS/20-009.html' }, 'parameters': [ {'$ref': '#/components/parameters/access_token'}, @@ -109,4 +109,4 @@ def get_oas_30(cfg: dict, locale: str) -> tuple[list[dict[str, str]], dict[str, } } - return [{'name': 'styles'}], {'paths': paths} \ No newline at end of file + return [{'name': 'styles'}], {'paths': paths} diff --git a/pygeoapi/flask_app.py b/pygeoapi/flask_app.py index 118d748f6..293c2db02 100644 --- a/pygeoapi/flask_app.py +++ b/pygeoapi/flask_app.py @@ -523,6 +523,7 @@ def get_collection_edr_query(collection_id, instance_id=None, skip_valid_check=True, ) + @BLUEPRINT.route('/styles') def styles_api_my_function(): """ @@ -532,6 +533,7 @@ def styles_api_my_function(): return execute_from_flask(styles_api.get_styles, request) + @BLUEPRINT.route('/stac') def stac_catalog_root(): """