Skip to content

Commit

Permalink
GH-586: Add support for "lists/{id}/like" DELETE request
Browse files Browse the repository at this point in the history
  • Loading branch information
henrikfroehling committed Jul 23, 2023
1 parent fd74168 commit fb410d8
Show file tree
Hide file tree
Showing 5 changed files with 334 additions and 4 deletions.
92 changes: 89 additions & 3 deletions Source/Lib/Trakt.NET/Modules/TraktListsModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ public Task<TraktPagedResponse<ITraktListLike>> GetListLikesAsync(string listIdO
}

/// <summary>
/// Likes a list.
/// Like a list.
/// <para>OAuth authorization required.</para>
/// <para>
/// See <a href="https://trakt.docs.apiary.io/#reference/lists/list-like/like-a-list">"Trakt API Doc - Lists: List Like"</a> for more information.
Expand All @@ -228,7 +228,7 @@ public Task<TraktNoContentResponse> LikeListAsync(string listIdOrSlug, Cancellat
}

/// <summary>
/// Likes a list.
/// Like a list.
/// <para>OAuth authorization required.</para>
/// <para>
/// See <a href="https://trakt.docs.apiary.io/#reference/lists/list-like/like-a-list">"Trakt API Doc - Lists: List Like"</a> for more information.
Expand All @@ -241,8 +241,12 @@ public Task<TraktNoContentResponse> LikeListAsync(string listIdOrSlug, Cancellat
/// </param>
/// <exception cref="TraktException">Thrown, if the request fails.</exception>
/// <exception cref="TraktRequestValidationException">Thrown, if validation of request data fails.</exception>
/// <exception cref="ArgumentException">Thrown, if the given <paramref name="traktListId"/> is 0.</exception>
public Task<TraktNoContentResponse> LikeListAsync(uint traktListId, CancellationToken cancellationToken = default)
{
if (traktListId == 0)
throw new ArgumentException("list id must not be 0", nameof(traktListId));

var requestHandler = new RequestHandler(Client);

return requestHandler.ExecuteNoContentRequestAsync(new ListLikeRequest
Expand All @@ -253,7 +257,7 @@ public Task<TraktNoContentResponse> LikeListAsync(uint traktListId, Cancellation
}

/// <summary>
/// Likes a list.
/// Like a list.
/// <para>OAuth authorization required.</para>
/// <para>
/// See <a href="https://trakt.docs.apiary.io/#reference/lists/list-like/like-a-list">"Trakt API Doc - Lists: List Like"</a> for more information.
Expand Down Expand Up @@ -281,6 +285,88 @@ public Task<TraktNoContentResponse> LikeListAsync(ITraktListIds listIds, Cancell
cancellationToken);
}

/// <summary>
/// Unlike a list.
/// <para>OAuth authorization required.</para>
/// <para>
/// See <a href="https://trakt.docs.apiary.io/#reference/lists/list-like/like-a-list">"Trakt API Doc - Lists: List Like"</a> for more information.
/// </para>
/// </summary>
/// <param name="listIdOrSlug">The id or slug of the list, which will be unliked.</param>
/// <param name="cancellationToken">
/// Propagates notification that the request should be canceled.<para/>
/// If provided, the exception <see cref="OperationCanceledException" /> should be catched.
/// </param>
/// <exception cref="TraktException">Thrown, if the request fails.</exception>
/// <exception cref="TraktRequestValidationException">Thrown, if validation of request data fails.</exception>
public Task<TraktNoContentResponse> UnlikeListAsync(string listIdOrSlug, CancellationToken cancellationToken = default)
{
var requestHandler = new RequestHandler(Client);

return requestHandler.ExecuteNoContentRequestAsync(new ListUnlikeRequest
{
Id = listIdOrSlug
},
cancellationToken);
}

/// <summary>
/// Unlike a list.
/// <para>OAuth authorization required.</para>
/// <para>
/// See <a href="https://trakt.docs.apiary.io/#reference/lists/list-like/like-a-list">"Trakt API Doc - Lists: List Like"</a> for more information.
/// </para>
/// </summary>
/// <param name="traktListId">The Trakt ID of the list, which will be unliked.</param>
/// <param name="cancellationToken">
/// Propagates notification that the request should be canceled.<para/>
/// If provided, the exception <see cref="OperationCanceledException" /> should be catched.
/// </param>
/// <exception cref="TraktException">Thrown, if the request fails.</exception>
/// <exception cref="TraktRequestValidationException">Thrown, if validation of request data fails.</exception>
/// <exception cref="ArgumentException">Thrown, if the given <paramref name="traktListId"/> is 0.</exception>
public Task<TraktNoContentResponse> UnlikeListAsync(uint traktListId, CancellationToken cancellationToken = default)
{
if (traktListId == 0)
throw new ArgumentException("list id must not be 0", nameof(traktListId));

var requestHandler = new RequestHandler(Client);

return requestHandler.ExecuteNoContentRequestAsync(new ListUnlikeRequest
{
Id = traktListId.ToString()
},
cancellationToken);
}

/// <summary>
/// Unlike a list.
/// <para>OAuth authorization required.</para>
/// <para>
/// See <a href="https://trakt.docs.apiary.io/#reference/lists/list-like/like-a-list">"Trakt API Doc - Lists: List Like"</a> for more information.
/// </para>
/// </summary>
/// <param name="listIds">The ids of the list, which will be unliked.</param>
/// <param name="cancellationToken">
/// Propagates notification that the request should be canceled.<para/>
/// If provided, the exception <see cref="OperationCanceledException" /> should be catched.
/// </param>
/// <exception cref="TraktException">Thrown, if the request fails.</exception>
/// <exception cref="TraktRequestValidationException">Thrown, if validation of request data fails.</exception>
public Task<TraktNoContentResponse> UnlikeListAsync(ITraktListIds listIds, CancellationToken cancellationToken = default)
{
if (listIds == null)
throw new ArgumentNullException(nameof(listIds));

var requestHandler = new RequestHandler(Client);

return requestHandler.ExecuteNoContentRequestAsync(new ListUnlikeRequest
{
Id = listIds.GetBestId()
},
cancellationToken);
}

/// <summary>
/// Gets top level comments for a list.
/// <para>
Expand Down
32 changes: 32 additions & 0 deletions Source/Lib/Trakt.NET/Requests/Lists/ListUnlikeRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
namespace TraktNet.Requests.Lists
{
using Base;
using Exceptions;
using Extensions;
using Interfaces;
using System.Collections.Generic;

internal sealed class ListUnlikeRequest : ADeleteRequest, IHasId
{
public string Id { get; set; }

public RequestObjectType RequestObjectType => RequestObjectType.Lists;

public override string UriTemplate => "lists/{id}/like";

public override IDictionary<string, object> GetUriPathParameters()
=> new Dictionary<string, object>
{
["id"] = Id
};

public override void Validate()
{
if (Id == null)
throw new TraktRequestValidationException(nameof(Id), "list id must not be null");

if (Id == string.Empty || Id.ContainsSpace())
throw new TraktRequestValidationException(nameof(Id), "list id not valid");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -124,12 +124,15 @@ public async Task Test_TraktListsModule_LikeList_Throws_API_Exception(HttpStatus
}

[Fact]
public async Task Test_TraktListsModule_LikeList_Throws_ArgumentNullException()
public async Task Test_TraktListsModule_LikeList_Throws_ArgumentExceptions()
{
TraktClient client = TestUtility.GetOAuthMockClient(LIKE_LIST_URI, HttpStatusCode.NoContent);

Func<Task<TraktNoContentResponse>> act = () => client.Lists.LikeListAsync(default(ITraktListIds));
await act.Should().ThrowAsync<ArgumentNullException>();

act = () => client.Lists.LikeListAsync(0);
await act.Should().ThrowAsync<ArgumentException>();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
namespace TraktNet.Modules.Tests.TraktListsModule
{
using FluentAssertions;
using System;
using System.Net;
using System.Threading.Tasks;
using Trakt.NET.Tests.Utility;
using Trakt.NET.Tests.Utility.Traits;
using TraktNet.Exceptions;
using TraktNet.Objects.Get.Lists;
using TraktNet.Responses;
using Xunit;

[TestCategory("Modules.Lists")]
public partial class TraktListsModule_Tests
{
private readonly string UNLIKE_LIST_URI = $"lists/{LIST_ID}/like";

[Fact]
public async Task Test_TraktListsModule_UnlikeList()
{
TraktClient client = TestUtility.GetOAuthMockClient(UNLIKE_LIST_URI, HttpStatusCode.NoContent);
TraktNoContentResponse response = await client.Lists.UnlikeListAsync(LIST_ID);

response.Should().NotBeNull();
response.IsSuccess.Should().BeTrue();
}

[Fact]
public async Task Test_TraktListsModule_UnlikeList_With_TraktID()
{
const uint traktID = 55;

TraktClient client = TestUtility.GetOAuthMockClient($"lists/{traktID}/like", HttpStatusCode.NoContent);
TraktNoContentResponse response = await client.Lists.UnlikeListAsync(traktID);

response.Should().NotBeNull();
response.IsSuccess.Should().BeTrue();
}

[Fact]
public async Task Test_TraktListsModule_UnlikeList_With_ListIds_TraktID()
{
const uint traktID = 55;

var listIds = new TraktListIds
{
Trakt = traktID
};

TraktClient client = TestUtility.GetOAuthMockClient($"lists/{traktID}/like", HttpStatusCode.NoContent);
TraktNoContentResponse response = await client.Lists.UnlikeListAsync(listIds);

response.Should().NotBeNull();
response.IsSuccess.Should().BeTrue();
}

[Fact]
public async Task Test_TraktListsModule_UnlikeList_With_ListIds_Slug()
{
const string listSlug = "incredible-thoughts";

var listIds = new TraktListIds
{
Slug = listSlug
};

TraktClient client = TestUtility.GetOAuthMockClient($"lists/{listSlug}/like", HttpStatusCode.NoContent);
TraktNoContentResponse response = await client.Lists.UnlikeListAsync(listIds);

response.Should().NotBeNull();
response.IsSuccess.Should().BeTrue();
}

[Fact]
public async Task Test_TraktListsModule_UnlikeList_With_ListIds()
{
const uint traktID = 55;
const string listSlug = "incredible-thoughts";

var listIds = new TraktListIds
{
Trakt = traktID,
Slug = listSlug
};

TraktClient client = TestUtility.GetOAuthMockClient($"lists/{traktID}/like", HttpStatusCode.NoContent);
TraktNoContentResponse response = await client.Lists.UnlikeListAsync(listIds);

response.Should().NotBeNull();
response.IsSuccess.Should().BeTrue();
}

[Theory]
[InlineData(HttpStatusCode.NotFound, typeof(TraktListNotFoundException))]
[InlineData(HttpStatusCode.Unauthorized, typeof(TraktAuthorizationException))]
[InlineData(HttpStatusCode.BadRequest, typeof(TraktBadRequestException))]
[InlineData(HttpStatusCode.Forbidden, typeof(TraktForbiddenException))]
[InlineData(HttpStatusCode.MethodNotAllowed, typeof(TraktMethodNotFoundException))]
[InlineData(HttpStatusCode.Conflict, typeof(TraktConflictException))]
[InlineData(HttpStatusCode.InternalServerError, typeof(TraktServerException))]
[InlineData(HttpStatusCode.BadGateway, typeof(TraktBadGatewayException))]
[InlineData(HttpStatusCode.PreconditionFailed, typeof(TraktPreconditionFailedException))]
[InlineData(HttpStatusCode.UnprocessableEntity, typeof(TraktValidationException))]
[InlineData(HttpStatusCode.TooManyRequests, typeof(TraktRateLimitException))]
[InlineData(HttpStatusCode.ServiceUnavailable, typeof(TraktServerUnavailableException))]
[InlineData(HttpStatusCode.GatewayTimeout, typeof(TraktServerUnavailableException))]
[InlineData((HttpStatusCode)520, typeof(TraktServerUnavailableException))]
[InlineData((HttpStatusCode)521, typeof(TraktServerUnavailableException))]
[InlineData((HttpStatusCode)522, typeof(TraktServerUnavailableException))]
public async Task Test_TraktListsModule_UnlikeList_Throws_API_Exception(HttpStatusCode statusCode, Type exceptionType)
{
TraktClient client = TestUtility.GetOAuthMockClient(UNLIKE_LIST_URI, statusCode);

try
{
await client.Lists.UnlikeListAsync(LIST_ID);
Assert.False(true);
}
catch (Exception exception)
{
(exception.GetType() == exceptionType).Should().BeTrue();
}
}

[Fact]
public async Task Test_TraktListsModule_UnlikeList_Throws_ArgumentExceptions()
{
TraktClient client = TestUtility.GetOAuthMockClient(UNLIKE_LIST_URI, HttpStatusCode.NoContent);

Func<Task<TraktNoContentResponse>> act = () => client.Lists.UnlikeListAsync(default(ITraktListIds));
await act.Should().ThrowAsync<ArgumentNullException>();

act = () => client.Lists.UnlikeListAsync(0);
await act.Should().ThrowAsync<ArgumentException>();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
namespace TraktNet.Requests.Tests.Lists
{
using FluentAssertions;
using System;
using System.Collections.Generic;
using Trakt.NET.Tests.Utility.Traits;
using TraktNet.Exceptions;
using TraktNet.Requests.Base;
using TraktNet.Requests.Lists;
using Xunit;

[TestCategory("Requests.Lists")]
public class ListUnlikeRequest_Tests
{
[Fact]
public void Test_ListUnlikeRequest_Has_AuthorizationRequirement_Required()
{
var request = new ListUnlikeRequest();
request.AuthorizationRequirement.Should().Be(AuthorizationRequirement.Required);
}

[Fact]
public void Test_ListUnlikeRequest_Returns_Valid_RequestObjectType()
{
var requestMock = new ListUnlikeRequest();
requestMock.RequestObjectType.Should().Be(RequestObjectType.Lists);
}

[Fact]
public void Test_ListUnlikeRequest_Has_Valid_UriTemplate()
{
var request = new ListUnlikeRequest();
request.UriTemplate.Should().Be("lists/{id}/like");
}

[Fact]
public void Test_ListUnlikeRequest_Returns_Valid_UriPathParameters()
{
var request = new ListUnlikeRequest { Id = "123" };

request.GetUriPathParameters().Should().NotBeNull()
.And.HaveCount(1)
.And.Contain(new Dictionary<string, object>
{
["id"] = "123"
});
}

[Fact]
public void Test_ListUnlikeRequest_Validate_Throws_Exceptions()
{
// id is null
var request = new ListUnlikeRequest();

Action act = () => request.Validate();
act.Should().Throw<TraktRequestValidationException>();

// empty id
request = new ListUnlikeRequest { Id = string.Empty };

act = () => request.Validate();
act.Should().Throw<TraktRequestValidationException>();

// id with spaces
request = new ListUnlikeRequest { Id = "invalid id" };

act = () => request.Validate();
act.Should().Throw<TraktRequestValidationException>();
}
}
}

0 comments on commit fb410d8

Please sign in to comment.