Skip to content

Commit 3671f07

Browse files
authored
Merge pull request #1 from networktocode-llc/library-api
API entry point refactor proposal
2 parents 8340939 + 453fd8a commit 3671f07

File tree

2 files changed

+62
-49
lines changed

2 files changed

+62
-49
lines changed

netcompare/check_type.py

Lines changed: 43 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""CheckType Implementation."""
2-
from typing import Mapping, Iterable, Tuple
2+
from typing import Mapping, Iterable, Tuple, Union, List
33
from .evaluator import diff_generator
4+
from .runner import extract_values_from_output
45

56

67
class CheckType:
@@ -21,24 +22,28 @@ def init(*args):
2122
else:
2223
raise NotImplementedError
2324

24-
def evaluate(self, pre_value: Mapping, post_value: Mapping, path: Mapping) -> Tuple[Mapping, bool]:
25-
"""Return a diff of the comparison and a boolean True if it passes or False otherwise."""
26-
self.diff = diff_generator(pre_value, post_value, path)
27-
# self.diff may get modified by a child class when self.check_logic is called.
28-
logic_check = self.check_logic()
29-
return (self.diff, logic_check)
25+
@staticmethod
26+
def extract_value_from_json_path(
27+
value: Mapping, path: Mapping, exclude: List = list()
28+
) -> Union[Mapping, List, int, str, bool]:
29+
"""Return the value contained into a Mapping for a defined path."""
30+
return extract_values_from_output(value, path, exclude)
31+
32+
def evaluate(self, reference_value: Mapping, value_to_compare: Mapping) -> Tuple[Mapping, bool]:
33+
"""Return the result of the evaluation and a boolean True if it passes it or False otherwise.
3034
31-
def check_logic(self) -> bool:
32-
"""docstring placeholder."""
35+
This method is the one that each CheckType has to implement.
36+
"""
3337
raise NotImplementedError
3438

3539

3640
class ExactMatchType(CheckType):
3741
"""Exact Match class docstring."""
3842

39-
def check_logic(self) -> bool:
40-
"""Return True if diff is empty indicating an exact match."""
41-
return not self.diff
43+
def evaluate(self, reference_value: Mapping, value_to_compare: Mapping) -> Tuple[Mapping, bool]:
44+
"""Returns the difference between values and the boolean."""
45+
diff = diff_generator(reference_value, value_to_compare)
46+
return diff, not diff
4247

4348

4449
class ToleranceType(CheckType):
@@ -49,16 +54,17 @@ def __init__(self, *args):
4954
self.tolerance_factor = float(args[1]) / 100
5055
super().__init__()
5156

52-
def check_logic(self) -> bool:
53-
"""Return True if the changed values are within tolerance."""
54-
self.diff = self._get_outliers()
55-
return not self.diff
57+
def evaluate(self, reference_value: Mapping, value_to_compare: Mapping) -> Tuple[Mapping, bool]:
58+
"""Returns the difference between values and the boolean."""
59+
diff = diff_generator(reference_value, value_to_compare)
60+
diff = self._get_outliers(diff)
61+
return diff, not diff
5662

57-
def _get_outliers(self) -> Mapping:
63+
def _get_outliers(self, diff: Mapping) -> Mapping:
5864
"""Return a mapping of values outside the tolerance threshold."""
5965
result = {
6066
key: {sub_key: values for sub_key, values in obj.items() if not self._within_tolerance(**values)}
61-
for key, obj in self.diff.items()
67+
for key, obj in diff.items()
6268
}
6369
return {key: obj for key, obj in result.items() if obj}
6470

@@ -68,20 +74,26 @@ def _within_tolerance(self, *, old_value: float, new_value: float) -> bool:
6874
return (old_value - max_diff) < new_value < (old_value + max_diff)
6975

7076

71-
def compare(
72-
pre_obj: Mapping, post_obj: Mapping, path: Mapping, type_info: Iterable, options: Mapping
73-
) -> Tuple[Mapping, bool]:
74-
"""Entry point function.
77+
# TODO: compare is no longer the entry point, we should use the libary as:
78+
# netcompare_check = CheckType.init(check_type_info, options)
79+
# pre_result = netcompare_check.extract_value_from_json_path(pre_obj, path)
80+
# post_result = netcompare_check.extract_value_from_json_path(post_obj, path)
81+
# netcompare_check.evaluate(pre_result, post_result)
82+
#
83+
# def compare(
84+
# pre_obj: Mapping, post_obj: Mapping, path: Mapping, type_info: Iterable, options: Mapping
85+
# ) -> Tuple[Mapping, bool]:
86+
# """Entry point function.
7587

