c382f036c3
Validate the validation by validating our schemas against the JSON Schema meta schema. This is an important first step in getting us of JSON Schema Draft 4 and onto Draft 2019-09, which OpenAPI is a superset of. Change-Id: I3b5a05aa0aa058e92c6927c9e3bee3cdd4477f8f Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
123 lines
4.5 KiB
Python
123 lines
4.5 KiB
Python
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import jsonschema.exceptions
|
|
|
|
from nova.api.openstack import compute
|
|
from nova.api.validation import validators
|
|
from nova import test
|
|
|
|
|
|
class SchemaTest(test.NoDBTestCase):
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.router = compute.APIRouterV21()
|
|
self.meta_schema = validators._SchemaValidator.validator_org
|
|
|
|
def test_schemas(self):
|
|
missing_schemas = set()
|
|
invalid_schemas = set()
|
|
|
|
def _validate_func(func, method):
|
|
if method in ("POST", "PUT", "PATCH"):
|
|
# request body validation
|
|
if not hasattr(func, '_request_schema'):
|
|
missing_schemas.add(func.__qualname__)
|
|
else:
|
|
try:
|
|
self.meta_schema.check_schema(func._request_schema)
|
|
except jsonschema.exceptions.SchemaError:
|
|
invalid_schemas.add(func.__qualname__)
|
|
|
|
for route in self.router.map.matchlist:
|
|
if 'controller' not in route.defaults:
|
|
continue
|
|
|
|
controller = route.defaults['controller']
|
|
|
|
path = ""
|
|
for part in route.routelist:
|
|
if isinstance(part, dict):
|
|
path += "{" + part["name"] + "}"
|
|
else:
|
|
path += part
|
|
|
|
method = (
|
|
route.conditions.get("method", "GET")[0]
|
|
if route.conditions
|
|
else "GET"
|
|
)
|
|
action = route.defaults["action"]
|
|
|
|
if path.endswith('/action'):
|
|
# all actions should use POST
|
|
assert method == 'POST'
|
|
|
|
wsgi_actions = [
|
|
(k, v, controller.controller) for k, v in
|
|
controller.controller.wsgi_actions.items()
|
|
]
|
|
for sub_controller in controller.sub_controllers:
|
|
wsgi_actions += [
|
|
(k, v, sub_controller) for k, v in
|
|
sub_controller.wsgi_actions.items()
|
|
]
|
|
|
|
for (
|
|
wsgi_action, wsgi_method, action_controller
|
|
) in wsgi_actions:
|
|
func = controller.wsgi_actions[wsgi_action]
|
|
|
|
if hasattr(action_controller, 'versioned_methods'):
|
|
if wsgi_method in action_controller.versioned_methods:
|
|
# currently all our actions are unversioned and if
|
|
# this changes then we need to fix this
|
|
funcs = action_controller.versioned_methods[
|
|
wsgi_method
|
|
]
|
|
assert len(funcs) == 1
|
|
func = funcs[0].func
|
|
|
|
# method will always be POST for actions
|
|
_validate_func(func, method)
|
|
else:
|
|
# body validation
|
|
versioned_methods = getattr(
|
|
controller.controller, 'versioned_methods', {}
|
|
)
|
|
if action in versioned_methods:
|
|
# versioned method
|
|
for versioned_method in sorted(
|
|
versioned_methods[action],
|
|
key=lambda v: v.start_version
|
|
):
|
|
func = versioned_method.func
|
|
|
|
_validate_func(func, method)
|
|
else:
|
|
# unversioned method
|
|
func = getattr(controller.controller, action)
|
|
_validate_func(func, method)
|
|
|
|
if missing_schemas:
|
|
raise test.TestingException(
|
|
f"Found API resources without schemas: "
|
|
f"{sorted(missing_schemas)}"
|
|
)
|
|
|
|
if invalid_schemas:
|
|
raise test.TestingException(
|
|
f"Found API resources with invalid schemas: "
|
|
f"{sorted(invalid_schemas)}"
|
|
)
|