diff options
Diffstat (limited to 'onap-client/onap_client/resource.py')
-rw-r--r-- | onap-client/onap_client/resource.py | 189 |
1 files changed, 189 insertions, 0 deletions
diff --git a/onap-client/onap_client/resource.py b/onap-client/onap_client/resource.py new file mode 100644 index 0000000..0af0fad --- /dev/null +++ b/onap-client/onap_client/resource.py @@ -0,0 +1,189 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +import inspect + +from abc import ABC, abstractmethod +from onap_client.exceptions import InvalidSpecException + + +class Resource(ABC): + resource_name = "abstract" + spec = {} + + def __init__(self, input): + self.attributes = {} + + attributes = self._create(input) + self.resolve_attributes(attributes) + + self._post_create() + + def __getattr__(self, attr): + return self.attributes.get(attr, None) + + @abstractmethod + def _create(self, input): + pass + + @abstractmethod + def _post_create(self): + pass + + @abstractmethod + def _submit(self): + pass + + @classmethod + def validate(cls, input, spec=None): + """Validates that an input dictionary spec + is valid according to a provided class spec. + + Recursively walksdown and checks if all required attributes are present, and + attribute types match spec types. + + Returns complete spec with all attributes. + """ + valid_spec = {} + + if not isinstance(input, dict): + raise InvalidSpecException("input spec was not a dictionary") + + if not spec: + spec = cls.spec + + for k, v in input.items(): + if not spec.get(k): + raise InvalidSpecException("Unknown property found: {}".format(k)) + + for k, v in spec.items(): + property_name = k + property_type = v.get("type") + property_required = v.get("required") + property_default = v.get("default", default_empty_value(property_type)) + + input_property = validate_property( + input, property_name, property_required, property_default, property_type + ) + + if ( + property_type == dict + and input_property != property_default + and v.get("nested") + ): + property_value = cls.validate(input_property, v.get("nested")) + elif property_type == list: + list_property_type = v.get("list_item") + list_spec = [] + for item in input_property: + if type(item) != list_property_type: + raise InvalidSpecException( + "list item {} not match type {}".format( + item, list_property_type + ) + ) + if list_property_type == str: + list_spec.insert(0, item) + else: + list_spec.insert(0, cls.validate(item, v.get("nested", {}))) + + property_value = list_spec + else: + property_value = input_property + + valid_spec[property_name] = property_value + + return valid_spec + + @classmethod + def create_from_spec(cls, spec, submit=True): + input_args = [] + + arguments = inspect.getfullargspec(cls).args + arguments.pop(0) + + for argument in arguments: + input_args.append(spec.get(argument)) + + instance = cls(*input_args) + + if submit: + instance._submit() + + return instance + + def resolve_attributes(self, attributes): + for key, val in attributes.items(): + self.attributes[key] = val + + def print(self): + for k, v in self.attributes.items(): + val = str(v) + value = val[:50] + "..." if len(val) > 50 else val + print("{}: {}".format(k, value)) + + +def validate_property( + input_spec, property_name, property_required, property_default, property_type +): + input_property = input_spec.get(property_name) + if not input_property: + if property_required: + raise InvalidSpecException( + "required property {} not found in input spec".format(property_name) + ) + else: + input_property = property_default + elif type(input_property) != property_type: + raise InvalidSpecException( + "input property {} not match type {}".format(property_name, property_type) + ) + + return input_property + + +def default_empty_value(property_type): + if property_type == str: + return None + elif property_type == list: + return [] + elif property_type == dict: + return {} + elif property_type == bool: + return False + else: + return None |