Refactor the smarts into the api classes.

The object classes are now simple wrappers.
Improve parameter testing, by passing foo="bar" where possible.
This commit is contained in:
sneakypete81 2013-09-10 17:45:53 +01:00
parent 7a7b43afc7
commit 86ba0914c8
18 changed files with 307 additions and 316 deletions

View file

@ -71,7 +71,7 @@ class TestActionCreate(TestActions):
invalid object. invalid object.
""" """
with self.assertRaises(AttributeError): with self.assertRaises(AttributeError):
self.client.action.create(target=object(), foo="bar") self.client.action.create(target=object())
@mock.patch.object(trovebox.Trovebox, 'post') @mock.patch.object(trovebox.Trovebox, 'post')
def test_action_create_invalid_return_type(self, mock_post): def test_action_create_invalid_return_type(self, mock_post):
@ -79,23 +79,23 @@ class TestActionCreate(TestActions):
mock_post.return_value = self._return_value({"target": "test", mock_post.return_value = self._return_value({"target": "test",
"target_type": "invalid"}) "target_type": "invalid"})
with self.assertRaises(NotImplementedError): with self.assertRaises(NotImplementedError):
self.client.action.create(target=self.test_photos[0], foo="bar") self.client.action.create(target=self.test_photos[0])
class TestActionDelete(TestActions): class TestActionDelete(TestActions):
@mock.patch.object(trovebox.Trovebox, 'post') @mock.patch.object(trovebox.Trovebox, 'post')
def test_action_delete(self, mock_post): def test_action_delete(self, mock_post):
"""Check that an action can be deleted""" """Check that an action can be deleted"""
mock_post.return_value = self._return_value(True) mock_post.return_value = self._return_value(True)
result = self.client.action.delete(self.test_actions[0]) result = self.client.action.delete(self.test_actions[0], foo="bar")
mock_post.assert_called_with("/action/1/delete.json") mock_post.assert_called_with("/action/1/delete.json", foo="bar")
self.assertEqual(result, True) self.assertEqual(result, True)
@mock.patch.object(trovebox.Trovebox, 'post') @mock.patch.object(trovebox.Trovebox, 'post')
def test_action_delete_id(self, mock_post): def test_action_delete_id(self, mock_post):
"""Check that an action can be deleted using its ID""" """Check that an action can be deleted using its ID"""
mock_post.return_value = self._return_value(True) mock_post.return_value = self._return_value(True)
result = self.client.action.delete("1") result = self.client.action.delete("1", foo="bar")
mock_post.assert_called_with("/action/1/delete.json") mock_post.assert_called_with("/action/1/delete.json", foo="bar")
self.assertEqual(result, True) self.assertEqual(result, True)
@mock.patch.object(trovebox.Trovebox, 'post') @mock.patch.object(trovebox.Trovebox, 'post')
@ -110,8 +110,8 @@ class TestActionDelete(TestActions):
"""Check that an action can be deleted using the action object directly""" """Check that an action can be deleted using the action object directly"""
mock_post.return_value = self._return_value(True) mock_post.return_value = self._return_value(True)
action = self.test_actions[0] action = self.test_actions[0]
result = action.delete() result = action.delete(foo="bar")
mock_post.assert_called_with("/action/1/delete.json") mock_post.assert_called_with("/action/1/delete.json", foo="bar")
self.assertEqual(result, True) self.assertEqual(result, True)
self.assertEqual(action.get_fields(), {}) self.assertEqual(action.get_fields(), {})
self.assertEqual(action.id, None) self.assertEqual(action.id, None)

View file

@ -42,8 +42,8 @@ class TestActivitiesList(TestActivities):
"""Check that the activity list is returned correctly""" """Check that the activity list is returned correctly"""
mock_get.return_value = self._return_value(self.test_activities_dict) mock_get.return_value = self._return_value(self.test_activities_dict)
result = self.client.activities.list() result = self.client.activities.list(foo="bar")
mock_get.assert_called_with("/activities/list.json") mock_get.assert_called_with("/activities/list.json", foo="bar")
self.assertEqual(len(result), 2) self.assertEqual(len(result), 2)
self.assertEqual(result[0].id, "1") self.assertEqual(result[0].id, "1")
self.assertEqual(result[0].type, "photo_upload") self.assertEqual(result[0].type, "photo_upload")
@ -56,16 +56,16 @@ class TestActivitiesList(TestActivities):
def test_empty_result(self, mock_get): def test_empty_result(self, mock_get):
"""Check that an empty result is transformed into an empty list """ """Check that an empty result is transformed into an empty list """
mock_get.return_value = self._return_value("") mock_get.return_value = self._return_value("")
result = self.client.activities.list() result = self.client.activities.list(foo="bar")
mock_get.assert_called_with("/activities/list.json") mock_get.assert_called_with("/activities/list.json", foo="bar")
self.assertEqual(result, []) self.assertEqual(result, [])
@mock.patch.object(trovebox.Trovebox, 'get') @mock.patch.object(trovebox.Trovebox, 'get')
def test_zero_rows(self, mock_get): def test_zero_rows(self, mock_get):
"""Check that totalRows=0 is transformed into an empty list """ """Check that totalRows=0 is transformed into an empty list """
mock_get.return_value = self._return_value([{"totalRows": 0}]) mock_get.return_value = self._return_value([{"totalRows": 0}])
result = self.client.activities.list() result = self.client.activities.list(foo="bar")
mock_get.assert_called_with("/activities/list.json") mock_get.assert_called_with("/activities/list.json", foo="bar")
self.assertEqual(result, []) self.assertEqual(result, [])
@mock.patch.object(trovebox.Trovebox, 'get') @mock.patch.object(trovebox.Trovebox, 'get')
@ -73,11 +73,13 @@ class TestActivitiesList(TestActivities):
"""Check that the activity list filters are applied properly""" """Check that the activity list filters are applied properly"""
mock_get.return_value = self._return_value(self.test_activities_dict) mock_get.return_value = self._return_value(self.test_activities_dict)
self.client.activities.list(filters={"foo": "bar", self.client.activities.list(filters={"foo": "bar",
"test1": "test2"}) "test1": "test2"},
foo="bar")
# Dict element can be any order # Dict element can be any order
self.assertIn(mock_get.call_args[0], self.assertIn(mock_get.call_args[0],
[("/activities/foo-bar/test1-test2/list.json",), [("/activities/foo-bar/test1-test2/list.json",),
("/activities/test1-test2/foo-bar/list.json",)]) ("/activities/test1-test2/foo-bar/list.json",)])
self.assertEqual(mock_get.call_args[1], {"foo": "bar"})
class TestActivitiesPurge(TestActivities): class TestActivitiesPurge(TestActivities):
@mock.patch.object(trovebox.Trovebox, 'post') @mock.patch.object(trovebox.Trovebox, 'post')
@ -135,4 +137,4 @@ class TestActivityView(TestActivities):
mock_get.return_value = self._return_value(self._view_wrapper( mock_get.return_value = self._return_value(self._view_wrapper(
{"data": "", "type": "invalid"})) {"data": "", "type": "invalid"}))
with self.assertRaises(NotImplementedError): with self.assertRaises(NotImplementedError):
self.client.activity.view(self.test_activities[0], foo="bar") self.client.activity.view(self.test_activities[0])

View file

