Merge pull request '🐛 🔥 ♻️ 💀 ⚰️ Fix #7 ; Fix #36 - Implement One-to-Many and set-up SQLite' (#62) from debug-one-to-many into master
continuous-integration/drone/push Build is passing Details

Reviewed-on: #62
pull/63/head
Alexis Drai 2 years ago
commit 48c0e0f03b

@ -3,12 +3,12 @@ using cat_cafe.Dto;
using cat_cafe.Entities;
using cat_cafe.Mappers;
using cat_cafe.Repositories;
using cat_cafe.WeSo;
using FluentAssertions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using FluentAssertions;
using cat_cafe.WeSo;
namespace cat_cafe.Controllers.Tests
{
@ -101,7 +101,7 @@ namespace cat_cafe.Controllers.Tests
actualResult!.Value.Should().BeEquivalentTo(aliceDto);
}
/*
[TestMethod()]
public async Task PutCatTest()
{
@ -111,12 +111,12 @@ namespace cat_cafe.Controllers.Tests
// Act
var responseType = await controller.PutCat(bob.Id, robert);
// System.InvalidOperationException:
// The instance of entity type 'Cat' cannot be tracked because another instance
// with the same key value for {'Id'} is already being tracked.
// System.InvalidOperationException:
// The instance of entity type 'Cat' cannot be tracked because another instance
// with the same key value for {'Id'} is already being tracked.
// ... this simple update should work out of the box,
// DbContext is already 'scoped' by default instead of singleton
// ... this simple update should work out of the box,
// DbContext is already 'scoped' by default instead of singleton
// Assert
responseType.Should().BeOfType<NoContentResult>();
@ -125,7 +125,7 @@ namespace cat_cafe.Controllers.Tests
var actualResult = actual.Result as OkObjectResult;
actualResult!.Value.Should().BeEquivalentTo(robert);
}
*/
[TestMethod()]
public async Task PostCatTest()

@ -1,29 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using AutoMapper;
using cat_cafe.Dto;
using cat_cafe.Entities;
using cat_cafe.Repositories;
using AutoMapper;
using cat_cafe.Dto;
using System.Collections;
using System.Xml.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace cat_cafe.Controllers
{
[Route("api/v{version:apiVersion}/[controller]")]
[ApiController]
[ApiVersion("2.0")]
[ApiVersion("1.0")]
public class BarsController : ControllerBase
{
private readonly CatCafeContext _context;
private readonly IMapper _mapper;
private readonly ILogger <BarsController> _logger;
private readonly ILogger<BarsController> _logger;
public BarsController(CatCafeContext context,IMapper mapper, ILogger<BarsController> logger)
public BarsController(CatCafeContext context, IMapper mapper, ILogger<BarsController> logger)
{
_context = context;
_mapper = mapper;
@ -32,97 +25,83 @@ namespace cat_cafe.Controllers
// GET: api/v1/Bars
[HttpGet]
[MapToApiVersion("1.0")]
public async Task<ActionResult<IEnumerable<BarDto>>> GetBars()
{
var bars = _context.Bars
.Include(a => a.cats)
.Select(a => new Bar
{
Id = a.Id,
Name = a.Name,
cats = a.cats.Select(p => new Cat { Name = p.Name, Age = p.Age, Id= p.Id}).ToList()
})
.ToList();
return _mapper.Map<List<BarDto>>(bars);
var bars = await _context.Bars
.Include(b => b.Cats)
.ToListAsync();
return Ok(_mapper.Map<IEnumerable<BarDto>>(bars));
}
// GET: api/v1/Bars/5
[HttpGet("{id}")]
[MapToApiVersion("1.0")]
public async Task<ActionResult<BarDto>> GetBar(long id)
{
var bar = _context.Bars.Include(p => p.cats)
.Select(a => new Bar
{
Id = a.Id,
Name = a.Name,
cats = a.cats.Select(p => new Cat { Name = p.Name, Age = p.Age, Id = p.Id }).ToList()
}).FirstOrDefaultAsync(p => p.Id == id);
var bar = await _context.Bars
.Include(b => b.Cats)
.SingleOrDefaultAsync(b => b.Id == id);
if (bar == null)
{
return NotFound();
}
return _mapper.Map<BarDto>(bar.Result);
return Ok(_mapper.Map<BarDto>(bar));
}
// PUT: api/v1/Bars/5
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
[HttpPut("{id}")]
[MapToApiVersion("1.0")]
public async Task<IActionResult> PutBar(long id, BarDto barDto)
{
if (id != barDto.Id)
{
return BadRequest();
}
Bar bar = _mapper.Map<Bar>(barDto);
_context.Entry(bar).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
var bar = await _context.Bars
.Include(b => b.Cats)
.SingleOrDefaultAsync(b => b.Id == id);
if (bar == null)
{
if (!BarExists(id))
{
return NotFound();
}
else
{
throw;
}
return NotFound();
}
_mapper.Map(barDto, bar);
bar.Cats = await _context.Cats
.Where(c => barDto.CatIds.Contains(c.Id))
.ToListAsync();
await _context.SaveChangesAsync();
return NoContent();
}
// POST: api/v1/Bars
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
[HttpPost]
[MapToApiVersion("1.0")]
public async Task<ActionResult<BarDto>> PostBar(BarDto barDto)
public async Task<ActionResult<BarDto>> CreateBar(BarDto barDto)
{
// Bar bar = _mapper.Map<Bar>(barDto);
var bar = _mapper.Map<Bar>(barDto);
bar.Cats = await _context.Cats
.Where(c => barDto.CatIds.Contains(c.Id))
.ToListAsync();
_context.Bars.Add(bar);
await _context.SaveChangesAsync();
return CreatedAtAction("GetBar", new { id = barDto.Id }, _mapper.Map<BarDto>(bar));
return CreatedAtAction(nameof(GetBar), new { id = bar.Id }, _mapper.Map<BarDto>(bar));
}
// DELETE: api/v1/Bars/5
[HttpDelete("{id}")]
[MapToApiVersion("1.0")]
public async Task<IActionResult> DeleteBar(long id)
{
var bar = await _context.Bars.FindAsync(id);
var bar = await _context.Bars
.Include(b => b.Cats)
.SingleOrDefaultAsync(b => b.Id == id);
if (bar == null)
{
return NotFound();
@ -133,10 +112,5 @@ namespace cat_cafe.Controllers
return NoContent();
}
private bool BarExists(long id)
{
return _context.Bars.Any(e => e.Id == id);
}
}
}

@ -1,18 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using AutoMapper;
using cat_cafe.Dto;
using cat_cafe.Entities;
using cat_cafe.Repositories;
using AutoMapper;
using cat_cafe.Dto;
using Serilog;
using Newtonsoft.Json;
using Microsoft.Extensions.Logging.Abstractions;
using cat_cafe.WeSo;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace cat_cafe.Controllers
{
@ -28,8 +20,8 @@ namespace cat_cafe.Controllers
private readonly WebSocketHandler _webSocketHandler;
public CatsController(
CatCafeContext context,
IMapper mapper,
CatCafeContext context,
IMapper mapper,
ILogger<CatsController> logger,
WebSocketHandler webSocketHandler
)
@ -65,7 +57,6 @@ namespace cat_cafe.Controllers
// GET: api/v1/Cats/5
[HttpGet("{id}")]
[MapToApiVersion("1.0")]
public async Task<ActionResult<CatDto>> GetCat(long id)
{
var cat = await _context.Cats.FindAsync(id);
@ -81,7 +72,6 @@ namespace cat_cafe.Controllers
// PUT: api/v1/Cats/5
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
[HttpPut("{id}")]
[MapToApiVersion("1.0")]
public async Task<IActionResult> PutCat(long id, CatDto catDto)
{
if (id != catDto.Id)
@ -89,32 +79,24 @@ namespace cat_cafe.Controllers
return BadRequest();
}
Cat cat = _mapper.Map<Cat>(catDto);
_context.Entry(cat).State = EntityState.Modified;
var cat = await _context.Cats
.SingleOrDefaultAsync(c => c.Id == id);
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
if (cat == null)
{
if (!CatExists(id))
{
return NotFound();
}
else
{
throw;
}
return NotFound();
}
_mapper.Map(catDto, cat);
await _context.SaveChangesAsync();
return NoContent();
}
// POST: api/v1/Cats
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
[HttpPost]
[MapToApiVersion("1.0")]
public async Task<ActionResult<CatDto>> PostCat(CatDto catDto)
{
Cat cat = _mapper.Map<Cat>(catDto);
@ -128,7 +110,6 @@ namespace cat_cafe.Controllers
// DELETE: api/v1/Cats/5
[HttpDelete("{id}")]
[MapToApiVersion("1.0")]
public async Task<IActionResult> DeleteCat(long id)
{
var cat = await _context.Cats.FindAsync(id);

@ -1,29 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using AutoMapper;
using cat_cafe.Dto;
using cat_cafe.Entities;
using cat_cafe.Repositories;
using cat_cafe.Dto;
using AutoMapper;
using Serilog;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using Serilog;
namespace cat_cafe.Controllers
{
[Route("api/v{version:apiVersion}/[controller]")]
[ApiController]
[ApiVersion("2.0")]
[ApiVersion("1.0")]
public class CustomersController : ControllerBase
{
private readonly CatCafeContext _context;
private readonly IMapper _mapper;
private readonly ILogger<CustomersController> _logger;
public CustomersController(CatCafeContext context,IMapper mapper,ILogger<CustomersController> logger)
public CustomersController(CatCafeContext context, IMapper mapper, ILogger<CustomersController> logger)
{
_context = context;
_mapper = mapper;
@ -32,7 +27,6 @@ namespace cat_cafe.Controllers
// GET: api/v1/Customers
[HttpGet]
[MapToApiVersion("1.0")]
public async Task<ActionResult<IEnumerable<CustomerDto>>> GetCustomers()
{
Log.Information(this.Request.Method + " => get All customers");
@ -48,10 +42,9 @@ namespace cat_cafe.Controllers
// GET: api/v1/Customers/5
[HttpGet("{id}")]
[MapToApiVersion("1.0")]
public async Task<ActionResult<CustomerDto>> GetCustomer(long id)
{
Log.Information(this.Request.Method + " => get by ID {@id}",id);
Log.Information(this.Request.Method + " => get by ID {@id}", id);
var customer = await _context.Customers.FindAsync(id);
if (customer == null)
@ -70,49 +63,34 @@ namespace cat_cafe.Controllers
// PUT: api/v1/Customers/5
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
[HttpPut("{id}")]
[MapToApiVersion("1.0")]
public async Task<IActionResult> PutCustomer(long id, CustomerDto customerDto)
{
Log.Information(this.Request.Method + " => put by ID {@id}", id);
if (id != customerDto.Id)
{
Log.Information(this.Request.Method + " => " + BadRequest().StatusCode.ToString()+" IDs not matching");
Log.Information(this.Request.Method + " => " + BadRequest().StatusCode.ToString() + " IDs not matching");
return BadRequest();
}
Customer customer = _mapper.Map<Customer>(customerDto);
_context.Entry(customer).State = EntityState.Modified;
var customer = await _context.Customers
.SingleOrDefaultAsync(c => c.Id == id);
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException e)
if (customer == null)
{
if (!CustomerExists(id))
{
Log.Information(this.Request.Method + " => " + NotFound().StatusCode.ToString());
return NotFound();
}
else
{
Log.Error(this.Request.Method + " => " + e.Message);
throw;
}
return NotFound();
}
Log.Information(this.Request.Method + " => "
+ this.Response.StatusCode.ToString() + " "
+ customer.GetType().ToString() + " "
+ JsonConvert.SerializeObject(customer).ToString());
return Ok();
_mapper.Map(customerDto, customer);
await _context.SaveChangesAsync();
return NoContent();
}
// POST: api/v1/Customers
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
[HttpPost]
[MapToApiVersion("1.0")]
public async Task<ActionResult<Customer>> PostCustomer(CustomerDto customerDto)
public async Task<ActionResult<CustomerDto>> PostCustomer(CustomerDto customerDto)
{
Log.Information(this.Request.Method + " => post customer");
@ -125,12 +103,11 @@ namespace cat_cafe.Controllers
+ customer.GetType().ToString() + " "
+ JsonConvert.SerializeObject(customer).ToString());
return CreatedAtAction("GetCustomer", new { id = customer.Id }, _mapper.Map<Customer>( customer));
return CreatedAtAction("GetCustomer", new { id = customer.Id }, _mapper.Map<CustomerDto>(customer));
}
// DELETE: api/v1/Customers/5
[HttpDelete("{id}")]
[MapToApiVersion("1.0")]
public async Task<IActionResult> DeleteCustomer(long id)
{
Log.Information(this.Request.Method + " => delete by ID {@id}", id);

@ -1,13 +1,10 @@
using System;
using cat_cafe.Entities;
namespace cat_cafe.Dto
namespace cat_cafe.Dto
{
public class BarDto
{
public class BarDto
{
public long Id { get; set; }
public string? Name { get; set; }
public List<CatDto> cats { get; set; } = new List<CatDto>();
public List<long> CatIds { get; set; } = new();
}
}

@ -1,9 +1,9 @@
using System;
namespace cat_cafe.Dto
namespace cat_cafe.Dto
{
public class CatDto
{
public long Id { get; set; }
public string? Name { get; set; }
public long? BarId { get; set; }
}
}

@ -1,10 +1,9 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations;
namespace cat_cafe.Dto
{
public class CustomerDto
{
public class CustomerDto
{
public long Id { get; set; }
[Required]
public string FullName { get; set; }

@ -1,24 +1,19 @@
using System;
namespace cat_cafe.Entities
namespace cat_cafe.Entities
{
public class Bar
{
public long Id { get; set; }
public string? Name { get; set; }
public List<Cat> cats { get; set; } = new List<Cat>();
public class Bar
{
public long Id { get; set; }
public string? Name { get; set; }
public List<Cat> Cats { get; set; } = new();
public void addCat(Cat c)
{
cats.Add(c);
}
public void removeCat(Cat c)
{
cats.Remove(c);
}
}
public void AddCat(Cat c)
{
Cats.Add(c);
}
public void RemoveCat(Cat c)
{
Cats.Remove(c);
}
}
}

@ -8,7 +8,8 @@ namespace cat_cafe.Entities
public string? Name { get; set; }
[Required]
public int Age { get; set; } = 0;
public long? BarId { get; set; }
public Bar? Bar { get; set; }
public string Meow() { return "meow"; }
}
}

@ -1,11 +1,10 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations;
namespace cat_cafe.Entities
{
public class Customer
{
public long Id { get; set; }
[Required]
public string? FullName { get; set; }

@ -1,18 +1,17 @@
using System;
using AutoMapper;
using AutoMapper;
using cat_cafe.Dto;
using cat_cafe.Entities;
namespace cat_cafe.Mappers
{
public class BarMapper:Profile
{
public BarMapper()
{
// var mapper = config.CreateMapper();
CreateMap<Bar, BarDto>().ReverseMap();
}
}
}
public class BarMapper : Profile
{
public BarMapper()
{
CreateMap<Bar, BarDto>()
.ForMember(dest => dest.CatIds, opt => opt.MapFrom(src => src.Cats.Select(c => c.Id)));
CreateMap<BarDto, Bar>();
}
}
}

@ -2,15 +2,14 @@
using cat_cafe.Dto;
using cat_cafe.Entities;
namespace cat_cafe.Mappers
{
public class CustomerMapper:Profile
{
public CustomerMapper()
{
CreateMap<Customer, CustomerDto>().ReverseMap();
}
}
public class CustomerMapper : Profile
{
public CustomerMapper()
{
CreateMap<Customer, CustomerDto>().ReverseMap();
}
}
}

@ -1,8 +1,8 @@
using Microsoft.EntityFrameworkCore;
using cat_cafe.Repositories;
using cat_cafe.WeSo;
using Microsoft.EntityFrameworkCore;
using Serilog;
using System.Net.WebSockets;
using cat_cafe.WeSo;
var builder = WebApplication.CreateBuilder(args);
@ -15,22 +15,32 @@ List<WebSocket> _sockets = new();
builder.Services.AddSingleton<List<WebSocket>>(x => _sockets);
builder.Services.AddSingleton<WebSocketHandler>();
builder.Services.AddControllers();
builder.Services.AddDbContext<CatCafeContext>(opt => opt.UseInMemoryDatabase("CatCafe"));
builder.Services.AddDbContext<CatCafeContext>(opt => opt.UseSqlite("Data Source=cat_cafe.db"));
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddAutoMapper(typeof(Program));
builder.Services.AddControllersWithViews();
builder.Services.AddApiVersioning(o => { o.ReportApiVersions = true; });
builder.Services.AddApiVersioning(opt => { opt.ReportApiVersions = true; });
builder.Services.AddVersionedApiExplorer(
options =>
opt =>
{
options.GroupNameFormat = "'v'VVV";
options.SubstituteApiVersionInUrl = true;
opt.GroupNameFormat = "'v'VVV";
opt.SubstituteApiVersionInUrl = true;
}
);
var app = builder.Build();
using (var serviceScope = app.Services.CreateScope())
{
var context = serviceScope.ServiceProvider.GetRequiredService<CatCafeContext>();
context.Database.EnsureCreated();
if (context.Database.GetPendingMigrations().Any())
{
context.Database.Migrate();
}
}
app.UseHttpLogging();
// Configure the HTTP request pipeline.
@ -48,7 +58,6 @@ app.MapControllers();
app.UseWebSockets();
app.Use(async (context, next) =>
{
if (context.Request.Path == "/ws")

@ -14,5 +14,12 @@ namespace cat_cafe.Repositories
public DbSet<Bar> Bars { get; set; } = null!;
public DbSet<Customer> Customers { get; set; } = null!;
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Cat>()
.HasOne(c => c.Bar)
.WithMany(b => b.Cats)
.HasForeignKey(c => c.BarId);
}
}
}

@ -4,16 +4,18 @@
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<StartWorkingDirectory>$(MSBuildProjectDirectory)</StartWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="5.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer" Version="5.0.0" />
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.2.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="7.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.12" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.12" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.12">
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="7.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

Loading…
Cancel
Save