mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
Add groupby_as_dict filter (#2323)
* Add groupby_as_dict filter. * Test all error cases.
This commit is contained in:
parent
23dda56913
commit
384655e15c
5 changed files with 123 additions and 0 deletions
3
changelogs/fragments/2323-groupby_as_dict-filter.yml
Normal file
3
changelogs/fragments/2323-groupby_as_dict-filter.yml
Normal file
|
@ -0,0 +1,3 @@
|
|||
add plugin.filter:
|
||||
- name: groupby_as_dict
|
||||
description: Transform a sequence of dictionaries to a dictionary where the dictionaries are indexed by an attribute
|
42
plugins/filter/groupby.py
Normal file
42
plugins/filter/groupby.py
Normal file
|
@ -0,0 +1,42 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2021, Felix Fontein <felix@fontein.de>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
from ansible.errors import AnsibleFilterError
|
||||
from ansible.module_utils.common._collections_compat import Mapping, Sequence
|
||||
|
||||
|
||||
def groupby_as_dict(sequence, attribute):
|
||||
'''
|
||||
Given a sequence of dictionaries and an attribute name, returns a dictionary mapping
|
||||
the value of this attribute to the dictionary.
|
||||
|
||||
If multiple dictionaries in the sequence have the same value for this attribute,
|
||||
the filter will fail.
|
||||
'''
|
||||
if not isinstance(sequence, Sequence):
|
||||
raise AnsibleFilterError('Input is not a sequence')
|
||||
|
||||
result = dict()
|
||||
for list_index, element in enumerate(sequence):
|
||||
if not isinstance(element, Mapping):
|
||||
raise AnsibleFilterError('Sequence element #{0} is not a mapping'.format(list_index))
|
||||
if attribute not in element:
|
||||
raise AnsibleFilterError('Attribute not contained in element #{0} of sequence'.format(list_index))
|
||||
result_index = element[attribute]
|
||||
if result_index in result:
|
||||
raise AnsibleFilterError('Multiple sequence entries have attribute value {0!r}'.format(result_index))
|
||||
result[result_index] = element
|
||||
return result
|
||||
|
||||
|
||||
class FilterModule(object):
|
||||
''' Ansible list filters '''
|
||||
|
||||
def filters(self):
|
||||
return {
|
||||
'groupby_as_dict': groupby_as_dict,
|
||||
}
|
2
tests/integration/targets/filter_groupby/aliases
Normal file
2
tests/integration/targets/filter_groupby/aliases
Normal file
|
@ -0,0 +1,2 @@
|
|||
shippable/posix/group4
|
||||
skip/python2.6 # filters are controller only, and we no longer support Python 2.6 on the controller
|
45
tests/integration/targets/filter_groupby/tasks/main.yml
Normal file
45
tests/integration/targets/filter_groupby/tasks/main.yml
Normal file
|
@ -0,0 +1,45 @@
|
|||
---
|
||||
- name: Test functionality
|
||||
assert:
|
||||
that:
|
||||
- list1 | community.general.groupby_as_dict('name') == dict1
|
||||
|
||||
- name: 'Test error: not a list'
|
||||
set_fact:
|
||||
test: "{{ list_no_list | community.general.groupby_as_dict('name') }}"
|
||||
ignore_errors: true
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result.msg == 'Input is not a sequence'
|
||||
|
||||
- name: 'Test error: list element not a mapping'
|
||||
set_fact:
|
||||
test: "{{ list_no_dict | community.general.groupby_as_dict('name') }}"
|
||||
ignore_errors: true
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.msg == 'Sequence element #0 is not a mapping'"
|
||||
|
||||
- name: 'Test error: list element does not have attribute'
|
||||
set_fact:
|
||||
test: "{{ list_no_attribute | community.general.groupby_as_dict('name') }}"
|
||||
ignore_errors: true
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.msg == 'Attribute not contained in element #1 of sequence'"
|
||||
|
||||
- name: 'Test error: attribute collision'
|
||||
set_fact:
|
||||
test: "{{ list_collision | community.general.groupby_as_dict('name') }}"
|
||||
ignore_errors: true
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result.msg == "Multiple sequence entries have attribute value 'a'"
|
31
tests/integration/targets/filter_groupby/vars/main.yml
Normal file
31
tests/integration/targets/filter_groupby/vars/main.yml
Normal file
|
@ -0,0 +1,31 @@
|
|||
---
|
||||
list1:
|
||||
- name: a
|
||||
x: y
|
||||
- name: b
|
||||
z: 1
|
||||
|
||||
dict1:
|
||||
a:
|
||||
name: a
|
||||
x: y
|
||||
b:
|
||||
name: b
|
||||
z: 1
|
||||
|
||||
list_no_list:
|
||||
a:
|
||||
name: a
|
||||
|
||||
list_no_dict:
|
||||
- []
|
||||
- 1
|
||||
|
||||
list_no_attribute:
|
||||
- name: a
|
||||
foo: baz
|
||||
- foo: bar
|
||||
|
||||
list_collision:
|
||||
- name: a
|
||||
- name: a
|
Loading…
Reference in a new issue