From 871dc8706397e502a57f43e99465235457fa04fc Mon Sep 17 00:00:00 2001 From: defhead Date: Fri, 26 Mar 2021 23:25:52 +0100 Subject: [PATCH] Add missing sync history methods --- .../uwetrottmann/trakt5/services/Sync.java | 64 +++++++ .../com/uwetrottmann/trakt5/BaseTestCase.java | 11 ++ .../trakt5/services/SyncTest.java | 169 +++++++++++++++++- 3 files changed, 243 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/uwetrottmann/trakt5/services/Sync.java b/src/main/java/com/uwetrottmann/trakt5/services/Sync.java index 979ec8b8..36a68152 100644 --- a/src/main/java/com/uwetrottmann/trakt5/services/Sync.java +++ b/src/main/java/com/uwetrottmann/trakt5/services/Sync.java @@ -2,6 +2,7 @@ import com.uwetrottmann.trakt5.entities.BaseMovie; import com.uwetrottmann.trakt5.entities.BaseShow; +import com.uwetrottmann.trakt5.entities.HistoryEntry; import com.uwetrottmann.trakt5.entities.LastActivities; import com.uwetrottmann.trakt5.entities.PlaybackResponse; import com.uwetrottmann.trakt5.entities.RatedEpisode; @@ -13,7 +14,9 @@ import com.uwetrottmann.trakt5.entities.WatchlistedEpisode; import com.uwetrottmann.trakt5.entities.WatchlistedSeason; import com.uwetrottmann.trakt5.enums.Extended; +import com.uwetrottmann.trakt5.enums.HistoryType; import com.uwetrottmann.trakt5.enums.RatingsFilter; +import org.threeten.bp.OffsetDateTime; import retrofit2.Call; import retrofit2.http.Body; import retrofit2.http.GET; @@ -131,6 +134,67 @@ Call> watchedShows( @Query(value = "extended", encoded = true) Extended extended ); + /** + * OAuth Required + * + *

Returns movies and episodes that the a user has watched, sorted by most recent. + * + *

The {@code id} uniquely identifies each history event and can be used to remove events individually using the + * {@code POST /sync/history/remove method}. The action will be set to {@code scrobble}, {@code checkin}, or {@code + * watch}. + */ + @GET("sync/history") + Call> getHistory( + @Query("page") Integer page, + @Query("limit") Integer limit, + @Query(value = "extended", encoded = true) Extended extended, + @Query("start_at") OffsetDateTime startAt, + @Query("end_at") OffsetDateTime endAt + ); + + /** + * OAuth Required + * + *

Returns movies or episodes (depending on the type) that the a user has watched, + * sorted by most recent. + * + *

The {@code id} uniquely identifies each history event and can be used to remove events individually using the + * {@code POST /sync/history/remove method}. The action will be set to {@code scrobble}, {@code checkin}, or {@code + * watch}. + */ + @GET("sync/history/{type}") + Call> getHistory( + @Path("type") HistoryType type, + @Query("page") Integer page, + @Query("limit") Integer limit, + @Query(value = "extended", encoded = true) Extended extended, + @Query("start_at") OffsetDateTime startAt, + @Query("end_at") OffsetDateTime endAt + ); + + /** + * OAuth Required + * + *

Returns the history for just the specified item. For example, {@code /sync/history/movies/12601} would return + * all watches for TRON: Legacy and {@code /sync/history/shows/1388} would return all watched episodes + * for Breaking Bad. If an invalid {@code id} is sent, a 404 error will be returned. If the {@code id} is valid, + * but there is no history, an empty array will be returned. + * + *

The {@code id} uniquely identifies each history event and can be used to remove events individually using the + * {@code POST /sync/history/remove method}. The action will be set to {@code scrobble}, {@code checkin}, or {@code + * watch}. + */ + @GET("sync/history/{type}/{id}") + Call> getHistory( + @Path("type") HistoryType type, + @Path("id") int id, + @Query("page") Integer page, + @Query("limit") Integer limit, + @Query(value = "extended", encoded = true) Extended extended, + @Query("start_at") OffsetDateTime startAt, + @Query("end_at") OffsetDateTime endAt + ); + /** * OAuth Required * diff --git a/src/test/java/com/uwetrottmann/trakt5/BaseTestCase.java b/src/test/java/com/uwetrottmann/trakt5/BaseTestCase.java index 388756d4..9ae42bf6 100644 --- a/src/test/java/com/uwetrottmann/trakt5/BaseTestCase.java +++ b/src/test/java/com/uwetrottmann/trakt5/BaseTestCase.java @@ -91,6 +91,17 @@ public T executeCall(Call call) throws IOException { } } + /** + * Execute call with non-Void response body, without extracting it. + */ + public Response executeCallWithoutReadingBody(Call call) throws IOException { + Response response = call.execute(); + if (!response.isSuccessful()) { + handleFailedResponse(response); // will throw error + } + return response; + } + /** * Execute call with Void response body. */ diff --git a/src/test/java/com/uwetrottmann/trakt5/services/SyncTest.java b/src/test/java/com/uwetrottmann/trakt5/services/SyncTest.java index 39961f27..4452a153 100644 --- a/src/test/java/com/uwetrottmann/trakt5/services/SyncTest.java +++ b/src/test/java/com/uwetrottmann/trakt5/services/SyncTest.java @@ -5,6 +5,7 @@ import com.uwetrottmann.trakt5.entities.BaseMovie; import com.uwetrottmann.trakt5.entities.BaseShow; import com.uwetrottmann.trakt5.entities.EpisodeIds; +import com.uwetrottmann.trakt5.entities.HistoryEntry; import com.uwetrottmann.trakt5.entities.LastActivities; import com.uwetrottmann.trakt5.entities.LastActivity; import com.uwetrottmann.trakt5.entities.LastActivityMore; @@ -25,12 +26,15 @@ import com.uwetrottmann.trakt5.entities.SyncShow; import com.uwetrottmann.trakt5.entities.WatchlistedEpisode; import com.uwetrottmann.trakt5.entities.WatchlistedSeason; +import com.uwetrottmann.trakt5.enums.HistoryType; import com.uwetrottmann.trakt5.enums.Rating; import com.uwetrottmann.trakt5.enums.RatingsFilter; import org.junit.Test; import org.threeten.bp.Instant; import org.threeten.bp.OffsetDateTime; import org.threeten.bp.ZoneOffset; +import retrofit2.Call; +import retrofit2.Response; import java.io.IOException; import java.util.ArrayList; @@ -88,7 +92,7 @@ public void test_getPlayback() throws IOException, InterruptedException { assertThat(playback.paused_at).isNotNull(); assertThat(playback.progress).isEqualTo(25.0); } - + if (playback.movie != null && playback.movie.ids != null && playback.movie.ids.tmdb != null && playback.movie.ids.tmdb == interstellar) { foundMovie = true; @@ -531,6 +535,169 @@ public void test_deleteItemsFromWatchlist() throws IOException { assertSyncResponseDelete(requestResponse); } + @Test + public void test_getHistory() throws IOException { + Call> call = getTrakt().sync().getHistory(null, null, null, null, null); + Response> response = executeCallWithoutReadingBody(call); + assertThat(response.headers().get("X-Pagination-Page-Count")).isNotEmpty(); + assertThat(response.headers().get("X-Pagination-Item-Count")).isNotEmpty(); + assertThat(response.headers().get("X-Pagination-Limit")).isEqualTo("10"); + List body = response.body(); + if (body == null) { + throw new IllegalStateException("Body should not be null for successful response"); + } + + for (HistoryEntry entry : body) { + assertThat(entry.id).isGreaterThan(0); + assertThat(entry.watched_at).isNotNull(); + assertThat(entry.action).isNotEmpty(); + assertThat(entry.type).isNotEmpty(); + if ("episode".equals(entry.type)) { + assertThat(entry.episode).isNotNull(); + assertThat(entry.show).isNotNull(); + } else if ("movie".equals(entry.type)) { + assertThat(entry.movie).isNotNull(); + } + } + } + + @Test + public void test_getHistory_with_pagination() throws IOException { + Call> call = getTrakt().sync().getHistory(1, 5, null, null, null); + Response> response = executeCallWithoutReadingBody(call); + assertThat(response.headers().get("X-Pagination-Page-Count")).isNotEmpty(); + assertThat(response.headers().get("X-Pagination-Item-Count")).isNotEmpty(); + assertThat(response.headers().get("X-Pagination-Limit")).isEqualTo("5"); + List body = response.body(); + if (body == null) { + throw new IllegalStateException("Body should not be null for successful response"); + } + + for (HistoryEntry entry : body) { + assertThat(entry.id).isGreaterThan(0); + assertThat(entry.watched_at).isNotNull(); + assertThat(entry.action).isNotEmpty(); + assertThat(entry.type).isNotEmpty(); + if ("episode".equals(entry.type)) { + assertThat(entry.episode).isNotNull(); + assertThat(entry.show).isNotNull(); + } else if ("movie".equals(entry.type)) { + assertThat(entry.movie).isNotNull(); + } + } + } + + @Test + public void test_getHistory_movies() throws IOException { + Call> call = getTrakt().sync().getHistory(HistoryType.MOVIES, null, null, null, null, null); + Response> response = executeCallWithoutReadingBody(call); + assertThat(response.headers().get("X-Pagination-Page-Count")).isNotEmpty(); + assertThat(response.headers().get("X-Pagination-Item-Count")).isNotEmpty(); + assertThat(response.headers().get("X-Pagination-Limit")).isEqualTo("10"); + List body = response.body(); + if (body == null) { + throw new IllegalStateException("Body should not be null for successful response"); + } + + for (HistoryEntry entry : body) { + assertThat(entry.id).isGreaterThan(0); + assertThat(entry.watched_at).isNotNull(); + assertThat(entry.action).isNotEmpty(); + assertThat(entry.type).isNotEmpty(); + assertThat(entry.movie).isNotNull(); + } + } + + @Test + public void test_getHistory_movies_with_pagination() throws IOException { + Call> call = getTrakt().sync().getHistory(HistoryType.MOVIES, 1, 5, null, null, null); + Response> response = executeCallWithoutReadingBody(call); + assertThat(response.headers().get("X-Pagination-Page-Count")).isNotEmpty(); + assertThat(response.headers().get("X-Pagination-Item-Count")).isNotEmpty(); + assertThat(response.headers().get("X-Pagination-Limit")).isEqualTo("5"); + List body = response.body(); + if (body == null) { + throw new IllegalStateException("Body should not be null for successful response"); + } + + for (HistoryEntry entry : body) { + assertThat(entry.id).isGreaterThan(0); + assertThat(entry.watched_at).isNotNull(); + assertThat(entry.action).isNotEmpty(); + assertThat(entry.type).isNotEmpty(); + assertThat(entry.movie).isNotNull(); + } + } + + @Test + public void test_getHistory_episodes() throws IOException { + Call> call = getTrakt().sync().getHistory(HistoryType.EPISODES, null, null, null, null, + null); + Response> response = executeCallWithoutReadingBody(call); + assertThat(response.headers().get("X-Pagination-Page-Count")).isNotEmpty(); + assertThat(response.headers().get("X-Pagination-Item-Count")).isNotEmpty(); + assertThat(response.headers().get("X-Pagination-Limit")).isEqualTo("10"); + List body = response.body(); + if (body == null) { + throw new IllegalStateException("Body should not be null for successful response"); + } + + for (HistoryEntry entry : body) { + assertThat(entry.id).isGreaterThan(0); + assertThat(entry.watched_at).isNotNull(); + assertThat(entry.action).isNotEmpty(); + assertThat(entry.type).isNotEmpty(); + assertThat(entry.episode).isNotNull(); + assertThat(entry.show).isNotNull(); + } + } + + @Test + public void test_getHistory_episodes_with_pagination() throws IOException { + Call> call = getTrakt().sync().getHistory(HistoryType.EPISODES, 1, 5, null, null, null); + Response> response = executeCallWithoutReadingBody(call); + assertThat(response.headers().get("X-Pagination-Page-Count")).isNotEmpty(); + assertThat(response.headers().get("X-Pagination-Item-Count")).isNotEmpty(); + assertThat(response.headers().get("X-Pagination-Limit")).isEqualTo("5"); + List body = response.body(); + if (body == null) { + throw new IllegalStateException("Body should not be null for successful response"); + } + + for (HistoryEntry entry : body) { + assertThat(entry.id).isGreaterThan(0); + assertThat(entry.watched_at).isNotNull(); + assertThat(entry.action).isNotEmpty(); + assertThat(entry.type).isNotEmpty(); + assertThat(entry.episode).isNotNull(); + assertThat(entry.show).isNotNull(); + } + } + + @Test + public void test_getHistory_item() throws IOException { + Call> call = getTrakt().sync().getHistory(HistoryType.MOVIES, + TestData.MOVIE_WATCHED_TRAKT_ID, null, null, null, + OffsetDateTime.of(2016, 8, 3, 9, 0, 0, 0, ZoneOffset.UTC), + OffsetDateTime.of(2016, 8, 3, 10, 0, 0, 0, ZoneOffset.UTC)); + Response> response = executeCallWithoutReadingBody(call); + assertThat(response.headers().get("X-Pagination-Page-Count")).isNotEmpty(); + assertThat(response.headers().get("X-Pagination-Item-Count")).isNotEmpty(); + assertThat(response.headers().get("X-Pagination-Limit")).isEqualTo("10"); + List body = response.body(); + if (body == null) { + throw new IllegalStateException("Body should not be null for successful response"); + } + assertThat(body.size()).isGreaterThan(0); + + for (HistoryEntry entry : body) { + assertThat(entry.watched_at).isNotNull(); + assertThat(entry.action).isNotEmpty(); + assertThat(entry.type).isEqualTo("movie"); + assertThat(entry.movie).isNotNull(); + } + } + private MovieIds buildMovieIds() { return MovieIds.tmdb(TestData.MOVIE_TMDB_ID);