1# -*- coding: utf-8 -*-
2# (c) 2015, Filipe Niero Felisbino <filipenf@gmail.com>
3#
4# This file is part of Ansible
5#
6# Ansible is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation, either version 3 of the License, or
9# (at your option) any later version.
10#
11# Ansible is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
18
19from __future__ import (absolute_import, division, print_function)
20__metaclass__ = type
21
22from ansible.errors import AnsibleError, AnsibleFilterError
23
24try:
25    import jmespath
26    HAS_LIB = True
27except ImportError:
28    HAS_LIB = False
29
30
31def json_query(data, expr):
32    '''Query data using jmespath query language ( http://jmespath.org ). Example:
33    - ansible.builtin.debug: msg="{{ instance | json_query(tagged_instances[*].block_device_mapping.*.volume_id') }}"
34    '''
35    if not HAS_LIB:
36        raise AnsibleError('You need to install "jmespath" prior to running '
37                           'json_query filter')
38
39    # Hack to handle Ansible Unsafe text, AnsibleMapping and AnsibleSequence
40    # See issue: https://github.com/ansible-collections/community.general/issues/320
41    jmespath.functions.REVERSE_TYPES_MAP['string'] = jmespath.functions.REVERSE_TYPES_MAP['string'] + ('AnsibleUnicode', 'AnsibleUnsafeText', )
42    jmespath.functions.REVERSE_TYPES_MAP['array'] = jmespath.functions.REVERSE_TYPES_MAP['array'] + ('AnsibleSequence', )
43    jmespath.functions.REVERSE_TYPES_MAP['object'] = jmespath.functions.REVERSE_TYPES_MAP['object'] + ('AnsibleMapping', )
44    try:
45        return jmespath.search(expr, data)
46    except jmespath.exceptions.JMESPathError as e:
47        raise AnsibleFilterError('JMESPathError in json_query filter plugin:\n%s' % e)
48    except Exception as e:
49        # For older jmespath, we can get ValueError and TypeError without much info.
50        raise AnsibleFilterError('Error in jmespath.search in json_query filter plugin:\n%s' % e)
51
52
53class FilterModule(object):
54    ''' Query filter '''
55
56    def filters(self):
57        return {
58            'json_query': json_query
59        }
60