Skip to content

Commit 7ada77c

Browse files
Only one implementation of complex_filter construction
Signed-off-by: Stew Francis <[email protected]>
1 parent bf8b801 commit 7ada77c

File tree

2 files changed

+87
-158
lines changed

2 files changed

+87
-158
lines changed

plugins/module_utils/cmci.py

Lines changed: 81 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,6 @@ def init_url(self): # type: () -> str
387387
def init_request_params(self): # type: () -> Optional[Dict[str, str]]
388388
return None
389389

390-
# TODO: why do we have this and/or parsing twice?
391390
def get_resources_request_params(self): # type: () -> Dict[str, str]
392391
# get, delete, put will all need CRITERIA{}
393392
request_params = OrderedDict({})
@@ -397,8 +396,6 @@ def get_resources_request_params(self): # type: () -> Dict[str, str]
397396
if f:
398397
# AND basic filters together and use the = operator for each one
399398
filter_string = ''
400-
if not request_params:
401-
request_params = OrderedDict({})
402399
for key, value in f.items():
403400
filter_string = _append_filter_string(
404401
filter_string,
@@ -409,52 +406,10 @@ def get_resources_request_params(self): # type: () -> Dict[str, str]
409406

410407
complex_filter = resources.get(COMPLEX_FILTER)
411408
if complex_filter:
412-
complex_filter_string = ''
413-
if not request_params:
414-
request_params = OrderedDict({})
415-
416-
and_item = complex_filter[AND]
417-
or_item = complex_filter[OR]
418-
attribute_item = complex_filter[ATTRIBUTE]
419-
420-
if and_item is not None:
421-
complex_filter_string = self._get_filter(
422-
and_item,
423-
complex_filter_string,
424-
' AND ',
425-
' -> ' + AND
426-
)
427-
428-
if or_item is not None:
429-
complex_filter_string = self._get_filter(
430-
or_item,
431-
complex_filter_string,
432-
' OR ',
433-
' -> ' + OR
434-
)
435-
436-
if attribute_item is not None:
437-
operator = self._convert_filter_operator(
438-
complex_filter[OPERATOR],
439-
""
440-
)
441-
value = complex_filter[VALUE]
442-
443-
if operator == '¬=':
444-
# Provides a filter string in the format NOT(FOO=='BAR')
445-
complex_filter_string = _append_filter_string(
446-
complex_filter_string,
447-
'NOT('
448-
+ attribute_item + '==' + '\'' + value + '\''
449-
+ ')'
450-
)
451-
else:
452-
complex_filter_string = _append_filter_string(
453-
complex_filter_string,
454-
attribute_item + operator + '\'' + value + '\''
455-
)
456-
457-
request_params['CRITERIA'] = complex_filter_string
409+
request_params['CRITERIA'] = self._get_complex_filter(
410+
complex_filter,
411+
"resources -> complex_filter"
412+
)
458413

459414
parameters = resources.get(GET_PARAMETERS)
460415
if parameters:
@@ -586,126 +541,101 @@ def append_attributes(self, element):
586541
{'@' + key: value for key, value in items}
587542
)
588543

589-
def _get_filter(self, list_of_filters, complex_filter_string, joiner, path):
590-
# type: (List[Dict], str, str, str) -> str
544+
def _get_filter(self, list_of_filters, joiner, path):
545+
# type: (List[Dict], str, str) -> str
591546

592547
if not isinstance(list_of_filters, list):
593548
self._fail(
594-
"nested filters must be a list, was: %s found in "
595-
"resources -> complex_filter%s"
549+
"nested filters must be a list, was: %s found in %s"
596550
% (type(list_of_filters), path)
597551
)
552+
complex_filter_string = ''
598553
for i in list_of_filters:
599-
if not isinstance(i, dict):
600-
self._fail(
601-
"nested filter must be of type dict, was: %s found in "
602-
"resources -> complex_filter%s" % (type(i), path)
603-
)
554+
complex_filter_string = _append_filter_string(
555+
complex_filter_string,
556+
self._get_complex_filter(i, path),
557+
joiner
558+
)
559+
return complex_filter_string
604560

605-
valid_keys = [AND, ATTRIBUTE, OPERATOR, OR, VALUE]
606-
diff = set(i.keys()) - set(valid_keys)
607-
if len(diff) != 0:
608-
self._fail(
609-
"Unsupported parameters for (basic.py) module: %s found"
610-
" in resources -> complex_filter%s. Supported parameters "
611-
"include: %s"
612-
% (", ".join(diff), path, ", ".join(valid_keys))
613-
)
561+
def _get_complex_filter(self, i, path):
562+
if not isinstance(i, dict):
563+
self._fail(
564+
"nested filter must be of type dict, was: %s found in %s"
565+
% (type(i), path)
566+
)
614567