@ -37,8 +37,8 @@ class TestAlbumsList(TestAlbums):
def test_albums_list(self, mock_get): def test_albums_list(self, mock_get):
"""Check that the album list is returned correctly""" """Check that the album list is returned correctly"""
mock_get.return_value = self._return_value(self.test_albums_dict) mock_get.return_value = self._return_value(self.test_albums_dict)
result = self.client.albums.list() result = self.client.albums.list(foo="bar")
mock_get.assert_called_with("/albums/list.json") mock_get.assert_called_with("/albums/list.json", foo="bar")
self.assertEqual(len(result), 2) self.assertEqual(len(result), 2)
self.assertEqual(result[0].id, "1") self.assertEqual(result[0].id, "1")
self.assertEqual(result[0].name, "Album 1") self.assertEqual(result[0].name, "Album 1")
@ -49,24 +49,24 @@ class TestAlbumsList(TestAlbums):
def test_empty_result(self, mock_get): def test_empty_result(self, mock_get):
"""Check that an empty result is transformed into an empty list """ """Check that an empty result is transformed into an empty list """
mock_get.return_value = self._return_value("") mock_get.return_value = self._return_value("")
result = self.client.albums.list() result = self.client.albums.list(foo="bar")
mock_get.assert_called_with("/albums/list.json") mock_get.assert_called_with("/albums/list.json", foo="bar")
self.assertEqual(result, []) self.assertEqual(result, [])
@mock.patch.object(trovebox.Trovebox, 'get') @mock.patch.object(trovebox.Trovebox, 'get')
def test_zero_rows(self, mock_get): def test_zero_rows(self, mock_get):
"""Check that totalRows=0 is transformed into an empty list """ """Check that totalRows=0 is transformed into an empty list """
mock_get.return_value = self._return_value([{"totalRows": 0}]) mock_get.return_value = self._return_value([{"totalRows": 0}])
result = self.client.albums.list() result = self.client.albums.list(foo="bar")
mock_get.assert_called_with("/albums/list.json") mock_get.assert_called_with("/albums/list.json", foo="bar")
self.assertEqual(result, []) self.assertEqual(result, [])
@mock.patch.object(trovebox.Trovebox, 'get') @mock.patch.object(trovebox.Trovebox, 'get')
def test_albums_list_returns_cover_photos(self, mock_get): def test_albums_list_returns_cover_photos(self, mock_get):
"""Check that the album list returns cover photo objects""" """Check that the album list returns cover photo objects"""
mock_get.return_value = self._return_value(self.test_albums_dict) mock_get.return_value = self._return_value(self.test_albums_dict)
result = self.client.albums.list() result = self.client.albums.list(foo="bar")
mock_get.assert_called_with("/albums/list.json") mock_get.assert_called_with("/albums/list.json", foo="bar")
self.assertEqual(len(result), 2) self.assertEqual(len(result), 2)
self.assertEqual(result[0].id, "1") self.assertEqual(result[0].id, "1")
self.assertEqual(result[0].name, "Album 1") self.assertEqual(result[0].name, "Album 1")
@ -83,7 +83,8 @@ class TestAlbumUpdateCover(TestAlbums):
"""Check that an album cover can be updated""" """Check that an album cover can be updated"""
mock_post.return_value = self._return_value(self.test_albums_dict[1]) mock_post.return_value = self._return_value(self.test_albums_dict[1])
result = self.client.album.cover_update(self.test_albums[0], result = self.client.album.cover_update(self.test_albums[0],
self.test_photos[0], foo="bar") self.test_photos[0],
foo="bar")
mock_post.assert_called_with("/album/1/cover/1a/update.json", mock_post.assert_called_with("/album/1/cover/1a/update.json",
foo="bar") foo="bar")
self.assertEqual(result.id, "2") self.assertEqual(result.id, "2")
@ -134,16 +135,16 @@ class TestAlbumDelete(TestAlbums):
def test_album_delete(self, mock_post): def test_album_delete(self, mock_post):
"""Check that an album can be deleted""" """Check that an album can be deleted"""
mock_post.return_value = self._return_value(True) mock_post.return_value = self._return_value(True)
result = self.client.album.delete(self.test_albums[0]) result = self.client.album.delete(self.test_albums[0], foo="bar")
mock_post.assert_called_with("/album/1/delete.json") mock_post.assert_called_with("/album/1/delete.json", foo="bar")
self.assertEqual(result, True) self.assertEqual(result, True)
@mock.patch.object(trovebox.Trovebox, 'post') @mock.patch.object(trovebox.Trovebox, 'post')
def test_album_delete_id(self, mock_post): def test_album_delete_id(self, mock_post):
"""Check that an album can be deleted using its ID""" """Check that an album can be deleted using its ID"""
mock_post.return_value = self._return_value(True) mock_post.return_value = self._return_value(True)
result = self.client.album.delete("1") result = self.client.album.delete("1", foo="bar")
mock_post.assert_called_with("/album/1/delete.json") mock_post.assert_called_with("/album/1/delete.json", foo="bar")
self.assertEqual(result, True) self.assertEqual(result, True)
@mock.patch.object(trovebox.Trovebox, 'post') @mock.patch.object(trovebox.Trovebox, 'post')
@ -158,8 +159,8 @@ class TestAlbumDelete(TestAlbums):
"""Check that an album can be deleted using the album object directly""" """Check that an album can be deleted using the album object directly"""
mock_post.return_value = self._return_value(True) mock_post.return_value = self._return_value(True)
album = self.test_albums[0] album = self.test_albums[0]
result = album.delete() result = album.delete(foo="bar")
mock_post.assert_called_with("/album/1/delete.json") mock_post.assert_called_with("/album/1/delete.json", foo="bar")
self.assertEqual(result, True) self.assertEqual(result, True)
self.assertEqual(album.get_fields(), {}) self.assertEqual(album.get_fields(), {})
self.assertEqual(album.id, None) self.assertEqual(album.id, None)
@ -180,21 +181,23 @@ class TestAlbumAdd(TestAlbums):
def test_album_add(self, mock_post): def test_album_add(self, mock_post):
""" Check that photos can be added to an album """ """ Check that photos can be added to an album """
mock_post.return_value = self._return_value(self.test_albums_dict[1]) mock_post.return_value = self._return_value(self.test_albums_dict[1])
self.client.album.add(self.test_albums[0], self.test_photos, result = self.client.album.add(self.test_albums[0], self.test_photos,
foo="bar") foo="bar")
mock_post.assert_called_with("/album/1/photo/add.json", mock_post.assert_called_with("/album/1/photo/add.json",
ids=["1a", "2b"], foo="bar") ids=["1a", "2b"], foo="bar")
self.assertEqual(result.id, self.test_albums[1].id)
@mock.patch.object(trovebox.Trovebox, 'post') @mock.patch.object(trovebox.Trovebox, 'post')
def test_album_add_id(self, mock_post): def test_album_add_id(self, mock_post):
""" Check that photos can be added to an album using IDs """ """ Check that photos can be added to an album using IDs """
mock_post.return_value = self._return_value(self.test_albums_dict[1]) mock_post.return_value = self._return_value(self.test_albums_dict[1])
self.client.album.add(self.test_albums[0].id, result = self.client.album.add(self.test_albums[0].id,
objects=["1a", "2b"], objects=["1a", "2b"],
object_type="photo", object_type="photo",
foo="bar") foo="bar")
mock_post.assert_called_with("/album/1/photo/add.json", mock_post.assert_called_with("/album/1/photo/add.json",
ids=["1a", "2b"], foo="bar") ids=["1a", "2b"], foo="bar")
self.assertEqual(result.id, self.test_albums[1].id)
@mock.patch.object(trovebox.Trovebox, 'post') @mock.patch.object(trovebox.Trovebox, 'post')
def test_album_object_add(self, mock_post): def test_album_object_add(self, mock_post):
@ -203,9 +206,11 @@ class TestAlbumAdd(TestAlbums):
album object directly album object directly
""" """
mock_post.return_value = self._return_value(self.test_albums_dict[1]) mock_post.return_value = self._return_value(self.test_albums_dict[1])
self.test_albums[0].add(self.test_photos, foo="bar") album = self.test_albums[0]
album.add(self.test_photos, foo="bar")
mock_post.assert_called_with("/album/1/photo/add.json", mock_post.assert_called_with("/album/1/photo/add.json",
ids=["1a", "2b"], foo="bar") ids=["1a", "2b"], foo="bar")
self.assertEqual(album.id, self.test_albums[1].id)
@mock.patch.object(trovebox.Trovebox, 'post') @mock.patch.object(trovebox.Trovebox, 'post')
def test_album_add_single(self, mock_post): def test_album_add_single(self, mock_post):
@ -238,21 +243,23 @@ class TestAlbumRemovePhotos(TestAlbums):
def test_album_remove(self, mock_post): def test_album_remove(self, mock_post):
""" Check that photos can be removed from an album """ """ Check that photos can be removed from an album """
mock_post.return_value = self._return_value(self.test_albums_dict[1]) mock_post.return_value = self._return_value(self.test_albums_dict[1])
self.client.album.remove(self.test_albums[0], self.test_photos, result = self.client.album.remove(self.test_albums[0], self.test_photos,
foo="bar") foo="bar")
mock_post.assert_called_with("/album/1/photo/remove.json", mock_post.assert_called_with("/album/1/photo/remove.json",
ids=["1a", "2b"], foo="bar") ids=["1a", "2b"], foo="bar")
self.assertEqual(result.id, self.test_albums[1].id)
@mock.patch.object(trovebox.Trovebox, 'post') @mock.patch.object(trovebox.Trovebox, 'post')
def test_album_remove_id(self, mock_post): def test_album_remove_id(self, mock_post):
""" Check that photos can be removed from an album using IDs """ """ Check that photos can be removed from an album using IDs """
mock_post.return_value = self._return_value(self.test_albums_dict[1]) mock_post.return_value = self._return_value(self.test_albums_dict[1])
self.client.album.remove(self.test_albums[0].id, result = self.client.album.remove(self.test_albums[0].id,
objects=["1a", "2b"], objects=["1a", "2b"],
object_type="photo", object_type="photo",
foo="bar") foo="bar")
mock_post.assert_called_with("/album/1/photo/remove.json", mock_post.assert_called_with("/album/1/photo/remove.json",
ids=["1a", "2b"], foo="bar") ids=["1a", "2b"], foo="bar")
self.assertEqual(result.id, self.test_albums[1].id)
@mock.patch.object(trovebox.Trovebox, 'post') @mock.patch.object(trovebox.Trovebox, 'post')
def test_album_object_remove(self, mock_post): def test_album_object_remove(self, mock_post):
@ -261,9 +268,11 @@ class TestAlbumRemovePhotos(TestAlbums):
album object directly album object directly
""" """
mock_post.return_value = self._return_value(self.test_albums_dict[1]) mock_post.return_value = self._return_value(self.test_albums_dict[1])
self.test_albums[0].remove(self.test_photos, foo="bar") album = self.test_albums[0]
album.remove(self.test_photos, foo="bar")
mock_post.assert_called_with("/album/1/photo/remove.json", mock_post.assert_called_with("/album/1/photo/remove.json",
ids=["1a", "2b"], foo="bar") ids=["1a", "2b"], foo="bar")
self.assertEqual(album.id, self.test_albums[1].id)
@mock.patch.object(trovebox.Trovebox, 'post') @mock.patch.object(trovebox.Trovebox, 'post')
def test_album_remove_single(self, mock_post): def test_album_remove_single(self, mock_post):

