Jsonschema validation: base schema framework
This patch adds a base jsonschema framework. This patch follows the Nova-Schema-framework: https://github.com/openstack/nova/tree/master/nova/api/validation Partial-Implements: blueprint tosca-csar-mgmt-driver Co-Author: Neha Alhat <neha.alhat@nttdata.com> Change-Id: I6ffeaaffb92fa9949a439a049f3092878b884634
This commit is contained in:
parent
2e461e1223
commit
f519f23638
@ -10,6 +10,7 @@ anyjson>=0.3.3 # BSD
|
||||
Babel!=2.4.0,>=2.3.4 # BSD
|
||||
eventlet!=0.23.0,!=0.25.0,>=0.22.0 # MIT
|
||||
requests>=2.14.2 # Apache-2.0
|
||||
jsonschema>=2.6.0 # MIT
|
||||
keystonemiddleware>=4.17.0 # Apache-2.0
|
||||
kombu!=4.0.2,>=4.0.0 # BSD
|
||||
netaddr>=0.7.18 # BSD
|
||||
|
0
tacker/api/schemas/__init__.py
Normal file
0
tacker/api/schemas/__init__.py
Normal file
50
tacker/api/schemas/vnf_packages.py
Normal file
50
tacker/api/schemas/vnf_packages.py
Normal file
@ -0,0 +1,50 @@
|
||||
# Copyright (C) 2019 NTT DATA
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Schema for vnf packages create API.
|
||||
|
||||
"""
|
||||
|
||||
from tacker.api.validation import parameter_types
|
||||
|
||||
create = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'userDefinedData': parameter_types.keyvalue_pairs
|
||||
},
|
||||
'additionalProperties': False,
|
||||
}
|
||||
|
||||
upload_from_uri = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'addressInformation': {
|
||||
'type': 'string', 'minLength': 0,
|
||||
'maxLength': 2048, 'format': 'uri'
|
||||
},
|
||||
'userName': {
|
||||
'type': 'string', 'maxLength': 255,
|
||||
'pattern': '^[a-zA-Z0-9-_]*$'
|
||||
},
|
||||
'password': {
|
||||
# Allow to specify any string for strong password.
|
||||
'type': 'string', 'maxLength': 255,
|
||||
},
|
||||
|
||||
},
|
||||
'required': ['addressInformation'],
|
||||
'additionalProperties': False,
|
||||
}
|
51
tacker/api/validation/__init__.py
Normal file
51
tacker/api/validation/__init__.py
Normal file
@ -0,0 +1,51 @@
|
||||
# Copyright (C) 2019 NTT DATA
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Request Body validating middleware.
|
||||
|
||||
"""
|
||||
|
||||
import functools
|
||||
import webob
|
||||
|
||||
from tacker.api.validation import validators
|
||||
|
||||
|
||||
def schema(request_body_schema):
|
||||
"""Register a schema to validate request body.
|
||||
|
||||
Registered schema will be used for validating request body just before
|
||||
API method executing.
|
||||
|
||||
:param dict request_body_schema: a schema to validate request body
|
||||
|
||||
"""
|
||||
|
||||
def add_validator(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
schema_validator = validators._SchemaValidator(
|
||||
request_body_schema)
|
||||
try:
|
||||
schema_validator.validate(kwargs['body'])
|
||||
except KeyError:
|
||||
raise webob.exc.HTTPBadRequest(
|
||||
explanation=_("Malformed request body"))
|
||||
|
||||
return func(*args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
return add_validator
|
30
tacker/api/validation/parameter_types.py
Normal file
30
tacker/api/validation/parameter_types.py
Normal file
@ -0,0 +1,30 @@
|
||||
# Copyright (C) 2019 NTT DATA
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Common parameter types for validating request Body.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
keyvalue_pairs = {
|
||||
'type': 'object',
|
||||
'patternProperties': {
|
||||
'^[a-zA-Z0-9-_:. /]{1,255}$': {
|
||||
'type': 'string', 'maxLength': 255
|
||||
}
|
||||
},
|
||||
'additionalProperties': False
|
||||
}
|
105
tacker/api/validation/validators.py
Normal file
105
tacker/api/validation/validators.py
Normal file
@ -0,0 +1,105 @@
|
||||
# Copyright (C) 2019 NTT DATA
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Internal implementation of request Body validating middleware.
|
||||
|
||||
"""
|
||||
|
||||
import jsonschema
|
||||
from jsonschema import exceptions as jsonschema_exc
|
||||
import rfc3986
|
||||
import six
|
||||
|
||||
from tacker.common import exceptions as exception
|
||||
|
||||
|
||||
@jsonschema.FormatChecker.cls_checks('uri')
|
||||
def _validate_uri(instance):
|
||||
return rfc3986.is_valid_uri(instance, require_scheme=True,
|
||||
require_authority=True)
|
||||
|
||||
|
||||
class FormatChecker(jsonschema.FormatChecker):
|
||||
"""A FormatChecker can output the message from cause exception
|
||||
|
||||
We need understandable validation errors messages for users. When a
|
||||
custom checker has an exception, the FormatChecker will output a
|
||||
readable message provided by the checker.
|
||||
"""
|
||||
|
||||
def check(self, param_value, format):
|
||||
"""Check whether the param_value conforms to the given format.
|
||||
|
||||
:argument param_value: the param_value to check
|
||||
:type: any primitive type (str, number, bool)
|
||||
:argument str format: the format that param_value should conform to
|
||||
:raises: :exc:`FormatError` if param_value does not conform to format
|
||||
"""
|
||||
|
||||
if format not in self.checkers:
|
||||
return
|
||||
|
||||
# For safety reasons custom checkers can be registered with
|
||||
# allowed exception types. Anything else will fall into the
|
||||
# default formatter.
|
||||
func, raises = self.checkers[format]
|
||||
result, cause = None, None
|
||||
|
||||
try:
|
||||
result = func(param_value)
|
||||
except raises as e:
|
||||
cause = e
|
||||
if not result:
|
||||
msg = "%r is not a %r" % (param_value, format)
|
||||
raise jsonschema_exc.FormatError(msg, cause=cause)
|
||||
|
||||
|
||||
class _SchemaValidator(object):
|
||||
"""A validator class
|
||||
|
||||
This class is changed from Draft4Validator to validate minimum/maximum
|
||||
value of a string number(e.g. '10'). This changes can be removed when
|
||||
we tighten up the API definition and the XML conversion.
|
||||
Also FormatCheckers are added for checking data formats which would be
|
||||
passed through cinder api commonly.
|
||||
|
||||
"""
|
||||
validator_org = jsonschema.Draft4Validator
|
||||
|
||||
def __init__(self, schema):
|
||||
validator_cls = jsonschema.validators.extend(self.validator_org,
|
||||
validators={})
|
||||
format_checker = FormatChecker()
|
||||
self.validator = validator_cls(schema, format_checker=format_checker)
|
||||
|
||||
def validate(self, *args, **kwargs):
|
||||
try:
|
||||
self.validator.validate(*args, **kwargs)
|
||||
except jsonschema.ValidationError as ex:
|
||||
if len(ex.path) > 0:
|
||||
detail = _("Invalid input for field/attribute %(path)s."
|
||||
" Value: %(value)s. %(message)s") % {
|
||||
'path': ex.path.pop(), 'value': ex.instance,
|
||||
'message': ex.message
|
||||
}
|
||||
else:
|
||||
detail = ex.message
|
||||
raise exception.ValidationError(detail=detail)
|
||||
except TypeError as ex:
|
||||
# NOTE: If passing non string value to patternProperties parameter,
|
||||
# TypeError happens. Here is for catching the TypeError.
|
||||
detail = six.text_type(ex)
|
||||
raise exception.ValidationError(detail=detail)
|
@ -195,3 +195,7 @@ class DuplicateResourceName(TackerException):
|
||||
|
||||
class DuplicateEntity(Conflict):
|
||||
message = _("%(_type)s already exist with given %(entry)s")
|
||||
|
||||
|
||||
class ValidationError(BadRequest):
|
||||
message = "%(detail)s"
|
||||
|
Loading…
Reference in New Issue
Block a user