Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add missing sync history methods #125

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions src/main/java/com/uwetrottmann/trakt5/services/Sync.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -131,6 +134,67 @@ Call<List<BaseShow>> watchedShows(
@Query(value = "extended", encoded = true) Extended extended
);

/**
* <b>OAuth Required</b>
*
* <p> Returns movies and episodes that the a user has watched, sorted by most recent.
*
* <p>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<List<HistoryEntry>> 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
);

/**
* <b>OAuth Required</b>
*
* <p> Returns movies or episodes (depending on the <code>type</code>) that the a user has watched,
* sorted by most recent.
*
* <p>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<List<HistoryEntry>> 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
);

/**
* <b>OAuth Required</b>
*
* <p>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.
*
* <p>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<List<HistoryEntry>> 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
);

/**
* <b>OAuth Required</b>
*
Expand Down
11 changes: 11 additions & 0 deletions src/test/java/com/uwetrottmann/trakt5/BaseTestCase.java
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,17 @@ public <T> T executeCall(Call<T> call) throws IOException {
}
}

/**
* Execute call with non-Void response body, without extracting it.
*/
public <T> Response<T> executeCallWithoutReadingBody(Call<T> call) throws IOException {
Response<T> response = call.execute();
if (!response.isSuccessful()) {
handleFailedResponse(response); // will throw error
}
return response;
}

/**
* Execute call with Void response body.
*/
Expand Down
169 changes: 168 additions & 1 deletion src/test/java/com/uwetrottmann/trakt5/services/SyncTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -531,6 +535,169 @@ public void test_deleteItemsFromWatchlist() throws IOException {
assertSyncResponseDelete(requestResponse);
}

@Test
public void test_getHistory() throws IOException {
Call<List<HistoryEntry>> call = getTrakt().sync().getHistory(null, null, null, null, null);
Response<List<HistoryEntry>> 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<HistoryEntry> 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<List<HistoryEntry>> call = getTrakt().sync().getHistory(1, 5, null, null, null);
Response<List<HistoryEntry>> 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<HistoryEntry> 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<List<HistoryEntry>> call = getTrakt().sync().getHistory(HistoryType.MOVIES, null, null, null, null, null);
Response<List<HistoryEntry>> 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<HistoryEntry> 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<List<HistoryEntry>> call = getTrakt().sync().getHistory(HistoryType.MOVIES, 1, 5, null, null, null);
Response<List<HistoryEntry>> 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<HistoryEntry> 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<List<HistoryEntry>> call = getTrakt().sync().getHistory(HistoryType.EPISODES, null, null, null, null,
null);
Response<List<HistoryEntry>> 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<HistoryEntry> 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<List<HistoryEntry>> call = getTrakt().sync().getHistory(HistoryType.EPISODES, 1, 5, null, null, null);
Response<List<HistoryEntry>> 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<HistoryEntry> 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<List<HistoryEntry>> 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<List<HistoryEntry>> 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<HistoryEntry> 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);
Expand Down