From 36c7bd157f56f9a1e594692b33961d0696f69e10 Mon Sep 17 00:00:00 2001 From: Judith Weber Date: Tue, 28 Mar 2023 11:40:12 +0200 Subject: [PATCH 1/4] define expected behaviour --- docs/source/how_to_use.rst | 8 +++++ tests/test_layergroup.py | 63 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/docs/source/how_to_use.rst b/docs/source/how_to_use.rst index 7cbe03d..43d44c5 100644 --- a/docs/source/how_to_use.rst +++ b/docs/source/how_to_use.rst @@ -119,6 +119,14 @@ You can create a layer group from layers that have been uploaded previously with layer_workspace = "my_space" ) + # remove a layer + geo.remove_layer_from_layergroup( + layergroup_name = "my_fancy_layergroup", + layergroup_workspace = "my_space", + layer_name = "superfancy_layer", + layer_workspace = "my_space" + ) + Uploading and publishing styles ------------------------------- diff --git a/tests/test_layergroup.py b/tests/test_layergroup.py index ea2d299..4bb9dbd 100644 --- a/tests/test_layergroup.py +++ b/tests/test_layergroup.py @@ -319,5 +319,68 @@ def test_add_layer_that_doesnt_exist_to_layergroup(self): layer_workspace = "unittest" ) + @data("unittest", None) + def test_remove_layer_from_layergroup(self, workspace): + + self.geoserver.create_layergroup( + name = "test-layergroup-name", + mode = "single", + title = "test_layergroup_to_add", + abstract_text = "this is an abstract text", + layers = ["test_layer_1", "test_layer_2", "test_layer_3"], + workspace = workspace, + keywords = [] + ) + + self.geoserver.remove_layer_from_layergroup( + layergroup_name = "test-layergroup-name", + layergroup_workspace = workspace, + layer_name = "test_layer_1", + layer_workspace = "unittest" + ) + + updated_layer_group_dict = self.geoserver.get_layergroup( + layer_name = "test-layergroup-name", + workspace=workspace, + ) + + self.assertEqual( + len(updated_layer_group_dict["layerGroup"]["publishables"]["published"]), + 2, + f'{len(updated_layer_group_dict["layerGroup"]["publishables"]["published"])} instead of 2 layers in layergroup' + ) + + def test_remove_layer_from_layergroup_that_doesnt_exist(self): + + with self.assertRaises(Exception): + + self.geoserver.remove_layer_from_layergroup( + layergroup_name = "foo", + layergroup_workspace = "unittest", + layer_name = "test_layer_2", + layer_workspace = "unittest" + ) + + def test_remove_layer_that_doesnt_exist_from_layergroup(self): + + self.geoserver.create_layergroup( + name = "test-layergroup-name", + mode = "single", + title = "test_layergroup_to_add", + abstract_text = "this is an abstract text", + layers = ["test_layer_1"], + workspace = "unittest", + keywords = [] + ) + + with self.assertRaises(Exception): + + self.geoserver.remove_layer_from_layergroup( + layergroup_name = "test-layergroup-name", + layergroup_workspace = "unittest", + layer_name = "bar", + layer_workspace = "unittest" + ) + if __name__ == '__main__': unittest.main() From e48c4a7ef127749afdc61fc70df25bbd317f1bff Mon Sep 17 00:00:00 2001 From: Judith Weber Date: Tue, 28 Mar 2023 16:21:29 +0200 Subject: [PATCH 2/4] add remove_layer_from_layergroup method --- geo/Geoserver.py | 138 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/geo/Geoserver.py b/geo/Geoserver.py index 28051a1..047e297 100644 --- a/geo/Geoserver.py +++ b/geo/Geoserver.py @@ -1089,6 +1089,144 @@ def add_layer_to_layergroup( else: raise GeoserverException(r.status_code, r.content) + def remove_layer_from_layergroup( + self, + layer_name: str, + layer_workspace:str, + layergroup_name:str, + layergroup_workspace: str = None + ) -> None: + """ + Add remove the specified layer from an existing layer group and raise an exception if + either the layer or layergroup doesn't exist, or the geoserver is unavailable. + + Parameters + ---------- + layer_name: str, required The name of the layer + layer_workspace: str, required The workspace the layer is located in + layergroup_workspace: str, optional The workspace the layergroup is located in + layergroup_name: str, required The name of the layer group + layergroup_workspace: str, optional The workspace the layergroup is located in + """ + + try: + layergroup_info = self.get_layergroup( + layer_name=layergroup_name, workspace=layergroup_workspace + ) + layer_info = self.get_layer(layer_name=layer_name, workspace=layer_workspace) + except Exception as e: + raise(e) + + # build list of existing publishables & styles + publishables = layergroup_info["layerGroup"]["publishables"]["published"] + if isinstance(publishables, dict): # only 1 layer up to now + publishables = [publishables] + + styles = layergroup_info["layerGroup"]["styles"]["style"] + if isinstance(styles, str): # only 1 layer up to now + styles = [styles] + + layer_to_remove = f"{layer_workspace}:{layer_name}" + + revised_set_of_publishables_and_styles = [ + (pub,style) for (pub,style) in zip( + layergroup_info["layerGroup"]["publishables"]["published"], + layergroup_info["layerGroup"]["styles"]["style"]) + if pub["name"] != layer_to_remove + ] + + revised_set_of_publishables = list(map(list, zip(*revised_set_of_publishables_and_styles)))[0] + revised_set_of_styles = list(map(list, zip(*revised_set_of_publishables_and_styles)))[1] + + xml_payload = self._layergroup_definition_from_layers_and_styles( + publishables = revised_set_of_publishables, + styles = revised_set_of_styles + ) + + if layergroup_workspace == None: + url = f"{self.service_url}/rest/layergroups/{layergroup_name}" + else: + url = f"{self.service_url}/rest/workspaces/{layergroup_workspace}/layergroups/{layergroup_name}" + + r = self._requests( + method="put", + url=url, + data=xml_payload, + headers={"content-type": "text/xml", "accept": "application/xml"}, + ) + if r.status_code == 200: + return + else: + raise GeoserverException(r.status_code, r.content) + + + def _layergroup_definition_from_layers_and_styles(self, publishables:list, styles: list) -> str: + """ + Helper function for add_layer_to_layergroup and remove_layer_from_layergroup + + Parameters + ---------- + layer_name: str, required The name of the layer + layer_workspace: str, required The workspace the layer is located in + + Returns + ------- + Formatted xml request body for PUT layergroup + """ + + # the get_layergroup method may return an empty string for style; + # so we get the default styles for each layer with no style information in the layergroup + if len(styles) == 1: + index = [0] + else: + index = range(len(styles)) + + for ix, this_style, this_layer in zip(index, styles, publishables): + if this_style == "": + this_layer_info = self.get_layer( + layer_name=this_layer["name"].split(":")[1], + workspace=this_layer["name"].split(":")[0] + ) + styles[ix] = { + "name" : this_layer_info["layer"]["defaultStyle"]["name"], + "href" : this_layer_info["layer"]["defaultStyle"]["href"]} + + # build xml structure + layer_skeleton = "" + style_skeleton = "" + + for publishable in publishables: + layer_str = f""" + + {publishable['name']} + {publishable['href']} + + """ + layer_skeleton += layer_str + + for style in styles: + style_str = f""" + + """ + style_skeleton += style_str + + data = f""" + + + {layer_skeleton} + + + {style_skeleton} + + + """ + + return data + + # _______________________________________________________________________________________________ # From d252e40c072e7bef4a4d79880bfa437beb9179b1 Mon Sep 17 00:00:00 2001 From: Judith Weber Date: Tue, 28 Mar 2023 16:25:12 +0200 Subject: [PATCH 3/4] refactor add_layer_to_layergroup --- geo/Geoserver.py | 53 ++++-------------------------------------------- 1 file changed, 4 insertions(+), 49 deletions(-) diff --git a/geo/Geoserver.py b/geo/Geoserver.py index 047e297..70e0128 100644 --- a/geo/Geoserver.py +++ b/geo/Geoserver.py @@ -1013,23 +1013,6 @@ def add_layer_to_layergroup( if isinstance(styles, str): # only 1 layer up to now styles = [styles] - # the get_layergroup method may return an empty string for style; - # so we get the default styles for each layer with no style information in the layergroup - if len(styles) == 1: - index = [0] - else: - index = range(len(styles)-1) - - for ix, this_style, this_layer in zip(index, styles, publishables): - if this_style == "": - this_layer_info = self.get_layer( - layer_name=this_layer["name"].split(":")[1], - workspace=this_layer["name"].split(":")[0] - ) - styles[ix] = { - "name" : this_layer_info["layer"]["defaultStyle"]["name"], - "href" : this_layer_info["layer"]["defaultStyle"]["href"]} - # add publishable & style for the new layer new_pub = { "name" : f"{layer_workspace}:{layer_name}", @@ -1040,38 +1023,10 @@ def add_layer_to_layergroup( new_style = layer_info["layer"]["defaultStyle"] styles.append(new_style) - # build xml structure - layer_skeleton = "" - style_skeleton = "" - - for publishable in publishables: - layer_str = f""" - - {publishable['name']} - {publishable['href']} - - """ - layer_skeleton += layer_str - - for style in styles: - style_str = f""" - - """ - style_skeleton += style_str - - data = f""" - - - {layer_skeleton} - - - {style_skeleton} - - - """ + data = self._layergroup_definition_from_layers_and_styles( + publishables=publishables, + styles=styles + ) if layergroup_workspace == None: url = f"{self.service_url}/rest/layergroups/{layergroup_name}" From c257bbb0ee6d35a43140c584b6b9529421b2e573 Mon Sep 17 00:00:00 2001 From: Judith Weber Date: Tue, 28 Mar 2023 16:25:37 +0200 Subject: [PATCH 4/4] update change log --- docs/source/change_log.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/source/change_log.rst b/docs/source/change_log.rst index 7944700..de32cbc 100644 --- a/docs/source/change_log.rst +++ b/docs/source/change_log.rst @@ -3,6 +3,10 @@ Change Log ``Master branch`` ^^^^^^^^^^^^^^^^^ +* New method `remove_layer_from_layergroup` + +``[v2.4.1 - 2023-01-14]`` +^^^^^^^^^^^^^^^^^^^^^^^^^^ * New method `add_layer_to_layergroup` (see issue `#102 `) * Allow deletion of layergroups from workspaces (see issue `#100 `) and add unittests for the layergroup methods. * Fix json-bug in create_coveragestore method (see issue `#86 `)