diff --git a/docs/source/plugins.rst b/docs/source/plugins.rst index 4fb9fa1..7f5eb07 100644 --- a/docs/source/plugins.rst +++ b/docs/source/plugins.rst @@ -94,6 +94,10 @@ The below template provides a minimal example (let's call the file ``mycoolvecto }] } + def get_schema(): + # return a `dict` of a JSON schema (inline or reference) + return ('application/geo+json', {'$ref': 'https://geojson.org/schema/Feature.json'}) + For brevity, the above code will always return the single feature of the dataset. In reality, the plugin developer would connect to a data source with capabilities to run queries and return a relevant result set, diff --git a/pygeoapi/api.py b/pygeoapi/api.py index 6a37b19..fd4d9a5 100644 --- a/pygeoapi/api.py +++ b/pygeoapi/api.py @@ -1920,7 +1920,7 @@ class API: LOGGER.debug('Creating item') try: identifier = p.create(request.data) - except ProviderInvalidDataError as err: + except (ProviderInvalidDataError, TypeError) as err: msg = str(err) return self.get_exception( 400, headers, request.format, 'InvalidParameterValue', msg) @@ -1934,7 +1934,7 @@ class API: LOGGER.debug('Updating item') try: _ = p.update(identifier, request.data) - except ProviderGenericError as err: + except (ProviderInvalidDataError, TypeError) as err: msg = str(err) return self.get_exception( 400, headers, request.format, 'InvalidParameterValue', msg) diff --git a/pygeoapi/openapi.py b/pygeoapi/openapi.py index 0f5c932..730d366 100644 --- a/pygeoapi/openapi.py +++ b/pygeoapi/openapi.py @@ -39,7 +39,7 @@ import yaml from pygeoapi import __version__ from pygeoapi import l10n from pygeoapi.plugin import load_plugin -from pygeoapi.provider.base import ProviderTypeError +from pygeoapi.provider.base import ProviderTypeError, SchemaType from pygeoapi.util import (filter_dict_by_key_value, get_provider_by_type, filter_providers_by_type, to_json, yaml_load) @@ -523,19 +523,21 @@ def get_oas_30(cfg): if p.editable: LOGGER.debug('Provider is editable; adding post') + paths[items_path]['post'] = { 'summary': 'Add {} items'.format(title), # noqa 'description': desc, 'tags': [name], 'operationId': 'add{}Features'.format(name.capitalize()), - 'consumes': ['application/json'], - 'produces': ['application/json'], - 'parameters': [{ - 'in': 'body', - 'name': 'body', + 'requestBody': { 'description': 'Adds item to collection', - 'required': True, - }], + 'content': { + 'application/json': { + 'schema': {} + } + }, + 'required': True + }, 'responses': { '201': {'description': 'Successful creation'}, '400': {'$ref': '{}#/components/responses/InvalidParameter'.format(OPENAPI_YAML['oapif'])}, # noqa @@ -543,6 +545,14 @@ def get_oas_30(cfg): } } + try: + schema_ref = p.get_schema(SchemaType.create) + paths[items_path]['post']['requestBody']['content'][schema_ref[0]] = { # noqa + 'schema': schema_ref[1] + } + except Exception as err: + LOGGER.debug(err) + if ptype == 'record': paths[items_path]['get']['parameters'].append( {'$ref': '{}/parameters/q.yaml'.format(OPENAPI_YAML['oapir'])}) # noqa @@ -631,36 +641,58 @@ def get_oas_30(cfg): } } + try: + schema_ref = p.get_schema() + paths['{}/items/{{featureId}}'.format(collection_name_path)]['get']['responses']['200'] = { # noqa + 'content': { + schema_ref[0]: { + 'schema': schema_ref[1] + } + } + } + except Exception as err: + LOGGER.debug(err) + if p.editable: LOGGER.debug('Provider is editable; adding put/delete') - paths['{}/items/{{featureId}}'.format(collection_name_path)]['put'] = { # noqa + put_path = '{}/items/{{featureId}}'.format(collection_name_path) # noqa + paths[put_path]['put'] = { # noqa 'summary': 'Update {} items'.format(title), 'description': desc, 'tags': [name], 'operationId': 'update{}Features'.format(name.capitalize()), # noqa - 'consumes': ['application/json'], - 'produces': ['application/json'], 'parameters': [ {'$ref': '{}#/components/parameters/featureId'.format(OPENAPI_YAML['oapif'])}, # noqa - { - 'in': 'body', - 'name': 'body', - 'description': 'Updates item in collection', - 'required': True, - } ], + 'requestBody': { + 'description': 'Updates item in collection', + 'content': { + 'application/json': { + 'schema': {} + } + }, + 'required': True + }, 'responses': { '204': {'description': 'Successful update'}, '400': {'$ref': '{}#/components/responses/InvalidParameter'.format(OPENAPI_YAML['oapif'])}, # noqa '500': {'$ref': '{}#/components/responses/ServerError'.format(OPENAPI_YAML['oapif'])} # noqa } } + + try: + schema_ref = p.get_schema(SchemaType.replace) + paths[put_path]['put']['requestBody']['content'][schema_ref[0]] = { # noqa + 'schema': schema_ref[1] + } + except Exception as err: + LOGGER.debug(err) + paths['{}/items/{{featureId}}'.format(collection_name_path)]['delete'] = { # noqa 'summary': 'Delete {} items'.format(title), 'description': desc, 'tags': [name], 'operationId': 'delete{}Features'.format(name.capitalize()), # noqa - 'produces': ['application/json'], 'parameters': [ {'$ref': '{}#/components/parameters/featureId'.format(OPENAPI_YAML['oapif'])}, # noqa ], diff --git a/pygeoapi/provider/base.py b/pygeoapi/provider/base.py index 7ab8d42..769b8a0 100644 --- a/pygeoapi/provider/base.py +++ b/pygeoapi/provider/base.py @@ -29,10 +29,18 @@ import json import logging +from enum import Enum LOGGER = logging.getLogger(__name__) +class SchemaType(Enum): + item = 'item' + create = 'create' + update = 'update' + replace = 'replace' + + class BaseProvider: """generic Provider ABC""" @@ -79,6 +87,18 @@ class BaseProvider: raise NotImplementedError() + def get_schema(self, schema_type: SchemaType = SchemaType.item): + """ + Get provider schema model + + :param schema_type: `SchemaType` of schema (default is 'item') + + :returns: tuple pair of `str` of media type and `dict` of schema + (i.e. JSON Schema) + """ + + raise NotImplementedError() + def get_data_path(self, baseurl, urlpath, dirpath): """ Gets directory listing or file description or raw file dump diff --git a/pygeoapi/templates/openapi/swagger.html b/pygeoapi/templates/openapi/swagger.html index fbd4a8c..e2885c4 100644 --- a/pygeoapi/templates/openapi/swagger.html +++ b/pygeoapi/templates/openapi/swagger.html @@ -4,9 +4,9 @@ Swagger UI - {{ config['metadata']['identification']['title'] }} - - - + + +