View file

@ -31,8 +31,8 @@ class TestPhotosList(TestPhotos):
"""Check that the photo list is returned correctly""" """Check that the photo list is returned correctly"""
mock_get.return_value = self._return_value(self.test_photos_dict) mock_get.return_value = self._return_value(self.test_photos_dict)
result = self.client.photos.list() result = self.client.photos.list(foo="bar")
mock_get.assert_called_with("/photos/list.json") mock_get.assert_called_with("/photos/list.json", foo="bar")
self.assertEqual(len(result), 2) self.assertEqual(len(result), 2)
self.assertEqual(result[0].id, "1a") self.assertEqual(result[0].id, "1a")
self.assertEqual(result[0].tags, ["tag1", "tag2"]) self.assertEqual(result[0].tags, ["tag1", "tag2"])
@ -43,16 +43,16 @@ class TestPhotosList(TestPhotos):
def test_empty_result(self, mock_get): def test_empty_result(self, mock_get):
"""Check that an empty result is transformed into an empty list """ """Check that an empty result is transformed into an empty list """
mock_get.return_value = self._return_value("") mock_get.return_value = self._return_value("")
result = self.client.photos.list() result = self.client.photos.list(foo="bar")
mock_get.assert_called_with("/photos/list.json") mock_get.assert_called_with("/photos/list.json", foo="bar")
self.assertEqual(result, []) self.assertEqual(result, [])
@mock.patch.object(trovebox.Trovebox, 'get') @mock.patch.object(trovebox.Trovebox, 'get')
def test_zero_rows(self, mock_get): def test_zero_rows(self, mock_get):
"""Check that totalRows=0 is transformed into an empty list """ """Check that totalRows=0 is transformed into an empty list """
mock_get.return_value = self._return_value([{"totalRows": 0}]) mock_get.return_value = self._return_value([{"totalRows": 0}])
result = self.client.photos.list() result = self.client.photos.list(foo="bar")
mock_get.assert_called_with("/photos/list.json") mock_get.assert_called_with("/photos/list.json", foo="bar")
self.assertEqual(result, []) self.assertEqual(result, [])
class TestPhotosUpdate(TestPhotos): class TestPhotosUpdate(TestPhotos):
@ -89,16 +89,18 @@ class TestPhotosDelete(TestPhotos):
def test_photos_delete(self, mock_post): def test_photos_delete(self, mock_post):
"""Check that multiple photos can be deleted""" """Check that multiple photos can be deleted"""
mock_post.return_value = self._return_value(True) mock_post.return_value = self._return_value(True)
result = self.client.photos.delete(self.test_photos) result = self.client.photos.delete(self.test_photos, foo="bar")
mock_post.assert_called_with("/photos/delete.json", ids=["1a", "2b"]) mock_post.assert_called_with("/photos/delete.json",
ids=["1a", "2b"], foo="bar")
self.assertEqual(result, True) self.assertEqual(result, True)
@mock.patch.object(trovebox.Trovebox, 'post') @mock.patch.object(trovebox.Trovebox, 'post')
def test_photos_delete_ids(self, mock_post): def test_photos_delete_ids(self, mock_post):
"""Check that multiple photos can be deleted using their IDs""" """Check that multiple photos can be deleted using their IDs"""
mock_post.return_value = self._return_value(True) mock_post.return_value = self._return_value(True)
result = self.client.photos.delete(["1a", "2b"]) result = self.client.photos.delete(["1a", "2b"], foo="bar")
mock_post.assert_called_with("/photos/delete.json", ids=["1a", "2b"]) mock_post.assert_called_with("/photos/delete.json",
ids=["1a", "2b"], foo="bar")
self.assertEqual(result, True) self.assertEqual(result, True)
@mock.patch.object(trovebox.Trovebox, 'post') @mock.patch.object(trovebox.Trovebox, 'post')
@ -116,16 +118,16 @@ class TestPhotoDelete(TestPhotos):
def test_photo_delete(self, mock_post): def test_photo_delete(self, mock_post):
"""Check that a photo can be deleted""" """Check that a photo can be deleted"""
mock_post.return_value = self._return_value(True) mock_post.return_value = self._return_value(True)
result = self.client.photo.delete(self.test_photos[0]) result = self.client.photo.delete(self.test_photos[0], foo="bar")
mock_post.assert_called_with("/photo/1a/delete.json") mock_post.assert_called_with("/photo/1a/delete.json", foo="bar")
self.assertEqual(result, True) self.assertEqual(result, True)
@mock.patch.object(trovebox.Trovebox, 'post') @mock.patch.object(trovebox.Trovebox, 'post')
def test_photo_delete_id(self, mock_post): def test_photo_delete_id(self, mock_post):
"""Check that a photo can be deleted using its ID""" """Check that a photo can be deleted using its ID"""
mock_post.return_value = self._return_value(True) mock_post.return_value = self._return_value(True)
result = self.client.photo.delete("1a") result = self.client.photo.delete("1a", foo="bar")
mock_post.assert_called_with("/photo/1a/delete.json") mock_post.assert_called_with("/photo/1a/delete.json", foo="bar")
self.assertEqual(result, True) self.assertEqual(result, True)
@mock.patch.object(trovebox.Trovebox, 'post') @mock.patch.object(trovebox.Trovebox, 'post')
@ -143,8 +145,8 @@ class TestPhotoDelete(TestPhotos):
""" """
mock_post.return_value = self._return_value(True) mock_post.return_value = self._return_value(True)
photo = self.test_photos[0] photo = self.test_photos[0]
result = photo.delete() result = photo.delete(foo="bar")
mock_post.assert_called_with("/photo/1a/delete.json") mock_post.assert_called_with("/photo/1a/delete.json", foo="bar")
self.assertEqual(result, True) self.assertEqual(result, True)
self.assertEqual(photo.get_fields(), {}) self.assertEqual(photo.get_fields(), {})
self.assertEqual(photo.id, None) self.assertEqual(photo.id, None)
@ -309,8 +311,10 @@ class TestPhotoNextPrevious(TestPhotos):
mock_get.return_value = self._return_value( mock_get.return_value = self._return_value(
{"next": [self.test_photos_dict[0]], {"next": [self.test_photos_dict[0]],
"previous": [self.test_photos_dict[1]]}) "previous": [self.test_photos_dict[1]]})
result = self.client.photo.next_previous(self.test_photos[0]) result = self.client.photo.next_previous(self.test_photos[0],
mock_get.assert_called_with("/photo/1a/nextprevious.json") foo="bar")
mock_get.assert_called_with("/photo/1a/nextprevious.json",
foo="bar")
self.assertEqual(result["next"][0].get_fields(), self.assertEqual(result["next"][0].get_fields(),
self.test_photos_dict[0]) self.test_photos_dict[0])
self.assertEqual(result["previous"][0].get_fields(), self.assertEqual(result["previous"][0].get_fields(),
@ -325,8 +329,9 @@ class TestPhotoNextPrevious(TestPhotos):
mock_get.return_value = self._return_value( mock_get.return_value = self._return_value(
{"next": [self.test_photos_dict[0]], {"next": [self.test_photos_dict[0]],
"previous": [self.test_photos_dict[1]]}) "previous": [self.test_photos_dict[1]]})
result = self.client.photo.next_previous("1a") result = self.client.photo.next_previous("1a", foo="bar")
mock_get.assert_called_with("/photo/1a/nextprevious.json") mock_get.assert_called_with("/photo/1a/nextprevious.json",
foo="bar")
self.assertEqual(result["next"][0].get_fields(), self.assertEqual(result["next"][0].get_fields(),
self.test_photos_dict[0]) self.test_photos_dict[0])
self.assertEqual(result["previous"][0].get_fields(), self.assertEqual(result["previous"][0].get_fields(),
@ -341,8 +346,9 @@ class TestPhotoNextPrevious(TestPhotos):
mock_get.return_value = self._return_value( mock_get.return_value = self._return_value(
{"next": [self.test_photos_dict[0]], {"next": [self.test_photos_dict[0]],
"previous": [self.test_photos_dict[1]]}) "previous": [self.test_photos_dict[1]]})
result = self.test_photos[0].next_previous() result = self.test_photos[0].next_previous(foo="bar")
mock_get.assert_called_with("/photo/1a/nextprevious.json") mock_get.assert_called_with("/photo/1a/nextprevious.json",
foo="bar")
self.assertEqual(result["next"][0].get_fields(), self.assertEqual(result["next"][0].get_fields(),
self.test_photos_dict[0]) self.test_photos_dict[0])
self.assertEqual(result["previous"][0].get_fields(), self.assertEqual(result["previous"][0].get_fields(),
@ -353,8 +359,10 @@ class TestPhotoNextPrevious(TestPhotos):
"""Check that the next photos are returned""" """Check that the next photos are returned"""
mock_get.return_value = self._return_value( mock_get.return_value = self._return_value(
{"next": [self.test_photos_dict[0]]}) {"next": [self.test_photos_dict[0]]})
result = self.client.photo.next_previous(self.test_photos[0]) result = self.client.photo.next_previous(self.test_photos[0],
mock_get.assert_called_with("/photo/1a/nextprevious.json") foo="bar")
mock_get.assert_called_with("/photo/1a/nextprevious.json",
foo="bar")
self.assertEqual(result["next"][0].get_fields(), self.assertEqual(result["next"][0].get_fields(),
self.test_photos_dict[0]) self.test_photos_dict[0])
self.assertNotIn("previous", result) self.assertNotIn("previous", result)
@ -364,8 +372,10 @@ class TestPhotoNextPrevious(TestPhotos):
"""Check that the previous photos are returned""" """Check that the previous photos are returned"""
mock_get.return_value = self._return_value( mock_get.return_value = self._return_value(
{"previous": [self.test_photos_dict[1]]}) {"previous": [self.test_photos_dict[1]]})
result = self.client.photo.next_previous(self.test_photos[0]) result = self.client.photo.next_previous(self.test_photos[0],
mock_get.assert_called_with("/photo/1a/nextprevious.json") foo="bar")
mock_get.assert_called_with("/photo/1a/nextprevious.json",
foo="bar")
self.assertEqual(result["previous"][0].get_fields(), self.assertEqual(result["previous"][0].get_fields(),
self.test_photos_dict[1]) self.test_photos_dict[1])
self.assertNotIn("next", result) self.assertNotIn("next", result)
@ -376,8 +386,10 @@ class TestPhotoNextPrevious(TestPhotos):
mock_get.return_value = self._return_value( mock_get.return_value = self._return_value(
{"next": [self.test_photos_dict[0], self.test_photos_dict[0]], {"next": [self.test_photos_dict[0], self.test_photos_dict[0]],
"previous": [self.test_photos_dict[1], self.test_photos_dict[1]]}) "previous": [self.test_photos_dict[1], self.test_photos_dict[1]]})
result = self.client.photo.next_previous(self.test_photos[0]) result = self.client.photo.next_previous(self.test_photos[0],
mock_get.assert_called_with("/photo/1a/nextprevious.json") foo="bar")
mock_get.assert_called_with("/photo/1a/nextprevious.json",
foo="bar")
self.assertEqual(result["next"][0].get_fields(), self.assertEqual(result["next"][0].get_fields(),
self.test_photos_dict[0]) self.test_photos_dict[0])
self.assertEqual(result["next"][1].get_fields(), self.assertEqual(result["next"][1].get_fields(),

View file

@ -27,8 +27,8 @@ class TestTagsList(TestTags):
def test_tags_list(self, mock_get): def test_tags_list(self, mock_get):
"""Check that the tag list is returned correctly""" """Check that the tag list is returned correctly"""
mock_get.return_value = self._return_value(self.test_tags_dict) mock_get.return_value = self._return_value(self.test_tags_dict)
result = self.client.tags.list() result = self.client.tags.list(foo="bar")
mock_get.assert_called_with("/tags/list.json") mock_get.assert_called_with("/tags/list.json", foo="bar")
self.assertEqual(len(result), 2) self.assertEqual(len(result), 2)
self.assertEqual(result[0].id, "tag1") self.assertEqual(result[0].id, "tag1")
self.assertEqual(result[0].count, 11) self.assertEqual(result[0].count, 11)
@ -39,16 +39,16 @@ class TestTagsList(TestTags):
def test_empty_result(self, mock_get): def test_empty_result(self, mock_get):
"""Check that an empty result is transformed into an empty list """ """Check that an empty result is transformed into an empty list """
mock_get.return_value = self._return_value("") mock_get.return_value = self._return_value("")
result = self.client.tags.list() result = self.client.tags.list(foo="bar")
mock_get.assert_called_with("/tags/list.json") mock_get.assert_called_with("/tags/list.json", foo="bar")
self.assertEqual(result, []) self.assertEqual(result, [])
@mock.patch.object(trovebox.Trovebox, 'get') @mock.patch.object(trovebox.Trovebox, 'get')
def test_zero_rows(self, mock_get): def test_zero_rows(self, mock_get):
"""Check that totalRows=0 is transformed into an empty list """ """Check that totalRows=0 is transformed into an empty list """
mock_get.return_value = self._return_value([{"totalRows": 0}]) mock_get.return_value = self._return_value([{"totalRows": 0}])
result = self.client.tags.list() result = self.client.tags.list(foo="bar")
mock_get.assert_called_with("/tags/list.json") mock_get.assert_called_with("/tags/list.json", foo="bar")
self.assertEqual(result, []) self.assertEqual(result, [])
class TestTagCreate(TestTags): class TestTagCreate(TestTags):
@ -56,8 +56,9 @@ class TestTagCreate(TestTags):
def test_tag_create(self, mock_post): def test_tag_create(self, mock_post):
"""Check that a tag can be created""" """Check that a tag can be created"""
mock_post.return_value = self._return_value(True) mock_post.return_value = self._return_value(True)
result = self.client.tag.create("test") result = self.client.tag.create("test", foo="bar")
mock_post.assert_called_with("/tag/create.json", tag="test") mock_post.assert_called_with("/tag/create.json", tag="test",
foo="bar")
self.assertEqual(result, True) self.assertEqual(result, True)
class TestTagDelete(TestTags): class TestTagDelete(TestTags):
@ -65,16 +66,16 @@ class TestTagDelete(TestTags):
def test_tag_delete(self, mock_post): def test_tag_delete(self, mock_post):
"""Check that a tag can be deleted""" """Check that a tag can be deleted"""
mock_post.return_value = self._return_value(True) mock_post.return_value = self._return_value(True)
result = self.client.tag.delete(self.test_tags[0]) result = self.client.tag.delete(self.test_tags[0], foo="bar")
mock_post.assert_called_with("/tag/tag1/delete.json") mock_post.assert_called_with("/tag/tag1/delete.json", foo="bar")
self.assertEqual(result, True) self.assertEqual(result, True)
@mock.patch.object(trovebox.Trovebox, 'post') @mock.patch.object(trovebox.Trovebox, 'post')
def test_tag_delete_id(self, mock_post): def test_tag_delete_id(self, mock_post):
"""Check that a tag can be deleted using its ID""" """Check that a tag can be deleted using its ID"""
mock_post.return_value = self._return_value(True) mock_post.return_value = self._return_value(True)
result = self.client.tag.delete("tag1") result = self.client.tag.delete("tag1", foo="bar")
mock_post.assert_called_with("/tag/tag1/delete.json") mock_post.assert_called_with("/tag/tag1/delete.json", foo="bar")
self.assertEqual(result, True) self.assertEqual(result, True)
@mock.patch.object(trovebox.Trovebox, 'post') @mock.patch.object(trovebox.Trovebox, 'post')
@ -89,8 +90,8 @@ class TestTagDelete(TestTags):
"""Check that a tag can be deleted when using the tag object directly""" """Check that a tag can be deleted when using the tag object directly"""
mock_post.return_value = self._return_value(True) mock_post.return_value = self._return_value(True)
tag = self.test_tags[0] tag = self.test_tags[0]
result = tag.delete() result = tag.delete(foo="bar")
mock_post.assert_called_with("/tag/tag1/delete.json") mock_post.assert_called_with("/tag/tag1/delete.json", foo="bar")
self.assertEqual(result, True) self.assertEqual(result, True)
self.assertEqual(tag.get_fields(), {}) self.assertEqual(tag.get_fields(), {})
self.assertEqual(tag.id, None) self.assertEqual(tag.id, None)

View file

@ -14,7 +14,7 @@ diff --unified --recursive '--exclude=.pylint-ignores.patch' original/api/api_al
--- original/api/api_album.py --- original/api/api_album.py
+++ patched/api/api_album.py +++ patched/api/api_album.py
@@ -8,7 +8,7 @@ @@ -8,7 +8,7 @@
from trovebox import http from trovebox.objects.album import Album
from .api_base import ApiBase from .api_base import ApiBase
-class ApiAlbums(ApiBase): -class ApiAlbums(ApiBase):
@ -37,7 +37,16 @@ diff --unified --recursive '--exclude=.pylint-ignores.patch' original/api/api_ba
diff --unified --recursive '--exclude=.pylint-ignores.patch' original/api/api_tag.py patched/api/api_tag.py diff --unified --recursive '--exclude=.pylint-ignores.patch' original/api/api_tag.py patched/api/api_tag.py
--- original/api/api_tag.py --- original/api/api_tag.py
+++ patched/api/api_tag.py +++ patched/api/api_tag.py
@@ -5,7 +5,7 @@ @@ -2,7 +2,7 @@
api_tag.py : Trovebox Tag API Classes
"""
try:
- from urllib.parse import quote # Python3
+ from urllib.parse import quote # Python3 # pylint: disable=import-error,no-name-in-module
except ImportError:
from urllib import quote # Python2
@@ -10,7 +10,7 @@
from trovebox.objects.tag import Tag from trovebox.objects.tag import Tag
from .api_base import ApiBase from .api_base import ApiBase
@ -159,27 +168,15 @@ diff --unified --recursive '--exclude=.pylint-ignores.patch' original/main.py pa
files=files, **params) files=files, **params)
for file_ in files: for file_ in files:
files[file_].close() files[file_].close()
diff --unified --recursive '--exclude=.pylint-ignores.patch' original/objects/tag.py patched/objects/tag.py
--- original/objects/tag.py
+++ patched/objects/tag.py
@@ -2,7 +2,7 @@
Representation of a Tag object
"""
try:
- from urllib.parse import quote # Python3
+ from urllib.parse import quote # Python3 # pylint: disable=import-error,no-name-in-module
except ImportError:
from urllib import quote # Python2
diff --unified --recursive '--exclude=.pylint-ignores.patch' original/objects/trovebox_object.py patched/objects/trovebox_object.py diff --unified --recursive '--exclude=.pylint-ignores.patch' original/objects/trovebox_object.py patched/objects/trovebox_object.py
--- original/objects/trovebox_object.py --- original/objects/trovebox_object.py
+++ patched/objects/trovebox_object.py +++ patched/objects/trovebox_object.py
@@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
""" Base object supporting the storage of custom fields as attributes """ """ Base object supporting the storage of custom fields as attributes """
def __init__(self, trovebox, json_dict): _type = "None"
self._type = "None" def __init__(self, client, json_dict):
- self.id = None - self.id = None
+ self.id = None # pylint: disable=invalid-name + self.id = None # pylint: disable=invalid-name
self.name = None self.name = None
self._trovebox = trovebox self._client = client
self._json_dict = json_dict self._json_dict = json_dict

View file

@ -1,6 +1,7 @@
""" """
api_action.py : Trovebox Action API Classes api_action.py : Trovebox Action API Classes
""" """
from trovebox.errors import TroveboxError
from trovebox.objects.action import Action from trovebox.objects.action import Action
from .api_base import ApiBase from .api_base import ApiBase
@ -15,15 +16,14 @@ class ApiAction(ApiBase):
If a Trovebox object is used, the target type is inferred If a Trovebox object is used, the target type is inferred
automatically. automatically.
""" """
# Extract the type from the target # Extract the target type
if target_type is None: if target_type is None:
target_type = target.get_type() target_type = target.get_type()
# Extract the ID from the target # Extract the target ID
try: try:
target_id = target.id target_id = target.id
except AttributeError: except AttributeError:
# Assume the ID was passed in directly
target_id = target target_id = target
result = self._client.post("/action/%s/%s/create.json" % result = self._client.post("/action/%s/%s/create.json" %
@ -39,9 +39,12 @@ class ApiAction(ApiBase):
Returns True if successful. Returns True if successful.
Raises a TroveboxError if not. Raises a TroveboxError if not.
""" """
if not isinstance(action, Action): result = self._client.post("/action/%s/delete.json" %
action = Action(self._client, {"id": action}) self._extract_id(action),
return action.delete(**kwds) **kwds)["result"]
if not result:
raise TroveboxError("Delete response returned False")
return result
def view(self, action, **kwds): def view(self, action, **kwds):
""" """
@ -50,7 +53,7 @@ class ApiAction(ApiBase):
Requests all properties of an action. Requests all properties of an action.
Returns the requested action object. Returns the requested action object.
""" """
if not isinstance(action, Action): result = self._client.get("/action/%s/view.json" %
action = Action(self._client, {"id": action}) self._extract_id(action),
action.view(**kwds) **kwds)["result"]
return action return Action(self._client, result)

View file

@ -1,7 +1,7 @@
""" """
api_activity.py : Trovebox Activity API Classes api_activity.py : Trovebox Activity API Classes
""" """
from trovebox import http import json
from trovebox.errors import TroveboxError from trovebox.errors import TroveboxError
from trovebox.objects.activity import Activity from trovebox.objects.activity import Activity
from .api_base import ApiBase from .api_base import ApiBase
@ -19,7 +19,7 @@ class ApiActivities(ApiBase):
filter_string = self._build_filter_string(filters) filter_string = self._build_filter_string(filters)
activities = self._client.get("/activities/%slist.json" % filter_string, activities = self._client.get("/activities/%slist.json" % filter_string,
**kwds)["result"] **kwds)["result"]
activities = http.result_to_list(activities) activities = self._result_to_list(activities)
return [Activity(self._client, activity) for activity in activities] return [Activity(self._client, activity) for activity in activities]
def purge(self, **kwds): def purge(self, **kwds):
@ -42,7 +42,11 @@ class ApiActivity(ApiBase):
Requests all properties of an activity. Requests all properties of an activity.
Returns the requested activity object. Returns the requested activity object.
""" """
if not isinstance(activity, Activity): result = self._client.get("/activity/%s/view.json" %
activity = Activity(self._client, {"id": activity}) self._extract_id(activity),
activity.view(**kwds) **kwds)["result"]
return activity
# TBD: Why is the result enclosed/encoded like this?
result = result["0"]
result["data"] = json.loads(result["data"])
return Activity(self._client, result)

View file

@ -3,9 +3,9 @@ api_album.py : Trovebox Album API Classes
""" """
import collections import collections
from trovebox.errors import TroveboxError
from trovebox.objects.trovebox_object import TroveboxObject from trovebox.objects.trovebox_object import TroveboxObject
from trovebox.objects.album import Album from trovebox.objects.album import Album
from trovebox import http
from .api_base import ApiBase from .api_base import ApiBase
class ApiAlbums(ApiBase): class ApiAlbums(ApiBase):
@ -17,7 +17,7 @@ class ApiAlbums(ApiBase):
Returns a list of Album objects. Returns a list of Album objects.
""" """
albums = self._client.get("/albums/list.json", **kwds)["result"] albums = self._client.get("/albums/list.json", **kwds)["result"]
albums = http.result_to_list(albums) albums = self._result_to_list(albums)
return [Album(self._client, album) for album in albums] return [Album(self._client, album) for album in albums]
class ApiAlbum(ApiBase): class ApiAlbum(ApiBase):
@ -29,10 +29,18 @@ class ApiAlbum(ApiBase):
Update the cover photo of an album. Update the cover photo of an album.
Returns the updated album object. Returns the updated album object.
""" """
if not isinstance(album, Album): result = self._client.post("/album/%s/cover/%s/update.json" %
album = Album(self._client, {"id": album}) (self._extract_id(album),
album.cover_update(photo, **kwds) self._extract_id(photo)),
return album **kwds)["result"]
# API currently doesn't return the updated album
# (frontend issue #1369)
if isinstance(result, bool): # pragma: no cover
result = self._client.get("/album/%s/view.json" %
self._extract_id(album))["result"]
return Album(self._client, result)
def create(self, name, **kwds): def create(self, name, **kwds):
""" """
@ -52,9 +60,12 @@ class ApiAlbum(ApiBase):
Returns True if successful. Returns True if successful.
Raises a TroveboxError if not. Raises a TroveboxError if not.
""" """
if not isinstance(album, Album): result = self._client.post("/album/%s/delete.json" %
album = Album(self._client, {"id": album}) self._extract_id(album),
return album.delete(**kwds) **kwds)["result"]
if not result:
raise TroveboxError("Delete response returned False")
return result
def add(self, album, objects, object_type=None, **kwds): def add(self, album, objects, object_type=None, **kwds):
""" """
@ -64,7 +75,7 @@ class ApiAlbum(ApiBase):
The objects are a list of either IDs or Trovebox objects. The objects are a list of either IDs or Trovebox objects.
If Trovebox objects are used, the object type is inferred If Trovebox objects are used, the object type is inferred
automatically. automatically.
Returns True if the album was updated successfully. Returns the updated album object.
""" """
return self._add_remove("add", album, objects, object_type, return self._add_remove("add", album, objects, object_type,
**kwds) **kwds)
@ -77,7 +88,7 @@ class ApiAlbum(ApiBase):
The objects are a list of either IDs or Trovebox objects. The objects are a list of either IDs or Trovebox objects.
If Trovebox objects are used, the object type is inferred If Trovebox objects are used, the object type is inferred
automatically. automatically.
Returns True if the album was updated successfully. Returns the updated album object.
""" """
return self._add_remove("remove", album, objects, object_type, return self._add_remove("remove", album, objects, object_type,
**kwds) **kwds)
@ -85,10 +96,6 @@ class ApiAlbum(ApiBase):
def _add_remove(self, action, album, objects, object_type=None, def _add_remove(self, action, album, objects, object_type=None,
**kwds): **kwds):
"""Common code for the add and remove endpoints.""" """Common code for the add and remove endpoints."""
# Extract the id of the album
if isinstance(album, Album):
album = album.id
# Ensure we have an iterable of objects # Ensure we have an iterable of objects
if not isinstance(objects, collections.Iterable): if not isinstance(objects, collections.Iterable):
objects = [objects] objects = [objects]
@ -106,9 +113,17 @@ class ApiAlbum(ApiBase):
# Extract the ids of the objects # Extract the ids of the objects
objects[i] = obj.id objects[i] = obj.id
return self._client.post("/album/%s/%s/%s.json" % result = self._client.post("/album/%s/%s/%s.json" %
(album, object_type, action), (self._extract_id(album),
ids=objects, **kwds)["result"] object_type, action),
ids=objects, **kwds)["result"]
# API currently doesn't return the updated album
# (frontend issue #1369)
if isinstance(result, bool): # pragma: no cover
result = self._client.get("/album/%s/view.json" %
self._extract_id(album))["result"]
return Album(self._client, result)
def update(self, album, **kwds): def update(self, album, **kwds):
""" """
@ -117,10 +132,16 @@ class ApiAlbum(ApiBase):
Updates an album with the specified parameters. Updates an album with the specified parameters.
Returns the updated album object. Returns the updated album object.
""" """
if not isinstance(album, Album): result = self._client.post("/album/%s/update.json" %
album = Album(self._client, {"id": album}) self._extract_id(album),
album.update(**kwds) **kwds)["result"]
return album
# APIv1 doesn't return the updated album (frontend issue #937)
if isinstance(result, bool): # pragma: no cover
result = self._client.get("/album/%s/view.json" %
self._extract_id(album))["result"]
return Album(self._client, result)
def view(self, album, **kwds): def view(self, album, **kwds):
""" """
@ -129,7 +150,7 @@ class ApiAlbum(ApiBase):
Requests all properties of an album. Requests all properties of an album.
Returns the requested album object. Returns the requested album object.
""" """
if not isinstance(album, Album): result = self._client.get("/album/%s/view.json" %
album = Album(self._client, {"id": album}) self._extract_id(album),
album.view(**kwds) **kwds)["result"]
return album return Album(self._client, result)

View file

@ -18,3 +18,21 @@ class ApiBase(object):
for filt in filters: for filt in filters:
filter_string += "%s-%s/" % (filt, filters[filt]) filter_string += "%s-%s/" % (filt, filters[filt])
return filter_string return filter_string
@staticmethod
def _extract_id(obj):
""" Return obj.id, or obj if the object doesn't have an ID """
try:
return obj.id
except AttributeError:
return obj
@staticmethod
def _result_to_list(result):
""" Handle the case where the result contains no items """
if not result:
return []
if "totalRows" in result[0] and result[0]["totalRows"] == 0:
return []
else:
return result

View file

@ -3,24 +3,10 @@ api_photo.py : Trovebox Photo API Classes
""" """
import base64 import base64
from trovebox import http
from trovebox.errors import TroveboxError from trovebox.errors import TroveboxError
from trovebox.objects.photo import Photo from trovebox.objects.photo import Photo
from .api_base import ApiBase from .api_base import ApiBase
def _extract_ids(photos):
"""
Given a list of objects, extract the photo id for each Photo
object.
"""
ids = []
for photo in photos:
if isinstance(photo, Photo):
ids.append(photo.id)
else:
ids.append(photo)
return ids
class ApiPhotos(ApiBase): class ApiPhotos(ApiBase):
""" Definitions of /photos/ API endpoints """ """ Definitions of /photos/ API endpoints """
# TODO: Add options # TODO: Add options
@ -31,7 +17,7 @@ class ApiPhotos(ApiBase):
Returns a list of Photo objects. Returns a list of Photo objects.
""" """
photos = self._client.get("/photos/list.json", **kwds)["result"] photos = self._client.get("/photos/list.json", **kwds)["result"]
photos = http.result_to_list(photos) photos = self._result_to_list(photos)
return [Photo(self._client, photo) for photo in photos] return [Photo(self._client, photo) for photo in photos]
# def share(self, **kwds): # def share(self, **kwds):
@ -44,7 +30,7 @@ class ApiPhotos(ApiBase):
Returns True if successful. Returns True if successful.
Raises a TroveboxError if not. Raises a TroveboxError if not.
""" """
ids = _extract_ids(photos) ids = [self._extract_id(photo) for photo in photos]
if not self._client.post("/photos/delete.json", ids=ids, if not self._client.post("/photos/delete.json", ids=ids,
**kwds)["result"]: **kwds)["result"]:
raise TroveboxError("Delete response returned False") raise TroveboxError("Delete response returned False")
@ -58,7 +44,7 @@ class ApiPhotos(ApiBase):
Returns True if successful. Returns True if successful.
Raises TroveboxError if not. Raises TroveboxError if not.
""" """
ids = _extract_ids(photos) ids = [self._extract_id(photo) for photo in photos]
if not self._client.post("/photos/update.json", ids=ids, if not self._client.post("/photos/update.json", ids=ids,
**kwds)["result"]: **kwds)["result"]:
raise TroveboxError("Update response returned False") raise TroveboxError("Update response returned False")
@ -74,9 +60,12 @@ class ApiPhoto(ApiBase):
Returns True if successful. Returns True if successful.
Raises a TroveboxError if not. Raises a TroveboxError if not.
""" """
if not isinstance(photo, Photo): result = self._client.post("/photo/%s/delete.json" %
photo = Photo(self._client, {"id": photo}) self._extract_id(photo),
return photo.delete(**kwds) **kwds)["result"]
if not result:
raise TroveboxError("Delete response returned False")
return result
# def delete_source(self, photo, **kwds): # def delete_source(self, photo, **kwds):
@ -95,10 +84,10 @@ class ApiPhoto(ApiBase):
Updates a photo with the specified parameters. Updates a photo with the specified parameters.
Returns the updated photo object. Returns the updated photo object.
""" """
if not isinstance(photo, Photo): result = self._client.post("/photo/%s/update.json" %
photo = Photo(self._client, {"id": photo}) self._extract_id(photo),
photo.update(**kwds) **kwds)["result"]
return photo return Photo(self._client, result)
# TODO: Add options # TODO: Add options
def view(self, photo, **kwds): def view(self, photo, **kwds):
@ -110,10 +99,10 @@ class ApiPhoto(ApiBase):
by using the "returnSizes" parameter. by using the "returnSizes" parameter.
Returns the requested photo object. Returns the requested photo object.
""" """
if not isinstance(photo, Photo): result = self._client.get("/photo/%s/view.json" %
photo = Photo(self._client, {"id": photo}) self._extract_id(photo),
photo.view(**kwds) **kwds)["result"]
return photo return Photo(self._client, result)
def upload(self, photo_file, **kwds): def upload(self, photo_file, **kwds):
""" """
@ -151,9 +140,29 @@ class ApiPhoto(ApiBase):
Returns a dict containing the next and previous photo lists Returns a dict containing the next and previous photo lists
(there may be more than one next/previous photo returned). (there may be more than one next/previous photo returned).
""" """
if not isinstance(photo, Photo): result = self._client.get("/photo/%s/nextprevious.json" %
photo = Photo(self._client, {"id": photo}) self._extract_id(photo),
return photo.next_previous(**kwds) **kwds)["result"]
value = {}
if "next" in result:
# Workaround for APIv1
if not isinstance(result["next"], list): # pragma: no cover
result["next"] = [result["next"]]
value["next"] = []
for photo in result["next"]:
value["next"].append(Photo(self._client, photo))
if "previous" in result:
# Workaround for APIv1
if not isinstance(result["previous"], list): # pragma: no cover
result["previous"] = [result["previous"]]
value["previous"] = []
for photo in result["previous"]:
value["previous"].append(Photo(self._client, photo))
return value
def transform(self, photo, **kwds): def transform(self, photo, **kwds):
""" """
@ -163,7 +172,13 @@ class ApiPhoto(ApiBase):
eg. transform(photo, rotate=90) eg. transform(photo, rotate=90)
Returns the transformed photo. Returns the transformed photo.
""" """
if not isinstance(photo, Photo): result = self._client.post("/photo/%s/transform.json" %
photo = Photo(self._client, {"id": photo}) self._extract_id(photo),
photo.transform(**kwds) **kwds)["result"]
return photo
# APIv1 doesn't return the transformed photo (frontend issue #955)
if isinstance(result, bool): # pragma: no cover
result = self._client.get("/photo/%s/view.json" %
self._extract_id(photo))["result"]
return Photo(self._client, result)

View file

@ -1,7 +1,12 @@
""" """
api_tag.py : Trovebox Tag API Classes api_tag.py : Trovebox Tag API Classes
""" """
from trovebox import http try:
from urllib.parse import quote # Python3
except ImportError:
from urllib import quote # Python2
from trovebox.errors import TroveboxError
from trovebox.objects.tag import Tag from trovebox.objects.tag import Tag
from .api_base import ApiBase from .api_base import ApiBase
@ -14,7 +19,7 @@ class ApiTags(ApiBase):
Returns a list of Tag objects. Returns a list of Tag objects.
""" """
tags = self._client.get("/tags/list.json", **kwds)["result"] tags = self._client.get("/tags/list.json", **kwds)["result"]
tags = http.result_to_list(tags) tags = self._result_to_list(tags)
return [Tag(self._client, tag) for tag in tags] return [Tag(self._client, tag) for tag in tags]
class ApiTag(ApiBase): class ApiTag(ApiBase):
@ -37,9 +42,12 @@ class ApiTag(ApiBase):
Returns True if successful. Returns True if successful.
Raises a TroveboxError if not. Raises a TroveboxError if not.
""" """
if not isinstance(tag, Tag): result = self._client.post("/tag/%s/delete.json" %
tag = Tag(self._client, {"id": tag}) quote(self._extract_id(tag)),
return tag.delete(**kwds) **kwds)["result"]
if not result:
raise TroveboxError("Delete response returned False")
return result
def update(self, tag, **kwds): def update(self, tag, **kwds):
""" """
@ -48,9 +56,9 @@ class ApiTag(ApiBase):
Updates a tag with the specified parameters. Updates a tag with the specified parameters.
Returns the updated tag object. Returns the updated tag object.
""" """
if not isinstance(tag, Tag): result = self._client.post("/tag/%s/update.json" %
tag = Tag(self._client, {"id": tag}) quote(self._extract_id(tag)),
tag.update(**kwds) **kwds)["result"]
return tag return Tag(self._client, result)
# def view(self, tag, **kwds): # def view(self, tag, **kwds):

View file

@ -249,12 +249,3 @@ class Http(object):
raise TroveboxDuplicateError("Code %d: %s" % (code, message)) raise TroveboxDuplicateError("Code %d: %s" % (code, message))
else: else:
raise TroveboxError("Code %d: %s" % (code, message)) raise TroveboxError("Code %d: %s" % (code, message))
def result_to_list(result):
""" Handle the case where the result contains no items """
if not result:
return []
if "totalRows" in result[0] and result[0]["totalRows"] == 0:
return []
else:
return result

View file

@ -1,7 +1,6 @@
""" """
Representation of an Action object Representation of an Action object
""" """
from trovebox.errors import TroveboxError
from .trovebox_object import TroveboxObject from .trovebox_object import TroveboxObject
from .photo import Photo from .photo import Photo
@ -33,10 +32,7 @@ class Action(TroveboxObject):
Returns True if successful. Returns True if successful.
Raises a TroveboxError if not. Raises a TroveboxError if not.
""" """
result = self._client.post("/action/%s/delete.json" % result = self._client.action.delete(self, **kwds)
self.id, **kwds)["result"]
if not result:
raise TroveboxError("Delete response returned False")
self._delete_fields() self._delete_fields()
return result return result
@ -45,9 +41,8 @@ class Action(TroveboxObject):
Endpoint: /action/<id>/view.json Endpoint: /action/<id>/view.json
Requests the full contents of the action. Requests the full contents of the action.
Updates the action's fields with the response. Updates the action object's fields with the response.
""" """
result = self._client.get("/action/%s/view.json" % result = self._client.action.view(self, **kwds)
self.id, **kwds)["result"] self._replace_fields(result.get_fields())
self._replace_fields(result)
self._update_fields_with_objects() self._update_fields_with_objects()

View file

@ -1,8 +1,6 @@
""" """
Representation of an Activity object Representation of an Activity object
""" """
import json
from .trovebox_object import TroveboxObject from .trovebox_object import TroveboxObject
from .photo import Photo from .photo import Photo
@ -33,12 +31,7 @@ class Activity(TroveboxObject):
Requests the full contents of the activity. Requests the full contents of the activity.
Updates the activity's fields with the response. Updates the activity's fields with the response.
""" """
result = self._client.get("/activity/%s/view.json" % result = self._client.activity.view(self, **kwds)
self.id, **kwds)["result"] self._replace_fields(result.get_fields())
# TBD: Why is the result enclosed/encoded like this?
result = result["0"]
result["data"] = json.loads(result["data"])
self._replace_fields(result)
self._update_fields_with_objects() self._update_fields_with_objects()

View file

@ -1,7 +1,6 @@
""" """
Representation of an Album object Representation of an Album object
""" """
from trovebox.errors import TroveboxError
from .trovebox_object import TroveboxObject from .trovebox_object import TroveboxObject
from .photo import Photo from .photo import Photo
@ -38,19 +37,8 @@ class Album(TroveboxObject):
Update the cover photo of this album. Update the cover photo of this album.
""" """
if not isinstance(photo, Photo): result = self._client.album.cover_update(self, photo, **kwds)
photo = Photo(self._client, {"id": photo}) self._replace_fields(result.get_fields())
result = self._client.post("/album/%s/cover/%s/update.json" %
(self.id, photo.id),
**kwds)["result"]
# API currently doesn't return the updated album
# (frontend issue #1369)
if isinstance(result, bool): # pragma: no cover
result = self._client.get("/album/%s/view.json" %
self.id)["result"]
self._replace_fields(result)
self._update_fields_with_objects() self._update_fields_with_objects()
def delete(self, **kwds): def delete(self, **kwds):
@ -61,10 +49,7 @@ class Album(TroveboxObject):
Returns True if successful. Returns True if successful.
Raises a TroveboxError if not. Raises a TroveboxError if not.
""" """
result = self._client.post("/album/%s/delete.json" % result = self._client.album.delete(self, **kwds)
self.id, **kwds)["result"]
if not result:
raise TroveboxError("Delete response returned False")
self._delete_fields() self._delete_fields()
return result return result
@ -79,13 +64,7 @@ class Album(TroveboxObject):
Updates the album's fields with the response. Updates the album's fields with the response.
""" """
result = self._client.album.add(self, objects, object_type, **kwds) result = self._client.album.add(self, objects, object_type, **kwds)
self._replace_fields(result.get_fields())
# API currently doesn't return the updated album
# (frontend issue #1369)
if isinstance(result, bool): # pragma: no cover
result = self._client.get("/album/%s/view.json" %
self.id)["result"]
self._replace_fields(result)
self._update_fields_with_objects() self._update_fields_with_objects()
def remove(self, objects, object_type=None, **kwds): def remove(self, objects, object_type=None, **kwds):
@ -100,12 +79,7 @@ class Album(TroveboxObject):
""" """
result = self._client.album.remove(self, objects, object_type, result = self._client.album.remove(self, objects, object_type,
**kwds) **kwds)
# API currently doesn't return the updated album self._replace_fields(result.get_fields())
# (frontend issue #1369)
if isinstance(result, bool): # pragma: no cover
result = self._client.get("/album/%s/view.json" %
self.id)["result"]
self._replace_fields(result)
self._update_fields_with_objects() self._update_fields_with_objects()
def update(self, **kwds): def update(self, **kwds):
@ -114,15 +88,8 @@ class Album(TroveboxObject):
Updates this album with the specified parameters. Updates this album with the specified parameters.
""" """
result = self._client.post("/album/%s/update.json" % result = self._client.album.update(self, **kwds)
self.id, **kwds)["result"] self._replace_fields(result.get_fields())
# APIv1 doesn't return the updated album (frontend issue #937)
if isinstance(result, bool): # pragma: no cover
result = self._client.get("/album/%s/view.json" %
self.id)["result"]
self._replace_fields(result)
self._update_fields_with_objects() self._update_fields_with_objects()
def view(self, **kwds): def view(self, **kwds):
@ -132,7 +99,6 @@ class Album(TroveboxObject):
Requests all properties of an album. Requests all properties of an album.
Updates the album's fields with the response. Updates the album's fields with the response.
""" """
result = self._client.get("/album/%s/view.json" % result = self._client.album.view(self, **kwds)
self.id, **kwds)["result"] self._replace_fields(result.get_fields())
self._replace_fields(result)
self._update_fields_with_objects() self._update_fields_with_objects()

View file

@ -1,7 +1,6 @@
""" """
Representation of a Photo object Representation of a Photo object
""" """
from trovebox.errors import TroveboxError
from .trovebox_object import TroveboxObject from .trovebox_object import TroveboxObject
class Photo(TroveboxObject): class Photo(TroveboxObject):
@ -16,10 +15,7 @@ class Photo(TroveboxObject):
Returns True if successful. Returns True if successful.
Raises a TroveboxError if not. Raises a TroveboxError if not.
""" """
result = self._client.post("/photo/%s/delete.json" % result = self._client.photo.delete(self, **kwds)
self.id, **kwds)["result"]
if not result:
raise TroveboxError("Delete response returned False")
self._delete_fields() self._delete_fields()
return result return result
@ -39,9 +35,8 @@ class Photo(TroveboxObject):
Updates this photo with the specified parameters. Updates this photo with the specified parameters.
""" """
result = self._client.post("/photo/%s/update.json" % result = self._client.photo.update(self, **kwds)
self.id, **kwds)["result"] self._replace_fields(result.get_fields())
self._replace_fields(result)
# TODO: Add options # TODO: Add options
def view(self, **kwds): def view(self, **kwds):
@ -53,9 +48,8 @@ class Photo(TroveboxObject):
by using the "returnSizes" parameter. by using the "returnSizes" parameter.
Updates the photo's fields with the response. Updates the photo's fields with the response.
""" """
result = self._client.get("/photo/%s/view.json" % result = self._client.photo.view(self, **kwds)
self.id, **kwds)["result"] self._replace_fields(result.get_fields())
self._replace_fields(result)
def dynamic_url(self, **kwds): def dynamic_url(self, **kwds):
""" Not implemented yet """ """ Not implemented yet """
@ -69,28 +63,7 @@ class Photo(TroveboxObject):
Returns a dict containing the next and previous photo lists Returns a dict containing the next and previous photo lists
(there may be more than one next/previous photo returned). (there may be more than one next/previous photo returned).
""" """
result = self._client.get("/photo/%s/nextprevious.json" % return self._client.photo.next_previous(self, **kwds)
self.id, **kwds)["result"]
value = {}
if "next" in result:
# Workaround for APIv1
if not isinstance(result["next"], list): # pragma: no cover
result["next"] = [result["next"]]
value["next"] = []
for photo in result["next"]:
value["next"].append(Photo(self._client, photo))
if "previous" in result:
# Workaround for APIv1
if not isinstance(result["previous"], list): # pragma: no cover
result["previous"] = [result["previous"]]
value["previous"] = []
for photo in result["previous"]:
value["previous"].append(Photo(self._client, photo))
return value
def transform(self, **kwds): def transform(self, **kwds):
""" """
@ -100,12 +73,5 @@ class Photo(TroveboxObject):
eg. transform(photo, rotate=90) eg. transform(photo, rotate=90)
Updates the photo's fields with the response. Updates the photo's fields with the response.
""" """
result = self._client.post("/photo/%s/transform.json" % result = self._client.photo.transform(self, **kwds)
self.id, **kwds)["result"] self._replace_fields(result.get_fields())
# APIv1 doesn't return the transformed photo (frontend issue #955)
if isinstance(result, bool): # pragma: no cover
result = self._client.get("/photo/%s/view.json" %
self.id)["result"]
self._replace_fields(result)

View file

@ -1,12 +1,6 @@
""" """
Representation of a Tag object Representation of a Tag object
""" """
try:
from urllib.parse import quote # Python3
except ImportError:
from urllib import quote # Python2
from trovebox.errors import TroveboxError
from .trovebox_object import TroveboxObject from .trovebox_object import TroveboxObject
class Tag(TroveboxObject): class Tag(TroveboxObject):
@ -21,10 +15,7 @@ class Tag(TroveboxObject):
Returns True if successful. Returns True if successful.
Raises a TroveboxError if not. Raises a TroveboxError if not.
""" """
result = self._client.post("/tag/%s/delete.json" % result = self._client.tag.delete(self, **kwds)
quote(self.id), **kwds)["result"]
if not result:
raise TroveboxError("Delete response returned False")
self._delete_fields() self._delete_fields()
return result return result
@ -35,8 +26,7 @@ class Tag(TroveboxObject):
Updates this tag with the specified parameters. Updates this tag with the specified parameters.
Returns the updated tag object. Returns the updated tag object.
""" """
result = self._client.post("/tag/%s/update.json" % quote(self.id), result = self._client.tag.update(self, **kwds)
**kwds)["result"] self._replace_fields(result.get_fields())
self._replace_fields(result)
# def view(self, **kwds): # def view(self, **kwds):