diff --git a/Controllers/PokemonController.cs b/Controllers/PokemonController.cs deleted file mode 100644 index 6cacdbd..0000000 --- a/Controllers/PokemonController.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using PokeApiNet; -using Pokespearean.Models.Generic; -using Pokespearean.Models.Pokemon; -using System; -using System.Linq; -using System.Threading.Tasks; - -namespace Pokespearean.Controllers -{ - [ApiController] - [Route("[controller]")] - public class PokemonController : ControllerBase - { - private readonly ILogger _logger; - - public PokemonController(ILogger logger) - { - _logger = logger; - } - - [Route("{pokemon}")] - public async Task Get([FromRoute] string pokemon = "ho-oh") - { - var result = new WebResult(); - try - { - var client = new PokeApiClient(); - - var pokedex = await client.GetResourceAsync(pokemon); - - return Ok(new PokeResult - { - Name = pokedex.Name, - Description = pokedex.Descriptions.FirstOrDefault(d => d.Language.Name == "en").Description - }); - } - catch (Exception ex) - { - return BadRequest(result.Invalidate(ex.Message, ex)); - } - } - } -} diff --git a/Pokespearean.Models/Generic/WebResult.cs b/Pokespearean.Models/Generic/WebResult.cs new file mode 100644 index 0000000..7295622 --- /dev/null +++ b/Pokespearean.Models/Generic/WebResult.cs @@ -0,0 +1,25 @@ +using System; +using System.Text.Json.Serialization; + +namespace Pokespearean.Models.Generic +{ + public class WebResult + { + [JsonIgnore] + public bool IsValid { get; set; } = true; + [JsonIgnore] + public Exception Exception { get; set; } + [JsonIgnore] + public object Data { get; set; } + public string ErrorMessage { get; set; } + + public WebResult Invalidate(string errorMessage, Exception exception = default) + { + IsValid = false; + ErrorMessage = errorMessage; + Exception = exception; + + return this; + } + } +} diff --git a/Pokespearean.Models/Pokemon/PokeResult.cs b/Pokespearean.Models/Pokemon/PokeResult.cs new file mode 100644 index 0000000..33a6db6 --- /dev/null +++ b/Pokespearean.Models/Pokemon/PokeResult.cs @@ -0,0 +1,8 @@ +namespace Pokespearean.Models.Pokemon +{ + public class PokeResult + { + public string Name { get; set; } + public string Description { get; set; } + } +} diff --git a/Pokespearean.Models/Pokespearean.Models.csproj b/Pokespearean.Models/Pokespearean.Models.csproj new file mode 100644 index 0000000..3249c10 --- /dev/null +++ b/Pokespearean.Models/Pokespearean.Models.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/Pokespearean.Models/Shakespeare/ShakespearResponse.cs b/Pokespearean.Models/Shakespeare/ShakespearResponse.cs new file mode 100644 index 0000000..5c11781 --- /dev/null +++ b/Pokespearean.Models/Shakespeare/ShakespearResponse.cs @@ -0,0 +1,18 @@ +namespace Pokespearean.Models.Shakespeare +{ + public class ShakespearResponse + { + public ShakespearResponseSuccess Success { get; set; } + public ShakespearResponseContents Contents { get; set; } + } + public class ShakespearResponseSuccess + { + public int Total { get; set; } + } + public class ShakespearResponseContents + { + public string Translated { get; set; } + public string Text { get; set; } + public string Translation { get; set; } + } +} diff --git a/Pokespearean.Tests/ControllerTests/PokemonControllerTests.cs b/Pokespearean.Tests/ControllerTests/PokemonControllerTests.cs new file mode 100644 index 0000000..7d422ec --- /dev/null +++ b/Pokespearean.Tests/ControllerTests/PokemonControllerTests.cs @@ -0,0 +1,52 @@ +using Microsoft.AspNetCore.Mvc; +using Pokespearean.Controllers; +using Pokespearean.Models.Generic; +using Pokespearean.Models.Pokemon; +using Pokespearean.Services; +using System.Threading.Tasks; +using Xunit; + +namespace Pokespearean.Tests +{ + public class PokemonControllerTests + { + private readonly IPokemonService PokemonService; + private readonly IShakespeareService ShakespeareService; + + public PokemonControllerTests(IPokemonService pokemonService, IShakespeareService shakespeareService) + { + PokemonService = pokemonService; + ShakespeareService = shakespeareService; + } + + [Theory] + [InlineData("charizard")] + public async Task GetTest(string pokemonName) + { + var controller = new PokemonController(PokemonService, ShakespeareService); + Assert.NotNull(controller); + var result = await controller.Get(pokemonName); + Assert.NotNull(result); + Assert.IsType(result); + var okResult = result as OkObjectResult; + Assert.IsType(okResult.Value); + var pokemon = okResult.Value as PokeResult; + Assert.Equal("charizard", pokemon.Name); + Assert.Equal("Charizard flies 'round the sky in search of powerful opponents. 't breathes fire of such most wondrous heat yond 't melts aught. However, 't nev'r turns its fiery breath on any opponent weaker than itself.", + pokemon.Description); + } + + [Theory] + [InlineData("amIaPokemon?")] + public async Task GetWithWrongPokemonTest(string pokemonName) + { + var controller = new PokemonController(PokemonService, ShakespeareService); + Assert.NotNull(controller); + var result = await controller.Get(pokemonName); + Assert.NotNull(result); + Assert.IsType(result); + var badRequestResult = result as BadRequestObjectResult; + Assert.IsType(badRequestResult.Value); + } + } +} diff --git a/Pokespearean.Tests/Pokespearean.Tests.csproj b/Pokespearean.Tests/Pokespearean.Tests.csproj new file mode 100644 index 0000000..4fb0380 --- /dev/null +++ b/Pokespearean.Tests/Pokespearean.Tests.csproj @@ -0,0 +1,29 @@ + + + + net5.0 + + false + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/Pokespearean.Tests/Startup.cs b/Pokespearean.Tests/Startup.cs new file mode 100644 index 0000000..6088de5 --- /dev/null +++ b/Pokespearean.Tests/Startup.cs @@ -0,0 +1,16 @@ +using Microsoft.Extensions.DependencyInjection; +using Pokespearean.Models; +using Pokespearean.Services; + +namespace Pokespearean.Tests +{ + public class Startup + { + public void ConfigureServices(IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + services.Configure(s => s.ShakespeareApi = "https://api.funtranslations.com"); + } + } +} diff --git a/Pokespearean.sln b/Pokespearean.sln index bc09bab..6a92d62 100644 --- a/Pokespearean.sln +++ b/Pokespearean.sln @@ -3,9 +3,11 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30711.63 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pokespearean", "Pokespearean.csproj", "{B9920ADA-7244-4E6A-8935-4EE1D66273D7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pokespearean", "Pokespearean\Pokespearean.csproj", "{9EE4EE5B-670B-44C4-B327-FED8A35AABA8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pokespearean.Models", "..\Pokespearean.Models\Pokespearean.Models.csproj", "{B62A4320-ED4F-4B29-9CE7-56EC1EBA4A26}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pokespearean.Models", "Pokespearean.Models\Pokespearean.Models.csproj", "{AA12865F-0EC1-4057-AC55-A1CC9B0A66AF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pokespearean.Tests", "Pokespearean.Tests\Pokespearean.Tests.csproj", "{2B740E78-E3E7-4285-BE87-27B188BE0B01}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +15,18 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B9920ADA-7244-4E6A-8935-4EE1D66273D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B9920ADA-7244-4E6A-8935-4EE1D66273D7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B9920ADA-7244-4E6A-8935-4EE1D66273D7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B9920ADA-7244-4E6A-8935-4EE1D66273D7}.Release|Any CPU.Build.0 = Release|Any CPU - {B62A4320-ED4F-4B29-9CE7-56EC1EBA4A26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B62A4320-ED4F-4B29-9CE7-56EC1EBA4A26}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B62A4320-ED4F-4B29-9CE7-56EC1EBA4A26}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B62A4320-ED4F-4B29-9CE7-56EC1EBA4A26}.Release|Any CPU.Build.0 = Release|Any CPU + {9EE4EE5B-670B-44C4-B327-FED8A35AABA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9EE4EE5B-670B-44C4-B327-FED8A35AABA8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9EE4EE5B-670B-44C4-B327-FED8A35AABA8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9EE4EE5B-670B-44C4-B327-FED8A35AABA8}.Release|Any CPU.Build.0 = Release|Any CPU + {AA12865F-0EC1-4057-AC55-A1CC9B0A66AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AA12865F-0EC1-4057-AC55-A1CC9B0A66AF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AA12865F-0EC1-4057-AC55-A1CC9B0A66AF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AA12865F-0EC1-4057-AC55-A1CC9B0A66AF}.Release|Any CPU.Build.0 = Release|Any CPU + {2B740E78-E3E7-4285-BE87-27B188BE0B01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2B740E78-E3E7-4285-BE87-27B188BE0B01}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2B740E78-E3E7-4285-BE87-27B188BE0B01}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2B740E78-E3E7-4285-BE87-27B188BE0B01}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/.dockerignore b/Pokespearean/.dockerignore similarity index 100% rename from .dockerignore rename to Pokespearean/.dockerignore diff --git a/Pokespearean/Controllers/PokemonController.cs b/Pokespearean/Controllers/PokemonController.cs new file mode 100644 index 0000000..33885b0 --- /dev/null +++ b/Pokespearean/Controllers/PokemonController.cs @@ -0,0 +1,57 @@ +using Microsoft.AspNetCore.Mvc; +using Pokespearean.Models.Generic; +using Pokespearean.Models.Pokemon; +using Pokespearean.Services; +using System; +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; + +namespace Pokespearean.Controllers +{ + [ApiController] + [Route("[controller]")] + public class PokemonController : ControllerBase + { + private readonly IPokemonService PokemonService; + private readonly IShakespeareService ShakespeareService; + + public PokemonController(IPokemonService pokemonService, + IShakespeareService shakespeareService) + { + PokemonService = pokemonService; + ShakespeareService = shakespeareService; + } + + [Route("{pokemonName}")] + public async Task Get([FromRoute, Required] string pokemonName) + { + var result = new WebResult(); + try + { + result = await PokemonService.GetPokemonDescription(pokemonName); + if (!result.IsValid) + return BadRequest(result); + + var pokemonDescription = result.Data as string; + + result = await ShakespeareService.ToShakespearean(pokemonDescription); + if (!result.IsValid) + return BadRequest(result); + + var shakespeareTranslation = result.Data as string; + + var pokeResult = new PokeResult + { + Name = pokemonName, + Description = shakespeareTranslation + }; + + return Ok(pokeResult); + } + catch (Exception ex) + { + return BadRequest(result.Invalidate(ex.Message, ex)); + } + } + } +} diff --git a/Dockerfile b/Pokespearean/Dockerfile similarity index 93% rename from Dockerfile rename to Pokespearean/Dockerfile index c3073b1..01419f7 100644 --- a/Dockerfile +++ b/Pokespearean/Dockerfile @@ -1,7 +1,6 @@ #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. FROM mcr.microsoft.com/dotnet/aspnet:5.0-buster-slim AS base -USER 1001 WORKDIR /app EXPOSE 80 diff --git a/LICENSE b/Pokespearean/LICENSE similarity index 100% rename from LICENSE rename to Pokespearean/LICENSE diff --git a/Pokespearean/Models/Settings.cs b/Pokespearean/Models/Settings.cs new file mode 100644 index 0000000..f460032 --- /dev/null +++ b/Pokespearean/Models/Settings.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Pokespearean.Models +{ + public class Settings + { + public string ShakespeareApi { get; set; } + } +} diff --git a/Pokespearean.csproj b/Pokespearean/Pokespearean.csproj similarity index 100% rename from Pokespearean.csproj rename to Pokespearean/Pokespearean.csproj diff --git a/Program.cs b/Pokespearean/Program.cs similarity index 100% rename from Program.cs rename to Pokespearean/Program.cs diff --git a/Properties/launchSettings.json b/Pokespearean/Properties/launchSettings.json similarity index 100% rename from Properties/launchSettings.json rename to Pokespearean/Properties/launchSettings.json diff --git a/README.md b/Pokespearean/README.md similarity index 100% rename from README.md rename to Pokespearean/README.md diff --git a/Pokespearean/Services/IPokemonService.cs b/Pokespearean/Services/IPokemonService.cs new file mode 100644 index 0000000..ed25eee --- /dev/null +++ b/Pokespearean/Services/IPokemonService.cs @@ -0,0 +1,10 @@ +using Pokespearean.Models.Generic; +using System.Threading.Tasks; + +namespace Pokespearean.Services +{ + public interface IPokemonService + { + Task GetPokemonDescription(string pokemonName); + } +} diff --git a/Pokespearean/Services/IShakespeareService.cs b/Pokespearean/Services/IShakespeareService.cs new file mode 100644 index 0000000..8e79de6 --- /dev/null +++ b/Pokespearean/Services/IShakespeareService.cs @@ -0,0 +1,10 @@ +using Pokespearean.Models.Generic; +using System.Threading.Tasks; + +namespace Pokespearean.Services +{ + public interface IShakespeareService + { + Task ToShakespearean(string text); + } +} diff --git a/Pokespearean/Services/PokemonService.cs b/Pokespearean/Services/PokemonService.cs new file mode 100644 index 0000000..3e95d0f --- /dev/null +++ b/Pokespearean/Services/PokemonService.cs @@ -0,0 +1,38 @@ +using PokeApiNet; +using Pokespearean.Models.Generic; +using System; +using System.Threading.Tasks; +using System.Linq; + +namespace Pokespearean.Services +{ + public class PokemonService : IPokemonService + { + private readonly PokeApiClient pokeApiClient; + + public PokemonService() + { + pokeApiClient = new PokeApiClient(); + } + + public async Task GetPokemonDescription(string pokemonName) + { + var result = new WebResult(); + try + { + var pokemonSpecies = await pokeApiClient.GetResourceAsync(pokemonName); + var hasRubyVersion = pokemonSpecies.FlavorTextEntries.Where(fte => fte.Language.Name == "en" && fte.Version.Name == "ruby").Any(); + result.Data = pokemonSpecies.FlavorTextEntries + .FirstOrDefault(fte => (hasRubyVersion ? fte.Version.Name == "ruby" : true) && fte.Language.Name == "en")? + .FlavorText?.Replace("\n", " ")?.Replace("\f", " "); + + return result; + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + return result.Invalidate(ex.Message, ex); + } + } + } +} \ No newline at end of file diff --git a/Pokespearean/Services/ShakespeareService.cs b/Pokespearean/Services/ShakespeareService.cs new file mode 100644 index 0000000..f792394 --- /dev/null +++ b/Pokespearean/Services/ShakespeareService.cs @@ -0,0 +1,43 @@ +using Pokespearean.Models.Generic; +using System; +using System.Threading.Tasks; +using Pokespearean.Models; +using Pokespearean.Models.Shakespeare; +using Flurl; +using Flurl.Http; +using Microsoft.Extensions.Options; + +namespace Pokespearean.Services +{ + public class ShakespeareService : IShakespeareService + { + private string shakespeareApi; + + public ShakespeareService(IOptions settingsOption) + { + shakespeareApi = settingsOption.Value.ShakespeareApi; + } + + public async Task ToShakespearean(string text) + { + var result = new WebResult(); + try + { + var response = await shakespeareApi + .AppendPathSegment("translate") + .AppendPathSegment("shakespeare.json") + .SetQueryParam(nameof(text), text, isEncoded: true) + .GetJsonAsync(); + + result.Data = response.Contents.Translated; + + return result; + } + catch (System.Exception ex) + { + Console.WriteLine(ex.Message); + return result.Invalidate(ex.Message, ex); + } + } + } +} \ No newline at end of file diff --git a/Startup.cs b/Pokespearean/Startup.cs similarity index 59% rename from Startup.cs rename to Pokespearean/Startup.cs index b693671..497f755 100644 --- a/Startup.cs +++ b/Pokespearean/Startup.cs @@ -1,15 +1,10 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text.Json; -using System.Threading.Tasks; +using Pokespearean.Models; +using Pokespearean.Services; namespace Pokespearean { @@ -22,21 +17,19 @@ namespace Pokespearean public IConfiguration Configuration { get; } - // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { + services.AddScoped(); + services.AddScoped(); + services.Configure(Configuration.GetSection(nameof(Settings))); services.AddControllers().AddJsonOptions(options => { - //options.JsonSerializerOptions.MaxDepth = 0; - //options.JsonSerializerOptions.PropertyNameCaseInsensitive = true; - //options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; options.JsonSerializerOptions.IgnoreReadOnlyFields = true; options.JsonSerializerOptions.IgnoreReadOnlyProperties = true; }); } - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) @@ -45,9 +38,6 @@ namespace Pokespearean } app.UseRouting(); - - app.UseAuthorization(); - app.UseEndpoints(endpoints => { endpoints.MapControllers(); diff --git a/appsettings.Development.json b/Pokespearean/appsettings.Development.json similarity index 100% rename from appsettings.Development.json rename to Pokespearean/appsettings.Development.json diff --git a/appsettings.json b/Pokespearean/appsettings.json similarity index 70% rename from appsettings.json rename to Pokespearean/appsettings.json index d9d9a9b..7d13d0c 100644 --- a/appsettings.json +++ b/Pokespearean/appsettings.json @@ -1,4 +1,7 @@ { + "Settings": { + "ShakespeareApi": "https://api.funtranslations.com" + }, "Logging": { "LogLevel": { "Default": "Information", diff --git a/WeatherForecast.cs b/WeatherForecast.cs deleted file mode 100644 index f673bb6..0000000 --- a/WeatherForecast.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace Pokespearean -{ - public class WeatherForecast - { - public DateTime Date { get; set; } - - public int TemperatureC { get; set; } - - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); - - public string Summary { get; set; } - } -}