Commit cad047da authored by Steve Smith's avatar Steve Smith

Fleshing out todoitem api and tests

Added ValidateModelAttribute
parent f6250567
...@@ -4,11 +4,14 @@ using System.Linq; ...@@ -4,11 +4,14 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using CleanArchitecture.Core.Entities; using CleanArchitecture.Core.Entities;
using CleanArchitecture.Core.Interfaces; using CleanArchitecture.Core.Interfaces;
using CleanArchitecture.Web.ApiModels;
using CleanArchitecture.Web.Filters;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace CleanArchitecture.Web.Api namespace CleanArchitecture.Web.Api
{ {
[Route("api/[controller]")] [Route("api/[controller]")]
[ValidateModel]
public class ToDoItemsController : Controller public class ToDoItemsController : Controller
{ {
private readonly IRepository<ToDoItem> _todoRepository; private readonly IRepository<ToDoItem> _todoRepository;
...@@ -22,7 +25,8 @@ namespace CleanArchitecture.Web.Api ...@@ -22,7 +25,8 @@ namespace CleanArchitecture.Web.Api
[HttpGet] [HttpGet]
public IActionResult List() public IActionResult List()
{ {
var items = _todoRepository.List(); var items = _todoRepository.List()
.Select(item => ToDoItemDTO.FromToDoItem(item));
return Ok(items); return Ok(items);
} }
...@@ -30,8 +34,20 @@ namespace CleanArchitecture.Web.Api ...@@ -30,8 +34,20 @@ namespace CleanArchitecture.Web.Api
[HttpGet("{id:int}")] [HttpGet("{id:int}")]
public IActionResult GetById(int id) public IActionResult GetById(int id)
{ {
var items = _todoRepository.GetById(id); var item = ToDoItemDTO.FromToDoItem(_todoRepository.GetById(id));
return Ok(items); return Ok(item);
}
// POST: api/ToDoItems
public async Task<IActionResult> Post([FromBody] ToDoItemDTO item)
{
var todoItem = new ToDoItem()
{
Title = item.Title,
Description = item.Description
};
_todoRepository.Add(todoItem);
return Ok(ToDoItemDTO.FromToDoItem(todoItem));
} }
} }
} }
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using CleanArchitecture.Core.Entities;
namespace CleanArchitecture.Web.ApiModels
{
// Note: doesn't expose events or behavior
public class ToDoItemDTO
{
public int Id { get; set; }
[Required]
public string Title { get; set; }
public string Description { get; set; }
public bool IsDone { get; private set; }
public static ToDoItemDTO FromToDoItem(ToDoItem item)
{
return new ToDoItemDTO()
{
Id = item.Id,
Title = item.Title,
Description = item.Description,
IsDone = item.IsDone
};
}
}
}
...@@ -25,23 +25,29 @@ namespace CleanArchitecture.Web.Controllers ...@@ -25,23 +25,29 @@ namespace CleanArchitecture.Web.Controllers
public IActionResult Populate() public IActionResult Populate()
{ {
if (_todoRepository.List().Any()) return Ok(0); int recordsAdded = PopulateDatabase();
return Ok(recordsAdded);
}
public int PopulateDatabase()
{
if (_todoRepository.List().Any()) return 0;
_todoRepository.Add(new ToDoItem() _todoRepository.Add(new ToDoItem()
{ {
Title = "One", Title = "Get Sample Working",
Description = "The first item" Description = "Try to get the sample to build."
}); });
_todoRepository.Add(new ToDoItem() _todoRepository.Add(new ToDoItem()
{ {
Title = "Two", Title = "Review Solution",
Description = "The second item" Description = "Review the different projects in the solution and how they relate to one another."
}); });
_todoRepository.Add(new ToDoItem() _todoRepository.Add(new ToDoItem()
{ {
Title = "Three", Title = "Run and Review Tests",
Description = "The three item" Description = "Make sure all the tests run and review what they are doing."
}); });
return Ok(3); return 3;
} }
} }
} }
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
namespace CleanArchitecture.Web.Filters
{
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
context.Result = new BadRequestObjectResult(context.ModelState);
}
}
}
}
...@@ -5,6 +5,7 @@ using CleanArchitecture.Core.SharedKernel; ...@@ -5,6 +5,7 @@ using CleanArchitecture.Core.SharedKernel;
using CleanArchitecture.Infrastructure; using CleanArchitecture.Infrastructure;
using CleanArchitecture.Infrastructure.Data; using CleanArchitecture.Infrastructure.Data;
using CleanArchitecture.Infrastructure.DomainEvents; using CleanArchitecture.Infrastructure.DomainEvents;
using CleanArchitecture.Web.ApiModels;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
...@@ -56,7 +57,7 @@ namespace CleanArchitecture.Web ...@@ -56,7 +57,7 @@ namespace CleanArchitecture.Web
_.ConnectImplementationsToTypesClosing(typeof(IHandle<>)); _.ConnectImplementationsToTypesClosing(typeof(IHandle<>));
}); });
// ToDo: Add Registry Classes // TODO: Add Registry Classes
// TODO: Move to Infrastucture Registry // TODO: Move to Infrastucture Registry
config.For(typeof(IRepository<>)).Add(typeof(EfRepository<>)); config.For(typeof(IRepository<>)).Add(typeof(EfRepository<>));
...@@ -69,6 +70,40 @@ namespace CleanArchitecture.Web ...@@ -69,6 +70,40 @@ namespace CleanArchitecture.Web
services.AddTransient<IRepository<ToDoItem>, EfRepository<ToDoItem>>(); services.AddTransient<IRepository<ToDoItem>, EfRepository<ToDoItem>>();
} }
public void ConfigureTesting(IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory)
{
this.Configure(app, env, loggerFactory);
PopulateTestData(app);
//var authorRepository = app.ApplicationServices
// .GetService<IAuthorRepository>();
//Task.Run(() => PopulateSampleData(authorRepository));
}
private void PopulateTestData(IApplicationBuilder app)
{
var dbContext = app.ApplicationServices.GetService<AppDbContext>();
var toDos = dbContext.ToDoItems;
foreach (var item in toDos)
{
dbContext.Remove(item);
}
dbContext.SaveChanges();
dbContext.ToDoItems.Add(new ToDoItem()
{
Title = "Test Item 1",
Description = "Test Description One"
});
dbContext.ToDoItems.Add(new ToDoItem()
{
Title = "Test Item 2",
Description = "Test Description Two"
});
dbContext.SaveChanges();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{ {
......
...@@ -6,7 +6,8 @@ ...@@ -6,7 +6,8 @@
<div class="col-md-6"> <div class="col-md-6">
<h2>To Do Items</h2> <h2>To Do Items</h2>
<ul> <ul>
<li><a asp-area="" asp-controller="ToDo" asp-action="Index">To Do Items</a></li> <li><a asp-area="" asp-controller="ToDo" asp-action="Populate">Load Sample To Do Items</a></li>
<li><a asp-area="" asp-controller="ToDo" asp-action="Index">List To Do Items</a></li>
</ul> </ul>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
......
...@@ -6,6 +6,6 @@ ...@@ -6,6 +6,6 @@
<ul> <ul>
@foreach (var item in Model) @foreach (var item in Model)
{ {
<li>@item.Title</li> <li>@item.Title<br/>@item.Description</li>
} }
</ul> </ul>
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
using CleanArchitecture.Infrastructure.Data;
using CleanArchitecture.Core.Entities;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using CleanArchitecture.Core.Events;
using CleanArchitecture.Core.Interfaces;
using CleanArchitecture.Web;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Moq;
using Newtonsoft.Json;
namespace CleanArchitecture.Tests.Integration.Web
{
public class ApiToDoItemsControllerListShould
{
private readonly HttpClient _client;
public ApiToDoItemsControllerListShould()
{
_client = GetClient();
}
protected HttpClient GetClient()
{
var builder = new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseEnvironment("Testing"); // ensure ConfigureTesting is called in Startup
var server = new TestServer(builder);
var client = server.CreateClient();
// client always expects json results
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
return client;
}
[Fact]
public async Task ReturnTwoItems()
{
var response = await _client.GetAsync("/api/todoitems");
response.EnsureSuccessStatusCode();
var stringResponse = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<IEnumerable<ToDoItem>>(stringResponse).ToList();
Assert.Equal(2, result.Count());
Assert.Equal(1, result.Count(a => a.Title == "Test Item 1"));
Assert.Equal(1, result.Count(a => a.Title == "Test Item 2"));
}
}
}
\ No newline at end of file
...@@ -7,10 +7,13 @@ ...@@ -7,10 +7,13 @@
"NETStandard.Library": "1.6.0", "NETStandard.Library": "1.6.0",
"xunit": "2.2.0-beta2-build3300", "xunit": "2.2.0-beta2-build3300",
"dotnet-test-xunit": "2.2.0-preview2-build1029", "dotnet-test-xunit": "2.2.0-preview2-build1029",
"Microsoft.AspNetCore.TestHost": "1.0.0",
"Microsoft.EntityFrameworkCore": "1.0.0", "Microsoft.EntityFrameworkCore": "1.0.0",
"Microsoft.EntityFrameworkCore.InMemory": "1.0.0", "Microsoft.EntityFrameworkCore.InMemory": "1.0.0",
"Newtonsoft.Json": "9.0.1",
"System.Diagnostics.TraceSource": "4.0.0", "System.Diagnostics.TraceSource": "4.0.0",
"Moq": "4.6.25-alpha" "Moq": "4.6.25-alpha",
"CleanArchitecture.Web": "1.0.0-*"
}, },
"tools": { "tools": {
"Microsoft.DotNet.Watcher.Tools": "1.0.0-preview2-final" "Microsoft.DotNet.Watcher.Tools": "1.0.0-preview2-final"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment