Skip to content
Permalink
Browse files

Issue 221 wfs3 Probe: OpenAPI spec validator for /api endpoint (#253)

* #221 add OpenAPI Validator Probe for WFS3

* #221 refine OpenAPI Validator Probe for WFS3
  • Loading branch information...
justb4 authored and tomkralidis committed Jun 11, 2019
1 parent cdd5333 commit 9f1818ea29a590d4b3f57f19f01f8f389c491027
Showing with 104 additions and 22 deletions.
  1. +103 −22 GeoHealthCheck/plugins/probe/wfs3.py
  2. +1 −0 requirements.txt
@@ -1,5 +1,6 @@
import requests
from owslib.wfs import WebFeatureService
from openapi_spec_validator import openapi_v3_spec_validator

from GeoHealthCheck.probe import Probe
from GeoHealthCheck.result import Result
@@ -59,8 +60,8 @@ def perform_request(self):
val = api.get(attr, None)
if val is None:
msg = '/api: missing attr: %s' % attr
result = self.add_result(
result, False, msg, 'Test Collection')
result = add_result(
self, result, False, msg, 'Test Collection')
continue

collections = wfs3.collections()
@@ -95,37 +96,37 @@ def perform_request(self):
if val is None:
msg = '%s: missing attr: %s' \
% (coll_name, attr)
result = self.add_result(
result, False, msg, 'Test Collection')
result = add_result(
self, result, False, msg, 'Test Collection')
continue
except Exception as e:
msg = 'GetCollection %s: OWSLib err: %s ' \
% (str(e), coll_name)
result = self.add_result(result,
False, msg, 'Test GetCollection')
result = add_result(
self, result, False, msg, 'Test GetCollection')
continue

try:
items = wfs3.collection_items(coll_name, limit=1)
except Exception as e:
msg = 'GetItems %s: OWSLib err: %s ' % (str(e), coll_name)
result = self.add_result(result,
False, msg, 'Test GetItems')
result = add_result(
self, result, False, msg, 'Test GetItems')
continue

features = items.get('features', None)
if features is None:
msg = 'GetItems %s: No features attr' % coll_name
result = self.add_result(result,
False, msg, 'Test GetItems')
result = add_result(
self, result, False, msg, 'Test GetItems')
continue

type = items.get('type', '')
if type != 'FeatureCollection':
msg = '%s:%s type not FeatureCollection: %s' \
% (coll_name, type, val)
result = self.add_result(
result, False, msg, 'Test GetItems')
result = add_result(
self, result, False, msg, 'Test GetItems')
continue

if len(items['features']) > 0:
@@ -136,8 +137,8 @@ def perform_request(self):
except Exception as e:
msg = 'GetItem %s: OWSLib err: %s' \
% (str(e), coll_name)
result = self.add_result(result,
False, msg, 'Test GetItem')
result = add_result(
self, result, False, msg, 'Test GetItem')
continue

for attr in \
@@ -146,15 +147,15 @@ def perform_request(self):
if val is None:
msg = '%s:%s missing attr: %s' \
% (coll_name, str(fid), attr)
result = self.add_result(
result, False, msg, 'Test GetItem')
result = add_result(
self, result, False, msg, 'Test GetItem')
continue

if attr == 'type' and val != 'Feature':
msg = '%s:%s type not Feature: %s' \
% (coll_name, str(fid), val)
result = self.add_result(
result, False, msg, 'Test GetItem')
result = add_result(
self, result, False, msg, 'Test GetItem')
continue

except Exception as err:
@@ -166,13 +167,93 @@ def perform_request(self):
# Add to overall Probe result
self.result.add_result(result)

def add_result(self, result, val, msg, new_result_name):
result.set(val, msg)

class WFS3OpenAPIValidator(Probe):
"""
Probe for WFS3 OpenAPI Spec Validation (/api endpoint).
Uses https://pypi.org/project/openapi-spec-validator/.
"""

NAME = 'WFS3 OpenAPI Validator'
DESCRIPTION = 'Validates WFS3 /api endpoint for OpenAPI compliance'
RESOURCE_TYPE = 'OGC:WFS3'

REQUEST_METHOD = 'GET'

"""Param defs"""

def __init__(self):
Probe.__init__(self)

def perform_request(self):
"""
Perform the validation.
Uses https://github.com/p1c2u/openapi-spec-validator on
the specfile (dict) returned from the OpenAPI endpoint.
"""

# Step 1 basic sanity check
result = Result(True, 'OpenAPI Validation Test')
result.start()
api_doc = None
try:
wfs3 = WebFeatureService(self._resource.url, version='3.0')

# TODO: OWSLib 0.17.1 has no call to '/api yet.
# Build endpoint URL (may have f=json etc)
api_url = wfs3._build_url('api')

# Get OpenAPI spec from endpoint as dict once
api_doc = requests.get(api_url).json()

# Basic sanity check
for attr in ['components', 'paths', 'openapi']:
val = api_doc.get(attr, None)
if val is None:
msg = '/api: missing attr: %s' % attr
result.set(False, msg)
break
except Exception as err:
result.set(False, str(err))

result.stop()
self.result.add_result(result)
result = Result(True, new_result_name)

# No use to proceed if OpenAPI basics not complied
if api_doc is None or result.success is False:
return

# ASSERTION: /api exists, next OpenAPI Validation

# Step 2 detailed OpenAPI Compliance test
result = Result(True, 'Validate OpenAPI Compliance')
result.start()
return result
try:
# Call the openapi-spec-validator and iterate through errors
errors_iterator = openapi_v3_spec_validator.iter_errors(api_doc)
for error in errors_iterator:
# Add each validation error as separate Result object
result = add_result(
self, result, False,
str(error), 'OpenAPI Compliance Result')
except Exception as err:
result.set(False, 'OpenAPI Validation err: e=%s' % str(err))

result.stop()

# Add to overall Probe result
self.result.add_result(result)


# Util to quickly add Results and open new one.
def add_result(obj, result, val, msg, new_result_name):
result.set(val, msg)
result.stop()
obj.result.add_result(result)
result = Result(True, new_result_name)
result.start()
return result

# class WFS3Caps(Probe):
# """Probe for OGC WFS3 API main endpoint url"""
@@ -8,6 +8,7 @@ Flask-SQLAlchemy==2.1
itsdangerous==1.1.0
pyproj==1.9.6
OWSLib==0.17.1
openapi-spec-validator==0.2.7
Sphinx==1.8.4
sphinx-rtd-theme==0.4.3
requests>=2.20.0

0 comments on commit 9f1818e

Please sign in to comment.
You can’t perform that action at this time.