615-
# Validate required_one_of
616-
and_item = i.get(AND)
617-
or_item = i.get(OR)
618-
attribute = i.get(ATTRIBUTE)
568+
valid_keys = [AND, ATTRIBUTE, OPERATOR, OR, VALUE]
569+
diff = set(i.keys()) - set(valid_keys)
570+
if len(diff) != 0:
571+
self._fail(
572+
"Unsupported parameters for (basic.py) module: %s found"
573+
" in %s. Supported parameters include: %s"
574+
% (", ".join(diff), path, ", ".join(valid_keys))
575+
)
619576

620-
if not and_item and not or_item and not attribute:
621-
self._fail(
622-
"one of the following is required: %s found"
623-
" in resources -> complex_filter%s"
624-
% (", ".join([ATTRIBUTE, AND, OR]), path)
625-
)
577+
# Validate required_one_of
578+
and_item = i.get(AND)
579+
or_item = i.get(OR)
580+
attribute = i.get(ATTRIBUTE)
626581

627-
# Validate required_by
628-
op = i.get(OPERATOR)
582+
if not and_item and not or_item and not attribute:
583+
self._fail(
584+
"one of the following is required: %s found in %s"
585+
% (", ".join([ATTRIBUTE, AND, OR]), path)
586+
)
629587

630-
if op and not attribute:
631-
self._fail(
632-
"missing parameter(s) required by '%s': %s"
633-
% (OPERATOR, ATTRIBUTE)
634-
)
588+
# Validate required_by
589+
op = i.get(OPERATOR)
635590

636-
value = i.get(VALUE)
591+
if op and not attribute:
592+
self._fail(
593+
"missing parameter(s) required by '%s': %s"
594+
% (OPERATOR, ATTRIBUTE)
595+
)
637596

638-
if (value and not attribute) or (attribute and not value):
639-
self._fail(
640-
'parameters are required together: %s, %s found in '
641-
'resources -> complex_filter%s'
642-
% (ATTRIBUTE, VALUE, path)
643-
)
597+
value = i.get(VALUE)
644598

645-
# Validate mutually exclusive parameters
646-
if (and_item and or_item) or (and_item and attribute) or \
647-
(or_item and attribute):
648-
self._fail(
649-
'parameters are mutually exclusive: %s|%s|%s found in '
650-
'resources -> complex_filter%s' % (ATTRIBUTE, AND, OR, path)
651-
)
599+
if (value and not attribute) or (attribute and not value):
600+
self._fail(
601+
'parameters are required together: %s, %s found in %s'
602+
% (ATTRIBUTE, VALUE, path)
603+
)
652604

653-
if and_item is not None:
654-
and_filter_string = self._get_filter(
655-
and_item,
656-
'',
657-
' AND ',
658-
'%s -> %s' % (path, AND)
659-
)
660-
complex_filter_string = _append_filter_string(
661-
complex_filter_string,
662-
and_filter_string, joiner
663-
)
664-
if or_item is not None:
665-
or_filter_string = self._get_filter(
666-
or_item,
667-
'',
668-
' OR ',
669-
'%s -> %s' % (path, OR)
670-
)
671-
complex_filter_string = _append_filter_string(
672-
complex_filter_string,
673-
or_filter_string, joiner
674-
)
675-
if attribute is not None:
676-
operator = self._convert_filter_operator(op, path)
677-
678-
# Validate attribute type
679-
if not isinstance(attribute, str):
680-
self._fail(
681-
"%s must be of type str, was: %s found in "
682-
"resources -> complex_filter%s"
683-
% (ATTRIBUTE, type(attribute), path)
684-
)
605+
# Validate mutually exclusive parameters
606+
if (and_item and or_item) or (and_item and attribute) or \
607+
(or_item and attribute):
608+
self._fail(
609+
'parameters are mutually exclusive: %s|%s|%s found in %s'
610+
% (ATTRIBUTE, AND, OR, path)
611+
)
685612

686-
# Validate value type
687-
if not isinstance(value, str):
688-
self._fail(
689-
"%s must be of type str, was: %s found in "
690-
"resources -> complex_filter%s"
691-
% (VALUE, type(value), path)
692-
)
613+
if and_item is not None:
614+
return self._get_filter(and_item, ' AND ', '%s -> %s' % (path, AND))
615+
if or_item is not None:
616+
return self._get_filter(or_item, ' OR ', '%s -> %s' % (path, OR))
617+
if attribute is not None:
618+
operator = self._convert_filter_operator(op, path)
693619

