Skip to content
Open
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
2 changes: 2 additions & 0 deletions docs/source/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,8 @@ default.
storage_crs: http://www.opengis.net/def/crs/OGC/1.3/CRS84 # optional CRS in which data is stored, default: as 'crs' field
storage_crs_coordinate_epoch: 2017.23 # optional, if storage_crs is a dynamic coordinate reference system
always_xy: false # optional should CRS respect axis ordering
validator:
name: path.to.validator # Python path of validation definition
formatters: # list of 1..n formatter definitions
- name: path.to.formatter # Python path of formatter definition
attachment: true # whether or not to provide as an attachment or normal response
Expand Down
2 changes: 1 addition & 1 deletion docs/source/cql2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Queries

The PostgreSQL provider uses `pygeofilter <https://github.com/geopython/pygeofilter>`_ allowing a range of filter expressions, see examples for:

* `Comparison predicates (`Advanced <https://docs.ogc.org/is/21-065r2/21-065r2.html#advanced-comparison-operators>`_, `Case-insensitive <https://docs.ogc.org/is/21-065r2/21-065r2.html#case-insensitive-comparison>`_)
* Comparison predicates (`Advanced <https://docs.ogc.org/is/21-065r2/21-065r2.html#advanced-comparison-operators>`_, `Case-insensitive <https://docs.ogc.org/is/21-065r2/21-065r2.html#case-insensitive-comparison>`_)
* `Spatial predicates <https://docs.ogc.org/is/21-065r2/21-065r2.html#spatial-functions>`_
* `Temporal predicates <https://docs.ogc.org/is/21-065r2/21-065r2.html#temporal-functions>`_

Expand Down
38 changes: 37 additions & 1 deletion docs/source/plugins.rst
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,7 @@ Below is a sample process definition as a Python dictionary:

.. note::

Additional processing plugins can also be found in ``pygeoapi/process``.
Additional processing plugins can be found in ``pygeoapi/process``.

.. _example-custom-pygeoapi-formatter:

