Repository: hetznercloud/hcloud-python

Issue:
`FirewallRequest.create` add argument `apply_to`
## Feature Request

**Is your feature request related to a problem? Please describe.**
The [REST documentation](https://docs.hetzner.cloud/#firewalls-create-a-firewall) and the [Go client](https://github.com/hetznercloud/hcloud-go/blob/4cf75f4cdc3e6885eaf0bd4316fa544eba0cf00d/hcloud/firewall.go#L219) both support an argument to immediately apply a new firewall to a server on creation.

The Python client should do so as well.

Discussion:

Patch:
diff --git a/hcloud/firewalls/client.py b/hcloud/firewalls/client.py
index b0a1b23..a1bd86a 100644
--- a/hcloud/firewalls/client.py
+++ b/hcloud/firewalls/client.py
@@ -238,6 +238,7 @@ class FirewallsClient(ClientEntityBase, GetEntityByNameMixin):
                name,  # type: str
                rules=None,  # type: Optional[List[FirewallRule]]
                labels=None,  # type: Optional[str]
+               resources=None,  # type: Optional[List[FirewallResource]]
                ):
         # type: (...) -> CreateFirewallResponse
         """Creates a new Firewall.
@@ -247,6 +248,7 @@ class FirewallsClient(ClientEntityBase, GetEntityByNameMixin):
         :param rules: List[:class:`FirewallRule <hcloud.firewalls.domain.FirewallRule>`] (optional)
         :param labels: Dict[str, str] (optional)
                User-defined labels (key-value pairs)
+        :param resources: List[:class:`FirewallResource <hcloud.firewalls.domain.FirewallResource>`] (optional)
         :return: :class:`CreateFirewallResponse <hcloud.firewalls.domain.CreateFirewallResponse>`
         """
 
@@ -260,16 +262,19 @@ class FirewallsClient(ClientEntityBase, GetEntityByNameMixin):
             data.update({"rules": []})
             for rule in rules:
                 data['rules'].append(rule.to_payload())
-
+        if resources is not None:
+            data.update({"apply_to": []})
+            for resource in resources:
+                data['apply_to'].append(resource.to_payload())
         response = self._client.request(url="/firewalls", json=data, method="POST")
 
-        action = None
-        if response.get('action') is not None:
-            action = BoundAction(self._client.actions, response['action'])
+        actions = []
+        if response.get('actions') is not None:
+            actions = [BoundAction(self._client.actions, _) for _ in response['actions']]
 
         result = CreateFirewallResponse(
             firewall=BoundFirewall(self, response['firewall']),
-            action=action
+            actions=actions
         )
         return result
 
diff --git a/hcloud/firewalls/domain.py b/hcloud/firewalls/domain.py
index c516709..2a8f147 100644
--- a/hcloud/firewalls/domain.py
+++ b/hcloud/firewalls/domain.py
@@ -150,18 +150,18 @@ class CreateFirewallResponse(BaseDomain):
 
     :param firewall: :class:`BoundFirewall <hcloud.firewalls.client.BoundFirewall>`
            The Firewall which was created