694-
if operator == '¬=':
695-
# Provides a filter string in the format NOT(FOO=='BAR')
696-
attribute_filter_string = \
697-
'NOT(' + attribute + '==' + '\'' + value + '\'' + ')'
698-
else:
699-
attribute_filter_string = \
700-
attribute + operator + '\'' + value + '\''
701-
702-
complex_filter_string = _append_filter_string(
703-
complex_filter_string,
704-
attribute_filter_string,
705-
joiner
620+
# Validate attribute type
621+
if not isinstance(attribute, str):
622+
self._fail(
623+
"%s must be of type str, was: %s found in %s"
624+
% (ATTRIBUTE, type(attribute), path)
706625
)
707626

708-
return complex_filter_string
627+
# Validate value type
628+
if not isinstance(value, str):
629+
self._fail(
630+
"%s must be of type str, was: %s found in %s"
631+
% (VALUE, type(value), path)
632+
)
633+
634+
if operator == '¬=':
635+
# Provides a filter string in the format NOT(FOO=='BAR')
636+
return 'NOT(' + attribute + '==' + '\'' + value + '\'' + ')'
637+
else:
638+
return attribute + operator + '\'' + value + '\''
709639

710640
def _convert_filter_operator(self, operator, path):
711641
if operator in ['<', 'LT']:
@@ -723,8 +653,7 @@ def _convert_filter_operator(self, operator, path):
723653
if operator in ['==', 'IS']:
724654
return '=='
725655
self._fail(
726-
'value of operator must be one of: %s, got: %s found in '
727-
'resources -> complex_filter%s'
656+
'value of operator must be one of: %s, got: %s found in %s'
728657
% (", ".join(OPERATORS), operator, path)
729658
)
730659

tests/unit/modules/test_cmci_filters.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -139,11 +139,11 @@ def test_complex_filter_or(cmci_module): # type: (CMCITestHelper) -> None
139139
def test_complex_filter_operator(cmci_module): # type: (CMCITestHelper) -> None
140140
records = [{'name': 'bat', 'dsname': 'STEWF.BLOP.BLIP'}]
141141
cmci_module.stub_records('GET', 'cicslocalfile', records, scope=SCOPE,
142-
parameters='?CRITERIA=%28NOT%28FOO%3D%3D%27BAR%27%29%29')
142+
parameters='?CRITERIA=NOT%28FOO%3D%3D%27BAR%27%29')
143143

144144
cmci_module.expect(result(
145145
'https://winmvs2c.hursley.ibm.com:26040/CICSSystemManagement/'
146-
'cicslocalfile/CICSEX56/IYCWEMW2?CRITERIA=%28NOT%28FOO%3D%3D%27BAR%27%29%29',
146+
'cicslocalfile/CICSEX56/IYCWEMW2?CRITERIA=NOT%28FOO%3D%3D%27BAR%27%29',
147147
records=records
148148
))
149149

@@ -400,11 +400,11 @@ def test_query_criteria_complex_filter_and_no_value(cmci_module):
400400

401401
def test_complex_filter_operator_letters(cmci_module): # type: (CMCITestHelper) -> None
402402
records = [{'name': 'bat', 'dsname': 'STEWF.BLOP.BLIP'}]
403-
cmci_module.stub_records('GET', 'cicslocalfile', records, scope=SCOPE, parameters='?CRITERIA=%28FOO%3E%27BAR%27%29')
403+
cmci_module.stub_records('GET', 'cicslocalfile', records, scope=SCOPE, parameters='?CRITERIA=FOO%3E%27BAR%27')
404404

405405
cmci_module.expect(result(
406406
'https://winmvs2c.hursley.ibm.com:26040/CICSSystemManagement/'
407-
'cicslocalfile/CICSEX56/IYCWEMW2?CRITERIA=%28FOO%3E%27BAR%27%29',
407+
'cicslocalfile/CICSEX56/IYCWEMW2?CRITERIA=FOO%3E%27BAR%27',
408408
records=records
409409
))
410410

@@ -518,11 +518,11 @@ def test_complex_filter_default_operator_root(cmci_module):
518518
# type: (CMCITestHelper) -> None
519519
records = [{'name': 'bat', 'dsname': 'STEWF.BLOP.BLIP'}]
520520
cmci_module.stub_records('GET', 'cicslocalfile', records, scope=SCOPE,
521-
parameters='?CRITERIA=%28FOO%3D%27BAR%27%29')
521+
parameters='?CRITERIA=FOO%3D%27BAR%27')
522522

523523
cmci_module.expect(result(
524524
'https://winmvs2c.hursley.ibm.com:26040/CICSSystemManagement/'
525-
'cicslocalfile/CICSEX56/IYCWEMW2?CRITERIA=%28FOO%3D%27BAR%27%29',
525+
'cicslocalfile/CICSEX56/IYCWEMW2?CRITERIA=FOO%3D%27BAR%27',
526526
records=records
527527
))
528528

0 commit comments

Comments
 (0)