Repository: linode/linode_api4-python

Issue:
inconsistent update behavior
This works:
```
client = ...
instance = client.load(Instance, 123)
instance.tags = ['new-tags']
instance.save()
```
This does nothing:
```
client = ...
instance = Instance(client, 123) # from documentation it is not clear that this will make call at all
instance.tags = ['new-tags']
instance.save()
```
However this does work:
```
client = ...
volume = Volume(client, 1234)
volume.resize(25)
```
Same behavior for all objects. Is it intended?

Also could you confirm that first snippet will cause 2 remote API calls while second will cause 3 and how many call at what method calls will cause third snippet?

Discussion:

Patch:
diff --git a/README.rst b/README.rst
index 3cd7aca..9b8430b 100644
--- a/README.rst
+++ b/README.rst
@@ -84,8 +84,8 @@ Check out the `Getting Started guide`_ for more details on getting started
 with this library, or read `the docs`_ for more extensive documentation.
 
 .. _Linode Personal Access Token: https://www.linode.com/docs/products/tools/api/guides/manage-api-tokens/
-.. _Getting Started guide: http://linode_api4.readthedocs.io/en/latest/guides/getting_started.html
-.. _the docs: http://linode_api4.readthedocs.io/en/latest/index.html
+.. _Getting Started guide: http://linode-api4.readthedocs.io/en/latest/guides/getting_started.html
+.. _the docs: http://linode-api4.readthedocs.io/en/latest/index.html
 
 Examples
 --------
diff --git a/docs/guides/core_concepts.rst b/docs/guides/core_concepts.rst
index 22b0bbc..535d807 100644
--- a/docs/guides/core_concepts.rst
+++ b/docs/guides/core_concepts.rst
@@ -82,6 +82,7 @@ In addition to looking up models from collections, you can simply import the
 model class and create it by ID.::
 
    from linode_api4 import Instance
+
    my_linode = Instance(client, 123)
 
 All models take a `LinodeClient` as their first parameter, and their ID as the
@@ -94,6 +95,14 @@ Be aware that when creating a model this way, it is _not_ loaded from the API
 immediately.  Models in this library are **lazy-loaded**, and will not be looked
 up until one of their attributes that is currently unknown is accessed.
 
+In order to automatically populate a model for an existing Linode resource,
+consider using the :any:`LinodeClient.load` method::
+
+    from linode_api4 import Instance, Disk
+
+    instance = client.load(Instance, 12345)
+    instance_disk = client.load(Disk, 123, instance.id)
+
 Lazy Loading
 ^^^^^^^^^^^^
 
@@ -149,10 +158,15 @@ models can also be deleted in a similar fashion.::
 .. note::
 
    Saving a model *may* fail if the values you are attempting to save are invalid.
-   If the values you are attemting to save are coming from an untrusted source,
+   If the values you are attempting to save are coming from an untrusted source,
    be sure to handle a potential :any:`ApiError` raised by the API returning
    an unsuccessful response code.
 
+    When updating an attribute on a model, ensure that the model has been populated
+    *before* any local changes have been made. Attempting to update an attribute
+    and save a model before the model has been populated will result in no changes
+    being applied.
+
 Relationships
 ^^^^^^^^^^^^^
 
diff --git a/linode_api4/objects/longview.py b/linode_api4/objects/longview.py
index 35b1593..cfbb398 100644
--- a/linode_api4/objects/longview.py
+++ b/linode_api4/objects/longview.py
@@ -16,7 +16,8 @@ class LongviewClient(Base):
 
 
 class LongviewSubscription(Base):
-    api_endpoint = "longview/subscriptions/{id}"
+    api_endpoint = "/longview/subscriptions/{id}"
+
     properties = {
         "id": Property(identifier=True),
         "label": Property(),
diff --git a/linode_api4/objects/support.py b/linode_api4/objects/support.py
index 965346b..c12def1 100644
--- a/linode_api4/objects/support.py
+++ b/linode_api4/objects/support.py
@@ -40,6 +40,10 @@ class SupportTicket(Base):
         "updated": Property(is_datetime=True),
         "updated_by": Property(),
         "replies": Property(derived_class=TicketReply),
+        "attachments": Property(),
+        "closable": Property(),
+        "gravatar_id": Property(),
+        "opened_by": Property(),
     }
 
     @property
@@ -114,3 +118,6 @@ class SupportTicket(Base):
             raise ApiError("{}: {}".format(result.status_code, errors), json=j)
 
         return True
+
+    def support_ticket_close(self):
+        self._client.post("{}/close".format(self.api_endpoint), model=self)


Test patch:
diff --git a/test/fixtures/support_tickets_123.json b/test/fixtures/support_tickets_123.json
new file mode 100644
index 0000000..4a568f1
--- /dev/null
+++ b/test/fixtures/support_tickets_123.json
@@ -0,0 +1,22 @@
+{
+    "attachments": [
+      null
+    ],
+    "closable": false,
+    "closed": "2015-06-04T16:07:03",
+    "description": "I'm having trouble setting the root password on my Linode. I tried following the instructions but something is not working and I'm not sure what I'm doing wrong. Can you please help me figure out how I can reset it?\n",
+    "entity": {
+      "id": 10400,
+      "label": "linode123456",
+      "type": "linode",
+      "url": "/v4/linode/instances/123456"
+    },
+    "gravatar_id": "474a1b7373ae0be4132649e69c36ce30",
+    "id": 123,
+    "opened": "2015-06-04T14:16:44",
+    "opened_by": "some_user",
+    "status": "open",
+    "summary": "Having trouble resetting root password on my Linode\n",
+    "updated": "2015-06-04T16:07:03",
+    "updated_by": "some_other_user"
+  }
diff --git a/test/linode_client_test.py b/test/linode_client_test.py
index d1e444c..6e2bcae 100644
--- a/test/linode_client_test.py
+++ b/test/linode_client_test.py
@@ -396,7 +396,10 @@ class LongviewGroupTest(ClientBaseCase):
         """
         Tests that Longview subscriptions can be retrieved
         """
-        r = self.client.longview.subscriptions()
+
+        with self.mock_get("longview/subscriptions") as m:
+            r = self.client.longview.subscriptions()
+            self.assertEqual(m.call_url, "/longview/subscriptions")
 
         self.assertEqual(len(r), 4)
 
diff --git a/test/objects/support_test.py b/test/objects/support_test.py
new file mode 100644
index 0000000..50ca0f9
--- /dev/null
+++ b/test/objects/support_test.py
@@ -0,0 +1,28 @@
+from test.base import ClientBaseCase
+
+from linode_api4.objects import SupportTicket
+
+
+class SupportTest(ClientBaseCase):
+    """
+    Tests methods of the SupportTicket class
+    """
+
+    def test_get_support_ticket(self):
+        ticket = SupportTicket(self.client, 123)
+
+        self.assertIsNotNone(ticket.attachments)
+        self.assertFalse(ticket.closable)
+        self.assertIsNotNone(ticket.entity)
+        self.assertEqual(ticket.gravatar_id, "474a1b7373ae0be4132649e69c36ce30")
+        self.assertEqual(ticket.id, 123)
+        self.assertEqual(ticket.opened_by, "some_user")
+        self.assertEqual(ticket.status, "open")
+        self.assertEqual(ticket.updated_by, "some_other_user")
+
+    def test_support_ticket_close(self):
+        ticket = SupportTicket(self.client, 123)
+
+        with self.mock_post({}) as m:
+            ticket.support_ticket_close()
+            self.assertEqual(m.call_url, "/support/tickets/123/close")