Reorganizing the structure of the project, adding unit testing, adding PokemonService and ShakespeareService.

This commit is contained in:
2020-11-22 18:38:59 +01:00
parent 6c5d77d696
commit d47641ccb4
26 changed files with 353 additions and 86 deletions

View File

@ -0,0 +1,25 @@
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md

View File

@ -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<IActionResult> 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));
}
}
}
}

22
Pokespearean/Dockerfile Normal file
View File

@ -0,0 +1,22 @@
#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
WORKDIR /app
EXPOSE 80
FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim AS build
WORKDIR /src
COPY ["Pokespearean.csproj", ""]
COPY ["../Pokespearean.Models/Pokespearean.Models.csproj", "../Pokespearean.Models/"]
RUN dotnet restore "./Pokespearean.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "Pokespearean.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "Pokespearean.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Pokespearean.dll"]

19
Pokespearean/LICENSE Normal file
View File

@ -0,0 +1,19 @@
MIT License Copyright (c) <year> <copyright holders>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -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; }
}
}

View File

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<DockerfileContext>.</DockerfileContext>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Flurl.Http" Version="3.0.0" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.10.9" />
<PackageReference Include="PokeApiNet" Version="3.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Pokespearean.Models\Pokespearean.Models.csproj" />
</ItemGroup>
</Project>

26
Pokespearean/Program.cs Normal file
View File

@ -0,0 +1,26 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Pokespearean
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}

View File

@ -0,0 +1,36 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:5000",
"sslPort": 0
}
},
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Pokespearean": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "weatherforecast",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"dotnetRunMessages": "true",
"applicationUrl": "http://localhost:5000"
},
"Docker": {
"commandName": "Docker",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}",
"publishAllPorts": true
}
}
}

3
Pokespearean/README.md Normal file
View File

@ -0,0 +1,3 @@
# Pokespearean
Pokemon description API as if it was told by Shakespeare

View File

@ -0,0 +1,10 @@
using Pokespearean.Models.Generic;
using System.Threading.Tasks;
namespace Pokespearean.Services
{
public interface IPokemonService
{
Task<WebResult> GetPokemonDescription(string pokemonName);
}
}

View File

@ -0,0 +1,10 @@
using Pokespearean.Models.Generic;
using System.Threading.Tasks;
namespace Pokespearean.Services
{
public interface IShakespeareService
{
Task<WebResult> ToShakespearean(string text);
}
}

View File

@ -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<WebResult> GetPokemonDescription(string pokemonName)
{
var result = new WebResult();
try
{
var pokemonSpecies = await pokeApiClient.GetResourceAsync<PokemonSpecies>(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);
}
}
}
}

View File

@ -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<Settings> settingsOption)
{
shakespeareApi = settingsOption.Value.ShakespeareApi;
}
public async Task<WebResult> ToShakespearean(string text)
{
var result = new WebResult();
try
{
var response = await shakespeareApi
.AppendPathSegment("translate")
.AppendPathSegment("shakespeare.json")
.SetQueryParam(nameof(text), text, isEncoded: true)
.GetJsonAsync<ShakespearResponse>();
result.Data = response.Contents.Translated;
return result;
}
catch (System.Exception ex)
{
Console.WriteLine(ex.Message);
return result.Invalidate(ex.Message, ex);
}
}
}
}

47
Pokespearean/Startup.cs Normal file
View File

@ -0,0 +1,47 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Pokespearean.Models;
using Pokespearean.Services;
namespace Pokespearean
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IPokemonService, PokemonService>();
services.AddScoped<IShakespeareService, ShakespeareService>();
services.Configure<Settings>(Configuration.GetSection(nameof(Settings)));
services.AddControllers().AddJsonOptions(options =>
{
options.JsonSerializerOptions.IgnoreReadOnlyFields = true;
options.JsonSerializerOptions.IgnoreReadOnlyProperties = true;
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}

View File

@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}

View File

@ -0,0 +1,13 @@
{
"Settings": {
"ShakespeareApi": "https://api.funtranslations.com"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}