From ef5236bba15f3aa70e137207dd1a8979e61c127a Mon Sep 17 00:00:00 2001 From: Rangababu-R <72373312+Rangababu-R@users.noreply.github.com> Date: Mon, 13 Dec 2021 14:39:23 +0530 Subject: [PATCH 01/33] Update common.py --- openapiart/common.py | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/openapiart/common.py b/openapiart/common.py index e40061f1..ea6e1cfd 100644 --- a/openapiart/common.py +++ b/openapiart/common.py @@ -218,6 +218,7 @@ class OpenApiValidator(object): __slots__ = () def __init__(self): + self._validation_errors = [] pass def validate_mac(self, mac): @@ -358,7 +359,8 @@ def types_validation(self, value, type_, err_msg, itemtype=None, min=None, max=N else: verdict = v_obj(value) if verdict is False: - raise TypeError(err_msg) + self._validation_errors.append(err_msg) + # raise TypeError(err_msg) class OpenApiObject(OpenApiBase, OpenApiValidator): @@ -506,12 +508,13 @@ def _validate_required(self): if getattr(self, "_REQUIRED", None) is None: return for name in self._REQUIRED: - if getattr(self, name, None) is None: + if self._properties.get(name) is None: msg = "{} is a mandatory property of {}" " and should not be set to None".format( name, self.__class__, ) - raise ValueError(msg) + # raise ValueError(msg) + self._validation_errors.append(msg) def _validate_types(self, property_name, property_value): common_data_types = [list, str, int, float, bool] @@ -523,7 +526,10 @@ def _validate_types(self, property_name, property_value): return if "enum" in details and property_value not in details["enum"]: msg = "property {} shall be one of these" " {} enum, but got {} at {}" - raise TypeError(msg.format(property_name, details["enum"], property_value, self.__class__)) + self._validation_errors.append( + msg.format(property_name, details["enum"], property_value, self.__class__) + ) + # raise TypeError(msg.format(property_name, details["enum"], property_value, self.__class__)) if details["type"] in common_data_types and "format" not in details: msg = "property {} shall be of type {} at {}".format(property_name, details["type"], self.__class__) self.types_validation(property_value, details["type"], msg, details.get("itemtype"), details.get("minimum"), details.get("maximum"), @@ -536,7 +542,10 @@ def _validate_types(self, property_name, property_value): object_class = getattr(module, class_name) if not isinstance(property_value, object_class): msg = "property {} shall be of type {}," " but got {} at {}" - raise TypeError(msg.format(property_name, class_name, type(property_value), self.__class__)) + self._validation_errors.append( + msg.format(property_name, class_name, type(property_value), self.__class__) + ) + # raise TypeError(msg.format(property_name, class_name, type(property_value), self.__class__)) if "format" in details: msg = "Invalid {} format, expected {} at {}".format(property_value, details["format"], self.__class__) _type = details["type"] if details["type"] is list else details["format"] @@ -544,9 +553,12 @@ def _validate_types(self, property_name, property_value): details.get("minLength"), details.get("maxLength")) def validate(self): - self._validate_required() - for key, value in self._properties.items(): - self._validate_types(key, value) + self.serialize() + if self._validation_errors > 0: + raise Exception("\n".join(self._validation_errors)) + # self._validate_required() + # for key, value in self._properties.items(): + # self._validate_types(key, value) def get(self, name, with_default=False): """ @@ -649,6 +661,12 @@ def append(self, item): def clear(self): del self._items[:] self._index = -1 + + def set(self, index, item): + if isinstance(item, OpenApiObject) is False: + raise Exception("Item is not an instance of OpenApiObject") + self._items[index] = item + return self def _encode(self): return [item._encode() for item in self._items] From 90355214ba440305b586d09f37e172e882fc8215 Mon Sep 17 00:00:00 2001 From: Rangababu-R <72373312+Rangababu-R@users.noreply.github.com> Date: Wed, 15 Dec 2021 15:40:43 +0530 Subject: [PATCH 02/33] clubbed validation error fixes --- openapiart/common.py | 55 ++++++++++++++++++++++---------- openapiart/tests/test_add.py | 4 +-- openapiart/tests/test_formats.py | 24 +++++++------- openapiart/tests/test_func.py | 25 ++++++++++----- 4 files changed, 68 insertions(+), 40 deletions(-) diff --git a/openapiart/common.py b/openapiart/common.py index ea6e1cfd..39159433 100644 --- a/openapiart/common.py +++ b/openapiart/common.py @@ -216,11 +216,17 @@ def _decode(self, dict_object): class OpenApiValidator(object): __slots__ = () + _validation_errors = [] def __init__(self): - self._validation_errors = [] pass + def _append_error(self, msg): + self._validation_errors.append(msg) + + def _get_validation_errors(self): + return self._validation_errors + def validate_mac(self, mac): if mac is None or not isinstance(mac, (str, unicode)) or mac.count(" ") != 0: return False @@ -359,8 +365,14 @@ def types_validation(self, value, type_, err_msg, itemtype=None, min=None, max=N else: verdict = v_obj(value) if verdict is False: - self._validation_errors.append(err_msg) + self._append_error(err_msg) # raise TypeError(err_msg) + + def _raise_validation(self): + errors = "\n".join(self._validation_errors) + if len(self._get_validation_errors()) > 0: + self._validation_errors.clear() + raise Exception(errors) class OpenApiObject(OpenApiBase, OpenApiValidator): @@ -399,16 +411,24 @@ def _has_choice(self, name): return True else: return False + + def _is_enum_valid(self, name, value): + if name in self._TYPES and "enum" in self._TYPES[name]: + if value in self._TYPES[name]["enum"]: + return True + else: + return False + return True def _get_property(self, name, default_value=None, parent=None, choice=None): if name in self._properties and self._properties[name] is not None: return self._properties[name] if isinstance(default_value, type) is True: self._set_choice(name) - if "_choice" in default_value.__slots__: - self._properties[name] = default_value(parent=parent, choice=choice) - else: - self._properties[name] = default_value(parent=parent) + # if "_choice" in default_value.__slots__: + # self._properties[name] = default_value(parent=parent, choice=choice) + # else: + self._properties[name] = default_value(parent=parent) if "_DEFAULTS" in dir(self._properties[name]) and "choice" in self._properties[name]._DEFAULTS: getattr(self._properties[name], self._properties[name]._DEFAULTS["choice"]) else: @@ -424,8 +444,11 @@ def _set_property(self, name, value, choice=None): self._set_choice(name) self._properties[name] = self._DEFAULTS[name] else: - self._set_choice(name) - self._properties[name] = value + if not self._is_enum_valid(name, value): + self._append_error("{} is not a valid enum for property {}".format(value, name)) + else: + self._set_choice(name) + self._properties[name] = value if self._parent is not None and self._choice is not None and value is not None: self._parent._set_property("choice", self._choice) @@ -441,6 +464,7 @@ def _encode(self): if key in self._TYPES and "format" in self._TYPES[key] and self._TYPES[key]["format"] == "int64": value = str(value) output[key] = value + self._raise_validation() return output def _decode(self, obj): @@ -450,7 +474,7 @@ def _decode(self, obj): if isinstance(property_value, dict): child = self._get_child_class(property_name) if "choice" in child[1]._TYPES and "_parent" in child[1].__slots__: - property_value = child[1](self, property_name)._decode(property_value) + property_value = child[1](self)._decode(property_value) elif "_parent" in child[1].__slots__: property_value = child[1](self)._decode(property_value) else: @@ -471,6 +495,7 @@ def _decode(self, obj): self._properties[property_name] = property_value self._validate_types(property_name, property_value) self._validate_required() + self._raise_validation() return self def _get_child_class(self, property_name, is_property_list=False): @@ -514,7 +539,7 @@ def _validate_required(self): self.__class__, ) # raise ValueError(msg) - self._validation_errors.append(msg) + self._append_error(msg) def _validate_types(self, property_name, property_value): common_data_types = [list, str, int, float, bool] @@ -526,7 +551,7 @@ def _validate_types(self, property_name, property_value): return if "enum" in details and property_value not in details["enum"]: msg = "property {} shall be one of these" " {} enum, but got {} at {}" - self._validation_errors.append( + self._append_error( msg.format(property_name, details["enum"], property_value, self.__class__) ) # raise TypeError(msg.format(property_name, details["enum"], property_value, self.__class__)) @@ -542,7 +567,7 @@ def _validate_types(self, property_name, property_value): object_class = getattr(module, class_name) if not isinstance(property_value, object_class): msg = "property {} shall be of type {}," " but got {} at {}" - self._validation_errors.append( + self._append_error( msg.format(property_name, class_name, type(property_value), self.__class__) ) # raise TypeError(msg.format(property_name, class_name, type(property_value), self.__class__)) @@ -554,11 +579,7 @@ def _validate_types(self, property_name, property_value): def validate(self): self.serialize() - if self._validation_errors > 0: - raise Exception("\n".join(self._validation_errors)) - # self._validate_required() - # for key, value in self._properties.items(): - # self._validate_types(key, value) + self._raise_validation() def get(self, name, with_default=False): """ diff --git a/openapiart/tests/test_add.py b/openapiart/tests/test_add.py index eff880ee..f1a58e76 100644 --- a/openapiart/tests/test_add.py +++ b/openapiart/tests/test_add.py @@ -12,8 +12,8 @@ def test_add(api): assert config.f.f_b == config.f._DEFAULTS["f_b"] g1 = config.g.add(name="unique list name", g_a="dkdkd", g_b=3, g_c=22.2) g1.g_d = "gdgdgd" - j = config.j.add() - j.j_b.f_a = "a" + jval = config.j.add() + jval.j_b.f_a = "a" print(config) assert config.g[0].choice == "g_d" yaml = config.serialize(encoding=config.YAML) diff --git a/openapiart/tests/test_formats.py b/openapiart/tests/test_formats.py index 80e36de7..8818f0ec 100644 --- a/openapiart/tests/test_formats.py +++ b/openapiart/tests/test_formats.py @@ -20,10 +20,8 @@ def test_formats_bad_string(config, value): config.l.string_param = value try: config.deserialize(config.serialize(encoding=config.YAML)) - pytest.fail( - "Value {value} was successfully validated".format(value=value) - ) - except TypeError: + pytest.fail("Value {value} was successfully validated".format(value)) + except Exception: pass @@ -33,7 +31,7 @@ def test_formats_bad_integer(config, value): try: config.deserialize(config.serialize(encoding=config.YAML)) pytest.fail("Value {} was successfully validated".format(value)) - except TypeError: + except Exception: pass @@ -43,7 +41,7 @@ def test_formats_integer_to_be_removed(config, value): config.l.integer = value config.deserialize(config.serialize(encoding=config.YAML)) pytest.fail("Value {} was successfully validated".format(value)) - except TypeError: + except Exception: pass @@ -52,7 +50,7 @@ def test_formats_good_ipv4(config, value): config.l.ipv4 = value try: config.deserialize(config.serialize(encoding=config.YAML)) - except TypeError: + except Exception as e: pytest.fail("Value {} was not valid".format(value)) @@ -75,7 +73,7 @@ def test_formats_bad_ipv4(config, value): try: config.deserialize(config.serialize(encoding=config.YAML)) pytest.fail("Value {} was successfully validated".format(value)) - except TypeError: + except Exception: pass @@ -85,7 +83,7 @@ def test_formats_ipv4_to_be_removed(config, value): config.l.ipv4 = value config.deserialize(config.serialize(encoding=config.YAML)) pytest.fail("Value {} was successfully validated".format(value)) - except TypeError: + except Exception: pass @@ -108,7 +106,7 @@ def test_formats_bad_ipv6(config, value): try: config.deserialize(config.serialize(encoding=config.YAML)) pytest.fail("Value {} was successfully validated".format(value)) - except TypeError: + except Exception: pass @@ -130,7 +128,7 @@ def test_formats_bad_mac(config, value): try: config.deserialize(config.serialize(encoding=config.YAML)) pytest.fail("Value {} was successfully validated".format(value)) - except TypeError: + except Exception: pass @@ -142,7 +140,7 @@ def test_formats_bad_hex(config, value): try: config.deserialize(config.serialize(encoding=config.YAML)) pytest.fail("Value {} was successfully validated".format(value)) - except TypeError: + except Exception: pass @@ -152,7 +150,7 @@ def test_string_length(config, value): try: config.deserialize(config.serialize(encoding=config.YAML)) pytest.fail("Value {} was successfully validated".format(value)) - except TypeError: + except Exception: pass diff --git a/openapiart/tests/test_func.py b/openapiart/tests/test_func.py index d2a34bbe..f5f59a2f 100644 --- a/openapiart/tests/test_func.py +++ b/openapiart/tests/test_func.py @@ -20,7 +20,7 @@ def test_required(api): config.mandatory config.mandatory.serialize() pytest.fail("config got validated") - except ValueError: + except Exception: pass @@ -103,7 +103,7 @@ def test_x_pattern_ipv4_good_and_bad_list(default_config, ipv4): try: default_config.ipv4_pattern.serialize(default_config.DICT) pytest.fail("ipv4 values got serialize") - except TypeError as e: + except Exception as e: if "['-255.-255.-255.-255']" not in str(e): pytest.fail("Invalid ipv4 list is not proper in error message") @@ -114,7 +114,7 @@ def test_x_pattern_ipv6_good_and_bad_list(default_config, ipv6): try: default_config.ipv6_pattern.serialize(default_config.DICT) pytest.fail("ipv6 values got serialize") - except TypeError as e: + except Exception as e: if "[':', 'abcd::abcd::']" not in str(e): pytest.fail("Invalid ipv6 list is not proper in error message") @@ -125,7 +125,7 @@ def test_x_pattern_mac_good_and_bad_list(default_config, mac): try: default_config.mac_pattern.serialize(default_config.DICT) pytest.fail("mac values got serialize") - except TypeError as e: + except Exception as e: if "[':', 'abcd::abcd::']" not in str(e): pytest.fail("Invalid mac list is not proper in error message") @@ -138,7 +138,7 @@ def test_x_pattern_integer_good_and_bad_list(default_config, integer): try: default_config.integer_pattern.serialize(default_config.DICT) pytest.fail("integer values got serialize") - except TypeError as e: + except Exception as e: if "['abcd::abcd::', 256, 'ab:ab:ab:ab:ab:ab']" not in str(e): pytest.fail("Invalid integer list is not proper in error message") @@ -159,7 +159,7 @@ def test_x_pattern_good_inc_dec(default_config, index, direction): dir_obj.count = count[index] try: default_config.serialize(default_config.DICT) - except TypeError: + except Exception as e: pytest.fail("%s with %s Failed to serialize" % (enum, direction)) @@ -180,7 +180,7 @@ def test_x_pattern_bad_inc_dec(default_config, index, direction): try: default_config.serialize(default_config.DICT) pytest.fail("%s with %s got serialized" % (enum, direction)) - except TypeError as e: + except Exception as e: print(e) @@ -196,9 +196,18 @@ def test_int_64_format(api, default_config): conf.integer64 = "2000" try: conf.validate() - except TypeError as e: + except Exception as e: + print(e) + + +def test_enum_setter(api, default_config): + default_config.response = "abc" + try: + default_config.validate() + except Exception as e: print(e) if __name__ == "__main__": pytest.main(["-v", "-s", __file__]) + From c9a0695dfb95815a9fc5d4ed7c86831064ae2303 Mon Sep 17 00:00:00 2001 From: Rangababu-R <72373312+Rangababu-R@users.noreply.github.com> Date: Thu, 16 Dec 2021 12:36:19 +0530 Subject: [PATCH 03/33] Update openapiartgo.py --- openapiart/openapiartgo.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openapiart/openapiartgo.py b/openapiart/openapiartgo.py index bbb90c24..29b9bbf5 100644 --- a/openapiart/openapiartgo.py +++ b/openapiart/openapiartgo.py @@ -265,6 +265,7 @@ def _write_common_code(self): self._write('import "github.com/ghodss/yaml"') self._write('import "google.golang.org/protobuf/encoding/protojson"') self._write('import "google.golang.org/protobuf/proto"') + self._write('import "google.golang.org/grpc/credentials/insecure"') go_pkg_fp = self._fp go_pkg_filename = self._filename self._filename = os.path.normpath( @@ -593,7 +594,8 @@ def _build_api_interface(self): if api.grpcClient == nil {{ ctx, cancelFunc := context.WithTimeout(context.Background(), api.grpc.dialTimeout) defer cancelFunc() - conn, err := grpc.DialContext(ctx, api.grpc.location, grpc.WithTransportCredentials(insecure.NewCredentials())) + conn, err := grpc.DialContext( + ctx, api.grpc.location, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil {{ return err }} From c296e93a6434c2b96b63b7d10b88e497643c520d Mon Sep 17 00:00:00 2001 From: Rangababu-R <72373312+Rangababu-R@users.noreply.github.com> Date: Thu, 16 Dec 2021 15:38:11 +0530 Subject: [PATCH 04/33] validation tests and fixes --- openapiart/common.py | 21 ++++++++--- openapiart/tests/test_py_go_diff.py | 56 +++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 4 deletions(-) create mode 100644 openapiart/tests/test_py_go_diff.py diff --git a/openapiart/common.py b/openapiart/common.py index 39159433..d5d30367 100644 --- a/openapiart/common.py +++ b/openapiart/common.py @@ -547,7 +547,7 @@ def _validate_types(self, property_name, property_value): # raise ValueError("Invalid Property {}".format(property_name)) return details = self._TYPES[property_name] - if property_value is None and property_name not in self._DEFAULTS and property_name not in self._REQUIRED: + if property_value is None: return if "enum" in details and property_value not in details["enum"]: msg = "property {} shall be one of these" " {} enum, but got {} at {}" @@ -572,13 +572,26 @@ def _validate_types(self, property_name, property_value): ) # raise TypeError(msg.format(property_name, class_name, type(property_value), self.__class__)) if "format" in details: - msg = "Invalid {} format, expected {} at {}".format(property_value, details["format"], self.__class__) + msg = "Invalid {} format on property {}, expected {} at {}".format( + property_value, property_name, details["format"], self.__class__ + ) _type = details["type"] if details["type"] is list else details["format"] self.types_validation(property_value, _type, msg, details["format"], details.get("minimum"), details.get("maximum"), details.get("minLength"), details.get("maxLength")) - def validate(self): - self.serialize() + def validate(self, skip_exception=False): + self._validate_required() + for key, value in self._properties.items(): + if isinstance(value, OpenApiObject): + value.validate(True) + elif isinstance(value, OpenApiIter): + for item in value: + if not isinstance(item, OpenApiObject): + continue + item.validate(True) + self._validate_types(key, value) + if skip_exception: + return self._validation_errors self._raise_validation() def get(self, name, with_default=False): diff --git a/openapiart/tests/test_py_go_diff.py b/openapiart/tests/test_py_go_diff.py new file mode 100644 index 00000000..de6c47fb --- /dev/null +++ b/openapiart/tests/test_py_go_diff.py @@ -0,0 +1,56 @@ +import importlib +import pytest +module = importlib.import_module("sanity") + + +def test_iter_set_method(default_config): + default_config.j.add() + default_config.j.set(0, module.JObject()) + assert len(default_config.j) == 1 + # below 2 statements shall raise exception need to modify generator + # if exception is raised, it would conflict with below mentioned + # comments about flow.packet + default_config.j.append(module.FObject()) + default_config.j.set(1, module.FObject()) + + default_config.j.set(1, module.JObject()) + # below statement is a replica of snappi flow.packet Iter + # indexing will return protocol header obj instead of FlowHeader + # which is actually set with set or append or add methods + assert isinstance(default_config.j[1], module.EObject) + + +def test_validation_errors(): + p = module.Api().prefix_config() + p.e + try: + p.validate() + pytest.fail + except Exception as e: + assert "a is a mandatory property" in str(e) + assert "b is a mandatory property" in str(e) + assert "c is a mandatory property" in str(e) + assert "required_object is a mandatory property" in str(e) + assert "e_a is a mandatory property" in str(e) + assert "e_b is a mandatory property" in str(e) + + p.e.e_a = "abc" + try: + p.validate() + except Exception as e: + assert "Invalid abc format on property e_a" in str(e) + p.a = "abc" + p.b = 10.1 + p.c = 20 + p.required_object.e_a = 10.1 + p.required_object.e_b = 20 + p.j.add().j_a + errors = p.validate(True) + assert len([True for e in errors if "e_b is a mandatory property" in e]) == 2 + + +def test_enum_setter(): + p = module.Api().prefix_config() + p.response = "abc" + errors = p.validate(True) + assert "abc is not a valid enum for property response" in errors From 86a291ad56ac61517ce2ce36add5ece471c831a1 Mon Sep 17 00:00:00 2001 From: Rangababu-R <72373312+Rangababu-R@users.noreply.github.com> Date: Tue, 21 Dec 2021 15:53:57 +0530 Subject: [PATCH 05/33] added Clone Support --- openapiart/openapiartgo.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/openapiart/openapiartgo.py b/openapiart/openapiartgo.py index 29b9bbf5..e295e2b9 100644 --- a/openapiart/openapiartgo.py +++ b/openapiart/openapiartgo.py @@ -1060,6 +1060,17 @@ def _write_interface(self, new): return err.Error() }} return str + func (obj *{struct}) Clone() ({interface}, error) {{ + newObj := New{interface}() + json, err := obj.ToJson() + if err != nil {{ + return nil, err + }} + jErr := newObj.FromJson(json) + if jErr != nil {{ + return nil, jErr + }} + return newObj, nil }} """.format( struct=new.struct, @@ -1099,6 +1110,8 @@ def _write_interface(self, new): "Validate() error", "// A stringer function", "String() string", + "// Clones the object", + "Clone() ({interface}, error)", "validateFromText() error", "validateObj(set_default bool)", "setDefault()", From 289deac15a783e1869e5226a84a4d583245ea428 Mon Sep 17 00:00:00 2001 From: Rangababu-R Date: Thu, 6 Jan 2022 13:22:11 +0530 Subject: [PATCH 06/33] iter validation --- openapiart/common.py | 9 +++++---- openapiart/generator.py | 4 ++++ openapiart/tests/test_py_go_diff.py | 21 +++++++++++---------- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/openapiart/common.py b/openapiart/common.py index d5d30367..18285039 100644 --- a/openapiart/common.py +++ b/openapiart/common.py @@ -687,8 +687,7 @@ def append(self, item): """Append an item to the end of OpenApiIter TBD: type check, raise error on mismatch """ - if isinstance(item, OpenApiObject) is False: - raise Exception("Item is not an instance of OpenApiObject") + self._instanceOf(item) self._add(item) return self @@ -697,8 +696,7 @@ def clear(self): self._index = -1 def set(self, index, item): - if isinstance(item, OpenApiObject) is False: - raise Exception("Item is not an instance of OpenApiObject") + self._instanceOf(item) self._items[index] = item return self @@ -724,3 +722,6 @@ def __str__(self): def __eq__(self, other): return self.__str__() == other.__str__() + + def _instanceOf(self, item): + raise NotImplementedError("validating an OpenApiIter object is not supported") diff --git a/openapiart/generator.py b/openapiart/generator.py index 33ea52d9..0e45b6bd 100644 --- a/openapiart/generator.py +++ b/openapiart/generator.py @@ -1069,6 +1069,10 @@ def _write_openapilist_special_methods( self._write(1, "def next(self):") self._write(2, "# type: () -> %s" % contained_class_name) self._write(2, "return self._next()") + self._write() + self._write(1, "def _instanceOf(self, item):") + self._write(2, "if not isinstance(item, %s):" % (contained_class_name)) + self._write(3, "raise Exception(\"Item is not an instance of %s\")" % (contained_class_name)) def _write_factory_method( self, diff --git a/openapiart/tests/test_py_go_diff.py b/openapiart/tests/test_py_go_diff.py index de6c47fb..9889c35b 100644 --- a/openapiart/tests/test_py_go_diff.py +++ b/openapiart/tests/test_py_go_diff.py @@ -7,17 +7,18 @@ def test_iter_set_method(default_config): default_config.j.add() default_config.j.set(0, module.JObject()) assert len(default_config.j) == 1 - # below 2 statements shall raise exception need to modify generator - # if exception is raised, it would conflict with below mentioned - # comments about flow.packet - default_config.j.append(module.FObject()) - default_config.j.set(1, module.FObject()) + try: + default_config.j.append(module.FObject()) + pytest.fail("appending an invalid object is not throwing exception") + except Exception: + pass + try: + default_config.j.set(0, module.FObject()) + pytest.fail("setting an invalid object is not throwing exception") + except Exception: + pass - default_config.j.set(1, module.JObject()) - # below statement is a replica of snappi flow.packet Iter - # indexing will return protocol header obj instead of FlowHeader - # which is actually set with set or append or add methods - assert isinstance(default_config.j[1], module.EObject) + assert isinstance(default_config.j[0], module.EObject) def test_validation_errors(): From c801fe21910a874b9d1e0dacdbd949a57df2a47b Mon Sep 17 00:00:00 2001 From: Rangababu-R Date: Thu, 6 Jan 2022 13:30:43 +0530 Subject: [PATCH 07/33] skip_excep made private --- openapiart/common.py | 9 ++++++--- openapiart/tests/test_py_go_diff.py | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/openapiart/common.py b/openapiart/common.py index 18285039..984dff76 100644 --- a/openapiart/common.py +++ b/openapiart/common.py @@ -579,20 +579,23 @@ def _validate_types(self, property_name, property_value): self.types_validation(property_value, _type, msg, details["format"], details.get("minimum"), details.get("maximum"), details.get("minLength"), details.get("maxLength")) - def validate(self, skip_exception=False): + def _validate(self, skip_exception=False): self._validate_required() for key, value in self._properties.items(): if isinstance(value, OpenApiObject): - value.validate(True) + value._validate(True) elif isinstance(value, OpenApiIter): for item in value: if not isinstance(item, OpenApiObject): continue - item.validate(True) + item._validate(True) self._validate_types(key, value) if skip_exception: return self._validation_errors self._raise_validation() + + def validate(self): + return self._validate() def get(self, name, with_default=False): """ diff --git a/openapiart/tests/test_py_go_diff.py b/openapiart/tests/test_py_go_diff.py index 9889c35b..4b19175d 100644 --- a/openapiart/tests/test_py_go_diff.py +++ b/openapiart/tests/test_py_go_diff.py @@ -46,12 +46,12 @@ def test_validation_errors(): p.required_object.e_a = 10.1 p.required_object.e_b = 20 p.j.add().j_a - errors = p.validate(True) + errors = p._validate(True) assert len([True for e in errors if "e_b is a mandatory property" in e]) == 2 def test_enum_setter(): p = module.Api().prefix_config() p.response = "abc" - errors = p.validate(True) + errors = p._validate(True) assert "abc is not a valid enum for property response" in errors From 647ea8ee0e5b83eae492f3dbb686eb009e86d4e9 Mon Sep 17 00:00:00 2001 From: Rangababu-R <72373312+Rangababu-R@users.noreply.github.com> Date: Wed, 17 Nov 2021 10:53:43 +0530 Subject: [PATCH 08/33] Error message change for validation --- openapiart/openapiartgo.py | 86 +++++++++++++++++----------------- pkg/generated_required_test.go | 66 ++++++++++++++++++-------- pkg/unit_test.go | 9 ++-- 3 files changed, 94 insertions(+), 67 deletions(-) diff --git a/openapiart/openapiartgo.py b/openapiart/openapiartgo.py index e295e2b9..ab41185a 100644 --- a/openapiart/openapiartgo.py +++ b/openapiart/openapiartgo.py @@ -79,6 +79,7 @@ def isOptional(self, property_name): class FluentField(object): def __init__(self): self.name = None + self.schema_name = None self.description = None self.getter_method_description = None self.setter_method_description = None @@ -958,7 +959,7 @@ def _write_interface(self, new): return retObj }} {nil_call} - vErr := obj.validateFromText() + vErr := obj.validateFromText("{struct}.FromPbText()") if vErr != nil {{ return vErr }} @@ -1000,7 +1001,7 @@ def _write_interface(self, new): uError.Error(), "\\u00a0", " ", -1)[7:]) }} {nil_call} - vErr := obj.validateFromText() + vErr := obj.validateFromText("{struct}.FromYaml()") if vErr != nil {{ return vErr }} @@ -1037,20 +1038,20 @@ def _write_interface(self, new): uError.Error(), "\\u00a0", " ", -1)[7:]) }} {nil_call} - err := obj.validateFromText() + err := obj.validateFromText("{struct}.FromJson()") if err != nil {{ return err }} return nil }} - func (obj *{struct}) validateFromText() error {{ - obj.validateObj(true) + func (obj *{struct}) validateFromText(path string) error {{ + obj.validateObj(true, path) return validationResult() }} func (obj *{struct}) Validate() error {{ - obj.validateObj(false) + obj.validateObj(false, "{struct}.Validate()") return validationResult() }} @@ -1112,8 +1113,8 @@ def _write_interface(self, new): "String() string", "// Clones the object", "Clone() ({interface}, error)", - "validateFromText() error", - "validateObj(set_default bool)", + "validateFromText(path string) error", + "validateObj(set_default bool, path string)", "setDefault()", ] for field in new.interface_fields: @@ -1714,6 +1715,7 @@ def _build_setters_getters(self, fluent_new): field.schema = property_schema field.description = self._get_description(property_schema) field.name = self._get_external_field_name(property_name) + field.schema_name = property_name field.type = self._get_struct_field_type(property_schema, field) if ( len(choice_enums) == 1 @@ -1774,9 +1776,8 @@ def _build_setters_getters(self, fluent_new): if field.type == "[]byte": field.name = "Bytes" elif "$ref" in property_schema: - schema_name = self._get_schema_object_name_from_ref( - property_schema["$ref"] - ) + schema_name = self._get_schema_object_name_from_ref(property_schema["$ref"]) + field.schema_name = schema_name field.name = self._get_external_struct_name(schema_name) field.isOptional = fluent_new.isOptional(property_name) field.isPointer = ( @@ -1978,10 +1979,11 @@ def _validate_types(self, new, field): body = """ // {name} is required if obj.obj.{name}{enum} == {value} {{ - validation = append(validation, "{name} is required field on interface {interface}") + validation = append(validation, + fmt.Sprintf("{intf_name}.{field_name} is a required field from path %s", path)) }} """.format( - name=field.name, - interface=new.interface, + name=field.name, intf_name=new.schema_name, + field_name=field.schema_name, value=0 if field.isEnum and field.isArray is False else value, enum=".Number()" if field.isEnum and field.isArray is False @@ -2005,15 +2007,14 @@ def _validate_types(self, new, field): + """ {{ validation = append( validation, - fmt.Sprintf("{min} <= {interface}.{name} <= {max} but Got {form}", {pointer}{value})) + fmt.Sprintf( + "{interface}.{name} shall be in the range of [{min}, {max}] but Got {form} from path %s", {pointer}{value}, path)) }} """ ).format( - name=field.name, - interface=new.interface, - max="max({})".format(field.type.lstrip("[]")) - if field.max is None - else field.max, + name=field.schema_name, + interface=new.schema_name, + max="max({})".format(field.type.lstrip("[]")) if field.max is None else field.max, pointer="*" if field.isPointer else "", min="min({})".format(field.type.lstrip("[]")) if field.min is None @@ -2045,16 +2046,14 @@ def _validate_types(self, new, field): validation = append( validation, fmt.Sprintf( - "{min_length} <= length of {interface}.{name} <= {max_length} but Got %d", - len({pointer}{value}))) + "length of {interface}.{name} shall be in the range of [{min_length}, {max_length}] but Got %d from path %s", + len({pointer}{value}), path)) }} """ ).format( - name=field.name, - interface=new.interface, - max_length="any" - if field.max_length is None - else field.max_length, + name=field.schema_name, + interface=new.schema_name, + max_length="any" if field.max_length is None else field.max_length, pointer="*" if field.isPointer else "", min_length=field.min_length if field.min_length is None @@ -2082,15 +2081,15 @@ def _validate_types(self, new, field): inner_body = """ err := validate{format}(obj.{name}()) if err != nil {{ - validation = append(validation, fmt.Sprintf("%s %s", err.Error(), "on {interface}.{name}")) + validation = append(validation, + fmt.Sprintf("%s %s from path %s", err.Error(), "on {interface}.{field_name}", path)) }} """.format( - name=field.name, - interface=new.interface, - format=field.format.capitalize() - if field.isArray is False - else field.format.capitalize() + "Slice", - ) + name=field.name, interface=new.schema_name, + field_name=field.schema_name, + format=field.format.capitalize() if field.isArray is False + else field.format.capitalize() + "Slice", + ) # Enum will be handled via wrapper lib if inner_body == "": return body @@ -2109,13 +2108,14 @@ def _validate_struct(self, new, field): body = """ // {name} is required if obj.obj.{name} == nil {{ - validation = append(validation, "{name} is required field on interface {interface}") + validation = append( + validation, + fmt.Sprintf("{interface}.{field_name} is a required field from path %s", path)) }} - """.format( - name=field.name, interface=new.interface - ) + """.format(name=field.name, interface=new.schema_name, field_name=field.schema_name) - inner_body = "obj.{external_name}().validateObj(set_default)".format( + inner_body = """obj.{external_name}().validateObj( + set_default, fmt.Sprintf("%s.{external_name}", path))""".format( external_name=self._get_external_struct_name(field.name) ) if field.isArray: @@ -2125,9 +2125,9 @@ def _validate_struct(self, new, field): for _, item := range obj.obj.{name} {{ obj.{name}().appendHolderSlice(&{field_internal_struct}{{obj: item}}) }} - }} - for _, item := range obj.{name}().Items() {{ - item.validateObj(set_default) + }} + for ind, item := range obj.{name}().Items() {{ + item.validateObj(set_default, fmt.Sprintf("%s.{name}[%d]", path, ind)) }} """.format( name=field.name, @@ -2178,7 +2178,7 @@ def p(): p() body = "\n".join(statements) self._write( - """func (obj *{struct}) validateObj(set_default bool) {{ + """func (obj *{struct}) validateObj(set_default bool, path string) {{ if set_default {{ obj.setDefault() }} diff --git a/pkg/generated_required_test.go b/pkg/generated_required_test.go index d891c9d2..9b41db0e 100644 --- a/pkg/generated_required_test.go +++ b/pkg/generated_required_test.go @@ -20,11 +20,16 @@ func TestPrefixConfigRequired(t *testing.T) { data, _ := opts.Marshal(object.Msg()) err := object.FromJson(string(data)) err1 := object.FromYaml(string(data)) - protoMarshal, _ := proto.Marshal(object.Msg()) - err2 := object.FromPbText(string(protoMarshal)) - assert.Contains(t, err.Error(), "RequiredObject", "A", "B", "C") - assert.Contains(t, err1.Error(), "RequiredObject", "A", "B", "C") - assert.Contains(t, err2.Error(), "RequiredObject", "A", "B", "C") + str, _ := proto.Marshal(object.Msg()) + err2 := object.FromPbText(string(str)) + assert.Contains(t, err.Error(), "PrefixConfig.required_object") + assert.Contains(t, err.Error(), "PrefixConfig.a") + assert.Contains(t, err1.Error(), "PrefixConfig.required_object") + assert.Contains(t, err1.Error(), "PrefixConfig.a") + assert.Contains(t, err2.Error(), "PrefixConfig.required_object") + assert.Contains(t, err2.Error(), "PrefixConfig.a") + // assert.Contains(t, err2.Error(), "PrefixConfig.b") + // assert.Contains(t, err2.Error(), "PrefixConfig.c") } // func TestEObjectRequired(t *testing.T) { @@ -54,11 +59,11 @@ func TestMandateRequired(t *testing.T) { data, _ := opts.Marshal(object.Msg()) err := object.FromJson(string(data)) err1 := object.FromYaml(string(data)) - protoMarshal, _ := proto.Marshal(object.Msg()) - err2 := object.FromPbText(string(protoMarshal)) - assert.Contains(t, err.Error(), "RequiredParam") - assert.Contains(t, err1.Error(), "RequiredParam") - assert.Contains(t, err2.Error(), "RequiredParam") + str, _ := proto.Marshal(object.Msg()) + err2 := object.FromPbText(string(str)) + assert.Contains(t, err.Error(), "Mandate.required_param") + assert.Contains(t, err1.Error(), "Mandate.required_param") + assert.Contains(t, err2.Error(), "Mandate.required_param") } func TestMObjectRequired(t *testing.T) { object := openapiart.NewMObject() @@ -71,11 +76,32 @@ func TestMObjectRequired(t *testing.T) { data, _ := opts.Marshal(object.Msg()) err := object.FromJson(string(data)) err1 := object.FromYaml(string(data)) - protoMarshal, _ := proto.Marshal(object.Msg()) - err2 := object.FromPbText(string(protoMarshal)) - assert.Contains(t, err.Error(), "StringParam", "Integer", "Float", "Double", "Mac", "Ipv4", "Ipv6", "Hex") - assert.Contains(t, err1.Error(), "StringParam", "Integer", "Float", "Double", "Mac", "Ipv4", "Ipv6", "Hex") - assert.Contains(t, err2.Error(), "StringParam", "Integer", "Float", "Double", "Mac", "Ipv4", "Ipv6", "Hex") + str, _ := proto.Marshal(object.Msg()) + err2 := object.FromPbText(string(str)) + assert.Contains(t, err.Error(), "MObject.string") + // assert.Contains(t, err.Error(), "MObject.integer") + assert.Contains(t, err.Error(), "MObject.ipv4") + assert.Contains(t, err.Error(), "MObject.mac") + // assert.Contains(t, err.Error(), "MObject.float") + // assert.Contains(t, err.Error(), "MObject.double") + assert.Contains(t, err.Error(), "MObject.ipv6") + assert.Contains(t, err.Error(), "MObject.hex") + assert.Contains(t, err1.Error(), "MObject.string") + // assert.Contains(t, err1.Error(), "MObject.integer") + assert.Contains(t, err1.Error(), "MObject.ipv4") + assert.Contains(t, err1.Error(), "MObject.mac") + // assert.Contains(t, err1.Error(), "MObject.float") + // assert.Contains(t, err1.Error(), "MObject.double") + assert.Contains(t, err1.Error(), "MObject.ipv6") + assert.Contains(t, err1.Error(), "MObject.hex") + assert.Contains(t, err2.Error(), "MObject.string") + // assert.Contains(t, err2.Error(), "MObject.integer") + assert.Contains(t, err2.Error(), "MObject.ipv4") + assert.Contains(t, err2.Error(), "MObject.mac") + // assert.Contains(t, err2.Error(), "MObject.float") + // assert.Contains(t, err2.Error(), "MObject.double") + assert.Contains(t, err2.Error(), "MObject.ipv6") + assert.Contains(t, err2.Error(), "MObject.hex") } func TestPortMetricRequired(t *testing.T) { object := openapiart.NewPortMetric() @@ -88,9 +114,9 @@ func TestPortMetricRequired(t *testing.T) { data, _ := opts.Marshal(object.Msg()) err := object.FromJson(string(data)) err1 := object.FromYaml(string(data)) - protoMarshal, _ := proto.Marshal(object.Msg()) - err2 := object.FromPbText(string(protoMarshal)) - assert.Contains(t, err.Error(), "Name", "TxFrames", "RxFrames") - assert.Contains(t, err1.Error(), "Name", "TxFrames", "RxFrames") - assert.Contains(t, err2.Error(), "Name", "TxFrames", "RxFrames") + str, _ := proto.Marshal(object.Msg()) + err2 := object.FromPbText(string(str)) + assert.Contains(t, err.Error(), "PortMetric.name") + assert.Contains(t, err1.Error(), "PortMetric.name") + assert.Contains(t, err2.Error(), "PortMetric.name") } diff --git a/pkg/unit_test.go b/pkg/unit_test.go index f27c1ea8..8bd1222c 100644 --- a/pkg/unit_test.go +++ b/pkg/unit_test.go @@ -670,7 +670,7 @@ func TestRequiredField(t *testing.T) { mandate := openapiart.NewMandate() err := mandate.Validate() assert.NotNil(t, err) - assert.Contains(t, err.Error(), "RequiredParam is required field") + assert.Contains(t, err.Error(), "Mandate.required_param is a required field") } func TestOptionalDefault(t *testing.T) { @@ -757,7 +757,7 @@ func TestFromJsonToCleanObject(t *testing.T) { }` err1 := config.FromJson(new_json1) assert.NotNil(t, err1) - assert.Contains(t, err1.Error(), "A is required field") + assert.Contains(t, err1.Error(), ".a is a required field") } func TestChoiceStale(t *testing.T) { @@ -980,7 +980,8 @@ func TestStringLengthError(t *testing.T) { config.Name() err := config.Validate() if assert.Error(t, err) { - assert.Contains(t, strings.ToLower(err.Error()), "3 <= length of prefixconfig.strlen <= 6 but got 8") + assert.Contains(t, err.Error(), + "length of PrefixConfig.str_len shall be in the range of [3, 6] but Got 8 from path prefixConfig.Validate()") } } @@ -1015,7 +1016,7 @@ func TestMObjectValidation(t *testing.T) { mObject := openapiart.NewMObject() err := mObject.Validate() if assert.Error(t, err) { - assert.Contains(t, strings.ToLower(err.Error()), "required field on interface mobject") + assert.Contains(t, err.Error(), "required field from path mObject.Validate()") } } From 6dd2248600ff316f85c3ad9d70bcce210b7ba466 Mon Sep 17 00:00:00 2001 From: Rangababu-R <72373312+Rangababu-R@users.noreply.github.com> Date: Wed, 17 Nov 2021 11:02:10 +0530 Subject: [PATCH 09/33] Update openapiartgo.py --- openapiart/openapiartgo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openapiart/openapiartgo.py b/openapiart/openapiartgo.py index ab41185a..9a1d9e7a 100644 --- a/openapiart/openapiartgo.py +++ b/openapiart/openapiartgo.py @@ -2127,7 +2127,7 @@ def _validate_struct(self, new, field): }} }} for ind, item := range obj.{name}().Items() {{ - item.validateObj(set_default, fmt.Sprintf("%s.{name}[%d]", path, ind)) + item.validateObj(set_default, fmt.Sprintf("%s.{name}().Items()[%d]", path, ind)) }} """.format( name=field.name, From 6e3c22b29adb9129111a519cba8a3f8e348daa46 Mon Sep 17 00:00:00 2001 From: Rangababu-R <72373312+Rangababu-R@users.noreply.github.com> Date: Thu, 18 Nov 2021 15:57:20 +0530 Subject: [PATCH 10/33] lower case --- openapiart/common.go | 22 +++++++++++----------- pkg/common.go | 22 +++++++++++----------- pkg/unit_test.go | 10 +++++----- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/openapiart/common.go b/openapiart/common.go index 2a6b99f5..f7f3267c 100644 --- a/openapiart/common.go +++ b/openapiart/common.go @@ -164,13 +164,13 @@ func validationResult() error { func validateMac(mac string) error { macSlice := strings.Split(mac, ":") if len(macSlice) != 6 { - return fmt.Errorf(fmt.Sprintf("Invalid Mac address %s", mac)) + return fmt.Errorf(fmt.Sprintf("invalid mac address %s", mac)) } octInd := []string{"0th", "1st", "2nd", "3rd", "4th", "5th"} for ind, val := range macSlice { num, err := strconv.ParseUint(val, 16, 32) if err != nil || num > 255 { - return fmt.Errorf(fmt.Sprintf("Invalid Mac address at %s octet in %s mac", octInd[ind], mac)) + return fmt.Errorf(fmt.Sprintf("invalid mac address at %s octet in %s mac", octInd[ind], mac)) } } return nil @@ -179,13 +179,13 @@ func validateMac(mac string) error { func validateIpv4(ip string) error { ipSlice := strings.Split(ip, ".") if len(ipSlice) != 4 { - return fmt.Errorf(fmt.Sprintf("Invalid Ipv4 address %s", ip)) + return fmt.Errorf(fmt.Sprintf("invalid ipv4 address %s", ip)) } octInd := []string{"1st", "2nd", "3rd", "4th"} for ind, val := range ipSlice { num, err := strconv.ParseUint(val, 10, 32) if err != nil || num > 255 { - return fmt.Errorf(fmt.Sprintf("Invalid Ipv4 address at %s octet in %s ipv4", octInd[ind], ip)) + return fmt.Errorf(fmt.Sprintf("invalid ipv4 address at %s octet in %s ipv4", octInd[ind], ip)) } } return nil @@ -196,13 +196,13 @@ func validateIpv6(ip string) error { if strings.Count(ip, " ") > 0 || strings.Count(ip, ":") > 7 || strings.Count(ip, "::") > 1 || strings.Count(ip, ":::") > 0 || strings.Count(ip, ":") == 0 { - return fmt.Errorf(fmt.Sprintf("Invalid ipv6 address %s", ip)) + return fmt.Errorf(fmt.Sprintf("invalid ipv6 address %s", ip)) } if (string(ip[0]) == ":" && string(ip[:2]) != "::") || (string(ip[len(ip)-1]) == ":" && string(ip[len(ip)-2:]) != "::") { - return fmt.Errorf(fmt.Sprintf("Invalid ipv6 address %s", ip)) + return fmt.Errorf(fmt.Sprintf("invalid ipv6 address %s", ip)) } if strings.Count(ip, "::") == 0 && strings.Count(ip, ":") != 7 { - return fmt.Errorf(fmt.Sprintf("Invalid ipv6 address %s", ip)) + return fmt.Errorf(fmt.Sprintf("invalid ipv6 address %s", ip)) } if ip == "::" { return nil @@ -224,7 +224,7 @@ func validateIpv6(ip string) error { for ind, val := range ipSlice { num, err := strconv.ParseUint(val, 16, 64) if err != nil || num > 65535 { - return fmt.Errorf(fmt.Sprintf("Invalid Ipv6 address at %s octet in %s ipv6", octInd[ind], ip)) + return fmt.Errorf(fmt.Sprintf("invalid ipv6 address at %s octet in %s ipv6", octInd[ind], ip)) } } @@ -234,7 +234,7 @@ func validateIpv6(ip string) error { func validateHex(hex string) error { matched, err := regexp.MatchString(`^[0-9a-fA-F]+$|^0[x|X][0-9a-fA-F]+$`, hex) if err != nil || !matched { - return fmt.Errorf(fmt.Sprintf("Invalid hex value %s", hex)) + return fmt.Errorf(fmt.Sprintf("invalid hex value %s", hex)) } return nil } @@ -253,7 +253,7 @@ func validateSlice(valSlice []string, sliceType string) error { } else if sliceType == "hex" { err = validateHex(val) } else { - return fmt.Errorf(fmt.Sprintf("Invalid slice type received <%s>", sliceType)) + return fmt.Errorf(fmt.Sprintf("invalid slice type received <%s>", sliceType)) } if err != nil { @@ -262,7 +262,7 @@ func validateSlice(valSlice []string, sliceType string) error { } if len(indices) > 0 { return fmt.Errorf( - fmt.Sprintf("Invalid %s addresses at indices %s", sliceType, strings.Join(indices, ",")), + fmt.Sprintf("invalid %s addresses at indices %s", sliceType, strings.Join(indices, ",")), ) } return nil diff --git a/pkg/common.go b/pkg/common.go index c7eadf1a..7423611d 100644 --- a/pkg/common.go +++ b/pkg/common.go @@ -167,13 +167,13 @@ func validationResult() error { func validateMac(mac string) error { macSlice := strings.Split(mac, ":") if len(macSlice) != 6 { - return fmt.Errorf(fmt.Sprintf("Invalid Mac address %s", mac)) + return fmt.Errorf(fmt.Sprintf("invalid mac address %s", mac)) } octInd := []string{"0th", "1st", "2nd", "3rd", "4th", "5th"} for ind, val := range macSlice { num, err := strconv.ParseUint(val, 16, 32) if err != nil || num > 255 { - return fmt.Errorf(fmt.Sprintf("Invalid Mac address at %s octet in %s mac", octInd[ind], mac)) + return fmt.Errorf(fmt.Sprintf("invalid mac address at %s octet in %s mac", octInd[ind], mac)) } } return nil @@ -182,13 +182,13 @@ func validateMac(mac string) error { func validateIpv4(ip string) error { ipSlice := strings.Split(ip, ".") if len(ipSlice) != 4 { - return fmt.Errorf(fmt.Sprintf("Invalid Ipv4 address %s", ip)) + return fmt.Errorf(fmt.Sprintf("invalid ipv4 address %s", ip)) } octInd := []string{"1st", "2nd", "3rd", "4th"} for ind, val := range ipSlice { num, err := strconv.ParseUint(val, 10, 32) if err != nil || num > 255 { - return fmt.Errorf(fmt.Sprintf("Invalid Ipv4 address at %s octet in %s ipv4", octInd[ind], ip)) + return fmt.Errorf(fmt.Sprintf("invalid ipv4 address at %s octet in %s ipv4", octInd[ind], ip)) } } return nil @@ -199,13 +199,13 @@ func validateIpv6(ip string) error { if strings.Count(ip, " ") > 0 || strings.Count(ip, ":") > 7 || strings.Count(ip, "::") > 1 || strings.Count(ip, ":::") > 0 || strings.Count(ip, ":") == 0 { - return fmt.Errorf(fmt.Sprintf("Invalid ipv6 address %s", ip)) + return fmt.Errorf(fmt.Sprintf("invalid ipv6 address %s", ip)) } if (string(ip[0]) == ":" && string(ip[:2]) != "::") || (string(ip[len(ip)-1]) == ":" && string(ip[len(ip)-2:]) != "::") { - return fmt.Errorf(fmt.Sprintf("Invalid ipv6 address %s", ip)) + return fmt.Errorf(fmt.Sprintf("invalid ipv6 address %s", ip)) } if strings.Count(ip, "::") == 0 && strings.Count(ip, ":") != 7 { - return fmt.Errorf(fmt.Sprintf("Invalid ipv6 address %s", ip)) + return fmt.Errorf(fmt.Sprintf("invalid ipv6 address %s", ip)) } if ip == "::" { return nil @@ -227,7 +227,7 @@ func validateIpv6(ip string) error { for ind, val := range ipSlice { num, err := strconv.ParseUint(val, 16, 64) if err != nil || num > 65535 { - return fmt.Errorf(fmt.Sprintf("Invalid Ipv6 address at %s octet in %s ipv6", octInd[ind], ip)) + return fmt.Errorf(fmt.Sprintf("invalid ipv6 address at %s octet in %s ipv6", octInd[ind], ip)) } } @@ -237,7 +237,7 @@ func validateIpv6(ip string) error { func validateHex(hex string) error { matched, err := regexp.MatchString(`^[0-9a-fA-F]+$|^0[x|X][0-9a-fA-F]+$`, hex) if err != nil || !matched { - return fmt.Errorf(fmt.Sprintf("Invalid hex value %s", hex)) + return fmt.Errorf(fmt.Sprintf("invalid hex value %s", hex)) } return nil } @@ -256,7 +256,7 @@ func validateSlice(valSlice []string, sliceType string) error { } else if sliceType == "hex" { err = validateHex(val) } else { - return fmt.Errorf(fmt.Sprintf("Invalid slice type received <%s>", sliceType)) + return fmt.Errorf(fmt.Sprintf("invalid slice type received <%s>", sliceType)) } if err != nil { @@ -265,7 +265,7 @@ func validateSlice(valSlice []string, sliceType string) error { } if len(indices) > 0 { return fmt.Errorf( - fmt.Sprintf("Invalid %s addresses at indices %s", sliceType, strings.Join(indices, ",")), + fmt.Sprintf("invalid %s addresses at indices %s", sliceType, strings.Join(indices, ",")), ) } return nil diff --git a/pkg/unit_test.go b/pkg/unit_test.go index 8bd1222c..d17e743f 100644 --- a/pkg/unit_test.go +++ b/pkg/unit_test.go @@ -381,7 +381,7 @@ func TestBadMacValidation(t *testing.T) { macObj := config.MacPattern().Mac().SetValue(mac) err := macObj.Validate() if assert.Error(t, err) { - assert.Contains(t, err.Error(), "Invalid Mac") + assert.Contains(t, err.Error(), "invalid mac") } } } @@ -449,7 +449,7 @@ func TestBadIpv4Validation(t *testing.T) { ipv4 := config.Ipv4Pattern().Ipv4().SetValue(ip) err := ipv4.Validate() if assert.Error(t, err) { - assert.Contains(t, err.Error(), "Invalid Ipv4") + assert.Contains(t, err.Error(), "invalid ipv4") } } } @@ -460,7 +460,7 @@ func TestBadIpv4Values(t *testing.T) { ipv4 := config.Ipv4Pattern().Ipv4().SetValues(BadIpv4) err := ipv4.Validate() if assert.Error(t, err) { - assert.Contains(t, err.Error(), "Invalid ipv4 addresses") + assert.Contains(t, err.Error(), "invalid ipv4 addresses") } } @@ -472,7 +472,7 @@ func TestBadIpv4Increment(t *testing.T) { ipv4.SetCount(10) err := ipv4.Validate() if assert.Error(t, err) { - assert.Contains(t, err.Error(), "Invalid Ipv4") + assert.Contains(t, err.Error(), "invalid ipv4") } } @@ -484,7 +484,7 @@ func TestBadIpv4Decrement(t *testing.T) { ipv4.SetCount(10) err := ipv4.Validate() if assert.Error(t, err) { - assert.Contains(t, err.Error(), "Invalid Ipv4") + assert.Contains(t, err.Error(), "invalid ipv4") } } From 85c1a3a81b97ffdbae5b8e08d958115b06791db0 Mon Sep 17 00:00:00 2001 From: Rangababu-R Date: Mon, 10 Jan 2022 16:01:09 +0530 Subject: [PATCH 11/33] Update openapiartgo.py --- openapiart/openapiartgo.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openapiart/openapiartgo.py b/openapiart/openapiartgo.py index 9a1d9e7a..a245a139 100644 --- a/openapiart/openapiartgo.py +++ b/openapiart/openapiartgo.py @@ -1061,6 +1061,8 @@ def _write_interface(self, new): return err.Error() }} return str + }} + func (obj *{struct}) Clone() ({interface}, error) {{ newObj := New{interface}() json, err := obj.ToJson() From a75da53307885a46f06ca114206255d8693dc0ab Mon Sep 17 00:00:00 2001 From: Rangababu-R Date: Mon, 10 Jan 2022 16:25:38 +0530 Subject: [PATCH 12/33] Update common.py --- openapiart/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openapiart/common.py b/openapiart/common.py index 984dff76..1f1b3ba7 100644 --- a/openapiart/common.py +++ b/openapiart/common.py @@ -371,7 +371,7 @@ def types_validation(self, value, type_, err_msg, itemtype=None, min=None, max=N def _raise_validation(self): errors = "\n".join(self._validation_errors) if len(self._get_validation_errors()) > 0: - self._validation_errors.clear() + self._validation_errors = [] raise Exception(errors) From 364caebac0a84a2bee8ffc3cc000b41d047f56af Mon Sep 17 00:00:00 2001 From: Rangababu-R Date: Mon, 10 Jan 2022 16:34:44 +0530 Subject: [PATCH 13/33] Update common.py --- openapiart/common.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openapiart/common.py b/openapiart/common.py index 1f1b3ba7..c729caac 100644 --- a/openapiart/common.py +++ b/openapiart/common.py @@ -226,6 +226,9 @@ def _append_error(self, msg): def _get_validation_errors(self): return self._validation_errors + + def _clear_errors(self): + self._validation_errors = [] def validate_mac(self, mac): if mac is None or not isinstance(mac, (str, unicode)) or mac.count(" ") != 0: @@ -371,7 +374,7 @@ def types_validation(self, value, type_, err_msg, itemtype=None, min=None, max=N def _raise_validation(self): errors = "\n".join(self._validation_errors) if len(self._get_validation_errors()) > 0: - self._validation_errors = [] + self._clear_errors() raise Exception(errors) From d64d4602cec89e09be14d2df5b098ceff6643732 Mon Sep 17 00:00:00 2001 From: Rangababu-R Date: Mon, 10 Jan 2022 16:58:31 +0530 Subject: [PATCH 14/33] Update common.py --- openapiart/common.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/openapiart/common.py b/openapiart/common.py index c729caac..69ea9d07 100644 --- a/openapiart/common.py +++ b/openapiart/common.py @@ -219,7 +219,7 @@ class OpenApiValidator(object): _validation_errors = [] def __init__(self): - pass + pass def _append_error(self, msg): self._validation_errors.append(msg) @@ -228,7 +228,11 @@ def _get_validation_errors(self): return self._validation_errors def _clear_errors(self): - self._validation_errors = [] + import platform + if '2.7' in platform.python_version().rsplit(".", 1)[0]: + self._validation_errors = [] + else: + self._validation_errors.clear() def validate_mac(self, mac): if mac is None or not isinstance(mac, (str, unicode)) or mac.count(" ") != 0: From 3b871935499e1e15cd6cb45b44e1d9c5b8df63e3 Mon Sep 17 00:00:00 2001 From: Rangababu-R Date: Mon, 10 Jan 2022 18:06:09 +0530 Subject: [PATCH 15/33] Update common.py --- openapiart/common.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openapiart/common.py b/openapiart/common.py index 69ea9d07..e7d97705 100644 --- a/openapiart/common.py +++ b/openapiart/common.py @@ -216,23 +216,23 @@ def _decode(self, dict_object): class OpenApiValidator(object): __slots__ = () - _validation_errors = [] + validation_errors = [] def __init__(self): pass def _append_error(self, msg): - self._validation_errors.append(msg) + self.validation_errors.append(msg) def _get_validation_errors(self): - return self._validation_errors + return self.validation_errors def _clear_errors(self): import platform if '2.7' in platform.python_version().rsplit(".", 1)[0]: - self._validation_errors = [] + self.validation_errors = [] else: - self._validation_errors.clear() + self.validation_errors.clear() def validate_mac(self, mac): if mac is None or not isinstance(mac, (str, unicode)) or mac.count(" ") != 0: From d41a119c9b94ff6ee6b6f46e7f3cfdd308217051 Mon Sep 17 00:00:00 2001 From: Rangababu-R Date: Mon, 10 Jan 2022 18:13:52 +0530 Subject: [PATCH 16/33] Update common.py --- openapiart/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openapiart/common.py b/openapiart/common.py index e7d97705..c4e3346e 100644 --- a/openapiart/common.py +++ b/openapiart/common.py @@ -376,7 +376,7 @@ def types_validation(self, value, type_, err_msg, itemtype=None, min=None, max=N # raise TypeError(err_msg) def _raise_validation(self): - errors = "\n".join(self._validation_errors) + errors = "\n".join(self.validation_errors) if len(self._get_validation_errors()) > 0: self._clear_errors() raise Exception(errors) @@ -598,7 +598,7 @@ def _validate(self, skip_exception=False): item._validate(True) self._validate_types(key, value) if skip_exception: - return self._validation_errors + return self.validation_errors self._raise_validation() def validate(self): From e06ecc9111e2e297d2b22bc69d88a74c65d75c8f Mon Sep 17 00:00:00 2001 From: Rangababu-R Date: Mon, 10 Jan 2022 21:24:24 +0530 Subject: [PATCH 17/33] Update common.py --- openapiart/common.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/openapiart/common.py b/openapiart/common.py index c4e3346e..00dd5e07 100644 --- a/openapiart/common.py +++ b/openapiart/common.py @@ -216,23 +216,23 @@ def _decode(self, dict_object): class OpenApiValidator(object): __slots__ = () - validation_errors = [] + _validation_errors = [] def __init__(self): pass def _append_error(self, msg): - self.validation_errors.append(msg) + self._validation_errors.append(msg) def _get_validation_errors(self): - return self.validation_errors + return self._validation_errors def _clear_errors(self): import platform if '2.7' in platform.python_version().rsplit(".", 1)[0]: - self.validation_errors = [] + self._validation_errors = [] else: - self.validation_errors.clear() + self._validation_errors.clear() def validate_mac(self, mac): if mac is None or not isinstance(mac, (str, unicode)) or mac.count(" ") != 0: @@ -376,7 +376,7 @@ def types_validation(self, value, type_, err_msg, itemtype=None, min=None, max=N # raise TypeError(err_msg) def _raise_validation(self): - errors = "\n".join(self.validation_errors) + errors = "\n".join(self._validation_errors) if len(self._get_validation_errors()) > 0: self._clear_errors() raise Exception(errors) @@ -392,6 +392,8 @@ class OpenApiObject(OpenApiBase, OpenApiValidator): """ __slots__ = ("_properties", "_parent", "_choice") + _validation_errors = [] + _DEFAULTS = {} _TYPES = {} _REQUIRED = [] @@ -598,7 +600,7 @@ def _validate(self, skip_exception=False): item._validate(True) self._validate_types(key, value) if skip_exception: - return self.validation_errors + return self._validation_errors self._raise_validation() def validate(self): From ea9e2a00fdcd30611635b49b577ab0f03621d71d Mon Sep 17 00:00:00 2001 From: Rangababu-R Date: Mon, 10 Jan 2022 22:36:29 +0530 Subject: [PATCH 18/33] final_fix --- .gitignore | 2 ++ openapiart/common.py | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 796927f5..eb333ed5 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,5 @@ __pycache__ /pkg/openapiart.go /pkg/httpapi _debug_bin +.env +.env2 \ No newline at end of file diff --git a/openapiart/common.py b/openapiart/common.py index 00dd5e07..6b699b61 100644 --- a/openapiart/common.py +++ b/openapiart/common.py @@ -230,7 +230,7 @@ def _get_validation_errors(self): def _clear_errors(self): import platform if '2.7' in platform.python_version().rsplit(".", 1)[0]: - self._validation_errors = [] + del self._validation_errors[:] else: self._validation_errors.clear() @@ -392,7 +392,6 @@ class OpenApiObject(OpenApiBase, OpenApiValidator): """ __slots__ = ("_properties", "_parent", "_choice") - _validation_errors = [] _DEFAULTS = {} _TYPES = {} From 2e16a1263fde3b98f4592625bc15d91ac75e2b73 Mon Sep 17 00:00:00 2001 From: Rangababu-R Date: Mon, 24 Jan 2022 13:02:54 +0530 Subject: [PATCH 19/33] Comments addressed --- openapiart/common.go | 66 +++++++++++++++++----------------- openapiart/openapiartgo.py | 63 ++++++++++++++++++++------------ pkg/common.go | 66 +++++++++++++++++----------------- pkg/generated_required_test.go | 54 ++++++++++++++-------------- pkg/unit_test.go | 52 +++++++++++++-------------- 5 files changed, 157 insertions(+), 144 deletions(-) diff --git a/openapiart/common.go b/openapiart/common.go index f7f3267c..acdce537 100644 --- a/openapiart/common.go +++ b/openapiart/common.go @@ -161,48 +161,46 @@ func validationResult() error { return nil } -func validateMac(mac string) error { +func validateMac(mac string, path string) error { macSlice := strings.Split(mac, ":") if len(macSlice) != 6 { - return fmt.Errorf(fmt.Sprintf("invalid mac address %s", mac)) + return fmt.Errorf(fmt.Sprintf("value of `%s` must be a valid mac string, instead of `%s`", path, mac)) } - octInd := []string{"0th", "1st", "2nd", "3rd", "4th", "5th"} - for ind, val := range macSlice { + for _, val := range macSlice { num, err := strconv.ParseUint(val, 16, 32) if err != nil || num > 255 { - return fmt.Errorf(fmt.Sprintf("invalid mac address at %s octet in %s mac", octInd[ind], mac)) + return fmt.Errorf(fmt.Sprintf("value of `%s` must be a valid mac string, instead of `%s`", path, mac)) } } return nil } -func validateIpv4(ip string) error { +func validateIpv4(ip string, path string) error { ipSlice := strings.Split(ip, ".") if len(ipSlice) != 4 { - return fmt.Errorf(fmt.Sprintf("invalid ipv4 address %s", ip)) + return fmt.Errorf(fmt.Sprintf("value of `%s` must be a valid ipv4 string, instead of `%s`", path, ip)) } - octInd := []string{"1st", "2nd", "3rd", "4th"} - for ind, val := range ipSlice { + for _, val := range ipSlice { num, err := strconv.ParseUint(val, 10, 32) if err != nil || num > 255 { - return fmt.Errorf(fmt.Sprintf("invalid ipv4 address at %s octet in %s ipv4", octInd[ind], ip)) + return fmt.Errorf(fmt.Sprintf("value of `%s` must be a valid ipv4 string, instead of `%s`", path, ip)) } } return nil } -func validateIpv6(ip string) error { +func validateIpv6(ip string, path string) error { ip = strings.Trim(ip, " \t") if strings.Count(ip, " ") > 0 || strings.Count(ip, ":") > 7 || strings.Count(ip, "::") > 1 || strings.Count(ip, ":::") > 0 || strings.Count(ip, ":") == 0 { - return fmt.Errorf(fmt.Sprintf("invalid ipv6 address %s", ip)) + return fmt.Errorf(fmt.Sprintf("value of `%s` must be a valid ipv6 string, instead of `%s`", path, ip)) } if (string(ip[0]) == ":" && string(ip[:2]) != "::") || (string(ip[len(ip)-1]) == ":" && string(ip[len(ip)-2:]) != "::") { - return fmt.Errorf(fmt.Sprintf("invalid ipv6 address %s", ip)) + return fmt.Errorf(fmt.Sprintf("value of `%s` must be a valid ipv6 string, instead of `%s`", path, ip)) } if strings.Count(ip, "::") == 0 && strings.Count(ip, ":") != 7 { - return fmt.Errorf(fmt.Sprintf("invalid ipv6 address %s", ip)) + return fmt.Errorf(fmt.Sprintf("value of `%s` must be a valid ipv6 string, instead of `%s`", path, ip)) } if ip == "::" { return nil @@ -217,69 +215,69 @@ func validateIpv6(ip string) error { r := strings.NewReplacer("::", ":0:") ip = r.Replace(ip) } - octInd := []string{"1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th"} ipSlice := strings.Split(ip, ":") - for ind, val := range ipSlice { + for _, val := range ipSlice { num, err := strconv.ParseUint(val, 16, 64) if err != nil || num > 65535 { - return fmt.Errorf(fmt.Sprintf("invalid ipv6 address at %s octet in %s ipv6", octInd[ind], ip)) + return fmt.Errorf(fmt.Sprintf("value of `%s` must be a valid ipv6 string, instead of `%s`", path, ip)) } } return nil } -func validateHex(hex string) error { +func validateHex(hex string, path string) error { matched, err := regexp.MatchString(`^[0-9a-fA-F]+$|^0[x|X][0-9a-fA-F]+$`, hex) if err != nil || !matched { - return fmt.Errorf(fmt.Sprintf("invalid hex value %s", hex)) + return fmt.Errorf(fmt.Sprintf("value of `%s` must be a valid hex string, instead of %s", path, hex)) } return nil } -func validateSlice(valSlice []string, sliceType string) error { +func validateSlice(valSlice []string, sliceType string, path string) error { indices := []string{} var err error for i, val := range valSlice { if sliceType == "mac" { - err = validateMac(val) + err = validateMac(val, path) } else if sliceType == "ipv4" { - err = validateIpv4(val) + err = validateIpv4(val, path) } else if sliceType == "ipv6" { - err = validateIpv6(val) + err = validateIpv6(val, path) } else if sliceType == "hex" { - err = validateHex(val) + err = validateHex(val, path) } else { return fmt.Errorf(fmt.Sprintf("invalid slice type received <%s>", sliceType)) } if err != nil { - indices = append(indices, fmt.Sprintf("%d", i)) + indices = append(indices, + fmt.Sprintf("value of `%s[%d]` must be a valid %s string, instead of `%s`", path, i, sliceType, val)) } } if len(indices) > 0 { return fmt.Errorf( - fmt.Sprintf("invalid %s addresses at indices %s", sliceType, strings.Join(indices, ",")), + strings.Join(indices, "\n"), ) } return nil } -func validateMacSlice(mac []string) error { - return validateSlice(mac, "mac") +func validateMacSlice(mac []string, path string) error { + return validateSlice(mac, "mac", path) } -func validateIpv4Slice(ip []string) error { - return validateSlice(ip, "ipv4") +func validateIpv4Slice(ip []string, path string) error { + return validateSlice(ip, "ipv4", path) } -func validateIpv6Slice(ip []string) error { - return validateSlice(ip, "ipv6") +func validateIpv6Slice(ip []string, path string) error { + return validateSlice(ip, "ipv6", path) } -func validateHexSlice(hex []string) error { - return validateSlice(hex, "hex") +func validateHexSlice(hex []string, path string) error { + return validateSlice(hex, "hex", path) } diff --git a/openapiart/openapiartgo.py b/openapiart/openapiartgo.py index a245a139..b87fe977 100644 --- a/openapiart/openapiartgo.py +++ b/openapiart/openapiartgo.py @@ -65,6 +65,7 @@ def __init__(self): self.description = None self.method_description = None self.interface_fields = [] + self.schema_raw_name = None def isOptional(self, property_name): if self.schema_object is None: @@ -430,6 +431,7 @@ def _build_api_interface(self): new.schema_name = self._get_schema_object_name_from_ref( ref[0].value ) + new.schema_raw_name = self._get_schema_json_name_from_ref(ref[0].value) new.schema_object = self._get_schema_object_from_ref( ref[0].value ) @@ -883,6 +885,7 @@ def _build_response_interfaces(self): self._get_description(new.schema_object, True).lstrip("// "), ) new.schema_name = self._get_external_struct_name(new.interface) + new.schema_raw_name = rpc.operation_name # new.isRpcResponse = True self._api.external_new_methods.append(new) @@ -959,7 +962,7 @@ def _write_interface(self, new): return retObj }} {nil_call} - vErr := obj.validateFromText("{struct}.FromPbText()") + vErr := obj.validateFromText("FromPbText -> {obj_name}") if vErr != nil {{ return vErr }} @@ -1001,7 +1004,7 @@ def _write_interface(self, new): uError.Error(), "\\u00a0", " ", -1)[7:]) }} {nil_call} - vErr := obj.validateFromText("{struct}.FromYaml()") + vErr := obj.validateFromText("FromYaml -> {obj_name}") if vErr != nil {{ return vErr }} @@ -1038,7 +1041,7 @@ def _write_interface(self, new): uError.Error(), "\\u00a0", " ", -1)[7:]) }} {nil_call} - err := obj.validateFromText("{struct}.FromJson()") + err := obj.validateFromText("FromJson -> {obj_name}") if err != nil {{ return err }} @@ -1051,7 +1054,7 @@ def _write_interface(self, new): }} func (obj *{struct}) Validate() error {{ - obj.validateObj(false, "{struct}.Validate()") + obj.validateObj(false, "{obj_name}") return validationResult() }} @@ -1065,13 +1068,13 @@ def _write_interface(self, new): func (obj *{struct}) Clone() ({interface}, error) {{ newObj := New{interface}() - json, err := obj.ToJson() + pbText, err := obj.ToPbText() if err != nil {{ return nil, err }} - jErr := newObj.FromJson(json) - if jErr != nil {{ - return nil, jErr + pbErr := newObj.FromPbText(pbText) + if pbErr != nil {{ + return nil, pbErr }} return newObj, nil }} @@ -1083,6 +1086,7 @@ def _write_interface(self, new): if len(internal_items) == 0 else "\n".join(internal_items), nil_call="obj.setNil()" if len(internal_items_nil) > 0 else "", + obj_name=new.schema_raw_name ) ) if len(internal_items_nil) > 0: @@ -1779,7 +1783,7 @@ def _build_setters_getters(self, fluent_new): field.name = "Bytes" elif "$ref" in property_schema: schema_name = self._get_schema_object_name_from_ref(property_schema["$ref"]) - field.schema_name = schema_name + field.schema_name = self._get_schema_json_name_from_ref(property_schema["$ref"]) field.name = self._get_external_struct_name(schema_name) field.isOptional = fluent_new.isOptional(property_name) field.isPointer = ( @@ -1982,9 +1986,9 @@ def _validate_types(self, new, field): // {name} is required if obj.obj.{name}{enum} == {value} {{ validation = append(validation, - fmt.Sprintf("{intf_name}.{field_name} is a required field from path %s", path)) + fmt.Sprintf("required field `%s.{field_name}` must not be empty", path)) }} """.format( - name=field.name, intf_name=new.schema_name, + name=field.name, intf_name=new.schema_raw_name, field_name=field.schema_name, value=0 if field.isEnum and field.isArray is False else value, enum=".Number()" @@ -2010,12 +2014,12 @@ def _validate_types(self, new, field): validation = append( validation, fmt.Sprintf( - "{interface}.{name} shall be in the range of [{min}, {max}] but Got {form} from path %s", {pointer}{value}, path)) + "length of field `%s.{name}` must be in range [{min}, {max}], instead of `{form}`", path, {pointer}{value})) }} """ ).format( name=field.schema_name, - interface=new.schema_name, + interface=new.schema_raw_name, max="max({})".format(field.type.lstrip("[]")) if field.max is None else field.max, pointer="*" if field.isPointer else "", min="min({})".format(field.type.lstrip("[]")) @@ -2048,8 +2052,8 @@ def _validate_types(self, new, field): validation = append( validation, fmt.Sprintf( - "length of {interface}.{name} shall be in the range of [{min_length}, {max_length}] but Got %d from path %s", - len({pointer}{value}), path)) + "length of field `%s.{name}` must be in range [{min_length}, {max_length}], instead of `%d`", + path, len({pointer}{value}))) }} """ ).format( @@ -2081,10 +2085,9 @@ def _validate_types(self, new, field): if field.format is None: field.format = field.itemformat inner_body = """ - err := validate{format}(obj.{name}()) + err := validate{format}(obj.{name}(), fmt.Sprintf("%s.{field_name}", path)) if err != nil {{ - validation = append(validation, - fmt.Sprintf("%s %s from path %s", err.Error(), "on {interface}.{field_name}", path)) + validation = append(validation, err.Error()) }} """.format( name=field.name, interface=new.schema_name, @@ -2112,13 +2115,14 @@ def _validate_struct(self, new, field): if obj.obj.{name} == nil {{ validation = append( validation, - fmt.Sprintf("{interface}.{field_name} is a required field from path %s", path)) + fmt.Sprintf("required field `%s.{field_name}` must not be empty", path)) }} - """.format(name=field.name, interface=new.schema_name, field_name=field.schema_name) + """.format(name=field.name, interface=new.schema_raw_name, field_name=field.schema_name) inner_body = """obj.{external_name}().validateObj( - set_default, fmt.Sprintf("%s.{external_name}", path))""".format( - external_name=self._get_external_struct_name(field.name) + set_default, fmt.Sprintf("%s.{json_name}", path))""".format( + external_name=self._get_external_struct_name(field.name), + json_name=field.schema_name ) if field.isArray: inner_body = """ @@ -2129,10 +2133,13 @@ def _validate_struct(self, new, field): }} }} for ind, item := range obj.{name}().Items() {{ - item.validateObj(set_default, fmt.Sprintf("%s.{name}().Items()[%d]", path, ind)) + item.validateObj(set_default, fmt.Sprintf("%s.{field_name}[%d]", path, ind)) }} """.format( name=field.name, + field_name=field.schema_name, + field_type=field.type, + internal_items_name="{}Slice".format(field.struct), field_internal_struct=field.struct, ) body += """ @@ -2377,6 +2384,15 @@ def _write_default_method(self, new): struct=new.struct, body=body ) ) + + def _get_schema_json_name_from_ref(self, ref): + final_piece = ref.split("/")[-1] + if "." in final_piece: + return final_piece.replace(".", "_").lower() + return self._lower_first_char(final_piece) + + def _lower_first_char(self, word): + return word[0].lower() + word[1:] def _get_schema_object_name_from_ref(self, ref): final_piece = ref.split("/")[-1] @@ -2430,6 +2446,7 @@ def _get_struct_field_type(self, property_schema, fluent_field=None): new = FluentNew() new.schema_object = schema_object new.schema_name = schema_object_name + new.schema_raw_name = self._get_schema_json_name_from_ref(ref) new.struct = self._get_internal_name(schema_object_name) new.interface = self._get_external_struct_name( schema_object_name diff --git a/pkg/common.go b/pkg/common.go index 7423611d..df86b0ff 100644 --- a/pkg/common.go +++ b/pkg/common.go @@ -164,48 +164,46 @@ func validationResult() error { return nil } -func validateMac(mac string) error { +func validateMac(mac string, path string) error { macSlice := strings.Split(mac, ":") if len(macSlice) != 6 { - return fmt.Errorf(fmt.Sprintf("invalid mac address %s", mac)) + return fmt.Errorf(fmt.Sprintf("value of `%s` must be a valid mac string, instead of `%s`", path, mac)) } - octInd := []string{"0th", "1st", "2nd", "3rd", "4th", "5th"} - for ind, val := range macSlice { + for _, val := range macSlice { num, err := strconv.ParseUint(val, 16, 32) if err != nil || num > 255 { - return fmt.Errorf(fmt.Sprintf("invalid mac address at %s octet in %s mac", octInd[ind], mac)) + return fmt.Errorf(fmt.Sprintf("value of `%s` must be a valid mac string, instead of `%s`", path, mac)) } } return nil } -func validateIpv4(ip string) error { +func validateIpv4(ip string, path string) error { ipSlice := strings.Split(ip, ".") if len(ipSlice) != 4 { - return fmt.Errorf(fmt.Sprintf("invalid ipv4 address %s", ip)) + return fmt.Errorf(fmt.Sprintf("value of `%s` must be a valid ipv4 string, instead of `%s`", path, ip)) } - octInd := []string{"1st", "2nd", "3rd", "4th"} - for ind, val := range ipSlice { + for _, val := range ipSlice { num, err := strconv.ParseUint(val, 10, 32) if err != nil || num > 255 { - return fmt.Errorf(fmt.Sprintf("invalid ipv4 address at %s octet in %s ipv4", octInd[ind], ip)) + return fmt.Errorf(fmt.Sprintf("value of `%s` must be a valid ipv4 string, instead of `%s`", path, ip)) } } return nil } -func validateIpv6(ip string) error { +func validateIpv6(ip string, path string) error { ip = strings.Trim(ip, " \t") if strings.Count(ip, " ") > 0 || strings.Count(ip, ":") > 7 || strings.Count(ip, "::") > 1 || strings.Count(ip, ":::") > 0 || strings.Count(ip, ":") == 0 { - return fmt.Errorf(fmt.Sprintf("invalid ipv6 address %s", ip)) + return fmt.Errorf(fmt.Sprintf("value of `%s` must be a valid ipv6 string, instead of `%s`", path, ip)) } if (string(ip[0]) == ":" && string(ip[:2]) != "::") || (string(ip[len(ip)-1]) == ":" && string(ip[len(ip)-2:]) != "::") { - return fmt.Errorf(fmt.Sprintf("invalid ipv6 address %s", ip)) + return fmt.Errorf(fmt.Sprintf("value of `%s` must be a valid ipv6 string, instead of `%s`", path, ip)) } if strings.Count(ip, "::") == 0 && strings.Count(ip, ":") != 7 { - return fmt.Errorf(fmt.Sprintf("invalid ipv6 address %s", ip)) + return fmt.Errorf(fmt.Sprintf("value of `%s` must be a valid ipv6 string, instead of `%s`", path, ip)) } if ip == "::" { return nil @@ -220,69 +218,69 @@ func validateIpv6(ip string) error { r := strings.NewReplacer("::", ":0:") ip = r.Replace(ip) } - octInd := []string{"1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th"} ipSlice := strings.Split(ip, ":") - for ind, val := range ipSlice { + for _, val := range ipSlice { num, err := strconv.ParseUint(val, 16, 64) if err != nil || num > 65535 { - return fmt.Errorf(fmt.Sprintf("invalid ipv6 address at %s octet in %s ipv6", octInd[ind], ip)) + return fmt.Errorf(fmt.Sprintf("value of `%s` must be a valid ipv6 string, instead of `%s`", path, ip)) } } return nil } -func validateHex(hex string) error { +func validateHex(hex string, path string) error { matched, err := regexp.MatchString(`^[0-9a-fA-F]+$|^0[x|X][0-9a-fA-F]+$`, hex) if err != nil || !matched { - return fmt.Errorf(fmt.Sprintf("invalid hex value %s", hex)) + return fmt.Errorf(fmt.Sprintf("value of `%s` must be a valid hex string, instead of %s", path, hex)) } return nil } -func validateSlice(valSlice []string, sliceType string) error { +func validateSlice(valSlice []string, sliceType string, path string) error { indices := []string{} var err error for i, val := range valSlice { if sliceType == "mac" { - err = validateMac(val) + err = validateMac(val, path) } else if sliceType == "ipv4" { - err = validateIpv4(val) + err = validateIpv4(val, path) } else if sliceType == "ipv6" { - err = validateIpv6(val) + err = validateIpv6(val, path) } else if sliceType == "hex" { - err = validateHex(val) + err = validateHex(val, path) } else { return fmt.Errorf(fmt.Sprintf("invalid slice type received <%s>", sliceType)) } if err != nil { - indices = append(indices, fmt.Sprintf("%d", i)) + indices = append(indices, + fmt.Sprintf("value of `%s[%d]` must be a valid %s string, instead of `%s`", path, i, sliceType, val)) } } if len(indices) > 0 { return fmt.Errorf( - fmt.Sprintf("invalid %s addresses at indices %s", sliceType, strings.Join(indices, ",")), + strings.Join(indices, "\n"), ) } return nil } -func validateMacSlice(mac []string) error { - return validateSlice(mac, "mac") +func validateMacSlice(mac []string, path string) error { + return validateSlice(mac, "mac", path) } -func validateIpv4Slice(ip []string) error { - return validateSlice(ip, "ipv4") +func validateIpv4Slice(ip []string, path string) error { + return validateSlice(ip, "ipv4", path) } -func validateIpv6Slice(ip []string) error { - return validateSlice(ip, "ipv6") +func validateIpv6Slice(ip []string, path string) error { + return validateSlice(ip, "ipv6", path) } -func validateHexSlice(hex []string) error { - return validateSlice(hex, "hex") +func validateHexSlice(hex []string, path string) error { + return validateSlice(hex, "hex", path) } diff --git a/pkg/generated_required_test.go b/pkg/generated_required_test.go index 9b41db0e..3b065e75 100644 --- a/pkg/generated_required_test.go +++ b/pkg/generated_required_test.go @@ -22,12 +22,12 @@ func TestPrefixConfigRequired(t *testing.T) { err1 := object.FromYaml(string(data)) str, _ := proto.Marshal(object.Msg()) err2 := object.FromPbText(string(str)) - assert.Contains(t, err.Error(), "PrefixConfig.required_object") - assert.Contains(t, err.Error(), "PrefixConfig.a") - assert.Contains(t, err1.Error(), "PrefixConfig.required_object") - assert.Contains(t, err1.Error(), "PrefixConfig.a") - assert.Contains(t, err2.Error(), "PrefixConfig.required_object") - assert.Contains(t, err2.Error(), "PrefixConfig.a") + assert.Contains(t, err.Error(), "prefix_config.required_object") + assert.Contains(t, err.Error(), "prefix_config.a") + assert.Contains(t, err1.Error(), "prefix_config.required_object") + assert.Contains(t, err1.Error(), "prefix_config.a") + assert.Contains(t, err2.Error(), "prefix_config.required_object") + assert.Contains(t, err2.Error(), "prefix_config.a") // assert.Contains(t, err2.Error(), "PrefixConfig.b") // assert.Contains(t, err2.Error(), "PrefixConfig.c") } @@ -61,9 +61,9 @@ func TestMandateRequired(t *testing.T) { err1 := object.FromYaml(string(data)) str, _ := proto.Marshal(object.Msg()) err2 := object.FromPbText(string(str)) - assert.Contains(t, err.Error(), "Mandate.required_param") - assert.Contains(t, err1.Error(), "Mandate.required_param") - assert.Contains(t, err2.Error(), "Mandate.required_param") + assert.Contains(t, err.Error(), "mandate.required_param") + assert.Contains(t, err1.Error(), "mandate.required_param") + assert.Contains(t, err2.Error(), "mandate.required_param") } func TestMObjectRequired(t *testing.T) { object := openapiart.NewMObject() @@ -78,30 +78,30 @@ func TestMObjectRequired(t *testing.T) { err1 := object.FromYaml(string(data)) str, _ := proto.Marshal(object.Msg()) err2 := object.FromPbText(string(str)) - assert.Contains(t, err.Error(), "MObject.string") + assert.Contains(t, err.Error(), "mObject.string_param") // assert.Contains(t, err.Error(), "MObject.integer") - assert.Contains(t, err.Error(), "MObject.ipv4") - assert.Contains(t, err.Error(), "MObject.mac") + assert.Contains(t, err.Error(), "mObject.ipv4") + assert.Contains(t, err.Error(), "mObject.mac") // assert.Contains(t, err.Error(), "MObject.float") // assert.Contains(t, err.Error(), "MObject.double") - assert.Contains(t, err.Error(), "MObject.ipv6") - assert.Contains(t, err.Error(), "MObject.hex") - assert.Contains(t, err1.Error(), "MObject.string") + assert.Contains(t, err.Error(), "mObject.ipv6") + assert.Contains(t, err.Error(), "mObject.hex") + assert.Contains(t, err1.Error(), "mObject.string") // assert.Contains(t, err1.Error(), "MObject.integer") - assert.Contains(t, err1.Error(), "MObject.ipv4") - assert.Contains(t, err1.Error(), "MObject.mac") + assert.Contains(t, err1.Error(), "mObject.ipv4") + assert.Contains(t, err1.Error(), "mObject.mac") // assert.Contains(t, err1.Error(), "MObject.float") // assert.Contains(t, err1.Error(), "MObject.double") - assert.Contains(t, err1.Error(), "MObject.ipv6") - assert.Contains(t, err1.Error(), "MObject.hex") - assert.Contains(t, err2.Error(), "MObject.string") + assert.Contains(t, err1.Error(), "mObject.ipv6") + assert.Contains(t, err1.Error(), "mObject.hex") + assert.Contains(t, err2.Error(), "mObject.string") // assert.Contains(t, err2.Error(), "MObject.integer") - assert.Contains(t, err2.Error(), "MObject.ipv4") - assert.Contains(t, err2.Error(), "MObject.mac") + assert.Contains(t, err2.Error(), "mObject.ipv4") + assert.Contains(t, err2.Error(), "mObject.mac") // assert.Contains(t, err2.Error(), "MObject.float") // assert.Contains(t, err2.Error(), "MObject.double") - assert.Contains(t, err2.Error(), "MObject.ipv6") - assert.Contains(t, err2.Error(), "MObject.hex") + assert.Contains(t, err2.Error(), "mObject.ipv6") + assert.Contains(t, err2.Error(), "mObject.hex") } func TestPortMetricRequired(t *testing.T) { object := openapiart.NewPortMetric() @@ -116,7 +116,7 @@ func TestPortMetricRequired(t *testing.T) { err1 := object.FromYaml(string(data)) str, _ := proto.Marshal(object.Msg()) err2 := object.FromPbText(string(str)) - assert.Contains(t, err.Error(), "PortMetric.name") - assert.Contains(t, err1.Error(), "PortMetric.name") - assert.Contains(t, err2.Error(), "PortMetric.name") + assert.Contains(t, err.Error(), "port_metric.name") + assert.Contains(t, err1.Error(), "port_metric.name") + assert.Contains(t, err2.Error(), "port_metric.name") } diff --git a/pkg/unit_test.go b/pkg/unit_test.go index d17e743f..b9df67b1 100644 --- a/pkg/unit_test.go +++ b/pkg/unit_test.go @@ -381,7 +381,7 @@ func TestBadMacValidation(t *testing.T) { macObj := config.MacPattern().Mac().SetValue(mac) err := macObj.Validate() if assert.Error(t, err) { - assert.Contains(t, err.Error(), "invalid mac") + assert.Contains(t, err.Error(), "value of `pattern_macpattern_mac.value` must be a valid mac") } } } @@ -401,7 +401,7 @@ func TestBadMacValues(t *testing.T) { err := mac.Validate() fmt.Println(err.Error()) if assert.Error(t, err) { - assert.Contains(t, strings.ToLower(err.Error()), "invalid mac address") + assert.Contains(t, strings.ToLower(err.Error()), "must be a valid mac string") } } @@ -414,7 +414,7 @@ func TestBadMacIncrement(t *testing.T) { err := mac.Validate() fmt.Println(err.Error()) if assert.Error(t, err) { - assert.Contains(t, strings.ToLower(err.Error()), "invalid mac address") + assert.Contains(t, strings.ToLower(err.Error()), "must be a valid mac string") } } @@ -427,7 +427,7 @@ func TestBadMacDecrement(t *testing.T) { err := mac.Validate() fmt.Println(err.Error()) if assert.Error(t, err) { - assert.Contains(t, strings.ToLower(err.Error()), "invalid mac address") + assert.Contains(t, strings.ToLower(err.Error()), "must be a valid mac string") } } @@ -449,7 +449,7 @@ func TestBadIpv4Validation(t *testing.T) { ipv4 := config.Ipv4Pattern().Ipv4().SetValue(ip) err := ipv4.Validate() if assert.Error(t, err) { - assert.Contains(t, err.Error(), "invalid ipv4") + assert.Contains(t, err.Error(), "must be a valid ipv4 string") } } } @@ -460,7 +460,7 @@ func TestBadIpv4Values(t *testing.T) { ipv4 := config.Ipv4Pattern().Ipv4().SetValues(BadIpv4) err := ipv4.Validate() if assert.Error(t, err) { - assert.Contains(t, err.Error(), "invalid ipv4 addresses") + assert.Contains(t, err.Error(), "must be a valid ipv4 string") } } @@ -472,7 +472,7 @@ func TestBadIpv4Increment(t *testing.T) { ipv4.SetCount(10) err := ipv4.Validate() if assert.Error(t, err) { - assert.Contains(t, err.Error(), "invalid ipv4") + assert.Contains(t, err.Error(), "must be a valid ipv4 string") } } @@ -484,7 +484,7 @@ func TestBadIpv4Decrement(t *testing.T) { ipv4.SetCount(10) err := ipv4.Validate() if assert.Error(t, err) { - assert.Contains(t, err.Error(), "invalid ipv4") + assert.Contains(t, err.Error(), "must be a valid ipv4 string") } } @@ -506,7 +506,7 @@ func TestBadIpv6Validation(t *testing.T) { ipv6 := config.Ipv6Pattern().Ipv6().SetValue(ip) err := ipv6.Validate() if assert.Error(t, err) { - assert.Contains(t, strings.ToLower(err.Error()), "invalid ipv6") + assert.Contains(t, strings.ToLower(err.Error()), "must be a valid ipv6 string") } } } @@ -517,7 +517,7 @@ func TestBadIpv6Values(t *testing.T) { ipv6 := config.Ipv6Pattern().Ipv6().SetValues(BadIpv6) err := ipv6.Validate() if assert.Error(t, err) { - assert.Contains(t, strings.ToLower(err.Error()), "invalid ipv6 address") + assert.Contains(t, strings.ToLower(err.Error()), "must be a valid ipv6 string") } } @@ -529,7 +529,7 @@ func TestBadIpv6Increment(t *testing.T) { ipv6.SetCount(10) err := ipv6.Validate() if assert.Error(t, err) { - assert.Contains(t, strings.ToLower(err.Error()), "invalid ipv6") + assert.Contains(t, strings.ToLower(err.Error()), "must be a valid ipv6 string") } } @@ -541,7 +541,7 @@ func TestBadIpv6Decrement(t *testing.T) { ipv6.SetCount(10) err := ipv6.Validate() if assert.Error(t, err) { - assert.Contains(t, strings.ToLower(err.Error()), "invalid ipv6") + assert.Contains(t, strings.ToLower(err.Error()), "must be a valid ipv6 string") } } @@ -670,7 +670,7 @@ func TestRequiredField(t *testing.T) { mandate := openapiart.NewMandate() err := mandate.Validate() assert.NotNil(t, err) - assert.Contains(t, err.Error(), "Mandate.required_param is a required field") + assert.Contains(t, err.Error(), "required field `mandate.required_param` must not be empty") } func TestOptionalDefault(t *testing.T) { @@ -757,7 +757,7 @@ func TestFromJsonToCleanObject(t *testing.T) { }` err1 := config.FromJson(new_json1) assert.NotNil(t, err1) - assert.Contains(t, err1.Error(), ".a is a required field") + assert.Contains(t, err1.Error(), "required field `FromJson -> prefix_config.a` must not be empty") } func TestChoiceStale(t *testing.T) { @@ -981,7 +981,7 @@ func TestStringLengthError(t *testing.T) { err := config.Validate() if assert.Error(t, err) { assert.Contains(t, err.Error(), - "length of PrefixConfig.str_len shall be in the range of [3, 6] but Got 8 from path prefixConfig.Validate()") + "length of field `prefix_config.str_len` must be in range [3, 6], instead of `8`") } } @@ -1016,7 +1016,7 @@ func TestMObjectValidation(t *testing.T) { mObject := openapiart.NewMObject() err := mObject.Validate() if assert.Error(t, err) { - assert.Contains(t, err.Error(), "required field from path mObject.Validate()") + assert.Contains(t, err.Error(), "required field") } } @@ -1038,13 +1038,14 @@ func TestMobjectValidationError(t *testing.T) { SetIpv4("1.1.1.1.2") config.SetResponse(openapiart.PrefixConfigResponse.STATUS_400) err := config.Validate() + fmt.Println(err.Error()) assert.NotNil(t, err) if assert.Error(t, err) { - assert.Contains(t, strings.ToLower(err.Error()), - "invalid mac address", - "invalid ipv4 address", - "invalid hex value", - "invalid ipv6 address") + assert.Contains(t, err.Error(), "must be in range [10, 90], instead of `120`") + assert.Contains(t, err.Error(), "must be a valid hex") + assert.Contains(t, err.Error(), "must be a valid mac") + assert.Contains(t, err.Error(), "must be a valid ipv4") + assert.Contains(t, err.Error(), "must be a valid ipv6") } } @@ -1063,11 +1064,10 @@ func TestLObjectError(t *testing.T) { err := config.Validate() assert.NotNil(t, err) if assert.Error(t, err) { - assert.Contains(t, strings.ToLower(err.Error()), - "invalid mac address", - "invalid ipv4 address", - "invalid hex value", - "invalid ipv6 address") + assert.Contains(t, err.Error(), "must be a valid hex") + assert.Contains(t, err.Error(), "must be a valid mac") + assert.Contains(t, err.Error(), "must be a valid ipv4") + assert.Contains(t, err.Error(), "must be a valid ipv6") } } From 9a6a533eb641e146269dc06bb670b71efef46174 Mon Sep 17 00:00:00 2001 From: Rangababu-R Date: Mon, 24 Jan 2022 15:05:52 +0530 Subject: [PATCH 20/33] Update config.json --- openapiart/tests/json_configs/config.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openapiart/tests/json_configs/config.json b/openapiart/tests/json_configs/config.json index aaefa52e..77800e65 100644 --- a/openapiart/tests/json_configs/config.json +++ b/openapiart/tests/json_configs/config.json @@ -1,4 +1,8 @@ { + "required_object": { + "e_a": 10.1, + "e_b": 2245.1111 + }, "a": "asdf", "b": 1.1, "c": 1, From 4c2684d875c3ea5b8b7c3177239354fd94d8ec82 Mon Sep 17 00:00:00 2001 From: Rangababu-R Date: Thu, 27 Jan 2022 17:49:32 +0530 Subject: [PATCH 21/33] py error msgs fix --- openapiart/common.py | 236 ++++++++++++++-------------- openapiart/generator.py | 11 +- openapiart/tests/test_func.py | 9 +- openapiart/tests/test_py_go_diff.py | 31 ++-- 4 files changed, 152 insertions(+), 135 deletions(-) diff --git a/openapiart/common.py b/openapiart/common.py index 6b699b61..5b0f9f22 100644 --- a/openapiart/common.py +++ b/openapiart/common.py @@ -1,6 +1,7 @@ import importlib import logging import json +from markupsafe import string import yaml import requests import urllib3 @@ -234,35 +235,44 @@ def _clear_errors(self): else: self._validation_errors.clear() - def validate_mac(self, mac): + def validate_mac(self, path, mac): + msg = "value of `{}` must be a valid mac address, instead of `{}`".format(path, mac) if mac is None or not isinstance(mac, (str, unicode)) or mac.count(" ") != 0: - return False + self._append_error(msg) try: if len(mac) != 17: - return False - return all([0 <= int(oct, 16) <= 255 for oct in mac.split(":")]) + self._append_error(msg) + if all([0 <= int(oct, 16) <= 255 for oct in mac.split(":")]) is False: + self._append_error(msg) except Exception: - return False + self._append_error(msg) - def validate_ipv4(self, ip): + def validate_ipv4(self, path, ip): + msg = "value of `{}` must be a valid ipv4 address, instead of `{}`".format(path, ip) if ip is None or not isinstance(ip, (str, unicode)) or ip.count(" ") != 0: - return False + self._append_error(msg) if len(ip.split(".")) != 4: - return False + self._append_error(msg) try: - return all([0 <= int(oct) <= 255 for oct in ip.split(".", 3)]) + if all([0 <= int(oct) <= 255 for oct in ip.split(".", 3)]) is False: + self._append_error(msg) except Exception: - return False + self._append_error(msg) - def validate_ipv6(self, ip): + def validate_ipv6(self, path, ip): + msg = "value of `{}` must be a valid ipv6 address, instead of `{}`".format(path, ip) if ip is None or not isinstance(ip, (str, unicode)): + self._append_error(msg) return False ip = ip.strip() if ip.count(" ") > 0 or ip.count(":") > 7 or ip.count("::") > 1 or ip.count(":::") > 0: + self._append_error(msg) return False if (ip[0] == ":" and ip[:2] != "::") or (ip[-1] == ":" and ip[-2:] != "::"): + self._append_error(msg) return False if ip.count("::") == 0 and ip.count(":") != 7: + self._append_error(msg) return False if ip == "::": return True @@ -273,68 +283,90 @@ def validate_ipv6(self, ip): else: ip = ip.replace("::", ":0:") try: - return all([True if (0 <= int(oct, 16) <= 65535) and (1 <= len(oct) <= 4) else False for oct in ip.split(":")]) + verdict = all([ + True if (0 <= int(oct, 16) <= 65535) and (1 <= len(oct) <= 4) else False for oct in ip.split(":") + ]) + if verdict is False: + self._append_error(msg) except Exception: - return False + self._append_error(msg) - def validate_hex(self, hex): + def validate_hex(self, path, hex): + msg = "value of `{}` must be a valid hex string, instead of `{}`".format(path, hex) if hex is None or not isinstance(hex, (str, unicode)): - return False + self._append_error(msg) try: int(hex, 16) return True except Exception: - return False + self._append_error(msg) - def validate_integer(self, value, min, max): + def validate_integer(self, path, value): if value is None or not isinstance(value, int): - return False - if value < 0: - return False - if min is not None and value < min: - return False - if max is not None and value > max: - return False - return True + self._append_error("value of `{}` must be a valid int type, instead of `{}`".format( + path, value + )) + + def validate_min_max(self, path, value, min, max): + if isinstance(value, str): + value = len(value) + if (min is not None and value < min) or (max is not None and value > max): + self._append_error("length of `{}` must be in the range of [{}, {}], instead of `{}`".format( + path, + min if min is not None else "", + max if max is not None else "", + value + )) - def validate_float(self, value): - return isinstance(value, (int, float)) + def validate_float(self, path, value): + if isinstance(value, (int, float)) is False: + self._append_error("value of `{}` must be a valid float type, instead of `{}`".format( + path, value + )) - def validate_string(self, value, min_length, max_length): + def validate_string(self, path, value): if value is None or not isinstance(value, (str, unicode)): - return False - if min_length is not None and len(value) < min_length: - return False - if max_length is not None and len(value) > max_length: - return False - return True + self._append_error("value of `{}` must be a valid string type, instead of `{}`".format( + path, value + )) - def validate_bool(self, value): - return isinstance(value, bool) + def validate_bool(self, path, value): + if isinstance(value, bool) is False: + self._append_error("value of `{}` must be a valid bool type, instead of `{}`".format( + path, value + )) - def validate_list(self, value, itemtype, min, max, min_length, max_length): + def validate_list(self, path, value, itemtype, min, max): if value is None or not isinstance(value, list): return False v_obj = getattr(self, "validate_{}".format(itemtype), None) if v_obj is None: raise AttributeError("{} is not a valid attribute".format(itemtype)) - v_obj_lst = [] - for item in value: - if itemtype == "integer": - v_obj_lst.append(v_obj(item, min, max)) - elif itemtype == "string": - v_obj_lst.append(v_obj(item, min_length, max_length)) + for ind, item in enumerate(value): + if itemtype in ["integer", "string", "float"]: + v_obj(path + "[{}]".format(ind), item) + self.validate_min_max(path, item, min, max) else: - v_obj_lst.append(v_obj(item)) - return v_obj_lst + v_obj(path + "[{}]".format(ind), item) - def validate_binary(self, value): - if value is None or not isinstance(value, (str, unicode)): - return False - return all([True if int(bin) == 0 or int(bin) == 1 else False for bin in value]) + def validate_binary(self, path, value): + if value is None or not isinstance(value, (str, unicode)) or \ + all([True if int(bin) == 0 or int(bin) == 1 else False for bin in value]) is False: + self._append_error("value of `{}` must be a valid binary string, instead of `{}`".format( + path, value + )) - def types_validation(self, value, type_, err_msg, itemtype=None, min=None, max=None, min_length=None, max_length=None): - type_map = {int: "integer", str: "string", float: "float", bool: "bool", list: "list", "int64": "integer", "int32": "integer", "double": "float"} + def types_validation(self, value, type_, path, itemtype=None, min=None, max=None): + type_map = { + int: "integer", + str: "string", + float: "float", + bool: "bool", + list: "list", + "int64": "integer", + "int32": "integer", + "double": "float" + } if type_ in type_map: type_ = type_map[type_] if itemtype is not None and itemtype in type_map: @@ -343,37 +375,9 @@ def types_validation(self, value, type_, err_msg, itemtype=None, min=None, max=N if v_obj is None: msg = "{} is not a valid or unsupported format".format(type_) raise TypeError(msg) - if type_ == "list": - verdict = v_obj(value, itemtype, min, max, min_length, max_length) - if all(verdict) is True: - return - err_msg = "{} \n {} are not valid".format(err_msg, [value[index] for index, item in enumerate(verdict) if item is False]) - verdict = False - elif type_ == "integer": - verdict = v_obj(value, min, max) - if verdict is True: - return - min_max = "" - if min is not None: - min_max = ", expected min {}".format(min) - if max is not None: - min_max = min_max + ", expected max {}".format(max) - err_msg = "{} \n got {} of type {} {}".format(err_msg, value, type(value), min_max) - elif type_ == "string": - verdict = v_obj(value, min_length, max_length) - if verdict is True: - return - msg = "" - if min_length is not None: - msg = ", expected min {}".format(min_length) - if max_length is not None: - msg = msg + ", expected max {}".format(max_length) - err_msg = "{} \n got {} of type {} {}".format(err_msg, value, type(value), msg) - else: - verdict = v_obj(value) - if verdict is False: - self._append_error(err_msg) - # raise TypeError(err_msg) + v_obj(path, value) if type_ != "list" else v_obj(path, value, itemtype, min, max) + if type_ in ["integer", "string", "float"]: + self.validate_min_max(path, value, min, max) def _raise_validation(self): errors = "\n".join(self._validation_errors) @@ -393,6 +397,8 @@ class OpenApiObject(OpenApiBase, OpenApiValidator): __slots__ = ("_properties", "_parent", "_choice") + _JSON_NAME = "" + _DEFAULTS = {} _TYPES = {} _REQUIRED = [] @@ -433,9 +439,6 @@ def _get_property(self, name, default_value=None, parent=None, choice=None): return self._properties[name] if isinstance(default_value, type) is True: self._set_choice(name) - # if "_choice" in default_value.__slots__: - # self._properties[name] = default_value(parent=parent, choice=choice) - # else: self._properties[name] = default_value(parent=parent) if "_DEFAULTS" in dir(self._properties[name]) and "choice" in self._properties[name]._DEFAULTS: getattr(self._properties[name], self._properties[name]._DEFAULTS["choice"]) @@ -462,17 +465,15 @@ def _set_property(self, name, value, choice=None): def _encode(self): """Helper method for serialization""" + self._validate(self._JSON_NAME) output = {} - self._validate_required() for key, value in self._properties.items(): - self._validate_types(key, value) if isinstance(value, (OpenApiObject, OpenApiIter)): output[key] = value._encode() elif value is not None: if key in self._TYPES and "format" in self._TYPES[key] and self._TYPES[key]["format"] == "int64": value = str(value) output[key] = value - self._raise_validation() return output def _decode(self, obj): @@ -501,9 +502,7 @@ def _decode(self, obj): if "format" in self._TYPES[property_name] and self._TYPES[property_name]["format"] == "int64": property_value = int(property_value) self._properties[property_name] = property_value - self._validate_types(property_name, property_value) - self._validate_required() - self._raise_validation() + self._validate(self._JSON_NAME) return self def _get_child_class(self, property_name, is_property_list=False): @@ -534,7 +533,7 @@ def clone(self): """Creates a deep copy of the current object""" return self.__deepcopy__(None) - def _validate_required(self): + def _validate_required(self, path): """Validates the required properties are set Use getattr as it will set any defaults prior to validating """ @@ -542,31 +541,29 @@ def _validate_required(self): return for name in self._REQUIRED: if self._properties.get(name) is None: - msg = "{} is a mandatory property of {}" " and should not be set to None".format( - name, - self.__class__, + msg = "required field `{}.{}` must not be empty".format( + path, name ) - # raise ValueError(msg) self._append_error(msg) - def _validate_types(self, property_name, property_value): + def _validate_types(self, path, property_name, property_value): common_data_types = [list, str, int, float, bool] if property_name not in self._TYPES: - # raise ValueError("Invalid Property {}".format(property_name)) return details = self._TYPES[property_name] if property_value is None: return if "enum" in details and property_value not in details["enum"]: - msg = "property {} shall be one of these" " {} enum, but got {} at {}" - self._append_error( - msg.format(property_name, details["enum"], property_value, self.__class__) + msg = "enum field `{}` must be one of {}, instead of `{}`".format( + path, details["enum"], property_value ) - # raise TypeError(msg.format(property_name, details["enum"], property_value, self.__class__)) + self._append_error(msg) if details["type"] in common_data_types and "format" not in details: - msg = "property {} shall be of type {} at {}".format(property_name, details["type"], self.__class__) - self.types_validation(property_value, details["type"], msg, details.get("itemtype"), details.get("minimum"), details.get("maximum"), - details.get("minLength"), details.get("maxLength")) + self.types_validation( + property_value, details["type"], path, details.get("itemtype"), + details.get("minimum", details.get("minLength")), + details.get("maximum", details.get("maxLength")) + ) if details["type"] not in common_data_types: class_name = details["type"] @@ -574,36 +571,35 @@ def _validate_types(self, property_name, property_value): module = importlib.import_module(self.__module__) object_class = getattr(module, class_name) if not isinstance(property_value, object_class): - msg = "property {} shall be of type {}," " but got {} at {}" + msg = "value of `{}` must be a valid {} type, instead of `{}`" self._append_error( - msg.format(property_name, class_name, type(property_value), self.__class__) + msg.format(path, class_name, type(property_value)) ) - # raise TypeError(msg.format(property_name, class_name, type(property_value), self.__class__)) if "format" in details: - msg = "Invalid {} format on property {}, expected {} at {}".format( - property_value, property_name, details["format"], self.__class__ - ) _type = details["type"] if details["type"] is list else details["format"] - self.types_validation(property_value, _type, msg, details["format"], details.get("minimum"), details.get("maximum"), - details.get("minLength"), details.get("maxLength")) + self.types_validation( + property_value, _type, path, details["format"], + details.get("minimum", details.get("minLength")), + details.get("maximum", details.get("maxLength")) + ) - def _validate(self, skip_exception=False): - self._validate_required() + def _validate(self, path, skip_exception=False): + self._validate_required(path) for key, value in self._properties.items(): if isinstance(value, OpenApiObject): - value._validate(True) + value._validate(path + ".%s" % key, True) elif isinstance(value, OpenApiIter): - for item in value: + for ind, item in enumerate(value): if not isinstance(item, OpenApiObject): continue - item._validate(True) - self._validate_types(key, value) + item._validate(path + ".%s[%d]" % (key, ind), True) + self._validate_types(path + ".%s" % (key), key, value) if skip_exception: return self._validation_errors self._raise_validation() def validate(self): - return self._validate() + return self._validate(self._JSON_NAME) def get(self, name, with_default=False): """ diff --git a/openapiart/generator.py b/openapiart/generator.py index 0e45b6bd..238b7fae 100644 --- a/openapiart/generator.py +++ b/openapiart/generator.py @@ -704,11 +704,20 @@ def _get_external_field_name(self, openapi_name): if external in ["String"]: external += "_" return external + + def _small_first_char(self, words): + if isinstance(words, list): + ret = [] + for wrd in words: + ret.append(wrd[0].lower() + wrd[1:]) + return ret + return words[0].lower() + words[1:] def _write_openapi_object(self, ref, choice_method_name=None): schema_object = self._get_object_from_ref(ref) ref_name = ref.split("/")[-1] class_name = ref_name.replace(".", "") + json_name = "_".join(self._small_first_char(ref_name.split("."))) if class_name in self._generated_classes: return self._generated_classes.append(class_name) @@ -723,7 +732,7 @@ def _write_openapi_object(self, ref, choice_method_name=None): if "choice" in self._get_choice_names(schema_object): slots.append("'_choice'") self._write(1, "__slots__ = (%s)" % ",".join(slots)) - self._write() + self._write(1, "_JSON_NAME = \"%s\"" % json_name) # write _TYPES definition # TODO: this func won't detect whether $ref for a given property is diff --git a/openapiart/tests/test_func.py b/openapiart/tests/test_func.py index f5f59a2f..65ff3440 100644 --- a/openapiart/tests/test_func.py +++ b/openapiart/tests/test_func.py @@ -104,7 +104,7 @@ def test_x_pattern_ipv4_good_and_bad_list(default_config, ipv4): default_config.ipv4_pattern.serialize(default_config.DICT) pytest.fail("ipv4 values got serialize") except Exception as e: - if "['-255.-255.-255.-255']" not in str(e): + if "`-255.-255.-255.-255`" not in str(e): pytest.fail("Invalid ipv4 list is not proper in error message") @@ -115,7 +115,7 @@ def test_x_pattern_ipv6_good_and_bad_list(default_config, ipv6): default_config.ipv6_pattern.serialize(default_config.DICT) pytest.fail("ipv6 values got serialize") except Exception as e: - if "[':', 'abcd::abcd::']" not in str(e): + if "`abcd::abcd::`" not in str(e) or "`:`" not in str(e): pytest.fail("Invalid ipv6 list is not proper in error message") @@ -126,7 +126,7 @@ def test_x_pattern_mac_good_and_bad_list(default_config, mac): default_config.mac_pattern.serialize(default_config.DICT) pytest.fail("mac values got serialize") except Exception as e: - if "[':', 'abcd::abcd::']" not in str(e): + if "`abcd::abcd::`" not in str(e) or "`:`" not in str(e): pytest.fail("Invalid mac list is not proper in error message") @@ -139,7 +139,8 @@ def test_x_pattern_integer_good_and_bad_list(default_config, integer): default_config.integer_pattern.serialize(default_config.DICT) pytest.fail("integer values got serialize") except Exception as e: - if "['abcd::abcd::', 256, 'ab:ab:ab:ab:ab:ab']" not in str(e): + if "`abcd::abcd::`" not in str(e) \ + or "`256`" not in str(e) or "`ab:ab:ab:ab:ab:ab`" not in str(e): pytest.fail("Invalid integer list is not proper in error message") diff --git a/openapiart/tests/test_py_go_diff.py b/openapiart/tests/test_py_go_diff.py index 4b19175d..221feaa1 100644 --- a/openapiart/tests/test_py_go_diff.py +++ b/openapiart/tests/test_py_go_diff.py @@ -28,30 +28,41 @@ def test_validation_errors(): p.validate() pytest.fail except Exception as e: - assert "a is a mandatory property" in str(e) - assert "b is a mandatory property" in str(e) - assert "c is a mandatory property" in str(e) - assert "required_object is a mandatory property" in str(e) - assert "e_a is a mandatory property" in str(e) - assert "e_b is a mandatory property" in str(e) + assert "required field `prefix_config.a` must not be empty" in str(e) + assert "required field `prefix_config.b` must not be empty" in str(e) + assert "required field `prefix_config.c` must not be empty" in str(e) + assert "required field `prefix_config.required_object` must not be empty" in str(e) + assert "required field `prefix_config.e.e_a` must not be empty" in str(e) + assert "required field `prefix_config.e.e_b` must not be empty" in str(e) p.e.e_a = "abc" try: p.validate() except Exception as e: - assert "Invalid abc format on property e_a" in str(e) + print(e) + assert "value of `prefix_config.e.e_a` must be a valid float type, instead of `abc`" in str(e) p.a = "abc" p.b = 10.1 p.c = 20 p.required_object.e_a = 10.1 p.required_object.e_b = 20 p.j.add().j_a - errors = p._validate(True) - assert len([True for e in errors if "e_b is a mandatory property" in e]) == 2 + p.mac_pattern.mac.values = ["1", "20"] + p.ipv4_pattern.ipv4.value = "1.1" + errors = p._validate(p._JSON_NAME, True) + assert len([True for e in errors if ".e_b` must not be empty" in e]) == 2 + assert "required field `prefix_config.j[0].e_a` must not be empty" in errors + assert "required field `prefix_config.e.e_b` must not be empty" in errors + assert "value of `prefix_config.e.e_a` must be a valid float type, instead of `abc`" in errors + assert "required field `prefix_config.j[0].e_a` must not be empty" in errors + assert "required field `prefix_config.j[0].e_b` must not be empty" in errors + assert "value of `prefix_config.mac_pattern.mac.values[0]` must be a valid mac address, instead of `1`" in errors + assert "value of `prefix_config.mac_pattern.mac.values[1]` must be a valid mac address, instead of `20`" in errors + assert "value of `prefix_config.ipv4_pattern.ipv4.value` must be a valid ipv4 address, instead of `1.1`" in errors def test_enum_setter(): p = module.Api().prefix_config() p.response = "abc" - errors = p._validate(True) + errors = p._validate(p._JSON_NAME, True) assert "abc is not a valid enum for property response" in errors From b2225fd64025fadbb0409d0f12ebe05419bcf047 Mon Sep 17 00:00:00 2001 From: Ranga-R Date: Fri, 25 Feb 2022 15:48:55 +0530 Subject: [PATCH 22/33] Python lint errors --- openapiart/generator.py | 10 ++++-- openapiart/openapiartgo.py | 50 ++++++++++++++++---------- openapiart/tests/test_formats.py | 4 +-- openapiart/tests/test_func.py | 10 +++--- openapiart/tests/test_py_go_diff.py | 55 ++++++++++++++++++++++------- 5 files changed, 88 insertions(+), 41 deletions(-) diff --git a/openapiart/generator.py b/openapiart/generator.py index 238b7fae..9243ab23 100644 --- a/openapiart/generator.py +++ b/openapiart/generator.py @@ -704,7 +704,7 @@ def _get_external_field_name(self, openapi_name): if external in ["String"]: external += "_" return external - + def _small_first_char(self, words): if isinstance(words, list): ret = [] @@ -732,7 +732,7 @@ def _write_openapi_object(self, ref, choice_method_name=None): if "choice" in self._get_choice_names(schema_object): slots.append("'_choice'") self._write(1, "__slots__ = (%s)" % ",".join(slots)) - self._write(1, "_JSON_NAME = \"%s\"" % json_name) + self._write(1, '_JSON_NAME = "%s"' % json_name) # write _TYPES definition # TODO: this func won't detect whether $ref for a given property is @@ -1081,7 +1081,11 @@ def _write_openapilist_special_methods( self._write() self._write(1, "def _instanceOf(self, item):") self._write(2, "if not isinstance(item, %s):" % (contained_class_name)) - self._write(3, "raise Exception(\"Item is not an instance of %s\")" % (contained_class_name)) + self._write( + 3, + 'raise Exception("Item is not an instance of %s")' + % (contained_class_name), + ) def _write_factory_method( self, diff --git a/openapiart/openapiartgo.py b/openapiart/openapiartgo.py index b87fe977..772691ac 100644 --- a/openapiart/openapiartgo.py +++ b/openapiart/openapiartgo.py @@ -431,7 +431,9 @@ def _build_api_interface(self): new.schema_name = self._get_schema_object_name_from_ref( ref[0].value ) - new.schema_raw_name = self._get_schema_json_name_from_ref(ref[0].value) + new.schema_raw_name = self._get_schema_json_name_from_ref( + ref[0].value + ) new.schema_object = self._get_schema_object_from_ref( ref[0].value ) @@ -1065,7 +1067,7 @@ def _write_interface(self, new): }} return str }} - + func (obj *{struct}) Clone() ({interface}, error) {{ newObj := New{interface}() pbText, err := obj.ToPbText() @@ -1086,7 +1088,7 @@ def _write_interface(self, new): if len(internal_items) == 0 else "\n".join(internal_items), nil_call="obj.setNil()" if len(internal_items_nil) > 0 else "", - obj_name=new.schema_raw_name + obj_name=new.schema_raw_name, ) ) if len(internal_items_nil) > 0: @@ -1782,8 +1784,12 @@ def _build_setters_getters(self, fluent_new): if field.type == "[]byte": field.name = "Bytes" elif "$ref" in property_schema: - schema_name = self._get_schema_object_name_from_ref(property_schema["$ref"]) - field.schema_name = self._get_schema_json_name_from_ref(property_schema["$ref"]) + schema_name = self._get_schema_object_name_from_ref( + property_schema["$ref"] + ) + field.schema_name = self._get_schema_json_name_from_ref( + property_schema["$ref"] + ) field.name = self._get_external_struct_name(schema_name) field.isOptional = fluent_new.isOptional(property_name) field.isPointer = ( @@ -1988,7 +1994,7 @@ def _validate_types(self, new, field): validation = append(validation, fmt.Sprintf("required field `%s.{field_name}` must not be empty", path)) }} """.format( - name=field.name, intf_name=new.schema_raw_name, + name=field.name, field_name=field.schema_name, value=0 if field.isEnum and field.isArray is False else value, enum=".Number()" @@ -2020,7 +2026,9 @@ def _validate_types(self, new, field): ).format( name=field.schema_name, interface=new.schema_raw_name, - max="max({})".format(field.type.lstrip("[]")) if field.max is None else field.max, + max="max({})".format(field.type.lstrip("[]")) + if field.max is None + else field.max, pointer="*" if field.isPointer else "", min="min({})".format(field.type.lstrip("[]")) if field.min is None @@ -2059,7 +2067,9 @@ def _validate_types(self, new, field): ).format( name=field.schema_name, interface=new.schema_name, - max_length="any" if field.max_length is None else field.max_length, + max_length="any" + if field.max_length is None + else field.max_length, pointer="*" if field.isPointer else "", min_length=field.min_length if field.min_length is None @@ -2090,11 +2100,12 @@ def _validate_types(self, new, field): validation = append(validation, err.Error()) }} """.format( - name=field.name, interface=new.schema_name, - field_name=field.schema_name, - format=field.format.capitalize() if field.isArray is False - else field.format.capitalize() + "Slice", - ) + name=field.name, + field_name=field.schema_name, + format=field.format.capitalize() + if field.isArray is False + else field.format.capitalize() + "Slice", + ) # Enum will be handled via wrapper lib if inner_body == "": return body @@ -2117,12 +2128,15 @@ def _validate_struct(self, new, field): validation, fmt.Sprintf("required field `%s.{field_name}` must not be empty", path)) }} - """.format(name=field.name, interface=new.schema_raw_name, field_name=field.schema_name) + """.format( + name=field.name, + field_name=field.schema_name, + ) inner_body = """obj.{external_name}().validateObj( set_default, fmt.Sprintf("%s.{json_name}", path))""".format( external_name=self._get_external_struct_name(field.name), - json_name=field.schema_name + json_name=field.schema_name, ) if field.isArray: inner_body = """ @@ -2138,8 +2152,6 @@ def _validate_struct(self, new, field): """.format( name=field.name, field_name=field.schema_name, - field_type=field.type, - internal_items_name="{}Slice".format(field.struct), field_internal_struct=field.struct, ) body += """ @@ -2384,13 +2396,13 @@ def _write_default_method(self, new): struct=new.struct, body=body ) ) - + def _get_schema_json_name_from_ref(self, ref): final_piece = ref.split("/")[-1] if "." in final_piece: return final_piece.replace(".", "_").lower() return self._lower_first_char(final_piece) - + def _lower_first_char(self, word): return word[0].lower() + word[1:] diff --git a/openapiart/tests/test_formats.py b/openapiart/tests/test_formats.py index 8818f0ec..b9f80f4f 100644 --- a/openapiart/tests/test_formats.py +++ b/openapiart/tests/test_formats.py @@ -20,7 +20,7 @@ def test_formats_bad_string(config, value): config.l.string_param = value try: config.deserialize(config.serialize(encoding=config.YAML)) - pytest.fail("Value {value} was successfully validated".format(value)) + pytest.fail("Value {} was successfully validated".format(value)) except Exception: pass @@ -50,7 +50,7 @@ def test_formats_good_ipv4(config, value): config.l.ipv4 = value try: config.deserialize(config.serialize(encoding=config.YAML)) - except Exception as e: + except Exception: pytest.fail("Value {} was not valid".format(value)) diff --git a/openapiart/tests/test_func.py b/openapiart/tests/test_func.py index 65ff3440..a9097b40 100644 --- a/openapiart/tests/test_func.py +++ b/openapiart/tests/test_func.py @@ -139,8 +139,11 @@ def test_x_pattern_integer_good_and_bad_list(default_config, integer): default_config.integer_pattern.serialize(default_config.DICT) pytest.fail("integer values got serialize") except Exception as e: - if "`abcd::abcd::`" not in str(e) \ - or "`256`" not in str(e) or "`ab:ab:ab:ab:ab:ab`" not in str(e): + if ( + "`abcd::abcd::`" not in str(e) + or "`256`" not in str(e) + or "`ab:ab:ab:ab:ab:ab`" not in str(e) + ): pytest.fail("Invalid integer list is not proper in error message") @@ -160,7 +163,7 @@ def test_x_pattern_good_inc_dec(default_config, index, direction): dir_obj.count = count[index] try: default_config.serialize(default_config.DICT) - except Exception as e: + except Exception: pytest.fail("%s with %s Failed to serialize" % (enum, direction)) @@ -211,4 +214,3 @@ def test_enum_setter(api, default_config): if __name__ == "__main__": pytest.main(["-v", "-s", __file__]) - diff --git a/openapiart/tests/test_py_go_diff.py b/openapiart/tests/test_py_go_diff.py index 221feaa1..eaaa7501 100644 --- a/openapiart/tests/test_py_go_diff.py +++ b/openapiart/tests/test_py_go_diff.py @@ -1,5 +1,6 @@ import importlib import pytest + module = importlib.import_module("sanity") @@ -31,16 +32,26 @@ def test_validation_errors(): assert "required field `prefix_config.a` must not be empty" in str(e) assert "required field `prefix_config.b` must not be empty" in str(e) assert "required field `prefix_config.c` must not be empty" in str(e) - assert "required field `prefix_config.required_object` must not be empty" in str(e) - assert "required field `prefix_config.e.e_a` must not be empty" in str(e) - assert "required field `prefix_config.e.e_b` must not be empty" in str(e) - + assert ( + "required field `prefix_config.required_object` must not be empty" + in str(e) + ) + assert "required field `prefix_config.e.e_a` must not be empty" in str( + e + ) + assert "required field `prefix_config.e.e_b` must not be empty" in str( + e + ) + p.e.e_a = "abc" try: p.validate() except Exception as e: print(e) - assert "value of `prefix_config.e.e_a` must be a valid float type, instead of `abc`" in str(e) + assert ( + "value of `prefix_config.e.e_a` must be a valid float type, instead of `abc`" + in str(e) + ) p.a = "abc" p.b = 10.1 p.c = 20 @@ -51,15 +62,33 @@ def test_validation_errors(): p.ipv4_pattern.ipv4.value = "1.1" errors = p._validate(p._JSON_NAME, True) assert len([True for e in errors if ".e_b` must not be empty" in e]) == 2 - assert "required field `prefix_config.j[0].e_a` must not be empty" in errors + assert ( + "required field `prefix_config.j[0].e_a` must not be empty" in errors + ) assert "required field `prefix_config.e.e_b` must not be empty" in errors - assert "value of `prefix_config.e.e_a` must be a valid float type, instead of `abc`" in errors - assert "required field `prefix_config.j[0].e_a` must not be empty" in errors - assert "required field `prefix_config.j[0].e_b` must not be empty" in errors - assert "value of `prefix_config.mac_pattern.mac.values[0]` must be a valid mac address, instead of `1`" in errors - assert "value of `prefix_config.mac_pattern.mac.values[1]` must be a valid mac address, instead of `20`" in errors - assert "value of `prefix_config.ipv4_pattern.ipv4.value` must be a valid ipv4 address, instead of `1.1`" in errors - + assert ( + "value of `prefix_config.e.e_a` must be a valid float type, instead of `abc`" + in errors + ) + assert ( + "required field `prefix_config.j[0].e_a` must not be empty" in errors + ) + assert ( + "required field `prefix_config.j[0].e_b` must not be empty" in errors + ) + assert ( + "value of `prefix_config.mac_pattern.mac.values[0]` must be a valid mac address, instead of `1`" + in errors + ) + assert ( + "value of `prefix_config.mac_pattern.mac.values[1]` must be a valid mac address, instead of `20`" + in errors + ) + assert ( + "value of `prefix_config.ipv4_pattern.ipv4.value` must be a valid ipv4 address, instead of `1.1`" + in errors + ) + def test_enum_setter(): p = module.Api().prefix_config() From a606fa99f43735a082f5995d19d9d8e69837262f Mon Sep 17 00:00:00 2001 From: Rangababu-R Date: Mon, 4 Apr 2022 14:57:42 +0530 Subject: [PATCH 23/33] Deprecated warning message for properties --- openapiart/common.go | 16 ++++++++++++++++ openapiart/common.py | 14 ++++++++++++++ openapiart/generator.py | 9 +++++++++ openapiart/openapiartgo.py | 25 +++++++++++++++++++++++++ pkg/common.go | 6 ++++++ 5 files changed, 70 insertions(+) diff --git a/openapiart/common.go b/openapiart/common.go index acdce537..901bd906 100644 --- a/openapiart/common.go +++ b/openapiart/common.go @@ -100,6 +100,8 @@ type Api interface { NewHttpTransport() HttpTransport hasHttpTransport() bool Close() error + GetApiWarnings() []string + ClearApiWarnings() } // NewGrpcTransport sets the underlying transport of the Api as grpc @@ -137,6 +139,14 @@ func (api *api) hasHttpTransport() bool { return api.http != nil } +func (api *api) GetApiWarnings() []string { + return openapi_warnings +} + +func (api *api) ClearApiWarnings() { + openapi_warnings = nil +} + // HttpRequestDoer will return True for HTTP transport type httpRequestDoer interface { Do(req *http.Request) (*http.Response, error) @@ -161,6 +171,12 @@ func validationResult() error { return nil } +var openapi_warnings []string + +func deprecated(message string) { + openapi_warnings = append(openapi_warnings, message) +} + func validateMac(mac string, path string) error { macSlice := strings.Split(mac, ":") if len(macSlice) != 6 { diff --git a/openapiart/common.py b/openapiart/common.py index 5b0f9f22..802946a0 100644 --- a/openapiart/common.py +++ b/openapiart/common.py @@ -9,6 +9,8 @@ import sys import time import grpc +import functools +import platform from google.protobuf import json_format import sanity_pb2_grpc as pb2_grpc import sanity_pb2 as pb2 @@ -22,6 +24,18 @@ unicode = str +openapi_warnings = [] + +def deprecated(message=None): + openapi_warnings.append(message) + def caller(func): + @functools.wraps(func) + def inner(self, *args, **kwargs): + return func(self, *args, **kwargs) + return inner + return caller + + class Transport: HTTP = "http" GRPC = "grpc" diff --git a/openapiart/generator.py b/openapiart/generator.py index 9243ab23..febfdcf0 100644 --- a/openapiart/generator.py +++ b/openapiart/generator.py @@ -647,6 +647,15 @@ def _write_api_class(self, methods, factories): self._write() self._write(1, "def close(self):") self._write(2, "pass") + self._write() + self._write(1, "def get_api_warnings(self):") + self._write(2, "return openapi_warnings") + self._write() + self._write(1, "def clear_api_warnings(self):") + self._write(2, "if \"2.7\" in platform.python_version().rsplit(\".\", 1)[0]:") + self._write(3, "del openapi_warnings[:]") + self._write(2, "else:") + self._write(3, "openapi_warnings.clear()") def _get_object_property_class_names(self, ref): """Returns: `Tuple(object_name, property_name, class_name, ref_name)`""" diff --git a/openapiart/openapiartgo.py b/openapiart/openapiartgo.py index 772691ac..ecd5c827 100644 --- a/openapiart/openapiartgo.py +++ b/openapiart/openapiartgo.py @@ -106,6 +106,7 @@ def __init__(self): self.hasminmaxlength = False self.min_length = None self.max_length = None + self.status = None class OpenApiArtGo(OpenApiArtPlugin): @@ -1353,6 +1354,9 @@ def _write_field_getter(self, new, field): if set_enum_choice is not None else "", ) + status = False + if field.status is not None and field.status == "deprecated": + status = True self._write( """ // {fieldname} returns a {fieldtype}\n{description} @@ -1366,12 +1370,22 @@ def _write_field_getter(self, new, field): body=body, description=field.description, fieldtype=field.type, + # TODO message needs to modified once the py_go_diff PR is merged + # https://github.com/open-traffic-generator/openapiart/pull/281 + status="" if status is False + else 'deprecated("{interface}.{fieldname} is deprecated")'.format( + interface=new.interface, + fieldname=field.name + ) ) ) def _write_field_setter(self, new, field, set_nil): if field.setter_method is None: return + status = False + if field.status is not None and field.status == "deprecated": + status = True if field.isArray and field.isEnum: body = """items := []{pb_pkg_name}.{interface}_{fieldname}_Enum{{}} @@ -1559,6 +1573,7 @@ def _write_field_setter(self, new, field, set_nil): """ // Set{fieldname} sets the {fieldtype} value in the {fieldstruct} object\n{description} func (obj *{newstruct}) {setter_method} {{ + {status} {set_choice} {body} return obj @@ -1572,6 +1587,13 @@ def _write_field_setter(self, new, field, set_nil): fieldtype=field.type, fieldstruct=new.interface, set_choice=set_choice, + # TODO message needs to modified once the py_go_diff PR is merged + # https://github.com/open-traffic-generator/openapiart/pull/281 + status="" if status is False + else 'deprecated("{interface}.{fieldname} is deprecated")'.format( + interface=new.interface, + fieldname=field.name + ) ) ) @@ -1725,6 +1747,7 @@ def _build_setters_getters(self, fluent_new): field.name = self._get_external_field_name(property_name) field.schema_name = property_name field.type = self._get_struct_field_type(property_schema, field) + field.status = property_schema.get("x-status") if ( len(choice_enums) == 1 and property_name in choice_enums[0].value @@ -2167,6 +2190,8 @@ def _validate_struct(self, new, field): return body def _write_validate_method(self, new): + # TODO add the deprecated code once the py_go_diff PR is merged + # https://github.com/open-traffic-generator/openapiart/pull/281 statements = [] def p(): diff --git a/pkg/common.go b/pkg/common.go index df86b0ff..67540fed 100644 --- a/pkg/common.go +++ b/pkg/common.go @@ -164,6 +164,12 @@ func validationResult() error { return nil } +var openapi_warnings []string + +func deprecated(message string) { + openapi_warnings = append(openapi_warnings, message) +} + func validateMac(mac string, path string) error { macSlice := strings.Split(mac, ":") if len(macSlice) != 6 { From 8fef9d40f8b4551d50188a0082df59c85e1a7ed6 Mon Sep 17 00:00:00 2001 From: Rangababu-R Date: Mon, 4 Apr 2022 15:23:48 +0530 Subject: [PATCH 24/33] black format --- openapiart/generator.py | 4 +++- openapiart/openapiartgo.py | 17 +++++++++-------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/openapiart/generator.py b/openapiart/generator.py index febfdcf0..4ce4a002 100644 --- a/openapiart/generator.py +++ b/openapiart/generator.py @@ -652,7 +652,9 @@ def _write_api_class(self, methods, factories): self._write(2, "return openapi_warnings") self._write() self._write(1, "def clear_api_warnings(self):") - self._write(2, "if \"2.7\" in platform.python_version().rsplit(\".\", 1)[0]:") + self._write( + 2, 'if "2.7" in platform.python_version().rsplit(".", 1)[0]:' + ) self._write(3, "del openapi_warnings[:]") self._write(2, "else:") self._write(3, "openapi_warnings.clear()") diff --git a/openapiart/openapiartgo.py b/openapiart/openapiartgo.py index ecd5c827..e422e2fc 100644 --- a/openapiart/openapiartgo.py +++ b/openapiart/openapiartgo.py @@ -1361,6 +1361,7 @@ def _write_field_getter(self, new, field): """ // {fieldname} returns a {fieldtype}\n{description} func (obj *{struct}) {getter_method} {{ + {status} {body} }} """.format( @@ -1372,11 +1373,11 @@ def _write_field_getter(self, new, field): fieldtype=field.type, # TODO message needs to modified once the py_go_diff PR is merged # https://github.com/open-traffic-generator/openapiart/pull/281 - status="" if status is False + status="" + if status is False else 'deprecated("{interface}.{fieldname} is deprecated")'.format( - interface=new.interface, - fieldname=field.name - ) + interface=new.interface, fieldname=field.name + ), ) ) @@ -1589,11 +1590,11 @@ def _write_field_setter(self, new, field, set_nil): set_choice=set_choice, # TODO message needs to modified once the py_go_diff PR is merged # https://github.com/open-traffic-generator/openapiart/pull/281 - status="" if status is False + status="" + if status is False else 'deprecated("{interface}.{fieldname} is deprecated")'.format( - interface=new.interface, - fieldname=field.name - ) + interface=new.interface, fieldname=field.name + ), ) ) From 3e06df8c654d8486b488f339512a48803d41ae17 Mon Sep 17 00:00:00 2001 From: Rangababu-R Date: Tue, 5 Apr 2022 15:05:09 +0530 Subject: [PATCH 25/33] update --- openapiart/common.py | 3 ++- openapiart/generator.py | 13 ++++++++++++- openapiart/openapiartgo.py | 15 +++++++++++---- pkg/common.go | 10 ++++++++++ 4 files changed, 35 insertions(+), 6 deletions(-) diff --git a/openapiart/common.py b/openapiart/common.py index 802946a0..a3d28f62 100644 --- a/openapiart/common.py +++ b/openapiart/common.py @@ -27,7 +27,8 @@ openapi_warnings = [] def deprecated(message=None): - openapi_warnings.append(message) + if message not in openapi_warnings: + openapi_warnings.append(message) def caller(func): @functools.wraps(func) def inner(self, *args, **kwargs): diff --git a/openapiart/generator.py b/openapiart/generator.py index 4ce4a002..83b6162b 100644 --- a/openapiart/generator.py +++ b/openapiart/generator.py @@ -912,7 +912,8 @@ def _process_properties( if "$ref" not in schema_object["properties"][choice_name]: continue ref = schema_object["properties"][choice_name]["$ref"] - self._write_factory_method(None, choice_name, ref) + status = schema_object["properties"][choice_name].get("x-status") + self._write_factory_method(None, choice_name, ref, property_status=status) excluded_property_names.append(choice_name) for property_name in schema_object["properties"]: if property_name in excluded_property_names: @@ -1105,6 +1106,7 @@ def _write_factory_method( ref, openapi_list=False, choice_method=False, + property_status=None, ): yobject = self._get_object_from_ref(ref) _, _, class_name, _ = self._get_object_property_class_names(ref) @@ -1160,6 +1162,8 @@ def _write_factory_method( self._write() else: self._write(1, "@property") + if property_status is not None and property_status == "deprecated": + self._write(1, "@deprecated(message=\"{}.%s is deprecated\".format(_JSON_NAME))" % method_name) self._write(1, "def %s(self):" % (method_name)) self._write(2, "# type: () -> %s" % (class_name)) self._write( @@ -1282,6 +1286,8 @@ def _write_openapi_property( type_name = restriction self._write() self._write(1, "@property") + if property.get("x-status") is not None and property.get("x-status") == "deprecated": + self._write(1, "@deprecated(message=\"{}.%s is deprecated\".format(_JSON_NAME))" % name) self._write(1, "def %s(self):" % name) self._write(2, "# type: () -> %s" % (type_name)) self._write(2, '"""%s getter' % (name)) @@ -1297,6 +1303,11 @@ def _write_openapi_property( self._write(2, "return self._get_property('%s')" % (name)) self._write() self._write(1, "@%s.setter" % name) + if property.get("x-status") is not None and property.get("x-status") == "deprecated": + self._write( + 1, + "@deprecated(message=\"{}.%s is deprecated\".format(_JSON_NAME))" % name + ) self._write(1, "def %s(self, value):" % name) self._write(2, '"""%s setter' % (name)) self._write() diff --git a/openapiart/openapiartgo.py b/openapiart/openapiartgo.py index e422e2fc..3b656e8e 100644 --- a/openapiart/openapiartgo.py +++ b/openapiart/openapiartgo.py @@ -1,3 +1,4 @@ +from ntpath import join from .openapiartplugin import OpenApiArtPlugin import os import subprocess @@ -1376,7 +1377,7 @@ def _write_field_getter(self, new, field): status="" if status is False else 'deprecated("{interface}.{fieldname} is deprecated")'.format( - interface=new.interface, fieldname=field.name + interface=new.schema_raw_name, fieldname=field.schema_name ), ) ) @@ -1593,7 +1594,7 @@ def _write_field_setter(self, new, field, set_nil): status="" if status is False else 'deprecated("{interface}.{fieldname} is deprecated")'.format( - interface=new.interface, fieldname=field.name + interface=new.schema_raw_name, fieldname=field.schema_name ), ) ) @@ -2205,9 +2206,13 @@ def p(): req="Optional" if field.isOptional else "required", ) ) - + deprecate_msgs = [] for field in new.interface_fields: valid = 0 + if field.status is not None and field.status == "deprecated": + deprecate_msgs.append(""" + deprecated(fmt.Sprintf("%s.{field_name} is deprecated", path)) + """.format(field_name=field.schema_name)) if field.type.lstrip("[]") in self._oapi_go_types.values(): block = self._validate_types(new, field) if block is None or block.strip() == "": @@ -2229,10 +2234,12 @@ def p(): if set_default {{ obj.setDefault() }} + {deprecate} {body} }} """.format( - struct=new.struct, body=body + struct=new.struct, body=body, + deprecate="" if deprecate_msgs == [] else "\n".join(deprecate_msgs) ) ) diff --git a/pkg/common.go b/pkg/common.go index 67540fed..f75d3d11 100644 --- a/pkg/common.go +++ b/pkg/common.go @@ -103,6 +103,8 @@ type Api interface { NewHttpTransport() HttpTransport hasHttpTransport() bool Close() error + GetApiWarnings() []string + ClearApiWarnings() } // NewGrpcTransport sets the underlying transport of the Api as grpc @@ -140,6 +142,14 @@ func (api *api) hasHttpTransport() bool { return api.http != nil } +func (api *api) GetApiWarnings() []string { + return openapi_warnings +} + +func (api *api) ClearApiWarnings() { + openapi_warnings = nil +} + // HttpRequestDoer will return True for HTTP transport type httpRequestDoer interface { Do(req *http.Request) (*http.Response, error) From 44ca7a59a81b3895f6e3713959e2a74a0844b75e Mon Sep 17 00:00:00 2001 From: Rangababu-R Date: Tue, 5 Apr 2022 15:11:24 +0530 Subject: [PATCH 26/33] Black format --- openapiart/generator.py | 33 ++++++++++++++++++++++++++------- openapiart/openapiartgo.py | 16 ++++++++++++---- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/openapiart/generator.py b/openapiart/generator.py index 83b6162b..851e33b0 100644 --- a/openapiart/generator.py +++ b/openapiart/generator.py @@ -912,8 +912,12 @@ def _process_properties( if "$ref" not in schema_object["properties"][choice_name]: continue ref = schema_object["properties"][choice_name]["$ref"] - status = schema_object["properties"][choice_name].get("x-status") - self._write_factory_method(None, choice_name, ref, property_status=status) + status = schema_object["properties"][choice_name].get( + "x-status" + ) + self._write_factory_method( + None, choice_name, ref, property_status=status + ) excluded_property_names.append(choice_name) for property_name in schema_object["properties"]: if property_name in excluded_property_names: @@ -1163,7 +1167,11 @@ def _write_factory_method( else: self._write(1, "@property") if property_status is not None and property_status == "deprecated": - self._write(1, "@deprecated(message=\"{}.%s is deprecated\".format(_JSON_NAME))" % method_name) + self._write( + 1, + '@deprecated(message="{}.%s is deprecated".format(_JSON_NAME))' + % method_name, + ) self._write(1, "def %s(self):" % (method_name)) self._write(2, "# type: () -> %s" % (class_name)) self._write( @@ -1286,8 +1294,15 @@ def _write_openapi_property( type_name = restriction self._write() self._write(1, "@property") - if property.get("x-status") is not None and property.get("x-status") == "deprecated": - self._write(1, "@deprecated(message=\"{}.%s is deprecated\".format(_JSON_NAME))" % name) + if ( + property.get("x-status") is not None + and property.get("x-status") == "deprecated" + ): + self._write( + 1, + '@deprecated(message="{}.%s is deprecated".format(_JSON_NAME))' + % name, + ) self._write(1, "def %s(self):" % name) self._write(2, "# type: () -> %s" % (type_name)) self._write(2, '"""%s getter' % (name)) @@ -1303,10 +1318,14 @@ def _write_openapi_property( self._write(2, "return self._get_property('%s')" % (name)) self._write() self._write(1, "@%s.setter" % name) - if property.get("x-status") is not None and property.get("x-status") == "deprecated": + if ( + property.get("x-status") is not None + and property.get("x-status") == "deprecated" + ): self._write( 1, - "@deprecated(message=\"{}.%s is deprecated\".format(_JSON_NAME))" % name + '@deprecated(message="{}.%s is deprecated".format(_JSON_NAME))' + % name, ) self._write(1, "def %s(self, value):" % name) self._write(2, '"""%s setter' % (name)) diff --git a/openapiart/openapiartgo.py b/openapiart/openapiartgo.py index 3b656e8e..ed365e72 100644 --- a/openapiart/openapiartgo.py +++ b/openapiart/openapiartgo.py @@ -2206,13 +2206,18 @@ def p(): req="Optional" if field.isOptional else "required", ) ) + deprecate_msgs = [] for field in new.interface_fields: valid = 0 if field.status is not None and field.status == "deprecated": - deprecate_msgs.append(""" + deprecate_msgs.append( + """ deprecated(fmt.Sprintf("%s.{field_name} is deprecated", path)) - """.format(field_name=field.schema_name)) + """.format( + field_name=field.schema_name + ) + ) if field.type.lstrip("[]") in self._oapi_go_types.values(): block = self._validate_types(new, field) if block is None or block.strip() == "": @@ -2238,8 +2243,11 @@ def p(): {body} }} """.format( - struct=new.struct, body=body, - deprecate="" if deprecate_msgs == [] else "\n".join(deprecate_msgs) + struct=new.struct, + body=body, + deprecate="" + if deprecate_msgs == [] + else "\n".join(deprecate_msgs), ) ) From 21e897357fbc3ff03e580df385802253f813350d Mon Sep 17 00:00:00 2001 From: Rangababu-R Date: Wed, 6 Apr 2022 13:31:15 +0530 Subject: [PATCH 27/33] modified x-status from enum to object --- openapiart/bundler.py | 10 ++++-- openapiart/generator.py | 52 +++++++++++++++++++++-------- openapiart/openapiartgo.py | 20 +++++------ openapiart/openapiartprotobuf.py | 2 +- openapiart/tests/config/config.yaml | 15 ++++++--- 5 files changed, 65 insertions(+), 34 deletions(-) diff --git a/openapiart/bundler.py b/openapiart/bundler.py index 7b10f5a0..7e249fb7 100644 --- a/openapiart/bundler.py +++ b/openapiart/bundler.py @@ -552,8 +552,11 @@ def _resolve_x_status(self): """ import jsonpath_ng + # st = self._get_parser("$..x-status").find(self._content) + # if st: + # import pdb; pdb.set_trace() for xstatus in self._get_parser("$..x-status").find(self._content): - if xstatus.value == "current": + if xstatus.value.get("status") == "current": continue print("resolving %s..." % (str(xstatus.full_path))) parent_schema_object = jsonpath_ng.Parent().find(xstatus)[0].value @@ -561,8 +564,9 @@ def _resolve_x_status(self): parent_schema_object["description"] = "TBD" parent_schema_object[ "description" - ] = "Status: {status}\n{description}".format( - status=xstatus.value, + ] = "Status: {status}\n{add_info}\n{description}".format( + status=xstatus.value.get("status"), + add_info=xstatus.value.get("additional_info", ""), description=parent_schema_object["description"], ) diff --git a/openapiart/generator.py b/openapiart/generator.py index 851e33b0..48359440 100644 --- a/openapiart/generator.py +++ b/openapiart/generator.py @@ -1295,14 +1295,26 @@ def _write_openapi_property( self._write() self._write(1, "@property") if ( - property.get("x-status") is not None - and property.get("x-status") == "deprecated" + property.get("x-status", {}).get("status", "current") + == "deprecated" ): - self._write( - 1, - '@deprecated(message="{}.%s is deprecated".format(_JSON_NAME))' - % name, - ) + if "\n" in property.get("x-status")["additional_information"]: + self._write(1, '@deprecated(message="""') + [ + self._write(3, line) + for line in property["x-status"][ + "additional_information" + ].split("\n") + ] + self._write(2, '"""') + self._write(1, ")") + else: + self._write( + 1, + '@deprecated(message="{}")'.format( + property["x-status"]["additional_information"] + ), + ) self._write(1, "def %s(self):" % name) self._write(2, "# type: () -> %s" % (type_name)) self._write(2, '"""%s getter' % (name)) @@ -1319,14 +1331,26 @@ def _write_openapi_property( self._write() self._write(1, "@%s.setter" % name) if ( - property.get("x-status") is not None - and property.get("x-status") == "deprecated" + property.get("x-status", {}).get("status", "current") + == "deprecated" ): - self._write( - 1, - '@deprecated(message="{}.%s is deprecated".format(_JSON_NAME))' - % name, - ) + if "\n" in property.get("x-status")["additional_information"]: + self._write(1, '@deprecated(message="""') + [ + self._write(3, line) + for line in property["x-status"][ + "additional_information" + ].split("\n") + ] + self._write(2, '"""') + self._write(1, ")") + else: + self._write( + 1, + '@deprecated(message="{}")'.format( + property["x-status"]["additional_information"] + ), + ) self._write(1, "def %s(self, value):" % name) self._write(2, '"""%s setter' % (name)) self._write() diff --git a/openapiart/openapiartgo.py b/openapiart/openapiartgo.py index ed365e72..31e0a090 100644 --- a/openapiart/openapiartgo.py +++ b/openapiart/openapiartgo.py @@ -108,6 +108,7 @@ def __init__(self): self.min_length = None self.max_length = None self.status = None + self.status_msg = None class OpenApiArtGo(OpenApiArtPlugin): @@ -1376,9 +1377,7 @@ def _write_field_getter(self, new, field): # https://github.com/open-traffic-generator/openapiart/pull/281 status="" if status is False - else 'deprecated("{interface}.{fieldname} is deprecated")'.format( - interface=new.schema_raw_name, fieldname=field.schema_name - ), + else "deprecated(`{msg}`)".format(msg=field.status_msg), ) ) @@ -1593,9 +1592,7 @@ def _write_field_setter(self, new, field, set_nil): # https://github.com/open-traffic-generator/openapiart/pull/281 status="" if status is False - else 'deprecated("{interface}.{fieldname} is deprecated")'.format( - interface=new.schema_raw_name, fieldname=field.schema_name - ), + else "deprecated(`{msg}`)".format(msg=field.status_msg), ) ) @@ -1749,7 +1746,10 @@ def _build_setters_getters(self, fluent_new): field.name = self._get_external_field_name(property_name) field.schema_name = property_name field.type = self._get_struct_field_type(property_schema, field) - field.status = property_schema.get("x-status") + field.status = property_schema.get("x-status", {}).get("status") + field.status_msg = property_schema.get("x-status", {}).get( + "additional_information" + ) if ( len(choice_enums) == 1 and property_name in choice_enums[0].value @@ -2212,11 +2212,7 @@ def p(): valid = 0 if field.status is not None and field.status == "deprecated": deprecate_msgs.append( - """ - deprecated(fmt.Sprintf("%s.{field_name} is deprecated", path)) - """.format( - field_name=field.schema_name - ) + "deprecated(`{msg}`)".format(msg=field.status_msg) ) if field.type.lstrip("[]") in self._oapi_go_types.values(): block = self._validate_types(new, field) diff --git a/openapiart/openapiartprotobuf.py b/openapiart/openapiartprotobuf.py index 1e88a2ea..efa9453a 100644 --- a/openapiart/openapiartprotobuf.py +++ b/openapiart/openapiartprotobuf.py @@ -175,7 +175,7 @@ def _write_header(self, info_object): ) ) self._write() - self._write('import "google/protobuf/descriptor.proto";') + # self._write('import "google/protobuf/descriptor.proto";') self._write('import "google/protobuf/empty.proto";') def _get_field_type(self, property_name, openapi_object): diff --git a/openapiart/tests/config/config.yaml b/openapiart/tests/config/config.yaml index d1c80413..be178ca5 100644 --- a/openapiart/tests/config/config.yaml +++ b/openapiart/tests/config/config.yaml @@ -28,12 +28,17 @@ components: enum: [status_200, status_400, status_404, status_500] default: status_200 a: - x-status: under-review + x-status: + status: under-review description: |- Small single line description type: string b: - x-status: deprecated + x-status: + status: deprecated + additional_information: |- + Deprecated message and use instead information and + deprecated from version and lib names can be provided description: |- Longer multi-line description Second line is here @@ -118,14 +123,16 @@ components: format: checksum length: 16 str_len: - x-status: under-review + x-status: + status: under-review description: |- string minimum&maximum Length type: string minLength: 3 maxLength: 6 hex_slice: - x-status: under-review + x-status: + status: under-review description: |- Array of Hex type: array From 4c1061f4a834e717d06b349300cf402bfd27bc66 Mon Sep 17 00:00:00 2001 From: Rangababu-R Date: Wed, 6 Apr 2022 17:13:27 +0530 Subject: [PATCH 28/33] py_deprecate_update --- openapiart/common.py | 37 ++++++++++++------ openapiart/generator.py | 60 ++++++++++++------------------ openapiart/tests/test_deprecate.py | 39 +++++++++++++++++++ 3 files changed, 88 insertions(+), 48 deletions(-) create mode 100644 openapiart/tests/test_deprecate.py diff --git a/openapiart/common.py b/openapiart/common.py index a3d28f62..d8adf1b8 100644 --- a/openapiart/common.py +++ b/openapiart/common.py @@ -9,7 +9,7 @@ import sys import time import grpc -import functools +import types import platform from google.protobuf import json_format import sanity_pb2_grpc as pb2_grpc @@ -26,16 +26,6 @@ openapi_warnings = [] -def deprecated(message=None): - if message not in openapi_warnings: - openapi_warnings.append(message) - def caller(func): - @functools.wraps(func) - def inner(self, *args, **kwargs): - return func(self, *args, **kwargs) - return inner - return caller - class Transport: HTTP = "http" @@ -164,6 +154,29 @@ def send_recv(self, method, relative_url, payload=None, return_object=None, head raise Exception(response.status_code, yaml.safe_load(response.text)) +class Deprecator: + messages = {} + + @classmethod + def deprecate(cls, key): + if cls.messages.get(key) is not None: + if cls.messages[key] in openapi_warnings: + return + openapi_warnings.append(cls.messages[key]) + + @staticmethod + def deprecated(func_or_data): + def inner(self, *args, **kwargs): + Deprecator.deprecate( + "{{}}.{{}}".format(type(self).__name__, func_or_data.__name__) + ) + func_or_data(self, *args, **kwargs) + + if isinstance(func_or_data, types.FunctionType): + return inner + Deprecator.deprecate(func_or_data) + + class OpenApiBase(object): """Base class for all generated classes""" @@ -489,6 +502,7 @@ def _encode(self): if key in self._TYPES and "format" in self._TYPES[key] and self._TYPES[key]["format"] == "int64": value = str(value) output[key] = value + Deprecator.deprecate("{}.{}".format(type(self).__name__, key)) return output def _decode(self, obj): @@ -517,6 +531,7 @@ def _decode(self, obj): if "format" in self._TYPES[property_name] and self._TYPES[property_name]["format"] == "int64": property_value = int(property_value) self._properties[property_name] = property_value + Deprecator.deprecate("{}.{}".format(type(self).__name__, property_name)) self._validate(self._JSON_NAME) return self diff --git a/openapiart/generator.py b/openapiart/generator.py index 48359440..6bc996aa 100644 --- a/openapiart/generator.py +++ b/openapiart/generator.py @@ -66,6 +66,7 @@ def __init__( self._protobuf_package_name = protobuf_package_name self._output_file = package_name self._docs_dir = os.path.join(self._src_dir, "..", "docs") + self._deprecated_properties = {} self._get_openapi_file() # self._plugins = self._load_plugins() @@ -174,6 +175,7 @@ def generate(self): self._write_http_api_class(methods) self._write_rpc_api_class(rpc_methods) self._write_init() + self._write_deprecator() return self def _get_base_url(self): @@ -927,7 +929,7 @@ def _process_properties( property_name in choice_names and property_name != "choice" ) self._write_openapi_property( - schema_object, property_name, property, write_set_choice + schema_object, property_name, property, class_name, write_set_choice ) for property_name, property in schema_object["properties"].items(): ref = self._get_parser("$..'$ref'").find(property) @@ -1169,7 +1171,7 @@ def _write_factory_method( if property_status is not None and property_status == "deprecated": self._write( 1, - '@deprecated(message="{}.%s is deprecated".format(_JSON_NAME))' + '@Deprecator.deprecated(message="{}.%s is deprecated".format(_JSON_NAME))' % method_name, ) self._write(1, "def %s(self):" % (method_name)) @@ -1279,7 +1281,7 @@ def _get_property_param_string(self, yobject): return (", ".join(property_param_string), properties, types) def _write_openapi_property( - self, schema_object, name, property, write_set_choice=False + self, schema_object, name, property, klass_name,write_set_choice=False ): ref = self._get_parser("$..'$ref'").find(property) restriction = self._get_type_restriction(property) @@ -1298,23 +1300,9 @@ def _write_openapi_property( property.get("x-status", {}).get("status", "current") == "deprecated" ): - if "\n" in property.get("x-status")["additional_information"]: - self._write(1, '@deprecated(message="""') - [ - self._write(3, line) - for line in property["x-status"][ - "additional_information" - ].split("\n") - ] - self._write(2, '"""') - self._write(1, ")") - else: - self._write( - 1, - '@deprecated(message="{}")'.format( - property["x-status"]["additional_information"] - ), - ) + self._write(1, "@Deprecator.deprecated") + key = "{}.{}".format(klass_name, name) + self._deprecated_properties[key] = property["x-status"]["additional_information"] self._write(1, "def %s(self):" % name) self._write(2, "# type: () -> %s" % (type_name)) self._write(2, '"""%s getter' % (name)) @@ -1334,23 +1322,9 @@ def _write_openapi_property( property.get("x-status", {}).get("status", "current") == "deprecated" ): - if "\n" in property.get("x-status")["additional_information"]: - self._write(1, '@deprecated(message="""') - [ - self._write(3, line) - for line in property["x-status"][ - "additional_information" - ].split("\n") - ] - self._write(2, '"""') - self._write(1, ")") - else: - self._write( - 1, - '@deprecated(message="{}")'.format( - property["x-status"]["additional_information"] - ), - ) + self._write(1, "@Deprecator.deprecated") + key = "{}.{}".format(klass_name, name) + self._deprecated_properties[key] = property["x-status"]["additional_information"] self._write(1, "def %s(self, value):" % name) self._write(2, '"""%s setter' % (name)) self._write() @@ -1629,3 +1603,15 @@ def _get_classname_from_ref(self, ref): def _write(self, indent=0, line=""): self._fid.write(" " * indent + line + "\n") + + def _write_deprecator(self): + with open(self._api_filename, "a") as self._fid: + self._write(0, "Deprecator.messages = {") + for klass,msg in self._deprecated_properties.items(): + if "\n" in msg: + print(msg) + self._write(1, '"{}" : """{}""",'.format(klass, msg)) + else: + self._write(1, '"{}" : "{}",'.format(klass, msg)) + self._write(1, "}") + diff --git a/openapiart/tests/test_deprecate.py b/openapiart/tests/test_deprecate.py new file mode 100644 index 00000000..c102b536 --- /dev/null +++ b/openapiart/tests/test_deprecate.py @@ -0,0 +1,39 @@ +def test_deprecate(api): + conf = api.prefix_config() + conf.required_object.e_a = 10 + conf.required_object.e_b = 20 + conf.a = "abc" + conf.b = 10.2 + conf.c = 30 + + msg = ( + "Deprecated message and use instead information and " + "deprecated from version and lib names can be provided" + ) + + def strip_data(message): + return " ".join([ln.strip() for ln in message.split("\n")]).strip() + + assert api.get_api_warnings() != [] + assert len(api.get_api_warnings()) == 1 + assert strip_data(api.get_api_warnings()[0]) == msg + + api.clear_api_warnings() + assert api.get_api_warnings() == [] + + data = conf.serialize(conf.DICT) + assert api.get_api_warnings() != [] + assert len(api.get_api_warnings()) == 1 + assert strip_data(api.get_api_warnings()[0]) == msg + + api.clear_api_warnings() + assert api.get_api_warnings() == [] + + conf1 = api.prefix_config() + conf1.deserialize(data) + assert api.get_api_warnings() != [] + assert len(api.get_api_warnings()) == 1 + assert strip_data(api.get_api_warnings()[0]) == msg + + api.clear_api_warnings() + assert api.get_api_warnings() == [] From 97b3da905f896fa19d0b63120caf577948bdea9d Mon Sep 17 00:00:00 2001 From: Rangababu-R Date: Wed, 6 Apr 2022 17:15:40 +0530 Subject: [PATCH 29/33] black fix --- openapiart/generator.py | 21 ++++++++++++++------- openapiart/tests/test_deprecate.py | 2 +- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/openapiart/generator.py b/openapiart/generator.py index 6bc996aa..e8acc80b 100644 --- a/openapiart/generator.py +++ b/openapiart/generator.py @@ -929,7 +929,11 @@ def _process_properties( property_name in choice_names and property_name != "choice" ) self._write_openapi_property( - schema_object, property_name, property, class_name, write_set_choice + schema_object, + property_name, + property, + class_name, + write_set_choice, ) for property_name, property in schema_object["properties"].items(): ref = self._get_parser("$..'$ref'").find(property) @@ -1281,7 +1285,7 @@ def _get_property_param_string(self, yobject): return (", ".join(property_param_string), properties, types) def _write_openapi_property( - self, schema_object, name, property, klass_name,write_set_choice=False + self, schema_object, name, property, klass_name, write_set_choice=False ): ref = self._get_parser("$..'$ref'").find(property) restriction = self._get_type_restriction(property) @@ -1302,7 +1306,9 @@ def _write_openapi_property( ): self._write(1, "@Deprecator.deprecated") key = "{}.{}".format(klass_name, name) - self._deprecated_properties[key] = property["x-status"]["additional_information"] + self._deprecated_properties[key] = property["x-status"][ + "additional_information" + ] self._write(1, "def %s(self):" % name) self._write(2, "# type: () -> %s" % (type_name)) self._write(2, '"""%s getter' % (name)) @@ -1324,7 +1330,9 @@ def _write_openapi_property( ): self._write(1, "@Deprecator.deprecated") key = "{}.{}".format(klass_name, name) - self._deprecated_properties[key] = property["x-status"]["additional_information"] + self._deprecated_properties[key] = property["x-status"][ + "additional_information" + ] self._write(1, "def %s(self, value):" % name) self._write(2, '"""%s setter' % (name)) self._write() @@ -1603,15 +1611,14 @@ def _get_classname_from_ref(self, ref): def _write(self, indent=0, line=""): self._fid.write(" " * indent + line + "\n") - + def _write_deprecator(self): with open(self._api_filename, "a") as self._fid: self._write(0, "Deprecator.messages = {") - for klass,msg in self._deprecated_properties.items(): + for klass, msg in self._deprecated_properties.items(): if "\n" in msg: print(msg) self._write(1, '"{}" : """{}""",'.format(klass, msg)) else: self._write(1, '"{}" : "{}",'.format(klass, msg)) self._write(1, "}") - diff --git a/openapiart/tests/test_deprecate.py b/openapiart/tests/test_deprecate.py index c102b536..3bd200a6 100644 --- a/openapiart/tests/test_deprecate.py +++ b/openapiart/tests/test_deprecate.py @@ -20,7 +20,7 @@ def strip_data(message): api.clear_api_warnings() assert api.get_api_warnings() == [] - + data = conf.serialize(conf.DICT) assert api.get_api_warnings() != [] assert len(api.get_api_warnings()) == 1 From d68c17e5c52f482f6099a8ccbaca5885e53c0b82 Mon Sep 17 00:00:00 2001 From: Rangababu-R Date: Thu, 7 Apr 2022 11:31:13 +0530 Subject: [PATCH 30/33] golang update --- openapiart/openapiartgo.py | 39 +++++++++++++++++++-------------- pkg/unit_test.go | 45 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 16 deletions(-) diff --git a/openapiart/openapiartgo.py b/openapiart/openapiartgo.py index 31e0a090..fda49422 100644 --- a/openapiart/openapiartgo.py +++ b/openapiart/openapiartgo.py @@ -1373,8 +1373,6 @@ def _write_field_getter(self, new, field): body=body, description=field.description, fieldtype=field.type, - # TODO message needs to modified once the py_go_diff PR is merged - # https://github.com/open-traffic-generator/openapiart/pull/281 status="" if status is False else "deprecated(`{msg}`)".format(msg=field.status_msg), @@ -1588,8 +1586,6 @@ def _write_field_setter(self, new, field, set_nil): fieldtype=field.type, fieldstruct=new.interface, set_choice=set_choice, - # TODO message needs to modified once the py_go_diff PR is merged - # https://github.com/open-traffic-generator/openapiart/pull/281 status="" if status is False else "deprecated(`{msg}`)".format(msg=field.status_msg), @@ -2012,6 +2008,22 @@ def _validate_types(self, new, field): value = '''""''' else: value = 0 + status_body = "" + if field.status is not None and field.status == "deprecated": + status_body = """ + // {name} is deprecated + if obj.obj.{name}{enum} != {value} {{ + deprecated(`{msg}`) + }} + """.format( + name=field.name, + field_name=field.schema_name, + value=0 if field.isEnum and field.isArray is False else value, + enum=".Number()" + if field.isEnum and field.isArray is False + else "", + msg=field.status_msg + ) if field.isOptional is False and "string" in field.type: body = """ // {name} is required @@ -2131,6 +2143,8 @@ def _validate_types(self, new, field): if field.isArray is False else field.format.capitalize() + "Slice", ) + if status_body != "": + body = "{} \n {}".format(body, status_body) # Enum will be handled via wrapper lib if inner_body == "": return body @@ -2139,7 +2153,7 @@ def _validate_types(self, new, field): {body} }} """.format( - name=field.name, value=value, body=inner_body + name=field.name, value=value, body=inner_body, ) return body @@ -2181,6 +2195,7 @@ def _validate_struct(self, new, field): ) body += """ if {condition} {{ + {msg} {body} }} """.format( @@ -2188,12 +2203,13 @@ def _validate_struct(self, new, field): condition="len(obj.obj.{name}) != 0".format(name=field.name) if field.isArray is True else "obj.obj.{name} != nil".format(name=field.name), + msg="deprecated(`{}`)".format(field.status_msg) + if field.status is not None and field.status == "deprecated" + else "" ) return body def _write_validate_method(self, new): - # TODO add the deprecated code once the py_go_diff PR is merged - # https://github.com/open-traffic-generator/openapiart/pull/281 statements = [] def p(): @@ -2207,13 +2223,8 @@ def p(): ) ) - deprecate_msgs = [] for field in new.interface_fields: valid = 0 - if field.status is not None and field.status == "deprecated": - deprecate_msgs.append( - "deprecated(`{msg}`)".format(msg=field.status_msg) - ) if field.type.lstrip("[]") in self._oapi_go_types.values(): block = self._validate_types(new, field) if block is None or block.strip() == "": @@ -2235,15 +2246,11 @@ def p(): if set_default {{ obj.setDefault() }} - {deprecate} {body} }} """.format( struct=new.struct, body=body, - deprecate="" - if deprecate_msgs == [] - else "\n".join(deprecate_msgs), ) ) diff --git a/pkg/unit_test.go b/pkg/unit_test.go index b9df67b1..feed40c1 100644 --- a/pkg/unit_test.go +++ b/pkg/unit_test.go @@ -1658,3 +1658,48 @@ func TestStringer(t *testing.T) { lObj.SetDouble(10.1).SetFloat(20.1).SetStringParam("abc") fmt.Println(lObj) } + +func TestDeprecationWarning(t *testing.T) { + + // Warning by config + api := openapiart.NewApi() + config := api.NewPrefixConfig() + config.RequiredObject().SetEA(10).SetEB(20) + config.SetA("abc") + config.SetB(20) + config.SetC(30) + + warnings := api.GetApiWarnings() + + assert.NotNil(t, warnings) + assert.Len(t, warnings, 1) + api.ClearApiWarnings() + + assert.Len(t, api.GetApiWarnings(), 0) + + // Warning by ToJson + data, err := config.ToJson() + + assert.Nil(t, err) + warnings1 := api.GetApiWarnings() + + assert.NotNil(t, warnings1) + assert.Len(t, warnings1, 1) + api.ClearApiWarnings() + + assert.Len(t, api.GetApiWarnings(), 0) + + config1 := api.NewPrefixConfig() + + // Warning by FromJson + err1 := config1.FromJson(data) + assert.Nil(t, err1) + warnings2 := api.GetApiWarnings() + + assert.NotNil(t, warnings2) + assert.Len(t, warnings2, 1) + api.ClearApiWarnings() + + assert.Len(t, api.GetApiWarnings(), 0) + +} From ceaab2e92b9374bb7ca2a8227a2fa29098a279da Mon Sep 17 00:00:00 2001 From: Rangababu-R Date: Thu, 7 Apr 2022 11:42:49 +0530 Subject: [PATCH 31/33] lint fixes --- openapiart/generator.py | 1 + openapiart/openapiartgo.py | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/openapiart/generator.py b/openapiart/generator.py index e8acc80b..469251c5 100644 --- a/openapiart/generator.py +++ b/openapiart/generator.py @@ -606,6 +606,7 @@ def _write_api_class(self, methods, factories): self._write(1, '"""') self._write() self._write(1, "def __init__(self, **kwargs):") + self._write(2, "openapi_warnings = []") self._write(2, "pass") for method in methods: diff --git a/openapiart/openapiartgo.py b/openapiart/openapiartgo.py index fda49422..20e16bf6 100644 --- a/openapiart/openapiartgo.py +++ b/openapiart/openapiartgo.py @@ -642,6 +642,7 @@ def _build_api_interface(self): // NewApi returns a new instance of the top level interface hierarchy func NewApi() {interface} {{ api := {internal_struct_name}{{}} + openapi_warnings = nil return &api }} @@ -2017,12 +2018,11 @@ def _validate_types(self, new, field): }} """.format( name=field.name, - field_name=field.schema_name, value=0 if field.isEnum and field.isArray is False else value, enum=".Number()" if field.isEnum and field.isArray is False else "", - msg=field.status_msg + msg=field.status_msg, ) if field.isOptional is False and "string" in field.type: body = """ @@ -2153,7 +2153,9 @@ def _validate_types(self, new, field): {body} }} """.format( - name=field.name, value=value, body=inner_body, + name=field.name, + value=value, + body=inner_body, ) return body @@ -2205,7 +2207,7 @@ def _validate_struct(self, new, field): else "obj.obj.{name} != nil".format(name=field.name), msg="deprecated(`{}`)".format(field.status_msg) if field.status is not None and field.status == "deprecated" - else "" + else "", ) return body From ca5c4d0f6c7a43d6565efded1d16e2d2f40b084d Mon Sep 17 00:00:00 2001 From: Rangababu-R Date: Thu, 21 Apr 2022 14:58:36 +0530 Subject: [PATCH 32/33] Update common.py --- openapiart/common.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openapiart/common.py b/openapiart/common.py index d8adf1b8..d2324c22 100644 --- a/openapiart/common.py +++ b/openapiart/common.py @@ -264,7 +264,7 @@ def _clear_errors(self): self._validation_errors.clear() def validate_mac(self, path, mac): - msg = "value of `{}` must be a valid mac address, instead of `{}`".format(path, mac) + msg = "value of `{}` must be a valid mac string, instead of `{}`".format(path, mac) if mac is None or not isinstance(mac, (str, unicode)) or mac.count(" ") != 0: self._append_error(msg) try: @@ -276,7 +276,7 @@ def validate_mac(self, path, mac): self._append_error(msg) def validate_ipv4(self, path, ip): - msg = "value of `{}` must be a valid ipv4 address, instead of `{}`".format(path, ip) + msg = "value of `{}` must be a valid ipv4 string, instead of `{}`".format(path, ip) if ip is None or not isinstance(ip, (str, unicode)) or ip.count(" ") != 0: self._append_error(msg) if len(ip.split(".")) != 4: @@ -288,7 +288,7 @@ def validate_ipv4(self, path, ip): self._append_error(msg) def validate_ipv6(self, path, ip): - msg = "value of `{}` must be a valid ipv6 address, instead of `{}`".format(path, ip) + msg = "value of `{}` must be a valid ipv6 string, instead of `{}`".format(path, ip) if ip is None or not isinstance(ip, (str, unicode)): self._append_error(msg) return False @@ -339,7 +339,7 @@ def validate_min_max(self, path, value, min, max): if isinstance(value, str): value = len(value) if (min is not None and value < min) or (max is not None and value > max): - self._append_error("length of `{}` must be in the range of [{}, {}], instead of `{}`".format( + self._append_error("length of field `{}` must be in the range of [{}, {}], instead of `{}`".format( path, min if min is not None else "", max if max is not None else "", From 13ec9477a0bfaec99d596906bb0099132e6237f0 Mon Sep 17 00:00:00 2001 From: Rangababu-R Date: Thu, 21 Apr 2022 15:19:56 +0530 Subject: [PATCH 33/33] ut update --- openapiart/tests/test_py_go_diff.py | 6 +++--- test_requirements.txt | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/openapiart/tests/test_py_go_diff.py b/openapiart/tests/test_py_go_diff.py index eaaa7501..ec37cb81 100644 --- a/openapiart/tests/test_py_go_diff.py +++ b/openapiart/tests/test_py_go_diff.py @@ -77,15 +77,15 @@ def test_validation_errors(): "required field `prefix_config.j[0].e_b` must not be empty" in errors ) assert ( - "value of `prefix_config.mac_pattern.mac.values[0]` must be a valid mac address, instead of `1`" + "value of `prefix_config.mac_pattern.mac.values[0]` must be a valid mac string, instead of `1`" in errors ) assert ( - "value of `prefix_config.mac_pattern.mac.values[1]` must be a valid mac address, instead of `20`" + "value of `prefix_config.mac_pattern.mac.values[1]` must be a valid mac string, instead of `20`" in errors ) assert ( - "value of `prefix_config.ipv4_pattern.ipv4.value` must be a valid ipv4 address, instead of `1.1`" + "value of `prefix_config.ipv4_pattern.ipv4.value` must be a valid ipv4 string, instead of `1.1`" in errors ) diff --git a/test_requirements.txt b/test_requirements.txt index d3d893b7..61bffb10 100644 --- a/test_requirements.txt +++ b/test_requirements.txt @@ -1,5 +1,4 @@ -pytest -openapiart.egg openapiart Flask sanity +pytest