76-
Returns a diff object and the boolean of the comparison.
77-
"""
88+
# Returns a diff object and the boolean of the comparison.
89+
# """
7890

79-
type_info = type_info.lower()
91+
# type_info = type_info.lower()
8092

81-
try:
82-
type_obj = CheckType.init(type_info, options)
83-
except Exception:
84-
# We will be here if we can't infer the type_obj
85-
raise
93+
# try:
94+
# type_obj = CheckType.init(type_info, options)
95+
# except Exception:
96+
# # We will be here if we can't infer the type_obj
97+
# raise
8698

87-
return type_obj.evaluate(pre_obj, post_obj, path)
99+
# return type_obj.evaluate(pre_obj, post_obj, path)

netcompare/runner.py

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
#!/ur/bin/env python3
22
import re
33
import jmespath
4-
from typing import Mapping, List, Generator
4+
from typing import Mapping, List, Generator, Union
55

66

7-
def extract_values_from_output(check_definition: Mapping, output: Mapping) -> List:
7+
def extract_values_from_output(value: Mapping, path: Mapping, exclude: List) -> Union[Mapping, List, int, str, bool]:
88
"""Return data from output depending on the check path. See unit text for complete example.
99
1010
Args:
11-
check_definition: {
12-
"path": "result[0].vrfs.default.peerList[*].[$peerAddress$,prefixesReceived]",
13-
}
14-
output: {
11+
path: "result[0].vrfs.default.peerList[*].[$peerAddress$,prefixesReceived]",
12+
value: {
1513
"jsonrpc": "2.0",
1614
"id": "EapiExplorer-1",
1715
"result": [
@@ -20,23 +18,26 @@ def extract_values_from_output(check_definition: Mapping, output: Mapping) -> Li
2018
"default": {
2119
"peerList": [
2220
{ ...
21+
exclude: [...]
22+
23+
TODO: This function should be able to return a list, or a Dict, or a Integer, or a Boolean or a String
2324
Return:
2425
[{'7.7.7.7': {'prefixesReceived': 101}}, {'10.1.0.0': {'prefixesReceived': 120}}, ...
2526
"""
2627

27-
if check_definition.get("path"):
28-
found_values = jmespath.search(jmspath_value_parser(check_definition["path"]), output)
28+
if path:
29+
found_values = jmespath.search(jmspath_value_parser(path), value)
2930
else:
30-
found_values = output
31+
found_values = value
3132

32-
if "exclude" in check_definition:
33-
my_value_exclude_cleaner(found_values, check_definition["exclude"])
33+
if exclude:
34+
my_value_exclude_cleaner(found_values, exclude)
3435
my_meaningful_values = found_values
3536
else:
36-
my_meaningful_values = get_meaningful_values(check_definition, found_values)
37+
my_meaningful_values = get_meaningful_values(path, found_values)
3738

38-
if check_definition.get("path") and re.search(r"\$.*\$", check_definition["path"]):
39-
wanted_reference_keys = jmespath.search(jmspath_refkey_parser(check_definition["path"]), output)
39+
if path and re.search(r"\$.*\$", path):
40+
wanted_reference_keys = jmespath.search(jmspath_refkey_parser(path), value)
4041
list_of_reference_keys = keys_cleaner(wanted_reference_keys)
4142
return keys_values_zipper(list_of_reference_keys, my_meaningful_values)
4243
else:
@@ -89,13 +90,13 @@ def jmspath_refkey_parser(path):
8990
return ".".join(splitted_jmspath)
9091

9192

92-
def get_meaningful_values(check_definition, found_values):
93-
if check_definition.get("path"):
93+
def get_meaningful_values(path, found_values):
94+
if path:
9495
# check if list of lists
9596
if not any(isinstance(i, list) for i in found_values):
9697
raise TypeError(
9798
"Catching value must be defined as list in jmespath expression i.e. result[*].state -> result[*].[state]. You have {}'.".format(
98-
check_definition["path"]
99+
path
99100
)
100101
)
101102
for element in found_values:
@@ -110,7 +111,7 @@ def get_meaningful_values(check_definition, found_values):
110111
found_values = flatten_list(found_values)
111112
break
112113

113-
my_meaningful_values = associate_key_of_my_value(jmspath_value_parser(check_definition["path"]), found_values)
114+
my_meaningful_values = associate_key_of_my_value(jmspath_value_parser(path), found_values)
114115
else:
115116
my_meaningful_values = found_values
116117
return my_meaningful_values

0 commit comments

Comments
 (0)