diff --git a/app/api/network.py b/app/api/network.py index 144d5c5bac1993de22c865ea1f2126f3725927ec..ee16cd0c9fa620efc1525c5c8d5955cd10dce16b 100644 --- a/app/api/network.py +++ b/app/api/network.py @@ -97,6 +97,42 @@ def create_network(): ) +@bp.route("/networks/<int:network_id>", methods=["PATCH"]) +@login_groups_accepted("admin") +def patch_network(network_id): + r"""Patch an existing network + + .. :quickref: Network; Update an existing network + + :param network_id: network primary key + :jsonparam vlan_name: vlan name + :jsonparam address: vlan address + :jsonparam first_ip: first IP of the allowed range + :jsonparam last_ip: last IP of the allowed range + :jsonparam gateway: gateway IP + :jsonparam domain: domain name + :jsonparam admin_only: boolean to restrict the network to admin users + :type admin_only: bool + :jsonparam sensitive: hide the network and all hosts if True (for non admin) + :type sensitive: bool + :jsonparam description: description + """ + # The method currently doesn't allow to update the network_scope + # as this will have an impact on the address + allowed_fields = ( + ("vlan_name", str, None), + ("address", str, None), + ("first_ip", str, None), + ("last_ip", str, None), + ("gateway", str, None), + ("domain", models.Domain, "name"), + ("admin_only", bool, None), + ("sensitive", bool, None), + ("description", str, None), + ) + return utils.update_generic_model(models.Network, network_id, allowed_fields) + + @bp.route("/interfaces") @login_required def get_interfaces(): diff --git a/tests/functional/test_api.py b/tests/functional/test_api.py index ce9be1455d201c4463626889bed9422588f8aeef..324645edaf229b86249fd62496b04011df6a41ac 100644 --- a/tests/functional/test_api.py +++ b/tests/functional/test_api.py @@ -2498,3 +2498,72 @@ def test_patch_interface_new_network_permission( token=user_token, ) assert response.status_code == 200 + + +def test_patch_network_no_data(client, network_factory, admin_token): + network = network_factory() + response = patch( + client, f"{API_URL}/network/networks/{network.id}", data={}, token=admin_token + ) + check_response_message(response, "At least one field is required", 422) + + +@pytest.mark.parametrize("field,value", [("foo", "xxxx"), ("name", "mynetwork")]) +def test_patch_network_invalid_fields( + client, network_factory, admin_token, field, value +): + network = network_factory() + response = patch( + client, + f"{API_URL}/network/networks/{network.id}", + data={field: value}, + token=admin_token, + ) + check_response_message(response, f"Invalid field '{field}'", 422) + + +@pytest.mark.parametrize( + "field,value", + [ + ("vlan_name", "new-name"), + ("description", "This is a test"), + ("admin_only", True), + ("sensitive", False), + ("sensitive", True), + ], +) +def test_patch_network(client, network_factory, admin_token, field, value): + network = network_factory() + data = {field: value} + response = patch( + client, f"{API_URL}/network/networks/{network.id}", data=data, token=admin_token + ) + assert response.status_code == 200 + assert response.get_json()[field] == value + updated_network = models.Network.query.get(network.id) + if isinstance(value, bool): + assert getattr(updated_network, field) is value + else: + assert getattr(updated_network, field) == value + + +def test_patch_network_domain(client, network_factory, domain_factory, admin_token): + network = network_factory() + domain = domain_factory(name="foo.example.org") + data = {"domain": domain.name} + response = patch( + client, f"{API_URL}/network/networks/{network.id}", data=data, token=admin_token + ) + assert response.status_code == 200 + assert response.get_json()["domain"] == domain.name + updated_network = models.Network.query.get(network.id) + assert updated_network.domain == domain + + +def test_patch_network_invalid_domain(client, network_factory, admin_token): + network = network_factory() + data = {"domain": "foo"} + response = patch( + client, f"{API_URL}/network/networks/{network.id}", data=data, token=admin_token + ) + check_response_message(response, f"foo is not a valid domain", 400)