diff --git a/awx/main/utils/filters.py b/awx/main/utils/filters.py index 347facccc0..9d1e5a7aa4 100644 --- a/awx/main/utils/filters.py +++ b/awx/main/utils/filters.py @@ -13,7 +13,6 @@ from django.db import models __all__ = ['DynamicFilter'] - unicode_spaces = [unichr(c) for c in xrange(sys.maxunicode) if unichr(c).isspace()] unicode_spaces_other = unicode_spaces + [u'(', u')', u'=', u'"'] @@ -34,7 +33,7 @@ def string_to_type(t): class DynamicFilter(object): - + SEARCHABLE_RELATIONSHIP = 'ansible_facts' class BoolOperand(object): def __init__(self, t): @@ -44,6 +43,16 @@ class DynamicFilter(object): kwargs[k] = v self.result = models.Q(**kwargs) + def strip_quotes_traditional_logic(self, v): + if type(v) is unicode and v.startswith('"') and v.endswith('"'): + return v[1:-1] + return v + + def strip_quotes_json_logic(self, v): + if type(v) is unicode and v.startswith('"') and v.endswith('"') and v != u'"null"': + return v[1:-1] + return v + ''' TODO: We should be able to express this in the grammar and let pyparsing do the heavy lifting. @@ -53,66 +62,50 @@ class DynamicFilter(object): relationship refered to to see if it's a jsonb type. ''' def _json_path_to_contains(self, k, v): - pieces = k.split('__') + if not k.startswith(DynamicFilter.SEARCHABLE_RELATIONSHIP): + v = self.strip_quotes_traditional_logic(v) + return (k, v) - flag_first_arr_found = False + # Strip off leading relationship key + if k.startswith(DynamicFilter.SEARCHABLE_RELATIONSHIP + '__'): + strip_len = len(DynamicFilter.SEARCHABLE_RELATIONSHIP) + 2 + else: + strip_len = len(DynamicFilter.SEARCHABLE_RELATIONSHIP) + k = k[strip_len:] - assembled_k = '' - assembled_v = v + pieces = k.split(u'__') + + assembled_k = u'%s__contains' % (DynamicFilter.SEARCHABLE_RELATIONSHIP) + assembled_v = None - last_kv = None last_v = None + last_kv = None - contains_count = 0 for i, piece in enumerate(pieces): - if flag_first_arr_found is False and piece.endswith('[]'): - assembled_k += u'%s__contains' % (piece[0:-2]) - contains_count += 1 - flag_first_arr_found = True - elif flag_first_arr_found is False and i == len(pieces) - 1: - assembled_k += u'%s' % piece - elif flag_first_arr_found is False: - assembled_k += u'%s__' % piece - elif flag_first_arr_found is True: - new_kv = dict() - if piece.endswith('[]'): - new_v = [] - new_kv[piece[0:-2]] = new_v - else: - new_v = dict() - new_kv[piece] = new_v + new_kv = dict() + if piece.endswith(u'[]'): + new_v = [] + new_kv[piece[0:-2]] = new_v + else: + new_v = dict() + new_kv[piece] = new_v + if last_kv is None: + assembled_v = new_kv + elif type(last_v) is list: + last_v.append(new_kv) + elif type(last_v) is dict: + last_kv[last_kv.keys()[0]] = new_kv - if last_v is None: - last_v = [] - assembled_v = last_v + last_v = new_v + last_kv = new_kv - if type(last_v) is list: - last_v.append(new_kv) - elif type(last_v) is dict: - last_kv[last_kv.keys()[0]] = new_kv + v = self.strip_quotes_json_logic(v) - last_v = new_v - last_kv = new_kv - contains_count += 1 - - ''' - Explicit quotes are kept until this point. - Note: we could have totally "ripped" them off earlier when we decided - what type to convert the token to. - ''' - if type(v) is unicode and v.startswith('"') and v.endswith('"') and v != u'"null"': - v = v[1:-1] - - if contains_count == 0: - assembled_v = v - elif contains_count == 1: - assembled_v = [v] - elif contains_count > 1: - if type(last_v) is list: - last_v.append(v) - if type(last_v) is dict: - last_kv[last_kv.keys()[0]] = v + if type(last_v) is list: + last_v.append(v) + elif type(last_v) is dict: + last_kv[last_kv.keys()[0]] = v return (assembled_k, assembled_v) @@ -181,12 +174,12 @@ class DynamicFilter(object): @classmethod def query_from_string(cls, filter_string): + ''' TODO: * handle values with " via: a.b.c.d="hello\"world" * handle keys with " via: a.\"b.c="yeah" * handle key with __ in it - ''' filter_string_raw = filter_string filter_string = unicode(filter_string) @@ -207,6 +200,7 @@ class DynamicFilter(object): try: res = boolExpr.parseString('(' + filter_string + ')') + #except ParseException as e: except Exception: raise RuntimeError(u"Invalid query %s" % filter_string_raw) @@ -214,5 +208,3 @@ class DynamicFilter(object): return res[0].result raise RuntimeError("Parsing the filter_string %s went terribly wrong" % filter_string) - -