mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
Introduce 'insertbefore' and 'insertafter' to specify the position (#44811)
* Introduce 'insertbefore' and 'insertafter' to specify the position children have to be inserted. * Added version_added to new options * Xpath in example needs single quotes * Added integration tests for insertafter and insertbefore * Changed version_added to 2.8
This commit is contained in:
parent
1793cad07b
commit
2dbade4adc
6 changed files with 160 additions and 4 deletions
|
@ -122,6 +122,26 @@ options:
|
|||
type: bool
|
||||
default: no
|
||||
version_added: '2.7'
|
||||
insertbefore:
|
||||
description:
|
||||
- Add additional child-element(s) before the first selected element for a given C(xpath).
|
||||
- Child elements must be given in a list and each item may be either a string
|
||||
(eg. C(children=ansible) to add an empty C(<ansible/>) child element),
|
||||
or a hash where the key is an element name and the value is the element value.
|
||||
- This parameter requires C(xpath) to be set.
|
||||
type: bool
|
||||
default: no
|
||||
version_added: '2.8'
|
||||
insertafter:
|
||||
description:
|
||||
- Add additional child-element(s) after the last selected element for a given C(xpath).
|
||||
- Child elements must be given in a list and each item may be either a string
|
||||
(eg. C(children=ansible) to add an empty C(<ansible/>) child element),
|
||||
or a hash where the key is an element name and the value is the element value.
|
||||
- This parameter requires C(xpath) to be set.
|
||||
type: bool
|
||||
default: no
|
||||
version_added: '2.8'
|
||||
requirements:
|
||||
- lxml >= 2.3.0
|
||||
notes:
|
||||
|
@ -202,6 +222,16 @@ EXAMPLES = r'''
|
|||
- beer: Old Motor Oil
|
||||
- beer: Old Curmudgeon
|
||||
|
||||
- name: Add several more beers to the 'beers' element and add them before the 'Rochefort 10' element
|
||||
xml:
|
||||
path: /foo/bar.xml
|
||||
xpath: '/business/beers/beer[text()=\"Rochefort 10\"]'
|
||||
insertbefore: yes
|
||||
add_children:
|
||||
- beer: Old Rasputin
|
||||
- beer: Old Motor Oil
|
||||
- beer: Old Curmudgeon
|
||||
|
||||
# NOTE: The 'state' defaults to 'present' and 'value' defaults to 'null' for elements
|
||||
- name: Add a 'validxhtml' element to the 'website' element
|
||||
xml:
|
||||
|
@ -446,16 +476,35 @@ def set_target_children(module, tree, xpath, namespaces, children, in_type):
|
|||
finish(module, tree, xpath, namespaces, changed=changed)
|
||||
|
||||
|
||||
def add_target_children(module, tree, xpath, namespaces, children, in_type):
|
||||
def add_target_children(module, tree, xpath, namespaces, children, in_type, insertbefore, insertafter):
|
||||
if is_node(tree, xpath, namespaces):
|
||||
new_kids = children_to_nodes(module, children, in_type)
|
||||
for node in tree.xpath(xpath, namespaces=namespaces):
|
||||
node.extend(new_kids)
|
||||
if insertbefore or insertafter:
|
||||
insert_target_children(tree, xpath, namespaces, new_kids, insertbefore, insertafter)
|
||||
else:
|
||||
for node in tree.xpath(xpath, namespaces=namespaces):
|
||||
node.extend(new_kids)
|
||||
finish(module, tree, xpath, namespaces, changed=True)
|
||||
else:
|
||||
finish(module, tree, xpath, namespaces)
|
||||
|
||||
|
||||
def insert_target_children(tree, xpath, namespaces, children, insertbefore, insertafter):
|
||||
"""
|
||||
Insert the given children before or after the given xpath. If insertbefore is True, it is inserted before the
|
||||
first xpath hit, with insertafter, it is inserted after the last xpath hit.
|
||||
"""
|
||||
insert_target = tree.xpath(xpath, namespaces=namespaces)
|
||||
loc_index = 0 if insertbefore else -1
|
||||
index_in_parent = insert_target[loc_index].getparent().index(insert_target[loc_index])
|
||||
parent = insert_target[0].getparent()
|
||||
if insertafter:
|
||||
index_in_parent += 1
|
||||
for child in children:
|
||||
parent.insert(index_in_parent, child)
|
||||
index_in_parent += 1
|
||||
|
||||
|
||||
def _extract_xpstr(g):
|
||||
return g[1:-1]
|
||||
|
||||
|
@ -776,6 +825,8 @@ def main():
|
|||
input_type=dict(type='str', default='yaml', choices=['xml', 'yaml']),
|
||||
backup=dict(type='bool', default=False),
|
||||
strip_cdata_tags=dict(type='bool', default=False),
|
||||
insertbefore=dict(type='bool', default=False),
|
||||
insertafter=dict(type='bool', default=False),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
# TODO: Implement this as soon as #28662 (required_by functionality) is merged
|
||||
|
@ -790,6 +841,8 @@ def main():
|
|||
['content', 'text', ['xpath']],
|
||||
['count', True, ['xpath']],
|
||||
['print_match', True, ['xpath']],
|
||||
['insertbefore', True, ['xpath']],
|
||||
['insertafter', True, ['xpath']],
|
||||
],
|
||||
required_one_of=[
|
||||
['path', 'xmlstring'],
|
||||
|
@ -798,6 +851,7 @@ def main():
|
|||
mutually_exclusive=[
|
||||
['add_children', 'content', 'count', 'print_match', 'set_children', 'value'],
|
||||
['path', 'xmlstring'],
|
||||
['insertbefore', 'insertafter'],
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -817,6 +871,8 @@ def main():
|
|||
count = module.params['count']
|
||||
backup = module.params['backup']
|
||||
strip_cdata_tags = module.params['strip_cdata_tags']
|
||||
insertbefore = module.params['insertbefore']
|
||||
insertafter = module.params['insertafter']
|
||||
|
||||
# Check if we have lxml 2.3.0 or newer installed
|
||||
if not HAS_LXML:
|
||||
|
@ -881,7 +937,7 @@ def main():
|
|||
|
||||
# add_children set?
|
||||
if add_children:
|
||||
add_target_children(module, doc, xpath, namespaces, add_children, input_type)
|
||||
add_target_children(module, doc, xpath, namespaces, add_children, input_type, insertbefore, insertafter)
|
||||
|
||||
# No?: Carry on
|
||||
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<business type="bar">
|
||||
<name>Tasty Beverage Co.</name>
|
||||
<beers>
|
||||
<beer>Rochefort 10</beer>
|
||||
<beer>St. Bernardus Abbot 12</beer>
|
||||
<beer>Old Rasputin</beer>
|
||||
<beer>Old Motor Oil</beer>
|
||||
<beer>Old Curmudgeon</beer>
|
||||
<beer>Schlitz</beer>
|
||||
</beers>
|
||||
<rating subjective="true">10</rating>
|
||||
<website>
|
||||
<mobilefriendly/>
|
||||
<address>http://tastybeverageco.com</address>
|
||||
</website>
|
||||
</business>
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<business type="bar">
|
||||
<name>Tasty Beverage Co.</name>
|
||||
<beers>
|
||||
<beer>Rochefort 10</beer>
|
||||
<beer>Old Rasputin</beer>
|
||||
<beer>Old Motor Oil</beer>
|
||||
<beer>Old Curmudgeon</beer>
|
||||
<beer>St. Bernardus Abbot 12</beer>
|
||||
<beer>Schlitz</beer>
|
||||
</beers>
|
||||
<rating subjective="true">10</rating>
|
||||
<website>
|
||||
<mobilefriendly/>
|
||||
<address>http://tastybeverageco.com</address>
|
||||
</website>
|
||||
</business>
|
|
@ -37,6 +37,8 @@
|
|||
|
||||
- include_tasks: test-add-children-elements.yml
|
||||
- include_tasks: test-add-children-from-groupvars.yml
|
||||
- include_tasks: test-add-children-insertafter.yml
|
||||
- include_tasks: test-add-children-insertbefore.yml
|
||||
- include_tasks: test-add-children-with-attributes.yml
|
||||
- include_tasks: test-add-element-implicitly.yml
|
||||
- include_tasks: test-count.yml
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
- name: Setup test fixture
|
||||
copy:
|
||||
src: fixtures/ansible-xml-beers.xml
|
||||
dest: /tmp/ansible-xml-beers.xml
|
||||
|
||||
|
||||
- name: Add child element
|
||||
xml:
|
||||
path: /tmp/ansible-xml-beers.xml
|
||||
xpath: '/business/beers/beer[text()="St. Bernardus Abbot 12"]'
|
||||
insertafter: yes
|
||||
add_children:
|
||||
- beer: Old Rasputin
|
||||
- beer: Old Motor Oil
|
||||
- beer: Old Curmudgeon
|
||||
pretty_print: yes
|
||||
register: add_children_insertafter
|
||||
|
||||
- name: Compare to expected result
|
||||
copy:
|
||||
src: results/test-add-children-insertafter.xml
|
||||
dest: /tmp/ansible-xml-beers.xml
|
||||
check_mode: yes
|
||||
diff: yes
|
||||
register: comparison
|
||||
|
||||
- name: Test expected result
|
||||
assert:
|
||||
that:
|
||||
- add_children_insertafter.changed == true
|
||||
- comparison.changed == false # identical
|
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
- name: Setup test fixture
|
||||
copy:
|
||||
src: fixtures/ansible-xml-beers.xml
|
||||
dest: /tmp/ansible-xml-beers.xml
|
||||
|
||||
|
||||
- name: Add child element
|
||||
xml:
|
||||
path: /tmp/ansible-xml-beers.xml
|
||||
xpath: '/business/beers/beer[text()="St. Bernardus Abbot 12"]'
|
||||
insertbefore: yes
|
||||
add_children:
|
||||
- beer: Old Rasputin
|
||||
- beer: Old Motor Oil
|
||||
- beer: Old Curmudgeon
|
||||
pretty_print: yes
|
||||
register: add_children_insertbefore
|
||||
|
||||
- name: Compare to expected result
|
||||
copy:
|
||||
src: results/test-add-children-insertbefore.xml
|
||||
dest: /tmp/ansible-xml-beers.xml
|
||||
check_mode: yes
|
||||
diff: yes
|
||||
register: comparison
|
||||
|
||||
- name: Test expected result
|
||||
assert:
|
||||
that:
|
||||
- add_children_insertbefore.changed == true
|
||||
- comparison.changed == false # identical
|
Loading…
Reference in a new issue