blob: aa049134fdaf480fca0fe4a4e837f2d05d4959f3 [file] [log] [blame]
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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.
from ..validation import Issue
from .utils import (parse_types_dict_names, report_issue_for_unknown_type,
report_issue_for_parent_is_self, report_issue_for_unknown_parent_type,
report_issue_for_circular_type_hierarchy)
def type_validator(type_name, *types_dict_names):
"""
Makes sure that the field refers to an existing type defined in the root presenter.
The arguments from the second onwards are used to locate a nested field under
``service_template`` under the root presenter. The first of these can optionally be a function,
in which case it will be called to convert type names. This can be used to support shorthand
type names, aliases, etc.
Can be used with the :func:`field_validator` decorator.
"""
types_dict_names, convert = parse_types_dict_names(types_dict_names)
def validator_fn(field, presentation, context):
field.default_validate(presentation, context)
# Make sure type exists
value = getattr(presentation, field.name)
if value is not None:
types_dict = context.presentation.get('service_template', *types_dict_names) or {}
if convert:
value = convert(context, value, types_dict)
if value not in types_dict:
report_issue_for_unknown_type(context, presentation, type_name, field.name)
return validator_fn
def list_type_validator(type_name, *types_dict_names):
"""
Makes sure that the field's elements refer to existing types defined in the root presenter.
Assumes that the field is a list.
The arguments from the second onwards are used to locate a nested field under
``service_template`` under the root presenter. The first of these can optionally be a function,
in which case it will be called to convert type names. This can be used to support shorthand
type names, aliases, etc.
Can be used with the :func:`field_validator` decorator.
"""
types_dict_names, convert = parse_types_dict_names(types_dict_names)
def validator_fn(field, presentation, context):
field.default_validate(presentation, context)
# Make sure types exist
values = getattr(presentation, field.name)
if values is not None:
types_dict = context.presentation.get('service_template', *types_dict_names) or {}
for value in values:
if convert:
value = convert(context, value, types_dict)
if value not in types_dict:
report_issue_for_unknown_type(context, presentation, type_name, field.name)
return validator_fn
def list_length_validator(length):
"""
Makes sure the field has exactly a specific number of elements.
Assumes that the field is a list.
Can be used with the :func:`field_validator` decorator.
"""
def validator_fn(field, presentation, context):
field.default_validate(presentation, context)
# Make sure list has exactly the length
values = getattr(presentation, field.name)
if isinstance(values, list):
if len(values) != length:
context.validation.report('field "%s" does not have exactly %d elements in "%s"'
% (field.name, length, presentation._fullname),
locator=presentation._get_child_locator(field.name),
level=Issue.FIELD)
return validator_fn
def derived_from_validator(*types_dict_names):
"""
Makes sure that the field refers to a valid parent type defined in the root presenter.
Checks that we do not derive from ourselves and that we do not cause a circular hierarchy.
The arguments are used to locate a nested field under ``service_template`` under the root
presenter. The first of these can optionally be a function, in which case it will be called to
convert type names. This can be used to support shorthand type names, aliases, etc.
Can be used with the :func:`field_validator` decorator.
"""
types_dict_names, convert = parse_types_dict_names(types_dict_names)
def validator_fn(field, presentation, context):
field.default_validate(presentation, context)
value = getattr(presentation, field.name)
if value is not None:
types_dict = context.presentation.get('service_template', *types_dict_names) or {}
if convert:
value = convert(context, value, types_dict)
# Make sure not derived from self
if value == presentation._name:
report_issue_for_parent_is_self(context, presentation, field.name)
# Make sure derived from type exists
elif value not in types_dict:
report_issue_for_unknown_parent_type(context, presentation, field.name)
else:
# Make sure derivation hierarchy is not circular
hierarchy = [presentation._name]
presentation_tmp = presentation
while presentation_tmp.derived_from is not None:
derived_from = presentation_tmp.derived_from
if convert:
derived_from = convert(context, derived_from, types_dict)
if derived_from == presentation_tmp._name:
# This should cause a validation issue at that type
break
elif derived_from not in types_dict:
# This should cause a validation issue at that type
break
presentation_tmp = types_dict[derived_from]
if presentation_tmp._name in hierarchy:
report_issue_for_circular_type_hierarchy(context, presentation, field.name)
break
hierarchy.append(presentation_tmp._name)
return validator_fn