Expand Down Expand Up @@ -503,6 +503,42 @@ The below template provides a minimal example (let's call the file ``mycooljsonf

return out_data

Example: custom pygeoapi validator
----------------------------------

Python code
^^^^^^^^^^^

The below template provides a minimal example (let's call the file ``mycooldatavalidator.py``:

.. code-block:: python

from typing import Any

from pygeoapi.validator.base import BaseValidator, ValidatorValidationError

class MyCoolDataValidator(BaseValidator):
def __init__(self, validator_def):
"""Inherit from parent class"""

super().__init__(validator_def)

def validate(self, data: Any, partial: bool = False) -> None:
if partial: # plugin does not support partial updates to a given item (PATCH)
msg = 'Partial validation not supported'
raise ValidatorValidationError(msg)

# data is a dict of incoming data, validate accordingly
if 'some_property' not in data:
msg = 'Invalid data payload!' # to add more detailed messaging, pass user_msg="string of text" to ValidatorValidationError
raise ValidatorValidationError(msg)

def __repr__(self):
return '<MyCoolValidator>'

.. note::

Additional validator plugins can be found in ``pygeoapi/validator``.

Featured plugins
----------------
Expand Down
34 changes: 32 additions & 2 deletions docs/source/transactions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ pygeoapi supports the `OGC API - Features - Part 4: Create, Replace, Update and
for transactional capabilities against feature and record data.

To enable transactions in pygeoapi, a given resource provider needs to be editable (via the configuration resource provider
``editable: true`` property). Note that the feature or record provider MUST support create/update/delete. See the
:ref:`ogcapi-features` and :ref:`ogcapi-records` documentation for transaction support status of pygeoapi backends.
``editable: true`` property). Note that the feature or record provider MUST support create/update/delete. See
:ref:`ogcapi-features` and :ref:`ogcapi-records` for transaction support status of pygeoapi backends.

Access control
^^^^^^^^^^^^^^
Expand All @@ -17,3 +17,33 @@ It should be made clear that authentication and authorization is beyond the resp
if a pygeoapi user enables transactions, they must provide access control explicitly via another service.

.. _`OGC API - Features - Part 4: Create, Replace, Update and Delete`: https://docs.ogc.org/DRAFTS/20-002.html

Validation
^^^^^^^^^^

pygeoapi transaction support includes the option to implement custom validation when adding or updating features or records.

To enable validation in transactions in pygeoapi, a given resource provider can specify a custom validator plugin to implement
custom business rules as needed to ensure data is valid prior to adding or updating a given provider backend.

Given the example below:

.. code-block:: yaml

providers:
- type: feature
name: Elasticsearch
data /path/to/file
id_field: stn_id
editable: true
validator:
name: mycooldatapackage.mycooldatavalidator.MyCoolDataValidator
# name: GeoJSON # shipped with pygeoapi, referred to be a shortname

The ``validator.name`` element refers to one of the following:

* a validator plugin that is shipped with pygeoapi (noting that the core
pygeoapi plugin registry can be found in ``pygeoapi.plugin.PLUGINS``) which can be referred to
via a shortname
* a custom Python module/class that implements a pygeoapi validator plugin. See :ref:`plugins`
for more information on implementing validator plugins.
22 changes: 22 additions & 0 deletions pygeoapi/api/itemtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,28 @@ def manage_collection_item(
HTTPStatus.BAD_REQUEST, headers, request.format,
'InvalidParameterValue', msg)

if action in ['create', 'update']:
if p.validator is not None:
LOGGER.debug('Provider is configured for validation')
LOGGER.debug('Loading validator')
try:
v = load_plugin('validator', {'name': p.validator['name']})
except Exception:
msg = 'Invalid validator configured'
return api.get_exception(
HTTPStatus.INTERNAL_SERVER_ERROR, headers, request.format,
'NoApplicableCode', msg)

LOGGER.debug('Validating item')
try:
v.validate(request.data)
except Exception as err:
msg = err.user_msg or 'Item is not valid, please check and validate payload' # noqa
LOGGER.error(f'Validation errors: {err.message}')
return api.get_exception(
HTTPStatus.INTERNAL_SERVER_ERROR, headers, request.format,
'InvalidParameterValue', msg)

if action == 'create':
LOGGER.debug('Creating item')
try:
Expand Down
3 changes: 3 additions & 0 deletions pygeoapi/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@
'HTTP': 'pygeoapi.pubsub.http.HTTPPubSubClient',
'Kafka': 'pygeoapi.pubsub.kafka.KafkaPubSubClient',
'MQTT': 'pygeoapi.pubsub.mqtt.MQTTPubSubClient'
},
'validator': {
'GeoJSON': 'pygeoapi.validator.geojson.GeoJSONValidator'
}
}

Expand Down
3 changes: 2 additions & 1 deletion pygeoapi/provider/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ def __init__(self, provider_def):
self.include_extra_query_parameters = provider_def.get('include_extra_query_parameters', False) # noqa
self._fields = {}
self.filename = None
self.validator = provider_def.get('validator')

# CRS properties
storage_crs_uri = provider_def.get('storage_crs', DEFAULT_STORAGE_CRS)
Expand Down Expand Up @@ -337,7 +338,7 @@ class ProviderTypeError(ProviderGenericError):

class ProviderInvalidQueryError(ProviderGenericError):
"""provider invalid query error"""
ogc_exception_code = 'InvalidQuery'
ogc_exception_code = 'InvalidParameterValue'
http_status_code = HTTPStatus.BAD_REQUEST
default_msg = "query error"

Expand Down
11 changes: 10 additions & 1 deletion pygeoapi/resources/schemas/config/pygeoapi-config-0.x.yml
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,15 @@ properties:
type: boolean
description: whether to include extra query parameters
default: false
validator:
type: object
description: custom validator to apply on transactions
properties:
name:
type: string
description: name of validator
required:
- name
required:
- type
- name
Expand Down Expand Up @@ -753,4 +762,4 @@ required:
- server
- logging
- metadata
- resources
- resources
Loading
Loading