From bb14419065383e65e18fe06126710fa52918591b Mon Sep 17 00:00:00 2001 From: kindler chase Date: Wed, 17 Aug 2022 10:36:51 -0500 Subject: [PATCH] Shore up integration Tests. Resolve minor issue w/ Discover and add more options. (#48) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Resolves #38 * Update code formatting from PR #37 / Issue #36 * Url Encode the API requests. Should have done this forever ago. Resolves #39 * Update Lucasfilm integration tests to use revised API values. * Resolves #40 * Also fixed error for AssemblyInit class decorated with a [TestClass] attribute, but declared as an internal class. Apparently only public classes are supposed to be decorated with the TestClassAttribute. Who knew 🤷‍♀️ * Cleaning up ApiRequestBase * use updated syntax for using statements * refactor Func into a local method * add error message when response uri is null * Update integration tests to use file scoped namespaces / global usings. * Battle of Lucasfile continues; guess it's back to Ltd. * Update MovieApi project to use file scoped namespaces / global usings. * Add assert structure for known types in AssertCanPageSearchResponse. * Simplify condition for AssertCanPageSearchResponse. * Simplify AssertCanPageSearchResponse w/ more straight forward page num. * Consistent formatting. * Add can page test for recommendations. * Use the util for paging test with get similar (already does the same) * Set the known page size as a constant. * Increase the tolerance for duplicate results in a search query test. * Clean up/simplify some of the tests validating type/interfaces. * Remove discovery builder interface; improve discovery; add more methods. Remove discovery builder interface; improve discovery; add more methods. * No need for the IDiscoverMovieParameterBuilder - should be a concrete type as no other implementations should be allowed in the api. * fix discovery parameter for original language (was wrong name) * add a few more discovery builder options * shore up the discovery tests w/ more data/tests. --- .../ApiResponse/ApiResponseTests.cs | 189 +++--- .../ApiResponse/TmdbStatusCodeTests.cs | 279 ++++----- .../ApiResponseUtil.cs | 560 ++++++++--------- DM.MovieApi.IntegrationTests/AssemblyInit.cs | 94 ++- DM.MovieApi.IntegrationTests/GlobalUsings.cs | 20 + .../Infrastructure/IntegrationApiRequest.cs | 19 +- .../IntegrationMovieDbSettings.cs | 101 ++- .../ApiMovieRatingRequestTests.cs | 81 ++- .../Companies/ApiCompanyRequestTests.cs | 107 ++-- .../ApiConfigurationRequestTests.cs | 95 ++- .../Discover/ApiDiscoverRequestTests.cs | 202 +++--- .../MovieDb/Genres/ApiGenreRequestTests.cs | 227 ++++--- .../MovieDb/Movies/ApiMovieRequestTests.cs | 554 ++++++++-------- .../Movies/ApiMovieRequestTests_GetCredits.cs | 125 ++-- ...ApiMovieRequestTests_GetRecommendations.cs | 68 +- .../Movies/ApiMovieRequestTests_GetSimilar.cs | 121 ++-- .../MovieDb/People/ApiPeopleRequestTests.cs | 591 +++++++++--------- .../Professions/ApiProfessionRequestTests.cs | 59 +- .../MovieDb/TV/ApiTVShowRequestTests.cs | 455 +++++++------- .../MovieDbApiTests.cs | 107 ++-- .../MovieDbFactoryTests.cs | 381 ++++++----- DM.MovieApi/ApiRequest/ApiRequestBase.cs | 234 ++++--- DM.MovieApi/ApiRequest/IApiRequest.cs | 15 +- .../ApiRequest/IsoDateTimeConverterEx.cs | 74 ++- DM.MovieApi/ApiResponse/ApiError.cs | 42 +- DM.MovieApi/ApiResponse/ApiQueryResponse.cs | 23 +- DM.MovieApi/ApiResponse/ApiResponseBase.cs | 37 +- DM.MovieApi/ApiResponse/ApiSearchResponse.cs | 63 +- DM.MovieApi/ApiResponse/TmdbStatusCode.cs | 423 +++++++------ DM.MovieApi/GlobalUsings.cs | 12 + DM.MovieApi/IApiSettings.cs | 11 +- DM.MovieApi/IMovieDbApi.cs | 85 ++- .../Certifications/ApiMovieRatingRequest.cs | 74 +-- .../Certifications/IApiMovieRatingRequest.cs | 19 +- .../MovieDb/Certifications/MovieRatings.cs | 87 ++- .../MovieDb/Collections/CollectionInfo.cs | 37 +- .../MovieDb/Companies/ApiCompanyRequest.cs | 67 +- .../MovieDb/Companies/IApiCompanyRequest.cs | 38 +- .../MovieDb/Companies/ParentCompany.cs | 33 +- .../MovieDb/Companies/ProductionCompany.cs | 41 +- .../Companies/ProductionCompanyInfo.cs | 78 ++- .../MovieDb/Configuration/ApiConfiguration.cs | 30 +- .../Configuration/ApiConfigurationRequest.cs | 26 +- .../Configuration/IApiConfigurationRequest.cs | 27 +- .../Configuration/ImageConfiguration.cs | 50 +- DM.MovieApi/MovieDb/Country.cs | 78 ++- .../MovieDb/Discover/ApiDiscoverRequest.cs | 53 +- DM.MovieApi/MovieDb/Discover/DiscoverEnums.cs | 45 ++ .../Discover/DiscoverMovieParameterBuilder.cs | 209 +++++-- .../MovieDb/Discover/IApiDiscoverRequest.cs | 26 +- .../IDiscoverMovieParameterBuilder.cs | 38 -- DM.MovieApi/MovieDb/Genres/ApiGenreRequest.cs | 210 +++---- DM.MovieApi/MovieDb/Genres/Genre.cs | 70 +-- DM.MovieApi/MovieDb/Genres/GenreFactory.cs | 150 +++-- .../GenreIdCollectionMappingExtensions.cs | 75 ++- .../MovieDb/Genres/IApiGenreRequest.cs | 101 ++- .../ApiProfessionRequest.cs | 50 +- .../IApiProfessionRequest.cs | 20 +- .../MovieDb/IndustryProfessions/Profession.cs | 22 +- DM.MovieApi/MovieDb/Keywords/Keyword.cs | 84 ++- .../MovieDb/Keywords/KeywordConverter.cs | 61 +- DM.MovieApi/MovieDb/Language.cs | 78 ++- DM.MovieApi/MovieDb/Movies/ApiMovieRequest.cs | 262 ++++---- .../MovieDb/Movies/IApiMovieRequest.cs | 137 ++-- DM.MovieApi/MovieDb/Movies/Movie.cs | 147 +++-- DM.MovieApi/MovieDb/Movies/MovieCredit.cs | 98 ++- DM.MovieApi/MovieDb/Movies/MovieInfo.cs | 82 ++- .../MovieDb/People/ApiPeopleRequest.cs | 112 ++-- DM.MovieApi/MovieDb/People/Gender.cs | 13 +- .../MovieDb/People/IApiPeopleRequest.cs | 61 +- DM.MovieApi/MovieDb/People/Person.cs | 75 ++- DM.MovieApi/MovieDb/People/PersonInfo.cs | 252 ++++---- .../MovieDb/People/PersonMovieCredit.cs | 115 ++-- DM.MovieApi/MovieDb/People/PersonTVCredit.cs | 115 ++-- DM.MovieApi/MovieDb/TV/ApiTVShowRequest.cs | 170 +++-- DM.MovieApi/MovieDb/TV/Crew.cs | 57 +- DM.MovieApi/MovieDb/TV/Episode.cs | 73 +-- DM.MovieApi/MovieDb/TV/GuestStars.cs | 57 +- DM.MovieApi/MovieDb/TV/IApiTVShowRequest.cs | 85 ++- DM.MovieApi/MovieDb/TV/Network.cs | 70 +-- DM.MovieApi/MovieDb/TV/Season.cs | 50 +- DM.MovieApi/MovieDb/TV/SeasonInfo.cs | 51 +- DM.MovieApi/MovieDb/TV/TVShow.cs | 137 ++-- DM.MovieApi/MovieDb/TV/TVShowCreator.cs | 76 ++- DM.MovieApi/MovieDb/TV/TVShowInfo.cs | 85 ++- DM.MovieApi/MovieDbFactory.cs | 287 +++++---- DM.MovieApi/Shims/CollectionExtensions.cs | 14 +- DM.MovieApi/Shims/EnumExtensions.cs | 17 + .../Shims/ImportingConstructorAttribute.cs | 11 +- 89 files changed, 4936 insertions(+), 5204 deletions(-) create mode 100644 DM.MovieApi.IntegrationTests/GlobalUsings.cs create mode 100644 DM.MovieApi/GlobalUsings.cs create mode 100644 DM.MovieApi/MovieDb/Discover/DiscoverEnums.cs delete mode 100644 DM.MovieApi/MovieDb/Discover/IDiscoverMovieParameterBuilder.cs create mode 100644 DM.MovieApi/Shims/EnumExtensions.cs diff --git a/DM.MovieApi.IntegrationTests/ApiResponse/ApiResponseTests.cs b/DM.MovieApi.IntegrationTests/ApiResponse/ApiResponseTests.cs index be1e4d0..f3fea59 100644 --- a/DM.MovieApi.IntegrationTests/ApiResponse/ApiResponseTests.cs +++ b/DM.MovieApi.IntegrationTests/ApiResponse/ApiResponseTests.cs @@ -1,139 +1,130 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using DM.MovieApi.ApiResponse; -using DM.MovieApi.IntegrationTests.Infrastructure; -using DM.MovieApi.MovieDb.Configuration; -using DM.MovieApi.MovieDb.Movies; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace DM.MovieApi.IntegrationTests.ApiResponse +namespace DM.MovieApi.IntegrationTests.ApiResponse; + +[TestClass] +public class ApiResponseBaseTests { - [TestClass] - public class ApiResponseBaseTests + private IntegrationApiRequest _api; + + [TestInitialize] + public void TestInit() { - private IntegrationApiRequest _api; + ApiResponseUtil.ThrottleTests(); + _api = new IntegrationApiRequest( AssemblyInit.Settings ); + } - [TestInitialize] - public void TestInit() - { - ApiResponseUtil.ThrottleTests(); - _api = new IntegrationApiRequest( AssemblyInit.Settings ); - } + [TestMethod] + public async Task ApiQueryResponse_Includes_CommandText() + { + const string command = "configuration"; - [TestMethod] - public async Task ApiQueryResponse_Includes_CommandText() - { - const string command = "configuration"; + ApiQueryResponse response = await _api.QueryAsync( command ); - ApiQueryResponse response = await _api.QueryAsync( command ); + ApiResponseUtil.AssertErrorIsNull( response ); - ApiResponseUtil.AssertErrorIsNull( response ); + string actualCommandText = $"Actual: {response.CommandText}"; - string actualCommandText = $"Actual: {response.CommandText}"; + Assert.IsTrue( response.CommandText.Contains( command ), actualCommandText ); + Assert.IsTrue( response.CommandText.Contains( AssemblyInit.Settings.ApiUrl ), actualCommandText ); + } - Assert.IsTrue( response.CommandText.Contains( command ), actualCommandText ); - Assert.IsTrue( response.CommandText.Contains( AssemblyInit.Settings.ApiUrl ), actualCommandText ); - } + [TestMethod] + public async Task ApiQueryResponse_Includes_CommandText_OnError() + { + const string command = "invalid/request"; - [TestMethod] - public async Task ApiQueryResponse_Includes_CommandText_OnError() - { - const string command = "invalid/request"; + ApiQueryResponse response = await _api.QueryAsync( command ); - ApiQueryResponse response = await _api.QueryAsync( command ); + Assert.IsNotNull( response.Error ); - Assert.IsNotNull( response.Error ); + string actualCommandText = $"Actual: {response.CommandText}"; - string actualCommandText = $"Actual: {response.CommandText}"; + Assert.IsTrue( response.CommandText.Contains( command ), actualCommandText ); + Assert.IsTrue( response.CommandText.Contains( AssemblyInit.Settings.ApiUrl ), actualCommandText ); + } - Assert.IsTrue( response.CommandText.Contains( command ), actualCommandText ); - Assert.IsTrue( response.CommandText.Contains( AssemblyInit.Settings.ApiUrl ), actualCommandText ); - } + [TestMethod] + public async Task ApiSearchAsyncResponse_Includes_CommandText() + { + const string command = "search/movie"; - [TestMethod] - public async Task ApiSearchAsyncResponse_Includes_CommandText() + var param = new Dictionary { - const string command = "search/movie"; + {"query", "Run Lola Run"}, + }; - var param = new Dictionary - { - {"query", "Run Lola Run"}, - }; + ApiSearchResponse response = await _api.SearchAsync( command, param ); - ApiSearchResponse response = await _api.SearchAsync( command, param ); + ApiResponseUtil.AssertErrorIsNull( response ); - ApiResponseUtil.AssertErrorIsNull( response ); + AssertResponseIncludesCommandText( response, command ); + } - AssertResponseIncludesCommandText( response, command ); - } + [TestMethod] + public async Task ApiSearchAsyncResponse_Includes_CommandText_OnError() + { + const string command = "search/invalid"; - [TestMethod] - public async Task ApiSearchAsyncResponse_Includes_CommandText_OnError() + var param = new Dictionary { - const string command = "search/invalid"; + {"query", "Run Lola Run"}, + }; - var param = new Dictionary - { - {"query", "Run Lola Run"}, - }; + ApiSearchResponse response = await _api.SearchAsync( command, param ); - ApiSearchResponse response = await _api.SearchAsync( command, param ); + Assert.IsNotNull( response.Error ); - Assert.IsNotNull( response.Error ); + AssertResponseIncludesCommandText( response, command ); + } - AssertResponseIncludesCommandText( response, command ); - } + [TestMethod] + public async Task ApiSearchAsyncResponse_Includes_Json() + { + const string command = "search/movie"; - [TestMethod] - public async Task ApiSearchAsyncResponse_Includes_Json() + var param = new Dictionary { - const string command = "search/movie"; - - var param = new Dictionary - { - {"query", "Run Lola Run"}, - }; + {"query", "Run Lola Run"}, + }; - ApiSearchResponse response = await _api.SearchAsync( command, param ); + ApiSearchResponse response = await _api.SearchAsync( command, param ); - ApiResponseUtil.AssertErrorIsNull( response ); + ApiResponseUtil.AssertErrorIsNull( response ); - AssertResponseIncludesJson( response ); - } + AssertResponseIncludesJson( response ); + } - [TestMethod] - public async Task ApiQueryAsyncResponse_Includes_Json() - { - // Run Lola Run MovieId=104 - const string command = "movie/104"; + [TestMethod] + public async Task ApiQueryAsyncResponse_Includes_Json() + { + // Run Lola Run MovieId=104 + const string command = "movie/104"; - ApiQueryResponse response = await _api.QueryAsync( command ); + ApiQueryResponse response = await _api.QueryAsync( command ); - AssertResponseIncludesJson( response ); - } + AssertResponseIncludesJson( response ); + } - // ReSharper disable once UnusedParameter.Local - private void AssertResponseIncludesCommandText( ApiResponseBase response, string command ) - { - string actualCommandText = $"Actual: {response.CommandText}"; + // ReSharper disable once UnusedParameter.Local + private void AssertResponseIncludesCommandText( ApiResponseBase response, string command ) + { + string actualCommandText = $"Actual: {response.CommandText}"; - Assert.IsTrue( response.CommandText.Contains( command ), actualCommandText ); - Assert.IsTrue( response.CommandText.Contains( "&page=" ), actualCommandText ); - Assert.IsTrue( response.CommandText.Contains( "?query=Run+Lola+Run" ), actualCommandText ); - Assert.IsTrue( response.CommandText.Contains( AssemblyInit.Settings.ApiUrl ), actualCommandText ); - } + Assert.IsTrue( response.CommandText.Contains( command ), actualCommandText ); + Assert.IsTrue( response.CommandText.Contains( "&page=" ), actualCommandText ); + Assert.IsTrue( response.CommandText.Contains( "?query=Run+Lola+Run" ), actualCommandText ); + Assert.IsTrue( response.CommandText.Contains( AssemblyInit.Settings.ApiUrl ), actualCommandText ); + } - private void AssertResponseIncludesJson( ApiResponseBase response ) - { - string actualJson = $"Actual: {response.Json}"; + private void AssertResponseIncludesJson( ApiResponseBase response ) + { + string actualJson = $"Actual: {response.Json}"; - ApiResponseUtil.AssertErrorIsNull( response ); + ApiResponseUtil.AssertErrorIsNull( response ); - Assert.IsTrue( response.Json.Contains( "\"release_date\":\"1998-03-03\"" ), actualJson ); - Assert.IsTrue( response.Json.Contains( "\"original_title\":\"Lola rennt\"" ), actualJson ); - Assert.IsTrue( response.Json.Contains( "\"title\":\"Run Lola Run\"" ), actualJson ); - Assert.IsTrue( response.Json.Contains( "\"original_language\":\"de\"" ), actualJson ); - Assert.IsTrue( response.Json.Contains( "\"id\":104" ), actualJson ); - } + Assert.IsTrue( response.Json.Contains( "\"release_date\":\"1998-03-03\"" ), actualJson ); + Assert.IsTrue( response.Json.Contains( "\"original_title\":\"Lola rennt\"" ), actualJson ); + Assert.IsTrue( response.Json.Contains( "\"title\":\"Run Lola Run\"" ), actualJson ); + Assert.IsTrue( response.Json.Contains( "\"original_language\":\"de\"" ), actualJson ); + Assert.IsTrue( response.Json.Contains( "\"id\":104" ), actualJson ); } } diff --git a/DM.MovieApi.IntegrationTests/ApiResponse/TmdbStatusCodeTests.cs b/DM.MovieApi.IntegrationTests/ApiResponse/TmdbStatusCodeTests.cs index 508312c..9044c5d 100644 --- a/DM.MovieApi.IntegrationTests/ApiResponse/TmdbStatusCodeTests.cs +++ b/DM.MovieApi.IntegrationTests/ApiResponse/TmdbStatusCodeTests.cs @@ -1,198 +1,189 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using DM.MovieApi.ApiResponse; -using DM.MovieApi.IntegrationTests.Infrastructure; -using DM.MovieApi.MovieDb.Genres; -using DM.MovieApi.MovieDb.Movies; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace DM.MovieApi.IntegrationTests.ApiResponse +namespace DM.MovieApi.IntegrationTests.ApiResponse; + +[TestClass] +public class TmdbStatusCodeTests { - [TestClass] - public class TmdbStatusCodeTests + /// + /// is a private class to TmdbStatusCodeTests. Uses all the same plumbing + /// that every other IApiRequest uses to query themoviedb.org api. + /// + private IntegrationApiRequest _api; + + [TestInitialize] + public void TestInit() { - /// - /// is a private class to TmdbStatusCodeTests. Uses all the same plumbing - /// that every other IApiRequest uses to query themoviedb.org api. - /// - private IntegrationApiRequest _api; - - [TestInitialize] - public void TestInit() - { - ApiResponseUtil.ThrottleTests(); - _api = new IntegrationApiRequest( AssemblyInit.Settings ); - } + ApiResponseUtil.ThrottleTests(); + _api = new IntegrationApiRequest( AssemblyInit.Settings ); + } - [TestMethod] - public async Task InvalidBearerToken() - { - var api = new IntegrationApiRequest( new IntegrationMovieDbSettings( "crappy-bearer-token" ) ); + [TestMethod] + public async Task InvalidBearerToken() + { + var api = new IntegrationApiRequest( new IntegrationMovieDbSettings( "crappy-bearer-token" ) ); - ApiQueryResponse result = await api.QueryAsync( "invalid/service" ); + ApiQueryResponse result = await api.QueryAsync( "invalid/service" ); - AssertErrorCode( result, TmdbStatusCode.InvalidApiKey ); - } + AssertErrorCode( result, TmdbStatusCode.InvalidApiKey ); + } - [TestMethod] - public async Task ResourceNotFound() - { - ApiQueryResponse result = await _api.QueryAsync( "invalid/service" ); + [TestMethod] + public async Task ResourceNotFound() + { + ApiQueryResponse result = await _api.QueryAsync( "invalid/service" ); - AssertErrorCode( result, TmdbStatusCode.ResourceNotFound ); - } + AssertErrorCode( result, TmdbStatusCode.ResourceNotFound ); + } - [TestMethod] - public async Task ResourceNotFound_MovieById() - { - const int validId = 104; - const int invalidId = 0; + [TestMethod] + public async Task ResourceNotFound_MovieById() + { + const int validId = 104; + const int invalidId = 0; - // Step 1: Ensure there is a valid result + // Step 1: Ensure there is a valid result - ApiQueryResponse validResult = await _api.QueryAsync( $"movie/{validId}" ); - Assert.IsNull( validResult.Error ); - Assert.AreEqual( validId, validResult.Item.Id ); - Assert.AreEqual( "Run Lola Run", validResult.Item.Title ); + ApiQueryResponse validResult = await _api.QueryAsync( $"movie/{validId}" ); + Assert.IsNull( validResult.Error ); + Assert.AreEqual( validId, validResult.Item.Id ); + Assert.AreEqual( "Run Lola Run", validResult.Item.Title ); - // Step 2: Ensure there is an invalid result + // Step 2: Ensure there is an invalid result - ApiQueryResponse invalidResult = await _api.QueryAsync( $"movie/{invalidId}" ); - AssertErrorCode( invalidResult, TmdbStatusCode.ResourceNotFound ); - } + ApiQueryResponse invalidResult = await _api.QueryAsync( $"movie/{invalidId}" ); + AssertErrorCode( invalidResult, TmdbStatusCode.ResourceNotFound ); + } + + [Ignore] + [TestMethod] + public async Task InvalidPage_LessThanOne_SearchMovie() + { + // TODO: (K. Chase) [2016-04-04] InvalidPage status codes are expected to fail for now; see AssertInvalidPage. - [Ignore] - [TestMethod] - public async Task InvalidPage_LessThanOne_SearchMovie() + const string invalidPage = "0"; + + var param = new Dictionary { - // TODO: (K. Chase) [2016-04-04] InvalidPage status codes are expected to fail for now; see AssertInvalidPage. + {"query", "Run Lola Run"}, + {"page", invalidPage}, + }; - const string invalidPage = "0"; + ApiSearchResponse result = await _api.SearchAsync( "search/movie", param ); - var param = new Dictionary - { - {"query", "Run Lola Run"}, - {"page", invalidPage}, - }; + AssertInvalidPage( result ); + } - ApiSearchResponse result = await _api.SearchAsync( "search/movie", param ); + [Ignore] + [TestMethod] + public async Task InvalidPage_GreaterThanOneThousand_SearchMovie() + { + // TODO: (K. Chase) [2016-04-04] InvalidPage status codes are expected to fail for now; see AssertInvalidPage. - AssertInvalidPage( result ); - } + const string invalidPage = "1001"; - [Ignore] - [TestMethod] - public async Task InvalidPage_GreaterThanOneThousand_SearchMovie() + var param = new Dictionary { - // TODO: (K. Chase) [2016-04-04] InvalidPage status codes are expected to fail for now; see AssertInvalidPage. + {"query", "Run Lola Run"}, + {"page", invalidPage}, + }; - const string invalidPage = "1001"; + ApiSearchResponse result = await _api.SearchAsync( "search/movie", param ); - var param = new Dictionary - { - {"query", "Run Lola Run"}, - {"page", invalidPage}, - }; + AssertInvalidPage( result ); + } - ApiSearchResponse result = await _api.SearchAsync( "search/movie", param ); + [Ignore] + [TestMethod] + public async Task InvalidPage_PageNotAnInteger_SearchMovie() + { + // TODO: (K. Chase) [2016-04-04] InvalidPage status codes are expected to fail for now; see AssertInvalidPage. - AssertInvalidPage( result ); - } + const string invalidPage = "One"; - [Ignore] - [TestMethod] - public async Task InvalidPage_PageNotAnInteger_SearchMovie() + var param = new Dictionary { - // TODO: (K. Chase) [2016-04-04] InvalidPage status codes are expected to fail for now; see AssertInvalidPage. - - const string invalidPage = "One"; + {"query", "Run Lola Run"}, + {"page", invalidPage}, + }; - var param = new Dictionary - { - {"query", "Run Lola Run"}, - {"page", invalidPage}, - }; + ApiSearchResponse result = await _api.SearchAsync( "search/movie", param ); - ApiSearchResponse result = await _api.SearchAsync( "search/movie", param ); + AssertInvalidPage( result ); + } - AssertInvalidPage( result ); - } + [TestMethod] + public async Task InvalidPage_LessThanOne_MoviesByGenre() + { + const string invalidPage = "0"; - [TestMethod] - public async Task InvalidPage_LessThanOne_MoviesByGenre() + var param = new Dictionary { - const string invalidPage = "0"; + {"page", invalidPage}, + }; - var param = new Dictionary - { - {"page", invalidPage}, - }; + string command = $"genre/{GenreFactory.Comedy().Id}/movies"; - string command = $"genre/{GenreFactory.Comedy().Id}/movies"; + ApiSearchResponse result = await _api.SearchAsync( command, param ); - ApiSearchResponse result = await _api.SearchAsync( command, param ); + AssertInvalidPage( result ); + } - AssertInvalidPage( result ); - } + [TestMethod] + public async Task InvalidPage_GreaterThanOneThousand_MoviesByGenre() + { + const string invalidPage = "1001"; - [TestMethod] - public async Task InvalidPage_GreaterThanOneThousand_MoviesByGenre() + var param = new Dictionary { - const string invalidPage = "1001"; + {"page", invalidPage}, + }; - var param = new Dictionary - { - {"page", invalidPage}, - }; + string command = $"genre/{GenreFactory.Comedy().Id}/movies"; - string command = $"genre/{GenreFactory.Comedy().Id}/movies"; + ApiSearchResponse result = await _api.SearchAsync( command, param ); - ApiSearchResponse result = await _api.SearchAsync( command, param ); + AssertInvalidPage( result ); + } - AssertInvalidPage( result ); - } + [TestMethod] + public async Task InvalidPage_PageNotAnInteger_MoviesByGenre() + { + const string invalidPage = "One"; - [TestMethod] - public async Task InvalidPage_PageNotAnInteger_MoviesByGenre() + var param = new Dictionary { - const string invalidPage = "One"; + {"page", invalidPage}, + }; - var param = new Dictionary - { - {"page", invalidPage}, - }; + string command = $"genre/{GenreFactory.Comedy().Id}/movies"; - string command = $"genre/{GenreFactory.Comedy().Id}/movies"; + ApiSearchResponse result = await _api.SearchAsync( command, param ); - ApiSearchResponse result = await _api.SearchAsync( command, param ); + AssertInvalidPage( result ); + } - AssertInvalidPage( result ); - } + private void AssertErrorCode( ApiQueryResponse result, TmdbStatusCode expectedCode ) + { + Assert.IsNull( result.Item ); + Assert.IsNotNull( result.Error ); + Assert.AreEqual( expectedCode, result.Error.TmdbStatusCode, result.Error.ToString() ); + } - private void AssertErrorCode( ApiQueryResponse result, TmdbStatusCode expectedCode ) - { - Assert.IsNull( result.Item ); - Assert.IsNotNull( result.Error ); - Assert.AreEqual( expectedCode, result.Error.TmdbStatusCode, result.Error.ToString() ); - } + private void AssertInvalidPage( ApiSearchResponse result ) + { + const string note = + "InvalidPage status codes are expected to fail for now. TheMovieDb.org api is currently being updated. See: https://plus.google.com/u/0/+KindlerChase/posts/5CqrtakFTGS"; - private void AssertInvalidPage( ApiSearchResponse result ) + if( result.Error == null || result.Error.TmdbStatusCode == TmdbStatusCode.Unknown ) { - const string note = - "InvalidPage status codes are expected to fail for now. TheMovieDb.org api is currently being updated. See: https://plus.google.com/u/0/+KindlerChase/posts/5CqrtakFTGS"; - - if( result.Error == null || result.Error.TmdbStatusCode == TmdbStatusCode.Unknown ) - { - Assert.Fail( note ); - } - - Assert.IsNotNull( result.Error ); - Assert.AreEqual( TmdbStatusCode.InvalidPage, result.Error.TmdbStatusCode, result.Error.ToString() ); - Assert.IsNull( result.Results ); + Assert.Fail( note ); } - // ReSharper disable once ClassNeverInstantiated.Local - private class InvalidObject - { } + Assert.IsNotNull( result.Error ); + Assert.AreEqual( TmdbStatusCode.InvalidPage, result.Error.TmdbStatusCode, result.Error.ToString() ); + Assert.IsNull( result.Results ); } + + // ReSharper disable once ClassNeverInstantiated.Local + private class InvalidObject + { } } diff --git a/DM.MovieApi.IntegrationTests/ApiResponseUtil.cs b/DM.MovieApi.IntegrationTests/ApiResponseUtil.cs index c66863b..09302e0 100644 --- a/DM.MovieApi.IntegrationTests/ApiResponseUtil.cs +++ b/DM.MovieApi.IntegrationTests/ApiResponseUtil.cs @@ -1,357 +1,357 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using DM.MovieApi.ApiResponse; -using DM.MovieApi.MovieDb; -using DM.MovieApi.MovieDb.Companies; -using DM.MovieApi.MovieDb.Genres; -using DM.MovieApi.MovieDb.Movies; -using DM.MovieApi.MovieDb.People; -using DM.MovieApi.MovieDb.TV; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace DM.MovieApi.IntegrationTests +namespace DM.MovieApi.IntegrationTests; +internal static class ApiResponseUtil { - internal static class ApiResponseUtil + internal const int TestInitThrottle = 375; + internal const int PagingThrottle = 225; + private static readonly DateTime MinDate = new( 1900, 1, 1 ); + + /// + /// Slows down the starting of tests to keep themoviedb.org api from denying the request + /// due to too many requests. This should be placed in the [TestInitialize] method. + /// + public static void ThrottleTests() { - internal const int TestInitThrottle = 375; - internal const int PagingThrottle = 225; - private static readonly DateTime MinDate = new( 1900, 1, 1 ); - - /// - /// Slows down the starting of tests to keep themoviedb.org api from denying the request - /// due to too many requests. This should be placed in the [TestInitialize] method. - /// - public static void ThrottleTests() - { - System.Threading.Thread.Sleep( TestInitThrottle ); - } + System.Threading.Thread.Sleep( TestInitThrottle ); + } - public static void AssertErrorIsNull( ApiResponseBase response ) - { - Log( response.CommandText ); - Assert.IsNull( response.Error, response.Error?.ToString() ?? "Makes Compiler Happy" ); - } + public static void AssertErrorIsNull( ApiResponseBase response ) + { + Log( response.CommandText ); + Assert.IsNull( response.Error, response.Error?.ToString() ?? "Makes Compiler Happy" ); + } - public static void AssertImagePath( string path ) - { - if( path == null ) return; + public static void AssertImagePath( string path ) + { + if( path == null ) return; - Assert.IsTrue( path.StartsWith( "/" ), $"Actual: {path}" ); + Assert.IsTrue( path.StartsWith( "/" ), $"Actual: {path}" ); - Assert.IsTrue( - path.EndsWith( ".jpg" ) || path.EndsWith( ".png" ), - $"Actual: {path}" ); - } + Assert.IsTrue( + path.EndsWith( ".jpg" ) || path.EndsWith( ".png" ), + $"Actual: {path}" ); + } - public static void AssertNoSearchResults( ApiSearchResponse response ) - { - AssertErrorIsNull( response ); + public static void AssertNoSearchResults( ApiSearchResponse response ) + { + AssertErrorIsNull( response ); + + Assert.AreEqual( 0, response.Results.Count, $"Actual: {response}" ); + Assert.AreEqual( 1, response.PageNumber, $"Actual: {response}" ); + Assert.AreEqual( 0, response.TotalPages, $"Actual: {response}" ); + Assert.AreEqual( 0, response.TotalResults, $"Actual: {response}" ); + } - Assert.AreEqual( 0, response.Results.Count, $"Actual: {response}" ); - Assert.AreEqual( 1, response.PageNumber, $"Actual: {response}" ); - Assert.AreEqual( 0, response.TotalPages, $"Actual: {response}" ); - Assert.AreEqual( 0, response.TotalResults, $"Actual: {response}" ); + public static async Task AssertCanPageSearchResponse( + TSearch search, + int minimumPageCount, + Func>> apiSearch, + Func keySelector ) + { + if( minimumPageCount < 2 ) + { + Assert.Fail( "minimumPageCount must be greater than 1." ); } - public static async Task AssertCanPageSearchResponse( - TSearch search, - int minimumPageCount, - Func>> apiSearch, - Func keySelector ) + // the api defaults to a page size of 20. + const int apiPageSize = 20; + + var ids = new HashSet(); + var dups = new List(); + int totalResults = 0; + int pageNumber = 0; + + do { - if( minimumPageCount < 2 ) - { - Assert.Fail( "minimumPageCount must be greater than 1." ); - } + pageNumber++; - var ids = new HashSet(); - var dups = new List(); - int totalResults = 0; - int pageNumber = 1; + Log( $"search: {search} | page: {pageNumber}", "AssertCanPage" ); + ApiSearchResponse response = await apiSearch( search, pageNumber ); - do + AssertErrorIsNull( response ); + Assert.IsNotNull( response.Results ); + Assert.IsTrue( response.Results.Any() ); + + if( typeof( T ) == typeof( Movie ) ) + { + AssertMovieStructure( (IEnumerable)response.Results ); + } + else if( typeof( T ) == typeof( MovieInfo ) ) { - Log( $"search: {search} | page: {pageNumber}", "AssertCanPage" ); - ApiSearchResponse response = await apiSearch( search, pageNumber ); + AssertMovieInformationStructure( (IEnumerable)response.Results ); + } + else if( typeof( T ) == typeof( PersonInfo ) ) + { + AssertPersonInfoStructure( (IEnumerable)response.Results ); + } + else if( typeof( T ) == typeof( TVShowInfo ) ) + { + AssertTVShowInformationStructure( (IEnumerable)response.Results ); + } + else + { + Assert.Fail( $"Unsupported type: {typeof( T ).Name}" ); + } - AssertErrorIsNull( response ); - Assert.IsNotNull( response.Results ); - Assert.IsTrue( response.Results.Any() ); + totalResults += response.Results.Count; - if( typeof( T ) == typeof( Movie ) ) - { - AssertMovieStructure( (IEnumerable)response.Results ); - } - else if( typeof( T ) == typeof( PersonInfo ) ) - { - AssertPersonInfoStructure( (IEnumerable)response.Results ); - } - - if( keySelector == null ) - { - totalResults += response.Results.Count; - } - else + if( keySelector != null ) + { + foreach( T res in response.Results ) { - foreach( T res in response.Results ) + int key = keySelector( res ); + if( ids.Add( key ) == false ) { - totalResults++; - int key = keySelector( res ); - if( ids.Add( key ) == false ) - { - dups.Add( res.ToString() ); - } + dups.Add( res.ToString() ); } } + } - Assert.AreEqual( pageNumber, response.PageNumber ); + Assert.AreEqual( pageNumber, response.PageNumber ); - Assert.IsTrue( response.TotalPages >= minimumPageCount, - $"Expected minimum of {minimumPageCount} TotalPages. Actual TotalPages: {response.TotalPages}" ); + Assert.IsTrue( response.TotalPages >= minimumPageCount, + $"Expected minimum of {minimumPageCount} TotalPages. Actual TotalPages: {response.TotalPages}" ); - // keeps the system from being throttled - System.Threading.Thread.Sleep( PagingThrottle ); - } while( ++pageNumber <= minimumPageCount ); + // keeps the system from being throttled + System.Threading.Thread.Sleep( PagingThrottle ); + } while( pageNumber < minimumPageCount ); - // will be 1 greater than minimumPageCount in the last loop - Assert.AreEqual( minimumPageCount, --pageNumber ); + Assert.AreEqual( minimumPageCount, pageNumber ); - // 20 results per page - int minCount = pageNumber * 20; - Assert.IsTrue( totalResults >= minCount, - $"Actual results: {totalResults} | Expected min: {minCount}" ); + int minCount = pageNumber * apiPageSize; + Assert.IsTrue( totalResults >= minCount, + $"Actual results: {totalResults} | Expected min: {minCount}" ); - if( keySelector == null ) - { - // people searches will usually have dups since they return movie and tv roles - // in separate objects in the same result set. - return; - } + if( keySelector == null ) + { + // people searches will usually have dups since they return movie and tv roles + // in separate objects in the same result set. + return; + } - // api tends to return duplicate results when paging - // shouldn't be more than 2 or 3 per page; at 20 per page, - // that's approximately 4-6; let's target 20% - int min = (int)(totalResults * 0.8); - var d = dups - .GroupBy( x => x ) - .Select( x => $"{x.Key} (x {x.Count()})" ); - Log( $"Results: {totalResults}, Dups: {dups.Count}\r\n{string.Join( "\r\n", d )}" ); + // api tends to return duplicate results when paging + // shouldn't be more than 2 or 3 per page; at 20 per page, + // that's approximately 4-6; let's target 25% + int min = (int)(totalResults * 0.75); + var d = dups + .GroupBy( x => x ) + .Select( x => $"{x.Key} (x {x.Count()})" ); + Log( $"Results: {totalResults}, Dups: {dups.Count}\r\n{string.Join( "\r\n", d )}" ); - if( min >= ids.Count ) - { - Assert.Fail( "Every now and then themoviedb.org API returns a duplicate from a prior page. " + - "But this time it exceeded our tolerance of 20% dups.\r\n" + - $"Actual: {ids.Count} vs {min}" ); - } + if( min >= ids.Count ) + { + Assert.Fail( "Every now and then themoviedb.org API returns a duplicate from a prior page. " + + "But this time it exceeded our tolerance of 25% dups.\r\n" + + $"Actual: {ids.Count} vs {min}" ); } + } - private static void AssertPersonInfoStructure( IEnumerable people ) + private static void AssertPersonInfoStructure( IEnumerable people ) + { + // ReSharper disable PossibleMultipleEnumeration + Assert.IsTrue( people.Any() ); + + foreach( PersonInfo person in people ) { - // ReSharper disable PossibleMultipleEnumeration - Assert.IsTrue( people.Any() ); + Assert.IsTrue( person.Id > 0 ); + Assert.IsFalse( string.IsNullOrWhiteSpace( person.Name ) ); - foreach( PersonInfo person in people ) + foreach( PersonInfoRole role in person.KnownFor ) { - Assert.IsTrue( person.Id > 0 ); - Assert.IsFalse( string.IsNullOrWhiteSpace( person.Name ) ); - - foreach( PersonInfoRole role in person.KnownFor ) + // not asserting movie/tv dates as some valid dates will be null. + if( role.MediaType == MediaType.Movie ) { - // not asserting movie/tv dates as some valid dates will be null. - if( role.MediaType == MediaType.Movie ) - { - Assert.IsFalse( string.IsNullOrWhiteSpace( role.MovieTitle ) ); - Assert.IsFalse( string.IsNullOrWhiteSpace( role.MovieOriginalTitle ) ); - - Assert.IsNull( role.TVShowName ); - Assert.IsNull( role.TVShowOriginalName ); - Assert.AreEqual( DateTime.MinValue, role.TVShowFirstAirDate ); - } - else - { - Assert.IsFalse( string.IsNullOrWhiteSpace( role.TVShowName ) ); - Assert.IsFalse( string.IsNullOrWhiteSpace( role.TVShowOriginalName ) ); - - Assert.IsNull( role.MovieTitle ); - Assert.IsNull( role.MovieOriginalTitle ); - Assert.AreEqual( DateTime.MinValue, role.MovieReleaseDate ); - } + Assert.IsFalse( string.IsNullOrWhiteSpace( role.MovieTitle ) ); + Assert.IsFalse( string.IsNullOrWhiteSpace( role.MovieOriginalTitle ) ); - AssertGenres( role.GenreIds, role.Genres ); + Assert.IsNull( role.TVShowName ); + Assert.IsNull( role.TVShowOriginalName ); + Assert.AreEqual( DateTime.MinValue, role.TVShowFirstAirDate ); } - } - // ReSharper restore PossibleMultipleEnumeration - } + else + { + Assert.IsFalse( string.IsNullOrWhiteSpace( role.TVShowName ) ); + Assert.IsFalse( string.IsNullOrWhiteSpace( role.TVShowOriginalName ) ); - public static void AssertMovieStructure( IEnumerable movies ) - { - // ReSharper disable PossibleMultipleEnumeration - Assert.IsTrue( movies.Any() ); + Assert.IsNull( role.MovieTitle ); + Assert.IsNull( role.MovieOriginalTitle ); + Assert.AreEqual( DateTime.MinValue, role.MovieReleaseDate ); + } - foreach( Movie movie in movies ) - { - AssertMovieStructure( movie ); + AssertGenres( role.GenreIds, role.Genres ); } - // ReSharper restore PossibleMultipleEnumeration } + // ReSharper restore PossibleMultipleEnumeration + } - public static void AssertMovieStructure( Movie movie ) - { - Assert.IsTrue( movie.Id > 0 ); - Assert.IsFalse( string.IsNullOrWhiteSpace( movie.Title ) ); - - foreach( Genre genre in movie.Genres ) - { - Assert.IsTrue( genre.Id > 0 ); - Assert.IsFalse( string.IsNullOrWhiteSpace( genre.Name ) ); - } + public static void AssertMovieStructure( IEnumerable movies ) + { + // ReSharper disable PossibleMultipleEnumeration + Assert.IsTrue( movies.Any() ); - foreach( ProductionCompanyInfo info in movie.ProductionCompanies ) - { - Assert.IsTrue( info.Id > 0 ); - Assert.IsFalse( string.IsNullOrWhiteSpace( info.Name ) ); - } + foreach( Movie movie in movies ) + { + AssertMovieStructure( movie ); + } + // ReSharper restore PossibleMultipleEnumeration + } - foreach( Country country in movie.ProductionCountries ) - { - Assert.IsFalse( string.IsNullOrWhiteSpace( country.Iso3166Code ) ); - Assert.IsFalse( string.IsNullOrWhiteSpace( country.Name ) ); - } + public static void AssertMovieStructure( Movie movie ) + { + Assert.IsTrue( movie.Id > 0 ); + Assert.IsFalse( string.IsNullOrWhiteSpace( movie.Title ) ); - foreach( Language language in movie.SpokenLanguages ) - { - Assert.IsFalse( string.IsNullOrWhiteSpace( language.Iso639Code ) ); - Assert.IsFalse( string.IsNullOrWhiteSpace( language.Name ) ); - } + foreach( Genre genre in movie.Genres ) + { + Assert.IsTrue( genre.Id > 0 ); + Assert.IsFalse( string.IsNullOrWhiteSpace( genre.Name ) ); } - public static void AssertMovieInformationStructure( IEnumerable movies ) + foreach( ProductionCompanyInfo info in movie.ProductionCompanies ) { - // ReSharper disable once PossibleMultipleEnumeration - Assert.IsTrue( movies.Any() ); + Assert.IsTrue( info.Id > 0 ); + Assert.IsFalse( string.IsNullOrWhiteSpace( info.Name ) ); + } - // ReSharper disable once PossibleMultipleEnumeration - foreach( MovieInfo movie in movies ) - { - AssertMovieInformationStructure( movie ); - } + foreach( Country country in movie.ProductionCountries ) + { + Assert.IsFalse( string.IsNullOrWhiteSpace( country.Iso3166Code ) ); + Assert.IsFalse( string.IsNullOrWhiteSpace( country.Name ) ); } - public static void AssertMovieInformationStructure( MovieInfo movie ) + foreach( Language language in movie.SpokenLanguages ) { - Assert.IsFalse( string.IsNullOrWhiteSpace( movie.Title ), $"Actual: {movie}" ); - Assert.IsFalse( string.IsNullOrWhiteSpace( movie.OriginalTitle ), $"Actual {movie}" ); - // movie.Overview is sometimes empty + Assert.IsFalse( string.IsNullOrWhiteSpace( language.Iso639Code ) ); + Assert.IsFalse( string.IsNullOrWhiteSpace( language.Name ) ); + } + } - Assert.IsTrue( movie.Id > 1 ); - Assert.IsTrue( movie.ReleaseDate > MinDate, $"Actual: {movie.ReleaseDate} | {movie}" ); + public static void AssertMovieInformationStructure( IEnumerable movies ) + { + // ReSharper disable once PossibleMultipleEnumeration + Assert.IsTrue( movies.Any() ); - AssertImagePath( movie.BackdropPath ); - AssertImagePath( movie.PosterPath ); - AssertGenres( movie.GenreIds, movie.Genres ); + // ReSharper disable once PossibleMultipleEnumeration + foreach( MovieInfo movie in movies ) + { + AssertMovieInformationStructure( movie ); } + } - public static void AssertTVShowInformationStructure( IEnumerable tvShows ) - { - // ReSharper disable once PossibleMultipleEnumeration - Assert.IsTrue( tvShows.Any() ); + public static void AssertMovieInformationStructure( MovieInfo movie ) + { + Assert.IsFalse( string.IsNullOrWhiteSpace( movie.Title ), $"Actual: {movie}" ); + Assert.IsFalse( string.IsNullOrWhiteSpace( movie.OriginalTitle ), $"Actual {movie}" ); + // movie.Overview is sometimes empty - // ReSharper disable once PossibleMultipleEnumeration - foreach( TVShowInfo tvShow in tvShows ) - { - AssertTVShowInformationStructure( tvShow ); - } - } + Assert.IsTrue( movie.Id > 1 ); + Assert.IsTrue( movie.ReleaseDate > MinDate, $"Actual: {movie.ReleaseDate} | {movie}" ); - public static void AssertTVShowInformationStructure( TVShowInfo tvShow ) - { - Assert.IsTrue( tvShow.Id > 0 ); - Assert.IsFalse( string.IsNullOrEmpty( tvShow.Name ) ); + AssertImagePath( movie.BackdropPath ); + AssertImagePath( movie.PosterPath ); + AssertGenres( movie.GenreIds, movie.Genres ); + } - AssertGenres( tvShow.GenreIds, tvShow.Genres ); - } + public static void AssertTVShowInformationStructure( IEnumerable tvShows ) + { + // ReSharper disable once PossibleMultipleEnumeration + Assert.IsTrue( tvShows.Any() ); - private static void AssertGenres( IReadOnlyList genreIds, IReadOnlyList genres ) + // ReSharper disable once PossibleMultipleEnumeration + foreach( TVShowInfo tvShow in tvShows ) { - Assert.AreEqual( genreIds.Count, genres.Count ); - - foreach( Genre genre in genres ) - { - Assert.IsFalse( string.IsNullOrWhiteSpace( genre.Name ) ); - Assert.IsTrue( genre.Id > 0 ); - } + AssertTVShowInformationStructure( tvShow ); } + } - public static void AssertTVShowSeasonInformationStructure( SeasonInfo seasonInfo ) - { - // ReSharper disable once PossibleMultipleEnumeration - Assert.IsTrue( seasonInfo.Episodes.Any() ); + public static void AssertTVShowInformationStructure( TVShowInfo tvShow ) + { + Assert.IsTrue( tvShow.Id > 0 ); + Assert.IsFalse( string.IsNullOrEmpty( tvShow.Name ), $"Actual {tvShow}" ); + Assert.IsFalse( string.IsNullOrWhiteSpace( tvShow.OriginalName ), $"Actual {tvShow}" ); - // ReSharper disable once PossibleMultipleEnumeration - foreach( Episode episode in seasonInfo.Episodes ) - { - AssertTVShowSeasonInformationStructure( episode ); - } + AssertImagePath( tvShow.BackdropPath ); + AssertImagePath( tvShow.PosterPath ); + AssertGenres( tvShow.GenreIds, tvShow.Genres ); + } + + private static void AssertGenres( IReadOnlyList genreIds, IReadOnlyList genres ) + { + Assert.AreEqual( genreIds.Count, genres.Count ); + + foreach( Genre genre in genres ) + { + Assert.IsFalse( string.IsNullOrWhiteSpace( genre.Name ) ); + Assert.IsTrue( genre.Id > 0 ); } + } - private static void AssertTVShowSeasonInformationStructure( Episode episode ) + public static void AssertTVShowSeasonInformationStructure( SeasonInfo seasonInfo ) + { + // ReSharper disable once PossibleMultipleEnumeration + Assert.IsTrue( seasonInfo.Episodes.Any() ); + + // ReSharper disable once PossibleMultipleEnumeration + foreach( Episode episode in seasonInfo.Episodes ) { - Assert.IsTrue( episode.Id > 0 ); - Assert.IsFalse( episode.AirDate == default ); - Assert.IsTrue( episode.EpisodeNumber > 0 ); - Assert.IsFalse( string.IsNullOrWhiteSpace( episode.Name ) ); - Assert.IsFalse( string.IsNullOrWhiteSpace( episode.Overview ) ); - Assert.IsFalse( string.IsNullOrWhiteSpace( episode.ProductionCode ) ); - Assert.IsTrue( episode.SeasonNumber > 0 ); - AssertImagePath( episode.StillPath ); - Assert.IsTrue( episode.VoteAverage > 5 ); - Assert.IsTrue( episode.VoteCount > 5 ); - - foreach( Crew crew in episode.Crew ) - { - AssertTvShowCrewStructure( crew ); - } + AssertTVShowSeasonInformationStructure( episode ); + } + } - foreach( GuestStars guestStars in episode.GuestStars ) - { - AssertTvShowGuestStarsStructure( guestStars ); - } + private static void AssertTVShowSeasonInformationStructure( Episode episode ) + { + Assert.IsTrue( episode.Id > 0 ); + Assert.IsFalse( episode.AirDate == default ); + Assert.IsTrue( episode.EpisodeNumber > 0 ); + Assert.IsFalse( string.IsNullOrWhiteSpace( episode.Name ) ); + Assert.IsFalse( string.IsNullOrWhiteSpace( episode.Overview ) ); + Assert.IsFalse( string.IsNullOrWhiteSpace( episode.ProductionCode ) ); + Assert.IsTrue( episode.SeasonNumber > 0 ); + AssertImagePath( episode.StillPath ); + Assert.IsTrue( episode.VoteAverage > 5 ); + Assert.IsTrue( episode.VoteCount > 5 ); + + foreach( Crew crew in episode.Crew ) + { + AssertTvShowCrewStructure( crew ); } - private static void AssertTvShowCrewStructure( Crew crew ) + foreach( GuestStars guestStars in episode.GuestStars ) { - Assert.IsTrue( crew.Id > 0 ); - Assert.IsFalse( string.IsNullOrWhiteSpace( crew.Job ) ); - Assert.IsFalse( string.IsNullOrWhiteSpace( crew.Department ) ); - Assert.IsFalse( string.IsNullOrWhiteSpace( crew.CreditId ) ); - Assert.IsFalse( string.IsNullOrWhiteSpace( crew.KnownForDepartment ) ); - Assert.IsFalse( string.IsNullOrWhiteSpace( crew.Name ) ); - Assert.IsFalse( string.IsNullOrWhiteSpace( crew.OriginalName ) ); - Assert.IsTrue( crew.Popularity > 0 ); - - if( crew.ProfilePath != null ) - { - AssertImagePath( crew.ProfilePath ); - } + AssertTvShowGuestStarsStructure( guestStars ); } + } - private static void AssertTvShowGuestStarsStructure( GuestStars guestStars ) + private static void AssertTvShowCrewStructure( Crew crew ) + { + Assert.IsTrue( crew.Id > 0 ); + Assert.IsFalse( string.IsNullOrWhiteSpace( crew.Job ) ); + Assert.IsFalse( string.IsNullOrWhiteSpace( crew.Department ) ); + Assert.IsFalse( string.IsNullOrWhiteSpace( crew.CreditId ) ); + Assert.IsFalse( string.IsNullOrWhiteSpace( crew.KnownForDepartment ) ); + Assert.IsFalse( string.IsNullOrWhiteSpace( crew.Name ) ); + Assert.IsFalse( string.IsNullOrWhiteSpace( crew.OriginalName ) ); + Assert.IsTrue( crew.Popularity > 0 ); + + if( crew.ProfilePath != null ) { - Assert.IsTrue( guestStars.Id > 0 ); - Assert.IsTrue( guestStars.Order > 0 ); - Assert.IsFalse( string.IsNullOrWhiteSpace( guestStars.Character ) ); - Assert.IsFalse( string.IsNullOrWhiteSpace( guestStars.CreditId ) ); - Assert.IsFalse( string.IsNullOrWhiteSpace( guestStars.KnownForDepartment ) ); - Assert.IsFalse( string.IsNullOrWhiteSpace( guestStars.Name ) ); - Assert.IsFalse( string.IsNullOrWhiteSpace( guestStars.OriginalName ) ); - Assert.IsTrue( guestStars.Popularity > 0 ); - - AssertImagePath( guestStars.ProfilePath ); + AssertImagePath( crew.ProfilePath ); } + } - private static void Log( string msg, string category = null ) - => System.Diagnostics.Trace.WriteLine( msg, category ); + private static void AssertTvShowGuestStarsStructure( GuestStars guestStars ) + { + Assert.IsTrue( guestStars.Id > 0 ); + Assert.IsTrue( guestStars.Order > 0 ); + Assert.IsFalse( string.IsNullOrWhiteSpace( guestStars.Character ) ); + Assert.IsFalse( string.IsNullOrWhiteSpace( guestStars.CreditId ) ); + Assert.IsFalse( string.IsNullOrWhiteSpace( guestStars.KnownForDepartment ) ); + Assert.IsFalse( string.IsNullOrWhiteSpace( guestStars.Name ) ); + Assert.IsFalse( string.IsNullOrWhiteSpace( guestStars.OriginalName ) ); + Assert.IsTrue( guestStars.Popularity > 0 ); + + AssertImagePath( guestStars.ProfilePath ); } + + public static void Log( string msg, string category = null ) + => System.Diagnostics.Trace.WriteLine( msg, category ); } diff --git a/DM.MovieApi.IntegrationTests/AssemblyInit.cs b/DM.MovieApi.IntegrationTests/AssemblyInit.cs index a95a302..1012daf 100644 --- a/DM.MovieApi.IntegrationTests/AssemblyInit.cs +++ b/DM.MovieApi.IntegrationTests/AssemblyInit.cs @@ -1,67 +1,59 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using DM.MovieApi.IntegrationTests.Infrastructure; -using DM.MovieApi.MovieDb.Configuration; -using Microsoft.VisualStudio.TestTools.UnitTesting; +namespace DM.MovieApi.IntegrationTests; -namespace DM.MovieApi.IntegrationTests +[TestClass] +public class AssemblyInit { - [TestClass] - public class AssemblyInit - { - internal static readonly IApiSettings Settings = new IntegrationMovieDbSettings(); + internal static readonly IApiSettings Settings = new IntegrationMovieDbSettings(); - [AssemblyInitialize] - public static async Task Init( TestContext context ) - { - NCrunchGuard(); + [AssemblyInitialize] + public static async Task Init( TestContext context ) + { + NCrunchGuard(); - // register first; the factory will throw an ex if the BearerToken is invalid (fuzzy check). - RegisterFactorySettings(); + // register first; the factory will throw an ex if the BearerToken is invalid (fuzzy check). + RegisterFactorySettings(); - await ValidateSettings(); - } + await ValidateSettings(); + } - [TestMethod] - public void DebugMethod() - { - } + [TestMethod] + public void DebugMethod() + { + } - /// - /// Registers the for the - /// with the credentials from api.creds.json. - /// - public static void RegisterFactorySettings() - { - MovieDbFactory.RegisterSettings( Settings.BearerToken ); - } + /// + /// Registers the for the + /// with the credentials from api.creds.json. + /// + public static void RegisterFactorySettings() + { + MovieDbFactory.RegisterSettings( Settings.BearerToken ); + } - /// - /// NCrunch has issues with trying to be too clever when running tests concurrently. - /// Since this is integration testing and we don't need immediate feedback, use the R# or MS - /// test runner. And remember to ignore the integration tests in the NCrunch configuration. - /// - private static void NCrunchGuard() + /// + /// NCrunch has issues with trying to be too clever when running tests concurrently. + /// Since this is integration testing and we don't need immediate feedback, use the R# or MS + /// test runner. And remember to ignore the integration tests in the NCrunch configuration. + /// + private static void NCrunchGuard() + { + if( Environment.GetEnvironmentVariable( "NCrunch" ) == "1" ) { - if( Environment.GetEnvironmentVariable( "NCrunch" ) == "1" ) - { - throw new NotSupportedException( "NCrunch is not supported for integration testing. Use the R# or MS test runner." ); - } + throw new NotSupportedException( "NCrunch is not supported for integration testing. Use the R# or MS test runner." ); } + } - private static async Task ValidateSettings() - { - var request = new IntegrationApiRequest( Settings ); - var response = await request.QueryAsync( "configuration" ); + private static async Task ValidateSettings() + { + var request = new IntegrationApiRequest( Settings ); + var response = await request.QueryAsync( "configuration" ); - Assert.IsNull( response.Error, $"{response.Error} Query: {response.CommandText}" ); + Assert.IsNull( response.Error, $"{response.Error} Query: {response.CommandText}" ); - ApiConfiguration api = response.Item; - Assert.IsNotNull( api ); + ApiConfiguration api = response.Item; + Assert.IsNotNull( api ); - string[] someChangeKeys = { "crew", "cast", "episode", "title", "overview", "runtime", "adult", "season" }; - CollectionAssert.IsSubsetOf( someChangeKeys, api.ChangeKeys.ToArray() ); - } + string[] someChangeKeys = { "crew", "cast", "episode", "title", "overview", "runtime", "adult", "season" }; + CollectionAssert.IsSubsetOf( someChangeKeys, api.ChangeKeys.ToArray() ); } } diff --git a/DM.MovieApi.IntegrationTests/GlobalUsings.cs b/DM.MovieApi.IntegrationTests/GlobalUsings.cs new file mode 100644 index 0000000..0c1c41d --- /dev/null +++ b/DM.MovieApi.IntegrationTests/GlobalUsings.cs @@ -0,0 +1,20 @@ +// Global using directives + +global using System; +global using System.Collections.Generic; +global using System.Diagnostics.CodeAnalysis; +global using System.Linq; +global using System.Threading.Tasks; +global using DM.MovieApi.ApiRequest; +global using DM.MovieApi.ApiResponse; +global using DM.MovieApi.IntegrationTests.Infrastructure; +global using DM.MovieApi.MovieDb; +global using DM.MovieApi.MovieDb.Companies; +global using DM.MovieApi.MovieDb.Configuration; +global using DM.MovieApi.MovieDb.Genres; +global using DM.MovieApi.MovieDb.Keywords; +global using DM.MovieApi.MovieDb.Movies; +global using DM.MovieApi.MovieDb.People; +global using DM.MovieApi.MovieDb.TV; +global using DM.MovieApi.Shims; +global using Microsoft.VisualStudio.TestTools.UnitTesting; diff --git a/DM.MovieApi.IntegrationTests/Infrastructure/IntegrationApiRequest.cs b/DM.MovieApi.IntegrationTests/Infrastructure/IntegrationApiRequest.cs index 13e49ac..7d5e41d 100644 --- a/DM.MovieApi.IntegrationTests/Infrastructure/IntegrationApiRequest.cs +++ b/DM.MovieApi.IntegrationTests/Infrastructure/IntegrationApiRequest.cs @@ -1,14 +1,11 @@ -using DM.MovieApi.ApiRequest; +namespace DM.MovieApi.IntegrationTests.Infrastructure; -namespace DM.MovieApi.IntegrationTests.Infrastructure +/// +/// Bare bones class providing access to the . +/// +internal class IntegrationApiRequest : ApiRequestBase { - /// - /// Bare bones class providing access to the . - /// - internal class IntegrationApiRequest : ApiRequestBase - { - public IntegrationApiRequest( IApiSettings settings ) - : base( settings ) - { } - } + public IntegrationApiRequest( IApiSettings settings ) + : base( settings ) + { } } diff --git a/DM.MovieApi.IntegrationTests/Infrastructure/IntegrationMovieDbSettings.cs b/DM.MovieApi.IntegrationTests/Infrastructure/IntegrationMovieDbSettings.cs index b6d5961..3590ee7 100644 --- a/DM.MovieApi.IntegrationTests/Infrastructure/IntegrationMovieDbSettings.cs +++ b/DM.MovieApi.IntegrationTests/Infrastructure/IntegrationMovieDbSettings.cs @@ -1,74 +1,71 @@ -using System; -using System.IO; +using System.IO; using System.Reflection; -using Microsoft.VisualStudio.TestTools.UnitTesting; using Newtonsoft.Json; -namespace DM.MovieApi.IntegrationTests.Infrastructure +namespace DM.MovieApi.IntegrationTests.Infrastructure; + +internal class IntegrationMovieDbSettings : IApiSettings { - internal class IntegrationMovieDbSettings : IApiSettings - { - public const string FileName = "api.creds.json"; - public readonly string FilePath = Path.Combine( RootDirectory, FileName ); + public const string FileName = "api.creds.json"; + public readonly string FilePath = Path.Combine( RootDirectory, FileName ); - public string ApiUrl => MovieDbFactory.TheMovieDbApiUrl; + public string ApiUrl => MovieDbFactory.TheMovieDbApiUrl; - public string BearerToken { get; private set; } + public string BearerToken { get; private set; } - /// - /// Loads the API credentials from api.creds.json in the root of the test project. - /// - public IntegrationMovieDbSettings() - { - Hydrate(); - } + /// + /// Loads the API credentials from api.creds.json in the root of the test project. + /// + public IntegrationMovieDbSettings() + { + Hydrate(); + } - /// - /// Loads the API credentials from the provided values. - /// - public IntegrationMovieDbSettings( string apiBearerToken ) - { - BearerToken = apiBearerToken; - } + /// + /// Loads the API credentials from the provided values. + /// + public IntegrationMovieDbSettings( string apiBearerToken ) + { + BearerToken = apiBearerToken; + } - private void Hydrate() + private void Hydrate() + { + var anon = new { - var anon = new - { - BearerToken = "your-v4-bearer-token-here" - }; + BearerToken = "your-v4-bearer-token-here" + }; - if( File.Exists( FilePath ) == false ) - { - string structure = JsonConvert.SerializeObject( anon, Formatting.Indented ); + if( File.Exists( FilePath ) == false ) + { + string structure = JsonConvert.SerializeObject( anon, Formatting.Indented ); - Assert.Fail( $"The file {FileName} does not exist. Expected to find it here:\r\n{FilePath}\r\n\r\n" + - $"{FileName} must exist in the root of the integration project with your API Key " + - "from themoviedb.org. When the project is built, the json file will be output to " + - $"the directory listed above. Use the following json structure:\r\n{structure}\r\n" ); - } + Assert.Fail( $"The file {FileName} does not exist. Expected to find it here:\r\n{FilePath}\r\n\r\n" + + $"{FileName} must exist in the root of the integration project with your API Key " + + "from themoviedb.org. When the project is built, the json file will be output to " + + $"the directory listed above. Use the following json structure:\r\n{structure}\r\n" ); + } - var json = File.ReadAllText( FilePath ); - var api = JsonConvert.DeserializeAnonymousType( json, anon ); + var json = File.ReadAllText( FilePath ); + var api = JsonConvert.DeserializeAnonymousType( json, anon ); - BearerToken = api.BearerToken; - } + BearerToken = api.BearerToken; + } - // ReSharper disable InconsistentNaming - private static readonly Lazy _rootDirectory = new( GetRootDirectory ); + // ReSharper disable InconsistentNaming + private static readonly Lazy _rootDirectory = new( GetRootDirectory ); - internal static string RootDirectory => _rootDirectory.Value; + internal static string RootDirectory => _rootDirectory.Value; - private static string GetRootDirectory() - { - string location = Assembly.GetExecutingAssembly().Location; - string dir = Path.GetDirectoryName( location ); + private static string GetRootDirectory() + { + string location = Assembly.GetExecutingAssembly().Location; + string dir = Path.GetDirectoryName( location ); - Assert.IsFalse( string.IsNullOrEmpty( dir ) ); - Assert.IsTrue( Directory.Exists( dir ) ); + Assert.IsFalse( string.IsNullOrEmpty( dir ) ); + Assert.IsTrue( Directory.Exists( dir ) ); - return dir; - } + return dir; } } diff --git a/DM.MovieApi.IntegrationTests/MovieDb/Certifications/ApiMovieRatingRequestTests.cs b/DM.MovieApi.IntegrationTests/MovieDb/Certifications/ApiMovieRatingRequestTests.cs index 5073631..a56b2f7 100644 --- a/DM.MovieApi.IntegrationTests/MovieDb/Certifications/ApiMovieRatingRequestTests.cs +++ b/DM.MovieApi.IntegrationTests/MovieDb/Certifications/ApiMovieRatingRequestTests.cs @@ -1,56 +1,51 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using DM.MovieApi.ApiResponse; -using DM.MovieApi.MovieDb.Certifications; -using Microsoft.VisualStudio.TestTools.UnitTesting; +using DM.MovieApi.MovieDb.Certifications; -namespace DM.MovieApi.IntegrationTests.MovieDb.Certifications +namespace DM.MovieApi.IntegrationTests.MovieDb.Certifications; + +[TestClass] +public class ApiMovieRatingRequestTests { - [TestClass] - public class ApiMovieRatingRequestTests - { - private IApiMovieRatingRequest _api; + private IApiMovieRatingRequest _api; - [TestInitialize] - public void TestInit() - { - ApiResponseUtil.ThrottleTests(); + [TestInitialize] + public void TestInit() + { + ApiResponseUtil.ThrottleTests(); - _api = MovieDbFactory.Create().Value; + _api = MovieDbFactory.Create().Value; - Assert.IsInstanceOfType( _api, typeof( ApiMovieRatingRequest ) ); - } + Assert.IsInstanceOfType( _api, typeof( ApiMovieRatingRequest ) ); + } - [TestMethod] - public async Task GetMovieRatingsAsync_Returns_Ratings_InCorrectOrder() - { - ApiQueryResponse response = await _api.GetMovieRatingsAsync(); - - ApiResponseUtil.AssertErrorIsNull( response ); - - AssertOrderedRatings( 8, response.Item.Australia ); - AssertOrderedRatings( 5, response.Item.Canada ); - AssertOrderedRatings( 5, response.Item.France ); - AssertOrderedRatings( 5, response.Item.Germany ); - AssertOrderedRatings( 3, response.Item.India ); - AssertOrderedRatings( 8, response.Item.NewZealand ); - AssertOrderedRatings( 7, response.Item.UnitedKingdom ); - AssertOrderedRatings( 6, response.Item.UnitedStates ); - } + [TestMethod] + public async Task GetMovieRatingsAsync_Returns_Ratings_InCorrectOrder() + { + ApiQueryResponse response = await _api.GetMovieRatingsAsync(); + + ApiResponseUtil.AssertErrorIsNull( response ); + + AssertOrderedRatings( 8, response.Item.Australia ); + AssertOrderedRatings( 5, response.Item.Canada ); + AssertOrderedRatings( 5, response.Item.France ); + AssertOrderedRatings( 5, response.Item.Germany ); + AssertOrderedRatings( 3, response.Item.India ); + AssertOrderedRatings( 8, response.Item.NewZealand ); + AssertOrderedRatings( 7, response.Item.UnitedKingdom ); + AssertOrderedRatings( 6, response.Item.UnitedStates ); + } - private void AssertOrderedRatings( int expectedCount, IReadOnlyList items ) - { - Assert.AreEqual( expectedCount, items.Count ); + private void AssertOrderedRatings( int expectedCount, IReadOnlyList items ) + { + Assert.AreEqual( expectedCount, items.Count ); - int previousOrder = -1; + int previousOrder = -1; - foreach( Certification item in items ) - { - Assert.IsTrue( item.Order > previousOrder, - $"{item.Rating} item.Order: {item.Order} previousOrder: {previousOrder}" ); + foreach( Certification item in items ) + { + Assert.IsTrue( item.Order > previousOrder, + $"{item.Rating} item.Order: {item.Order} previousOrder: {previousOrder}" ); - previousOrder = item.Order; - } + previousOrder = item.Order; } } } diff --git a/DM.MovieApi.IntegrationTests/MovieDb/Companies/ApiCompanyRequestTests.cs b/DM.MovieApi.IntegrationTests/MovieDb/Companies/ApiCompanyRequestTests.cs index 78edc3a..f97730b 100644 --- a/DM.MovieApi.IntegrationTests/MovieDb/Companies/ApiCompanyRequestTests.cs +++ b/DM.MovieApi.IntegrationTests/MovieDb/Companies/ApiCompanyRequestTests.cs @@ -1,76 +1,69 @@ -using System; -using System.Threading.Tasks; -using DM.MovieApi.ApiResponse; -using DM.MovieApi.MovieDb.Companies; -using Microsoft.VisualStudio.TestTools.UnitTesting; +namespace DM.MovieApi.IntegrationTests.MovieDb.Companies; -namespace DM.MovieApi.IntegrationTests.MovieDb.Companies +[TestClass] +public class ApiCompanyRequestTests { - [TestClass] - public class ApiCompanyRequestTests - { - private IApiCompanyRequest _api; + private IApiCompanyRequest _api; - [TestInitialize] - public void TestInit() - { - ApiResponseUtil.ThrottleTests(); + [TestInitialize] + public void TestInit() + { + ApiResponseUtil.ThrottleTests(); - _api = MovieDbFactory.Create().Value; + _api = MovieDbFactory.Create().Value; - Assert.IsInstanceOfType( _api, typeof( ApiCompanyRequest ) ); - } + Assert.IsInstanceOfType( _api, typeof( ApiCompanyRequest ) ); + } - [TestMethod] - public async Task FindByIdAsync_Lucasfilm_WithResults_NoParentCompany() - { - const int id = 1; - const string expectedName = "Lucasfilm Ltd."; - const string expectedHeadquarters = "San Francisco, California"; - const string expectedHomepage = "https://www.lucasfilm.com"; + [TestMethod] + public async Task FindByIdAsync_Lucasfilm_WithResults_NoParentCompany() + { + const int id = 1; + const string expectedName = "Lucasfilm Ltd."; + const string expectedHeadquarters = "San Francisco, California"; + const string expectedHomepage = "https://www.lucasfilm.com"; - ApiQueryResponse response = await _api.FindByIdAsync( id ); + ApiQueryResponse response = await _api.FindByIdAsync( id ); - ApiResponseUtil.AssertErrorIsNull( response ); + ApiResponseUtil.AssertErrorIsNull( response ); - Assert.AreEqual( id, response.Item.Id ); - Assert.AreEqual( expectedName, response.Item.Name ); - Assert.AreEqual( expectedHeadquarters, response.Item.Headquarters ); - Assert.AreEqual( new Uri( expectedHomepage ), new Uri( response.Item.Homepage ) ); - ApiResponseUtil.AssertImagePath( response.Item.LogoPath ); + Assert.AreEqual( id, response.Item.Id ); + Assert.AreEqual( expectedName, response.Item.Name ); + Assert.AreEqual( expectedHeadquarters, response.Item.Headquarters ); + Assert.AreEqual( new Uri( expectedHomepage ), new Uri( response.Item.Homepage ) ); + ApiResponseUtil.AssertImagePath( response.Item.LogoPath ); - Assert.IsNull( response.Item.ParentCompany ); - } + Assert.IsNull( response.Item.ParentCompany ); + } - [TestMethod] - public async Task FindByIdAsync_Pixar_IncludesParentCompany() - { - const int id = 3; - const string expectedName = "Pixar"; - const string expectedParentName = "Walt Disney Pictures"; - const int expectedParentId = 2; + [TestMethod] + public async Task FindByIdAsync_Pixar_IncludesParentCompany() + { + const int id = 3; + const string expectedName = "Pixar"; + const string expectedParentName = "Walt Disney Pictures"; + const int expectedParentId = 2; - ApiQueryResponse response = await _api.FindByIdAsync( id ); + ApiQueryResponse response = await _api.FindByIdAsync( id ); - ApiResponseUtil.AssertErrorIsNull( response ); + ApiResponseUtil.AssertErrorIsNull( response ); - Assert.AreEqual( id, response.Item.Id ); - Assert.AreEqual( expectedName, response.Item.Name ); + Assert.AreEqual( id, response.Item.Id ); + Assert.AreEqual( expectedName, response.Item.Name ); - Assert.IsNotNull( response.Item.ParentCompany ); - Assert.AreEqual( expectedParentId, response.Item.ParentCompany.Id ); - Assert.AreEqual( expectedParentName, response.Item.ParentCompany.Name ); - ApiResponseUtil.AssertImagePath( response.Item.ParentCompany.LogoPath ); - } + Assert.IsNotNull( response.Item.ParentCompany ); + Assert.AreEqual( expectedParentId, response.Item.ParentCompany.Id ); + Assert.AreEqual( expectedParentName, response.Item.ParentCompany.Name ); + ApiResponseUtil.AssertImagePath( response.Item.ParentCompany.LogoPath ); + } - [TestMethod] - public async Task GetMoviesAsync_CanPageResults() - { - const int companyId = 3; - const int minimumPageCount = 5; + [TestMethod] + public async Task GetMoviesAsync_CanPageResults() + { + const int companyId = 3; + const int minimumPageCount = 5; - await ApiResponseUtil.AssertCanPageSearchResponse( companyId, minimumPageCount, - ( id, pageNumber ) => _api.GetMoviesAsync( id, pageNumber ), x => x.Id ); - } + await ApiResponseUtil.AssertCanPageSearchResponse( companyId, minimumPageCount, + ( id, pageNumber ) => _api.GetMoviesAsync( id, pageNumber ), x => x.Id ); } } diff --git a/DM.MovieApi.IntegrationTests/MovieDb/Configuration/ApiConfigurationRequestTests.cs b/DM.MovieApi.IntegrationTests/MovieDb/Configuration/ApiConfigurationRequestTests.cs index 36821f8..8a703d9 100644 --- a/DM.MovieApi.IntegrationTests/MovieDb/Configuration/ApiConfigurationRequestTests.cs +++ b/DM.MovieApi.IntegrationTests/MovieDb/Configuration/ApiConfigurationRequestTests.cs @@ -1,69 +1,62 @@ -using System.Linq; -using System.Threading.Tasks; -using DM.MovieApi.ApiResponse; -using DM.MovieApi.MovieDb.Configuration; -using Microsoft.VisualStudio.TestTools.UnitTesting; +namespace DM.MovieApi.IntegrationTests.MovieDb.Configuration; -namespace DM.MovieApi.IntegrationTests.MovieDb.Configuration +[TestClass] +public class ApiConfigurationRequestTests { - [TestClass] - public class ApiConfigurationRequestTests - { - private IApiConfigurationRequest _api; + private IApiConfigurationRequest _api; - [TestInitialize] - public void TestInit() - { - ApiResponseUtil.ThrottleTests(); + [TestInitialize] + public void TestInit() + { + ApiResponseUtil.ThrottleTests(); - _api = MovieDbFactory.Create().Value; + _api = MovieDbFactory.Create().Value; - Assert.IsInstanceOfType( _api, typeof( ApiConfigurationRequest ) ); - } + Assert.IsInstanceOfType( _api, typeof( ApiConfigurationRequest ) ); + } - [TestMethod] - public async Task ApiConfiguration_Includes_ListOf_ChangedKeys() - { - ApiQueryResponse config = await _api.GetAsync(); + [TestMethod] + public async Task ApiConfiguration_Includes_ListOf_ChangedKeys() + { + ApiQueryResponse config = await _api.GetAsync(); - Assert.IsNotNull( config.Item.ChangeKeys ); - Assert.IsTrue( config.Item.ChangeKeys.Any() ); - } + Assert.IsNotNull( config.Item.ChangeKeys ); + Assert.IsTrue( config.Item.ChangeKeys.Any() ); + } - [TestMethod] - public async Task Images_RootUrl_SameAs_SecureRootUrl_WithHTTPS() - { - ApiQueryResponse config = await _api.GetAsync(); + [TestMethod] + public async Task Images_RootUrl_SameAs_SecureRootUrl_WithHTTPS() + { + ApiQueryResponse config = await _api.GetAsync(); - string expectedSecureUrl = config.Item.Images.RootUrl.Replace( "http://", "https://" ); + string expectedSecureUrl = config.Item.Images.RootUrl.Replace( "http://", "https://" ); - Assert.AreEqual( expectedSecureUrl, config.Item.Images.SecureRootUrl ); - } + Assert.AreEqual( expectedSecureUrl, config.Item.Images.SecureRootUrl ); + } - [TestMethod] - public async Task Images_RootUrls_AreStatic() - { - const string expectedUrl = "http://image.tmdb.org/t/p/"; - const string expectedSecureUrl = "https://image.tmdb.org/t/p/"; + [TestMethod] + public async Task Images_RootUrls_AreStatic() + { + const string expectedUrl = "http://image.tmdb.org/t/p/"; + const string expectedSecureUrl = "https://image.tmdb.org/t/p/"; - ApiQueryResponse config = await _api.GetAsync(); + ApiQueryResponse config = await _api.GetAsync(); - Assert.AreEqual( expectedUrl, config.Item.Images.RootUrl ); - Assert.AreEqual( expectedSecureUrl, config.Item.Images.SecureRootUrl ); - } + Assert.AreEqual( expectedUrl, config.Item.Images.RootUrl ); + Assert.AreEqual( expectedSecureUrl, config.Item.Images.SecureRootUrl ); + } - [TestMethod] - public async Task Images_Collections_Have_AtLeast_ThreeItems() - { - const int minCount = 3; + [TestMethod] + public async Task Images_Collections_Have_AtLeast_ThreeItems() + { + const int minCount = 3; - ApiQueryResponse config = await _api.GetAsync(); + ApiQueryResponse config = await _api.GetAsync(); - Assert.IsTrue( config.Item.Images.BackDrops.Count >= minCount ); - Assert.IsTrue( config.Item.Images.Logos.Count >= minCount ); - Assert.IsTrue( config.Item.Images.Posters.Count >= minCount ); - Assert.IsTrue( config.Item.Images.Profiles.Count >= minCount ); - Assert.IsTrue( config.Item.Images.Stills.Count >= minCount ); - } + Assert.IsTrue( config.Item.Images.BackDrops.Count >= minCount ); + Assert.IsTrue( config.Item.Images.Logos.Count >= minCount ); + Assert.IsTrue( config.Item.Images.Posters.Count >= minCount ); + Assert.IsTrue( config.Item.Images.Profiles.Count >= minCount ); + Assert.IsTrue( config.Item.Images.Stills.Count >= minCount ); } } diff --git a/DM.MovieApi.IntegrationTests/MovieDb/Discover/ApiDiscoverRequestTests.cs b/DM.MovieApi.IntegrationTests/MovieDb/Discover/ApiDiscoverRequestTests.cs index 9db14ab..0f3e69d 100644 --- a/DM.MovieApi.IntegrationTests/MovieDb/Discover/ApiDiscoverRequestTests.cs +++ b/DM.MovieApi.IntegrationTests/MovieDb/Discover/ApiDiscoverRequestTests.cs @@ -1,146 +1,140 @@ -using System.Linq; -using System.Threading.Tasks; -using DM.MovieApi.ApiResponse; +using DM.MovieApi.IntegrationTests.MovieDb.People; using DM.MovieApi.MovieDb.Discover; -using DM.MovieApi.MovieDb.Movies; -using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace DM.MovieApi.IntegrationTests.MovieDb.Discover -{ - [TestClass] - public class ApiDiscoverRequestTests - { - private IApiDiscoverRequest _api; +namespace DM.MovieApi.IntegrationTests.MovieDb.Discover; - [TestInitialize] - public void TestInit() - { - ApiResponseUtil.ThrottleTests(); +[TestClass] +public class ApiDiscoverRequestTests +{ + private IApiDiscoverRequest _api; + private IApiMovieRequest _movie; - _api = MovieDbFactory.Create().Value; + [TestInitialize] + public void TestInit() + { + ApiResponseUtil.ThrottleTests(); - Assert.IsInstanceOfType( _api, typeof( ApiDiscoverRequest ) ); - } + _api = MovieDbFactory.Create().Value; + _movie = MovieDbFactory.Create().Value; - [TestMethod] - public async Task DiscoverMovies_WithCrew() - { - int directorId = 66212; + Assert.IsInstanceOfType( _api, typeof( ApiDiscoverRequest ) ); + } - IDiscoverMovieParameterBuilder builder = CreateBuilder(); - builder.WithCrew( directorId ); + [TestMethod] + public async Task WithCrew() + { + int directorId = 66212; - ApiSearchResponse response = await _api.DiscoverMoviesAsync( builder ); + var builder = new DiscoverMovieParameterBuilder().WithCrew( directorId ); - ApiResponseUtil.AssertErrorIsNull( response ); - ApiResponseUtil.AssertMovieInformationStructure( response.Results ); - } + ApiSearchResponse response = await _api.DiscoverMoviesAsync( builder ); - [TestMethod] - public async Task DiscoverMovies_WithCrew_HasNoResult_InvalidPersonId() - { - int personId = 0; + ApiResponseUtil.AssertErrorIsNull( response ); + ApiResponseUtil.AssertMovieInformationStructure( response.Results ); + } - IDiscoverMovieParameterBuilder builder = CreateBuilder(); - builder.WithCrew( personId ); + [TestMethod] + public async Task WithCrew_HasNoResult_InvalidPersonId() + { + var builder = new DiscoverMovieParameterBuilder().WithCrew( 0 ); - ApiSearchResponse response = await _api.DiscoverMoviesAsync( builder ); + ApiSearchResponse response = await _api.DiscoverMoviesAsync( builder ); - ApiResponseUtil.AssertNoSearchResults( response ); - } - - [TestMethod] - public async Task DiscoverMovies_WithCast() - { - int actorId = 66462; + ApiResponseUtil.AssertNoSearchResults( response ); + } - IDiscoverMovieParameterBuilder builder = CreateBuilder(); - builder.WithCast( actorId ); + [DataRow( ApiPeopleRequestTests.PersonId_MillaJovovich )] + [DataRow( ApiPeopleRequestTests.PersonId_KevinBacon )] + [DataRow( ApiPeopleRequestTests.PersonId_CourteneyCox )] + [TestMethod] + public async Task WithCast( int personId ) + { + var builder = new DiscoverMovieParameterBuilder().WithCast( personId ); - ApiSearchResponse response = await _api.DiscoverMoviesAsync( builder ); + ApiSearchResponse response = await _api.DiscoverMoviesAsync( builder ); - ApiResponseUtil.AssertErrorIsNull( response ); - ApiResponseUtil.AssertMovieInformationStructure( response.Results ); - } + ApiResponseUtil.AssertErrorIsNull( response ); + ApiResponseUtil.AssertMovieInformationStructure( response.Results ); - [TestMethod] - public async Task DiscoverMovies_WithCast_HasNoResult_InvalidPersonId() + foreach( MovieInfo info in response.Results ) { - int personId = 0; - - IDiscoverMovieParameterBuilder builder = CreateBuilder(); - builder.WithCast( personId ); - - ApiSearchResponse response = await _api.DiscoverMoviesAsync( builder ); + MovieCredit credits = (await _movie.GetCreditsAsync( info.Id )).Item; - ApiResponseUtil.AssertNoSearchResults( response ); + Assert.IsTrue( credits.CastMembers.Any( x => x.PersonId == personId ) ); } + } - [TestMethod] - public async Task DiscoverMovies_WithGenre() - { - int genreId = 28; + [TestMethod] + public async Task WithCast_HasNoResult_InvalidPersonId() + { + var builder = new DiscoverMovieParameterBuilder().WithCast( 0 ); - IDiscoverMovieParameterBuilder builder = CreateBuilder(); - builder.WithGenre( genreId ); + ApiSearchResponse response = await _api.DiscoverMoviesAsync( builder ); + + ApiResponseUtil.AssertNoSearchResults( response ); + } - ApiSearchResponse response = await _api.DiscoverMoviesAsync( builder ); + [DynamicData( nameof( GetGenreData ), DynamicDataSourceType.Method )] + [TestMethod] + public async Task WithGenre( Genre genre ) + { + var builder = new DiscoverMovieParameterBuilder().WithGenre( genre ); - ApiResponseUtil.AssertErrorIsNull( response ); - ApiResponseUtil.AssertMovieInformationStructure( response.Results ); + ApiSearchResponse response = await _api.DiscoverMoviesAsync( builder ); - Assert.IsTrue( response.Results - .All( r => r.Genres.Any( g => g.Id == genreId ) ), "No results with genre" ); - } + ApiResponseUtil.AssertErrorIsNull( response ); + ApiResponseUtil.AssertMovieInformationStructure( response.Results ); - [TestMethod] - public async Task DiscoverMovies_ExcludeGenre() + foreach( MovieInfo info in response.Results ) { - int genreId = 28; - - IDiscoverMovieParameterBuilder builder = CreateBuilder(); - builder.ExcludeGenre( genreId ); + Assert.IsTrue( info.GenreIds.Contains( genre.Id ) ); + Assert.IsTrue( info.Genres.Contains( genre ) ); + } + } - ApiSearchResponse response = await _api.DiscoverMoviesAsync( builder ); + [DynamicData( nameof( GetGenreData ), DynamicDataSourceType.Method )] + [TestMethod] + public async Task ExcludeGenre( Genre genre ) + { + var builder = new DiscoverMovieParameterBuilder().ExcludeGenre( genre ); - ApiResponseUtil.AssertErrorIsNull( response ); - ApiResponseUtil.AssertMovieInformationStructure( response.Results ); + ApiSearchResponse response = await _api.DiscoverMoviesAsync( builder ); - Assert.IsTrue( response.Results - .All( r => r.Genres.All( g => g.Id != genreId ) ), "Genre found in results" ); - } + ApiResponseUtil.AssertErrorIsNull( response ); + ApiResponseUtil.AssertMovieInformationStructure( response.Results ); - [TestMethod] - public async Task DiscoverMovies_WithOriginalLanguage_InFinnish() + foreach( MovieInfo info in response.Results ) { - int directorId = 66212; - string originalLanguage = "fi"; + Assert.IsFalse( info.GenreIds.Contains( genre.Id ) ); + Assert.IsFalse( info.Genres.Contains( genre ) ); + } + } - IDiscoverMovieParameterBuilder builder = CreateBuilder(); - builder.WithOriginalLanguage( originalLanguage ).WithCrew( directorId ); + [DataRow( "es" )] + [DataRow( "de" )] + [DataRow( "fi" )] + [TestMethod] + public async Task WithOriginalLanguage( string originalLanguage ) + { + var builder = new DiscoverMovieParameterBuilder().WithOriginalLanguage( originalLanguage ); - ApiSearchResponse response = await _api.DiscoverMoviesAsync( builder ); + ApiSearchResponse response = await _api.DiscoverMoviesAsync( builder ); - ApiResponseUtil.AssertErrorIsNull( response ); - ApiResponseUtil.AssertMovieInformationStructure( response.Results ); - } + ApiResponseUtil.AssertErrorIsNull( response ); + ApiResponseUtil.AssertMovieInformationStructure( response.Results ); - [TestMethod] - public async Task DiscoverMovies_WithOriginalLanguage_InGerman() + foreach( MovieInfo info in response.Results ) { - int directorId = 66212; - string originalLanguage = "de"; - - IDiscoverMovieParameterBuilder builder = CreateBuilder(); - builder.WithOriginalLanguage( originalLanguage ).WithCrew( directorId ); - - ApiSearchResponse response = await _api.DiscoverMoviesAsync( builder ); + Movie m = (await _movie.FindByIdAsync( info.Id )).Item; - ApiResponseUtil.AssertErrorIsNull( response ); - ApiResponseUtil.AssertMovieInformationStructure( response.Results ); + Assert.AreEqual( originalLanguage, m.OriginalLanguage, $"{m.OriginalLanguage} - {m}" ); } + } - private IDiscoverMovieParameterBuilder CreateBuilder() - => new DiscoverMovieParameterBuilder(); + private static IEnumerable GetGenreData() + { + yield return new object[] { GenreFactory.Comedy() }; + yield return new object[] { GenreFactory.Action() }; + yield return new object[] { GenreFactory.ScienceFiction() }; } } diff --git a/DM.MovieApi.IntegrationTests/MovieDb/Genres/ApiGenreRequestTests.cs b/DM.MovieApi.IntegrationTests/MovieDb/Genres/ApiGenreRequestTests.cs index d406f2b..8ecabb5 100644 --- a/DM.MovieApi.IntegrationTests/MovieDb/Genres/ApiGenreRequestTests.cs +++ b/DM.MovieApi.IntegrationTests/MovieDb/Genres/ApiGenreRequestTests.cs @@ -1,170 +1,161 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using DM.MovieApi.ApiResponse; -using DM.MovieApi.MovieDb.Genres; -using DM.MovieApi.MovieDb.Movies; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace DM.MovieApi.IntegrationTests.MovieDb.Genres +namespace DM.MovieApi.IntegrationTests.MovieDb.Genres; + +[TestClass] +public class ApiGenreRequestTests { - [TestClass] - public class ApiGenreRequestTests - { - private IApiGenreRequest _api; + private IApiGenreRequest _api; - [TestInitialize] - public void TestInit() - { - ApiResponseUtil.ThrottleTests(); + [TestInitialize] + public void TestInit() + { + ApiResponseUtil.ThrottleTests(); - _api = MovieDbFactory.Create().Value; + _api = MovieDbFactory.Create().Value; - Assert.IsInstanceOfType( _api, typeof( ApiGenreRequest ) ); + Assert.IsInstanceOfType( _api, typeof( ApiGenreRequest ) ); - ((ApiGenreRequest)_api).ClearAllGenres(); - } + ((ApiGenreRequest)_api).ClearAllGenres(); + } - [TestMethod] - public async Task FindById_Foreign_Genre_NoLongerExists() - { - const int id = 10769; - const string name = "Foreign"; + [TestMethod] + public async Task FindById_Foreign_Genre_NoLongerExists() + { + const int id = 10769; + const string name = "Foreign"; - CollectionAssert.DoesNotContain( _api.AllGenres.ToArray(), new Genre( id, name ) ); + CollectionAssert.DoesNotContain( _api.AllGenres.ToArray(), new Genre( id, name ) ); - // FindById will add to AllGenres when it does not exist - ApiQueryResponse response = await _api.FindByIdAsync( id ); + // FindById will add to AllGenres when it does not exist + ApiQueryResponse response = await _api.FindByIdAsync( id ); - Assert.IsNotNull( response.Error ); - Assert.AreEqual( TmdbStatusCode.ResourceNotFound, response.Error.TmdbStatusCode ); + Assert.IsNotNull( response.Error ); + Assert.AreEqual( TmdbStatusCode.ResourceNotFound, response.Error.TmdbStatusCode ); - // the genre should not have been added to AllGenres since TMDB no longer recognizes - // the foreign genre category. - CollectionAssert.DoesNotContain( _api.AllGenres.ToArray(), new Genre( id, name ) ); - } + // the genre should not have been added to AllGenres since TMDB no longer recognizes + // the foreign genre category. + CollectionAssert.DoesNotContain( _api.AllGenres.ToArray(), new Genre( id, name ) ); + } - [TestMethod] - public async Task AllGenres_And_GetAllAsync_Return_Same_Results() - { - IReadOnlyList allGenres = _api.AllGenres; + [TestMethod] + public async Task AllGenres_And_GetAllAsync_Return_Same_Results() + { + IReadOnlyList allGenres = _api.AllGenres; - ApiQueryResponse> response = await _api.GetAllAsync(); + ApiQueryResponse> response = await _api.GetAllAsync(); - CollectionAssert.AreEqual( allGenres.ToArray(), response.Item.ToArray() ); - } + CollectionAssert.AreEqual( allGenres.ToArray(), response.Item.ToArray() ); + } - [TestMethod] - public async Task GetAllAsync_Returns_AllResults_With_Id_and_Name() - { - ApiQueryResponse> response = await _api.GetAllAsync(); + [TestMethod] + public async Task GetAllAsync_Returns_AllResults_With_Id_and_Name() + { + ApiQueryResponse> response = await _api.GetAllAsync(); - ApiResponseUtil.AssertErrorIsNull( response ); + ApiResponseUtil.AssertErrorIsNull( response ); - foreach( Genre genre in response.Item ) - { - Assert.IsTrue( genre.Id > 0, genre.ToString() ); - Assert.IsNotNull( genre.Name, genre.ToString() ); - Assert.IsTrue( genre.Name.Length >= 3, genre.ToString() ); - } + foreach( Genre genre in response.Item ) + { + Assert.IsTrue( genre.Id > 0, genre.ToString() ); + Assert.IsNotNull( genre.Name, genre.ToString() ); + Assert.IsTrue( genre.Name.Length >= 3, genre.ToString() ); } + } - [TestMethod] - public async Task GetAllAsync_Returns_Known_Genres() - { - ApiQueryResponse> response = await _api.GetAllAsync(); + [TestMethod] + public async Task GetAllAsync_Returns_Known_Genres() + { + ApiQueryResponse> response = await _api.GetAllAsync(); - ApiResponseUtil.AssertErrorIsNull( response ); + ApiResponseUtil.AssertErrorIsNull( response ); - IReadOnlyList knownGenres = GenreFactory.GetAll(); + IReadOnlyList knownGenres = GenreFactory.GetAll(); - CollectionAssert.AreEquivalent( knownGenres.ToArray(), response.Item.ToArray() ); - } + CollectionAssert.AreEquivalent( knownGenres.ToArray(), response.Item.ToArray() ); + } - [TestMethod] - public async Task GetMoviesAsync_Returns_19_Results() - { - ApiQueryResponse> response = await _api.GetMoviesAsync(); + [TestMethod] + public async Task GetMoviesAsync_Returns_19_Results() + { + ApiQueryResponse> response = await _api.GetMoviesAsync(); - ApiResponseUtil.AssertErrorIsNull( response ); + ApiResponseUtil.AssertErrorIsNull( response ); - Assert.IsTrue( response.Item.Any() ); + Assert.IsTrue( response.Item.Any() ); - Assert.AreEqual( 19, response.Item.Count ); - } + Assert.AreEqual( 19, response.Item.Count ); + } - [TestMethod] - public async Task GetTelevisionAsync_Returns_16_Results() - { - ApiQueryResponse> response = await _api.GetTelevisionAsync(); + [TestMethod] + public async Task GetTelevisionAsync_Returns_16_Results() + { + ApiQueryResponse> response = await _api.GetTelevisionAsync(); - ApiResponseUtil.AssertErrorIsNull( response ); + ApiResponseUtil.AssertErrorIsNull( response ); - Assert.IsTrue( response.Item.Any() ); + Assert.IsTrue( response.Item.Any() ); - Assert.AreEqual( 16, response.Item.Count ); - } + Assert.AreEqual( 16, response.Item.Count ); + } - [TestMethod] - public async Task GetMoviesAsync_IsSubset_OfGetAll() - { - ApiQueryResponse> movies = await _api.GetMoviesAsync(); + [TestMethod] + public async Task GetMoviesAsync_IsSubset_OfGetAll() + { + ApiQueryResponse> movies = await _api.GetMoviesAsync(); - ApiResponseUtil.AssertErrorIsNull( movies ); + ApiResponseUtil.AssertErrorIsNull( movies ); - ApiQueryResponse> all = await _api.GetAllAsync(); + ApiQueryResponse> all = await _api.GetAllAsync(); - ApiResponseUtil.AssertErrorIsNull( all ); + ApiResponseUtil.AssertErrorIsNull( all ); - Assert.IsTrue( all.Item.Count > movies.Item.Count ); + Assert.IsTrue( all.Item.Count > movies.Item.Count ); - CollectionAssert.IsSubsetOf( movies.Item.ToArray(), all.Item.ToArray() ); - } + CollectionAssert.IsSubsetOf( movies.Item.ToArray(), all.Item.ToArray() ); + } - [TestMethod] - public async Task GetTelevisionAsync_IsSubset_OfGetAll() - { - ApiQueryResponse> tv = await _api.GetTelevisionAsync(); + [TestMethod] + public async Task GetTelevisionAsync_IsSubset_OfGetAll() + { + ApiQueryResponse> tv = await _api.GetTelevisionAsync(); - ApiResponseUtil.AssertErrorIsNull( tv ); + ApiResponseUtil.AssertErrorIsNull( tv ); - ApiQueryResponse> all = await _api.GetAllAsync(); + ApiQueryResponse> all = await _api.GetAllAsync(); - ApiResponseUtil.AssertErrorIsNull( all ); + ApiResponseUtil.AssertErrorIsNull( all ); - Assert.IsTrue( all.Item.Count > tv.Item.Count ); + Assert.IsTrue( all.Item.Count > tv.Item.Count ); - CollectionAssert.IsSubsetOf( tv.Item.ToArray(), all.Item.ToArray() ); - } + CollectionAssert.IsSubsetOf( tv.Item.ToArray(), all.Item.ToArray() ); + } - [TestMethod] - public async Task FindMoviesByIdAsync_Returns_ValidResult() - { - int genreId = GenreFactory.Comedy().Id; + [TestMethod] + public async Task FindMoviesByIdAsync_Returns_ValidResult() + { + int genreId = GenreFactory.Comedy().Id; - ApiSearchResponse response = await _api.FindMoviesByIdAsync( genreId ); + ApiSearchResponse response = await _api.FindMoviesByIdAsync( genreId ); - ApiResponseUtil.AssertErrorIsNull( response ); + ApiResponseUtil.AssertErrorIsNull( response ); - Assert.AreEqual( 20, response.Results.Count ); + Assert.AreEqual( 20, response.Results.Count ); - var expectedGenres = new[] { GenreFactory.Comedy() }; + var expectedGenres = new[] { GenreFactory.Comedy() }; - foreach( MovieInfo info in response.Results ) - { - CollectionAssert.IsSubsetOf( expectedGenres, info.Genres.ToArray() ); - } + foreach( MovieInfo info in response.Results ) + { + CollectionAssert.IsSubsetOf( expectedGenres, info.Genres.ToArray() ); } + } - [TestMethod] - public async Task FindMoviesByIdAsync_CanPageResults() - { - int genreId = GenreFactory.Comedy().Id; - // Comedy has upwards of 2k pages. - const int minimumPageCount = 10; + [TestMethod] + public async Task FindMoviesByIdAsync_CanPageResults() + { + int genreId = GenreFactory.Comedy().Id; + // Comedy has upwards of 2k pages. + const int minimumPageCount = 10; - await ApiResponseUtil.AssertCanPageSearchResponse( genreId, minimumPageCount, - ( id, page ) => _api.FindMoviesByIdAsync( id, page ), x => x.Id ); - } + await ApiResponseUtil.AssertCanPageSearchResponse( genreId, minimumPageCount, + ( id, page ) => _api.FindMoviesByIdAsync( id, page ), x => x.Id ); } } diff --git a/DM.MovieApi.IntegrationTests/MovieDb/Movies/ApiMovieRequestTests.cs b/DM.MovieApi.IntegrationTests/MovieDb/Movies/ApiMovieRequestTests.cs index 1731d93..e6ea630 100644 --- a/DM.MovieApi.IntegrationTests/MovieDb/Movies/ApiMovieRequestTests.cs +++ b/DM.MovieApi.IntegrationTests/MovieDb/Movies/ApiMovieRequestTests.cs @@ -1,349 +1,335 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using DM.MovieApi.ApiResponse; -using DM.MovieApi.IntegrationTests.Infrastructure; -using DM.MovieApi.MovieDb; -using DM.MovieApi.MovieDb.Companies; -using DM.MovieApi.MovieDb.Genres; -using DM.MovieApi.MovieDb.Keywords; -using DM.MovieApi.MovieDb.Movies; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace DM.MovieApi.IntegrationTests.MovieDb.Movies +namespace DM.MovieApi.IntegrationTests.MovieDb.Movies; + +[TestClass] +public class ApiMovieRequestTests { - [TestClass] - public class ApiMovieRequestTests + private IApiMovieRequest _api; + + [TestInitialize] + public void TestInit() { - private IApiMovieRequest _api; + ApiResponseUtil.ThrottleTests(); - [TestInitialize] - public void TestInit() - { - ApiResponseUtil.ThrottleTests(); + _api = MovieDbFactory.Create().Value; - _api = MovieDbFactory.Create().Value; + Assert.IsInstanceOfType( _api, typeof( ApiMovieRequest ) ); + } - Assert.IsInstanceOfType( _api, typeof( ApiMovieRequest ) ); - } + [TestMethod] + public async Task SearchByTitleAsync_Excludes_AdultFilms() + { + // 2 parts to test: + // 1. manual search to verify adult films + // 2. api search to verify no adult films + + const string adultFilmTitle = "Debbie Does Dallas"; - [TestMethod] - public async Task SearchByTitleAsync_Excludes_AdultFilms() + var param = new Dictionary { - // 2 parts to test: - // 1. manual search to verify adult films - // 2. api search to verify no adult films - - const string adultFilmTitle = "Debbie Does Dallas"; - - var param = new Dictionary - { - {"query", adultFilmTitle}, - {"include_adult", "true"}, - {"language", "en"}, - }; - - string[] expectedAdultTitles = - { - "Debbie Does Dallas", - "Debbie Does Dallas... Again", - "Debbie Does Dallas: The Revenge", - "Debbie Does Dallas Part II", - "Debbie Does Dallas III: The Final Chapter", - }; - - var integrationApi = new IntegrationApiRequest( AssemblyInit.Settings ); - - var adult = await integrationApi.SearchAsync( "search/movie", param ); - - foreach( string title in expectedAdultTitles ) - { - var adultMovie = adult.Results.SingleOrDefault( x => x.Title == title ); - Assert.IsNotNull( adultMovie ); - } - - ApiSearchResponse search = await _api.SearchByTitleAsync( adultFilmTitle ); - - // if any results are returned, ensure they are not marked as an adult film - foreach( MovieInfo movie in search.Results ) - { - Assert.IsFalse( movie.IsAdultThemed ); - } - } + {"query", adultFilmTitle}, + {"include_adult", "true"}, + {"language", "en"}, + }; - [TestMethod] - public async Task SearchByTitleAsync_RunLolaRun_Returns_SingleResult_WithExpectedValues() + string[] expectedAdultTitles = { - const string runLolaRun = "Run Lola Run"; + "Debbie Does Dallas", + "Debbie Does Dallas... Again", + "Debbie Does Dallas: The Revenge", + "Debbie Does Dallas Part II", + "Debbie Does Dallas III: The Final Chapter", + }; - ApiSearchResponse response = await _api.SearchByTitleAsync( runLolaRun ); + var integrationApi = new IntegrationApiRequest( AssemblyInit.Settings ); - AssertRunLolaRun( response, runLolaRun ); - } + var adult = await integrationApi.SearchAsync( "search/movie", param ); - [TestMethod] - public async Task SearchByTitleAsync_RunLolaRun_Returns_SingleResult_WithExpectedValues_InGerman() + foreach( string title in expectedAdultTitles ) { - const string runLolaRun = "Run Lola Run"; - const string expectedTitle = "Lola rennt"; + var adultMovie = adult.Results.SingleOrDefault( x => x.Title == title ); + Assert.IsNotNull( adultMovie ); + } - ApiSearchResponse response = await _api.SearchByTitleAsync( runLolaRun, 1, "de" ); + ApiSearchResponse search = await _api.SearchByTitleAsync( adultFilmTitle ); - AssertRunLolaRun( response, expectedTitle ); + // if any results are returned, ensure they are not marked as an adult film + foreach( MovieInfo movie in search.Results ) + { + Assert.IsFalse( movie.IsAdultThemed ); } + } - private void AssertRunLolaRun( ApiSearchResponse response, string expectedTitle ) - { - ApiResponseUtil.AssertErrorIsNull( response ); + [TestMethod] + public async Task SearchByTitleAsync_RunLolaRun_Returns_SingleResult_WithExpectedValues() + { + const string runLolaRun = "Run Lola Run"; + + ApiSearchResponse response = await _api.SearchByTitleAsync( runLolaRun ); - Assert.AreEqual( 1, response.TotalResults ); - Assert.AreEqual( 1, response.Results.Count ); + AssertRunLolaRun( response, runLolaRun ); + } - MovieInfo movie = response.Results.Single(); + [TestMethod] + public async Task SearchByTitleAsync_RunLolaRun_Returns_SingleResult_WithExpectedValues_InGerman() + { + const string runLolaRun = "Run Lola Run"; + const string expectedTitle = "Lola rennt"; - Assert.AreEqual( 104, movie.Id ); + ApiSearchResponse response = await _api.SearchByTitleAsync( runLolaRun, 1, "de" ); - Assert.AreEqual( expectedTitle, movie.Title ); + AssertRunLolaRun( response, expectedTitle ); + } - Assert.AreEqual( new DateTime( 1998, 03, 03 ), movie.ReleaseDate ); + private void AssertRunLolaRun( ApiSearchResponse response, string expectedTitle ) + { + ApiResponseUtil.AssertErrorIsNull( response ); - var expectedGenres = new[] - { - GenreFactory.Action(), - GenreFactory.Drama(), - GenreFactory.Thriller(), - }; - CollectionAssert.AreEquivalent( expectedGenres, movie.Genres.ToArray() ); - } + Assert.AreEqual( 1, response.TotalResults ); + Assert.AreEqual( 1, response.Results.Count ); - [TestMethod] - public async Task SearchByTitleAsync_CanPageResults() + MovieInfo movie = response.Results.Single(); + + Assert.AreEqual( 104, movie.Id ); + + Assert.AreEqual( expectedTitle, movie.Title ); + + Assert.AreEqual( new DateTime( 1998, 03, 03 ), movie.ReleaseDate ); + + var expectedGenres = new[] { - const string query = "Harry"; - const int minimumPageCount = 8; + GenreFactory.Action(), + GenreFactory.Drama(), + GenreFactory.Thriller(), + }; + CollectionAssert.AreEquivalent( expectedGenres, movie.Genres.ToArray() ); + } - await ApiResponseUtil.AssertCanPageSearchResponse( query, minimumPageCount, - ( search, pageNumber ) => _api.SearchByTitleAsync( search, pageNumber ), x => x.Id ); - } + [TestMethod] + public async Task SearchByTitleAsync_CanPageResults() + { + const string query = "Harry"; + const int minimumPageCount = 8; - [TestMethod] - public async Task FindByIdAsync_StarWarsTheForceAwakens_Returns_AllValues() + await ApiResponseUtil.AssertCanPageSearchResponse( query, minimumPageCount, + ( search, pageNumber ) => _api.SearchByTitleAsync( search, pageNumber ), x => x.Id ); + } + + [TestMethod] + public async Task FindByIdAsync_StarWarsTheForceAwakens_Returns_AllValues() + { + const int id = 140607; + const string expectedImdbId = "tt2488496"; + const string expectedTitle = "Star Wars: The Force Awakens"; + const string expectedOriginalTitle = "Star Wars: The Force Awakens"; + const string expectedTagline = "Every generation has a story."; + const string expectedOverview = "Thirty years after defeating the Galactic Empire"; // truncated + const string expectedOriginalLanguage = "en"; + const string expectedHomepage = "http://www.starwars.com/films/star-wars-episode-vii"; + const string expectedStatus = "Released"; + const int expectedBudget = 245000000; + const int expectedRuntime = 136; + var expectedReleaseDate = new DateTime( 2015, 12, 15 ); + + ApiQueryResponse response = await _api.FindByIdAsync( id ); + + ApiResponseUtil.AssertErrorIsNull( response ); + + Movie movie = response.Item; + + Assert.AreEqual( id, movie.Id ); + Assert.AreEqual( expectedImdbId, movie.ImdbId ); + Assert.AreEqual( expectedTitle, movie.Title ); + Assert.AreEqual( expectedOriginalTitle, movie.OriginalTitle ); + Assert.AreEqual( expectedTagline, movie.Tagline ); + Assert.AreEqual( expectedOriginalLanguage, movie.OriginalLanguage ); + Assert.AreEqual( expectedHomepage, movie.Homepage ); + Assert.AreEqual( expectedStatus, movie.Status ); + Assert.AreEqual( expectedBudget, movie.Budget ); + Assert.AreEqual( expectedRuntime, movie.Runtime ); + Assert.AreEqual( expectedReleaseDate, movie.ReleaseDate ); + + ApiResponseUtil.AssertImagePath( movie.BackdropPath ); + ApiResponseUtil.AssertImagePath( movie.PosterPath ); + + Assert.IsTrue( movie.Overview.StartsWith( expectedOverview ) ); + Assert.IsTrue( movie.Popularity > 7, $"Actual: {movie.Popularity}" ); + Assert.IsTrue( movie.VoteAverage > 5 ); + Assert.IsTrue( movie.VoteCount > 1500 ); + + // Spoken Languages + var languages = new[] { - const int id = 140607; - const string expectedImdbId = "tt2488496"; - const string expectedTitle = "Star Wars: The Force Awakens"; - const string expectedOriginalTitle = "Star Wars: The Force Awakens"; - const string expectedTagline = "Every generation has a story."; - const string expectedOverview = "Thirty years after defeating the Galactic Empire"; // truncated - const string expectedOriginalLanguage = "en"; - const string expectedHomepage = "http://www.starwars.com/films/star-wars-episode-vii"; - const string expectedStatus = "Released"; - const int expectedBudget = 245000000; - const int expectedRuntime = 136; - var expectedReleaseDate = new DateTime( 2015, 12, 15 ); - - ApiQueryResponse response = await _api.FindByIdAsync( id ); - - ApiResponseUtil.AssertErrorIsNull( response ); - - Movie movie = response.Item; - - Assert.AreEqual( id, movie.Id ); - Assert.AreEqual( expectedImdbId, movie.ImdbId ); - Assert.AreEqual( expectedTitle, movie.Title ); - Assert.AreEqual( expectedOriginalTitle, movie.OriginalTitle ); - Assert.AreEqual( expectedTagline, movie.Tagline ); - Assert.AreEqual( expectedOriginalLanguage, movie.OriginalLanguage ); - Assert.AreEqual( expectedHomepage, movie.Homepage ); - Assert.AreEqual( expectedStatus, movie.Status ); - Assert.AreEqual( expectedBudget, movie.Budget ); - Assert.AreEqual( expectedRuntime, movie.Runtime ); - Assert.AreEqual( expectedReleaseDate, movie.ReleaseDate ); - - ApiResponseUtil.AssertImagePath( movie.BackdropPath ); - ApiResponseUtil.AssertImagePath( movie.PosterPath ); - - Assert.IsTrue( movie.Overview.StartsWith( expectedOverview ) ); - Assert.IsTrue( movie.Popularity > 7, $"Actual: {movie.Popularity}" ); - Assert.IsTrue( movie.VoteAverage > 5 ); - Assert.IsTrue( movie.VoteCount > 1500 ); - - // Spoken Languages - var languages = new[] - { - new Language("en", "English"), - }; - CollectionAssert.AreEqual( languages, movie.SpokenLanguages.ToArray() ); - - // Production Companies - var companies = new[] - { - new ProductionCompanyInfo(1, "Lucasfilm Ltd."), - new ProductionCompanyInfo(11461, "Bad Robot") - }; - CollectionAssert.AreEquivalent( companies, movie.ProductionCompanies.ToArray(), - $"\r\nexpected:\r\n\t{string.Join( "\r\n\t", companies.Select( x => x ) )}" + - $"\r\nactual:\r\n\t{string.Join( "\r\n\t", movie.ProductionCompanies )}" ); - - // Production Countries - var countries = new[] - { - new Country("US", "United States of America"), - }; - CollectionAssert.AreEqual( countries, movie.ProductionCountries.ToArray() ); - - // Movie Collection - Assert.IsNotNull( movie.MovieCollectionInfo ); - Assert.AreEqual( 10, movie.MovieCollectionInfo.Id ); - Assert.AreEqual( "Star Wars Collection", movie.MovieCollectionInfo.Name ); - ApiResponseUtil.AssertImagePath( movie.MovieCollectionInfo.BackdropPath ); - ApiResponseUtil.AssertImagePath( movie.MovieCollectionInfo.PosterPath ); - - // Genres - var expectedGenres = new[] - { - GenreFactory.Action(), - GenreFactory.Adventure(), - GenreFactory.ScienceFiction(), - GenreFactory.Fantasy(), - }; - CollectionAssert.AreEquivalent( expectedGenres, movie.Genres.ToArray(), - "actual:\r\n" + string.Join( "\r\n", movie.Genres ) ); - - // Keywords - var expectedKeywords = new Keyword[] - { - new(803, "android"), - new(1612, "spacecraft"), - new(161176, "space opera") - }; - CollectionAssert.AreEquivalent( expectedKeywords, movie.Keywords.ToArray(), - $"\r\nactual:\r\n\t{string.Join( "\r\n\t", expectedKeywords.Select( x => x.ToString() ) )}" + - $"\r\nactual:\r\n\t{string.Join( "\r\n\t", movie.Keywords.Select( x => x.ToString() ) )}" ); - } + new Language("en", "English"), + }; + CollectionAssert.AreEqual( languages, movie.SpokenLanguages.ToArray() ); - [TestMethod] - public async Task FindByIdAsync_Returns_German_With_LanguageParameter() + // Production Companies + var companies = new[] + { + new ProductionCompanyInfo(1, "Lucasfilm Ltd."), + new ProductionCompanyInfo(11461, "Bad Robot") + }; + CollectionAssert.AreEquivalent( companies, movie.ProductionCompanies.ToArray(), + $"\r\nexpected:\r\n\t{string.Join( "\r\n\t", companies.Select( x => x ) )}" + + $"\r\nactual:\r\n\t{string.Join( "\r\n\t", movie.ProductionCompanies )}" ); + + // Production Countries + var countries = new[] + { + new Country("US", "United States of America"), + }; + CollectionAssert.AreEqual( countries, movie.ProductionCountries.ToArray() ); + + // Movie Collection + Assert.IsNotNull( movie.MovieCollectionInfo ); + Assert.AreEqual( 10, movie.MovieCollectionInfo.Id ); + Assert.AreEqual( "Star Wars Collection", movie.MovieCollectionInfo.Name ); + ApiResponseUtil.AssertImagePath( movie.MovieCollectionInfo.BackdropPath ); + ApiResponseUtil.AssertImagePath( movie.MovieCollectionInfo.PosterPath ); + + // Genres + var expectedGenres = new[] { - const int id = 140607; - const string language = "de"; - const string expectedTitle = "Star Wars: Das Erwachen der Macht"; + GenreFactory.Action(), + GenreFactory.Adventure(), + GenreFactory.ScienceFiction(), + GenreFactory.Fantasy(), + }; + CollectionAssert.AreEquivalent( expectedGenres, movie.Genres.ToArray(), + "actual:\r\n" + string.Join( "\r\n", movie.Genres ) ); + + // Keywords + var expectedKeywords = new Keyword[] + { + new(803, "android"), + new(1612, "spacecraft"), + new(161176, "space opera") + }; + CollectionAssert.AreEquivalent( expectedKeywords, movie.Keywords.ToArray(), + $"\r\nactual:\r\n\t{string.Join( "\r\n\t", expectedKeywords.Select( x => x.ToString() ) )}" + + $"\r\nactual:\r\n\t{string.Join( "\r\n\t", movie.Keywords.Select( x => x.ToString() ) )}" ); + } - ApiQueryResponse response = await _api.FindByIdAsync( id, language ); + [TestMethod] + public async Task FindByIdAsync_Returns_German_With_LanguageParameter() + { + const int id = 140607; + const string language = "de"; + const string expectedTitle = "Star Wars: Das Erwachen der Macht"; - ApiResponseUtil.AssertErrorIsNull( response ); + ApiQueryResponse response = await _api.FindByIdAsync( id, language ); - Assert.AreEqual( id, response.Item.Id ); - Assert.AreEqual( expectedTitle, response.Item.Title ); - } + ApiResponseUtil.AssertErrorIsNull( response ); - [TestMethod] - public async Task MovieWithLargeRevenue_Will_Deserialize() - { - const int id = 19995; - const string expectedTitle = "Avatar"; + Assert.AreEqual( id, response.Item.Id ); + Assert.AreEqual( expectedTitle, response.Item.Title ); + } - ApiQueryResponse response = await _api.FindByIdAsync( id ); + [TestMethod] + public async Task MovieWithLargeRevenue_Will_Deserialize() + { + const int id = 19995; + const string expectedTitle = "Avatar"; - ApiResponseUtil.AssertErrorIsNull( response ); + ApiQueryResponse response = await _api.FindByIdAsync( id ); - Assert.AreEqual( expectedTitle, response.Item.Title ); - Assert.IsTrue( response.Item.Revenue > int.MaxValue ); - } + ApiResponseUtil.AssertErrorIsNull( response ); + Assert.AreEqual( expectedTitle, response.Item.Title ); + Assert.IsTrue( response.Item.Revenue > int.MaxValue ); + } - [TestMethod] - public async Task GetLatestAsync_Returns_ValidResult() - { - ApiQueryResponse response = await _api.GetLatestAsync(); - ApiResponseUtil.AssertErrorIsNull( response ); + [TestMethod] + public async Task GetLatestAsync_Returns_ValidResult() + { + ApiQueryResponse response = await _api.GetLatestAsync(); - ApiResponseUtil.AssertMovieStructure( response.Item ); - } + ApiResponseUtil.AssertErrorIsNull( response ); - [TestMethod] - public async Task GetNowPlayingAsync_Returns_ValidResults() - { - ApiSearchResponse response = await _api.GetNowPlayingAsync(); + ApiResponseUtil.AssertMovieStructure( response.Item ); + } - ApiResponseUtil.AssertErrorIsNull( response ); + [TestMethod] + public async Task GetNowPlayingAsync_Returns_ValidResults() + { + ApiSearchResponse response = await _api.GetNowPlayingAsync(); - ApiResponseUtil.AssertMovieStructure( response.Results ); - } + ApiResponseUtil.AssertErrorIsNull( response ); - [TestMethod] - public async Task GetNowPlayingAsync_CanPageResults() - { - // Now Playing typically has 25+ pages. - const int minimumPageCount = 5; + ApiResponseUtil.AssertMovieStructure( response.Results ); + } - await ApiResponseUtil.AssertCanPageSearchResponse( "unused", minimumPageCount, - ( _, page ) => _api.GetNowPlayingAsync( page ), x => x.Id ); - } + [TestMethod] + public async Task GetNowPlayingAsync_CanPageResults() + { + // Now Playing typically has 25+ pages. + const int minimumPageCount = 5; - [TestMethod] - public async Task GetUpcomingAsync_Returns_ValidResults() - { - ApiSearchResponse response = await _api.GetUpcomingAsync(); + await ApiResponseUtil.AssertCanPageSearchResponse( "unused", minimumPageCount, + ( _, page ) => _api.GetNowPlayingAsync( page ), x => x.Id ); + } - ApiResponseUtil.AssertErrorIsNull( response ); + [TestMethod] + public async Task GetUpcomingAsync_Returns_ValidResults() + { + ApiSearchResponse response = await _api.GetUpcomingAsync(); - ApiResponseUtil.AssertMovieStructure( response.Results ); - } + ApiResponseUtil.AssertErrorIsNull( response ); - [TestMethod] - public async Task GetUpcomingAsync_CanPageResults() - { - // Now Playing typically has 5+ pages. - // note: sometimes upcoming movies are scarce and may occasionally fail. - const int minimumPageCount = 3; + ApiResponseUtil.AssertMovieStructure( response.Results ); + } - await ApiResponseUtil.AssertCanPageSearchResponse( "unused", minimumPageCount, - ( _, page ) => _api.GetUpcomingAsync( page ), x => x.Id ); - } + [TestMethod] + public async Task GetUpcomingAsync_CanPageResults() + { + // Now Playing typically has 5+ pages. + // note: sometimes upcoming movies are scarce and may occasionally fail. + const int minimumPageCount = 3; - [TestMethod] - public async Task GetTopRatedAsync_Returns_ValidResults() - { - ApiSearchResponse response = await _api.GetTopRatedAsync(); + await ApiResponseUtil.AssertCanPageSearchResponse( "unused", minimumPageCount, + ( _, page ) => _api.GetUpcomingAsync( page ), x => x.Id ); + } - ApiResponseUtil.AssertErrorIsNull( response ); + [TestMethod] + public async Task GetTopRatedAsync_Returns_ValidResults() + { + ApiSearchResponse response = await _api.GetTopRatedAsync(); - IReadOnlyList results = response.Results; + ApiResponseUtil.AssertErrorIsNull( response ); - ApiResponseUtil.AssertMovieInformationStructure( results ); - } + IReadOnlyList results = response.Results; - [TestMethod] - public async Task GetTopRatedAsync_CanPageResults() - { - const int minimumPageCount = 2; + ApiResponseUtil.AssertMovieInformationStructure( results ); + } - await ApiResponseUtil.AssertCanPageSearchResponse( "unused", minimumPageCount, - ( _, page ) => _api.GetTopRatedAsync( page ), x => x.Id ); - } + [TestMethod] + public async Task GetTopRatedAsync_CanPageResults() + { + const int minimumPageCount = 2; - [TestMethod] - public async Task GetPopularAsync_Returns_ValidResults() - { - ApiSearchResponse response = await _api.GetPopularAsync(); + await ApiResponseUtil.AssertCanPageSearchResponse( "unused", minimumPageCount, + ( _, page ) => _api.GetTopRatedAsync( page ), x => x.Id ); + } - ApiResponseUtil.AssertErrorIsNull( response ); + [TestMethod] + public async Task GetPopularAsync_Returns_ValidResults() + { + ApiSearchResponse response = await _api.GetPopularAsync(); - IReadOnlyList results = response.Results; + ApiResponseUtil.AssertErrorIsNull( response ); - ApiResponseUtil.AssertMovieInformationStructure( results ); - } + IReadOnlyList results = response.Results; - [TestMethod] - public async Task GetPopularAsync_CanPageResults() - { - const int minimumPageCount = 2; + ApiResponseUtil.AssertMovieInformationStructure( results ); + } - await ApiResponseUtil.AssertCanPageSearchResponse( "unused", minimumPageCount, - ( _, page ) => _api.GetPopularAsync( page ), x => x.Id ); - } + [TestMethod] + public async Task GetPopularAsync_CanPageResults() + { + const int minimumPageCount = 2; + + await ApiResponseUtil.AssertCanPageSearchResponse( "unused", minimumPageCount, + ( _, page ) => _api.GetPopularAsync( page ), x => x.Id ); } } diff --git a/DM.MovieApi.IntegrationTests/MovieDb/Movies/ApiMovieRequestTests_GetCredits.cs b/DM.MovieApi.IntegrationTests/MovieDb/Movies/ApiMovieRequestTests_GetCredits.cs index f9d11f0..d799300 100644 --- a/DM.MovieApi.IntegrationTests/MovieDb/Movies/ApiMovieRequestTests_GetCredits.cs +++ b/DM.MovieApi.IntegrationTests/MovieDb/Movies/ApiMovieRequestTests_GetCredits.cs @@ -1,90 +1,83 @@ -using System.Linq; -using System.Threading.Tasks; -using DM.MovieApi.ApiResponse; -using DM.MovieApi.MovieDb.Movies; -using Microsoft.VisualStudio.TestTools.UnitTesting; +namespace DM.MovieApi.IntegrationTests.MovieDb.Movies; -namespace DM.MovieApi.IntegrationTests.MovieDb.Movies +[TestClass] +public class GetCreditsTests { - [TestClass] - public class GetCreditsTests - { - private IApiMovieRequest _api; + private IApiMovieRequest _api; - [TestInitialize] - public void TestInit() - { - ApiResponseUtil.ThrottleTests(); + [TestInitialize] + public void TestInit() + { + ApiResponseUtil.ThrottleTests(); - _api = MovieDbFactory.Create().Value; - } + _api = MovieDbFactory.Create().Value; + } - [TestMethod] - public async Task GetCreditsAsync_Returns_ValidResults() - { - const int movieIdRunLolaRun = 104; + [TestMethod] + public async Task GetCreditsAsync_Returns_ValidResults() + { + const int movieIdRunLolaRun = 104; - ApiQueryResponse response = await _api.GetCreditsAsync( movieIdRunLolaRun ); + ApiQueryResponse response = await _api.GetCreditsAsync( movieIdRunLolaRun ); - ApiResponseUtil.AssertErrorIsNull( response ); + ApiResponseUtil.AssertErrorIsNull( response ); - Assert.AreEqual( movieIdRunLolaRun, response.Item.MovieId ); - Assert.AreEqual( 23, response.Item.CastMembers.Count ); - Assert.AreEqual( 37, response.Item.CrewMembers.Count ); - } + Assert.AreEqual( movieIdRunLolaRun, response.Item.MovieId ); + Assert.AreEqual( 23, response.Item.CastMembers.Count ); + Assert.AreEqual( 37, response.Item.CrewMembers.Count ); + } - [TestMethod] - public async Task GetCreditsAsync_ReturnsCastMembers() - { - const int movieIdRunLolaRun = 104; + [TestMethod] + public async Task GetCreditsAsync_ReturnsCastMembers() + { + const int movieIdRunLolaRun = 104; - ApiQueryResponse response = await _api.GetCreditsAsync( movieIdRunLolaRun ); + ApiQueryResponse response = await _api.GetCreditsAsync( movieIdRunLolaRun ); - ApiResponseUtil.AssertErrorIsNull( response ); + ApiResponseUtil.AssertErrorIsNull( response ); - MovieCredit credit = response.Item; + MovieCredit credit = response.Item; - MovieCastMember lola = credit.CastMembers.Single( x => x.Character == "Lola" ); - Assert.AreEqual( 679, lola.PersonId ); - Assert.AreEqual( 11, lola.CastId ); - Assert.AreEqual( "52fe4218c3a36847f80038df", lola.CreditId ); - Assert.AreEqual( "Franka Potente", lola.Name ); + MovieCastMember lola = credit.CastMembers.Single( x => x.Character == "Lola" ); + Assert.AreEqual( 679, lola.PersonId ); + Assert.AreEqual( 11, lola.CastId ); + Assert.AreEqual( "52fe4218c3a36847f80038df", lola.CreditId ); + Assert.AreEqual( "Franka Potente", lola.Name ); - foreach( MovieCastMember castMember in credit.CastMembers ) - { - Assert.IsTrue( castMember.PersonId > 0 ); - Assert.IsTrue( castMember.CastId > 0 ); - Assert.IsFalse( string.IsNullOrWhiteSpace( castMember.CreditId ) ); - Assert.IsFalse( string.IsNullOrWhiteSpace( castMember.Name ) ); - Assert.IsFalse( string.IsNullOrWhiteSpace( castMember.Character ) ); - } + foreach( MovieCastMember castMember in credit.CastMembers ) + { + Assert.IsTrue( castMember.PersonId > 0 ); + Assert.IsTrue( castMember.CastId > 0 ); + Assert.IsFalse( string.IsNullOrWhiteSpace( castMember.CreditId ) ); + Assert.IsFalse( string.IsNullOrWhiteSpace( castMember.Name ) ); + Assert.IsFalse( string.IsNullOrWhiteSpace( castMember.Character ) ); } + } - [TestMethod] - public async Task GetCreditsAsync_ReturnsCrewMembers() - { - const int movieIdRunLolaRun = 104; + [TestMethod] + public async Task GetCreditsAsync_ReturnsCrewMembers() + { + const int movieIdRunLolaRun = 104; - ApiQueryResponse response = await _api.GetCreditsAsync( movieIdRunLolaRun ); + ApiQueryResponse response = await _api.GetCreditsAsync( movieIdRunLolaRun ); - ApiResponseUtil.AssertErrorIsNull( response ); + ApiResponseUtil.AssertErrorIsNull( response ); - MovieCredit credit = response.Item; + MovieCredit credit = response.Item; - MovieCrewMember director = credit.CrewMembers.Single( x => x.Job == "Director" ); - Assert.AreEqual( 1071, director.PersonId ); - Assert.AreEqual( "52fe4218c3a36847f80038ab", director.CreditId ); - Assert.AreEqual( "Directing", director.Department ); - Assert.AreEqual( "Tom Tykwer", director.Name ); + MovieCrewMember director = credit.CrewMembers.Single( x => x.Job == "Director" ); + Assert.AreEqual( 1071, director.PersonId ); + Assert.AreEqual( "52fe4218c3a36847f80038ab", director.CreditId ); + Assert.AreEqual( "Directing", director.Department ); + Assert.AreEqual( "Tom Tykwer", director.Name ); - foreach( MovieCrewMember crewMember in credit.CrewMembers ) - { - Assert.IsTrue( crewMember.PersonId > 0 ); - Assert.IsFalse( string.IsNullOrWhiteSpace( crewMember.CreditId ) ); - Assert.IsFalse( string.IsNullOrWhiteSpace( crewMember.Department ) ); - Assert.IsFalse( string.IsNullOrWhiteSpace( crewMember.Job ) ); - Assert.IsFalse( string.IsNullOrWhiteSpace( crewMember.Name ) ); - } + foreach( MovieCrewMember crewMember in credit.CrewMembers ) + { + Assert.IsTrue( crewMember.PersonId > 0 ); + Assert.IsFalse( string.IsNullOrWhiteSpace( crewMember.CreditId ) ); + Assert.IsFalse( string.IsNullOrWhiteSpace( crewMember.Department ) ); + Assert.IsFalse( string.IsNullOrWhiteSpace( crewMember.Job ) ); + Assert.IsFalse( string.IsNullOrWhiteSpace( crewMember.Name ) ); } } } diff --git a/DM.MovieApi.IntegrationTests/MovieDb/Movies/ApiMovieRequestTests_GetRecommendations.cs b/DM.MovieApi.IntegrationTests/MovieDb/Movies/ApiMovieRequestTests_GetRecommendations.cs index f995741..017aa43 100644 --- a/DM.MovieApi.IntegrationTests/MovieDb/Movies/ApiMovieRequestTests_GetRecommendations.cs +++ b/DM.MovieApi.IntegrationTests/MovieDb/Movies/ApiMovieRequestTests_GetRecommendations.cs @@ -1,45 +1,49 @@ -using System.Threading.Tasks; -using DM.MovieApi.ApiResponse; -using DM.MovieApi.MovieDb.Movies; -using Microsoft.VisualStudio.TestTools.UnitTesting; +namespace DM.MovieApi.IntegrationTests.MovieDb.Movies; -namespace DM.MovieApi.IntegrationTests.MovieDb.Movies +[TestClass] +public class GetRecommendationsTests { - [TestClass] - public class GetRecommendationsTests + private IApiMovieRequest _api; + + [TestInitialize] + public void TestInit() { - private IApiMovieRequest _api; + ApiResponseUtil.ThrottleTests(); + + _api = MovieDbFactory.Create().Value; + } - [TestInitialize] - public void TestInit() - { - ApiResponseUtil.ThrottleTests(); + [TestMethod] + public async Task GetRecommendationsAsync_Returns_ValidResults() + { + const int movieId = 104; // run lola run - _api = MovieDbFactory.Create().Value; - } + ApiSearchResponse response = await _api.GetRecommendationsAsync( movieId ); - [TestMethod] - public async Task GetRecommendationsAsync_Returns_ValidResults() - { - const int movieIdRunLolaRun = 104; + ApiResponseUtil.AssertErrorIsNull( response ); + ApiResponseUtil.AssertMovieInformationStructure( response.Results ); - ApiSearchResponse response = await _api.GetRecommendationsAsync( movieIdRunLolaRun ); + Assert.IsTrue( response.TotalPages > 1 ); + Assert.IsTrue( response.TotalResults > 20 ); + Assert.AreEqual( 1, response.PageNumber ); + } - ApiResponseUtil.AssertErrorIsNull( response ); - ApiResponseUtil.AssertMovieInformationStructure( response.Results ); + [TestMethod] + public async Task GetRecommendationsAsync_CanPageResults() + { + const int movieId = 104; // run lola run + const int minimumPageCount = 2; - Assert.IsTrue( response.TotalPages > 1 ); - Assert.IsTrue( response.TotalResults > 20 ); - Assert.AreEqual( 1, response.PageNumber ); - } + await ApiResponseUtil.AssertCanPageSearchResponse( "unused", minimumPageCount, + ( _, pageNumber ) => _api.GetRecommendationsAsync( movieId, pageNumber ), x => x.Id ); + } - [TestMethod] - public async Task GetRecommendationsAsync_HasError_InvalidMovieId() - { - const int movieId = 1; + [TestMethod] + public async Task GetRecommendationsAsync_HasError_InvalidMovieId() + { + const int movieId = 1; - ApiSearchResponse response = await _api.GetRecommendationsAsync( movieId ); - Assert.IsNotNull( response.Error ); - } + ApiSearchResponse response = await _api.GetRecommendationsAsync( movieId ); + Assert.IsNotNull( response.Error ); } } diff --git a/DM.MovieApi.IntegrationTests/MovieDb/Movies/ApiMovieRequestTests_GetSimilar.cs b/DM.MovieApi.IntegrationTests/MovieDb/Movies/ApiMovieRequestTests_GetSimilar.cs index dd9e7cb..ccf1160 100644 --- a/DM.MovieApi.IntegrationTests/MovieDb/Movies/ApiMovieRequestTests_GetSimilar.cs +++ b/DM.MovieApi.IntegrationTests/MovieDb/Movies/ApiMovieRequestTests_GetSimilar.cs @@ -1,98 +1,51 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using DM.MovieApi.ApiResponse; -using DM.MovieApi.MovieDb.Movies; -using Microsoft.VisualStudio.TestTools.UnitTesting; +namespace DM.MovieApi.IntegrationTests.MovieDb.Movies; -namespace DM.MovieApi.IntegrationTests.MovieDb.Movies +[TestClass] +public class GetSimilarTests { - [TestClass] - public class GetSimilarTests - { - private IApiMovieRequest _api; - - [TestInitialize] - public void TestInit() - { - ApiResponseUtil.ThrottleTests(); - - _api = MovieDbFactory.Create().Value; - } - - [TestMethod] - public async Task GetSimilarAsync_Returns_ValidResults() - { - const int movieIdRunLolaRun = 104; - - ApiSearchResponse response = await _api.GetSimilarAsync( movieIdRunLolaRun ); - - ApiResponseUtil.AssertErrorIsNull( response ); - ApiResponseUtil.AssertMovieInformationStructure( response.Results ); - - // get similar will return the max number of results - Assert.AreEqual( 20, response.Results.Count ); - Assert.AreEqual( 500, response.TotalPages ); - Assert.AreEqual( 10000, response.TotalResults ); - Assert.AreEqual( 1, response.PageNumber ); - } - - [TestMethod] - public async Task GetSimilarAsync_CanPage() - { - const int movieIdRunLolaRun = 104; + private IApiMovieRequest _api; - // todo: move to ApiResponseUtil and refactor w/ existing AssertCanPageSearchResponse - // * each component may be able to become re-usable methods + [TestInitialize] + public void TestInit() + { + ApiResponseUtil.ThrottleTests(); - int num = 0; - var ids = new HashSet(); - var dups = new List(); + _api = MovieDbFactory.Create().Value; + } - for( int i = 1; i <= 10; i++ ) - { - int pageNumber = i; - ApiSearchResponse response = await _api.GetSimilarAsync( movieIdRunLolaRun, pageNumber ); + [TestMethod] + public async Task GetSimilarAsync_Returns_ValidResults() + { + const int movieIdRunLolaRun = 104; - ApiResponseUtil.AssertErrorIsNull( response ); - ApiResponseUtil.AssertMovieInformationStructure( response.Results ); + ApiSearchResponse response = await _api.GetSimilarAsync( movieIdRunLolaRun ); - // get similar will return the max number of results - Assert.AreEqual( 20, response.Results.Count ); - Assert.AreEqual( 500, response.TotalPages ); - Assert.AreEqual( 10000, response.TotalResults ); - Assert.AreEqual( pageNumber, response.PageNumber ); + ApiResponseUtil.AssertErrorIsNull( response ); + ApiResponseUtil.AssertMovieInformationStructure( response.Results ); - foreach( MovieInfo m in response.Results ) - { - num++; - if( ids.Add( m.Id ) == false ) - { - dups.Add( m.ToString() ); - } - } - } + // get similar will return the max number of results + Assert.AreEqual( 20, response.Results.Count ); + Assert.AreEqual( 500, response.TotalPages ); + Assert.AreEqual( 10000, response.TotalResults ); + Assert.AreEqual( 1, response.PageNumber ); + } - // api tends to return duplicate results when paging - // shouldn't be more than 2 or 3 per page; at 20 per page, - // that's approximately 4-6; let's target 25% - int min = (int)(num * 0.75); - var d = dups - .GroupBy( x => x ) - .Select( x => $"{x.Key} (x {x.Count()})" ); - System.Diagnostics.Trace.WriteLine( $"Results: {num}, Dups: {dups.Count}" + - $"\r\n{string.Join( "\r\n", d )}" ); + [TestMethod] + public async Task GetSimilarAsync_CanPageResults() + { + const int movieId = 104; // run lola run + const int minimumPageCount = 10; - Assert.IsTrue( ids.Count >= min, $"Total: {num}.\r\nUnique: {ids.Count}, Dup Threshold {min}" ); - } + await ApiResponseUtil.AssertCanPageSearchResponse( "unused", minimumPageCount, + ( _, pageNumber ) => _api.GetSimilarAsync( movieId, pageNumber ), x => x.Id ); + } - [TestMethod] - public async Task GetSimilarAsync_HasError_InvalidMovieId() - { - const int movieId = 1; + [TestMethod] + public async Task GetSimilarAsync_HasError_InvalidMovieId() + { + const int movieId = 1; - ApiSearchResponse response = await _api.GetSimilarAsync( movieId ); - Assert.IsNotNull( response.Error ); - } + ApiSearchResponse response = await _api.GetSimilarAsync( movieId ); + Assert.IsNotNull( response.Error ); } } diff --git a/DM.MovieApi.IntegrationTests/MovieDb/People/ApiPeopleRequestTests.cs b/DM.MovieApi.IntegrationTests/MovieDb/People/ApiPeopleRequestTests.cs index 4632a53..8475f6d 100644 --- a/DM.MovieApi.IntegrationTests/MovieDb/People/ApiPeopleRequestTests.cs +++ b/DM.MovieApi.IntegrationTests/MovieDb/People/ApiPeopleRequestTests.cs @@ -1,334 +1,325 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using DM.MovieApi.ApiResponse; -using DM.MovieApi.MovieDb.People; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace DM.MovieApi.IntegrationTests.MovieDb.People +namespace DM.MovieApi.IntegrationTests.MovieDb.People; + +[TestClass] +public class ApiPeopleRequestTests { - [TestClass] - public class ApiPeopleRequestTests + // ReSharper disable InconsistentNaming + public const int PersonId_MillaJovovich = 63; + public const int PersonId_KevinBacon = 4724; + public const int PersonId_CourteneyCox = 14405; + // ReSharper restore InconsistentNaming + + private IApiPeopleRequest _api; + + [TestInitialize] + public void TestInit() { - // ReSharper disable InconsistentNaming - const int PersonId_MillaJovovich = 63; - const int PersonId_KevinBacon = 4724; - const int PersonId_CourteneyCox = 14405; - // ReSharper restore InconsistentNaming + ApiResponseUtil.ThrottleTests(); + + _api = MovieDbFactory.Create().Value; - private IApiPeopleRequest _api; + Assert.IsInstanceOfType( _api, typeof( ApiPeopleRequest ) ); + } + + [TestMethod] + public async Task FindByIdAsync_MillaJovovich_Returns_ExpectedValues() + { + const string expectedName = "Milla Jovovich"; + const string expectedBiography = "Milla Jovovich (born December 17, 1975) is an Ukrainian-born American actress, supermodel, musician, and fashion designer."; // truncated + DateTime expectedBirthday = DateTime.Parse( "1975-12-17" ); + const Gender expectedGender = Gender.Female; + const string expectedHomepage = "http://www.millaj.com"; + const string expectedImdbId = "nm0000170"; + const string expectedPlaceOfBirth = "Kiev, Ukraine"; + + ApiQueryResponse response = await _api.FindByIdAsync( PersonId_MillaJovovich ); + + ApiResponseUtil.AssertErrorIsNull( response ); + + Person person = response.Item; + + Assert.AreEqual( expectedName, person.Name ); + Assert.IsTrue( person.Biography.StartsWith( expectedBiography ), $"actual: {person.Biography}" ); + Assert.IsFalse( person.IsAdultFilmStar ); + Assert.IsTrue( person.AlsoKnownAs.Count > 10, "actual:\r\n" + string.Join( "\r\n", person.AlsoKnownAs ) ); + Assert.AreEqual( expectedBirthday, person.Birthday ); + Assert.AreEqual( expectedGender, person.Gender ); + Assert.AreEqual( null, person.Deathday ); + Assert.AreEqual( expectedHomepage, person.Homepage ); + Assert.AreEqual( expectedImdbId, person.ImdbId ); + Assert.AreEqual( expectedPlaceOfBirth, person.PlaceOfBirth ); + Assert.IsTrue( person.Popularity > 5, $"Actual: {person.Popularity}" ); + Assert.IsNotNull( person.ProfilePath ); + } - [TestInitialize] - public void TestInit() + [TestMethod] + public async Task FindByIdAsync_KevinBacon_Returns_ExpectedValues() + { + const string expectedName = "Kevin Bacon"; + const string expectedBiography = "Kevin Norwood Bacon (born July 8, 1958) is an American film and theater actor"; // truncated + DateTime expectedBirthday = DateTime.Parse( "1958-07-08" ); + const Gender expectedGender = Gender.Male; + const string expectedHomepage = "http://www.baconbros.com"; + const string expectedImdbId = "nm0000102"; + const string expectedPlaceOfBirth = "Philadelphia, Pennsylvania, USA"; + + string[] alsoKnownAs = { - ApiResponseUtil.ThrottleTests(); + "Kevin Norwood Bacon", + }; - _api = MovieDbFactory.Create().Value; + ApiQueryResponse response = await _api.FindByIdAsync( PersonId_KevinBacon ); - Assert.IsInstanceOfType( _api, typeof( ApiPeopleRequest ) ); - } + ApiResponseUtil.AssertErrorIsNull( response ); - [TestMethod] - public async Task FindByIdAsync_MillaJovovich_Returns_ExpectedValues() - { - const string expectedName = "Milla Jovovich"; - const string expectedBiography = "Milla Jovovich (born December 17, 1975) is an Ukrainian-born American actress, supermodel, musician, and fashion designer."; // truncated - DateTime expectedBirthday = DateTime.Parse( "1975-12-17" ); - const Gender expectedGender = Gender.Female; - const string expectedHomepage = "http://www.millaj.com"; - const string expectedImdbId = "nm0000170"; - const string expectedPlaceOfBirth = "Kiev, Ukraine"; - - ApiQueryResponse response = await _api.FindByIdAsync( PersonId_MillaJovovich ); - - ApiResponseUtil.AssertErrorIsNull( response ); - - Person person = response.Item; - - Assert.AreEqual( expectedName, person.Name ); - Assert.IsTrue( person.Biography.StartsWith( expectedBiography ), $"actual: {person.Biography}" ); - Assert.IsFalse( person.IsAdultFilmStar ); - Assert.IsTrue( person.AlsoKnownAs.Count > 10, "actual:\r\n" + string.Join( "\r\n", person.AlsoKnownAs ) ); - Assert.AreEqual( expectedBirthday, person.Birthday ); - Assert.AreEqual( expectedGender, person.Gender ); - Assert.AreEqual( null, person.Deathday ); - Assert.AreEqual( expectedHomepage, person.Homepage ); - Assert.AreEqual( expectedImdbId, person.ImdbId ); - Assert.AreEqual( expectedPlaceOfBirth, person.PlaceOfBirth ); - Assert.IsTrue( person.Popularity > 5, $"Actual: {person.Popularity}" ); - Assert.IsNotNull( person.ProfilePath ); - } + Person person = response.Item; + + Assert.AreEqual( expectedName, person.Name ); + Assert.IsTrue( person.Biography.StartsWith( expectedBiography ), $"Actual Biography: {person.Biography}" ); + Assert.IsFalse( person.IsAdultFilmStar ); + Assert.AreEqual( expectedBirthday, person.Birthday ); + Assert.AreEqual( expectedGender, person.Gender ); + Assert.AreEqual( null, person.Deathday ); + Assert.AreEqual( expectedHomepage, person.Homepage ); + Assert.AreEqual( expectedImdbId, person.ImdbId ); + Assert.AreEqual( expectedPlaceOfBirth, person.PlaceOfBirth ); + Assert.IsTrue( person.Popularity > 3 ); + Assert.IsNotNull( person.ProfilePath ); + + CollectionAssert.IsSubsetOf( alsoKnownAs, person.AlsoKnownAs.ToArray(), + "actual:\r\n" + string.Join( "\r\n", person.AlsoKnownAs ) ); + } - [TestMethod] - public async Task FindByIdAsync_KevinBacon_Returns_ExpectedValues() + [TestMethod] + public async Task FindByIdAsync_CourteneyCox_Returns_ExpectedValues() + { + const string expectedName = "Courteney Cox"; + const string expectedBiography = "Courteney Bass Cox (previously Courteney Cox-Arquette; born June 15, 1964) " + + "is an American actress, director, and producer."; // truncated + DateTime expectedBirthday = DateTime.Parse( "1964-06-15" ); + const Gender expectedGender = Gender.Female; + const string expectedHomepage = null; + const string expectedImdbId = "nm0001073"; + const string expectedPlaceOfBirth = "Birmingham, Alabama, USA"; + + string[] alsoKnownAs = { - const string expectedName = "Kevin Bacon"; - const string expectedBiography = "Kevin Norwood Bacon (born July 8, 1958) is an American film and theater actor"; // truncated - DateTime expectedBirthday = DateTime.Parse( "1958-07-08" ); - const Gender expectedGender = Gender.Male; - const string expectedHomepage = "http://www.baconbros.com"; - const string expectedImdbId = "nm0000102"; - const string expectedPlaceOfBirth = "Philadelphia, Pennsylvania, USA"; - - string[] alsoKnownAs = - { - "Kevin Norwood Bacon", - }; - - ApiQueryResponse response = await _api.FindByIdAsync( PersonId_KevinBacon ); - - ApiResponseUtil.AssertErrorIsNull( response ); - - Person person = response.Item; - - Assert.AreEqual( expectedName, person.Name ); - Assert.IsTrue( person.Biography.StartsWith( expectedBiography ), $"Actual Biography: {person.Biography}" ); - Assert.IsFalse( person.IsAdultFilmStar ); - Assert.AreEqual( expectedBirthday, person.Birthday ); - Assert.AreEqual( expectedGender, person.Gender ); - Assert.AreEqual( null, person.Deathday ); - Assert.AreEqual( expectedHomepage, person.Homepage ); - Assert.AreEqual( expectedImdbId, person.ImdbId ); - Assert.AreEqual( expectedPlaceOfBirth, person.PlaceOfBirth ); - Assert.IsTrue( person.Popularity > 3 ); - Assert.IsNotNull( person.ProfilePath ); - - CollectionAssert.IsSubsetOf( alsoKnownAs, person.AlsoKnownAs.ToArray(), - "actual:\r\n" + string.Join( "\r\n", person.AlsoKnownAs ) ); - } + "CeCe", + "Coco", + "Courtney Cox", + }; + + ApiQueryResponse response = await _api.FindByIdAsync( PersonId_CourteneyCox ); + + ApiResponseUtil.AssertErrorIsNull( response ); + + Person person = response.Item; + + Assert.AreEqual( expectedName, person.Name ); + Assert.IsTrue( person.Biography.StartsWith( expectedBiography ), $"Actual: {person.Biography}" ); + Assert.IsFalse( person.IsAdultFilmStar ); + Assert.AreEqual( expectedBirthday, person.Birthday ); + Assert.AreEqual( expectedGender, person.Gender ); + Assert.AreEqual( null, person.Deathday ); + Assert.AreEqual( expectedHomepage, person.Homepage ); + Assert.AreEqual( expectedImdbId, person.ImdbId ); + Assert.AreEqual( expectedPlaceOfBirth, person.PlaceOfBirth ); + Assert.IsTrue( person.Popularity > 3 ); + Assert.IsNotNull( person.ProfilePath ); + + CollectionAssert.IsSubsetOf( alsoKnownAs, person.AlsoKnownAs.ToArray(), + "actual:\r\n" + string.Join( "\r\n", person.AlsoKnownAs ) ); + } + + [TestMethod] + public async Task GetMovieCreditsAsync_KevinBacon_Returns_ExpectedValues() + { + ApiQueryResponse response = await _api.GetMovieCreditsAsync( PersonId_KevinBacon ); + + ApiResponseUtil.AssertErrorIsNull( response ); + + PersonMovieCredit credits = response.Item; + + Assert.AreEqual( PersonId_KevinBacon, credits.PersonId ); + Assert.IsTrue( credits.CastRoles.Count > 70, $"Actual Count: {credits.CastRoles.Count}" ); + Assert.IsTrue( credits.CrewRoles.Count >= 5, $"Actual Count: {credits.CrewRoles.Count}" ); + + PersonMovieCastMember castMember = credits.CastRoles.SingleOrDefault( x => x.Title == "Footloose" ); + Assert.IsNotNull( castMember ); + + Assert.AreEqual( 1788, castMember.MovieId ); + Assert.IsFalse( castMember.IsAdultThemed ); + Assert.AreEqual( "Footloose", castMember.Title ); + Assert.AreEqual( "Footloose", castMember.OriginalTitle ); + Assert.AreEqual( "Ren McCormack", castMember.Character ); + Assert.AreEqual( "52fe4315c3a36847f8038fc3", castMember.CreditId ); + Assert.AreEqual( DateTime.Parse( "1984-02-17" ), castMember.ReleaseDate ); + Assert.IsNotNull( castMember.PosterPath ); + + + PersonMovieCrewMember crewMember = credits.CrewRoles.SingleOrDefault( x => x.Title == "Wild Things" ); + Assert.IsNotNull( crewMember ); + + Assert.AreEqual( 617, crewMember.MovieId ); + Assert.IsFalse( crewMember.IsAdultThemed ); + Assert.AreEqual( "Wild Things", crewMember.Title ); + Assert.AreEqual( "Wild Things", crewMember.OriginalTitle ); + Assert.AreEqual( "Production", crewMember.Department ); + Assert.AreEqual( "Executive Producer", crewMember.Job ); + Assert.AreEqual( "52fe425ec3a36847f8018f2d", crewMember.CreditId ); + Assert.IsNotNull( crewMember.PosterPath ); + Assert.AreEqual( DateTime.Parse( "1998-03-20" ), crewMember.ReleaseDate ); + - [TestMethod] - public async Task FindByIdAsync_CourteneyCox_Returns_ExpectedValues() + var expectedCastRoles = new Dictionary { - const string expectedName = "Courteney Cox"; - const string expectedBiography = "Courteney Bass Cox (previously Courteney Cox-Arquette; born June 15, 1964) " + - "is an American actress, director, and producer."; // truncated - DateTime expectedBirthday = DateTime.Parse( "1964-06-15" ); - const Gender expectedGender = Gender.Female; - const string expectedHomepage = null; - const string expectedImdbId = "nm0001073"; - const string expectedPlaceOfBirth = "Birmingham, Alabama, USA"; - - string[] alsoKnownAs = - { - "CeCe", - "Coco", - "Courtney Cox", - }; - - ApiQueryResponse response = await _api.FindByIdAsync( PersonId_CourteneyCox ); - - ApiResponseUtil.AssertErrorIsNull( response ); - - Person person = response.Item; - - Assert.AreEqual( expectedName, person.Name ); - Assert.IsTrue( person.Biography.StartsWith( expectedBiography ), $"Actual: {person.Biography}" ); - Assert.IsFalse( person.IsAdultFilmStar ); - Assert.AreEqual( expectedBirthday, person.Birthday ); - Assert.AreEqual( expectedGender, person.Gender ); - Assert.AreEqual( null, person.Deathday ); - Assert.AreEqual( expectedHomepage, person.Homepage ); - Assert.AreEqual( expectedImdbId, person.ImdbId ); - Assert.AreEqual( expectedPlaceOfBirth, person.PlaceOfBirth ); - Assert.IsTrue( person.Popularity > 3 ); - Assert.IsNotNull( person.ProfilePath ); - - CollectionAssert.IsSubsetOf( alsoKnownAs, person.AlsoKnownAs.ToArray(), - "actual:\r\n" + string.Join( "\r\n", person.AlsoKnownAs ) ); + {"Footloose", "Ren McCormack"}, + {"Animal House", "Chip Diller"}, + {"Flatliners", "David Labraccio"}, + {"Wild Things", "Sergeant Ray Duquette"}, + {"Tremors", "Valentine McKee"} + }; + foreach( KeyValuePair role in expectedCastRoles ) + { + PersonMovieCastMember cast = credits + .CastRoles + .SingleOrDefault( x => x.Title == role.Key && x.Character == role.Value ); + + Assert.IsNotNull( cast, $"missing: {role.Key} : {role.Value} \r\nactual:\r\n" + + string.Join( "\r\n", credits.CastRoles.Select( x => $"{x.Title} : {x.Character}" ) ) ); } - [TestMethod] - public async Task GetMovieCreditsAsync_KevinBacon_Returns_ExpectedValues() + var expectedCrewRoles = new Dictionary { - ApiQueryResponse response = await _api.GetMovieCreditsAsync( PersonId_KevinBacon ); - - ApiResponseUtil.AssertErrorIsNull( response ); - - PersonMovieCredit credits = response.Item; - - Assert.AreEqual( PersonId_KevinBacon, credits.PersonId ); - Assert.IsTrue( credits.CastRoles.Count > 70, $"Actual Count: {credits.CastRoles.Count}" ); - Assert.IsTrue( credits.CrewRoles.Count >= 5, $"Actual Count: {credits.CrewRoles.Count}" ); - - PersonMovieCastMember castMember = credits.CastRoles.SingleOrDefault( x => x.Title == "Footloose" ); - Assert.IsNotNull( castMember ); - - Assert.AreEqual( 1788, castMember.MovieId ); - Assert.IsFalse( castMember.IsAdultThemed ); - Assert.AreEqual( "Footloose", castMember.Title ); - Assert.AreEqual( "Footloose", castMember.OriginalTitle ); - Assert.AreEqual( "Ren McCormack", castMember.Character ); - Assert.AreEqual( "52fe4315c3a36847f8038fc3", castMember.CreditId ); - Assert.AreEqual( DateTime.Parse( "1984-02-17" ), castMember.ReleaseDate ); - Assert.IsNotNull( castMember.PosterPath ); - - - PersonMovieCrewMember crewMember = credits.CrewRoles.SingleOrDefault( x => x.Title == "Wild Things" ); - Assert.IsNotNull( crewMember ); - - Assert.AreEqual( 617, crewMember.MovieId ); - Assert.IsFalse( crewMember.IsAdultThemed ); - Assert.AreEqual( "Wild Things", crewMember.Title ); - Assert.AreEqual( "Wild Things", crewMember.OriginalTitle ); - Assert.AreEqual( "Production", crewMember.Department ); - Assert.AreEqual( "Executive Producer", crewMember.Job ); - Assert.AreEqual( "52fe425ec3a36847f8018f2d", crewMember.CreditId ); - Assert.IsNotNull( crewMember.PosterPath ); - Assert.AreEqual( DateTime.Parse( "1998-03-20" ), crewMember.ReleaseDate ); - - - var expectedCastRoles = new Dictionary - { - {"Footloose", "Ren McCormack"}, - {"Animal House", "Chip Diller"}, - {"Flatliners", "David Labraccio"}, - {"Wild Things", "Sergeant Ray Duquette"}, - {"Tremors", "Valentine McKee"} - }; - foreach( KeyValuePair role in expectedCastRoles ) - { - PersonMovieCastMember cast = credits - .CastRoles - .SingleOrDefault( x => x.Title == role.Key && x.Character == role.Value ); - - Assert.IsNotNull( cast, $"missing: {role.Key} : {role.Value} \r\nactual:\r\n" + - string.Join( "\r\n", credits.CastRoles.Select( x => $"{x.Title} : {x.Character}" ) ) ); - } - - var expectedCrewRoles = new Dictionary - { - {"Losing Chase", "Director"}, - {"Loverboy", "Director"}, - {"Wild Things", "Executive Producer"}, - {"The Woodsman", "Executive Producer"}, - {"Cop Car", "Executive Producer"}, - }; - foreach( KeyValuePair role in expectedCrewRoles ) - { - PersonMovieCrewMember cast = credits - .CrewRoles - .SingleOrDefault( x => x.Title == role.Key && x.Job == role.Value ); - - Assert.IsNotNull( cast ); - } + {"Losing Chase", "Director"}, + {"Loverboy", "Director"}, + {"Wild Things", "Executive Producer"}, + {"The Woodsman", "Executive Producer"}, + {"Cop Car", "Executive Producer"}, + }; + foreach( KeyValuePair role in expectedCrewRoles ) + { + PersonMovieCrewMember cast = credits + .CrewRoles + .SingleOrDefault( x => x.Title == role.Key && x.Job == role.Value ); + + Assert.IsNotNull( cast ); } + } + + [TestMethod] + public async Task GetTVCreditsAsync_CourteneyCox_Returns_ExpectedValues() + { + ApiQueryResponse response = await _api.GetTVCreditsAsync( PersonId_CourteneyCox ); + + ApiResponseUtil.AssertErrorIsNull( response ); + + PersonTVCredit credits = response.Item; + + Assert.AreEqual( PersonId_CourteneyCox, credits.PersonId ); + Assert.IsTrue( credits.CastRoles.Count > 20, $"Actual Count: {credits.CastRoles.Count}" ); + Assert.IsTrue( credits.CrewRoles.Count >= 3, $"Actual Count: {credits.CrewRoles.Count}" ); + + PersonTVCastMember castMember = credits.CastRoles.SingleOrDefault( x => x.Name == "Friends" ); + Assert.IsNotNull( castMember ); + + Assert.AreEqual( 1668, castMember.TVShowId ); + Assert.AreEqual( "525710bc19c295731c032341", castMember.CreditId ); + Assert.AreEqual( 236, castMember.EpisodeCount ); + Assert.AreEqual( DateTime.Parse( "1994-09-22" ), castMember.FirstAirDate ); + Assert.AreEqual( "Friends", castMember.Name ); + Assert.AreEqual( "Friends", castMember.OriginalName ); + Assert.AreEqual( "Monica Geller", castMember.Character ); + Assert.IsNotNull( castMember.PosterPath ); + + + PersonTVCrewMember crewMember = credits.CrewRoles.SingleOrDefault( x => x.Name == "Dirt" ); + Assert.IsNotNull( crewMember ); - [TestMethod] - public async Task GetTVCreditsAsync_CourteneyCox_Returns_ExpectedValues() + Assert.AreEqual( 284, crewMember.TVShowId ); + Assert.AreEqual( "Production", crewMember.Department ); + Assert.AreEqual( 20, crewMember.EpisodeCount ); + Assert.AreEqual( DateTime.Parse( "2007-01-02" ), crewMember.FirstAirDate ); + Assert.AreEqual( "Producer", crewMember.Job ); + Assert.AreEqual( "Dirt", crewMember.Name ); + Assert.AreEqual( "Dirt", crewMember.OriginalName ); + Assert.AreEqual( "52534b4119c29579400f66b9", crewMember.CreditId ); + Assert.IsNotNull( crewMember.PosterPath ); + + + var expectedCastRoles = new Dictionary { - ApiQueryResponse response = await _api.GetTVCreditsAsync( PersonId_CourteneyCox ); - - ApiResponseUtil.AssertErrorIsNull( response ); - - PersonTVCredit credits = response.Item; - - Assert.AreEqual( PersonId_CourteneyCox, credits.PersonId ); - Assert.IsTrue( credits.CastRoles.Count > 20, $"Actual Count: {credits.CastRoles.Count}" ); - Assert.IsTrue( credits.CrewRoles.Count >= 3, $"Actual Count: {credits.CrewRoles.Count}" ); - - PersonTVCastMember castMember = credits.CastRoles.SingleOrDefault( x => x.Name == "Friends" ); - Assert.IsNotNull( castMember ); - - Assert.AreEqual( 1668, castMember.TVShowId ); - Assert.AreEqual( "525710bc19c295731c032341", castMember.CreditId ); - Assert.AreEqual( 236, castMember.EpisodeCount ); - Assert.AreEqual( DateTime.Parse( "1994-09-22" ), castMember.FirstAirDate ); - Assert.AreEqual( "Friends", castMember.Name ); - Assert.AreEqual( "Friends", castMember.OriginalName ); - Assert.AreEqual( "Monica Geller", castMember.Character ); - Assert.IsNotNull( castMember.PosterPath ); - - - PersonTVCrewMember crewMember = credits.CrewRoles.SingleOrDefault( x => x.Name == "Dirt" ); - Assert.IsNotNull( crewMember ); - - Assert.AreEqual( 284, crewMember.TVShowId ); - Assert.AreEqual( "Production", crewMember.Department ); - Assert.AreEqual( 20, crewMember.EpisodeCount ); - Assert.AreEqual( DateTime.Parse( "2007-01-02" ), crewMember.FirstAirDate ); - Assert.AreEqual( "Producer", crewMember.Job ); - Assert.AreEqual( "Dirt", crewMember.Name ); - Assert.AreEqual( "Dirt", crewMember.OriginalName ); - Assert.AreEqual( "52534b4119c29579400f66b9", crewMember.CreditId ); - Assert.IsNotNull( crewMember.PosterPath ); - - - var expectedCastRoles = new Dictionary - { - {"Friends", "Monica Geller"}, - {"The Trouble with Larry", "Gabriella Easden"}, - {"Misfits of Science", "Gloria Dinallo"}, - {"Cougar Town", "Jules Cobb"}, - }; - foreach( KeyValuePair role in expectedCastRoles ) - { - PersonTVCastMember cast = credits - .CastRoles - .SingleOrDefault( x => x.Name == role.Key && x.Character == role.Value ); - - Assert.IsNotNull( cast ); - } - - var expectedCrewRoles = new Dictionary - { - {"Cougar Town", "Producer"}, - {"Dirt", "Producer"}, - {"Daisy Does America", "Producer"}, - }; - foreach( KeyValuePair role in expectedCrewRoles ) - { - PersonTVCrewMember cast = credits - .CrewRoles - .SingleOrDefault( x => x.Name == role.Key && x.Job == role.Value ); - - Assert.IsNotNull( cast ); - } + {"Friends", "Monica Geller"}, + {"The Trouble with Larry", "Gabriella Easden"}, + {"Misfits of Science", "Gloria Dinallo"}, + {"Cougar Town", "Jules Cobb"}, + }; + foreach( KeyValuePair role in expectedCastRoles ) + { + PersonTVCastMember cast = credits + .CastRoles + .SingleOrDefault( x => x.Name == role.Key && x.Character == role.Value ); + + Assert.IsNotNull( cast ); } - [TestMethod] - public async Task SearchByNameAsync_Milla_Jovovich_Returns_SingleResult_WithExpectedValues() + var expectedCrewRoles = new Dictionary { - const string millaJovovich = "Milla Jovovich"; + {"Cougar Town", "Producer"}, + {"Dirt", "Producer"}, + {"Daisy Does America", "Producer"}, + }; + foreach( KeyValuePair role in expectedCrewRoles ) + { + PersonTVCrewMember cast = credits + .CrewRoles + .SingleOrDefault( x => x.Name == role.Key && x.Job == role.Value ); - ApiSearchResponse response = await _api.SearchByNameAsync( millaJovovich ); + Assert.IsNotNull( cast ); + } + } - ApiResponseUtil.AssertErrorIsNull( response ); + [TestMethod] + public async Task SearchByNameAsync_Milla_Jovovich_Returns_SingleResult_WithExpectedValues() + { + const string millaJovovich = "Milla Jovovich"; - Assert.AreEqual( 1, response.TotalResults ); - Assert.AreEqual( 1, response.Results.Count ); + ApiSearchResponse response = await _api.SearchByNameAsync( millaJovovich ); - PersonInfo person = response.Results.Single(); + ApiResponseUtil.AssertErrorIsNull( response ); - Assert.AreEqual( PersonId_MillaJovovich, person.Id ); - Assert.AreEqual( millaJovovich, person.Name ); - Assert.IsFalse( person.IsAdultFilmStar ); - Assert.AreEqual( 3, person.KnownFor.Count ); + Assert.AreEqual( 1, response.TotalResults ); + Assert.AreEqual( 1, response.Results.Count ); - string[] roles = - { - "The Fifth Element", - "Resident Evil", - "Resident Evil: Apocalypse" - }; + PersonInfo person = response.Results.Single(); - foreach( string role in roles ) - { - PersonInfoRole info = person.KnownFor.SingleOrDefault( x => x.MovieTitle == role ); - Assert.IsNotNull( info, string.Join( ", ", person.KnownFor ) ); - Assert.AreEqual( MediaType.Movie, info.MediaType ); - } - } + Assert.AreEqual( PersonId_MillaJovovich, person.Id ); + Assert.AreEqual( millaJovovich, person.Name ); + Assert.IsFalse( person.IsAdultFilmStar ); + Assert.AreEqual( 3, person.KnownFor.Count ); - [TestMethod] - public async Task SearchByNameAsync_CanPageResults() + string[] roles = { - const string query = "Cox"; - const int minimumPageCount = 15; + "The Fifth Element", + "Resident Evil", + "Resident Evil: Apocalypse" + }; - await ApiResponseUtil.AssertCanPageSearchResponse( query, minimumPageCount, - ( search, pageNumber ) => _api.SearchByNameAsync( search, pageNumber ), null ); + foreach( string role in roles ) + { + PersonInfoRole info = person.KnownFor.SingleOrDefault( x => x.MovieTitle == role ); + Assert.IsNotNull( info, string.Join( ", ", person.KnownFor ) ); + Assert.AreEqual( MediaType.Movie, info.MediaType ); } } + + [TestMethod] + public async Task SearchByNameAsync_CanPageResults() + { + const string query = "Cox"; + const int minimumPageCount = 15; + + await ApiResponseUtil.AssertCanPageSearchResponse( query, minimumPageCount, + ( search, pageNumber ) => _api.SearchByNameAsync( search, pageNumber ), null ); + } } diff --git a/DM.MovieApi.IntegrationTests/MovieDb/Professions/ApiProfessionRequestTests.cs b/DM.MovieApi.IntegrationTests/MovieDb/Professions/ApiProfessionRequestTests.cs index 8eb4fc9..5f4a15a 100644 --- a/DM.MovieApi.IntegrationTests/MovieDb/Professions/ApiProfessionRequestTests.cs +++ b/DM.MovieApi.IntegrationTests/MovieDb/Professions/ApiProfessionRequestTests.cs @@ -1,47 +1,42 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using DM.MovieApi.ApiResponse; -using DM.MovieApi.MovieDb.IndustryProfessions; -using Microsoft.VisualStudio.TestTools.UnitTesting; +using DM.MovieApi.MovieDb.IndustryProfessions; -namespace DM.MovieApi.IntegrationTests.MovieDb.Professions +namespace DM.MovieApi.IntegrationTests.MovieDb.Professions; + +[TestClass] +public class ApiProfessionRequestTests { - [TestClass] - public class ApiProfessionRequestTests + private IApiProfessionRequest _api; + + [TestInitialize] + public void TestInit() { - private IApiProfessionRequest _api; + ApiResponseUtil.ThrottleTests(); - [TestInitialize] - public void TestInit() - { - ApiResponseUtil.ThrottleTests(); + _api = MovieDbFactory.Create().Value; - _api = MovieDbFactory.Create().Value; + Assert.IsInstanceOfType( _api, typeof( ApiProfessionRequest ) ); + } - Assert.IsInstanceOfType( _api, typeof( ApiProfessionRequest ) ); - } + [TestMethod] + public async Task GetAllAsync_Returns_ValidResults() + { + const int expectedCount = 12; - [TestMethod] - public async Task GetAllAsync_Returns_ValidResults() - { - const int expectedCount = 12; + ApiQueryResponse> response = await _api.GetAllAsync(); - ApiQueryResponse> response = await _api.GetAllAsync(); + ApiResponseUtil.AssertErrorIsNull( response ); - ApiResponseUtil.AssertErrorIsNull( response ); + Assert.AreEqual( expectedCount, response.Item.Count ); - Assert.AreEqual( expectedCount, response.Item.Count ); + foreach( Profession pro in response.Item ) + { + Assert.IsTrue( pro.Department.Length >= 3, pro.Department ); // Art + Assert.IsNotNull( pro.Jobs, $"Job Dept: {pro.Department}" ); + Assert.IsTrue( pro.Jobs.Count >= 5, $"Actual Count: {pro.Jobs.Count}" ); - foreach( Profession pro in response.Item ) + foreach( string job in pro.Jobs ) { - Assert.IsTrue( pro.Department.Length >= 3, pro.Department ); // Art - Assert.IsNotNull( pro.Jobs, $"Job Dept: {pro.Department}" ); - Assert.IsTrue( pro.Jobs.Count >= 5, $"Actual Count: {pro.Jobs.Count}" ); - - foreach( string job in pro.Jobs ) - { - Assert.IsTrue( job.Length >= 4, job ); // Idea - } + Assert.IsTrue( job.Length >= 4, job ); // Idea } } } diff --git a/DM.MovieApi.IntegrationTests/MovieDb/TV/ApiTVShowRequestTests.cs b/DM.MovieApi.IntegrationTests/MovieDb/TV/ApiTVShowRequestTests.cs index 4a5f395..65e33a4 100644 --- a/DM.MovieApi.IntegrationTests/MovieDb/TV/ApiTVShowRequestTests.cs +++ b/DM.MovieApi.IntegrationTests/MovieDb/TV/ApiTVShowRequestTests.cs @@ -1,305 +1,292 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Threading.Tasks; -using DM.MovieApi.ApiResponse; -using DM.MovieApi.MovieDb.Companies; -using DM.MovieApi.MovieDb.Genres; -using DM.MovieApi.MovieDb.Keywords; -using DM.MovieApi.MovieDb.TV; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace DM.MovieApi.IntegrationTests.MovieDb.TV -{ - [TestClass] - public class ApiTVShowRequestTests - { - private IApiTVShowRequest _api; +namespace DM.MovieApi.IntegrationTests.MovieDb.TV; - [TestInitialize] - public void TestInit() - { - ApiResponseUtil.ThrottleTests(); +[TestClass] +public class ApiTVShowRequestTests +{ + private IApiTVShowRequest _api; - _api = MovieDbFactory.Create().Value; + [TestInitialize] + public void TestInit() + { + ApiResponseUtil.ThrottleTests(); - Assert.IsInstanceOfType( _api, typeof( ApiTVShowRequest ) ); - } + _api = MovieDbFactory.Create().Value; - [TestMethod] - public async Task SearchByNameAsync_The_Nanny_Returns_ExpectedResults() - { - const string theNanny = "The Nanny"; + Assert.IsInstanceOfType( _api, typeof( ApiTVShowRequest ) ); + } - ApiSearchResponse response = await _api.SearchByNameAsync( theNanny ); + [TestMethod] + public async Task SearchByNameAsync_The_Nanny_Returns_ExpectedResults() + { + const string theNanny = "The Nanny"; - AssertTheNanny( response, theNanny ); - } + ApiSearchResponse response = await _api.SearchByNameAsync( theNanny ); - [TestMethod] - public async Task SearchByNameAsync_The_Nanny_Returns_ExpectedResults_InGerman() - { - const string theNanny = "Die Nanny"; + AssertTheNanny( response, theNanny ); + } - ApiSearchResponse response = await _api.SearchByNameAsync( theNanny, language: "de" ); + [TestMethod] + public async Task SearchByNameAsync_The_Nanny_Returns_ExpectedResults_InGerman() + { + const string theNanny = "Die Nanny"; - ApiResponseUtil.AssertErrorIsNull( response ); + ApiSearchResponse response = await _api.SearchByNameAsync( theNanny, language: "de" ); - AssertTheNanny( response, theNanny ); - } + ApiResponseUtil.AssertErrorIsNull( response ); - private void AssertTheNanny( ApiSearchResponse response, string theNanny ) - { - const int theNannyId = 2352; - const string us = "US"; - const string lang = "en"; - var firstAirDate = new DateTime( 1993, 11, 03 ); - - Assert.IsTrue( response.TotalResults > 0 ); - Assert.IsTrue( response.Results.Count > 0 ); + AssertTheNanny( response, theNanny ); + } - TVShowInfo show = response.Results.Single( x => x.Id == theNannyId ); + private void AssertTheNanny( ApiSearchResponse response, string theNanny ) + { + const int theNannyId = 2352; + const string us = "US"; + const string lang = "en"; + var firstAirDate = new DateTime( 1993, 11, 03 ); - Assert.AreEqual( theNanny, show.Name ); + Assert.IsTrue( response.TotalResults > 0 ); + Assert.IsTrue( response.Results.Count > 0 ); - Assert.AreEqual( 1, show.OriginCountry.Count ); + TVShowInfo show = response.Results.Single( x => x.Id == theNannyId ); - string country = show.OriginCountry.Single(); + Assert.AreEqual( theNanny, show.Name ); - Assert.AreEqual( us, country ); + Assert.AreEqual( 1, show.OriginCountry.Count ); - Assert.AreEqual( lang, show.OriginalLanguage ); + string country = show.OriginCountry.Single(); - Assert.AreEqual( firstAirDate.Date, show.FirstAirDate.Date ); - } + Assert.AreEqual( us, country ); - [TestMethod] - public async Task SearchByNameAsync_GameOfThrones_Returns_PopulatedGenres() - { - const string query = "Game of Thrones"; + Assert.AreEqual( lang, show.OriginalLanguage ); - ApiSearchResponse response = await _api.SearchByNameAsync( query ); + Assert.AreEqual( firstAirDate.Date, show.FirstAirDate.Date ); + } - ApiResponseUtil.AssertErrorIsNull( response ); + [TestMethod] + public async Task SearchByNameAsync_GameOfThrones_Returns_PopulatedGenres() + { + const string query = "Game of Thrones"; - TVShowInfo gameOfThrones = response.Results.Single( x => x.Name == query ); - Genre[] expGenres = - { - GenreFactory.SciFiAndFantasy(), - GenreFactory.ActionAndAdventure(), - GenreFactory.Drama() - }; + ApiSearchResponse response = await _api.SearchByNameAsync( query ); - CollectionAssert.AreEquivalent( expGenres, gameOfThrones.Genres.ToArray(), - string.Join( ", ", gameOfThrones.Genres.Select( x => x.Name ) ) ); - } + ApiResponseUtil.AssertErrorIsNull( response ); - [TestMethod] - public async Task SearchByNameAsync_CanPageResults() + TVShowInfo gameOfThrones = response.Results.Single( x => x.Name == query ); + Genre[] expGenres = { - const string query = "full"; - const int minimumPageCount = 4; + GenreFactory.SciFiAndFantasy(), + GenreFactory.ActionAndAdventure(), + GenreFactory.Drama() + }; - await ApiResponseUtil.AssertCanPageSearchResponse( query, minimumPageCount, - ( search, pageNumber ) => _api.SearchByNameAsync( search, pageNumber ), x => x.Id ); - } + CollectionAssert.AreEquivalent( expGenres, gameOfThrones.Genres.ToArray(), + string.Join( ", ", gameOfThrones.Genres.Select( x => x.Name ) ) ); + } - [TestMethod] - [SuppressMessage( "ReSharper", "StringLiteralTypo" )] - public async Task FindById_GameOfThrones_ReturnsAllValues() - { - var expFirstAirDate = new DateTime( 2011, 04, 17 ); - const string expHomepage = "http://www.hbo.com/game-of-thrones"; - const string expName = "Game of Thrones"; - const string expOriginalLanguage = "en"; + [TestMethod] + public async Task SearchByNameAsync_CanPageResults() + { + const string query = "full"; + const int minimumPageCount = 4; - ApiQueryResponse response = await _api.FindByIdAsync( 1399 ); + await ApiResponseUtil.AssertCanPageSearchResponse( query, minimumPageCount, + ( search, pageNumber ) => _api.SearchByNameAsync( search, pageNumber ), x => x.Id ); + } - ApiResponseUtil.AssertErrorIsNull( response ); + [TestMethod] + [SuppressMessage( "ReSharper", "StringLiteralTypo" )] + public async Task FindById_GameOfThrones_ReturnsAllValues() + { + var expFirstAirDate = new DateTime( 2011, 04, 17 ); + const string expHomepage = "http://www.hbo.com/game-of-thrones"; + const string expName = "Game of Thrones"; + const string expOriginalLanguage = "en"; - TVShow show = response.Item; + ApiQueryResponse response = await _api.FindByIdAsync( 1399 ); - TVShowCreator[] expCreatedBy = - { - new(9813, "David Benioff", "/8CuuNIKMzMUL1NKOPv9AqEwM7og.jpg"), - new(228068, "D. B. Weiss", "/caUAtilEe06OwOjoQY3B7BgpARi.jpg"), - }; + ApiResponseUtil.AssertErrorIsNull( response ); - CollectionAssert.AreEquivalent( expCreatedBy, show.CreatedBy.ToArray() ); + TVShow show = response.Item; - var expRunTime = new[] { 60 }; + TVShowCreator[] expCreatedBy = + { + new(9813, "David Benioff", "/8CuuNIKMzMUL1NKOPv9AqEwM7og.jpg"), + new(228068, "D. B. Weiss", "/caUAtilEe06OwOjoQY3B7BgpARi.jpg"), + }; - CollectionAssert.AreEquivalent( expRunTime, show.EpisodeRunTime.ToArray() ); + CollectionAssert.AreEquivalent( expCreatedBy, show.CreatedBy.ToArray() ); - Assert.AreEqual( expFirstAirDate.Date, show.FirstAirDate.Date ); + var expRunTime = new[] { 60 }; - Genre[] expGenres = - { - GenreFactory.SciFiAndFantasy(), - GenreFactory.ActionAndAdventure(), - GenreFactory.Drama() - }; + CollectionAssert.AreEquivalent( expRunTime, show.EpisodeRunTime.ToArray() ); - CollectionAssert.AreEquivalent( expGenres, show.Genres.ToArray(), - string.Join( ", ", show.Genres.Select( x => x.Name ) ) ); + Assert.AreEqual( expFirstAirDate.Date, show.FirstAirDate.Date ); - Assert.AreEqual( expHomepage, show.Homepage ); + Genre[] expGenres = + { + GenreFactory.SciFiAndFantasy(), + GenreFactory.ActionAndAdventure(), + GenreFactory.Drama() + }; - var expLanguages = new[] { "en" }; + CollectionAssert.AreEquivalent( expGenres, show.Genres.ToArray(), + string.Join( ", ", show.Genres.Select( x => x.Name ) ) ); - CollectionAssert.AreEquivalent( expLanguages, show.Languages.ToArray(), - string.Join( ", ", show.Languages ) ); + Assert.AreEqual( expHomepage, show.Homepage ); - Assert.AreEqual( expName, show.Name ); + var expLanguages = new[] { "en" }; - Network[] expNetworks = - { - new(49, "HBO") - }; + CollectionAssert.AreEquivalent( expLanguages, show.Languages.ToArray(), + string.Join( ", ", show.Languages ) ); - CollectionAssert.AreEquivalent( expNetworks, show.Networks.ToArray() ); + Assert.AreEqual( expName, show.Name ); - var expCountryCodes = new[] { "US" }; + Network[] expNetworks = + { + new(49, "HBO") + }; - CollectionAssert.AreEquivalent( expCountryCodes, show.OriginCountry.ToArray() ); + CollectionAssert.AreEquivalent( expNetworks, show.Networks.ToArray() ); - Assert.AreEqual( expOriginalLanguage, show.OriginalLanguage ); + var expCountryCodes = new[] { "US" }; - ApiResponseUtil.AssertImagePath( show.BackdropPath ); - ApiResponseUtil.AssertImagePath( show.PosterPath ); + CollectionAssert.AreEquivalent( expCountryCodes, show.OriginCountry.ToArray() ); - ProductionCompanyInfo[] expProductionCompanies = - { - new( 5820, "Generator Entertainment" ), - new( 12525, "Television 360" ), - new( 12526, "Bighead Littlehead" ), - new( 76043, "Revolution Sun Studios" ) - }; - CollectionAssert.AreEquivalent( expProductionCompanies, show.ProductionCompanies.ToArray(), - string.Join( ", ", show.ProductionCompanies ) ); + Assert.AreEqual( expOriginalLanguage, show.OriginalLanguage ); - Keyword[] expKeywords = - { - new(818, "based on novel or book"), - new(4152, "kingdom"), - new(12554, "dragon"), - new(13084, "king"), - new(34038, "intrigue"), - new(170362, "fantasy world"), - }; - CollectionAssert.AreEquivalent( expKeywords, show.Keywords.ToArray(), - string.Join( ", ", show.Keywords ) ); - } + ApiResponseUtil.AssertImagePath( show.BackdropPath ); + ApiResponseUtil.AssertImagePath( show.PosterPath ); - [TestMethod] - public async Task GetLatestAsync_Returns_ValidResult() + ProductionCompanyInfo[] expProductionCompanies = { - ApiQueryResponse response = await _api.GetLatestAsync(); + new( 5820, "Generator Entertainment" ), + new( 12525, "Television 360" ), + new( 12526, "Bighead Littlehead" ), + new( 76043, "Revolution Sun Studios" ) + }; + CollectionAssert.AreEquivalent( expProductionCompanies, show.ProductionCompanies.ToArray(), + string.Join( ", ", show.ProductionCompanies ) ); + + Keyword[] expKeywords = + { + new(818, "based on novel or book"), + new(4152, "kingdom"), + new(12554, "dragon"), + new(13084, "king"), + new(34038, "intrigue"), + new(170362, "fantasy world"), + }; + CollectionAssert.AreEquivalent( expKeywords, show.Keywords.ToArray(), + string.Join( ", ", show.Keywords ) ); + } - ApiResponseUtil.AssertErrorIsNull( response ); + [TestMethod] + public async Task GetLatestAsync_Returns_ValidResult() + { + ApiQueryResponse response = await _api.GetLatestAsync(); - TVShow show = response.Item; + ApiResponseUtil.AssertErrorIsNull( response ); - Assert.IsNotNull( show ); + TVShow show = response.Item; - Assert.IsTrue( show.Id > 0 ); - Assert.IsFalse( string.IsNullOrEmpty( show.Name ) ); - } + Assert.IsNotNull( show ); - [TestMethod] - public async Task GetTopRatedAsync_Returns_ValidResult() - { - ApiSearchResponse response = await _api.GetTopRatedAsync(); + Assert.IsTrue( show.Id > 0 ); + Assert.IsFalse( string.IsNullOrEmpty( show.Name ) ); + } - ApiResponseUtil.AssertErrorIsNull( response ); + [TestMethod] + public async Task GetTopRatedAsync_Returns_ValidResult() + { + ApiSearchResponse response = await _api.GetTopRatedAsync(); - IReadOnlyList results = response.Results; + ApiResponseUtil.AssertErrorIsNull( response ); - ApiResponseUtil.AssertTVShowInformationStructure( results ); - } + IReadOnlyList results = response.Results; - [TestMethod] - public async Task GetTopRatedAsync_CanPageResults() - { - const int minimumPageCount = 5; + ApiResponseUtil.AssertTVShowInformationStructure( results ); + } - await ApiResponseUtil.AssertCanPageSearchResponse( "none", minimumPageCount, - ( _, page ) => _api.GetTopRatedAsync( page ), x => x.Id ); - } + [TestMethod] + public async Task GetTopRatedAsync_CanPageResults() + { + const int minimumPageCount = 5; - [TestMethod] - public async Task GetPopularAsync_Returns_ValidResult() - { - ApiSearchResponse response = await _api.GetPopularAsync(); + await ApiResponseUtil.AssertCanPageSearchResponse( "none", minimumPageCount, + ( _, page ) => _api.GetTopRatedAsync( page ), x => x.Id ); + } - ApiResponseUtil.AssertErrorIsNull( response ); + [TestMethod] + public async Task GetPopularAsync_Returns_ValidResult() + { + ApiSearchResponse response = await _api.GetPopularAsync(); - IReadOnlyList results = response.Results; + ApiResponseUtil.AssertErrorIsNull( response ); - ApiResponseUtil.AssertTVShowInformationStructure( results ); - } + IReadOnlyList results = response.Results; - [TestMethod] - public async Task GetPopularAsync_CanPageResults() - { - const int minimumPageCount = 5; + ApiResponseUtil.AssertTVShowInformationStructure( results ); + } - await ApiResponseUtil.AssertCanPageSearchResponse( "none", minimumPageCount, - ( _, page ) => _api.GetPopularAsync( page ), x => x.Id ); - } + [TestMethod] + public async Task GetPopularAsync_CanPageResults() + { + const int minimumPageCount = 5; + + await ApiResponseUtil.AssertCanPageSearchResponse( "none", minimumPageCount, + ( _, page ) => _api.GetPopularAsync( page ), x => x.Id ); + } + + [TestMethod] + [SuppressMessage( "ReSharper", "StringLiteralTypo" )] + public async Task GetTvShow_SeasonInfo_GameOfThrones_ReturnsAllValues() + { + var expFirstAirDate = new DateTime( 2015, 04, 12 ); + const string expName = "Season 5"; + const string expOverview = "The War of the Five Kings, once thought to be drawing to a close, is instead entering a new and more chaotic phase. Westeros is on the brink of collapse, and many are seizing what they can while the realm implodes, like a corpse making a feast for crows."; + const int expEpisodeCount = 10; + const int expSeasonNumber = 5; - [TestMethod] - [SuppressMessage( "ReSharper", "StringLiteralTypo" )] - public async Task GetTvShow_SeasonInfo_GameOfThrones_ReturnsAllValues() + var expEpisodeOne = new Episode { - var expFirstAirDate = new DateTime( 2015, 04, 12 ); - const string expName = "Season 5"; - const string expOverview = "The War of the Five Kings, once thought to be drawing to a close, is instead entering a new and more chaotic phase. Westeros is on the brink of collapse, and many are seizing what they can while the realm implodes, like a corpse making a feast for crows."; - const int expEpisodeCount = 10; - const int expSeasonNumber = 5; - - var expEpisodeOne = new Episode - { - Id = 1043618, - AirDate = new DateTime( 2015, 04, 12 ), - EpisodeNumber = 1, - Name = "The Wars to Come", - Overview = "Cersei and Jaime adjust to a world without Tywin. Varys reveals a conspiracy to Tyrion. Dany faces a new threat to her rule. Jon is caught between two kings.", - ProductionCode = "501", - SeasonNumber = 5, - StillPath = "/shIFxmFySt9CtGXMTXWBipsNOIs.jpg", - VoteAverage = 7.0F, - VoteCount = 100 - }; - - ApiQueryResponse response = await _api.GetTvShowSeasonInfoAsync( 1399, 05, "" ); - - ApiResponseUtil.AssertErrorIsNull( response ); - - Assert.AreEqual( expFirstAirDate, response.Item.AirDate ); - Assert.AreEqual( expOverview, response.Item.Overview ); - Assert.AreEqual( expName, response.Item.Name ); - Assert.AreEqual( expSeasonNumber, response.Item.SeasonNumber ); - Assert.IsTrue( response.Item.Id > 0 ); - ApiResponseUtil.AssertImagePath( response.Item.PosterPath ); - - ApiResponseUtil.AssertTVShowSeasonInformationStructure( response.Item ); - - Assert.AreEqual( expEpisodeCount, response.Item.Episodes.Count ); - - Episode episodeResponse = response.Item.Episodes[0]; - - Assert.AreEqual( expEpisodeOne.Id, episodeResponse.Id ); - Assert.AreEqual( expEpisodeOne.AirDate, episodeResponse.AirDate ); - Assert.AreEqual( expEpisodeOne.EpisodeNumber, episodeResponse.EpisodeNumber ); - Assert.AreEqual( expEpisodeOne.Name, episodeResponse.Name ); - Assert.AreEqual( expEpisodeOne.Overview, episodeResponse.Overview ); - Assert.AreEqual( expEpisodeOne.ProductionCode, episodeResponse.ProductionCode ); - Assert.AreEqual( expEpisodeOne.SeasonNumber, episodeResponse.SeasonNumber ); - Assert.AreEqual( expEpisodeOne.StillPath, episodeResponse.StillPath ); - Assert.IsTrue( expEpisodeOne.VoteAverage < episodeResponse.VoteAverage ); - Assert.IsTrue( expEpisodeOne.VoteCount < episodeResponse.VoteCount ); - } + Id = 1043618, + AirDate = new DateTime( 2015, 04, 12 ), + EpisodeNumber = 1, + Name = "The Wars to Come", + Overview = "Cersei and Jaime adjust to a world without Tywin. Varys reveals a conspiracy to Tyrion. Dany faces a new threat to her rule. Jon is caught between two kings.", + ProductionCode = "501", + SeasonNumber = 5, + StillPath = "/shIFxmFySt9CtGXMTXWBipsNOIs.jpg", + VoteAverage = 7.0F, + VoteCount = 100 + }; + + ApiQueryResponse response = await _api.GetTvShowSeasonInfoAsync( 1399, 05, "" ); + + ApiResponseUtil.AssertErrorIsNull( response ); + + Assert.AreEqual( expFirstAirDate, response.Item.AirDate ); + Assert.AreEqual( expOverview, response.Item.Overview ); + Assert.AreEqual( expName, response.Item.Name ); + Assert.AreEqual( expSeasonNumber, response.Item.SeasonNumber ); + Assert.IsTrue( response.Item.Id > 0 ); + ApiResponseUtil.AssertImagePath( response.Item.PosterPath ); + + ApiResponseUtil.AssertTVShowSeasonInformationStructure( response.Item ); + + Assert.AreEqual( expEpisodeCount, response.Item.Episodes.Count ); + + Episode episodeResponse = response.Item.Episodes[0]; + + Assert.AreEqual( expEpisodeOne.Id, episodeResponse.Id ); + Assert.AreEqual( expEpisodeOne.AirDate, episodeResponse.AirDate ); + Assert.AreEqual( expEpisodeOne.EpisodeNumber, episodeResponse.EpisodeNumber ); + Assert.AreEqual( expEpisodeOne.Name, episodeResponse.Name ); + Assert.AreEqual( expEpisodeOne.Overview, episodeResponse.Overview ); + Assert.AreEqual( expEpisodeOne.ProductionCode, episodeResponse.ProductionCode ); + Assert.AreEqual( expEpisodeOne.SeasonNumber, episodeResponse.SeasonNumber ); + Assert.AreEqual( expEpisodeOne.StillPath, episodeResponse.StillPath ); + Assert.IsTrue( expEpisodeOne.VoteAverage < episodeResponse.VoteAverage ); + Assert.IsTrue( expEpisodeOne.VoteCount < episodeResponse.VoteCount ); } } diff --git a/DM.MovieApi.IntegrationTests/MovieDbApiTests.cs b/DM.MovieApi.IntegrationTests/MovieDbApiTests.cs index 541bf23..7121ac6 100644 --- a/DM.MovieApi.IntegrationTests/MovieDbApiTests.cs +++ b/DM.MovieApi.IntegrationTests/MovieDbApiTests.cs @@ -1,51 +1,68 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using DM.MovieApi.ApiRequest; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace DM.MovieApi.IntegrationTests +using System.Reflection; + +namespace DM.MovieApi.IntegrationTests; + +[TestClass] +public class MovieDbApiTests { - [TestClass] - public class MovieDbApiTests + /// + /// All types implementing . + /// + public static readonly Type[] Implementations = GetImplementations(); + + [TestMethod] + public void Ensure_Implementations_AreConsistent() { - [TestMethod] - public void Ensure_MovieDbApi_HasAllParts() + Assert.IsTrue( Implementations.Length > 5, $"Actual: {Implementations.Length}" ); + + foreach( Type t in Implementations ) { - Assembly load = Assembly.Load( "DM.MovieApi" ); - - List apiRequests = load.GetTypes() - .Where( x => typeof( IApiRequest ).IsAssignableFrom( x ) ) - .Where( x => x.IsClass ) - .Distinct() - .ToList(); - - Assert.IsTrue( apiRequests.Count > 5 ); - - foreach( Type type in apiRequests ) - { - Assert.IsFalse( type.IsPublic ); - } - - List dbApi = typeof( IMovieDbApi ) - .GetProperties() - .Where( x => typeof( IApiRequest ).IsAssignableFrom( x.PropertyType ) ) - .Distinct() - .ToList(); - - Assert.AreEqual( apiRequests.Count, dbApi.Count ); - - foreach( PropertyInfo pi in dbApi ) - { - Assert.IsTrue( pi.CanRead ); - Assert.IsFalse( pi.CanWrite ); - Assert.IsTrue( pi.PropertyType.IsPublic ); - Assert.IsTrue( pi.PropertyType.IsInterface ); - - Type match = apiRequests.SingleOrDefault( x => pi.PropertyType.IsAssignableFrom( x ) ); - Assert.IsNotNull( match ); - } + ApiResponseUtil.Log( t.Name, nameof( IApiRequest ) ); + + Assert.IsTrue( t.IsClass ); + Assert.IsFalse( t.IsAbstract ); + Assert.IsFalse( t.IsPublic ); + Assert.IsTrue( t.IsNotPublic ); + + Assert.IsTrue( t.Name.StartsWith( "Api", StringComparison.Ordinal ) ); + Assert.IsTrue( t.Name.EndsWith( "Request", StringComparison.Ordinal ) ); } } + + [TestMethod] + public void Ensure_MovieDbApi_HasAllParts() + { + PropertyInfo[] dbApi = typeof( IMovieDbApi ) + .GetProperties() + .Where( x => typeof( IApiRequest ).IsAssignableFrom( x.PropertyType ) ) + .ToArray(); + + Assert.AreEqual( Implementations.Length, dbApi.Length ); + + foreach( PropertyInfo pi in dbApi ) + { + Assert.IsTrue( pi.CanRead ); + Assert.IsFalse( pi.CanWrite ); + Assert.IsTrue( pi.PropertyType.IsPublic ); + Assert.IsTrue( pi.PropertyType.IsInterface ); + + Type match = Implementations.SingleOrDefault( x => pi.PropertyType.IsAssignableFrom( x ) ); + Assert.IsNotNull( match ); + } + } + + private static Type[] GetImplementations() + { + // alternatively: Assembly.Load( "DM.MovieApi" ) + + Type[] types = typeof( IApiRequest ) + .Assembly + .GetTypes() + .Where( x => x.IsClass ) + .Where( x => typeof( IApiRequest ).IsAssignableFrom( x ) ) + .OrderBy( x => x.Name ) + .ToArray(); + + return types; + } } diff --git a/DM.MovieApi.IntegrationTests/MovieDbFactoryTests.cs b/DM.MovieApi.IntegrationTests/MovieDbFactoryTests.cs index 22f4314..0f43dad 100644 --- a/DM.MovieApi.IntegrationTests/MovieDbFactoryTests.cs +++ b/DM.MovieApi.IntegrationTests/MovieDbFactoryTests.cs @@ -1,249 +1,236 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Reflection; -using System.Threading.Tasks; -using DM.MovieApi.ApiRequest; -using DM.MovieApi.ApiResponse; -using DM.MovieApi.MovieDb.Movies; -using DM.MovieApi.Shims; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace DM.MovieApi.IntegrationTests +using System.Reflection; + +namespace DM.MovieApi.IntegrationTests; + +[TestClass] +public class MovieDbFactoryTests { - [TestClass] - public class MovieDbFactoryTests + [TestCleanup] + public void TestCleanup() { - [TestCleanup] - public void TestCleanup() - { - // Some of the factory tests will de-register the settings registered in AssemblyInit. - // Ensure other tests are able to run with the registered settings. - AssemblyInit.RegisterFactorySettings(); - } + // Some of the factory tests will de-register the settings registered in AssemblyInit. + // Ensure other tests are able to run with the registered settings. + AssemblyInit.RegisterFactorySettings(); + } - [TestMethod] - public async Task RegisterSettings_CanUse_RawStrings() - { - MovieDbFactory.ResetFactory(); - MovieDbFactory.RegisterSettings( AssemblyInit.Settings.BearerToken ); + [TestMethod] + public async Task RegisterSettings_CanUse_RawStrings() + { + MovieDbFactory.ResetFactory(); + MovieDbFactory.RegisterSettings( AssemblyInit.Settings.BearerToken ); - var api = MovieDbFactory.Create().Value; + var api = MovieDbFactory.Create().Value; - ApiQueryResponse response = await api.GetLatestAsync(); + ApiQueryResponse response = await api.GetLatestAsync(); - Assert.IsNull( response.Error ); - Assert.IsNotNull( response.Item ); - Assert.IsTrue( response.Item.Id > 391000 ); - } + Assert.IsNull( response.Error ); + Assert.IsNotNull( response.Item ); + Assert.IsTrue( response.Item.Id > 391000 ); + } - [TestMethod] - public void Create_ThrowsException_When_SettingsNotRegistered() + [TestMethod] + public void Create_ThrowsException_When_SettingsNotRegistered() + { + try { - try - { - MovieDbFactory.ResetFactory(); - MovieDbFactory.Create(); - } - catch( InvalidOperationException ex ) - { - Assert.IsTrue( ex.Message.StartsWith( "RegisterSettings must be called" ), $"Actual: {ex.Message}" ); - return; - } - - Assert.Fail( $"{nameof( InvalidOperationException )} was expected, but none were thrown." ); + MovieDbFactory.ResetFactory(); + MovieDbFactory.Create(); } - - [TestMethod] - public void Create_ConstrainedBy_IApiRequest() + catch( InvalidOperationException ex ) { - MethodInfo mi = typeof( MovieDbFactory ).GetMethod( nameof( MovieDbFactory.Create ) ); - Assert.IsNotNull( mi ); + Assert.IsTrue( ex.Message.StartsWith( "RegisterSettings must be called" ), $"Actual: {ex.Message}" ); + return; + } - Type t1 = mi.GetGenericArguments() - .Single() - .GetGenericParameterConstraints() - .Single(); + Assert.Fail( $"{nameof( InvalidOperationException )} was expected, but none were thrown." ); + } - Assert.AreEqual( typeof( IApiRequest ), t1 ); - } + [TestMethod] + public void Create_ConstrainedBy_IApiRequest() + { + MethodInfo mi = typeof( MovieDbFactory ).GetMethod( nameof( MovieDbFactory.Create ) ); + Assert.IsNotNull( mi ); - [TestMethod] - public void Create_Returns_Lazy_IApiMovieRequest() - { - Lazy lazy = MovieDbFactory.Create(); + Type t1 = mi.GetGenericArguments() + .Single() + .GetGenericParameterConstraints() + .Single(); - Assert.IsNotNull( lazy ); + Assert.AreEqual( typeof( IApiRequest ), t1 ); + } - Assert.IsFalse( lazy.IsValueCreated ); + [TestMethod] + public void Create_Returns_Lazy_IApiMovieRequest() + { + Lazy lazy = MovieDbFactory.Create(); - IApiMovieRequest api = lazy.Value; + Assert.IsNotNull( lazy ); - Assert.IsTrue( lazy.IsValueCreated ); + Assert.IsFalse( lazy.IsValueCreated ); - Assert.IsNotNull( api ); - } + IApiMovieRequest api = lazy.Value; - [TestMethod] - public void GetAllApiRequests_CanCreate_IMovieApi() + Assert.IsTrue( lazy.IsValueCreated ); + + Assert.IsNotNull( api ); + } + + [TestMethod] + public void GetAllApiRequests_CanCreate_IMovieApi() + { + IMovieDbApi api; + + try { - PropertyInfo[] dbApi = typeof( IMovieDbApi ) - .GetProperties() - .Where( x => typeof( IApiRequest ).IsAssignableFrom( x.PropertyType ) ) - .Distinct() - .ToArray(); - - Assert.AreEqual( 9, dbApi.Length ); - - IMovieDbApi api; - - try - { - api = MovieDbFactory.GetAllApiRequests(); - } - catch( NotImplementedException ) - { - return; - } - - Assert.Fail( $"See the {nameof( GetAllApiRequests_IsDisabled_WithEx )} for expected failure." ); - - // ReSharper disable HeuristicUnreachableCode - Assert.IsNotNull( api ); - - foreach( PropertyInfo pi in dbApi ) - { - var val = pi.GetValue( api ) as IApiRequest; - Assert.IsNotNull( val ); - } - // ReSharper restore HeuristicUnreachableCode + api = MovieDbFactory.GetAllApiRequests(); } - - [TestMethod] - public void ResetFactory_Sets_IsFactoryComposed() + catch( NotImplementedException ) { - MovieDbFactory.ResetFactory(); - Assert.IsFalse( MovieDbFactory.IsFactoryComposed ); + return; + } - AssemblyInit.RegisterFactorySettings(); - Assert.IsTrue( MovieDbFactory.IsFactoryComposed ); + Assert.Fail( $"See the {nameof( GetAllApiRequests_IsDisabled_WithEx )} for expected failure." ); - MovieDbFactory.ResetFactory(); - Assert.IsFalse( MovieDbFactory.IsFactoryComposed ); - } + // ReSharper disable HeuristicUnreachableCode + Assert.IsNotNull( api ); - [TestMethod] - public void GetAllApiRequests_IsDisabled_WithEx() + PropertyInfo[] dbApi = typeof( IMovieDbApi ) + .GetProperties() + .Where( x => typeof( IApiRequest ).IsAssignableFrom( x.PropertyType ) ) + .ToArray(); + + foreach( PropertyInfo pi in dbApi ) { - try - { - MovieDbFactory.GetAllApiRequests(); - } - catch( NotImplementedException ) - { - return; - } - - Assert.Fail( $"{nameof( MovieDbFactory.GetAllApiRequests )} " + - $"should be disabled with a {nameof( NotImplementedException )}." ); + var val = pi.GetValue( api ) as IApiRequest; + Assert.IsNotNull( val ); } + // ReSharper restore HeuristicUnreachableCode + } + + [TestMethod] + public void ResetFactory_Sets_IsFactoryComposed() + { + MovieDbFactory.ResetFactory(); + Assert.IsFalse( MovieDbFactory.IsFactoryComposed ); - [TestMethod] - public void ImportingConstructorAttribute_IsMissing_ThrowsEx() + AssemblyInit.RegisterFactorySettings(); + Assert.IsTrue( MovieDbFactory.IsFactoryComposed ); + + MovieDbFactory.ResetFactory(); + Assert.IsFalse( MovieDbFactory.IsFactoryComposed ); + } + + [TestMethod] + public void GetAllApiRequests_IsDisabled_WithEx() + { + try { - try - { - // ReSharper disable once UnusedVariable - var api = MovieDbFactory.Create().Value; - } - catch( InvalidOperationException ex ) - { - Assert.IsTrue( ex.Message.StartsWith( "Multiple public constructors found." ), - $"Actual: {ex.Message}" ); - - Assert.IsTrue( ex.Message.Contains( nameof( ImportingConstructorAttribute ) ), - $"Actual: {ex.Message}" ); - - return; - } - - Assert.Fail( $"{nameof( MovieDbFactory.Create )} should throw an exception " + - "when multiple ctors are found without an decorated ctor for importing." ); + MovieDbFactory.GetAllApiRequests(); } - - [TestMethod] - public void IApiRequest_WithoutImplementation_ThrowsEx() + catch( NotImplementedException ) { - try - { - // ReSharper disable once UnusedVariable - var api = MovieDbFactory.Create().Value; - } - catch( NotSupportedException ex ) - { - Assert.AreEqual( ex.Message, - $"{nameof( IMockApiRequestNotImplemented )} must have a concrete implementation." ); - - return; - } - - Assert.Fail( $"{nameof( MovieDbFactory.Create )} should throw an exception " + - "when no concrete implementation is found." ); + return; } - [TestMethod] - public void BearerToken_MustBe_GreaterThan_200chars() + Assert.Fail( $"{nameof( MovieDbFactory.GetAllApiRequests )} " + + $"should be disabled with a {nameof( NotImplementedException )}." ); + } + + [TestMethod] + public void ImportingConstructorAttribute_IsMissing_ThrowsEx() + { + try + { + // ReSharper disable once UnusedVariable + var api = MovieDbFactory.Create().Value; + } + catch( InvalidOperationException ex ) { - const int minLength = 201; + Assert.IsTrue( ex.Message.StartsWith( "Multiple public constructors found." ), + $"Actual: {ex.Message}" ); - AssertRegisterSettingsThrowsEx( null ); - AssertRegisterSettingsThrowsEx( "" ); - AssertRegisterSettingsThrowsEx( " " ); - AssertRegisterSettingsThrowsEx( new string( '-', minLength - 1 ) ); + Assert.IsTrue( ex.Message.Contains( nameof( ImportingConstructorAttribute ) ), + $"Actual: {ex.Message}" ); - MovieDbFactory.ResetFactory(); - MovieDbFactory.RegisterSettings( new string( '*', minLength ) ); + return; } - private void AssertRegisterSettingsThrowsEx( string bearerToken ) + Assert.Fail( $"{nameof( MovieDbFactory.Create )} should throw an exception " + + "when multiple ctors are found without an decorated ctor for importing." ); + } + + [TestMethod] + public void IApiRequest_WithoutImplementation_ThrowsEx() + { + try { - MovieDbFactory.ResetFactory(); + // ReSharper disable once UnusedVariable + var api = MovieDbFactory.Create().Value; + } + catch( NotSupportedException ex ) + { + Assert.AreEqual( ex.Message, + $"{nameof( IMockApiRequestNotImplemented )} must have a concrete implementation." ); - try - { - MovieDbFactory.RegisterSettings( bearerToken ); - Assert.Fail( "Bearer Token < 200 chars was expected to throw ex." ); - } - catch( ArgumentException ex ) - { - string msg = ex.Message; - Assert.IsTrue( msg.StartsWith( "Must provide a valid TheMovieDb.org Bearer token." ) ); - Assert.IsTrue( msg.Contains( "A valid token can be found in your account page, under the API section." ) ); - Assert.IsTrue( msg.Contains( "API Read Access Token" ) ); - } - - Assert.IsFalse( MovieDbFactory.IsFactoryComposed ); + return; } + Assert.Fail( $"{nameof( MovieDbFactory.Create )} should throw an exception " + + "when no concrete implementation is found." ); + } - [SuppressMessage( "ReSharper", "UnusedType.Local" )] - private class MockApiRequestMultipleCtors : IMockApiRequestMultipleCtors - { - public MockApiRequestMultipleCtors() - { } + [TestMethod] + public void BearerToken_MustBe_GreaterThan_200chars() + { + const int minLength = 201; + + AssertRegisterSettingsThrowsEx( null ); + AssertRegisterSettingsThrowsEx( "" ); + AssertRegisterSettingsThrowsEx( " " ); + AssertRegisterSettingsThrowsEx( new string( '-', minLength - 1 ) ); + + MovieDbFactory.ResetFactory(); + MovieDbFactory.RegisterSettings( new string( '*', minLength ) ); + } - [SuppressMessage( "ReSharper", "UnusedParameter.Local" )] - public MockApiRequestMultipleCtors( string val ) - { } + private void AssertRegisterSettingsThrowsEx( string bearerToken ) + { + MovieDbFactory.ResetFactory(); + + try + { + MovieDbFactory.RegisterSettings( bearerToken ); + Assert.Fail( "Bearer Token < 200 chars was expected to throw ex." ); + } + catch( ArgumentException ex ) + { + string msg = ex.Message; + Assert.IsTrue( msg.StartsWith( "Must provide a valid TheMovieDb.org Bearer token." ) ); + Assert.IsTrue( msg.Contains( "A valid token can be found in your account page, under the API section." ) ); + Assert.IsTrue( msg.Contains( "API Read Access Token" ) ); } - private interface IMockApiRequestMultipleCtors : IApiRequest - { } + Assert.IsFalse( MovieDbFactory.IsFactoryComposed ); + } + - private interface IMockApiRequestNotImplemented : IApiRequest + [SuppressMessage( "ReSharper", "UnusedType.Local" )] + private class MockApiRequestMultipleCtors : IMockApiRequestMultipleCtors + { + public MockApiRequestMultipleCtors() { } - private interface IMockApiRequest : IApiRequest + [SuppressMessage( "ReSharper", "UnusedParameter.Local" )] + public MockApiRequestMultipleCtors( string val ) { } } + + private interface IMockApiRequestMultipleCtors : IApiRequest + { } + + private interface IMockApiRequestNotImplemented : IApiRequest + { } + + private interface IMockApiRequest : IApiRequest + { } } diff --git a/DM.MovieApi/ApiRequest/ApiRequestBase.cs b/DM.MovieApi/ApiRequest/ApiRequestBase.cs index 1d4e6d0..bd06f56 100644 --- a/DM.MovieApi/ApiRequest/ApiRequestBase.cs +++ b/DM.MovieApi/ApiRequest/ApiRequestBase.cs @@ -1,167 +1,161 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; +using System.Net; using System.Net.Http; using System.Net.Http.Headers; -using System.Threading.Tasks; -using DM.MovieApi.ApiResponse; using Newtonsoft.Json; -namespace DM.MovieApi.ApiRequest +namespace DM.MovieApi.ApiRequest; + +internal abstract class ApiRequestBase { - internal abstract class ApiRequestBase - { - private readonly IApiSettings _settings; + private readonly IApiSettings _settings; - static ApiRequestBase() + static ApiRequestBase() + { + var settings = new JsonSerializerSettings { - var settings = new JsonSerializerSettings - { - NullValueHandling = NullValueHandling.Ignore, - DateFormatHandling = DateFormatHandling.IsoDateFormat, - }; - settings.Converters.Add( new IsoDateTimeConverterEx() ); + NullValueHandling = NullValueHandling.Ignore, + DateFormatHandling = DateFormatHandling.IsoDateFormat, + }; + settings.Converters.Add( new IsoDateTimeConverterEx() ); - JsonConvert.DefaultSettings = () => settings; - } + JsonConvert.DefaultSettings = () => settings; + } - protected ApiRequestBase( IApiSettings settings ) - => _settings = settings; + protected ApiRequestBase( IApiSettings settings ) + => _settings = settings; - public async Task> QueryAsync( string command ) - => await QueryAsync( command, new Dictionary(), null ); + public async Task> QueryAsync( string command ) + => await QueryAsync( command, new Dictionary(), null ); - public async Task> QueryAsync( string command, IDictionary parameters ) - => await QueryAsync( command, parameters, null ); + public async Task> QueryAsync( string command, IDictionary parameters ) + => await QueryAsync( command, parameters, null ); - public async Task> QueryAsync( string command, Func deserializer ) - => await QueryAsync( command, new Dictionary(), deserializer ); + public async Task> QueryAsync( string command, Func deserializer ) + => await QueryAsync( command, new Dictionary(), deserializer ); - public async Task> QueryAsync( string command, IDictionary parameters, Func deserializer ) - { - deserializer ??= JsonConvert.DeserializeObject; + public async Task> QueryAsync( string command, IDictionary parameters, Func deserializer ) + { + deserializer ??= JsonConvert.DeserializeObject; - using HttpClient client = CreateClient(); - string cmd = CreateCommand( command, parameters ); + using HttpClient client = CreateClient(); + string cmd = CreateCommand( command, parameters ); - HttpResponseMessage response = await client.GetAsync( cmd ).ConfigureAwait( false ); + HttpResponseMessage response = await client.GetAsync( cmd ).ConfigureAwait( false ); - string json = await response.Content.ReadAsStringAsync().ConfigureAwait( false ); + string json = await response.Content.ReadAsStringAsync().ConfigureAwait( false ); - if( response.IsSuccessStatusCode == false ) - { - // rate limit will not exist if there is an error. - var error = new ApiQueryResponse - { - Error = JsonConvert.DeserializeObject( json ), - CommandText = response.RequestMessage?.RequestUri?.ToString() ?? "response message/uri was null", - Json = json, - }; - - return error; - } - - var result = new ApiQueryResponse + if( response.IsSuccessStatusCode == false ) + { + // rate limit will not exist if there is an error. + var error = new ApiQueryResponse { - // ReSharper disable PossibleNullReferenceException - CommandText = response.RequestMessage.RequestUri.ToString(), - // ReSharper restore PossibleNullReferenceException + Error = JsonConvert.DeserializeObject( json ), + CommandText = response.RequestMessage?.RequestUri?.ToString() ?? "response message/uri was null", Json = json, }; - T item = deserializer( json ); - result.Item = item; - return result; + return error; } - // ReSharper disable once UnusedMember.Global - public async Task> SearchAsync( string command ) - => await SearchAsync( command, 1 ); - - public async Task> SearchAsync( string command, int pageNumber ) - => await SearchAsync( command, pageNumber, new Dictionary() ); - - public async Task> SearchAsync( string command, IDictionary parameters ) - => await SearchAsync( command, 1, parameters ); - - public async Task> SearchAsync( string command, int pageNumber, IDictionary parameters ) + var result = new ApiQueryResponse { - pageNumber = pageNumber < 1 ? 1 : pageNumber; - pageNumber = pageNumber > 1000 ? 1000 : pageNumber; + // ReSharper disable PossibleNullReferenceException + CommandText = response.RequestMessage.RequestUri.ToString(), + // ReSharper restore PossibleNullReferenceException + Json = json, + }; - if( !parameters.Keys.Contains( "page", StringComparer.OrdinalIgnoreCase ) ) - { - parameters.Add( "page", pageNumber.ToString() ); - } + T item = deserializer( json ); + result.Item = item; + return result; + } - using HttpClient client = CreateClient(); - string cmd = CreateCommand( command, parameters ); + // ReSharper disable once UnusedMember.Global + public async Task> SearchAsync( string command ) + => await SearchAsync( command, 1 ); - HttpResponseMessage response = await client.GetAsync( cmd ).ConfigureAwait( false ); + public async Task> SearchAsync( string command, int pageNumber ) + => await SearchAsync( command, pageNumber, new Dictionary() ); - string json = await response.Content.ReadAsStringAsync().ConfigureAwait( false ); + public async Task> SearchAsync( string command, IDictionary parameters ) + => await SearchAsync( command, 1, parameters ); - // rate limit will not exist if there is an error. - if( response.IsSuccessStatusCode == false ) - { - var error = new ApiSearchResponse - { - // This will throw up if the error is page number = 0; the resultant json will be: {"errors":["page must be greater than 0"]} - // in other words, the json will not include a status_code. Asked the api devs and this is a known issue they are working on. - // What to do? Nothing really, the page guard at the top of the method will keep the page number > 0. - Error = JsonConvert.DeserializeObject( json ), - CommandText = response.RequestMessage?.RequestUri?.ToString() ?? "response message/uri was null", - Json = json, - }; + public async Task> SearchAsync( string command, int pageNumber, IDictionary parameters ) + { + pageNumber = pageNumber < 1 ? 1 : pageNumber; + pageNumber = pageNumber > 1000 ? 1000 : pageNumber; - return error; - } + if( !parameters.Keys.Contains( "page", StringComparer.OrdinalIgnoreCase ) ) + { + parameters.Add( "page", pageNumber.ToString() ); + } - var result = JsonConvert.DeserializeObject>( json ); + using HttpClient client = CreateClient(); + string cmd = CreateCommand( command, parameters ); - // ReSharper disable PossibleNullReferenceException - result.CommandText = response.RequestMessage.RequestUri.ToString(); - // ReSharper restore PossibleNullReferenceException - result.Json = json; + HttpResponseMessage response = await client.GetAsync( cmd ).ConfigureAwait( false ); - return result; - } + string json = await response.Content.ReadAsStringAsync().ConfigureAwait( false ); - protected HttpClient CreateClient() + // rate limit will not exist if there is an error. + if( response.IsSuccessStatusCode == false ) { - var handler = new HttpClientHandler + var error = new ApiSearchResponse { - AllowAutoRedirect = false, - UseCookies = false, - UseDefaultCredentials = true, - AutomaticDecompression = DecompressionMethods.GZip, + // This will throw up if the error is page number = 0; the resultant json will be: {"errors":["page must be greater than 0"]} + // in other words, the json will not include a status_code. Asked the api devs and this is a known issue they are working on. + // What to do? Nothing really, the page guard at the top of the method will keep the page number > 0. + Error = JsonConvert.DeserializeObject( json ), + CommandText = response.RequestMessage?.RequestUri?.ToString() ?? "response message/uri was null", + Json = json, }; - var client = new HttpClient( handler ); - client.DefaultRequestHeaders.Accept.Clear(); - client.DefaultRequestHeaders.Accept.Add( new MediaTypeWithQualityHeaderValue( "application/json" ) ); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( "Bearer", _settings.BearerToken ); - client.BaseAddress = new Uri( _settings.ApiUrl ); - - return client; + return error; } - protected string CreateCommand( string rootCommand ) - => CreateCommand( rootCommand, new Dictionary() ); + var result = JsonConvert.DeserializeObject>( json ); + + // ReSharper disable PossibleNullReferenceException + result.CommandText = response.RequestMessage.RequestUri.ToString(); + // ReSharper restore PossibleNullReferenceException + result.Json = json; - protected string CreateCommand( string rootCommand, IDictionary parameters ) + return result; + } + + protected HttpClient CreateClient() + { + var handler = new HttpClientHandler { - string tokens = parameters.Any() - ? string.Join( "&", parameters.Select( x => $"{x.Key}={WebUtility.UrlEncode( x.Value )}" ) ) - : string.Empty; + AllowAutoRedirect = false, + UseCookies = false, + UseDefaultCredentials = true, + AutomaticDecompression = DecompressionMethods.GZip, + }; + + var client = new HttpClient( handler ); + client.DefaultRequestHeaders.Accept.Clear(); + client.DefaultRequestHeaders.Accept.Add( new MediaTypeWithQualityHeaderValue( "application/json" ) ); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( "Bearer", _settings.BearerToken ); + client.BaseAddress = new Uri( _settings.ApiUrl ); + + return client; + } - if( string.IsNullOrWhiteSpace( tokens ) == false ) - { - rootCommand += $"?{tokens}"; - } + protected string CreateCommand( string rootCommand ) + => CreateCommand( rootCommand, new Dictionary() ); - return rootCommand; + protected string CreateCommand( string rootCommand, IDictionary parameters ) + { + string tokens = parameters.Any() + ? string.Join( "&", parameters.Select( x => $"{x.Key}={WebUtility.UrlEncode( x.Value )}" ) ) + : string.Empty; + + if( string.IsNullOrWhiteSpace( tokens ) == false ) + { + rootCommand += $"?{tokens}"; } + + return rootCommand; } } diff --git a/DM.MovieApi/ApiRequest/IApiRequest.cs b/DM.MovieApi/ApiRequest/IApiRequest.cs index 809449c..dd9c07a 100644 --- a/DM.MovieApi/ApiRequest/IApiRequest.cs +++ b/DM.MovieApi/ApiRequest/IApiRequest.cs @@ -1,8 +1,7 @@ -namespace DM.MovieApi.ApiRequest -{ - /// - /// Interface to provide a constraint for all MovieDb Api Request interfaces/classes. - /// - public interface IApiRequest - { } -} +namespace DM.MovieApi.ApiRequest; + +/// +/// Interface to provide a constraint for all MovieDb Api Request interfaces/classes. +/// +public interface IApiRequest +{ } diff --git a/DM.MovieApi/ApiRequest/IsoDateTimeConverterEx.cs b/DM.MovieApi/ApiRequest/IsoDateTimeConverterEx.cs index 805ba5f..a84f9b6 100644 --- a/DM.MovieApi/ApiRequest/IsoDateTimeConverterEx.cs +++ b/DM.MovieApi/ApiRequest/IsoDateTimeConverterEx.cs @@ -1,56 +1,54 @@ -using System; -using System.Diagnostics; +using System.Diagnostics; using Newtonsoft.Json; using Newtonsoft.Json.Converters; -namespace DM.MovieApi.ApiRequest +namespace DM.MovieApi.ApiRequest; + +/// +/// Extends the native Newtonsoft IsoDateTimeConverter to allow deserializing partial dates. +/// +public class IsoDateTimeConverterEx : IsoDateTimeConverter { /// - /// Extends the native Newtonsoft IsoDateTimeConverter to allow deserializing partial dates. + /// Reads the JSON representation of the object. /// - public class IsoDateTimeConverterEx : IsoDateTimeConverter + /// The Newtonsoft.Json.JsonReader to read from. + /// Type of the object. + /// The existing value of object being read. + /// The calling serializer. + /// The object value. + public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer ) { - /// - /// Reads the JSON representation of the object. - /// - /// The Newtonsoft.Json.JsonReader to read from. - /// Type of the object. - /// The existing value of object being read. - /// The calling serializer. - /// The object value. - public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer ) + ConditionalTraceReaderValue( reader ); + + try + { + return base.ReadJson( reader, objectType, existingValue, serializer ); + } + catch( Exception ex ) when( ex is FormatException + || ex is JsonSerializationException jse + && jse.Message.Contains( "System.DateTime" ) ) { - ConditionalTraceReaderValue( reader ); + string val = reader.Value?.ToString(); - try + if( val?.Length == 4 && int.TryParse( val, out int year ) ) { - return base.ReadJson( reader, objectType, existingValue, serializer ); + return new DateTime( year, 1, 1 ); } - catch( Exception ex ) when( ex is FormatException - || ex is JsonSerializationException jse - && jse.Message.Contains( "System.DateTime" ) ) - { - string val = reader.Value?.ToString(); - if( val?.Length == 4 && int.TryParse( val, out int year ) ) - { - return new DateTime( year, 1, 1 ); - } - - return DateTime.UnixEpoch; - } + return DateTime.UnixEpoch; } + } - [Conditional( "DEBUG" )] - private void ConditionalTraceReaderValue( JsonReader reader ) + [Conditional( "DEBUG" )] + private void ConditionalTraceReaderValue( JsonReader reader ) + { + string val = reader.Value?.ToString(); + if( string.IsNullOrWhiteSpace( val ) ) { - string val = reader.Value?.ToString(); - if( string.IsNullOrWhiteSpace( val ) ) - { - val = ""; - } - - Trace.WriteLine( val, "IsoDateTimeConverterEx" ); + val = ""; } + + Trace.WriteLine( val, "IsoDateTimeConverterEx" ); } } diff --git a/DM.MovieApi/ApiResponse/ApiError.cs b/DM.MovieApi/ApiResponse/ApiError.cs index 696d79f..5c3a1e9 100644 --- a/DM.MovieApi/ApiResponse/ApiError.cs +++ b/DM.MovieApi/ApiResponse/ApiError.cs @@ -1,33 +1,29 @@ -using System; -using System.Runtime.Serialization; +namespace DM.MovieApi.ApiResponse; -namespace DM.MovieApi.ApiResponse +[DataContract] +public class ApiError { - [DataContract] - public class ApiError - { - private int _statusCode; + private int _statusCode; - [DataMember( Name = "status_code" )] - public int StatusCode + [DataMember( Name = "status_code" )] + public int StatusCode + { + get => _statusCode; + private set { - get => _statusCode; - private set - { - _statusCode = value; + _statusCode = value; - TmdbStatusCode = Enum.IsDefined( typeof( TmdbStatusCode ), _statusCode ) - ? ( TmdbStatusCode )_statusCode - : TmdbStatusCode.Unknown; - } + TmdbStatusCode = Enum.IsDefined( typeof( TmdbStatusCode ), _statusCode ) + ? ( TmdbStatusCode )_statusCode + : TmdbStatusCode.Unknown; } + } - [DataMember( Name = "status_message" )] - public string Message { get; private set; } + [DataMember( Name = "status_message" )] + public string Message { get; private set; } - public TmdbStatusCode TmdbStatusCode { get; private set; } + public TmdbStatusCode TmdbStatusCode { get; private set; } - public override string ToString() - => $"Status: {StatusCode}: {Message}"; - } + public override string ToString() + => $"Status: {StatusCode}: {Message}"; } diff --git a/DM.MovieApi/ApiResponse/ApiQueryResponse.cs b/DM.MovieApi/ApiResponse/ApiQueryResponse.cs index 0551d51..a00e1ee 100644 --- a/DM.MovieApi/ApiResponse/ApiQueryResponse.cs +++ b/DM.MovieApi/ApiResponse/ApiQueryResponse.cs @@ -1,17 +1,16 @@ -namespace DM.MovieApi.ApiResponse +namespace DM.MovieApi.ApiResponse; + +/// +/// Standard response from an API call returning a single specific result. +/// Multiple item based based results (i.e., searches) are returned with an . +/// +public class ApiQueryResponse : ApiResponseBase { /// - /// Standard response from an API call returning a single specific result. - /// Multiple item based based results (i.e., searches) are returned with an . + /// The item returned from the API call. /// - public class ApiQueryResponse : ApiResponseBase - { - /// - /// The item returned from the API call. - /// - public T Item { get; internal set; } + public T Item { get; internal set; } - public override string ToString() - => Item.ToString(); - } + public override string ToString() + => Item.ToString(); } diff --git a/DM.MovieApi/ApiResponse/ApiResponseBase.cs b/DM.MovieApi/ApiResponse/ApiResponseBase.cs index e151fd2..d56cd01 100644 --- a/DM.MovieApi/ApiResponse/ApiResponseBase.cs +++ b/DM.MovieApi/ApiResponse/ApiResponseBase.cs @@ -1,26 +1,25 @@ -namespace DM.MovieApi.ApiResponse +namespace DM.MovieApi.ApiResponse; + +/// +/// Base class for all API responses from themoviedb.org. +/// +public abstract class ApiResponseBase { /// - /// Base class for all API responses from themoviedb.org. + /// Contains specific error information if an error was encountered during the API call to themoviedb.org. /// - public abstract class ApiResponseBase - { - /// - /// Contains specific error information if an error was encountered during the API call to themoviedb.org. - /// - public ApiError Error { get; internal set; } + public ApiError Error { get; internal set; } - /// - /// The API command text used for the API call to themoviedb.org. - /// - public string CommandText { get; internal set; } + /// + /// The API command text used for the API call to themoviedb.org. + /// + public string CommandText { get; internal set; } - /// - /// The JSON returned from themoviedb.org based on the query. - /// - public string Json { get; internal set; } + /// + /// The JSON returned from themoviedb.org based on the query. + /// + public string Json { get; internal set; } - public override string ToString() - => CommandText; - } + public override string ToString() + => CommandText; } diff --git a/DM.MovieApi/ApiResponse/ApiSearchResponse.cs b/DM.MovieApi/ApiResponse/ApiSearchResponse.cs index 80efba2..4f7d72c 100644 --- a/DM.MovieApi/ApiResponse/ApiSearchResponse.cs +++ b/DM.MovieApi/ApiResponse/ApiSearchResponse.cs @@ -1,42 +1,39 @@ -using System.Collections.Generic; -using System.Runtime.Serialization; -// ReSharper disable UnusedAutoPropertyAccessor.Local +// ReSharper disable UnusedAutoPropertyAccessor.Local -namespace DM.MovieApi.ApiResponse +namespace DM.MovieApi.ApiResponse; + +/// +/// Standard response from an API call returning a more than one result, i.e., a Search Result. +/// Single item based results are returned with an +/// . +/// +[DataContract] +public class ApiSearchResponse : ApiResponseBase { /// - /// Standard response from an API call returning a more than one result, i.e., a Search Result. - /// Single item based results are returned with an - /// . + /// The list of results from the search. /// - [DataContract] - public class ApiSearchResponse : ApiResponseBase - { - /// - /// The list of results from the search. - /// - [DataMember( Name = "results" )] - public IReadOnlyList Results { get; private set; } + [DataMember( Name = "results" )] + public IReadOnlyList Results { get; private set; } - /// - /// The current page number of the search result. - /// - [DataMember( Name = "page" )] - public int PageNumber { get; private set; } + /// + /// The current page number of the search result. + /// + [DataMember( Name = "page" )] + public int PageNumber { get; private set; } - /// - /// The total number of pages found from the search result. - /// - [DataMember( Name = "total_pages" )] - public int TotalPages { get; private set; } + /// + /// The total number of pages found from the search result. + /// + [DataMember( Name = "total_pages" )] + public int TotalPages { get; private set; } - /// - /// The total number of results from the search. - /// - [DataMember( Name = "total_results" )] - public int TotalResults { get; private set; } + /// + /// The total number of results from the search. + /// + [DataMember( Name = "total_results" )] + public int TotalResults { get; private set; } - public override string ToString() - => $"Page {PageNumber} of {TotalPages} ({TotalResults} total results)"; - } + public override string ToString() + => $"Page {PageNumber} of {TotalPages} ({TotalResults} total results)"; } diff --git a/DM.MovieApi/ApiResponse/TmdbStatusCode.cs b/DM.MovieApi/ApiResponse/TmdbStatusCode.cs index 235d904..3594197 100644 --- a/DM.MovieApi/ApiResponse/TmdbStatusCode.cs +++ b/DM.MovieApi/ApiResponse/TmdbStatusCode.cs @@ -1,217 +1,216 @@ using System.Diagnostics.CodeAnalysis; -namespace DM.MovieApi.ApiResponse +namespace DM.MovieApi.ApiResponse; + +/// +/// themoviedb.org Status Codes as defined by: https://www.themoviedb.org/documentation/api/status-codes +/// +[SuppressMessage( "ReSharper", "UnusedMember.Global" )] +public enum TmdbStatusCode { + Unknown = 0, + + /// + /// 200: Success. + /// + //[Description( "200: Success." )] + Success = 1, + + /// + /// 501: Invalid service: this service does not exist. + /// + //[Description( "501: Invalid service: this service does not exist." )] + InvalidService = 2, + + /// + /// 401: Authentication failed: You do not have permissions to access the service. + /// + //[Description( "401: Authentication failed: You do not have permissions to access the service." )] + InsufficientPermissions = 3, + + /// + /// 405: Invalid format: This service doesn't exist in that format. + /// + //[Description( "405: Invalid format: This service doesn't exist in that format." )] + InvalidFormat = 4, + + /// + /// 422: Invalid parameters: Your request parameters are incorrect. + /// + //[Description( "422: Invalid parameters: Your request parameters are incorrect." )] + InvalidParameters = 5, + + /// + /// 404: Invalid id: The pre-requisite id is invalid or not found. + /// + //[Description( "404: Invalid id: The pre-requisite id is invalid or not found." )] + InvalidId = 6, + + /// + /// 401: Invalid API key: You must be granted a valid key. + /// + //[Description( "401: Invalid API key: You must be granted a valid key." )] + InvalidApiKey = 7, + + /// + /// 403: Duplicate entry: The data you tried to submit already exists. + /// + //[Description( "403: Duplicate entry: The data you tried to submit already exists." )] + DuplicateEntry = 8, + + /// + /// 503: Service offline: This service is temporarily offline, try again later. + /// + //[Description( "503: Service offline: This service is temporarily offline, try again later." )] + ServiceOffline = 9, + + /// + /// 503: Service offline: This service is temporarily offline, try again later. + /// + //[Description( "401: Suspended API key: Access to your account has been suspended, contact TMDb." )] + SuspendedApiKey = 10, + + /// + /// 503: Service offline: This service is temporarily offline, try again later. + /// + //[Description( "500: Internal error: Something went wrong, contact TMDb." )] + InternalError = 11, + + /// + /// 201: The item/record was updated successfully. + /// + //[Description( "201: The item/record was updated successfully." )] + SuccessfulUpdate = 12, + + /// + /// 200: The item/record was deleted successfully. + /// + //[Description( "200: The item/record was deleted successfully." )] + SuccessfulDelete = 13, + + /// + /// 401: Authentication failed. + /// + //[Description( "401: Authentication failed." )] + AuthenticationFailed = 14, + + /// + /// 500: Failed. + /// + //[Description( "500: Failed." )] + Failed = 15, + + /// + /// 401: Device denied. + /// + //[Description( "401: Device denied." )] + DeviceDenied = 16, + + /// + /// 401: Session denied. + /// + //[Description( "401: Session denied." )] + SessionDenied = 17, + + /// + /// 400: Validation failed. + /// + //[Description( "400: Validation failed." )] + ValidationFailed = 18, + + /// + /// 406: Invalid accept header. + /// + //[Description( "406: Invalid accept header." )] + InvalidAcceptHeader = 19, + + /// + /// 422: Invalid date range: Should be a range no longer than 14 days. + /// + //[Description( "422: Invalid date range: Should be a range no longer than 14 days." )] + InvalidDateRange = 20, + + /// + /// 200: Entry not found: The item you are trying to edit cannot be found. + /// + //[Description( "200: Entry not found: The item you are trying to edit cannot be found." )] + EntryNotFound = 21, + + /// + /// 400: Invalid page: Pages start at 1 and max at 1000. They are expected to be an integer. + /// + //[Description( "400: Invalid page: Pages start at 1 and max at 1000. They are expected to be an integer." )] + InvalidPage = 22, + + /// + /// 400: Invalid date: Format needs to be YYYY-MM-DD. + /// + //[Description( "400: Invalid date: Format needs to be YYYY-MM-DD." )] + InvalidDate = 23, + + /// + /// 400: Invalid date: Format needs to be YYYY-MM-DD. + /// + //[Description( "504: Your request to the backend server timed out. Try again." )] + ServerTimeout = 24, + + /// + /// 400: Invalid date: Format needs to be YYYY-MM-DD. + /// + //[Description( "429: Your request count (#) is over the allowed limit of (40)." )] + RequestOverLimit = 25, + /// - /// themoviedb.org Status Codes as defined by: https://www.themoviedb.org/documentation/api/status-codes - /// - [SuppressMessage( "ReSharper", "UnusedMember.Global" )] - public enum TmdbStatusCode - { - Unknown = 0, - - /// - /// 200: Success. - /// - //[Description( "200: Success." )] - Success = 1, - - /// - /// 501: Invalid service: this service does not exist. - /// - //[Description( "501: Invalid service: this service does not exist." )] - InvalidService = 2, - - /// - /// 401: Authentication failed: You do not have permissions to access the service. - /// - //[Description( "401: Authentication failed: You do not have permissions to access the service." )] - InsufficientPermissions = 3, - - /// - /// 405: Invalid format: This service doesn't exist in that format. - /// - //[Description( "405: Invalid format: This service doesn't exist in that format." )] - InvalidFormat = 4, - - /// - /// 422: Invalid parameters: Your request parameters are incorrect. - /// - //[Description( "422: Invalid parameters: Your request parameters are incorrect." )] - InvalidParameters = 5, - - /// - /// 404: Invalid id: The pre-requisite id is invalid or not found. - /// - //[Description( "404: Invalid id: The pre-requisite id is invalid or not found." )] - InvalidId = 6, - - /// - /// 401: Invalid API key: You must be granted a valid key. - /// - //[Description( "401: Invalid API key: You must be granted a valid key." )] - InvalidApiKey = 7, - - /// - /// 403: Duplicate entry: The data you tried to submit already exists. - /// - //[Description( "403: Duplicate entry: The data you tried to submit already exists." )] - DuplicateEntry = 8, - - /// - /// 503: Service offline: This service is temporarily offline, try again later. - /// - //[Description( "503: Service offline: This service is temporarily offline, try again later." )] - ServiceOffline = 9, - - /// - /// 503: Service offline: This service is temporarily offline, try again later. - /// - //[Description( "401: Suspended API key: Access to your account has been suspended, contact TMDb." )] - SuspendedApiKey = 10, - - /// - /// 503: Service offline: This service is temporarily offline, try again later. - /// - //[Description( "500: Internal error: Something went wrong, contact TMDb." )] - InternalError = 11, - - /// - /// 201: The item/record was updated successfully. - /// - //[Description( "201: The item/record was updated successfully." )] - SuccessfulUpdate = 12, - - /// - /// 200: The item/record was deleted successfully. - /// - //[Description( "200: The item/record was deleted successfully." )] - SuccessfulDelete = 13, - - /// - /// 401: Authentication failed. - /// - //[Description( "401: Authentication failed." )] - AuthenticationFailed = 14, - - /// - /// 500: Failed. - /// - //[Description( "500: Failed." )] - Failed = 15, - - /// - /// 401: Device denied. - /// - //[Description( "401: Device denied." )] - DeviceDenied = 16, - - /// - /// 401: Session denied. - /// - //[Description( "401: Session denied." )] - SessionDenied = 17, - - /// - /// 400: Validation failed. - /// - //[Description( "400: Validation failed." )] - ValidationFailed = 18, - - /// - /// 406: Invalid accept header. - /// - //[Description( "406: Invalid accept header." )] - InvalidAcceptHeader = 19, - - /// - /// 422: Invalid date range: Should be a range no longer than 14 days. - /// - //[Description( "422: Invalid date range: Should be a range no longer than 14 days." )] - InvalidDateRange = 20, - - /// - /// 200: Entry not found: The item you are trying to edit cannot be found. - /// - //[Description( "200: Entry not found: The item you are trying to edit cannot be found." )] - EntryNotFound = 21, - - /// - /// 400: Invalid page: Pages start at 1 and max at 1000. They are expected to be an integer. - /// - //[Description( "400: Invalid page: Pages start at 1 and max at 1000. They are expected to be an integer." )] - InvalidPage = 22, - - /// - /// 400: Invalid date: Format needs to be YYYY-MM-DD. - /// - //[Description( "400: Invalid date: Format needs to be YYYY-MM-DD." )] - InvalidDate = 23, - - /// - /// 400: Invalid date: Format needs to be YYYY-MM-DD. - /// - //[Description( "504: Your request to the backend server timed out. Try again." )] - ServerTimeout = 24, - - /// - /// 400: Invalid date: Format needs to be YYYY-MM-DD. - /// - //[Description( "429: Your request count (#) is over the allowed limit of (40)." )] - RequestOverLimit = 25, - - /// - /// "400: You must provide a username and password. - /// - //[Description( "400: You must provide a username and password." )] - AuthenticationRequired = 26, - - /// - /// 400: Too many append to response objects: The maximum number of remote calls is 20. - /// - //[Description( "400: Too many append to response objects: The maximum number of remote calls is 20." )] - ResponseObjectOverflow = 27, - - /// - /// 400: Invalid timezone: Please consult the documentation for a valid timezone. - /// - //[Description( "400: Invalid timezone: Please consult the documentation for a valid timezone." )] - InvalidTimezone = 28, - - /// - /// 400: Invalid timezone: Please consult the documentation for a valid timezone. - /// - //[Description( "400: You must confirm this action: Please provide a confirm=true parameter." )] - ActionMustBeConfirmed = 29, - - /// - /// 401: Invalid username and/or password: You did not provide a valid login. - /// - //[Description( "401: Invalid username and/or password: You did not provide a valid login." )] - InvalidAuthentication = 30, - - /// - /// 401: Account disabled: Your account is no longer active. Contact TMDb if this is an error. - /// - //[Description( "401: Account disabled: Your account is no longer active. Contact TMDb if this is an error." )] - AccountDisabled = 31, - - /// - /// 401: Email not verified: Your email address has not been verified. - /// - //[Description( "401: Email not verified: Your email address has not been verified." )] - EmailNotVerified = 32, - - /// - /// 401: Invalid request token: The request token is either expired or invalid. - /// - //[Description( "401: Invalid request token: The request token is either expired or invalid." )] - InvalidRequestToken = 33, - - /// - /// 401: The resource you requested could not be found. - /// - //[Description( "401: The resource you requested could not be found." )] - ResourceNotFound = 34, - } + /// "400: You must provide a username and password. + /// + //[Description( "400: You must provide a username and password." )] + AuthenticationRequired = 26, + + /// + /// 400: Too many append to response objects: The maximum number of remote calls is 20. + /// + //[Description( "400: Too many append to response objects: The maximum number of remote calls is 20." )] + ResponseObjectOverflow = 27, + + /// + /// 400: Invalid timezone: Please consult the documentation for a valid timezone. + /// + //[Description( "400: Invalid timezone: Please consult the documentation for a valid timezone." )] + InvalidTimezone = 28, + + /// + /// 400: Invalid timezone: Please consult the documentation for a valid timezone. + /// + //[Description( "400: You must confirm this action: Please provide a confirm=true parameter." )] + ActionMustBeConfirmed = 29, + + /// + /// 401: Invalid username and/or password: You did not provide a valid login. + /// + //[Description( "401: Invalid username and/or password: You did not provide a valid login." )] + InvalidAuthentication = 30, + + /// + /// 401: Account disabled: Your account is no longer active. Contact TMDb if this is an error. + /// + //[Description( "401: Account disabled: Your account is no longer active. Contact TMDb if this is an error." )] + AccountDisabled = 31, + + /// + /// 401: Email not verified: Your email address has not been verified. + /// + //[Description( "401: Email not verified: Your email address has not been verified." )] + EmailNotVerified = 32, + + /// + /// 401: Invalid request token: The request token is either expired or invalid. + /// + //[Description( "401: Invalid request token: The request token is either expired or invalid." )] + InvalidRequestToken = 33, + + /// + /// 401: The resource you requested could not be found. + /// + //[Description( "401: The resource you requested could not be found." )] + ResourceNotFound = 34, } diff --git a/DM.MovieApi/GlobalUsings.cs b/DM.MovieApi/GlobalUsings.cs new file mode 100644 index 0000000..baa8d15 --- /dev/null +++ b/DM.MovieApi/GlobalUsings.cs @@ -0,0 +1,12 @@ +// Global using directives + +global using System; +global using System.Collections.Generic; +global using System.Linq; +global using System.Runtime.Serialization; +global using System.Threading.Tasks; +global using DM.MovieApi.ApiRequest; +global using DM.MovieApi.ApiResponse; +global using DM.MovieApi.MovieDb.Genres; +global using DM.MovieApi.MovieDb.Movies; +global using DM.MovieApi.Shims; diff --git a/DM.MovieApi/IApiSettings.cs b/DM.MovieApi/IApiSettings.cs index d90d93b..f6bcffc 100644 --- a/DM.MovieApi/IApiSettings.cs +++ b/DM.MovieApi/IApiSettings.cs @@ -1,9 +1,8 @@ -namespace DM.MovieApi +namespace DM.MovieApi; + +internal interface IApiSettings { - internal interface IApiSettings - { - string ApiUrl { get; } + string ApiUrl { get; } - string BearerToken { get; } - } + string BearerToken { get; } } diff --git a/DM.MovieApi/IMovieDbApi.cs b/DM.MovieApi/IMovieDbApi.cs index 5b1d0bc..5d1c3f0 100644 --- a/DM.MovieApi/IMovieDbApi.cs +++ b/DM.MovieApi/IMovieDbApi.cs @@ -2,63 +2,60 @@ using DM.MovieApi.MovieDb.Companies; using DM.MovieApi.MovieDb.Configuration; using DM.MovieApi.MovieDb.Discover; -using DM.MovieApi.MovieDb.Genres; using DM.MovieApi.MovieDb.IndustryProfessions; -using DM.MovieApi.MovieDb.Movies; using DM.MovieApi.MovieDb.People; using DM.MovieApi.MovieDb.TV; -namespace DM.MovieApi +namespace DM.MovieApi; + +/// +/// Global interface exposing all API interfaces against themoviedb.org that are +/// currently available in this release. +/// +public interface IMovieDbApi { /// - /// Global interface exposing all API interfaces against themoviedb.org that are - /// currently available in this release. + /// Provides access for retrieving production company information. /// - public interface IMovieDbApi - { - /// - /// Provides access for retrieving production company information. - /// - IApiCompanyRequest Companies { get; } + IApiCompanyRequest Companies { get; } - /// - /// Provides access for retrieving themoviedb.org configuration information. - /// - IApiConfigurationRequest Configuration { get; } + /// + /// Provides access for retrieving themoviedb.org configuration information. + /// + IApiConfigurationRequest Configuration { get; } - /// - /// Provides access for retrieving Movie and TV genres. - /// - IApiGenreRequest Genres { get; } + /// + /// Provides access for retrieving Movie and TV genres. + /// + IApiGenreRequest Genres { get; } - /// - /// Provides access for retrieving information about Movie/TV industry specific professions. - /// - IApiProfessionRequest IndustryProfessions { get; } + /// + /// Provides access for retrieving information about Movie/TV industry specific professions. + /// + IApiProfessionRequest IndustryProfessions { get; } - /// - /// Provides access for retrieving information about Movies. - /// - IApiMovieRequest Movies { get; } + /// + /// Provides access for retrieving information about Movies. + /// + IApiMovieRequest Movies { get; } - /// - /// Provides access for retrieving movie rating information. - /// - IApiMovieRatingRequest MovieRatings { get; } + /// + /// Provides access for retrieving movie rating information. + /// + IApiMovieRatingRequest MovieRatings { get; } - /// - /// Provides access for retrieving information about Television shows. - /// - IApiTVShowRequest Television { get; } + /// + /// Provides access for retrieving information about Television shows. + /// + IApiTVShowRequest Television { get; } - /// - /// Provides access for retrieving information about People. - /// - IApiPeopleRequest People { get; } + /// + /// Provides access for retrieving information about People. + /// + IApiPeopleRequest People { get; } - /// - /// Provides access for discovering movies based on a filtered set of parameters. - /// - IApiDiscoverRequest Discover { get; } - } + /// + /// Provides access for discovering movies based on a filtered set of parameters. + /// + IApiDiscoverRequest Discover { get; } } diff --git a/DM.MovieApi/MovieDb/Certifications/ApiMovieRatingRequest.cs b/DM.MovieApi/MovieDb/Certifications/ApiMovieRatingRequest.cs index 922cb3e..daced78 100644 --- a/DM.MovieApi/MovieDb/Certifications/ApiMovieRatingRequest.cs +++ b/DM.MovieApi/MovieDb/Certifications/ApiMovieRatingRequest.cs @@ -1,53 +1,45 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using DM.MovieApi.ApiRequest; -using DM.MovieApi.ApiResponse; -using DM.MovieApi.Shims; -using Newtonsoft.Json.Linq; - -namespace DM.MovieApi.MovieDb.Certifications +using Newtonsoft.Json.Linq; + +namespace DM.MovieApi.MovieDb.Certifications; + +internal class ApiMovieRatingRequest : ApiRequestBase, IApiMovieRatingRequest { - internal class ApiMovieRatingRequest : ApiRequestBase, IApiMovieRatingRequest - { - [ImportingConstructor] - public ApiMovieRatingRequest( IApiSettings settings ) - : base( settings ) - { } + [ImportingConstructor] + public ApiMovieRatingRequest( IApiSettings settings ) + : base( settings ) + { } - public async Task> GetMovieRatingsAsync() - { - const string command = "certification/movie/list"; + public async Task> GetMovieRatingsAsync() + { + const string command = "certification/movie/list"; - ApiQueryResponse response = await base.QueryAsync( command, RatingsDeserializer ); + ApiQueryResponse response = await base.QueryAsync( command, RatingsDeserializer ); - return response; - } + return response; + } - private MovieRatings RatingsDeserializer( string json ) - { - var obj = JObject.Parse( json ); + private MovieRatings RatingsDeserializer( string json ) + { + var obj = JObject.Parse( json ); - JToken certs = obj["certifications"]; + JToken certs = obj["certifications"]; - // ReSharper disable once PossibleNullReferenceException - var ratings = certs.ToObject(); + // ReSharper disable once PossibleNullReferenceException + var ratings = certs.ToObject(); - Func, IReadOnlyList> reorder = - list => list.OrderBy( x => x.Order ).ThenBy( x => x.Rating ).ToList().AsReadOnly(); + Func, IReadOnlyList> reorder = + list => list.OrderBy( x => x.Order ).ThenBy( x => x.Rating ).ToList().AsReadOnly(); - // ReSharper disable once PossibleNullReferenceException - ratings.Australia = reorder( ratings.Australia ); - ratings.Canada = reorder( ratings.Canada ); - ratings.France = reorder( ratings.France ); - ratings.Germany = reorder( ratings.Germany ); - ratings.India = reorder( ratings.India ); - ratings.NewZealand = reorder( ratings.NewZealand ); - ratings.UnitedKingdom = reorder( ratings.UnitedKingdom ); - ratings.UnitedStates = reorder( ratings.UnitedStates ); + // ReSharper disable once PossibleNullReferenceException + ratings.Australia = reorder( ratings.Australia ); + ratings.Canada = reorder( ratings.Canada ); + ratings.France = reorder( ratings.France ); + ratings.Germany = reorder( ratings.Germany ); + ratings.India = reorder( ratings.India ); + ratings.NewZealand = reorder( ratings.NewZealand ); + ratings.UnitedKingdom = reorder( ratings.UnitedKingdom ); + ratings.UnitedStates = reorder( ratings.UnitedStates ); - return ratings; - } + return ratings; } } diff --git a/DM.MovieApi/MovieDb/Certifications/IApiMovieRatingRequest.cs b/DM.MovieApi/MovieDb/Certifications/IApiMovieRatingRequest.cs index 337a749..826bbb9 100644 --- a/DM.MovieApi/MovieDb/Certifications/IApiMovieRatingRequest.cs +++ b/DM.MovieApi/MovieDb/Certifications/IApiMovieRatingRequest.cs @@ -1,17 +1,12 @@ -using System.Threading.Tasks; -using DM.MovieApi.ApiRequest; -using DM.MovieApi.ApiResponse; +namespace DM.MovieApi.MovieDb.Certifications; -namespace DM.MovieApi.MovieDb.Certifications +/// +/// Interface for retrieving movie rating information. +/// +public interface IApiMovieRatingRequest : IApiRequest { /// - /// Interface for retrieving movie rating information. + /// Gets the list of supported certifications (movie ratings) for movies. /// - public interface IApiMovieRatingRequest : IApiRequest - { - /// - /// Gets the list of supported certifications (movie ratings) for movies. - /// - Task> GetMovieRatingsAsync(); - } + Task> GetMovieRatingsAsync(); } diff --git a/DM.MovieApi/MovieDb/Certifications/MovieRatings.cs b/DM.MovieApi/MovieDb/Certifications/MovieRatings.cs index afd99e1..650a899 100644 --- a/DM.MovieApi/MovieDb/Certifications/MovieRatings.cs +++ b/DM.MovieApi/MovieDb/Certifications/MovieRatings.cs @@ -1,62 +1,57 @@ -using System; -using System.Collections.Generic; -using System.Runtime.Serialization; +namespace DM.MovieApi.MovieDb.Certifications; -namespace DM.MovieApi.MovieDb.Certifications +[DataContract] +public class Certification { - [DataContract] - public class Certification - { - [DataMember( Name = "certification" )] - public string Rating { get; set; } + [DataMember( Name = "certification" )] + public string Rating { get; set; } - [DataMember( Name = "meaning" )] - public string Meaning { get; set; } + [DataMember( Name = "meaning" )] + public string Meaning { get; set; } - [DataMember( Name = "order" )] - public int Order { get; set; } + [DataMember( Name = "order" )] + public int Order { get; set; } - public override string ToString() - => $"{Rating}: {Meaning.Substring( 75 )}"; - } + public override string ToString() + => $"{Rating}: {Meaning.Substring( 75 )}"; +} - [DataContract] - public class MovieRatings - { - [DataMember( Name = "AU" )] - public IReadOnlyList Australia { get; set; } +[DataContract] +public class MovieRatings +{ + [DataMember( Name = "AU" )] + public IReadOnlyList Australia { get; set; } - [DataMember( Name = "CA" )] - public IReadOnlyList Canada { get; set; } + [DataMember( Name = "CA" )] + public IReadOnlyList Canada { get; set; } - [DataMember( Name = "FR" )] - public IReadOnlyList France { get; set; } + [DataMember( Name = "FR" )] + public IReadOnlyList France { get; set; } - [DataMember( Name = "DE" )] - public IReadOnlyList Germany { get; set; } + [DataMember( Name = "DE" )] + public IReadOnlyList Germany { get; set; } - [DataMember( Name = "IN" )] - public IReadOnlyList India { get; set; } + [DataMember( Name = "IN" )] + public IReadOnlyList India { get; set; } - [DataMember( Name = "NZ" )] - public IReadOnlyList NewZealand { get; set; } + [DataMember( Name = "NZ" )] + public IReadOnlyList NewZealand { get; set; } - [DataMember( Name = "US" )] - public IReadOnlyList UnitedStates { get; set; } + [DataMember( Name = "US" )] + public IReadOnlyList UnitedStates { get; set; } - [DataMember( Name = "GB" )] - public IReadOnlyList UnitedKingdom { get; set; } + [DataMember( Name = "GB" )] + public IReadOnlyList UnitedKingdom { get; set; } - public MovieRatings() - { - UnitedStates = Array.Empty(); - Canada = Array.Empty(); - Australia = Array.Empty(); - Germany = Array.Empty(); - France = Array.Empty(); - NewZealand = Array.Empty(); - India = Array.Empty(); - UnitedKingdom = Array.Empty(); - } + public MovieRatings() + { + UnitedStates = Array.Empty(); + Canada = Array.Empty(); + Australia = Array.Empty(); + Germany = Array.Empty(); + France = Array.Empty(); + NewZealand = Array.Empty(); + India = Array.Empty(); + UnitedKingdom = Array.Empty(); } } diff --git a/DM.MovieApi/MovieDb/Collections/CollectionInfo.cs b/DM.MovieApi/MovieDb/Collections/CollectionInfo.cs index e1e7d2c..fdc5d96 100644 --- a/DM.MovieApi/MovieDb/Collections/CollectionInfo.cs +++ b/DM.MovieApi/MovieDb/Collections/CollectionInfo.cs @@ -1,30 +1,27 @@ -using System.Runtime.Serialization; +namespace DM.MovieApi.MovieDb.Collections; -namespace DM.MovieApi.MovieDb.Collections +[DataContract] +public class CollectionInfo { - [DataContract] - public class CollectionInfo - { - [DataMember( Name = "id" )] - public int Id { get; set; } + [DataMember( Name = "id" )] + public int Id { get; set; } - [DataMember( Name = "name" )] - public string Name { get; set; } + [DataMember( Name = "name" )] + public string Name { get; set; } - [DataMember( Name = "poster_path" )] - public string PosterPath { get; set; } + [DataMember( Name = "poster_path" )] + public string PosterPath { get; set; } - [DataMember( Name = "backdrop_path" )] - public string BackdropPath { get; set; } + [DataMember( Name = "backdrop_path" )] + public string BackdropPath { get; set; } - public override string ToString() + public override string ToString() + { + if( string.IsNullOrWhiteSpace( Name ) ) { - if( string.IsNullOrWhiteSpace( Name ) ) - { - return "n/a"; - } - - return $"{Name} ({Id})"; + return "n/a"; } + + return $"{Name} ({Id})"; } } diff --git a/DM.MovieApi/MovieDb/Companies/ApiCompanyRequest.cs b/DM.MovieApi/MovieDb/Companies/ApiCompanyRequest.cs index bdc7dc4..57f88ce 100644 --- a/DM.MovieApi/MovieDb/Companies/ApiCompanyRequest.cs +++ b/DM.MovieApi/MovieDb/Companies/ApiCompanyRequest.cs @@ -1,52 +1,43 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using DM.MovieApi.ApiRequest; -using DM.MovieApi.ApiResponse; -using DM.MovieApi.MovieDb.Genres; -using DM.MovieApi.MovieDb.Movies; -using DM.MovieApi.Shims; - -namespace DM.MovieApi.MovieDb.Companies +namespace DM.MovieApi.MovieDb.Companies; + +internal class ApiCompanyRequest : ApiRequestBase, IApiCompanyRequest { - internal class ApiCompanyRequest : ApiRequestBase, IApiCompanyRequest - { - private readonly IApiGenreRequest _genreApi; + private readonly IApiGenreRequest _genreApi; - [ImportingConstructor] - public ApiCompanyRequest( IApiSettings settings, IApiGenreRequest genreApi ) - : base( settings ) - { - _genreApi = genreApi; - } + [ImportingConstructor] + public ApiCompanyRequest( IApiSettings settings, IApiGenreRequest genreApi ) + : base( settings ) + { + _genreApi = genreApi; + } - public async Task> FindByIdAsync( int companyId ) - { - string command = $"company/{companyId}"; + public async Task> FindByIdAsync( int companyId ) + { + string command = $"company/{companyId}"; - ApiQueryResponse response = await base.QueryAsync( command ); + ApiQueryResponse response = await base.QueryAsync( command ); - return response; - } + return response; + } - public async Task> GetMoviesAsync( int companyId, int pageNumber = 1, string language = "en" ) + public async Task> GetMoviesAsync( int companyId, int pageNumber = 1, string language = "en" ) + { + var param = new Dictionary { - var param = new Dictionary - { - {"language", language}, - }; - - string command = $"company/{companyId}/movies"; + {"language", language}, + }; - ApiSearchResponse response = await base.SearchAsync( command, pageNumber, param ); + string command = $"company/{companyId}/movies"; - if( response.Error != null ) - { - return response; - } - - response.Results.PopulateGenres( _genreApi ); + ApiSearchResponse response = await base.SearchAsync( command, pageNumber, param ); + if( response.Error != null ) + { return response; } + + response.Results.PopulateGenres( _genreApi ); + + return response; } } diff --git a/DM.MovieApi/MovieDb/Companies/IApiCompanyRequest.cs b/DM.MovieApi/MovieDb/Companies/IApiCompanyRequest.cs index 5a7cbef..a9fdd22 100644 --- a/DM.MovieApi/MovieDb/Companies/IApiCompanyRequest.cs +++ b/DM.MovieApi/MovieDb/Companies/IApiCompanyRequest.cs @@ -1,28 +1,22 @@ -using System.Threading.Tasks; -using DM.MovieApi.ApiRequest; -using DM.MovieApi.ApiResponse; -using DM.MovieApi.MovieDb.Movies; +namespace DM.MovieApi.MovieDb.Companies; -namespace DM.MovieApi.MovieDb.Companies +/// +/// Interface for retrieving information about a production company. +/// +public interface IApiCompanyRequest : IApiRequest { /// - /// Interface for retrieving information about a production company. + /// Gets all the basic information about a specific company. /// - public interface IApiCompanyRequest : IApiRequest - { - /// - /// Gets all the basic information about a specific company. - /// - /// The company Id is typically found from a Movie or TV query. - Task> FindByIdAsync( int companyId ); + /// The company Id is typically found from a Movie or TV query. + Task> FindByIdAsync( int companyId ); - /// - /// Get the list of movies associated with a particular company. - /// - /// The company Id is typically found from a Movie or TV query. - /// Default is page 1. The page number to retrieve; the will contain the current page returned and the total number of pages available. - /// Default is English. The ISO 639-1 language code to retrieve the result from. - /// - Task> GetMoviesAsync( int companyId, int pageNumber = 1, string language = "en" ); - } + /// + /// Get the list of movies associated with a particular company. + /// + /// The company Id is typically found from a Movie or TV query. + /// Default is page 1. The page number to retrieve; the will contain the current page returned and the total number of pages available. + /// Default is English. The ISO 639-1 language code to retrieve the result from. + /// + Task> GetMoviesAsync( int companyId, int pageNumber = 1, string language = "en" ); } diff --git a/DM.MovieApi/MovieDb/Companies/ParentCompany.cs b/DM.MovieApi/MovieDb/Companies/ParentCompany.cs index 4600a58..5c51459 100644 --- a/DM.MovieApi/MovieDb/Companies/ParentCompany.cs +++ b/DM.MovieApi/MovieDb/Companies/ParentCompany.cs @@ -1,27 +1,24 @@ -using System.Runtime.Serialization; +namespace DM.MovieApi.MovieDb.Companies; -namespace DM.MovieApi.MovieDb.Companies +[DataContract] +public class ParentCompany { - [DataContract] - public class ParentCompany - { - [DataMember( Name = "id" )] - public int Id { get; set; } + [DataMember( Name = "id" )] + public int Id { get; set; } - [DataMember( Name = "name" )] - public string Name { get; set; } + [DataMember( Name = "name" )] + public string Name { get; set; } - [DataMember( Name = "logo_path" )] - public string LogoPath { get; set; } + [DataMember( Name = "logo_path" )] + public string LogoPath { get; set; } - public override string ToString() + public override string ToString() + { + if( string.IsNullOrWhiteSpace( Name ) ) { - if( string.IsNullOrWhiteSpace( Name ) ) - { - return "n/a"; - } - - return $"{Name} ({Id})"; + return "n/a"; } + + return $"{Name} ({Id})"; } } diff --git a/DM.MovieApi/MovieDb/Companies/ProductionCompany.cs b/DM.MovieApi/MovieDb/Companies/ProductionCompany.cs index 2daa3c1..0c60df8 100644 --- a/DM.MovieApi/MovieDb/Companies/ProductionCompany.cs +++ b/DM.MovieApi/MovieDb/Companies/ProductionCompany.cs @@ -1,32 +1,29 @@ -using System.Runtime.Serialization; +namespace DM.MovieApi.MovieDb.Companies; -namespace DM.MovieApi.MovieDb.Companies +[DataContract] +public class ProductionCompany { - [DataContract] - public class ProductionCompany - { - [DataMember( Name = "id" )] - public int Id { get; set; } + [DataMember( Name = "id" )] + public int Id { get; set; } - [DataMember( Name = "name" )] - public string Name { get; set; } + [DataMember( Name = "name" )] + public string Name { get; set; } - [DataMember( Name = "description" )] - public string Description { get; set; } + [DataMember( Name = "description" )] + public string Description { get; set; } - [DataMember( Name = "headquarters" )] - public string Headquarters { get; set; } + [DataMember( Name = "headquarters" )] + public string Headquarters { get; set; } - [DataMember( Name = "homepage" )] - public string Homepage { get; set; } + [DataMember( Name = "homepage" )] + public string Homepage { get; set; } - [DataMember( Name = "logo_path" )] - public string LogoPath { get; set; } + [DataMember( Name = "logo_path" )] + public string LogoPath { get; set; } - [DataMember( Name = "parent_company" )] - public ParentCompany ParentCompany { get; set; } + [DataMember( Name = "parent_company" )] + public ParentCompany ParentCompany { get; set; } - public override string ToString() - => $"{Name} ({Id})"; - } + public override string ToString() + => $"{Name} ({Id})"; } diff --git a/DM.MovieApi/MovieDb/Companies/ProductionCompanyInfo.cs b/DM.MovieApi/MovieDb/Companies/ProductionCompanyInfo.cs index 54266b3..5d8cc6d 100644 --- a/DM.MovieApi/MovieDb/Companies/ProductionCompanyInfo.cs +++ b/DM.MovieApi/MovieDb/Companies/ProductionCompanyInfo.cs @@ -1,58 +1,54 @@ -using System.Collections.Generic; -using System.Runtime.Serialization; +namespace DM.MovieApi.MovieDb.Companies; -namespace DM.MovieApi.MovieDb.Companies +[DataContract] +public class ProductionCompanyInfo : IEqualityComparer { - [DataContract] - public class ProductionCompanyInfo : IEqualityComparer - { - [DataMember( Name = "id" )] - public int Id { get; set; } + [DataMember( Name = "id" )] + public int Id { get; set; } - [DataMember( Name = "name" )] - public string Name { get; set; } + [DataMember( Name = "name" )] + public string Name { get; set; } - public ProductionCompanyInfo( int id, string name ) - { - Id = id; - Name = name; - } + public ProductionCompanyInfo( int id, string name ) + { + Id = id; + Name = name; + } - public override bool Equals( object obj ) + public override bool Equals( object obj ) + { + if( obj is not ProductionCompanyInfo info ) { - if( obj is not ProductionCompanyInfo info ) - { - return false; - } - - return Equals( this, info ); + return false; } - public bool Equals( ProductionCompanyInfo x, ProductionCompanyInfo y ) - => x != null && y != null && x.Id == y.Id && x.Name == y.Name; + return Equals( this, info ); + } + + public bool Equals( ProductionCompanyInfo x, ProductionCompanyInfo y ) + => x != null && y != null && x.Id == y.Id && x.Name == y.Name; - public override int GetHashCode() - => GetHashCode( this ); + public override int GetHashCode() + => GetHashCode( this ); - public int GetHashCode( ProductionCompanyInfo obj ) + public int GetHashCode( ProductionCompanyInfo obj ) + { + unchecked // Overflow is fine, just wrap { - unchecked // Overflow is fine, just wrap - { - int hash = 17; - hash = hash * 23 + obj.Id.GetHashCode(); - hash = hash * 23 + obj.Name.GetHashCode(); - return hash; - } + int hash = 17; + hash = hash * 23 + obj.Id.GetHashCode(); + hash = hash * 23 + obj.Name.GetHashCode(); + return hash; } + } - public override string ToString() + public override string ToString() + { + if( string.IsNullOrWhiteSpace( Name ) ) { - if( string.IsNullOrWhiteSpace( Name ) ) - { - return "n/a"; - } - - return $"{Name} ({Id})"; + return "n/a"; } + + return $"{Name} ({Id})"; } } diff --git a/DM.MovieApi/MovieDb/Configuration/ApiConfiguration.cs b/DM.MovieApi/MovieDb/Configuration/ApiConfiguration.cs index fa3fb4e..fa143fb 100644 --- a/DM.MovieApi/MovieDb/Configuration/ApiConfiguration.cs +++ b/DM.MovieApi/MovieDb/Configuration/ApiConfiguration.cs @@ -1,25 +1,21 @@ -using System.Collections.Generic; -using System.Runtime.Serialization; +namespace DM.MovieApi.MovieDb.Configuration; -namespace DM.MovieApi.MovieDb.Configuration +[DataContract] +public class ApiConfiguration { - [DataContract] - public class ApiConfiguration - { - [DataMember( Name = "images" )] - public ImageConfiguration Images { get; private set; } + [DataMember( Name = "images" )] + public ImageConfiguration Images { get; private set; } - [DataMember( Name = "change_keys" )] - public IReadOnlyList ChangeKeys { get; private set; } + [DataMember( Name = "change_keys" )] + public IReadOnlyList ChangeKeys { get; private set; } - public override string ToString() + public override string ToString() + { + if( !string.IsNullOrWhiteSpace( Images?.RootUrl ) ) { - if( !string.IsNullOrWhiteSpace( Images?.RootUrl ) ) - { - return Images.RootUrl; - } - - return "not set"; + return Images.RootUrl; } + + return "not set"; } } diff --git a/DM.MovieApi/MovieDb/Configuration/ApiConfigurationRequest.cs b/DM.MovieApi/MovieDb/Configuration/ApiConfigurationRequest.cs index c30d739..86bb3e7 100644 --- a/DM.MovieApi/MovieDb/Configuration/ApiConfigurationRequest.cs +++ b/DM.MovieApi/MovieDb/Configuration/ApiConfigurationRequest.cs @@ -1,22 +1,16 @@ -using System.Threading.Tasks; -using DM.MovieApi.ApiRequest; -using DM.MovieApi.ApiResponse; -using DM.MovieApi.Shims; +namespace DM.MovieApi.MovieDb.Configuration; -namespace DM.MovieApi.MovieDb.Configuration +internal class ApiConfigurationRequest : ApiRequestBase, IApiConfigurationRequest { - internal class ApiConfigurationRequest : ApiRequestBase, IApiConfigurationRequest - { - [ImportingConstructor] - public ApiConfigurationRequest( IApiSettings settings ) - : base( settings ) - { } + [ImportingConstructor] + public ApiConfigurationRequest( IApiSettings settings ) + : base( settings ) + { } - public async Task> GetAsync() - { - ApiQueryResponse response = await base.QueryAsync( "configuration" ); + public async Task> GetAsync() + { + ApiQueryResponse response = await base.QueryAsync( "configuration" ); - return response; - } + return response; } } diff --git a/DM.MovieApi/MovieDb/Configuration/IApiConfigurationRequest.cs b/DM.MovieApi/MovieDb/Configuration/IApiConfigurationRequest.cs index eea3087..5140b99 100644 --- a/DM.MovieApi/MovieDb/Configuration/IApiConfigurationRequest.cs +++ b/DM.MovieApi/MovieDb/Configuration/IApiConfigurationRequest.cs @@ -1,21 +1,16 @@ -using System.Threading.Tasks; -using DM.MovieApi.ApiRequest; -using DM.MovieApi.ApiResponse; +namespace DM.MovieApi.MovieDb.Configuration; -namespace DM.MovieApi.MovieDb.Configuration +/// +/// Interface for retrieving themoviedb.org configuration information. +/// +public interface IApiConfigurationRequest : IApiRequest { /// - /// Interface for retrieving themoviedb.org configuration information. + /// Get themoviedb.org system wide configuration information. Some elements of themoviedb.org + /// API require knowledge of the configuration data. The purpose of the + /// is to try and keep the actual API responses as light as possible. + /// It is recommended you cache this data within your application and check for updates every few days. + /// This method currently holds the data relevant to building image URLs as well as the change key map. /// - public interface IApiConfigurationRequest : IApiRequest - { - /// - /// Get themoviedb.org system wide configuration information. Some elements of themoviedb.org - /// API require knowledge of the configuration data. The purpose of the - /// is to try and keep the actual API responses as light as possible. - /// It is recommended you cache this data within your application and check for updates every few days. - /// This method currently holds the data relevant to building image URLs as well as the change key map. - /// - Task> GetAsync(); - } + Task> GetAsync(); } diff --git a/DM.MovieApi/MovieDb/Configuration/ImageConfiguration.cs b/DM.MovieApi/MovieDb/Configuration/ImageConfiguration.cs index 0cec007..46b8bf2 100644 --- a/DM.MovieApi/MovieDb/Configuration/ImageConfiguration.cs +++ b/DM.MovieApi/MovieDb/Configuration/ImageConfiguration.cs @@ -1,40 +1,36 @@ -using System.Collections.Generic; -using System.Runtime.Serialization; +namespace DM.MovieApi.MovieDb.Configuration; -namespace DM.MovieApi.MovieDb.Configuration +[DataContract] +public class ImageConfiguration { - [DataContract] - public class ImageConfiguration - { - [DataMember( Name = "base_url" )] - public string RootUrl { get; private set; } + [DataMember( Name = "base_url" )] + public string RootUrl { get; private set; } - [DataMember( Name = "secure_base_url" )] - public string SecureRootUrl { get; private set; } + [DataMember( Name = "secure_base_url" )] + public string SecureRootUrl { get; private set; } - [DataMember( Name = "backdrop_sizes" )] - public IReadOnlyList BackDrops { get; private set; } + [DataMember( Name = "backdrop_sizes" )] + public IReadOnlyList BackDrops { get; private set; } - [DataMember( Name = "logo_sizes" )] - public IReadOnlyList Logos { get; private set; } + [DataMember( Name = "logo_sizes" )] + public IReadOnlyList Logos { get; private set; } - [DataMember( Name = "poster_sizes" )] - public IReadOnlyList Posters { get; private set; } + [DataMember( Name = "poster_sizes" )] + public IReadOnlyList Posters { get; private set; } - [DataMember( Name = "profile_sizes" )] - public IReadOnlyList Profiles { get; private set; } + [DataMember( Name = "profile_sizes" )] + public IReadOnlyList Profiles { get; private set; } - [DataMember( Name = "still_sizes" )] - public IReadOnlyList Stills { get; private set; } + [DataMember( Name = "still_sizes" )] + public IReadOnlyList Stills { get; private set; } - public override string ToString() + public override string ToString() + { + if( !string.IsNullOrWhiteSpace( RootUrl ) ) { - if( !string.IsNullOrWhiteSpace( RootUrl ) ) - { - return RootUrl; - } - - return "not set"; + return RootUrl; } + + return "not set"; } } diff --git a/DM.MovieApi/MovieDb/Country.cs b/DM.MovieApi/MovieDb/Country.cs index 7c3d368..c9e0af5 100644 --- a/DM.MovieApi/MovieDb/Country.cs +++ b/DM.MovieApi/MovieDb/Country.cs @@ -1,58 +1,54 @@ -using System.Collections.Generic; -using System.Runtime.Serialization; +namespace DM.MovieApi.MovieDb; -namespace DM.MovieApi.MovieDb +[DataContract] +public class Country : IEqualityComparer { - [DataContract] - public class Country : IEqualityComparer - { - [DataMember( Name = "iso_3166_1" )] - public string Iso3166Code { get; set; } + [DataMember( Name = "iso_3166_1" )] + public string Iso3166Code { get; set; } - [DataMember( Name = "name" )] - public string Name { get; set; } + [DataMember( Name = "name" )] + public string Name { get; set; } - public Country( string iso3166Code, string name ) - { - Iso3166Code = iso3166Code; - Name = name; - } + public Country( string iso3166Code, string name ) + { + Iso3166Code = iso3166Code; + Name = name; + } - public override bool Equals( object obj ) + public override bool Equals( object obj ) + { + if( obj is not Country country ) { - if( obj is not Country country ) - { - return false; - } - - return Equals( this, country ); + return false; } - public bool Equals( Country x, Country y ) - => x != null && y != null && x.Iso3166Code == y.Iso3166Code && x.Name == y.Name; + return Equals( this, country ); + } + + public bool Equals( Country x, Country y ) + => x != null && y != null && x.Iso3166Code == y.Iso3166Code && x.Name == y.Name; - public override int GetHashCode() => - GetHashCode( this ); + public override int GetHashCode() => + GetHashCode( this ); - public int GetHashCode( Country obj ) + public int GetHashCode( Country obj ) + { + unchecked // Overflow is fine, just wrap { - unchecked // Overflow is fine, just wrap - { - int hash = 17; - hash = hash * 23 + obj.Iso3166Code.GetHashCode(); - hash = hash * 23 + obj.Name.GetHashCode(); - return hash; - } + int hash = 17; + hash = hash * 23 + obj.Iso3166Code.GetHashCode(); + hash = hash * 23 + obj.Name.GetHashCode(); + return hash; } + } - public override string ToString() + public override string ToString() + { + if( string.IsNullOrWhiteSpace( Name ) ) { - if( string.IsNullOrWhiteSpace( Name ) ) - { - return "n/a"; - } - - return $"{Name} ({Iso3166Code})"; + return "n/a"; } + + return $"{Name} ({Iso3166Code})"; } } diff --git a/DM.MovieApi/MovieDb/Discover/ApiDiscoverRequest.cs b/DM.MovieApi/MovieDb/Discover/ApiDiscoverRequest.cs index 0fb32c3..06cff34 100644 --- a/DM.MovieApi/MovieDb/Discover/ApiDiscoverRequest.cs +++ b/DM.MovieApi/MovieDb/Discover/ApiDiscoverRequest.cs @@ -1,41 +1,32 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using DM.MovieApi.ApiRequest; -using DM.MovieApi.ApiResponse; -using DM.MovieApi.MovieDb.Genres; -using DM.MovieApi.MovieDb.Movies; -using DM.MovieApi.Shims; - -namespace DM.MovieApi.MovieDb.Discover -{ - internal class ApiDiscoverRequest : ApiRequestBase, IApiDiscoverRequest - { - private readonly IApiGenreRequest _genreApi; +namespace DM.MovieApi.MovieDb.Discover; - [ImportingConstructor] - public ApiDiscoverRequest( IApiSettings settings, IApiGenreRequest genreApi ) - : base( settings ) - { - _genreApi = genreApi; - } - - public async Task> DiscoverMoviesAsync( IDiscoverMovieParameterBuilder builder, int pageNumber = 1, string language = "en" ) - { - Dictionary param = builder.Build(); - param.Add( "language", language ); +internal class ApiDiscoverRequest : ApiRequestBase, IApiDiscoverRequest +{ + private readonly IApiGenreRequest _genreApi; - const string command = "discover/movie"; + [ImportingConstructor] + public ApiDiscoverRequest( IApiSettings settings, IApiGenreRequest genreApi ) + : base( settings ) + { + _genreApi = genreApi; + } - ApiSearchResponse response = await base.SearchAsync( command, pageNumber, param ); + public async Task> DiscoverMoviesAsync( DiscoverMovieParameterBuilder builder, int pageNumber = 1, string language = "en" ) + { + Dictionary param = builder.Build(); + param.Add( "language", language ); - if( response.Error != null ) - { - return response; - } + const string command = "discover/movie"; - response.Results.PopulateGenres( _genreApi ); + ApiSearchResponse response = await base.SearchAsync( command, pageNumber, param ); + if( response.Error != null ) + { return response; } + + response.Results.PopulateGenres( _genreApi ); + + return response; } } diff --git a/DM.MovieApi/MovieDb/Discover/DiscoverEnums.cs b/DM.MovieApi/MovieDb/Discover/DiscoverEnums.cs new file mode 100644 index 0000000..d2b166a --- /dev/null +++ b/DM.MovieApi/MovieDb/Discover/DiscoverEnums.cs @@ -0,0 +1,45 @@ +using System.ComponentModel; + +namespace DM.MovieApi.MovieDb.Discover; + +public enum DiscoverSortBy +{ + [Description( "popularity" )] + Popularity, + + [Description( "release_date" )] + ReleaseDate, + + [Description( "revenue" )] + Revenue, + + [Description( "primary_release_date" )] + PrimaryReleaseDate, + + [Description( "original_title" )] + OriginalTitle, + + [Description( "vote_average" )] + VoteAverage, + + [Description( "vote_count" )] + VoteCount +} + +public enum SortDirection +{ + [Description( "asc" )] + Asc, + + [Description( "desc" )] + Desc +} + +public enum FilterExp +{ + [Description( "gte" )] + GreaterThanOrEqual, + + [Description( "lte" )] + LessThanOrEqual +} diff --git a/DM.MovieApi/MovieDb/Discover/DiscoverMovieParameterBuilder.cs b/DM.MovieApi/MovieDb/Discover/DiscoverMovieParameterBuilder.cs index 4fc44b2..f3fec6d 100644 --- a/DM.MovieApi/MovieDb/Discover/DiscoverMovieParameterBuilder.cs +++ b/DM.MovieApi/MovieDb/Discover/DiscoverMovieParameterBuilder.cs @@ -1,74 +1,189 @@ -using System.Collections.Generic; +namespace DM.MovieApi.MovieDb.Discover; -namespace DM.MovieApi.MovieDb.Discover +public class DiscoverMovieParameterBuilder { - public class DiscoverMovieParameterBuilder : IDiscoverMovieParameterBuilder + private readonly Dictionary> _param; + + public DiscoverMovieParameterBuilder() + { + _param = new Dictionary>(); + } + + /// + /// Builds all parameters for use with an . + /// Typically called by the internal engine. + /// + public Dictionary Build() { - private readonly Dictionary> _param; + var param = new Dictionary(); - public DiscoverMovieParameterBuilder() + foreach( var kvp in _param ) { - _param = new Dictionary>(); + param.Add( kvp.Key, string.Join( ",", kvp.Value ) ); } - public Dictionary Build() - { - var param = new Dictionary(); + return param; + } - foreach( var kvp in _param ) - { - param.Add( kvp.Key, string.Join( ",", kvp.Value ) ); - } + /// + /// Only include movies that have one of the cast members. + /// May be invoked more than once to add additional values. + /// + public DiscoverMovieParameterBuilder WithCast( params int[] personId ) + { + AddMultiParamType( "with_cast", personId ); - return param; - } + return this; + } - public IDiscoverMovieParameterBuilder WithCast( int personId ) - { - AddParamType( "with_cast" ); + /// + /// Only include movies that have one of the crew members. + /// May be invoked more than once to add additional values. + /// + public DiscoverMovieParameterBuilder WithCrew( params int[] personId ) + { + AddMultiParamType( "with_crew", personId ); - _param["with_cast"].Add( personId.ToString() ); + return this; + } - return this; - } + /// + /// Only include movies that have either a cast or crew member with the + /// provided value. + /// May be invoked more than once to add additional values. + /// + public DiscoverMovieParameterBuilder WithPeople( params int[] personId ) + { + AddMultiParamType( "with_people", personId ); - public IDiscoverMovieParameterBuilder WithCrew( int personId ) - { - AddParamType( "with_crew" ); + return this; + } - _param["with_crew"].Add( personId.ToString() ); + /// + /// Add for each genre to be included in the query. + /// May be invoked more than once to add additional values. + /// + public DiscoverMovieParameterBuilder WithGenre( params int[] genreId ) + { + AddMultiParamType( "with_genres", genreId ); - return this; - } + return this; + } - public IDiscoverMovieParameterBuilder WithGenre( int genreId ) - { - AddParamType( "with_genres" ); + /// + /// Add for each genre to be included in the query. + /// May be invoked more than once to add additional values. + /// + public DiscoverMovieParameterBuilder WithGenre( params Genre[] genre ) + => WithGenre( genre.Select( x => x.Id ).ToArray() ); + + /// + /// Add for each genre to be excluded from the query. + /// May be invoked more than once to add additional values. + /// + public DiscoverMovieParameterBuilder ExcludeGenre( params int[] genreId ) + { + AddMultiParamType( "without_genres", genreId ); - _param["with_genres"].Add( genreId.ToString() ); + return this; + } - return this; - } + /// + /// Add for each genre to be excluded from the query. + /// May be invoked more than once to add additional values. + /// + public DiscoverMovieParameterBuilder ExcludeGenre( params Genre[] genre ) + => ExcludeGenre( genre.Select( x => x.Id ).ToArray() ); + + /// + /// Specify an ISO 639-1 string to filter results by their original language value. + /// Invoking more than once will overwrite the prior value. + /// + public DiscoverMovieParameterBuilder WithOriginalLanguage( string language ) + { + AddSingleParamType( "with_original_language", language ); - public IDiscoverMovieParameterBuilder WithOriginalLanguage( string language ) - { - AddParamType( "original_language" ); + return this; + } - _param["original_language"].Add( language ); + /// + /// Return the results sorted. Default value is popularity descending. + /// Invoking more than once will overwrite the prior value. + /// + public DiscoverMovieParameterBuilder SortBy( DiscoverSortBy sort, SortDirection dir ) + { + string value = $"{sort.GetDescription()}.{dir.GetDescription()}"; - return this; - } + AddSingleParamType( "sort_by", value ); - public IDiscoverMovieParameterBuilder ExcludeGenre( int genreId ) - { - AddParamType( "without_genres" ); + return this; + } - _param["without_genres"].Add( genreId.ToString() ); + /// + /// A filter to limit the results to a specific primary release year. + /// Invoking more than once will overwrite the prior value. + /// + public DiscoverMovieParameterBuilder PrimaryReleaseYear( int year ) + { + AddSingleParamType( "primary_release_year", year.ToString() ); - return this; - } + return this; + } + + /// + /// A filter to limit the results to a specific year (looking at all release dates). + /// Invoking more than once will overwrite the prior value. + /// + public DiscoverMovieParameterBuilder Year( int year ) + { + AddSingleParamType( "year", year.ToString() ); + + return this; + } + + /// + /// Filter and only include movies that have a primary release date constrained + /// by the FilterExp. + /// May be invoked once for each type of FilterExp; + /// subsequent invocations will overwrite the prior value. + /// + public DiscoverMovieParameterBuilder PrimaryReleaseDate( DateTime date, FilterExp exp ) + { + string key = $"primary_release_date.{exp.GetDescription()}"; + + AddSingleParamType( key, date.ToString( "yyyy-MM-dd" ) ); - private void AddParamType( string name ) - => _param.TryAdd( name, new List() ); + return this; } + + /// + /// Filter and only include movies that have a release date (looking at all release + /// dates) constrained by the FilterExp. + /// May be invoked once for each type of FilterExp; + /// subsequent invocations will overwrite the prior value. + /// + public DiscoverMovieParameterBuilder ReleaseDate( DateTime date, FilterExp exp ) + { + string key = $"release_date.{exp.GetDescription()}"; + + AddSingleParamType( key, date.ToString( "yyyy-MM-dd" ) ); + + return this; + } + + private void AddMultiParamType( string name, IEnumerable values ) + { + _param.TryAdd( name, new HashSet( StringComparer.OrdinalIgnoreCase ) ); + + foreach( string value in values ) + { + _param[name].Add( value ); + } + } + + private void AddMultiParamType( string name, IEnumerable values ) + => AddMultiParamType( name, values.Select( x => x.ToString() ) ); + + private void AddSingleParamType( string name, string value ) + => _param[name] = new HashSet( new[] { value } ); } diff --git a/DM.MovieApi/MovieDb/Discover/IApiDiscoverRequest.cs b/DM.MovieApi/MovieDb/Discover/IApiDiscoverRequest.cs index 29540b5..150c723 100644 --- a/DM.MovieApi/MovieDb/Discover/IApiDiscoverRequest.cs +++ b/DM.MovieApi/MovieDb/Discover/IApiDiscoverRequest.cs @@ -1,21 +1,15 @@ -using System.Threading.Tasks; -using DM.MovieApi.ApiRequest; -using DM.MovieApi.ApiResponse; -using DM.MovieApi.MovieDb.Movies; +namespace DM.MovieApi.MovieDb.Discover; -namespace DM.MovieApi.MovieDb.Discover +/// +/// Interface for discovering movies based on the filter provided by the parameter builder. +/// +public interface IApiDiscoverRequest : IApiRequest { /// - /// Interface for discovering movies based on the filter provided by the parameter builder. + /// Allows for the discovery of movies by various types of data provided to the parameter. /// - public interface IApiDiscoverRequest : IApiRequest - { - /// - /// Allows for the discovery of movies by various types of data provided to the parameter. - /// - /// Provides a method of adding several types of parameters to filter the query. - /// Default is page 1. The page number to retrieve; the will contain the current page returned and the total number of pages available. - /// Default is English. The ISO 639-1 language code to retrieve the result from. - Task> DiscoverMoviesAsync( IDiscoverMovieParameterBuilder builder, int pageNumber = 1, string language = "en" ); - } + /// Provides a method of adding several types of parameters to filter the query. + /// Default is page 1. The page number to retrieve; the will contain the current page returned and the total number of pages available. + /// Default is English. The ISO 639-1 language code to retrieve the result from. + Task> DiscoverMoviesAsync( DiscoverMovieParameterBuilder builder, int pageNumber = 1, string language = "en" ); } diff --git a/DM.MovieApi/MovieDb/Discover/IDiscoverMovieParameterBuilder.cs b/DM.MovieApi/MovieDb/Discover/IDiscoverMovieParameterBuilder.cs deleted file mode 100644 index c3efc9e..0000000 --- a/DM.MovieApi/MovieDb/Discover/IDiscoverMovieParameterBuilder.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Collections.Generic; - -namespace DM.MovieApi.MovieDb.Discover -{ - public interface IDiscoverMovieParameterBuilder - { - /// - /// Builds all parameters for use with an . - /// Typically called by the internal engine. - /// - Dictionary Build(); - - /// - /// Add for each language version to be returned in the query. May be invoked more than once. - /// - IDiscoverMovieParameterBuilder WithOriginalLanguage( string language ); - - /// - /// Add for each crew member to be returned in the query. May be invoked more than once. - /// - IDiscoverMovieParameterBuilder WithCrew( int personId ); - - /// - /// Add for each cast member to be returned in the query. May be invoked more than once. - /// - IDiscoverMovieParameterBuilder WithCast( int personId ); - - /// - /// Add for each genre to be included in the query. May be invoked more than once. - /// - IDiscoverMovieParameterBuilder WithGenre( int genre ); - - /// - /// Add for each genre to be excluded from the query. May be invoked more than once. - /// - IDiscoverMovieParameterBuilder ExcludeGenre( int genre ); - } -} diff --git a/DM.MovieApi/MovieDb/Genres/ApiGenreRequest.cs b/DM.MovieApi/MovieDb/Genres/ApiGenreRequest.cs index f2ccc03..956931d 100644 --- a/DM.MovieApi/MovieDb/Genres/ApiGenreRequest.cs +++ b/DM.MovieApi/MovieDb/Genres/ApiGenreRequest.cs @@ -1,155 +1,147 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using DM.MovieApi.ApiRequest; -using DM.MovieApi.ApiResponse; -using DM.MovieApi.MovieDb.Movies; -using DM.MovieApi.Shims; -using Newtonsoft.Json.Linq; - -namespace DM.MovieApi.MovieDb.Genres +using Newtonsoft.Json.Linq; + +namespace DM.MovieApi.MovieDb.Genres; + +internal class ApiGenreRequest : ApiRequestBase, IApiGenreRequest { - internal class ApiGenreRequest : ApiRequestBase, IApiGenreRequest - { - // ReSharper disable once InconsistentNaming - private static readonly List _allGenres = new(); + // ReSharper disable once InconsistentNaming + private static readonly List _allGenres = new(); - public IReadOnlyList AllGenres + public IReadOnlyList AllGenres + { + get { - get + if( _allGenres.Any() == false ) { - if( _allGenres.Any() == false ) - { - var genres = Task.Run( () => GetAllAsync() ).GetAwaiter().GetResult().Item; - _allGenres.AddRange( genres ); - } - - return _allGenres.AsReadOnly(); + var genres = Task.Run( () => GetAllAsync() ).GetAwaiter().GetResult().Item; + _allGenres.AddRange( genres ); } + + return _allGenres.AsReadOnly(); } + } - [ImportingConstructor] - public ApiGenreRequest( IApiSettings settings ) - : base( settings ) - { } + [ImportingConstructor] + public ApiGenreRequest( IApiSettings settings ) + : base( settings ) + { } - public async Task> FindByIdAsync( int genreId, string language = "en" ) + public async Task> FindByIdAsync( int genreId, string language = "en" ) + { + var param = new Dictionary { - var param = new Dictionary - { - {"language", language} - }; + {"language", language} + }; - string command = $"genre/{genreId}"; + string command = $"genre/{genreId}"; - ApiQueryResponse response = await base.QueryAsync( command, param ); + ApiQueryResponse response = await base.QueryAsync( command, param ); - EnsureAllGenres( response ); + EnsureAllGenres( response ); - return response; - } + return response; + } - public async Task>> GetAllAsync( string language = "en" ) + public async Task>> GetAllAsync( string language = "en" ) + { + ApiQueryResponse> tv = await GetTelevisionAsync( language ); + if( tv.Error != null ) { - ApiQueryResponse> tv = await GetTelevisionAsync( language ); - if( tv.Error != null ) - { - return tv; - } - - ApiQueryResponse> movies = await GetMoviesAsync( language ); - if( movies.Error != null ) - { - return movies; - } - - List merged = movies.Item - .Union( tv.Item ) - .OrderBy( x => x.Name ) - .ToList(); - - movies.Item = merged.AsReadOnly(); + return tv; + } + ApiQueryResponse> movies = await GetMoviesAsync( language ); + if( movies.Error != null ) + { return movies; } - public async Task>> GetMoviesAsync( string language = "en" ) - { - var param = new Dictionary - { - {"language", language}, - }; + List merged = movies.Item + .Union( tv.Item ) + .OrderBy( x => x.Name ) + .ToList(); - ApiQueryResponse> genres = await base.QueryAsync( "genre/movie/list", param, GenreDeserializer ); + movies.Item = merged.AsReadOnly(); - return genres; - } + return movies; + } - public async Task>> GetTelevisionAsync( string language = "en" ) + public async Task>> GetMoviesAsync( string language = "en" ) + { + var param = new Dictionary { - var param = new Dictionary - { - {"language", language}, - }; + {"language", language}, + }; - ApiQueryResponse> genres = await base.QueryAsync( "genre/tv/list", param, GenreDeserializer ); + ApiQueryResponse> genres = await base.QueryAsync( "genre/movie/list", param, GenreDeserializer ); - return genres; - } + return genres; + } - public async Task> FindMoviesByIdAsync( int genreId, int pageNumber = 1, string language = "en" ) + public async Task>> GetTelevisionAsync( string language = "en" ) + { + var param = new Dictionary { - var param = new Dictionary - { - {"language", language}, - {"include_adult", "false"}, - }; + {"language", language}, + }; - string command = $"genre/{genreId}/movies"; + ApiQueryResponse> genres = await base.QueryAsync( "genre/tv/list", param, GenreDeserializer ); - ApiSearchResponse response = await base.SearchAsync( command, pageNumber, param ); + return genres; + } - if( response.Error != null ) - { - return response; - } + public async Task> FindMoviesByIdAsync( int genreId, int pageNumber = 1, string language = "en" ) + { + var param = new Dictionary + { + {"language", language}, + {"include_adult", "false"}, + }; + + string command = $"genre/{genreId}/movies"; - response.Results.PopulateGenres( this ); + ApiSearchResponse response = await base.SearchAsync( command, pageNumber, param ); + if( response.Error != null ) + { return response; } - internal void ClearAllGenres() - => _allGenres.Clear(); + response.Results.PopulateGenres( this ); - private void EnsureAllGenres( ApiQueryResponse response ) - { - if( response.Error != null ) - { - return; - } + return response; + } - if( response.Item == null ) - { - return; - } + internal void ClearAllGenres() + => _allGenres.Clear(); - if( _allGenres.Contains( response.Item ) == false ) - { - _allGenres.Add( response.Item ); - } + private void EnsureAllGenres( ApiQueryResponse response ) + { + if( response.Error != null ) + { + return; + } + + if( response.Item == null ) + { + return; } - private IReadOnlyList GenreDeserializer( string json ) + if( _allGenres.Contains( response.Item ) == false ) { - var obj = JObject.Parse( json ); + _allGenres.Add( response.Item ); + } + } - var arr = ( JArray )obj["genres"]; + private IReadOnlyList GenreDeserializer( string json ) + { + var obj = JObject.Parse( json ); - // ReSharper disable once PossibleNullReferenceException - var genres = arr.ToObject>(); + var arr = ( JArray )obj["genres"]; - return genres; - } + // ReSharper disable once PossibleNullReferenceException + var genres = arr.ToObject>(); + + return genres; } } diff --git a/DM.MovieApi/MovieDb/Genres/Genre.cs b/DM.MovieApi/MovieDb/Genres/Genre.cs index 2ab6c10..083adc9 100644 --- a/DM.MovieApi/MovieDb/Genres/Genre.cs +++ b/DM.MovieApi/MovieDb/Genres/Genre.cs @@ -1,51 +1,47 @@ -using System.Collections.Generic; -using System.Runtime.Serialization; +namespace DM.MovieApi.MovieDb.Genres; -namespace DM.MovieApi.MovieDb.Genres +[DataContract] +public class Genre : IEqualityComparer { - [DataContract] - public class Genre : IEqualityComparer - { - [DataMember( Name = "id" )] - public int Id { get; private set; } + [DataMember( Name = "id" )] + public int Id { get; private set; } - [DataMember( Name = "name" )] - public string Name { get; private set; } + [DataMember( Name = "name" )] + public string Name { get; private set; } - public Genre( int id, string name ) - { - Id = id; - Name = name; - } + public Genre( int id, string name ) + { + Id = id; + Name = name; + } - public override bool Equals( object obj ) + public override bool Equals( object obj ) + { + if( obj is not Genre genre ) { - if( obj is not Genre genre ) - { - return false; - } - - return Equals( this, genre ); + return false; } - public bool Equals( Genre x, Genre y ) - => x != null && y != null && x.Id == y.Id && x.Name == y.Name; + return Equals( this, genre ); + } + + public bool Equals( Genre x, Genre y ) + => x != null && y != null && x.Id == y.Id && x.Name == y.Name; - public override int GetHashCode() - => GetHashCode( this ); + public override int GetHashCode() + => GetHashCode( this ); - public int GetHashCode( Genre obj ) + public int GetHashCode( Genre obj ) + { + unchecked // Overflow is fine, just wrap { - unchecked // Overflow is fine, just wrap - { - int hash = 17; - hash = hash * 23 + obj.Id.GetHashCode(); - hash = hash * 23 + obj.Name.GetHashCode(); - return hash; - } + int hash = 17; + hash = hash * 23 + obj.Id.GetHashCode(); + hash = hash * 23 + obj.Name.GetHashCode(); + return hash; } - - public override string ToString() - => $"{Name} ({Id})"; } + + public override string ToString() + => $"{Name} ({Id})"; } diff --git a/DM.MovieApi/MovieDb/Genres/GenreFactory.cs b/DM.MovieApi/MovieDb/Genres/GenreFactory.cs index a8a1077..76ef14a 100644 --- a/DM.MovieApi/MovieDb/Genres/GenreFactory.cs +++ b/DM.MovieApi/MovieDb/Genres/GenreFactory.cs @@ -1,110 +1,106 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; +using System.Reflection; // ReSharper disable UnusedMember.Global -namespace DM.MovieApi.MovieDb.Genres -{ - public static class GenreFactory - { - public static Genre Action() - => new( 28, "Action" ); +namespace DM.MovieApi.MovieDb.Genres; - public static Genre Adventure() - => new( 12, "Adventure" ); +public static class GenreFactory +{ + public static Genre Action() + => new( 28, "Action" ); - public static Genre ActionAndAdventure() - => new( 10759, "Action & Adventure" ); + public static Genre Adventure() + => new( 12, "Adventure" ); - public static Genre Animation() - => new( 16, "Animation" ); + public static Genre ActionAndAdventure() + => new( 10759, "Action & Adventure" ); - public static Genre Comedy() - => new( 35, "Comedy" ); + public static Genre Animation() + => new( 16, "Animation" ); - public static Genre Crime() - => new( 80, "Crime" ); + public static Genre Comedy() + => new( 35, "Comedy" ); - public static Genre Drama() - => new( 18, "Drama" ); + public static Genre Crime() + => new( 80, "Crime" ); - public static Genre Documentary() - => new( 99, "Documentary" ); + public static Genre Drama() + => new( 18, "Drama" ); - public static Genre Family() - => new( 10751, "Family" ); + public static Genre Documentary() + => new( 99, "Documentary" ); - public static Genre Fantasy() - => new( 14, "Fantasy" ); + public static Genre Family() + => new( 10751, "Family" ); - public static Genre History() - => new( 36, "History" ); + public static Genre Fantasy() + => new( 14, "Fantasy" ); - public static Genre Horror() - => new( 27, "Horror" ); + public static Genre History() + => new( 36, "History" ); - public static Genre Kids() - => new( 10762, "Kids" ); + public static Genre Horror() + => new( 27, "Horror" ); - public static Genre Music() - => new( 10402, "Music" ); + public static Genre Kids() + => new( 10762, "Kids" ); - public static Genre Mystery() - => new( 9648, "Mystery" ); + public static Genre Music() + => new( 10402, "Music" ); - public static Genre News() - => new( 10763, "News" ); + public static Genre Mystery() + => new( 9648, "Mystery" ); - public static Genre Reality() - => new( 10764, "Reality" ); + public static Genre News() + => new( 10763, "News" ); - public static Genre Romance() - => new( 10749, "Romance" ); + public static Genre Reality() + => new( 10764, "Reality" ); - public static Genre ScienceFiction() - => new( 878, "Science Fiction" ); + public static Genre Romance() + => new( 10749, "Romance" ); - public static Genre SciFiAndFantasy() - => new( 10765, "Sci-Fi & Fantasy" ); + public static Genre ScienceFiction() + => new( 878, "Science Fiction" ); - public static Genre Soap() - => new( 10766, "Soap" ); + public static Genre SciFiAndFantasy() + => new( 10765, "Sci-Fi & Fantasy" ); - public static Genre Talk() - => new( 10767, "Talk" ); + public static Genre Soap() + => new( 10766, "Soap" ); - public static Genre Thriller() - => new( 53, "Thriller" ); + public static Genre Talk() + => new( 10767, "Talk" ); - public static Genre TvMovie() - => new( 10770, "TV Movie" ); + public static Genre Thriller() + => new( 53, "Thriller" ); - public static Genre War() - => new( 10752, "War" ); + public static Genre TvMovie() + => new( 10770, "TV Movie" ); - public static Genre WarAndPolitics() - => new( 10768, "War & Politics" ); + public static Genre War() + => new( 10752, "War" ); - public static Genre Western() - => new( 37, "Western" ); + public static Genre WarAndPolitics() + => new( 10768, "War & Politics" ); - public static IReadOnlyList GetAll() - => LazyAll.Value; + public static Genre Western() + => new( 37, "Western" ); + public static IReadOnlyList GetAll() + => LazyAll.Value; - private static readonly Lazy> LazyAll = new( () => - { - var all = typeof( GenreFactory ) - .GetTypeInfo() - .DeclaredMethods - .Where( x => x.IsStatic ) - .Where( x => x.IsPublic ) - .Where( x => x.ReturnType == typeof( Genre ) ) - .Select( x => ( Genre )x.Invoke( null, null ) ) - .ToList(); - return all.AsReadOnly(); - } ); - } + private static readonly Lazy> LazyAll = new( () => + { + var all = typeof( GenreFactory ) + .GetTypeInfo() + .DeclaredMethods + .Where( x => x.IsStatic ) + .Where( x => x.IsPublic ) + .Where( x => x.ReturnType == typeof( Genre ) ) + .Select( x => ( Genre )x.Invoke( null, null ) ) + .ToList(); + + return all.AsReadOnly(); + } ); } diff --git a/DM.MovieApi/MovieDb/Genres/GenreIdCollectionMappingExtensions.cs b/DM.MovieApi/MovieDb/Genres/GenreIdCollectionMappingExtensions.cs index 71027dc..53c3bc5 100644 --- a/DM.MovieApi/MovieDb/Genres/GenreIdCollectionMappingExtensions.cs +++ b/DM.MovieApi/MovieDb/Genres/GenreIdCollectionMappingExtensions.cs @@ -1,61 +1,56 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using DM.MovieApi.MovieDb.Movies; -using DM.MovieApi.MovieDb.People; +using DM.MovieApi.MovieDb.People; using DM.MovieApi.MovieDb.TV; -namespace DM.MovieApi.MovieDb.Genres +namespace DM.MovieApi.MovieDb.Genres; + +internal static class GenreIdCollectionMappingExtensions { - internal static class GenreIdCollectionMappingExtensions + public static void PopulateGenres( this IEnumerable movies, IApiGenreRequest api ) { - public static void PopulateGenres( this IEnumerable movies, IApiGenreRequest api ) + foreach( MovieInfo movie in movies ) { - foreach( MovieInfo movie in movies ) - { - movie.Genres = MapGenreIdsToGenres( movie.GenreIds, api ); - } + movie.Genres = MapGenreIdsToGenres( movie.GenreIds, api ); } + } - public static void PopulateGenres( this IEnumerable tvShows, IApiGenreRequest api ) + public static void PopulateGenres( this IEnumerable tvShows, IApiGenreRequest api ) + { + foreach( TVShowInfo tvShow in tvShows ) { - foreach( TVShowInfo tvShow in tvShows ) - { - tvShow.Genres = MapGenreIdsToGenres( tvShow.GenreIds, api ); - } + tvShow.Genres = MapGenreIdsToGenres( tvShow.GenreIds, api ); } + } - public static void PopulateGenres( this IEnumerable people, IApiGenreRequest api ) + public static void PopulateGenres( this IEnumerable people, IApiGenreRequest api ) + { + foreach( PersonInfo person in people ) { - foreach( PersonInfo person in people ) + foreach( PersonInfoRole role in person.KnownFor ) { - foreach( PersonInfoRole role in person.KnownFor ) - { - role.Genres = MapGenreIdsToGenres( role.GenreIds, api ); - } + role.Genres = MapGenreIdsToGenres( role.GenreIds, api ); } } + } - private static IReadOnlyList MapGenreIdsToGenres( IEnumerable genreIds, IApiGenreRequest api ) - { - IReadOnlyList genres = genreIds - .Select( x => MapGenre( x, api ) ) - .ToList() - .AsReadOnly(); - - return genres; - } + private static IReadOnlyList MapGenreIdsToGenres( IEnumerable genreIds, IApiGenreRequest api ) + { + IReadOnlyList genres = genreIds + .Select( x => MapGenre( x, api ) ) + .ToList() + .AsReadOnly(); - private static Genre MapGenre( int genreId, IApiGenreRequest api ) - { - Genre genre = api.AllGenres.FirstOrDefault( x => x.Id == genreId ); + return genres; + } - if( genre == null ) - { - genre = Task.Run( () => api.FindByIdAsync( genreId ) ).GetAwaiter().GetResult().Item; - } + private static Genre MapGenre( int genreId, IApiGenreRequest api ) + { + Genre genre = api.AllGenres.FirstOrDefault( x => x.Id == genreId ); - return genre; + if( genre == null ) + { + genre = Task.Run( () => api.FindByIdAsync( genreId ) ).GetAwaiter().GetResult().Item; } + + return genre; } } diff --git a/DM.MovieApi/MovieDb/Genres/IApiGenreRequest.cs b/DM.MovieApi/MovieDb/Genres/IApiGenreRequest.cs index b756aae..c83bbf3 100644 --- a/DM.MovieApi/MovieDb/Genres/IApiGenreRequest.cs +++ b/DM.MovieApi/MovieDb/Genres/IApiGenreRequest.cs @@ -1,64 +1,57 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using DM.MovieApi.ApiRequest; -using DM.MovieApi.ApiResponse; -using DM.MovieApi.MovieDb.Movies; +namespace DM.MovieApi.MovieDb.Genres; -namespace DM.MovieApi.MovieDb.Genres +/// +/// Interface representing Movie and TV genres. +/// +public interface IApiGenreRequest : IApiRequest { /// - /// Interface representing Movie and TV genres. + /// Provides a cache of all the genres (language='en') returned from . + /// As the Genres do not change much, if any, the cache is never evicted. /// - public interface IApiGenreRequest : IApiRequest - { - /// - /// Provides a cache of all the genres (language='en') returned from . - /// As the Genres do not change much, if any, the cache is never evicted. - /// - IReadOnlyList AllGenres { get; } + IReadOnlyList AllGenres { get; } - /// - /// Gets all the information about a specific Genre. - /// - /// The genre Id is typically found from a more generic Movie or TV query. - /// Default is English. The ISO 639-1 language code to retrieve the result from. - Task> FindByIdAsync( int genreId, string language = "en" ); + /// + /// Gets all the information about a specific Genre. + /// + /// The genre Id is typically found from a more generic Movie or TV query. + /// Default is English. The ISO 639-1 language code to retrieve the result from. + Task> FindByIdAsync( int genreId, string language = "en" ); - /// - /// It is recommended to use the property, unless a - /// language specific parameter other than 'en' is provided. - /// - /// themoviedb.org api mixes tv and movie genres into their movies and tv titles. - /// Use this method to ensure all genres are accounted for when attempting to join - /// on Genre.Id from a search result; by default, search results only contain genre - /// id and excludes the name. - /// - /// - /// In some rare cases, a genre is not included in the movie or tv genres list; when this - /// occurs, use the method to find a matching genre. - /// - /// - /// The merged set of Movie and TV Genres. - Task>> GetAllAsync( string language = "en" ); + /// + /// It is recommended to use the property, unless a + /// language specific parameter other than 'en' is provided. + /// + /// themoviedb.org api mixes tv and movie genres into their movies and tv titles. + /// Use this method to ensure all genres are accounted for when attempting to join + /// on Genre.Id from a search result; by default, search results only contain genre + /// id and excludes the name. + /// + /// + /// In some rare cases, a genre is not included in the movie or tv genres list; when this + /// occurs, use the method to find a matching genre. + /// + /// + /// The merged set of Movie and TV Genres. + Task>> GetAllAsync( string language = "en" ); - /// - /// Gets all movie related Genres. - /// - /// Default is English. The ISO 639-1 language code to retrieve the result from. - Task>> GetMoviesAsync( string language = "en" ); + /// + /// Gets all movie related Genres. + /// + /// Default is English. The ISO 639-1 language code to retrieve the result from. + Task>> GetMoviesAsync( string language = "en" ); - /// - /// Gets all tv related Genres. - /// - /// Default is English. The ISO 639-1 language code to retrieve the result from. - Task>> GetTelevisionAsync( string language = "en" ); + /// + /// Gets all tv related Genres. + /// + /// Default is English. The ISO 639-1 language code to retrieve the result from. + Task>> GetTelevisionAsync( string language = "en" ); - /// - /// Finds all movies related to a genre, where the Id passed to this method is a genre Id, not a movie Id. - /// - /// The genre Id is typically found through from a related Movie request or from any of the Genre API methods. - /// Default is page 1. The page number to retrieve; the will contain the current page returned and the total number of pages available. - /// Default is English. The ISO 639-1 language code to retrieve the result from. - Task> FindMoviesByIdAsync( int genreId, int pageNumber = 1, string language = "en" ); - } + /// + /// Finds all movies related to a genre, where the Id passed to this method is a genre Id, not a movie Id. + /// + /// The genre Id is typically found through from a related Movie request or from any of the Genre API methods. + /// Default is page 1. The page number to retrieve; the will contain the current page returned and the total number of pages available. + /// Default is English. The ISO 639-1 language code to retrieve the result from. + Task> FindMoviesByIdAsync( int genreId, int pageNumber = 1, string language = "en" ); } diff --git a/DM.MovieApi/MovieDb/IndustryProfessions/ApiProfessionRequest.cs b/DM.MovieApi/MovieDb/IndustryProfessions/ApiProfessionRequest.cs index a010c24..72af4b1 100644 --- a/DM.MovieApi/MovieDb/IndustryProfessions/ApiProfessionRequest.cs +++ b/DM.MovieApi/MovieDb/IndustryProfessions/ApiProfessionRequest.cs @@ -1,38 +1,32 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using DM.MovieApi.ApiRequest; -using DM.MovieApi.ApiResponse; -using DM.MovieApi.Shims; -using Newtonsoft.Json.Linq; - -namespace DM.MovieApi.MovieDb.IndustryProfessions +using Newtonsoft.Json.Linq; + +namespace DM.MovieApi.MovieDb.IndustryProfessions; + +internal class ApiProfessionRequest : ApiRequestBase, IApiProfessionRequest { - internal class ApiProfessionRequest : ApiRequestBase, IApiProfessionRequest - { - [ImportingConstructor] - public ApiProfessionRequest( IApiSettings settings ) - : base( settings ) - { } + [ImportingConstructor] + public ApiProfessionRequest( IApiSettings settings ) + : base( settings ) + { } - public Task>> GetAllAsync() - { - const string command = "job/list"; + public Task>> GetAllAsync() + { + const string command = "job/list"; - Task>> response = base.QueryAsync( command, ProfessionDeserializer ); + Task>> response = base.QueryAsync( command, ProfessionDeserializer ); - return response; - } + return response; + } - private IReadOnlyList ProfessionDeserializer( string json ) - { - var obj = JObject.Parse( json ); + private IReadOnlyList ProfessionDeserializer( string json ) + { + var obj = JObject.Parse( json ); - var arr = ( JArray )obj["jobs"]; + var arr = ( JArray )obj["jobs"]; - // ReSharper disable once PossibleNullReferenceException - var professions = arr.ToObject>(); + // ReSharper disable once PossibleNullReferenceException + var professions = arr.ToObject>(); - return professions; - } + return professions; } } diff --git a/DM.MovieApi/MovieDb/IndustryProfessions/IApiProfessionRequest.cs b/DM.MovieApi/MovieDb/IndustryProfessions/IApiProfessionRequest.cs index e83f1e6..a016c67 100644 --- a/DM.MovieApi/MovieDb/IndustryProfessions/IApiProfessionRequest.cs +++ b/DM.MovieApi/MovieDb/IndustryProfessions/IApiProfessionRequest.cs @@ -1,18 +1,12 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using DM.MovieApi.ApiRequest; -using DM.MovieApi.ApiResponse; +namespace DM.MovieApi.MovieDb.IndustryProfessions; -namespace DM.MovieApi.MovieDb.IndustryProfessions +/// +/// Interface for retrieving information about Movie/TV industry specific professions. +/// +public interface IApiProfessionRequest : IApiRequest { /// - /// Interface for retrieving information about Movie/TV industry specific professions. + /// Gets all the Movie/TV industry specific professions. /// - public interface IApiProfessionRequest : IApiRequest - { - /// - /// Gets all the Movie/TV industry specific professions. - /// - Task>> GetAllAsync(); - } + Task>> GetAllAsync(); } diff --git a/DM.MovieApi/MovieDb/IndustryProfessions/Profession.cs b/DM.MovieApi/MovieDb/IndustryProfessions/Profession.cs index 813bb5c..c096c35 100644 --- a/DM.MovieApi/MovieDb/IndustryProfessions/Profession.cs +++ b/DM.MovieApi/MovieDb/IndustryProfessions/Profession.cs @@ -1,18 +1,14 @@ -using System.Collections.Generic; -using System.Runtime.Serialization; +namespace DM.MovieApi.MovieDb.IndustryProfessions; -namespace DM.MovieApi.MovieDb.IndustryProfessions +[DataContract] +public class Profession { - [DataContract] - public class Profession - { - [DataMember( Name = "department" )] - public string Department { get; set; } + [DataMember( Name = "department" )] + public string Department { get; set; } - [DataMember( Name = "jobs" )] - public IReadOnlyList Jobs { get; set; } + [DataMember( Name = "jobs" )] + public IReadOnlyList Jobs { get; set; } - public override string ToString() - => $"{Department} {Jobs.Count} jobs"; - } + public override string ToString() + => $"{Department} {Jobs.Count} jobs"; } diff --git a/DM.MovieApi/MovieDb/Keywords/Keyword.cs b/DM.MovieApi/MovieDb/Keywords/Keyword.cs index 73cd10d..b4d3a4a 100644 --- a/DM.MovieApi/MovieDb/Keywords/Keyword.cs +++ b/DM.MovieApi/MovieDb/Keywords/Keyword.cs @@ -1,57 +1,53 @@ -using System.Collections.Generic; -using System.Runtime.Serialization; +namespace DM.MovieApi.MovieDb.Keywords; -namespace DM.MovieApi.MovieDb.Keywords +[DataContract] +public class Keyword : IEqualityComparer { - [DataContract] - public class Keyword : IEqualityComparer + /// + /// The keyword Id as identified by theMovieDB.org. + /// + [DataMember( Name = "id" )] + public int Id { get; set; } + + /// + /// The keyword. + /// + [DataMember( Name = "name" )] + public string Name { get; set; } + + public Keyword( int id, string name ) { - /// - /// The keyword Id as identified by theMovieDB.org. - /// - [DataMember( Name = "id" )] - public int Id { get; set; } - - /// - /// The keyword. - /// - [DataMember( Name = "name" )] - public string Name { get; set; } - - public Keyword( int id, string name ) - { - Id = id; - Name = name; - } + Id = id; + Name = name; + } - public override bool Equals( object obj ) + public override bool Equals( object obj ) + { + if( obj is not Keyword genre ) { - if( obj is not Keyword genre ) - { - return false; - } - - return Equals( this, genre ); + return false; } - public bool Equals( Keyword x, Keyword y ) - => x != null && y != null && x.Id == y.Id && x.Name == y.Name; + return Equals( this, genre ); + } + + public bool Equals( Keyword x, Keyword y ) + => x != null && y != null && x.Id == y.Id && x.Name == y.Name; - public override int GetHashCode() - => GetHashCode( this ); + public override int GetHashCode() + => GetHashCode( this ); - public int GetHashCode( Keyword obj ) + public int GetHashCode( Keyword obj ) + { + unchecked // Overflow is fine, just wrap { - unchecked // Overflow is fine, just wrap - { - int hash = 17; - hash = hash * 23 + obj.Id.GetHashCode(); - hash = hash * 23 + obj.Name.GetHashCode(); - return hash; - } + int hash = 17; + hash = hash * 23 + obj.Id.GetHashCode(); + hash = hash * 23 + obj.Name.GetHashCode(); + return hash; } - - public override string ToString() - => $"{Name} ({Id})"; } + + public override string ToString() + => $"{Name} ({Id})"; } diff --git a/DM.MovieApi/MovieDb/Keywords/KeywordConverter.cs b/DM.MovieApi/MovieDb/Keywords/KeywordConverter.cs index a30773e..cc5b805 100644 --- a/DM.MovieApi/MovieDb/Keywords/KeywordConverter.cs +++ b/DM.MovieApi/MovieDb/Keywords/KeywordConverter.cs @@ -1,43 +1,40 @@ -using System; -using System.Collections.Generic; -using Newtonsoft.Json; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; -namespace DM.MovieApi.MovieDb.Keywords -{ - /// - /// Expected parent json node is "keywords". The child node is variable - /// and should be set as a parameter to the JsonConverter attribute which - /// will use the KeywordConverter .ctor to create the converter with the - /// provided parameter. - /// - internal class KeywordConverter : JsonConverter - { - private readonly string _key; +namespace DM.MovieApi.MovieDb.Keywords; - public KeywordConverter( string key ) - { - _key = key; - } +/// +/// Expected parent json node is "keywords". The child node is variable +/// and should be set as a parameter to the JsonConverter attribute which +/// will use the KeywordConverter .ctor to create the converter with the +/// provided parameter. +/// +internal class KeywordConverter : JsonConverter +{ + private readonly string _key; - public override void WriteJson( JsonWriter writer, object value, JsonSerializer serializer ) - { - throw new NotImplementedException(); - } + public KeywordConverter( string key ) + { + _key = key; + } - public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer ) - { - JToken obj = JToken.Load( reader ); + public override void WriteJson( JsonWriter writer, object value, JsonSerializer serializer ) + { + throw new NotImplementedException(); + } - var arr = ( JArray )obj[_key]; + public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer ) + { + JToken obj = JToken.Load( reader ); - // ReSharper disable once PossibleNullReferenceException - var keywords = arr.ToObject>(); + var arr = ( JArray )obj[_key]; - return keywords; - } + // ReSharper disable once PossibleNullReferenceException + var keywords = arr.ToObject>(); - public override bool CanConvert( Type objectType ) - => false; + return keywords; } + + public override bool CanConvert( Type objectType ) + => false; } diff --git a/DM.MovieApi/MovieDb/Language.cs b/DM.MovieApi/MovieDb/Language.cs index a3fe94e..e397152 100644 --- a/DM.MovieApi/MovieDb/Language.cs +++ b/DM.MovieApi/MovieDb/Language.cs @@ -1,58 +1,54 @@ -using System.Collections.Generic; -using System.Runtime.Serialization; +namespace DM.MovieApi.MovieDb; -namespace DM.MovieApi.MovieDb +[DataContract] +public class Language : IEqualityComparer { - [DataContract] - public class Language : IEqualityComparer - { - [DataMember( Name = "iso_639_1" )] - public string Iso639Code { get; set; } + [DataMember( Name = "iso_639_1" )] + public string Iso639Code { get; set; } - [DataMember( Name = "name" )] - public string Name { get; set; } + [DataMember( Name = "name" )] + public string Name { get; set; } - public Language( string iso639Code, string name ) - { - Iso639Code = iso639Code; - Name = name; - } + public Language( string iso639Code, string name ) + { + Iso639Code = iso639Code; + Name = name; + } - public override bool Equals( object obj ) + public override bool Equals( object obj ) + { + if( obj is not Language language ) { - if( obj is not Language language ) - { - return false; - } - - return Equals( this, language ); + return false; } - public bool Equals( Language x, Language y ) - => x != null && y != null && x.Iso639Code == y.Iso639Code && x.Name == y.Name; + return Equals( this, language ); + } + + public bool Equals( Language x, Language y ) + => x != null && y != null && x.Iso639Code == y.Iso639Code && x.Name == y.Name; - public override int GetHashCode() - => GetHashCode( this ); + public override int GetHashCode() + => GetHashCode( this ); - public int GetHashCode( Language obj ) + public int GetHashCode( Language obj ) + { + unchecked // Overflow is fine, just wrap { - unchecked // Overflow is fine, just wrap - { - int hash = 17; - hash = hash * 23 + obj.Iso639Code.GetHashCode(); - hash = hash * 23 + obj.Name.GetHashCode(); - return hash; - } + int hash = 17; + hash = hash * 23 + obj.Iso639Code.GetHashCode(); + hash = hash * 23 + obj.Name.GetHashCode(); + return hash; } + } - public override string ToString() + public override string ToString() + { + if( string.IsNullOrWhiteSpace( Name ) ) { - if( string.IsNullOrWhiteSpace( Name ) ) - { - return "n/a"; - } - - return $"{Name} ({Iso639Code})"; + return "n/a"; } + + return $"{Name} ({Iso639Code})"; } } diff --git a/DM.MovieApi/MovieDb/Movies/ApiMovieRequest.cs b/DM.MovieApi/MovieDb/Movies/ApiMovieRequest.cs index ce758a5..140c51d 100644 --- a/DM.MovieApi/MovieDb/Movies/ApiMovieRequest.cs +++ b/DM.MovieApi/MovieDb/Movies/ApiMovieRequest.cs @@ -1,200 +1,194 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using DM.MovieApi.ApiRequest; -using DM.MovieApi.ApiResponse; -using DM.MovieApi.MovieDb.Genres; -using DM.MovieApi.Shims; - -namespace DM.MovieApi.MovieDb.Movies +namespace DM.MovieApi.MovieDb.Movies; + +internal class ApiMovieRequest : ApiRequestBase, IApiMovieRequest { - internal class ApiMovieRequest : ApiRequestBase, IApiMovieRequest + private readonly IApiGenreRequest _genreApi; + + [ImportingConstructor] + public ApiMovieRequest( IApiSettings settings, IApiGenreRequest genreApi ) + : base( settings ) { - private readonly IApiGenreRequest _genreApi; + _genreApi = genreApi; + } - [ImportingConstructor] - public ApiMovieRequest( IApiSettings settings, IApiGenreRequest genreApi ) - : base( settings ) + public async Task> FindByIdAsync( int movieId, string language = "en" ) + { + var param = new Dictionary { - _genreApi = genreApi; - } + {"language", language}, + {"append_to_response", "keywords"}, + }; + + string command = $"movie/{movieId}"; + + ApiQueryResponse response = await base.QueryAsync( command, param ); - public async Task> FindByIdAsync( int movieId, string language = "en" ) + return response; + } + + public async Task> SearchByTitleAsync( string query, int pageNumber = 1, string language = "en" ) + { + var param = new Dictionary { - var param = new Dictionary - { - {"language", language}, - {"append_to_response", "keywords"}, - }; + {"query", query}, + {"include_adult", "false"}, + {"language", language}, + }; - string command = $"movie/{movieId}"; + const string command = "search/movie"; - ApiQueryResponse response = await base.QueryAsync( command, param ); + ApiSearchResponse response = await base.SearchAsync( command, pageNumber, param ); + if( response.Error != null ) + { return response; } - public async Task> SearchByTitleAsync( string query, int pageNumber = 1, string language = "en" ) - { - var param = new Dictionary - { - {"query", query}, - {"include_adult", "false"}, - {"language", language}, - }; + response.Results.PopulateGenres( _genreApi ); - const string command = "search/movie"; + return response; + } - ApiSearchResponse response = await base.SearchAsync( command, pageNumber, param ); + public async Task> GetLatestAsync( string language = "en" ) + { + var param = new Dictionary + { + {"language", language}, + {"append_to_response", "keywords"}, + }; - if( response.Error != null ) - { - return response; - } + const string command = "movie/latest"; - response.Results.PopulateGenres( _genreApi ); + ApiQueryResponse response = await base.QueryAsync( command, param ); - return response; - } + return response; + } - public async Task> GetLatestAsync( string language = "en" ) + public async Task> GetNowPlayingAsync( int pageNumber = 1, string language = "en" ) + { + var param = new Dictionary { - var param = new Dictionary - { - {"language", language}, - {"append_to_response", "keywords"}, - }; + {"language", language}, + {"append_to_response", "keywords"}, + }; - const string command = "movie/latest"; + const string command = "movie/now_playing"; - ApiQueryResponse response = await base.QueryAsync( command, param ); + ApiSearchResponse response = await base.SearchAsync( command, pageNumber, param ); - return response; - } + return response; + } - public async Task> GetNowPlayingAsync( int pageNumber = 1, string language = "en" ) + public async Task> GetUpcomingAsync( int pageNumber = 1, string language = "en" ) + { + var param = new Dictionary { - var param = new Dictionary - { - {"language", language}, - {"append_to_response", "keywords"}, - }; + {"language", language}, + {"append_to_response", "keywords"}, + }; - const string command = "movie/now_playing"; + const string command = "movie/upcoming"; - ApiSearchResponse response = await base.SearchAsync( command, pageNumber, param ); + ApiSearchResponse response = await base.SearchAsync( command, pageNumber, param ); - return response; - } + return response; + } - public async Task> GetUpcomingAsync( int pageNumber = 1, string language = "en" ) + public async Task> GetTopRatedAsync( int pageNumber = 1, string language = "en" ) + { + var param = new Dictionary { - var param = new Dictionary - { - {"language", language}, - {"append_to_response", "keywords"}, - }; + {"language", language}, + }; - const string command = "movie/upcoming"; + const string command = "movie/top_rated"; - ApiSearchResponse response = await base.SearchAsync( command, pageNumber, param ); + ApiSearchResponse response = await base.SearchAsync( command, pageNumber, param ); + if( response.Error != null ) + { return response; } - public async Task> GetTopRatedAsync( int pageNumber = 1, string language = "en" ) - { - var param = new Dictionary - { - {"language", language}, - }; + response.Results.PopulateGenres( _genreApi ); - const string command = "movie/top_rated"; + return response; + } - ApiSearchResponse response = await base.SearchAsync( command, pageNumber, param ); + public async Task> GetPopularAsync( int pageNumber = 1, string language = "en" ) + { + var param = new Dictionary + { + {"language", language}, + }; - if( response.Error != null ) - { - return response; - } + const string command = "movie/popular"; - response.Results.PopulateGenres( _genreApi ); + ApiSearchResponse response = await base.SearchAsync( command, pageNumber, param ); + if( response.Error != null ) + { return response; } - public async Task> GetPopularAsync( int pageNumber = 1, string language = "en" ) - { - var param = new Dictionary - { - {"language", language}, - }; + response.Results.PopulateGenres( _genreApi ); - const string command = "movie/popular"; + return response; + } - ApiSearchResponse response = await base.SearchAsync( command, pageNumber, param ); + public async Task> GetCreditsAsync( int movieId, string language = "en" ) + { + var param = new Dictionary + { + {"language", language}, + }; - if( response.Error != null ) - { - return response; - } + string command = $"movie/{movieId}/credits"; - response.Results.PopulateGenres( _genreApi ); + ApiQueryResponse response = await base.QueryAsync( command, param ); - return response; - } + return response; + } - public async Task> GetCreditsAsync( int movieId, string language = "en" ) + public async Task> GetRecommendationsAsync( int movieId, int pageNumber = 1, string language = "en" ) + { + var param = new Dictionary { - var param = new Dictionary - { - {"language", language}, - }; + {"language", language}, + }; - string command = $"movie/{movieId}/credits"; + string command = $"movie/{movieId}/recommendations"; - ApiQueryResponse response = await base.QueryAsync( command, param ); + ApiSearchResponse response = await base.SearchAsync( command, pageNumber, param ); + if( response.Error != null ) + { return response; } - public async Task> GetRecommendationsAsync( int movieId, int pageNumber = 1, string language = "en" ) - { - var param = new Dictionary - { - {"language", language}, - }; - - string command = $"movie/{movieId}/recommendations"; - ApiSearchResponse response = await base.SearchAsync( command, pageNumber, param ); - - if( response.Error != null ) - { - return response; - } + response.Results.PopulateGenres( _genreApi ); - response.Results.PopulateGenres( _genreApi ); - - return response; - } + return response; + } - public async Task> GetSimilarAsync( int movieId, int pageNumber = 1, string language = "en" ) + public async Task> GetSimilarAsync( int movieId, int pageNumber = 1, string language = "en" ) + { + var param = new Dictionary { - var param = new Dictionary - { - {"language", language}, - }; - - string command = $"movie/{movieId}/similar"; - ApiSearchResponse response = await base.SearchAsync( command, pageNumber, param ); + {"language", language}, + }; - if( response.Error != null ) - { - return response; - } + string command = $"movie/{movieId}/similar"; - response.Results.PopulateGenres( _genreApi ); + ApiSearchResponse response = await base.SearchAsync( command, pageNumber, param ); + if( response.Error != null ) + { return response; } + + response.Results.PopulateGenres( _genreApi ); + + return response; } } diff --git a/DM.MovieApi/MovieDb/Movies/IApiMovieRequest.cs b/DM.MovieApi/MovieDb/Movies/IApiMovieRequest.cs index ea19faf..1d9ca7f 100644 --- a/DM.MovieApi/MovieDb/Movies/IApiMovieRequest.cs +++ b/DM.MovieApi/MovieDb/Movies/IApiMovieRequest.cs @@ -1,85 +1,80 @@ -using System.Threading.Tasks; -using DM.MovieApi.ApiRequest; -using DM.MovieApi.ApiResponse; +namespace DM.MovieApi.MovieDb.Movies; -namespace DM.MovieApi.MovieDb.Movies +/// +/// Interface for retrieving information about Movies. +/// +public interface IApiMovieRequest : IApiRequest { /// - /// Interface for retrieving information about Movies. + /// Gets all the information about a specific Movie. /// - public interface IApiMovieRequest : IApiRequest - { - /// - /// Gets all the information about a specific Movie. - /// - /// The movie Id is typically found from a more generic Movie query. - /// Default is English. The ISO 639-1 language code to retrieve the result from. - Task> FindByIdAsync( int movieId, string language = "en" ); + /// The movie Id is typically found from a more generic Movie query. + /// Default is English. The ISO 639-1 language code to retrieve the result from. + Task> FindByIdAsync( int movieId, string language = "en" ); - /// - /// Searches for Movies by title. - /// - /// The query to search for Movies. - /// Default is page 1. The page number to retrieve; the will contain the current page returned and the total number of pages available. - /// Default is English. The ISO 639-1 language code to retrieve the result from. - Task> SearchByTitleAsync( string query, int pageNumber = 1, string language = "en" ); + /// + /// Searches for Movies by title. + /// + /// The query to search for Movies. + /// Default is page 1. The page number to retrieve; the will contain the current page returned and the total number of pages available. + /// Default is English. The ISO 639-1 language code to retrieve the result from. + Task> SearchByTitleAsync( string query, int pageNumber = 1, string language = "en" ); - /// - /// Gets the most recent movie that has been added to TheMovieDb.org. - /// - /// Default is English. The ISO 639-1 language code to retrieve the result from. - Task> GetLatestAsync( string language = "en" ); + /// + /// Gets the most recent movie that has been added to TheMovieDb.org. + /// + /// Default is English. The ISO 639-1 language code to retrieve the result from. + Task> GetLatestAsync( string language = "en" ); - /// - /// Gets the list of movies playing that have been, or are being released this week. - /// - /// Default is page 1. The page number to retrieve; the will contain the current page returned and the total number of pages available. - /// Default is English. The ISO 639-1 language code to retrieve the result from. - Task> GetNowPlayingAsync( int pageNumber = 1, string language = "en" ); + /// + /// Gets the list of movies playing that have been, or are being released this week. + /// + /// Default is page 1. The page number to retrieve; the will contain the current page returned and the total number of pages available. + /// Default is English. The ISO 639-1 language code to retrieve the result from. + Task> GetNowPlayingAsync( int pageNumber = 1, string language = "en" ); - /// - /// Gets the list of upcoming movies by release date. - /// - /// Default is page 1. The page number to retrieve; the will contain the current page returned and the total number of pages available. - /// Default is English. The ISO 639-1 language code to retrieve the result from. - Task> GetUpcomingAsync( int pageNumber = 1, string language = "en" ); + /// + /// Gets the list of upcoming movies by release date. + /// + /// Default is page 1. The page number to retrieve; the will contain the current page returned and the total number of pages available. + /// Default is English. The ISO 639-1 language code to retrieve the result from. + Task> GetUpcomingAsync( int pageNumber = 1, string language = "en" ); - /// - /// Gets the list of top rated movies which is refreshed daily. - /// - /// Default is page 1. The page number to retrieve; the will contain the current page returned and the total number of pages available. - /// Default is English. The ISO 639-1 language code to retrieve the result from. - Task> GetTopRatedAsync( int pageNumber = 1, string language = "en" ); + /// + /// Gets the list of top rated movies which is refreshed daily. + /// + /// Default is page 1. The page number to retrieve; the will contain the current page returned and the total number of pages available. + /// Default is English. The ISO 639-1 language code to retrieve the result from. + Task> GetTopRatedAsync( int pageNumber = 1, string language = "en" ); - /// - /// Gets the list of popular movies which is refreshed daily. - /// - /// Default is page 1. The page number to retrieve; the will contain the current page returned and the total number of pages available. - /// Default is English. The ISO 639-1 language code to retrieve the result from. - Task> GetPopularAsync( int pageNumber = 1, string language = "en" ); + /// + /// Gets the list of popular movies which is refreshed daily. + /// + /// Default is page 1. The page number to retrieve; the will contain the current page returned and the total number of pages available. + /// Default is English. The ISO 639-1 language code to retrieve the result from. + Task> GetPopularAsync( int pageNumber = 1, string language = "en" ); - /// - /// Get the cast and crew information for a specific movie id. - /// - /// The movie Id is typically found from a more generic Movie query. - /// Default is English. The ISO 639-1 language code to retrieve the result from. - Task> GetCreditsAsync( int movieId, string language = "en" ); + /// + /// Get the cast and crew information for a specific movie id. + /// + /// The movie Id is typically found from a more generic Movie query. + /// Default is English. The ISO 639-1 language code to retrieve the result from. + Task> GetCreditsAsync( int movieId, string language = "en" ); - /// - /// Get a list of recommended movies for a movie. - /// - /// The movie Id is typically found from a more generic Movie query. - /// Default is page 1. The page number to retrieve; the will contain the current page returned and the total number of pages available. - /// Default is English. The ISO 639-1 language code to retrieve the result from. - Task> GetRecommendationsAsync( int movieId, int pageNumber = 1, string language = "en" ); + /// + /// Get a list of recommended movies for a movie. + /// + /// The movie Id is typically found from a more generic Movie query. + /// Default is page 1. The page number to retrieve; the will contain the current page returned and the total number of pages available. + /// Default is English. The ISO 639-1 language code to retrieve the result from. + Task> GetRecommendationsAsync( int movieId, int pageNumber = 1, string language = "en" ); - /// - /// Get a list of similar movies. This is not the same as the "Recommendation" system you see on the website. - /// These items are assembled by looking at keywords and genres. - /// - /// The movie Id is typically found from a more generic Movie query. - /// Default is page 1. The page number to retrieve; the will contain the current page returned and the total number of pages available. - /// Default is English. The ISO 639-1 language code to retrieve the result from. - Task> GetSimilarAsync( int movieId, int pageNumber = 1, string language = "en" ); - } + /// + /// Get a list of similar movies. This is not the same as the "Recommendation" system you see on the website. + /// These items are assembled by looking at keywords and genres. + /// + /// The movie Id is typically found from a more generic Movie query. + /// Default is page 1. The page number to retrieve; the will contain the current page returned and the total number of pages available. + /// Default is English. The ISO 639-1 language code to retrieve the result from. + Task> GetSimilarAsync( int movieId, int pageNumber = 1, string language = "en" ); } diff --git a/DM.MovieApi/MovieDb/Movies/Movie.cs b/DM.MovieApi/MovieDb/Movies/Movie.cs index 4dc1f84..6c32c97 100644 --- a/DM.MovieApi/MovieDb/Movies/Movie.cs +++ b/DM.MovieApi/MovieDb/Movies/Movie.cs @@ -1,109 +1,104 @@ -using System; -using System.Collections.Generic; -using System.Runtime.Serialization; -using DM.MovieApi.MovieDb.Collections; +using DM.MovieApi.MovieDb.Collections; using DM.MovieApi.MovieDb.Companies; -using DM.MovieApi.MovieDb.Genres; using DM.MovieApi.MovieDb.Keywords; using Newtonsoft.Json; -namespace DM.MovieApi.MovieDb.Movies -{ - [DataContract] - public class Movie - { - [DataMember( Name = "id" )] - public int Id { get; set; } +namespace DM.MovieApi.MovieDb.Movies; - [DataMember( Name = "title" )] - public string Title { get; set; } +[DataContract] +public class Movie +{ + [DataMember( Name = "id" )] + public int Id { get; set; } - [DataMember( Name = "adult" )] - public bool IsAdultThemed { get; set; } + [DataMember( Name = "title" )] + public string Title { get; set; } - [DataMember( Name = "backdrop_path" )] - public string BackdropPath { get; set; } + [DataMember( Name = "adult" )] + public bool IsAdultThemed { get; set; } - [DataMember( Name = "belongs_to_collection" )] - public CollectionInfo MovieCollectionInfo { get; set; } + [DataMember( Name = "backdrop_path" )] + public string BackdropPath { get; set; } - [DataMember( Name = "budget" )] - public int Budget { get; set; } + [DataMember( Name = "belongs_to_collection" )] + public CollectionInfo MovieCollectionInfo { get; set; } - [DataMember( Name = "genres" )] - public IReadOnlyList Genres { get; set; } + [DataMember( Name = "budget" )] + public int Budget { get; set; } - [DataMember( Name = "homepage" )] - public string Homepage { get; set; } + [DataMember( Name = "genres" )] + public IReadOnlyList Genres { get; set; } - [DataMember( Name = "imdb_id" )] - public string ImdbId { get; set; } + [DataMember( Name = "homepage" )] + public string Homepage { get; set; } - /// - /// ISO 3166-1 code. - /// - [DataMember( Name = "original_language" )] - public string OriginalLanguage { get; set; } + [DataMember( Name = "imdb_id" )] + public string ImdbId { get; set; } - [DataMember( Name = "original_title" )] - public string OriginalTitle { get; set; } + /// + /// ISO 3166-1 code. + /// + [DataMember( Name = "original_language" )] + public string OriginalLanguage { get; set; } - [DataMember( Name = "overview" )] - public string Overview { get; set; } + [DataMember( Name = "original_title" )] + public string OriginalTitle { get; set; } - [DataMember( Name = "popularity" )] - public double Popularity { get; set; } + [DataMember( Name = "overview" )] + public string Overview { get; set; } - [DataMember( Name = "poster_path" )] - public string PosterPath { get; set; } + [DataMember( Name = "popularity" )] + public double Popularity { get; set; } - [DataMember( Name = "production_companies" )] - public IReadOnlyList ProductionCompanies { get; set; } + [DataMember( Name = "poster_path" )] + public string PosterPath { get; set; } - [DataMember( Name = "production_countries" )] - public IReadOnlyList ProductionCountries { get; set; } + [DataMember( Name = "production_companies" )] + public IReadOnlyList ProductionCompanies { get; set; } - [DataMember( Name = "release_date" )] - public DateTime ReleaseDate { get; set; } + [DataMember( Name = "production_countries" )] + public IReadOnlyList ProductionCountries { get; set; } - [DataMember( Name = "revenue" )] - public decimal Revenue { get; set; } + [DataMember( Name = "release_date" )] + public DateTime ReleaseDate { get; set; } - [DataMember( Name = "runtime" )] - public int Runtime { get; set; } + [DataMember( Name = "revenue" )] + public decimal Revenue { get; set; } - [DataMember( Name = "spoken_languages" )] - public IReadOnlyList SpokenLanguages { get; set; } + [DataMember( Name = "runtime" )] + public int Runtime { get; set; } - [DataMember( Name = "status" )] - public string Status { get; set; } + [DataMember( Name = "spoken_languages" )] + public IReadOnlyList SpokenLanguages { get; set; } - [DataMember( Name = "tagline" )] - public string Tagline { get; set; } + [DataMember( Name = "status" )] + public string Status { get; set; } - [DataMember( Name = "video" )] - public bool IsVideo { get; set; } + [DataMember( Name = "tagline" )] + public string Tagline { get; set; } - [DataMember( Name = "vote_average" )] - public double VoteAverage { get; set; } + [DataMember( Name = "video" )] + public bool IsVideo { get; set; } - [DataMember( Name = "vote_count" )] - public int VoteCount { get; set; } + [DataMember( Name = "vote_average" )] + public double VoteAverage { get; set; } - [DataMember( Name = "keywords" )] - [JsonConverter( typeof( KeywordConverter ), "keywords" )] - public IReadOnlyList Keywords { get; set; } + [DataMember( Name = "vote_count" )] + public int VoteCount { get; set; } - public Movie() - { - Genres = Array.Empty(); - Keywords = Array.Empty(); - ProductionCompanies = Array.Empty(); - ProductionCountries = Array.Empty(); - SpokenLanguages = Array.Empty(); - } + [DataMember( Name = "keywords" )] + [JsonConverter( typeof( KeywordConverter ), "keywords" )] + public IReadOnlyList Keywords { get; set; } - public override string ToString() - => $"{Title} ({ReleaseDate:yyyy-MM-dd}) [{Id}]"; + public Movie() + { + Genres = Array.Empty(); + Keywords = Array.Empty(); + ProductionCompanies = Array.Empty(); + ProductionCountries = Array.Empty(); + SpokenLanguages = Array.Empty(); } + + public override string ToString() + => $"{Title} ({ReleaseDate:yyyy-MM-dd}) [{Id}]"; } diff --git a/DM.MovieApi/MovieDb/Movies/MovieCredit.cs b/DM.MovieApi/MovieDb/Movies/MovieCredit.cs index 11fa133..7afbd3b 100644 --- a/DM.MovieApi/MovieDb/Movies/MovieCredit.cs +++ b/DM.MovieApi/MovieDb/Movies/MovieCredit.cs @@ -1,71 +1,67 @@ -using System.Collections.Generic; -using System.Runtime.Serialization; +namespace DM.MovieApi.MovieDb.Movies; -namespace DM.MovieApi.MovieDb.Movies +[DataContract] +public class MovieCredit { - [DataContract] - public class MovieCredit - { - [DataMember( Name = "id" )] - public int MovieId { get; set; } + [DataMember( Name = "id" )] + public int MovieId { get; set; } - [DataMember( Name = "cast" )] - public IReadOnlyList CastMembers { get; set; } + [DataMember( Name = "cast" )] + public IReadOnlyList CastMembers { get; set; } - [DataMember( Name = "crew" )] - public IReadOnlyList CrewMembers { get; set; } - } + [DataMember( Name = "crew" )] + public IReadOnlyList CrewMembers { get; set; } +} - [DataContract] - public class MovieCastMember - { - [DataMember( Name = "id" )] - public int PersonId { get; set; } +[DataContract] +public class MovieCastMember +{ + [DataMember( Name = "id" )] + public int PersonId { get; set; } - [DataMember( Name = "cast_id" )] - public int CastId { get; set; } + [DataMember( Name = "cast_id" )] + public int CastId { get; set; } - [DataMember( Name = "credit_id" )] - public string CreditId { get; set; } + [DataMember( Name = "credit_id" )] + public string CreditId { get; set; } - [DataMember( Name = "character" )] - public string Character { get; set; } + [DataMember( Name = "character" )] + public string Character { get; set; } - [DataMember( Name = "name" )] - public string Name { get; set; } + [DataMember( Name = "name" )] + public string Name { get; set; } - [DataMember( Name = "order" )] - public int Order { get; set; } + [DataMember( Name = "order" )] + public int Order { get; set; } - [DataMember( Name = "profile_path" )] - public string ProfilePath { get; set; } + [DataMember( Name = "profile_path" )] + public string ProfilePath { get; set; } - public override string ToString() - => $"{Character}: {Name}"; - } + public override string ToString() + => $"{Character}: {Name}"; +} - [DataContract] - public class MovieCrewMember - { - [DataMember( Name = "id" )] - public int PersonId { get; set; } +[DataContract] +public class MovieCrewMember +{ + [DataMember( Name = "id" )] + public int PersonId { get; set; } - [DataMember( Name = "credit_id" )] - public string CreditId { get; set; } + [DataMember( Name = "credit_id" )] + public string CreditId { get; set; } - [DataMember( Name = "department" )] - public string Department { get; set; } + [DataMember( Name = "department" )] + public string Department { get; set; } - [DataMember( Name = "job" )] - public string Job { get; set; } + [DataMember( Name = "job" )] + public string Job { get; set; } - [DataMember( Name = "name" )] - public string Name { get; set; } + [DataMember( Name = "name" )] + public string Name { get; set; } - [DataMember( Name = "profile_path" )] - public string ProfilePath { get; set; } + [DataMember( Name = "profile_path" )] + public string ProfilePath { get; set; } - public override string ToString() - => $"{Name} | {Department} | {Job}"; - } + public override string ToString() + => $"{Name} | {Department} | {Job}"; } diff --git a/DM.MovieApi/MovieDb/Movies/MovieInfo.cs b/DM.MovieApi/MovieDb/Movies/MovieInfo.cs index 31e6d93..fa6f68f 100644 --- a/DM.MovieApi/MovieDb/Movies/MovieInfo.cs +++ b/DM.MovieApi/MovieDb/Movies/MovieInfo.cs @@ -1,62 +1,56 @@ -using System; -using System.Collections.Generic; -using System.Runtime.Serialization; -using DM.MovieApi.MovieDb.Genres; +namespace DM.MovieApi.MovieDb.Movies; -namespace DM.MovieApi.MovieDb.Movies +[DataContract] +public class MovieInfo { - [DataContract] - public class MovieInfo - { - [DataMember( Name = "id" )] - public int Id { get; set; } - - [DataMember( Name = "title" )] - public string Title { get; set; } + [DataMember( Name = "id" )] + public int Id { get; set; } - [DataMember( Name = "adult" )] - public bool IsAdultThemed { get; set; } + [DataMember( Name = "title" )] + public string Title { get; set; } - [DataMember( Name = "backdrop_path" )] - public string BackdropPath { get; set; } + [DataMember( Name = "adult" )] + public bool IsAdultThemed { get; set; } - [DataMember( Name = "genre_ids" )] - internal IReadOnlyList GenreIds { get; set; } + [DataMember( Name = "backdrop_path" )] + public string BackdropPath { get; set; } - public IReadOnlyList Genres { get; set; } + [DataMember( Name = "genre_ids" )] + internal IReadOnlyList GenreIds { get; set; } - [DataMember( Name = "original_title" )] - public string OriginalTitle { get; set; } + public IReadOnlyList Genres { get; set; } - [DataMember( Name = "overview" )] - public string Overview { get; set; } + [DataMember( Name = "original_title" )] + public string OriginalTitle { get; set; } - [DataMember( Name = "release_date" )] - public DateTime ReleaseDate { get; set; } + [DataMember( Name = "overview" )] + public string Overview { get; set; } - [DataMember( Name = "poster_path" )] - public string PosterPath { get; set; } + [DataMember( Name = "release_date" )] + public DateTime ReleaseDate { get; set; } - [DataMember( Name = "popularity" )] - public double Popularity { get; set; } + [DataMember( Name = "poster_path" )] + public string PosterPath { get; set; } - [DataMember( Name = "video" )] - public bool Video { get; set; } + [DataMember( Name = "popularity" )] + public double Popularity { get; set; } - [DataMember( Name = "vote_average" )] - public double VoteAverage { get; set; } + [DataMember( Name = "video" )] + public bool Video { get; set; } - [DataMember( Name = "vote_count" )] - public int VoteCount { get; set; } + [DataMember( Name = "vote_average" )] + public double VoteAverage { get; set; } - public MovieInfo() - { - GenreIds = Array.Empty(); - Genres = Array.Empty(); - ReleaseDate = DateTime.UnixEpoch; - } + [DataMember( Name = "vote_count" )] + public int VoteCount { get; set; } - public override string ToString() - => $"{Title} ({Id} - {ReleaseDate:yyyy-MM-dd})"; + public MovieInfo() + { + GenreIds = Array.Empty(); + Genres = Array.Empty(); + ReleaseDate = DateTime.UnixEpoch; } + + public override string ToString() + => $"{Title} ({Id} - {ReleaseDate:yyyy-MM-dd})"; } diff --git a/DM.MovieApi/MovieDb/People/ApiPeopleRequest.cs b/DM.MovieApi/MovieDb/People/ApiPeopleRequest.cs index f0b9469..9460e03 100644 --- a/DM.MovieApi/MovieDb/People/ApiPeopleRequest.cs +++ b/DM.MovieApi/MovieDb/People/ApiPeopleRequest.cs @@ -1,86 +1,78 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using DM.MovieApi.ApiRequest; -using DM.MovieApi.ApiResponse; -using DM.MovieApi.MovieDb.Genres; -using DM.MovieApi.Shims; - -namespace DM.MovieApi.MovieDb.People +namespace DM.MovieApi.MovieDb.People; + +internal class ApiPeopleRequest : ApiRequestBase, IApiPeopleRequest { - internal class ApiPeopleRequest : ApiRequestBase, IApiPeopleRequest - { - private readonly IApiGenreRequest _genreApi; + private readonly IApiGenreRequest _genreApi; - [ImportingConstructor] - public ApiPeopleRequest( IApiSettings settings, IApiGenreRequest genreApi ) - : base( settings ) - { - _genreApi = genreApi; - } + [ImportingConstructor] + public ApiPeopleRequest( IApiSettings settings, IApiGenreRequest genreApi ) + : base( settings ) + { + _genreApi = genreApi; + } - public async Task> FindByIdAsync( int personId, string language = "en" ) + public async Task> FindByIdAsync( int personId, string language = "en" ) + { + var param = new Dictionary { - var param = new Dictionary - { - {"language", language} - }; + {"language", language} + }; - string command = $"person/{personId}"; + string command = $"person/{personId}"; - ApiQueryResponse response = await base.QueryAsync( command, param ); + ApiQueryResponse response = await base.QueryAsync( command, param ); - return response; - } + return response; + } - public async Task> SearchByNameAsync( string query, int pageNumber = 1, string language = "en" ) + public async Task> SearchByNameAsync( string query, int pageNumber = 1, string language = "en" ) + { + var param = new Dictionary { - var param = new Dictionary - { - {"query", query}, - {"include_adult", "false"}, - {"language", language}, - }; - - const string command = "search/person"; + {"query", query}, + {"include_adult", "false"}, + {"language", language}, + }; - ApiSearchResponse response = await base.SearchAsync( command, pageNumber, param ); + const string command = "search/person"; - if( response.Error != null ) - { - return response; - } - - response.Results.PopulateGenres( _genreApi ); + ApiSearchResponse response = await base.SearchAsync( command, pageNumber, param ); + if( response.Error != null ) + { return response; } - public async Task> GetMovieCreditsAsync( int personId, string language = "en" ) + response.Results.PopulateGenres( _genreApi ); + + return response; + } + + public async Task> GetMovieCreditsAsync( int personId, string language = "en" ) + { + var param = new Dictionary { - var param = new Dictionary - { - {"language", language}, - }; + {"language", language}, + }; - string command = $"person/{personId}/movie_credits"; + string command = $"person/{personId}/movie_credits"; - ApiQueryResponse response = await base.QueryAsync( command, param ); + ApiQueryResponse response = await base.QueryAsync( command, param ); - return response; - } + return response; + } - public async Task> GetTVCreditsAsync( int personId, string language = "en" ) + public async Task> GetTVCreditsAsync( int personId, string language = "en" ) + { + var param = new Dictionary { - var param = new Dictionary - { - {"language", language}, - }; + {"language", language}, + }; - string command = $"person/{personId}/tv_credits"; + string command = $"person/{personId}/tv_credits"; - ApiQueryResponse response = await base.QueryAsync( command, param ); + ApiQueryResponse response = await base.QueryAsync( command, param ); - return response; - } + return response; } } diff --git a/DM.MovieApi/MovieDb/People/Gender.cs b/DM.MovieApi/MovieDb/People/Gender.cs index b49f512..e03a562 100644 --- a/DM.MovieApi/MovieDb/People/Gender.cs +++ b/DM.MovieApi/MovieDb/People/Gender.cs @@ -1,9 +1,8 @@ -namespace DM.MovieApi.MovieDb.People +namespace DM.MovieApi.MovieDb.People; + +public enum Gender { - public enum Gender - { - Unknown = 0, - Female = 1, - Male = 2, - } + Unknown = 0, + Female = 1, + Male = 2, } diff --git a/DM.MovieApi/MovieDb/People/IApiPeopleRequest.cs b/DM.MovieApi/MovieDb/People/IApiPeopleRequest.cs index c403666..cc5d7d3 100644 --- a/DM.MovieApi/MovieDb/People/IApiPeopleRequest.cs +++ b/DM.MovieApi/MovieDb/People/IApiPeopleRequest.cs @@ -1,41 +1,36 @@ -using System.Threading.Tasks; -using DM.MovieApi.ApiRequest; -using DM.MovieApi.ApiResponse; +namespace DM.MovieApi.MovieDb.People; -namespace DM.MovieApi.MovieDb.People +/// +/// Interface for retrieving information about People. +/// +public interface IApiPeopleRequest : IApiRequest { /// - /// Interface for retrieving information about People. + /// Gets all the information about a specific Person. /// - public interface IApiPeopleRequest : IApiRequest - { - /// - /// Gets all the information about a specific Person. - /// - /// The person Id is typically found from a more generic query such as movie or television or search. - /// Default is English. The ISO 639-1 language code to retrieve the result from. - Task> FindByIdAsync( int personId, string language = "en" ); + /// The person Id is typically found from a more generic query such as movie or television or search. + /// Default is English. The ISO 639-1 language code to retrieve the result from. + Task> FindByIdAsync( int personId, string language = "en" ); - /// - /// Searches for People by name. - /// - /// The query to search for People. - /// Default is page 1. The page number to retrieve; the will contain the current page returned and the total number of pages available. - /// Default is English. The ISO 639-1 language code to retrieve the result from. - Task> SearchByNameAsync( string query, int pageNumber = 1, string language = "en" ); + /// + /// Searches for People by name. + /// + /// The query to search for People. + /// Default is page 1. The page number to retrieve; the will contain the current page returned and the total number of pages available. + /// Default is English. The ISO 639-1 language code to retrieve the result from. + Task> SearchByNameAsync( string query, int pageNumber = 1, string language = "en" ); - /// - /// Get the movie credits for a specific person id. Includes movie cast and crew information for the person. - /// - /// The person Id is typically found from a more generic query such as movie or television or search. - /// Default is English. The ISO 639-1 language code to retrieve the result from. - Task> GetMovieCreditsAsync( int personId, string language = "en" ); + /// + /// Get the movie credits for a specific person id. Includes movie cast and crew information for the person. + /// + /// The person Id is typically found from a more generic query such as movie or television or search. + /// Default is English. The ISO 639-1 language code to retrieve the result from. + Task> GetMovieCreditsAsync( int personId, string language = "en" ); - /// - /// Get the television credits for a specific person id. Includes TV cast and crew information for the person. - /// - /// The person Id is typically found from a more generic query such as movie or television or search. - /// Default is English. The ISO 639-1 language code to retrieve the result from. - Task> GetTVCreditsAsync( int personId, string language = "en" ); - } + /// + /// Get the television credits for a specific person id. Includes TV cast and crew information for the person. + /// + /// The person Id is typically found from a more generic query such as movie or television or search. + /// Default is English. The ISO 639-1 language code to retrieve the result from. + Task> GetTVCreditsAsync( int personId, string language = "en" ); } diff --git a/DM.MovieApi/MovieDb/People/Person.cs b/DM.MovieApi/MovieDb/People/Person.cs index 5d765be..5e64869 100644 --- a/DM.MovieApi/MovieDb/People/Person.cs +++ b/DM.MovieApi/MovieDb/People/Person.cs @@ -1,57 +1,52 @@ -using System; -using System.Collections.Generic; -using System.Runtime.Serialization; +namespace DM.MovieApi.MovieDb.People; -namespace DM.MovieApi.MovieDb.People +[DataContract] +public class Person { - [DataContract] - public class Person - { - [DataMember( Name = "id" )] - public int Id { get; set; } - - [DataMember( Name = "name" )] - public string Name { get; set; } + [DataMember( Name = "id" )] + public int Id { get; set; } - [DataMember( Name = "also_known_as" )] - public IReadOnlyList AlsoKnownAs { get; set; } + [DataMember( Name = "name" )] + public string Name { get; set; } - [DataMember( Name = "adult" )] - public bool IsAdultFilmStar { get; set; } + [DataMember( Name = "also_known_as" )] + public IReadOnlyList AlsoKnownAs { get; set; } - [DataMember( Name = "biography" )] - public string Biography { get; set; } + [DataMember( Name = "adult" )] + public bool IsAdultFilmStar { get; set; } - [DataMember( Name = "birthday" )] - public DateTime Birthday { get; set; } + [DataMember( Name = "biography" )] + public string Biography { get; set; } - [DataMember( Name = "deathday" )] - public DateTime? Deathday { get; set; } + [DataMember( Name = "birthday" )] + public DateTime Birthday { get; set; } - [DataMember( Name = "gender" )] - public Gender Gender { get; set; } + [DataMember( Name = "deathday" )] + public DateTime? Deathday { get; set; } - [DataMember( Name = "homepage" )] - public string Homepage { get; set; } + [DataMember( Name = "gender" )] + public Gender Gender { get; set; } - [DataMember( Name = "imdb_id" )] - public string ImdbId { get; set; } + [DataMember( Name = "homepage" )] + public string Homepage { get; set; } - [DataMember( Name = "place_of_birth" )] - public string PlaceOfBirth { get; set; } + [DataMember( Name = "imdb_id" )] + public string ImdbId { get; set; } - [DataMember( Name = "popularity" )] - public double Popularity { get; set; } + [DataMember( Name = "place_of_birth" )] + public string PlaceOfBirth { get; set; } - [DataMember( Name = "profile_path" )] - public string ProfilePath { get; set; } + [DataMember( Name = "popularity" )] + public double Popularity { get; set; } - public Person() - { - AlsoKnownAs = Array.Empty(); - } + [DataMember( Name = "profile_path" )] + public string ProfilePath { get; set; } - public override string ToString() - => Name; + public Person() + { + AlsoKnownAs = Array.Empty(); } + + public override string ToString() + => Name; } diff --git a/DM.MovieApi/MovieDb/People/PersonInfo.cs b/DM.MovieApi/MovieDb/People/PersonInfo.cs index ad450b5..81ddf9c 100644 --- a/DM.MovieApi/MovieDb/People/PersonInfo.cs +++ b/DM.MovieApi/MovieDb/People/PersonInfo.cs @@ -1,146 +1,140 @@ -using System; -using System.Collections.Generic; -using System.Runtime.Serialization; -using DM.MovieApi.MovieDb.Genres; +namespace DM.MovieApi.MovieDb.People; -namespace DM.MovieApi.MovieDb.People +public enum MediaType { - public enum MediaType + Unknown, + Movie, + TV, +} + +[DataContract] +public class PersonInfo +{ + // TODO: (K. Chase) [2016-07-10] Update all POCO's to explicitly name the Id property, i.e,. PersonId, MovieId, TVShowId. + [DataMember( Name = "id" )] + public int Id { get; set; } + + [DataMember( Name = "name" )] + public string Name { get; set; } + + [DataMember( Name = "adult" )] + public bool IsAdultFilmStar { get; set; } + + [DataMember( Name = "known_for" )] + public IReadOnlyList KnownFor { get; set; } + + [DataMember( Name = "profile_path" )] + public string ProfilePath { get; set; } + + [DataMember( Name = "popularity" )] + public double Popularity { get; set; } + + public PersonInfo() { - Unknown, - Movie, - TV, + KnownFor = Array.Empty(); } - [DataContract] - public class PersonInfo - { - // TODO: (K. Chase) [2016-07-10] Update all POCO's to explicitly name the Id property, i.e,. PersonId, MovieId, TVShowId. - [DataMember( Name = "id" )] - public int Id { get; set; } + public override string ToString() + => $"{Name} ({Id})"; +} + +[DataContract] +public class PersonInfoRole +{ + // TODO: (K. Chase) [2016-07-10] Break into type for Movie and TV w/ a custom serializer. + // re: see TVShowName v MovieTitle (and related) + + /// + /// The MovieId or TVShowId as defined by the value of . + /// + [DataMember( Name = "id" )] + public int Id { get; set; } - [DataMember( Name = "name" )] - public string Name { get; set; } + [DataMember( Name = "media_type" )] + public MediaType MediaType { get; set; } - [DataMember( Name = "adult" )] - public bool IsAdultFilmStar { get; set; } + /// + /// Only populated when is TV. + /// + [DataMember( Name = "name" )] + public string TVShowName { get; set; } - [DataMember( Name = "known_for" )] - public IReadOnlyList KnownFor { get; set; } + /// + /// Only populated when is TV. + /// + [DataMember( Name = "original_name" )] + public string TVShowOriginalName { get; set; } - [DataMember( Name = "profile_path" )] - public string ProfilePath { get; set; } + /// + /// Only populated when is Movie. + /// + [DataMember( Name = "title" )] + public string MovieTitle { get; set; } - [DataMember( Name = "popularity" )] - public double Popularity { get; set; } + /// + /// Only populated when is Movie. + /// + [DataMember( Name = "original_title" )] + public string MovieOriginalTitle { set; get; } - public PersonInfo() - { - KnownFor = Array.Empty(); - } + [DataMember( Name = "backdrop_path" )] + public string BackdropPath { get; set; } - public override string ToString() - => $"{Name} ({Id})"; + [DataMember( Name = "poster_path" )] + public string PosterPath { get; set; } + + /// + /// Only populated when is Movie. + /// + [DataMember( Name = "release_date" )] + public DateTime MovieReleaseDate { get; set; } + + /// + /// Only populated when is TV. + /// + [DataMember( Name = "first_air_date" )] + public DateTime TVShowFirstAirDate { get; set; } + + [DataMember( Name = "overview" )] + public string Overview { get; set; } + + [DataMember( Name = "adult" )] + public bool IsAdultThemed { get; set; } + + [DataMember( Name = "video" )] + public bool IsVideo { get; set; } + + [DataMember( Name = "genre_ids" )] + internal IReadOnlyList GenreIds { get; set; } + + public IReadOnlyList Genres { get; set; } + + [DataMember( Name = "original_language" )] + public string OriginalLanguage { get; set; } + + [DataMember( Name = "popularity" )] + public double Popularity { get; set; } + + [DataMember( Name = "vote_count" )] + public int VoteCount { get; set; } + + [DataMember( Name = "vote_average" )] + public double VoteAverage { get; set; } + + [DataMember( Name = "origin_country" )] + public IReadOnlyList OriginCountry { get; set; } + + public PersonInfoRole() + { + GenreIds = Array.Empty(); + Genres = Array.Empty(); + OriginCountry = Array.Empty(); } - [DataContract] - public class PersonInfoRole + public override string ToString() { - // TODO: (K. Chase) [2016-07-10] Break into type for Movie and TV w/ a custom serializer. - // re: see TVShowName v MovieTitle (and related) - - /// - /// The MovieId or TVShowId as defined by the value of . - /// - [DataMember( Name = "id" )] - public int Id { get; set; } - - [DataMember( Name = "media_type" )] - public MediaType MediaType { get; set; } - - /// - /// Only populated when is TV. - /// - [DataMember( Name = "name" )] - public string TVShowName { get; set; } - - /// - /// Only populated when is TV. - /// - [DataMember( Name = "original_name" )] - public string TVShowOriginalName { get; set; } - - /// - /// Only populated when is Movie. - /// - [DataMember( Name = "title" )] - public string MovieTitle { get; set; } - - /// - /// Only populated when is Movie. - /// - [DataMember( Name = "original_title" )] - public string MovieOriginalTitle { set; get; } - - [DataMember( Name = "backdrop_path" )] - public string BackdropPath { get; set; } - - [DataMember( Name = "poster_path" )] - public string PosterPath { get; set; } - - /// - /// Only populated when is Movie. - /// - [DataMember( Name = "release_date" )] - public DateTime MovieReleaseDate { get; set; } - - /// - /// Only populated when is TV. - /// - [DataMember( Name = "first_air_date" )] - public DateTime TVShowFirstAirDate { get; set; } - - [DataMember( Name = "overview" )] - public string Overview { get; set; } - - [DataMember( Name = "adult" )] - public bool IsAdultThemed { get; set; } - - [DataMember( Name = "video" )] - public bool IsVideo { get; set; } - - [DataMember( Name = "genre_ids" )] - internal IReadOnlyList GenreIds { get; set; } - - public IReadOnlyList Genres { get; set; } - - [DataMember( Name = "original_language" )] - public string OriginalLanguage { get; set; } - - [DataMember( Name = "popularity" )] - public double Popularity { get; set; } - - [DataMember( Name = "vote_count" )] - public int VoteCount { get; set; } - - [DataMember( Name = "vote_average" )] - public double VoteAverage { get; set; } - - [DataMember( Name = "origin_country" )] - public IReadOnlyList OriginCountry { get; set; } - - public PersonInfoRole() - { - GenreIds = Array.Empty(); - Genres = Array.Empty(); - OriginCountry = Array.Empty(); - } - - public override string ToString() - { - return MediaType == MediaType.Movie - ? $"Movie: {MovieTitle} ({Id} - {MovieReleaseDate:yyyy-MM-dd})" - : $"TV: {TVShowName} ({Id} - {TVShowFirstAirDate:yyyy-MM-dd})"; - } + return MediaType == MediaType.Movie + ? $"Movie: {MovieTitle} ({Id} - {MovieReleaseDate:yyyy-MM-dd})" + : $"TV: {TVShowName} ({Id} - {TVShowFirstAirDate:yyyy-MM-dd})"; } } diff --git a/DM.MovieApi/MovieDb/People/PersonMovieCredit.cs b/DM.MovieApi/MovieDb/People/PersonMovieCredit.cs index 8555a65..ed00c56 100644 --- a/DM.MovieApi/MovieDb/People/PersonMovieCredit.cs +++ b/DM.MovieApi/MovieDb/People/PersonMovieCredit.cs @@ -1,84 +1,79 @@ -using System; -using System.Collections.Generic; -using System.Runtime.Serialization; +namespace DM.MovieApi.MovieDb.People; -namespace DM.MovieApi.MovieDb.People +[DataContract] +public class PersonMovieCredit { - [DataContract] - public class PersonMovieCredit - { - [DataMember( Name = "id" )] - public int PersonId { get; set; } + [DataMember( Name = "id" )] + public int PersonId { get; set; } - [DataMember( Name = "cast" )] - public IReadOnlyList CastRoles { get; set; } + [DataMember( Name = "cast" )] + public IReadOnlyList CastRoles { get; set; } - [DataMember( Name = "crew" )] - public IReadOnlyList CrewRoles { get; set; } + [DataMember( Name = "crew" )] + public IReadOnlyList CrewRoles { get; set; } - public PersonMovieCredit() - { - CastRoles = Array.Empty(); - CrewRoles = Array.Empty(); - } + public PersonMovieCredit() + { + CastRoles = Array.Empty(); + CrewRoles = Array.Empty(); } +} - [DataContract] - public class PersonMovieCastMember - { - [DataMember( Name = "id" )] - public int MovieId { get; set; } +[DataContract] +public class PersonMovieCastMember +{ + [DataMember( Name = "id" )] + public int MovieId { get; set; } - [DataMember( Name = "adult" )] - public bool IsAdultThemed { get; set; } + [DataMember( Name = "adult" )] + public bool IsAdultThemed { get; set; } - [DataMember( Name = "character" )] - public string Character { get; set; } + [DataMember( Name = "character" )] + public string Character { get; set; } - [DataMember( Name = "credit_id" )] - public string CreditId { get; set; } + [DataMember( Name = "credit_id" )] + public string CreditId { get; set; } - [DataMember( Name = "original_title" )] - public string OriginalTitle { get; set; } + [DataMember( Name = "original_title" )] + public string OriginalTitle { get; set; } - [DataMember( Name = "poster_path" )] - public string PosterPath { get; set; } + [DataMember( Name = "poster_path" )] + public string PosterPath { get; set; } - [DataMember( Name = "release_date" )] - public DateTime ReleaseDate { get; set; } + [DataMember( Name = "release_date" )] + public DateTime ReleaseDate { get; set; } - [DataMember( Name = "title" )] - public string Title { get; set; } - } + [DataMember( Name = "title" )] + public string Title { get; set; } +} - [DataContract] - public class PersonMovieCrewMember - { - [DataMember( Name = "id" )] - public int MovieId { get; set; } +[DataContract] +public class PersonMovieCrewMember +{ + [DataMember( Name = "id" )] + public int MovieId { get; set; } - [DataMember( Name = "adult" )] - public bool IsAdultThemed { get; set; } + [DataMember( Name = "adult" )] + public bool IsAdultThemed { get; set; } - [DataMember( Name = "credit_id" )] - public string CreditId { get; set; } + [DataMember( Name = "credit_id" )] + public string CreditId { get; set; } - [DataMember( Name = "department" )] - public string Department { get; set; } + [DataMember( Name = "department" )] + public string Department { get; set; } - [DataMember( Name = "job" )] - public string Job { get; set; } + [DataMember( Name = "job" )] + public string Job { get; set; } - [DataMember( Name = "original_title" )] - public string OriginalTitle { get; set; } + [DataMember( Name = "original_title" )] + public string OriginalTitle { get; set; } - [DataMember( Name = "poster_path" )] - public string PosterPath { get; set; } + [DataMember( Name = "poster_path" )] + public string PosterPath { get; set; } - [DataMember( Name = "release_date" )] - public DateTime ReleaseDate { get; set; } + [DataMember( Name = "release_date" )] + public DateTime ReleaseDate { get; set; } - [DataMember( Name = "title" )] - public string Title { get; set; } - } + [DataMember( Name = "title" )] + public string Title { get; set; } } diff --git a/DM.MovieApi/MovieDb/People/PersonTVCredit.cs b/DM.MovieApi/MovieDb/People/PersonTVCredit.cs index 9587162..38025e0 100644 --- a/DM.MovieApi/MovieDb/People/PersonTVCredit.cs +++ b/DM.MovieApi/MovieDb/People/PersonTVCredit.cs @@ -1,84 +1,79 @@ -using System; -using System.Collections.Generic; -using System.Runtime.Serialization; +namespace DM.MovieApi.MovieDb.People; -namespace DM.MovieApi.MovieDb.People +[DataContract] +public class PersonTVCredit { - [DataContract] - public class PersonTVCredit - { - [DataMember( Name = "id" )] - public int PersonId { get; set; } + [DataMember( Name = "id" )] + public int PersonId { get; set; } - [DataMember( Name = "cast" )] - public IReadOnlyList CastRoles { get; set; } + [DataMember( Name = "cast" )] + public IReadOnlyList CastRoles { get; set; } - [DataMember( Name = "crew" )] - public IReadOnlyList CrewRoles { get; set; } + [DataMember( Name = "crew" )] + public IReadOnlyList CrewRoles { get; set; } - public PersonTVCredit() - { - CastRoles = Array.Empty(); - CrewRoles = Array.Empty(); - } + public PersonTVCredit() + { + CastRoles = Array.Empty(); + CrewRoles = Array.Empty(); } +} - [DataContract] - public class PersonTVCastMember - { - [DataMember( Name = "id" )] - public int TVShowId { get; set; } +[DataContract] +public class PersonTVCastMember +{ + [DataMember( Name = "id" )] + public int TVShowId { get; set; } - [DataMember( Name = "character" )] - public string Character { get; set; } + [DataMember( Name = "character" )] + public string Character { get; set; } - [DataMember( Name = "credit_id" )] - public string CreditId { get; set; } + [DataMember( Name = "credit_id" )] + public string CreditId { get; set; } - [DataMember( Name = "episode_count" )] - public int EpisodeCount { get; set; } + [DataMember( Name = "episode_count" )] + public int EpisodeCount { get; set; } - [DataMember( Name = "first_air_date" )] - public DateTime FirstAirDate { get; set; } + [DataMember( Name = "first_air_date" )] + public DateTime FirstAirDate { get; set; } - [DataMember( Name = "name" )] - public string Name { get; set; } + [DataMember( Name = "name" )] + public string Name { get; set; } - [DataMember( Name = "original_name" )] - public string OriginalName { get; set; } + [DataMember( Name = "original_name" )] + public string OriginalName { get; set; } - [DataMember( Name = "poster_path" )] - public string PosterPath { get; set; } - } + [DataMember( Name = "poster_path" )] + public string PosterPath { get; set; } +} - [DataContract] - public class PersonTVCrewMember - { - [DataMember( Name = "id" )] - public int TVShowId { get; set; } +[DataContract] +public class PersonTVCrewMember +{ + [DataMember( Name = "id" )] + public int TVShowId { get; set; } - [DataMember( Name = "credit_id" )] - public string CreditId { get; set; } + [DataMember( Name = "credit_id" )] + public string CreditId { get; set; } - [DataMember( Name = "department" )] - public string Department { get; set; } + [DataMember( Name = "department" )] + public string Department { get; set; } - [DataMember( Name = "episode_count" )] - public int EpisodeCount { get; set; } + [DataMember( Name = "episode_count" )] + public int EpisodeCount { get; set; } - [DataMember( Name = "first_air_date" )] - public DateTime FirstAirDate { get; set; } + [DataMember( Name = "first_air_date" )] + public DateTime FirstAirDate { get; set; } - [DataMember( Name = "job" )] - public string Job { get; set; } + [DataMember( Name = "job" )] + public string Job { get; set; } - [DataMember( Name = "name" )] - public string Name { get; set; } + [DataMember( Name = "name" )] + public string Name { get; set; } - [DataMember( Name = "original_name" )] - public string OriginalName { get; set; } + [DataMember( Name = "original_name" )] + public string OriginalName { get; set; } - [DataMember( Name = "poster_path" )] - public string PosterPath { get; set; } - } + [DataMember( Name = "poster_path" )] + public string PosterPath { get; set; } } diff --git a/DM.MovieApi/MovieDb/TV/ApiTVShowRequest.cs b/DM.MovieApi/MovieDb/TV/ApiTVShowRequest.cs index 62f0119..94938df 100644 --- a/DM.MovieApi/MovieDb/TV/ApiTVShowRequest.cs +++ b/DM.MovieApi/MovieDb/TV/ApiTVShowRequest.cs @@ -1,130 +1,122 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using DM.MovieApi.ApiRequest; -using DM.MovieApi.ApiResponse; -using DM.MovieApi.MovieDb.Genres; -using DM.MovieApi.Shims; - -namespace DM.MovieApi.MovieDb.TV +namespace DM.MovieApi.MovieDb.TV; + +internal class ApiTVShowRequest : ApiRequestBase, IApiTVShowRequest { - internal class ApiTVShowRequest : ApiRequestBase, IApiTVShowRequest - { - private readonly IApiGenreRequest _genreApi; + private readonly IApiGenreRequest _genreApi; - [ImportingConstructor] - public ApiTVShowRequest( IApiSettings settings, IApiGenreRequest genreApi ) - : base( settings ) - { - _genreApi = genreApi; - } + [ImportingConstructor] + public ApiTVShowRequest( IApiSettings settings, IApiGenreRequest genreApi ) + : base( settings ) + { + _genreApi = genreApi; + } - public async Task> FindByIdAsync( int tvShowId, string language = "en" ) + public async Task> FindByIdAsync( int tvShowId, string language = "en" ) + { + var param = new Dictionary { - var param = new Dictionary - { - { "language", language }, - { "append_to_response", "keywords" }, - }; + { "language", language }, + { "append_to_response", "keywords" }, + }; - string command = $"tv/{tvShowId}"; + string command = $"tv/{tvShowId}"; - ApiQueryResponse response = await base.QueryAsync( command, param ); + ApiQueryResponse response = await base.QueryAsync( command, param ); - return response; - } + return response; + } - public async Task> SearchByNameAsync( string query, int pageNumber = 1, string language = "en" ) + public async Task> SearchByNameAsync( string query, int pageNumber = 1, string language = "en" ) + { + var param = new Dictionary { - var param = new Dictionary - { - { "query", query }, - { "language", language } - }; - - const string command = "search/tv"; + { "query", query }, + { "language", language } + }; - ApiSearchResponse response = await base.SearchAsync( command, pageNumber, param ); + const string command = "search/tv"; - if( response.Error != null ) - { - return response; - } - - response.Results.PopulateGenres( _genreApi ); + ApiSearchResponse response = await base.SearchAsync( command, pageNumber, param ); + if( response.Error != null ) + { return response; } - public async Task> GetLatestAsync( string language = "en" ) - { - var param = new Dictionary - { - { "language", language }, - { "append_to_response", "keywords" }, - }; + response.Results.PopulateGenres( _genreApi ); - const string command = "tv/latest"; + return response; + } - ApiQueryResponse response = await base.QueryAsync( command, param ); + public async Task> GetLatestAsync( string language = "en" ) + { + var param = new Dictionary + { + { "language", language }, + { "append_to_response", "keywords" }, + }; - return response; - } + const string command = "tv/latest"; - public async Task> GetTopRatedAsync( int pageNumber = 1, string language = "en" ) - { - var param = new Dictionary - { - { "language", language } - }; + ApiQueryResponse response = await base.QueryAsync( command, param ); - const string command = "tv/top_rated"; + return response; + } - ApiSearchResponse response = await base.SearchAsync( command, pageNumber, param ); + public async Task> GetTopRatedAsync( int pageNumber = 1, string language = "en" ) + { + var param = new Dictionary + { + { "language", language } + }; - if( response.Error != null ) - { - return response; - } + const string command = "tv/top_rated"; - response.Results.PopulateGenres( _genreApi ); + ApiSearchResponse response = await base.SearchAsync( command, pageNumber, param ); + if( response.Error != null ) + { return response; } - public async Task> GetPopularAsync( int pageNumber = 1, string language = "en" ) - { - var param = new Dictionary - { - { "language", language } - }; + response.Results.PopulateGenres( _genreApi ); - const string command = "tv/popular"; + return response; + } - ApiSearchResponse response = await base.SearchAsync( command, pageNumber, param ); + public async Task> GetPopularAsync( int pageNumber = 1, string language = "en" ) + { + var param = new Dictionary + { + { "language", language } + }; - if( response.Error != null ) - { - return response; - } + const string command = "tv/popular"; - response.Results.PopulateGenres( _genreApi ); + ApiSearchResponse response = await base.SearchAsync( command, pageNumber, param ); + if( response.Error != null ) + { return response; } - public async Task> GetTvShowSeasonInfoAsync( int tvShowId, int seasonNumber, string language = "en" ) + response.Results.PopulateGenres( _genreApi ); + + return response; + } + + public async Task> GetTvShowSeasonInfoAsync( int tvShowId, int seasonNumber, string language = "en" ) + { + var param = new Dictionary { - var param = new Dictionary - { - { "language", language }, - { "append_to_response", "keywords" }, - }; + { "language", language }, + { "append_to_response", "keywords" }, + }; - string command = $"tv/{tvShowId}/season/{seasonNumber}"; + string command = $"tv/{tvShowId}/season/{seasonNumber}"; - ApiQueryResponse response = await base.QueryAsync( command, param ); + ApiQueryResponse response = await base.QueryAsync( command, param ); - return response; - } + return response; } } diff --git a/DM.MovieApi/MovieDb/TV/Crew.cs b/DM.MovieApi/MovieDb/TV/Crew.cs index 776966f..3cb14bc 100644 --- a/DM.MovieApi/MovieDb/TV/Crew.cs +++ b/DM.MovieApi/MovieDb/TV/Crew.cs @@ -1,44 +1,41 @@ -using System.Runtime.Serialization; +namespace DM.MovieApi.MovieDb.TV; -namespace DM.MovieApi.MovieDb.TV +[DataContract] +public class Crew { - [DataContract] - public class Crew - { - [DataMember( Name = "id" )] - public int Id { get; set; } + [DataMember( Name = "id" )] + public int Id { get; set; } - [DataMember( Name = "job" )] - public string Job { get; set; } + [DataMember( Name = "job" )] + public string Job { get; set; } - [DataMember( Name = "department" )] - public string Department { get; set; } + [DataMember( Name = "department" )] + public string Department { get; set; } - [DataMember( Name = "credit_id" )] - public string CreditId { get; set; } + [DataMember( Name = "credit_id" )] + public string CreditId { get; set; } - [DataMember( Name = "adult" )] - public bool? Adult { get; set; } + [DataMember( Name = "adult" )] + public bool? Adult { get; set; } - [DataMember( Name = "gender" )] - public int Gender { get; set; } + [DataMember( Name = "gender" )] + public int Gender { get; set; } - [DataMember( Name = "known_for_department" )] - public string KnownForDepartment { get; set; } + [DataMember( Name = "known_for_department" )] + public string KnownForDepartment { get; set; } - [DataMember( Name = "name" )] - public string Name { get; set; } + [DataMember( Name = "name" )] + public string Name { get; set; } - [DataMember( Name = "original_name" )] - public string OriginalName { get; set; } + [DataMember( Name = "original_name" )] + public string OriginalName { get; set; } - [DataMember( Name = "popularity" )] - public float Popularity { get; set; } + [DataMember( Name = "popularity" )] + public float Popularity { get; set; } - [DataMember( Name = "profile_path" )] - public string ProfilePath { get; set; } + [DataMember( Name = "profile_path" )] + public string ProfilePath { get; set; } - public override string ToString() - => $"{Name} - {Job}"; - } + public override string ToString() + => $"{Name} - {Job}"; } diff --git a/DM.MovieApi/MovieDb/TV/Episode.cs b/DM.MovieApi/MovieDb/TV/Episode.cs index 3f1b477..55b8a53 100644 --- a/DM.MovieApi/MovieDb/TV/Episode.cs +++ b/DM.MovieApi/MovieDb/TV/Episode.cs @@ -1,55 +1,50 @@ -using System; -using System.Collections.Generic; -using System.Runtime.Serialization; +namespace DM.MovieApi.MovieDb.TV; -namespace DM.MovieApi.MovieDb.TV +[DataContract] +public class Episode { - [DataContract] - public class Episode - { - [DataMember( Name = "id" )] - public int Id { get; set; } - - [DataMember( Name = "air_date" )] - public DateTime AirDate { get; set; } + [DataMember( Name = "id" )] + public int Id { get; set; } - [DataMember( Name = "episode_number" )] - public int EpisodeNumber { get; set; } + [DataMember( Name = "air_date" )] + public DateTime AirDate { get; set; } - [DataMember( Name = "crew" )] - public IReadOnlyList Crew { get; set; } + [DataMember( Name = "episode_number" )] + public int EpisodeNumber { get; set; } - [DataMember( Name = "guest_stars" )] - public IReadOnlyList GuestStars { get; set; } + [DataMember( Name = "crew" )] + public IReadOnlyList Crew { get; set; } - [DataMember( Name = "name" )] - public string Name { get; set; } + [DataMember( Name = "guest_stars" )] + public IReadOnlyList GuestStars { get; set; } - [DataMember( Name = "overview" )] - public string Overview { get; set; } + [DataMember( Name = "name" )] + public string Name { get; set; } - [DataMember( Name = "production_code" )] - public string ProductionCode { get; set; } + [DataMember( Name = "overview" )] + public string Overview { get; set; } - [DataMember( Name = "season_number" )] - public int SeasonNumber { get; set; } + [DataMember( Name = "production_code" )] + public string ProductionCode { get; set; } - [DataMember( Name = "still_path" )] - public string StillPath { get; set; } + [DataMember( Name = "season_number" )] + public int SeasonNumber { get; set; } - [DataMember( Name = "vote_average" )] - public float VoteAverage { get; set; } + [DataMember( Name = "still_path" )] + public string StillPath { get; set; } - [DataMember( Name = "vote_count" )] - public int VoteCount { get; set; } + [DataMember( Name = "vote_average" )] + public float VoteAverage { get; set; } - public Episode() - { - Crew = Array.Empty(); - GuestStars = Array.Empty(); - } + [DataMember( Name = "vote_count" )] + public int VoteCount { get; set; } - public override string ToString() - => $"{Name} - {AirDate:yyyy-MM-dd}"; + public Episode() + { + Crew = Array.Empty(); + GuestStars = Array.Empty(); } + + public override string ToString() + => $"{Name} - {AirDate:yyyy-MM-dd}"; } diff --git a/DM.MovieApi/MovieDb/TV/GuestStars.cs b/DM.MovieApi/MovieDb/TV/GuestStars.cs index 8df6e96..0c5c36f 100644 --- a/DM.MovieApi/MovieDb/TV/GuestStars.cs +++ b/DM.MovieApi/MovieDb/TV/GuestStars.cs @@ -1,44 +1,41 @@ -using System.Runtime.Serialization; +namespace DM.MovieApi.MovieDb.TV; -namespace DM.MovieApi.MovieDb.TV +[DataContract] +public class GuestStars { - [DataContract] - public class GuestStars - { - [DataMember( Name = "id" )] - public int Id { get; set; } + [DataMember( Name = "id" )] + public int Id { get; set; } - [DataMember( Name = "character" )] - public string Character { get; set; } + [DataMember( Name = "character" )] + public string Character { get; set; } - [DataMember( Name = "credit_id" )] - public string CreditId { get; set; } + [DataMember( Name = "credit_id" )] + public string CreditId { get; set; } - [DataMember( Name = "order" )] - public int Order { get; set; } + [DataMember( Name = "order" )] + public int Order { get; set; } - [DataMember( Name = "adult" )] - public bool? Adult { get; set; } + [DataMember( Name = "adult" )] + public bool? Adult { get; set; } - [DataMember( Name = "gender" )] - public int? Gender { get; set; } + [DataMember( Name = "gender" )] + public int? Gender { get; set; } - [DataMember( Name = "known_for_department" )] - public string KnownForDepartment { get; set; } + [DataMember( Name = "known_for_department" )] + public string KnownForDepartment { get; set; } - [DataMember( Name = "name" )] - public string Name { get; set; } + [DataMember( Name = "name" )] + public string Name { get; set; } - [DataMember( Name = "original_name" )] - public string OriginalName { get; set; } + [DataMember( Name = "original_name" )] + public string OriginalName { get; set; } - [DataMember( Name = "popularity" )] - public float Popularity { get; set; } + [DataMember( Name = "popularity" )] + public float Popularity { get; set; } - [DataMember( Name = "profile_path" )] - public string ProfilePath { get; set; } + [DataMember( Name = "profile_path" )] + public string ProfilePath { get; set; } - public override string ToString() - => $"{Name} - {Character}"; - } + public override string ToString() + => $"{Name} - {Character}"; } diff --git a/DM.MovieApi/MovieDb/TV/IApiTVShowRequest.cs b/DM.MovieApi/MovieDb/TV/IApiTVShowRequest.cs index 8c6b417..a9d393d 100644 --- a/DM.MovieApi/MovieDb/TV/IApiTVShowRequest.cs +++ b/DM.MovieApi/MovieDb/TV/IApiTVShowRequest.cs @@ -1,55 +1,50 @@ -using System.Threading.Tasks; -using DM.MovieApi.ApiRequest; -using DM.MovieApi.ApiResponse; +namespace DM.MovieApi.MovieDb.TV; -namespace DM.MovieApi.MovieDb.TV +/// +/// Interface for retrieving information about TV shows. +/// +public interface IApiTVShowRequest : IApiRequest { /// - /// Interface for retrieving information about TV shows. + /// Gets all the information about a specific TV show. /// - public interface IApiTVShowRequest : IApiRequest - { - /// - /// Gets all the information about a specific TV show. - /// - /// The TV show Id which is typically found from a more generic TV show query. - /// Default is English. The ISO 639-1 language code to retrieve the result from. - Task> FindByIdAsync( int tvShowId, string language = "en" ); + /// The TV show Id which is typically found from a more generic TV show query. + /// Default is English. The ISO 639-1 language code to retrieve the result from. + Task> FindByIdAsync( int tvShowId, string language = "en" ); - /// - /// Searches for TV shows by title. - /// - /// The query to search for TV shows. - /// Default is page 1. The page number to retrieve; the will contain the current page returned and the total number of pages available. - /// Default is English. The ISO 639-1 language code to retrieve the result from. - Task> SearchByNameAsync( string query, int pageNumber = 1, string language = "en" ); + /// + /// Searches for TV shows by title. + /// + /// The query to search for TV shows. + /// Default is page 1. The page number to retrieve; the will contain the current page returned and the total number of pages available. + /// Default is English. The ISO 639-1 language code to retrieve the result from. + Task> SearchByNameAsync( string query, int pageNumber = 1, string language = "en" ); - /// - /// Gets the latest TV show added to TheMovieDb.org - /// - /// Default is English. The ISO 639-1 language code to retrieve the result from. - Task> GetLatestAsync( string language = "en" ); + /// + /// Gets the latest TV show added to TheMovieDb.org + /// + /// Default is English. The ISO 639-1 language code to retrieve the result from. + Task> GetLatestAsync( string language = "en" ); - /// - /// Gets the list of top rated TV shows which is refreshed daily. - /// - /// Default is page 1. The page number to retrieve; the will contain the current page returned and the total number of pages available. - /// Default is English. The ISO 639-1 language code to retrieve the result from. - Task> GetTopRatedAsync( int pageNumber = 1, string language = "en" ); + /// + /// Gets the list of top rated TV shows which is refreshed daily. + /// + /// Default is page 1. The page number to retrieve; the will contain the current page returned and the total number of pages available. + /// Default is English. The ISO 639-1 language code to retrieve the result from. + Task> GetTopRatedAsync( int pageNumber = 1, string language = "en" ); - /// - /// Gets the list of popular TV shows which is refreshed daily. - /// - /// Default is page 1. The page number to retrieve; the will contain the current page returned and the total number of pages available. - /// Default is English. The ISO 639-1 language code to retrieve the result from. - Task> GetPopularAsync( int pageNumber = 1, string language = "en" ); + /// + /// Gets the list of popular TV shows which is refreshed daily. + /// + /// Default is page 1. The page number to retrieve; the will contain the current page returned and the total number of pages available. + /// Default is English. The ISO 639-1 language code to retrieve the result from. + Task> GetPopularAsync( int pageNumber = 1, string language = "en" ); - /// - /// Get the TV season details by id. - /// - /// The TV show Id which is typically found from a more generic TV show query. - /// The Season Number is typically found from a more generic TV show query. - /// Default is English. The ISO 639-1 language code to retrieve the result from. - Task> GetTvShowSeasonInfoAsync( int tvShowId, int seasonNumber, string language = "en" ); - } + /// + /// Get the TV season details by id. + /// + /// The TV show Id which is typically found from a more generic TV show query. + /// The Season Number is typically found from a more generic TV show query. + /// Default is English. The ISO 639-1 language code to retrieve the result from. + Task> GetTvShowSeasonInfoAsync( int tvShowId, int seasonNumber, string language = "en" ); } diff --git a/DM.MovieApi/MovieDb/TV/Network.cs b/DM.MovieApi/MovieDb/TV/Network.cs index ec9a303..9cd8612 100644 --- a/DM.MovieApi/MovieDb/TV/Network.cs +++ b/DM.MovieApi/MovieDb/TV/Network.cs @@ -1,51 +1,47 @@ -using System.Collections.Generic; -using System.Runtime.Serialization; +namespace DM.MovieApi.MovieDb.TV; -namespace DM.MovieApi.MovieDb.TV +[DataContract] +public class Network : IEqualityComparer { - [DataContract] - public class Network : IEqualityComparer - { - [DataMember( Name = "id" )] - public int Id { get; set; } + [DataMember( Name = "id" )] + public int Id { get; set; } - [DataMember( Name = "name" )] - public string Name { get; set; } + [DataMember( Name = "name" )] + public string Name { get; set; } - public Network( int id, string name ) - { - Id = id; - Name = name; - } + public Network( int id, string name ) + { + Id = id; + Name = name; + } - public bool Equals( Network x, Network y ) - => x != null && y != null && x.Id == y.Id && x.Name == y.Name; + public bool Equals( Network x, Network y ) + => x != null && y != null && x.Id == y.Id && x.Name == y.Name; - public int GetHashCode( Network obj ) + public int GetHashCode( Network obj ) + { + unchecked // Overflow is fine, just wrap { - unchecked // Overflow is fine, just wrap - { - int hash = 17; - hash = hash * 23 + obj.Id.GetHashCode(); - hash = hash * 23 + obj.Name.GetHashCode(); - return hash; - } + int hash = 17; + hash = hash * 23 + obj.Id.GetHashCode(); + hash = hash * 23 + obj.Name.GetHashCode(); + return hash; } + } - public override bool Equals( object obj ) + public override bool Equals( object obj ) + { + if( obj is not Network network ) { - if( obj is not Network network ) - { - return false; - } - - return Equals( this, network ); + return false; } - public override int GetHashCode() - => GetHashCode( this ); - - public override string ToString() - => $"{Name} ({Id})"; + return Equals( this, network ); } + + public override int GetHashCode() + => GetHashCode( this ); + + public override string ToString() + => $"{Name} ({Id})"; } diff --git a/DM.MovieApi/MovieDb/TV/Season.cs b/DM.MovieApi/MovieDb/TV/Season.cs index aeb51fe..0d39cc6 100644 --- a/DM.MovieApi/MovieDb/TV/Season.cs +++ b/DM.MovieApi/MovieDb/TV/Season.cs @@ -1,36 +1,32 @@ -using System; -using System.Runtime.Serialization; +namespace DM.MovieApi.MovieDb.TV; -namespace DM.MovieApi.MovieDb.TV +[DataContract] +public class Season { - [DataContract] - public class Season - { - [DataMember( Name = "id" )] - public int Id { get; set; } - - [DataMember( Name = "air_date" )] - public DateTime AirDate { get; set; } + [DataMember( Name = "id" )] + public int Id { get; set; } - [DataMember( Name = "episode_count" )] - public int EpisodeCount { get; set; } + [DataMember( Name = "air_date" )] + public DateTime AirDate { get; set; } - [DataMember( Name = "poster_path" )] - public string PosterPath { get; set; } + [DataMember( Name = "episode_count" )] + public int EpisodeCount { get; set; } - [DataMember( Name = "season_number" )] - public int SeasonNumber { get; set; } + [DataMember( Name = "poster_path" )] + public string PosterPath { get; set; } - public Season( int id, DateTime airDate, int episodeCount, string posterPath, int seasonNumber ) - { - Id = id; - AirDate = airDate; - EpisodeCount = episodeCount; - PosterPath = posterPath; - SeasonNumber = seasonNumber; - } + [DataMember( Name = "season_number" )] + public int SeasonNumber { get; set; } - public override string ToString() - => $"({SeasonNumber} - {AirDate:yyyy-MM-dd})"; + public Season( int id, DateTime airDate, int episodeCount, string posterPath, int seasonNumber ) + { + Id = id; + AirDate = airDate; + EpisodeCount = episodeCount; + PosterPath = posterPath; + SeasonNumber = seasonNumber; } + + public override string ToString() + => $"({SeasonNumber} - {AirDate:yyyy-MM-dd})"; } diff --git a/DM.MovieApi/MovieDb/TV/SeasonInfo.cs b/DM.MovieApi/MovieDb/TV/SeasonInfo.cs index f8273c2..4cacd69 100644 --- a/DM.MovieApi/MovieDb/TV/SeasonInfo.cs +++ b/DM.MovieApi/MovieDb/TV/SeasonInfo.cs @@ -1,39 +1,34 @@ -using System; -using System.Collections.Generic; -using System.Runtime.Serialization; +namespace DM.MovieApi.MovieDb.TV; -namespace DM.MovieApi.MovieDb.TV +[DataContract] +public class SeasonInfo { - [DataContract] - public class SeasonInfo - { - [DataMember( Name = "id" )] - public int Id { get; set; } - - [DataMember( Name = "air_date" )] - public DateTime AirDate { get; set; } + [DataMember( Name = "id" )] + public int Id { get; set; } - [DataMember( Name = "overview" )] - public string Overview { get; set; } + [DataMember( Name = "air_date" )] + public DateTime AirDate { get; set; } - [DataMember( Name = "name" )] - public string Name { get; set; } + [DataMember( Name = "overview" )] + public string Overview { get; set; } - [DataMember( Name = "poster_path" )] - public string PosterPath { get; set; } + [DataMember( Name = "name" )] + public string Name { get; set; } - [DataMember( Name = "season_number" )] - public int SeasonNumber { get; set; } + [DataMember( Name = "poster_path" )] + public string PosterPath { get; set; } - [DataMember( Name = "episodes" )] - public IReadOnlyList Episodes { get; set; } + [DataMember( Name = "season_number" )] + public int SeasonNumber { get; set; } - public SeasonInfo() - { - Episodes = Array.Empty(); - } + [DataMember( Name = "episodes" )] + public IReadOnlyList Episodes { get; set; } - public override string ToString() - => $"{Name} - {AirDate:yyyy-MM-dd}"; + public SeasonInfo() + { + Episodes = Array.Empty(); } + + public override string ToString() + => $"{Name} - {AirDate:yyyy-MM-dd}"; } diff --git a/DM.MovieApi/MovieDb/TV/TVShow.cs b/DM.MovieApi/MovieDb/TV/TVShow.cs index 8db76c1..0c4b961 100644 --- a/DM.MovieApi/MovieDb/TV/TVShow.cs +++ b/DM.MovieApi/MovieDb/TV/TVShow.cs @@ -1,100 +1,95 @@ -using System; -using System.Collections.Generic; -using System.Runtime.Serialization; -using DM.MovieApi.MovieDb.Companies; -using DM.MovieApi.MovieDb.Genres; +using DM.MovieApi.MovieDb.Companies; using DM.MovieApi.MovieDb.Keywords; using Newtonsoft.Json; -namespace DM.MovieApi.MovieDb.TV -{ - [DataContract] - public class TVShow - { - [DataMember( Name = "id" )] - public int Id { get; set; } +namespace DM.MovieApi.MovieDb.TV; - [DataMember( Name = "backdrop_path" )] - public string BackdropPath { get; set; } +[DataContract] +public class TVShow +{ + [DataMember( Name = "id" )] + public int Id { get; set; } - [DataMember( Name = "created_by" )] - public IReadOnlyList CreatedBy { get; set; } + [DataMember( Name = "backdrop_path" )] + public string BackdropPath { get; set; } - [DataMember( Name = "episode_run_time" )] - public IReadOnlyList EpisodeRunTime { get; set; } + [DataMember( Name = "created_by" )] + public IReadOnlyList CreatedBy { get; set; } - [DataMember( Name = "first_air_date" )] - public DateTime FirstAirDate { get; set; } + [DataMember( Name = "episode_run_time" )] + public IReadOnlyList EpisodeRunTime { get; set; } - [DataMember( Name = "genres" )] - public IReadOnlyList Genres { get; set; } + [DataMember( Name = "first_air_date" )] + public DateTime FirstAirDate { get; set; } - [DataMember( Name = "homepage" )] - public string Homepage { get; set; } + [DataMember( Name = "genres" )] + public IReadOnlyList Genres { get; set; } - [DataMember( Name = "in_production" )] - public bool InProduction { get; set; } + [DataMember( Name = "homepage" )] + public string Homepage { get; set; } - [DataMember( Name = "languages" )] - public IReadOnlyList Languages { get; set; } + [DataMember( Name = "in_production" )] + public bool InProduction { get; set; } - [DataMember( Name = "last_air_date" )] - public DateTime LastAirDate { get; set; } + [DataMember( Name = "languages" )] + public IReadOnlyList Languages { get; set; } - [DataMember( Name = "name" )] - public string Name { get; set; } + [DataMember( Name = "last_air_date" )] + public DateTime LastAirDate { get; set; } - [DataMember( Name = "networks" )] - public IReadOnlyList Networks { get; set; } + [DataMember( Name = "name" )] + public string Name { get; set; } - [DataMember( Name = "number_of_episodes" )] - public int NumberOfEpisodes { get; set; } + [DataMember( Name = "networks" )] + public IReadOnlyList Networks { get; set; } - [DataMember( Name = "number_of_seasons" )] - public int NumberOfSeasons { get; set; } + [DataMember( Name = "number_of_episodes" )] + public int NumberOfEpisodes { get; set; } - [DataMember( Name = "origin_country" )] - public IReadOnlyList OriginCountry { get; set; } + [DataMember( Name = "number_of_seasons" )] + public int NumberOfSeasons { get; set; } - [DataMember( Name = "original_language" )] - public string OriginalLanguage { get; set; } + [DataMember( Name = "origin_country" )] + public IReadOnlyList OriginCountry { get; set; } - [DataMember( Name = "original_name" )] - public string OriginalName { get; set; } + [DataMember( Name = "original_language" )] + public string OriginalLanguage { get; set; } - [DataMember( Name = "overview" )] - public string Overview { get; set; } + [DataMember( Name = "original_name" )] + public string OriginalName { get; set; } - [DataMember( Name = "popularity" )] - public double Popularity { get; set; } + [DataMember( Name = "overview" )] + public string Overview { get; set; } - [DataMember( Name = "poster_path" )] - public string PosterPath { get; set; } + [DataMember( Name = "popularity" )] + public double Popularity { get; set; } - [DataMember( Name = "production_companies" )] - public IReadOnlyList ProductionCompanies { get; set; } + [DataMember( Name = "poster_path" )] + public string PosterPath { get; set; } - [DataMember( Name = "seasons" )] - public IReadOnlyList Seasons { get; set; } + [DataMember( Name = "production_companies" )] + public IReadOnlyList ProductionCompanies { get; set; } - [DataMember( Name = "keywords" )] - [JsonConverter( typeof( KeywordConverter ), "results" )] - public IReadOnlyList Keywords { get; set; } + [DataMember( Name = "seasons" )] + public IReadOnlyList Seasons { get; set; } - public TVShow() - { - CreatedBy = Array.Empty(); - EpisodeRunTime = Array.Empty(); - Genres = Array.Empty(); - Languages = Array.Empty(); - Networks = Array.Empty(); - OriginCountry = Array.Empty(); - ProductionCompanies = Array.Empty(); - Seasons = Array.Empty(); - Keywords = Array.Empty(); - } + [DataMember( Name = "keywords" )] + [JsonConverter( typeof( KeywordConverter ), "results" )] + public IReadOnlyList Keywords { get; set; } - public override string ToString() - => $"{Name} ({FirstAirDate:yyyy-MM-dd}) [{Id}]"; + public TVShow() + { + CreatedBy = Array.Empty(); + EpisodeRunTime = Array.Empty(); + Genres = Array.Empty(); + Languages = Array.Empty(); + Networks = Array.Empty(); + OriginCountry = Array.Empty(); + ProductionCompanies = Array.Empty(); + Seasons = Array.Empty(); + Keywords = Array.Empty(); } + + public override string ToString() + => $"{Name} ({FirstAirDate:yyyy-MM-dd}) [{Id}]"; } diff --git a/DM.MovieApi/MovieDb/TV/TVShowCreator.cs b/DM.MovieApi/MovieDb/TV/TVShowCreator.cs index 5625e1c..9738916 100644 --- a/DM.MovieApi/MovieDb/TV/TVShowCreator.cs +++ b/DM.MovieApi/MovieDb/TV/TVShowCreator.cs @@ -1,55 +1,51 @@ -using System.Collections.Generic; -using System.Runtime.Serialization; +namespace DM.MovieApi.MovieDb.TV; -namespace DM.MovieApi.MovieDb.TV +[DataContract] +public class TVShowCreator : IEqualityComparer { - [DataContract] - public class TVShowCreator : IEqualityComparer - { - [DataMember( Name = "id" )] - public int Id { get; set; } + [DataMember( Name = "id" )] + public int Id { get; set; } - [DataMember( Name = "name" )] - public string Name { get; set; } + [DataMember( Name = "name" )] + public string Name { get; set; } - [DataMember( Name = "profile_path" )] - public string ProfilePath { get; set; } + [DataMember( Name = "profile_path" )] + public string ProfilePath { get; set; } - public TVShowCreator( int id, string name, string profilePath ) - { - Id = id; - Name = name; - ProfilePath = profilePath; - } + public TVShowCreator( int id, string name, string profilePath ) + { + Id = id; + Name = name; + ProfilePath = profilePath; + } - public bool Equals( TVShowCreator x, TVShowCreator y ) - => x != null && y != null && x.Id == y.Id && x.Name == y.Name; + public bool Equals( TVShowCreator x, TVShowCreator y ) + => x != null && y != null && x.Id == y.Id && x.Name == y.Name; - public int GetHashCode( TVShowCreator obj ) + public int GetHashCode( TVShowCreator obj ) + { + unchecked // Overflow is fine, just wrap { - unchecked // Overflow is fine, just wrap - { - int hash = 17; - hash = hash * 23 + obj.Id.GetHashCode(); - hash = hash * 23 + obj.Name.GetHashCode(); - return hash; - } + int hash = 17; + hash = hash * 23 + obj.Id.GetHashCode(); + hash = hash * 23 + obj.Name.GetHashCode(); + return hash; } + } - public override bool Equals( object obj ) + public override bool Equals( object obj ) + { + if( obj is not TVShowCreator showCreator ) { - if( obj is not TVShowCreator showCreator ) - { - return false; - } - - return Equals( this, showCreator ); + return false; } - public override int GetHashCode() - => GetHashCode( this ); - - public override string ToString() - => $"{Name} ({Id})"; + return Equals( this, showCreator ); } + + public override int GetHashCode() + => GetHashCode( this ); + + public override string ToString() + => $"{Name} ({Id})"; } diff --git a/DM.MovieApi/MovieDb/TV/TVShowInfo.cs b/DM.MovieApi/MovieDb/TV/TVShowInfo.cs index 7b0abfa..67fa9c4 100644 --- a/DM.MovieApi/MovieDb/TV/TVShowInfo.cs +++ b/DM.MovieApi/MovieDb/TV/TVShowInfo.cs @@ -1,63 +1,58 @@ -using System; -using System.Collections.Generic; -using System.Runtime.Serialization; -using DM.MovieApi.MovieDb.Genres; -// ReSharper disable UnusedAutoPropertyAccessor.Local +// ReSharper disable UnusedAutoPropertyAccessor.Local -namespace DM.MovieApi.MovieDb.TV -{ - [DataContract] - public class TVShowInfo - { - [DataMember( Name = "id" )] - public int Id { get; private set; } +namespace DM.MovieApi.MovieDb.TV; - [DataMember( Name = "name" )] - public string Name { get; private set; } +[DataContract] +public class TVShowInfo +{ + [DataMember( Name = "id" )] + public int Id { get; private set; } - [DataMember( Name = "original_name" )] - public string OriginalName { get; private set; } + [DataMember( Name = "name" )] + public string Name { get; private set; } - [DataMember( Name = "poster_path" )] - public string PosterPath { get; set; } + [DataMember( Name = "original_name" )] + public string OriginalName { get; private set; } - [DataMember( Name = "backdrop_path" )] - public string BackdropPath { get; set; } + [DataMember( Name = "poster_path" )] + public string PosterPath { get; set; } - [DataMember( Name = "popularity" )] - public double Popularity { get; private set; } + [DataMember( Name = "backdrop_path" )] + public string BackdropPath { get; set; } - [DataMember( Name = "vote_average" )] - public double VoteAverage { get; private set; } + [DataMember( Name = "popularity" )] + public double Popularity { get; private set; } - [DataMember( Name = "vote_count" )] - public int VoteCount { get; private set; } + [DataMember( Name = "vote_average" )] + public double VoteAverage { get; private set; } - [DataMember( Name = "overview" )] - public string Overview { get; private set; } + [DataMember( Name = "vote_count" )] + public int VoteCount { get; private set; } - [DataMember( Name = "first_air_date" )] - public DateTime FirstAirDate { get; private set; } + [DataMember( Name = "overview" )] + public string Overview { get; private set; } - [DataMember( Name = "origin_country" )] - public IReadOnlyList OriginCountry { get; private set; } + [DataMember( Name = "first_air_date" )] + public DateTime FirstAirDate { get; private set; } - [DataMember( Name = "genre_ids" )] - internal IReadOnlyList GenreIds { get; set; } + [DataMember( Name = "origin_country" )] + public IReadOnlyList OriginCountry { get; private set; } - public IReadOnlyList Genres { get; internal set; } + [DataMember( Name = "genre_ids" )] + internal IReadOnlyList GenreIds { get; set; } - [DataMember( Name = "original_language" )] - public string OriginalLanguage { get; private set; } + public IReadOnlyList Genres { get; internal set; } - public TVShowInfo() - { - OriginCountry = Array.Empty(); - GenreIds = Array.Empty(); - Genres = Array.Empty(); - } + [DataMember( Name = "original_language" )] + public string OriginalLanguage { get; private set; } - public override string ToString() - => $"{Name} ({Id} - {FirstAirDate:yyyy-MM-dd})"; + public TVShowInfo() + { + OriginCountry = Array.Empty(); + GenreIds = Array.Empty(); + Genres = Array.Empty(); } + + public override string ToString() + => $"{Name} ({Id} - {FirstAirDate:yyyy-MM-dd})"; } diff --git a/DM.MovieApi/MovieDbFactory.cs b/DM.MovieApi/MovieDbFactory.cs index dfd2417..e8ded16 100644 --- a/DM.MovieApi/MovieDbFactory.cs +++ b/DM.MovieApi/MovieDbFactory.cs @@ -1,203 +1,196 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; +using System.Collections.Concurrent; using System.Reflection; using System.Runtime.CompilerServices; -using DM.MovieApi.ApiRequest; -using DM.MovieApi.MovieDb.Genres; -using DM.MovieApi.Shims; [assembly: InternalsVisibleTo( "DM.MovieApi.IntegrationTests" )] -namespace DM.MovieApi +namespace DM.MovieApi; + +/// +/// Note: one of the RegisterSettings must be called before the Factory can Create anything. +/// +public static class MovieDbFactory { + /// + public const string TheMovieDbApiUrl = "http://api.themoviedb.org/3/"; + /// - /// Note: one of the RegisterSettings must be called before the Factory can Create anything. + /// Determines if the underlying factory has been created. /// - public static class MovieDbFactory + public static bool IsFactoryComposed => Settings != null; + + internal static IApiSettings Settings { get; private set; } + + /// + /// Registers themoviedb.org settings for use with the internal DI container. + /// + /// + /// + /// + public static void RegisterSettings( string bearerToken ) { - /// - public const string TheMovieDbApiUrl = "http://api.themoviedb.org/3/"; - - /// - /// Determines if the underlying factory has been created. - /// - public static bool IsFactoryComposed => Settings != null; - - internal static IApiSettings Settings { get; private set; } - - /// - /// Registers themoviedb.org settings for use with the internal DI container. - /// - /// - /// - /// - public static void RegisterSettings( string bearerToken ) + ResetFactory(); + + if( bearerToken is null || bearerToken.Length <= 200 ) { - ResetFactory(); + // v3 access key was approx 33 chars; v4 bearer is approx 212 chars. + throw new ArgumentException( + $"Must provide a valid TheMovieDb.org Bearer token. Invalid: {bearerToken}. " + + "A valid token can be found in your account page, under the API section. " + + "You will see a new key listed under the header \"API Read Access Token\".", bearerToken ); + } - if( bearerToken is null || bearerToken.Length <= 200 ) - { - // v3 access key was approx 33 chars; v4 bearer is approx 212 chars. - throw new ArgumentException( - $"Must provide a valid TheMovieDb.org Bearer token. Invalid: {bearerToken}. " + - "A valid token can be found in your account page, under the API section. " + - "You will see a new key listed under the header \"API Read Access Token\".", bearerToken ); - } + Settings = new MovieDbSettings( TheMovieDbApiUrl, bearerToken ); + } - Settings = new MovieDbSettings( TheMovieDbApiUrl, bearerToken ); - } + /// + /// Creates the specific API requested. + /// + /// + public static Lazy Create() where T : IApiRequest + { + ContainerGuard(); - /// - /// Creates the specific API requested. - /// - /// - public static Lazy Create() where T : IApiRequest - { - ContainerGuard(); + var requestResolver = new ApiRequestResolver(); - var requestResolver = new ApiRequestResolver(); + return new Lazy( requestResolver.Get ); + } - return new Lazy( requestResolver.Get ); - } + /// + /// Creates a global instance exposing all API interfaces against themoviedb.org + /// that are currently available in this release. Each API is exposed via a Lazy property + /// ensuring no objects are created until they are needed. + /// + /// + public static IMovieDbApi GetAllApiRequests() + { + ContainerGuard(); - /// - /// Creates a global instance exposing all API interfaces against themoviedb.org - /// that are currently available in this release. Each API is exposed via a Lazy property - /// ensuring no objects are created until they are needed. - /// - /// - public static IMovieDbApi GetAllApiRequests() - { - ContainerGuard(); + // Note: the concrete implementation is currently excluded from the .csproj, but is still included in source control. - // Note: the concrete implementation is currently excluded from the .csproj, but is still included in source control. + string msg = $"{nameof( GetAllApiRequests )} has been temporarily disabled due to porting the code base to Asp.Net Core to provide support for portable library projects."; + throw new NotImplementedException( msg ); + } - string msg = $"{nameof( GetAllApiRequests )} has been temporarily disabled due to porting the code base to Asp.Net Core to provide support for portable library projects."; - throw new NotImplementedException( msg ); - } + /// + /// Clears all factory settings; forces the next call to be RegisterSettings. + /// before can be called. + /// + public static void ResetFactory() + { + Settings = null; + } - /// - /// Clears all factory settings; forces the next call to be RegisterSettings. - /// before can be called. - /// - public static void ResetFactory() + private static void ContainerGuard() + { + if( !IsFactoryComposed ) { - Settings = null; + throw new InvalidOperationException( $"{nameof( RegisterSettings )} must be called before the Factory can Create anything." ); } + } + + private class MovieDbSettings : IApiSettings + { + public string ApiUrl { get; } + public string BearerToken { get; } - private static void ContainerGuard() + public MovieDbSettings( string apiUrl, string bearerToken ) { - if( !IsFactoryComposed ) - { - throw new InvalidOperationException( $"{nameof( RegisterSettings )} must be called before the Factory can Create anything." ); - } + ApiUrl = apiUrl; + BearerToken = bearerToken; } + } - private class MovieDbSettings : IApiSettings - { - public string ApiUrl { get; } - public string BearerToken { get; } + private class ApiRequestResolver + { + private static readonly IReadOnlyDictionary> SupportedDependencyTypeMap; + private static readonly ConcurrentDictionary TypeCtorMap; - public MovieDbSettings( string apiUrl, string bearerToken ) + static ApiRequestResolver() + { + SupportedDependencyTypeMap = new Dictionary> { - ApiUrl = apiUrl; - BearerToken = bearerToken; - } + {typeof(IApiSettings), () => Settings}, + {typeof(IApiGenreRequest), () => new ApiGenreRequest( Settings )} + }; + + TypeCtorMap = new ConcurrentDictionary(); } - private class ApiRequestResolver + public T Get() where T : IApiRequest { - private static readonly IReadOnlyDictionary> SupportedDependencyTypeMap; - private static readonly ConcurrentDictionary TypeCtorMap; + ConstructorInfo ctor = TypeCtorMap.GetOrAdd( typeof( T ), GetConstructor ); - static ApiRequestResolver() - { - SupportedDependencyTypeMap = new Dictionary> - { - {typeof(IApiSettings), () => Settings}, - {typeof(IApiGenreRequest), () => new ApiGenreRequest( Settings )} - }; + ParameterInfo[] param = ctor.GetParameters(); - TypeCtorMap = new ConcurrentDictionary(); + if( param.Length == 0 ) + { + return (T)ctor.Invoke( null ); } - public T Get() where T : IApiRequest + var paramObjects = new List( param.Length ); + foreach( ParameterInfo p in param ) { - ConstructorInfo ctor = TypeCtorMap.GetOrAdd( typeof( T ), GetConstructor ); - - ParameterInfo[] param = ctor.GetParameters(); - - if( param.Length == 0 ) + if( SupportedDependencyTypeMap.ContainsKey( p.ParameterType ) == false ) { - return ( T )ctor.Invoke( null ); + throw new InvalidOperationException( $"{p.ParameterType.FullName} is not a supported dependency type for {typeof( T ).FullName}." ); } - var paramObjects = new List( param.Length ); - foreach( ParameterInfo p in param ) - { - if( SupportedDependencyTypeMap.ContainsKey( p.ParameterType ) == false ) - { - throw new InvalidOperationException( $"{p.ParameterType.FullName} is not a supported dependency type for {typeof( T ).FullName}." ); - } + Func typeResolver = SupportedDependencyTypeMap[p.ParameterType]; - Func typeResolver = SupportedDependencyTypeMap[p.ParameterType]; + paramObjects.Add( typeResolver() ); + } - paramObjects.Add( typeResolver() ); - } + return (T)ctor.Invoke( paramObjects.ToArray() ); + } - return ( T )ctor.Invoke( paramObjects.ToArray() ); + private ConstructorInfo GetConstructor( Type t ) + { + ConstructorInfo[] ctors = GetAvailableConstructors( t.GetTypeInfo() ); + + if( ctors.Length == 0 ) + { + throw new InvalidOperationException( $"No public constructors found for {t.FullName}." ); } - private ConstructorInfo GetConstructor( Type t ) + if( ctors.Length == 1 ) { - ConstructorInfo[] ctors = GetAvailableConstructors( t.GetTypeInfo() ); + return ctors[0]; + } - if( ctors.Length == 0 ) - { - throw new InvalidOperationException( $"No public constructors found for {t.FullName}." ); - } + var importingCtors = ctors + .Where( x => x.IsDefined( typeof( ImportingConstructorAttribute ) ) ) + .ToArray(); - if( ctors.Length == 1 ) - { - return ctors[0]; - } + if( importingCtors.Length != 1 ) + { + throw new InvalidOperationException( "Multiple public constructors found. " + + $"One must be decorated with the {nameof( ImportingConstructorAttribute )}." ); + } - var importingCtors = ctors - .Where( x => x.IsDefined( typeof( ImportingConstructorAttribute ) ) ) - .ToArray(); + return importingCtors[0]; + } - if( importingCtors.Length != 1 ) - { - throw new InvalidOperationException( "Multiple public constructors found. " + - $"One must be decorated with the {nameof( ImportingConstructorAttribute )}." ); - } + private ConstructorInfo[] GetAvailableConstructors( TypeInfo typeInfo ) + { + TypeInfo[] implementingTypes = typeInfo.Assembly.DefinedTypes + .Where( x => x.IsAbstract == false ) + .Where( x => x.IsInterface == false ) + .Where( typeInfo.IsAssignableFrom ) + .ToArray(); - return importingCtors[0]; + if( implementingTypes.Length == 0 ) + { + throw new NotSupportedException( $"{typeInfo.Name} must have a concrete implementation." ); } - private ConstructorInfo[] GetAvailableConstructors( TypeInfo typeInfo ) + if( implementingTypes.Length != 1 ) { - TypeInfo[] implementingTypes = typeInfo.Assembly.DefinedTypes - .Where( x => x.IsAbstract == false ) - .Where( x => x.IsInterface == false ) - .Where( typeInfo.IsAssignableFrom ) - .ToArray(); - - if( implementingTypes.Length == 0 ) - { - throw new NotSupportedException( $"{typeInfo.Name} must have a concrete implementation." ); - } - - if( implementingTypes.Length != 1 ) - { - throw new NotSupportedException( $"Requested type: {typeInfo.Name}. " + - "Multiple implementations per request interface is not supported." ); - } - - return implementingTypes[0].DeclaredConstructors.ToArray(); + throw new NotSupportedException( $"Requested type: {typeInfo.Name}. " + + "Multiple implementations per request interface is not supported." ); } + + return implementingTypes[0].DeclaredConstructors.ToArray(); } } } diff --git a/DM.MovieApi/Shims/CollectionExtensions.cs b/DM.MovieApi/Shims/CollectionExtensions.cs index 31bedf0..31cf21c 100644 --- a/DM.MovieApi/Shims/CollectionExtensions.cs +++ b/DM.MovieApi/Shims/CollectionExtensions.cs @@ -1,13 +1,11 @@ -using System.Collections.Generic; -using System.Collections.ObjectModel; +using System.Collections.ObjectModel; -namespace DM.MovieApi.Shims +namespace DM.MovieApi.Shims; + +public static class CollectionExtensions { - public static class CollectionExtensions + public static IReadOnlyList AsReadOnly( this List list ) { - public static IReadOnlyList AsReadOnly( this List list ) - { - return new ReadOnlyCollection( list ); - } + return new ReadOnlyCollection( list ); } } diff --git a/DM.MovieApi/Shims/EnumExtensions.cs b/DM.MovieApi/Shims/EnumExtensions.cs new file mode 100644 index 0000000..3803d45 --- /dev/null +++ b/DM.MovieApi/Shims/EnumExtensions.cs @@ -0,0 +1,17 @@ +using System.ComponentModel; +using System.Reflection; + +namespace DM.MovieApi.Shims; + +internal static class EnumExtensions +{ + public static string GetDescription( this Enum e ) + { + DescriptionAttribute attr = e.GetType() + .GetMember( e.ToString() ) + .First() + .GetCustomAttribute(); + + return attr?.Description ?? e.ToString(); + } +} diff --git a/DM.MovieApi/Shims/ImportingConstructorAttribute.cs b/DM.MovieApi/Shims/ImportingConstructorAttribute.cs index c288c0f..20d09ae 100644 --- a/DM.MovieApi/Shims/ImportingConstructorAttribute.cs +++ b/DM.MovieApi/Shims/ImportingConstructorAttribute.cs @@ -1,8 +1,5 @@ -using System; +namespace DM.MovieApi.Shims; -namespace DM.MovieApi.Shims -{ - [AttributeUsage( AttributeTargets.Constructor )] - internal sealed class ImportingConstructorAttribute : Attribute - { } -} +[AttributeUsage( AttributeTargets.Constructor )] +internal sealed class ImportingConstructorAttribute : Attribute +{ }