-    :param action: :class:`BoundAction <hcloud.actions.client.BoundAction>`
+    :param actions: List[:class:`BoundAction <hcloud.actions.client.BoundAction>`]
            The Action which shows the progress of the Firewall Creation
     """
     __slots__ = (
         "firewall",
-        "action"
+        "actions"
     )
 
     def __init__(
             self,
             firewall,  # type: BoundFirewall
-            action,  # type: BoundAction
+            actions,  # type: BoundAction
     ):
         self.firewall = firewall
-        self.action = action
+        self.actions = actions


Test patch:
diff --git a/tests/unit/firewalls/conftest.py b/tests/unit/firewalls/conftest.py
index 6e3235b..413f129 100644
--- a/tests/unit/firewalls/conftest.py
+++ b/tests/unit/firewalls/conftest.py
@@ -1,6 +1,92 @@
 import pytest
 
 
+@pytest.fixture()
+def response_create_firewall():
+    return {
+        "firewall": {
+            "id": 38,
+            "name": "Corporate Intranet Protection",
+            "labels": {},
+            "created": "2016-01-30T23:50:00+00:00",
+            "rules": [
+                {
+                    "direction": "in",
+                    "source_ips": [
+                        "28.239.13.1/32",
+                        "28.239.14.0/24",
+                        "ff21:1eac:9a3b:ee58:5ca:990c:8bc9:c03b/128"
+                    ],
+                    "destination_ips": [],
+                    "protocol": "tcp",
+                    "port": "80"
+                },
+                {
+                    "direction": "out",
+                    "source_ips": [],
+                    "destination_ips": [
+                        "28.239.13.1/32",
+                        "28.239.14.0/24",
+                        "ff21:1eac:9a3b:ee58:5ca:990c:8bc9:c03b/128"
+                    ],
+                    "protocol": "tcp",
+                    "port": "80"
+                }
+            ],
+            "applied_to": [
+                {
+                    "server": {
+                        "id": 42
+                    },
+                    "type": "server"
+                }
+            ]
+        },
+        "actions": [
+            {
+                "command": "set_firewall_rules",
+                "error": {
+                    "code": "action_failed",
+                    "message": "Action failed"
+                },
+                "finished": "2016-01-30T23:56:00+00:00",
+                "id": 13,
+                "progress": 100,
+                "resources": [
+                    {
+                        "id": 38,
+                        "type": "firewall"
+                    }
+                ],
+                "started": "2016-01-30T23:55:00+00:00",
+                "status": "success"
+            },
+            {
+                "command": "apply_firewall",
+                "error": {
+                    "code": "action_failed",
+                    "message": "Action failed"
+                },
+                "finished": "2016-01-30T23:56:00+00:00",
+                "id": 14,
+                "progress": 100,
+                "resources": [
+                    {
+                        "id": 42,
+                        "type": "server"
+                    },
+                    {
+                        "id": 38,
+                        "type": "firewall"
+                    }
+                ],
+                "started": "2016-01-30T23:55:00+00:00",
+                "status": "success"
+            }
+        ]
+    }
+
+
 @pytest.fixture()
 def firewall_response():
     return {
diff --git a/tests/unit/firewalls/test_client.py b/tests/unit/firewalls/test_client.py
index 8b1eeaf..d54f109 100644
--- a/tests/unit/firewalls/test_client.py
+++ b/tests/unit/firewalls/test_client.py
@@ -270,6 +270,48 @@ class TestFirewallsClient(object):
         assert actions[0].id == 13
         assert actions[0].command == "set_firewall_rules"
 
+    def test_create(self, firewalls_client, response_create_firewall):
+        firewalls_client._client.request.return_value = response_create_firewall
+        response = firewalls_client.create(
+            "Corporate Intranet Protection",
+            rules=[FirewallRule(direction=FirewallRule.DIRECTION_IN, protocol=FirewallRule.PROTOCOL_ICMP,
+                                source_ips=["0.0.0.0/0"])],
+            resources=[FirewallResource(type=FirewallResource.TYPE_SERVER, server=Server(id=4711))]
+        )
+        firewalls_client._client.request.assert_called_with(
+            url="/firewalls",
+            method="POST",
+            json={
+                'name': "Corporate Intranet Protection",
+                'rules': [
+                    {
+                        "direction": "in",
+                        "protocol": "icmp",
+                        "source_ips": [
+                            "0.0.0.0/0"
+                        ]
+                    }
+                ],
+                "apply_to": [
+                    {
+                        "type": "server",
+                        "server": {
+                            "id": 4711
+                        }
+                    }
+                ],
+            }
+        )
+
+        bound_firewall = response.firewall
+        actions = response.actions
+
+        assert bound_firewall._client is firewalls_client
+        assert bound_firewall.id == 38
+        assert bound_firewall.name == "Corporate Intranet Protection"
+
+        assert len(actions) == 2
+
     @pytest.mark.parametrize("firewall", [Firewall(id=38), BoundFirewall(mock.MagicMock(), dict(id=38))])
     def test_update(self, firewalls_client, firewall, response_update_firewall):
         firewalls_client._client.request.return_value = response_update_firewall