Merge branch 'react-version' of https://github.com/MarcEricMartel/420-5DW-HY-TP into react-version

This commit is contained in:
Jean-Daniel Lamontagne 2022-11-01 11:25:03 -04:00
commit 9d84aa9e9b
19 changed files with 856 additions and 163 deletions

View File

@ -63,35 +63,35 @@ public class InventoryController : Controller {
} }
switch (filterState) { switch (filterState) {
case "isAvailable": case "isAvailable":
ret = ret.Where(x => x.Status == Product.States.Available); ret = ret.Where(x => x.Status == ProductModel.States.Available);
break; break;
case "isUnavailable": case "isUnavailable":
ret = ret.Where(x => x.Status == Product.States.Unavailable); ret = ret.Where(x => x.Status == ProductModel.States.Unavailable);
break; break;
case "isBackOrder": case "isBackOrder":
ret = ret.Where(x => x.Status == Product.States.BackOrder); ret = ret.Where(x => x.Status == ProductModel.States.BackOrder);
break; ; break; ;
case "isClearance": case "isClearance":
ret = ret.Where(x => x.Status == Product.States.Clearance); ret = ret.Where(x => x.Status == ProductModel.States.Clearance);
break; break;
case "isDiscontinued": case "isDiscontinued":
ret = ret.Where(x => x.Status == Product.States.Discontinued); ret = ret.Where(x => x.Status == ProductModel.States.Discontinued);
break; break;
case "isPromoted": case "isPromoted":
ret = ret.Where(x => x.Status == Product.States.Clearance || ret = ret.Where(x => x.Status == ProductModel.States.Clearance ||
x.Status == Product.States.Promotion); x.Status == ProductModel.States.Promotion);
break; break;
default: break; default: break;
} }
switch (order) { switch (order) {
case "Price": case "Price":
ret = ret.OrderBy(x => x.Status == Product.States.Promotion || ret = ret.OrderBy(x => x.Status == ProductModel.States.Promotion ||
x.Status == Product.States.Clearance ? x.Status == ProductModel.States.Clearance ?
x.PromoPrice : x.Price); x.PromoPrice : x.Price);
break; break;
case "PriceDesc": case "PriceDesc":
ret = ret.OrderByDescending(x => x.Status == Product.States.Promotion || ret = ret.OrderByDescending(x => x.Status == ProductModel.States.Promotion ||
x.Status == Product.States.Clearance ? x.Status == ProductModel.States.Clearance ?
x.PromoPrice : x.Price); x.PromoPrice : x.Price);
break; break;
case "Title": case "Title":
@ -132,37 +132,37 @@ public class InventoryController : Controller {
} }
} }
// Inventory/Delete => Décrémenter un produit. Va aller chercher directement dans la BD. // Inventory/Delete => Décrémenter un produit. Va aller chercher directement dans la BD.
[EnableCors("_myAllowSpecificOrigins"), HttpDelete(Name = "Inventory"), AllowAnonymous] //[EnableCors("_myAllowSpecificOrigins"), HttpDelete(Name = "Inventory"), AllowAnonymous]
public ActionResult<int> Delete(int? id) { //public ActionResult<int> Delete(int? id) {
int rid = 0; // int rid = 0;
if (!id.HasValue) { // if (!id.HasValue) {
_logger.LogError(8, "Tentative de vente sans Id."); // _logger.LogError(8, "Tentative de vente sans Id.");
return BadRequest(); // return BadRequest();
} // }
try { // try {
Product prod = _context.Products.First(x => x.Id == id); // ProductModel prod = _context.Products.First(x => x.Id == id);
rid = prod.Id; // rid = prod.Id;
if (prod.Quantity > 0) { // if (prod.Quantity > 0) {
prod.Quantity = prod.Quantity - 1; // prod.Quantity = prod.Quantity - 1;
prod.Sales = prod.Sales + 1; // prod.Sales = prod.Sales + 1;
prod.LastSale = DateTime.Now; // prod.LastSale = DateTime.Now;
if (prod.Quantity == 0) // if (prod.Quantity == 0)
prod.Status = prod.Status == Product.States.Clearance ? // prod.Status = prod.Status == ProductModel.States.Clearance ?
Product.States.Discontinued : // ProductModel.States.Discontinued :
Product.States.BackOrder; // ProductModel.States.BackOrder;
} else { // } else {
_logger.LogError(8, $"Vente de produit pas en stock. Id Produit: {prod.Id}"); // _logger.LogError(8, $"Vente de produit pas en stock. Id Produit: {prod.Id}");
return BadRequest(); // return BadRequest();
} // }
_context.Products.Update(prod); // _context.Products.Update(prod);
_context.SaveChanges(); // _context.SaveChanges();
} catch (Exception e) { // } catch (Exception e) {
_logger.LogError(8, e.Message); // _logger.LogError(8, e.Message);
return BadRequest(); // return BadRequest();
} // }
_cache.askForRefresh(); // _cache.askForRefresh();
return rid; // return rid;
} //}
#endregion #endregion
} }

View File

@ -0,0 +1,21 @@
using GrossesMitainesAPI.Data;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
namespace GrossesMitainesAPI.Controllers;
[EnableCors("_myAllowSpecificOrigins"), ApiController, Route("api/[controller]"),
Authorize(AuthenticationSchemes = "Identity.Application", Roles = "Administrateur")]
public class InvoiceController : Controller {
private readonly ILogger<InvoiceController> _logger;
private readonly InventoryContext _context;
public InvoiceController(ILogger<InvoiceController> logger, InventoryContext context) {
this._logger = logger;
this._context = context;
}
}

View File

@ -1,5 +1,6 @@
namespace GrossesMitainesAPI.Controllers; namespace GrossesMitainesAPI.Controllers;
#region Dependencies
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
@ -10,35 +11,54 @@ using Microsoft.AspNetCore.Authentication.Cookies;
using System.Security.Principal; using System.Security.Principal;
using GrossesMitainesAPI.Models; using GrossesMitainesAPI.Models;
#endregion
[EnableCors("_myAllowSpecificOrigins"), ApiController, Route("api"), [EnableCors("_myAllowSpecificOrigins"), ApiController, Route("api"),
Authorize(AuthenticationSchemes = "Identity.Application")] Authorize(AuthenticationSchemes = "Identity.Application")]
public class LoginController : Controller { public class LoginController : Controller {
#region DI Fields
private readonly UserManager<InventoryUser> _userMan; private readonly UserManager<InventoryUser> _userMan;
private readonly SignInManager<InventoryUser> _signInMan; private readonly SignInManager<InventoryUser> _signInMan;
#endregion
#region Ctor
public LoginController(SignInManager<InventoryUser> signin, UserManager<InventoryUser> userman) { public LoginController(SignInManager<InventoryUser> signin, UserManager<InventoryUser> userman) {
this._signInMan = signin; this._signInMan = signin;
this._userMan = userman; this._userMan = userman;
} }
#endregion
#region Utility Classes
public class LoginUser { public class LoginUser {
public string email { get; set; } = ""; public string email { get; set; } = "";
public string password { get; set; } = ""; public string password { get; set; } = "";
} }
[HttpGet, Route("Login")] #endregion
public ReturnUserViewModel WhoAmI() {
var user = _userMan.GetUserAsync(_signInMan.Context.User); #region API Methods
user.Wait(); [HttpGet, Route("WhoAmI")]
return new ReturnUserViewModel(user.Result); public async Task<ReturnUserViewModel> WhoAmI() {
var user = await _userMan.GetUserAsync(_signInMan.Context.User);
var roles = await _userMan.GetRolesAsync(user);
string role = "";
if (roles.Contains("Administrateur"))
role = "Administrateur";
else role = "Client";
return new ReturnUserViewModel(user, role);
} }
[HttpPost, Route("Login"), AllowAnonymous] [HttpPost, Route("Login"), AllowAnonymous]
public async Task<SignInResult> Login(LoginUser user, bool rememberMe = false) { public async Task<SignInResult> Login(LoginUser user, bool rememberMe = false) {
return await _signInMan.PasswordSignInAsync(await _userMan.FindByEmailAsync(user.email), user.password, rememberMe, false); var User = await _userMan.FindByEmailAsync(user.email);
return await _signInMan.PasswordSignInAsync(User, user.password, rememberMe, false);
} }
[HttpPost, Route("Logout")] [HttpPost, Route("Logout")]
public void Logout() => _signInMan.SignOutAsync(); public void Logout() => _signInMan.SignOutAsync();
#endregion
} }

View File

@ -21,7 +21,7 @@ using GrossesMitainesAPI.Services;
/// l'éxécution d'une modification de la BD. /// l'éxécution d'une modification de la BD.
/// </summary> /// </summary>
[EnableCors("_myAllowSpecificOrigins"), ApiController, Route("api/[controller]"), [EnableCors("_myAllowSpecificOrigins"), ApiController, Route("api/[controller]"),
Authorize(AuthenticationSchemes = "Identity.Application")] Authorize(AuthenticationSchemes = "Identity.Application", Roles = "Administrateur")]
public class ProductController : ControllerBase { public class ProductController : ControllerBase {
#region DI Fields #region DI Fields
private readonly ILogger<ProductController> _logger; private readonly ILogger<ProductController> _logger;
@ -44,7 +44,7 @@ public class ProductController : ControllerBase {
#region API Methods #region API Methods
[EnableCors("_myAllowSpecificOrigins"), HttpGet(Name = "Product"), AllowAnonymous] [EnableCors("_myAllowSpecificOrigins"), HttpGet(Name = "Product"), AllowAnonymous]
public ActionResult<ProductViewModel> Get(int id) { public ActionResult<ProductViewModel> Get(int id) {
Product prod; ProductModel prod;
try { try {
prod = _context.Products.Where(x => x.Id == id).First(); prod = _context.Products.Where(x => x.Id == id).First();
} }
@ -57,7 +57,7 @@ public class ProductController : ControllerBase {
} }
[EnableCors("_myAllowSpecificOrigins"), HttpPost(Name = "Product")] [EnableCors("_myAllowSpecificOrigins"), HttpPost(Name = "Product")]
public async Task<ActionResult<Product>> Post(Product prod) { public async Task<ActionResult<ProductModel>> Post(ProductModel prod) {
if (prod.Price <= prod.PromoPrice) if (prod.Price <= prod.PromoPrice)
prod.PromoPrice = prod.Price - 0.01M; prod.PromoPrice = prod.Price - 0.01M;
try { try {
@ -76,7 +76,7 @@ public class ProductController : ControllerBase {
} }
[EnableCors("_myAllowSpecificOrigins"), HttpPatch(Name = "Product")] [EnableCors("_myAllowSpecificOrigins"), HttpPatch(Name = "Product")]
public ActionResult<Product> Patch(Product prod) { public ActionResult<ProductModel> Patch(ProductModel prod) {
try { try {
_context.Products.Update(prod); _context.Products.Update(prod);
_context.SaveChanges(); _context.SaveChanges();

View File

@ -25,7 +25,7 @@ public class SearchController : Controller {
private readonly ILogger<SearchController> _logger; private readonly ILogger<SearchController> _logger;
private readonly InventoryContext _context; private readonly InventoryContext _context;
private readonly DatabaseCacheService _cache; private readonly DatabaseCacheService _cache;
private IQueryable<Product>? _searchCache = null; private IQueryable<ProductModel>? _searchCache = null;
#endregion #endregion
@ -81,22 +81,22 @@ public class SearchController : Controller {
} }
switch (filterState) { switch (filterState) {
case "isAvailable": case "isAvailable":
ret = ret.Where(x => x.Status == Product.States.Available); ret = ret.Where(x => x.Status == ProductModel.States.Available);
break; break;
case "isUnavailable": case "isUnavailable":
ret = ret.Where(x => x.Status == Product.States.Unavailable); ret = ret.Where(x => x.Status == ProductModel.States.Unavailable);
break; break;
case "isBackOrder": case "isBackOrder":
ret = ret.Where(x => x.Status == Product.States.BackOrder); ret = ret.Where(x => x.Status == ProductModel.States.BackOrder);
break; ; break; ;
case "isClearance": case "isClearance":
ret = ret.Where(x => x.Status == Product.States.Clearance); ret = ret.Where(x => x.Status == ProductModel.States.Clearance);
break; break;
case "isDiscontinued": case "isDiscontinued":
ret = ret.Where(x => x.Status == Product.States.Discontinued); ret = ret.Where(x => x.Status == ProductModel.States.Discontinued);
break; break;
case "isPromoted": case "isPromoted":
ret = ret.Where(x => x.Status == Product.States.Clearance || x.Status == Product.States.Promotion); ret = ret.Where(x => x.Status == ProductModel.States.Clearance || x.Status == ProductModel.States.Promotion);
break; break;
default: break; default: break;
} }
@ -122,10 +122,10 @@ public class SearchController : Controller {
products.AddRange(cat); products.AddRange(cat);
break; break;
case "Price": case "Price":
ret = ret.OrderBy(x => x.Status == Product.States.Promotion || x.Status == Product.States.Clearance ? x.PromoPrice : x.Price); ret = ret.OrderBy(x => x.Status == ProductModel.States.Promotion || x.Status == ProductModel.States.Clearance ? x.PromoPrice : x.Price);
goto default; goto default;
case "PriceDesc": case "PriceDesc":
ret = ret.OrderByDescending(x => x.Status == Product.States.Promotion || x.Status == Product.States.Clearance ? x.PromoPrice : x.Price); ret = ret.OrderByDescending(x => x.Status == ProductModel.States.Promotion || x.Status == ProductModel.States.Clearance ? x.PromoPrice : x.Price);
goto default; goto default;
case "Title": case "Title":
ret = ret.OrderBy(x => x.Title); ret = ret.OrderBy(x => x.Title);

View File

@ -0,0 +1,25 @@
using GrossesMitainesAPI.Data;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
namespace GrossesMitainesAPI.Controllers;
[EnableCors("_myAllowSpecificOrigins"), ApiController, Route("api/[controller]"),
Authorize(AuthenticationSchemes = "Identity.Application", Roles = "Administrateur")]
public class UserController : Controller {
private readonly UserManager<InventoryUser> _userMan;
private readonly SignInManager<InventoryUser> _signInMan;
public UserController(SignInManager<InventoryUser> signin, UserManager<InventoryUser> userman) {
this._signInMan = signin;
this._userMan = userman;
}
}

View File

@ -6,205 +6,243 @@ using Microsoft.AspNetCore.Identity;
namespace GrossesMitainesAPI.Data; namespace GrossesMitainesAPI.Data;
public class InventoryContext : IdentityDbContext<InventoryUser> { public class InventoryContext : IdentityDbContext<InventoryUser> {
public DbSet<Product> Products { get; set; } public DbSet<ProductModel> Products { get; set; }
public DbSet<AddressModel> Addresses { get; set; }
public DbSet<InvoiceModel> Invoices { get; set; }
public InventoryContext(DbContextOptions<InventoryContext> options) : base(options) { } public InventoryContext(DbContextOptions<InventoryContext> options) : base(options) { }
protected override void OnModelCreating(ModelBuilder modelBuilder) { protected override void OnModelCreating(ModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
// Pour partir la BD. // Pour partir la BD.
modelBuilder.Entity<Product>().HasData(new Product { modelBuilder.Entity<ProductModel>().HasData(new ProductModel {
Id = 1, Id = 1,
Title = $"Ceinture flèchée", Title = $"Ceinture flèchée",
Category = $"Linge", Category = $"Linge",
Description = $"Pour faire votre propre bonhomme de 1837, comme dans le bon vieux temps.", Description = $"Pour faire votre propre bonhomme de 1837, comme dans le bon vieux temps.",
Status = Product.States.Promotion, Status = ProductModel.States.Promotion,
Price = 85.86M, Price = 85.86M,
PromoPrice = 29.99M, PromoPrice = 29.99M,
Quantity = 1, Quantity = 1,
ImageName = $"ceintureflechee" ImageName = $"ceintureflechee"
}); });
modelBuilder.Entity<Product>().HasData(new Product { modelBuilder.Entity<ProductModel>().HasData(new ProductModel {
Id = 2, Id = 2,
Title = $"Pantoufles du Canadien en Phentex", Title = $"Pantoufles du Canadien en Phentex",
Category = $"Linge", Category = $"Linge",
Description = $"Parce que ça sent la coupe!", Description = $"Parce que ça sent la coupe!",
Status = Product.States.Available, Status = ProductModel.States.Available,
Price = 15.64M, Price = 15.64M,
PromoPrice = 9.99M, PromoPrice = 9.99M,
Quantity = 54, Quantity = 54,
ImageName = $"pantouflesCH" ImageName = $"pantouflesCH"
}); });
modelBuilder.Entity<Product>().HasData(new Product { modelBuilder.Entity<ProductModel>().HasData(new ProductModel {
Id = 3, Id = 3,
Title = $"Jean-Luc Mongrain", Title = $"Jean-Luc Mongrain",
Category = $"Homme", Category = $"Homme",
Description = $"On ne lui ferait pas mal, en tout cas!!", Description = $"On ne lui ferait pas mal, en tout cas!!",
Status = Product.States.Clearance, Status = ProductModel.States.Clearance,
Price = 1453.12M, Price = 1453.12M,
PromoPrice = 999.99M, PromoPrice = 999.99M,
Quantity = 1, Quantity = 1,
ImageName = $"jeanlucmongrain" ImageName = $"jeanlucmongrain"
}); });
modelBuilder.Entity<Product>().HasData(new Product { modelBuilder.Entity<ProductModel>().HasData(new ProductModel {
Id = 4, Id = 4,
Title = $"T-Shirt", Title = $"T-Shirt",
Category = $"Linge", Category = $"Linge",
Description = $"Tellement simple et comfortable.", Description = $"Tellement simple et comfortable.",
Status = Product.States.Available, Status = ProductModel.States.Available,
Price = 12.12M, Price = 12.12M,
PromoPrice = 9.99M, PromoPrice = 9.99M,
Quantity = 143, Quantity = 143,
ImageName = $"tshirt" ImageName = $"tshirt"
}); });
modelBuilder.Entity<Product>().HasData(new Product { modelBuilder.Entity<ProductModel>().HasData(new ProductModel {
Id = 5, Id = 5,
Title = $"Mitaines", Title = $"Mitaines",
Category = $"Vêtement d'extérieur", Category = $"Vêtement d'extérieur",
Description = $"Deux pour un!", Description = $"Deux pour un!",
Status = Product.States.Available, Status = ProductModel.States.Available,
Price = 8.18M, Price = 8.18M,
PromoPrice = 6.99M, PromoPrice = 6.99M,
Quantity = 1423, Quantity = 1423,
ImageName = $"mitaines" ImageName = $"mitaines"
}); });
modelBuilder.Entity<Product>().HasData(new Product { modelBuilder.Entity<ProductModel>().HasData(new ProductModel {
Id = 6, Id = 6,
Title = $"Foulard", Title = $"Foulard",
Category = $"Vêtement d'extérieur", Category = $"Vêtement d'extérieur",
Description = $"Deux pour un!", Description = $"Deux pour un!",
Status = Product.States.Promotion, Status = ProductModel.States.Promotion,
Price = 10.56M, Price = 10.56M,
PromoPrice = 8.99M, PromoPrice = 8.99M,
Quantity = 14, Quantity = 14,
ImageName = $"foulard" ImageName = $"foulard"
}); });
modelBuilder.Entity<Product>().HasData(new Product { modelBuilder.Entity<ProductModel>().HasData(new ProductModel {
Id = 7, Id = 7,
Title = $"Jock-Strap en phentex", Title = $"Jock-Strap en phentex",
Category = $"Sous-Vêtement", Category = $"Sous-Vêtement",
Description = $"Pour garder le p'tit bout au chaud.", Description = $"Pour garder le p'tit bout au chaud.",
Status = Product.States.Promotion, Status = ProductModel.States.Promotion,
Price = 15.45M, Price = 15.45M,
PromoPrice = 12.99M, PromoPrice = 12.99M,
Quantity = 144, Quantity = 144,
ImageName = $"kokin" ImageName = $"kokin"
}); });
modelBuilder.Entity<Product>().HasData(new Product { modelBuilder.Entity<ProductModel>().HasData(new ProductModel {
Id = 8, Id = 8,
Title = $"Jock-Strap féminin en phentex", Title = $"Jock-Strap féminin en phentex",
Category = $"Sous-Vêtement", Category = $"Sous-Vêtement",
Description = $"Pour garder l'absence de p'tit bout au chaud.", Description = $"Pour garder l'absence de p'tit bout au chaud.",
Status = Product.States.Promotion, Status = ProductModel.States.Promotion,
Price = 15.45M, Price = 15.45M,
PromoPrice = 12.99M, PromoPrice = 12.99M,
Quantity = 224, Quantity = 224,
ImageName = $"kokin" ImageName = $"kokin"
}); });
modelBuilder.Entity<Product>().HasData(new Product { modelBuilder.Entity<ProductModel>().HasData(new ProductModel {
Id = 9, Id = 9,
Title = $"Bibi", Title = $"Bibi",
Category = $"Alien", Category = $"Alien",
Description = $"En chiffon.", Description = $"En chiffon.",
Status = Product.States.Clearance, Status = ProductModel.States.Clearance,
Price = 1045.45M, Price = 1045.45M,
PromoPrice = 1023.99M, PromoPrice = 1023.99M,
Quantity = 1, Quantity = 1,
ImageName = $"bibi" ImageName = $"bibi"
}); });
modelBuilder.Entity<Product>().HasData(new Product { modelBuilder.Entity<ProductModel>().HasData(new ProductModel {
Id = 10, Id = 10,
Title = $"Tuque en laine", Title = $"Tuque en laine",
Category = $"Vêtement d'extérieur", Category = $"Vêtement d'extérieur",
Description = $"En chiffon.", Description = $"En chiffon.",
Status = Product.States.Available, Status = ProductModel.States.Available,
Price = 15.45M, Price = 15.45M,
PromoPrice = 12.99M, PromoPrice = 12.99M,
Quantity = 1, Quantity = 1,
ImageName = $"tuque" ImageName = $"tuque"
}); });
modelBuilder.Entity<Product>().HasData(new Product { modelBuilder.Entity<ProductModel>().HasData(new ProductModel {
Id = 11, Id = 11,
Title = $"Habit de Bonhomme Carnaval", Title = $"Habit de Bonhomme Carnaval",
Category = $"Vêtement d'extérieur", Category = $"Vêtement d'extérieur",
Description = $"Pour se faire taper dessus avec une poêle à frire tout en restant au chaud.", Description = $"Pour se faire taper dessus avec une poêle à frire tout en restant au chaud.",
Status = Product.States.Promotion, Status = ProductModel.States.Promotion,
Price = 145.45M, Price = 145.45M,
PromoPrice = 123.99M, PromoPrice = 123.99M,
Quantity = 1, Quantity = 1,
ImageName = $"bonhomme" ImageName = $"bonhomme"
}); });
modelBuilder.Entity<Product>().HasData(new Product { modelBuilder.Entity<ProductModel>().HasData(new ProductModel {
Id = 12, Id = 12,
Title = $"Gauze en phentex", Title = $"Gauze en phentex",
Category = $"Autre", Category = $"Autre",
Description = $"Pour se pêter la fiole avec style.", Description = $"Pour se pêter la fiole avec style.",
Status = Product.States.BackOrder, Status = ProductModel.States.BackOrder,
Price = 145.45M, Price = 145.45M,
PromoPrice = 123.99M, PromoPrice = 123.99M,
Quantity = 0, Quantity = 0,
ImageName = $"gauze" ImageName = $"gauze"
}); });
modelBuilder.Entity<Product>().HasData(new Product { modelBuilder.Entity<ProductModel>().HasData(new ProductModel {
Id = 13, Id = 13,
Title = $"Petit Jésus de plâtre", Title = $"Petit Jésus de plâtre",
Category = $"Homme", Category = $"Homme",
Description = $"En chiffon.", Description = $"En chiffon.",
Status = Product.States.Clearance, Status = ProductModel.States.Clearance,
Price = 145.45M, Price = 145.45M,
PromoPrice = 123.99M, PromoPrice = 123.99M,
Quantity = 1, Quantity = 1,
ImageName = $"jesus" ImageName = $"jesus"
}); });
modelBuilder.Entity<Product>().HasData(new Product { modelBuilder.Entity<ProductModel>().HasData(new ProductModel {
Id = 14, Id = 14,
Title = $"VHS de la Guerre des Tuques", Title = $"VHS de la Guerre des Tuques",
Category = $"Autre", Category = $"Autre",
Description = $"À écouter dans l'habit de Bonhomme Carnaval tant que possible.", Description = $"À écouter dans l'habit de Bonhomme Carnaval tant que possible.",
Status = Product.States.Clearance, Status = ProductModel.States.Clearance,
Price = 3.45M, Price = 3.45M,
PromoPrice = 1.99M, PromoPrice = 1.99M,
Quantity = 164363, Quantity = 164363,
ImageName = $"vhs" ImageName = $"vhs"
}); });
modelBuilder.Entity<Product>().HasData(new Product { modelBuilder.Entity<ProductModel>().HasData(new ProductModel {
Id = 15, Id = 15,
Title = $"Gilet pare-balle en laine", Title = $"Gilet pare-balle en laine",
Category = $"Linge", Category = $"Linge",
Description = $"(N'est pas réellement pare-balle).", Description = $"(N'est pas réellement pare-balle).",
Status = Product.States.Clearance, Status = ProductModel.States.Clearance,
Price = 1435.45M, Price = 1435.45M,
PromoPrice = 1223.99M, PromoPrice = 1223.99M,
Quantity = 18, Quantity = 18,
ImageName = $"chandailquetaine" ImageName = $"chandailquetaine"
}); });
modelBuilder.Entity<Product>().HasData(new Product { modelBuilder.Entity<ProductModel>().HasData(new ProductModel {
Id = 16, Id = 16,
Title = $"Doudou", Title = $"Doudou",
Category = $"Autre", Category = $"Autre",
Description = $"Pour s'éffoirer le nez dedans.", Description = $"Pour s'éffoirer le nez dedans.",
Status = Product.States.Available, Status = ProductModel.States.Available,
Price = 14.45M, Price = 14.45M,
PromoPrice = 13.99M, PromoPrice = 13.99M,
Quantity = 14, Quantity = 14,
ImageName = $"doudou" ImageName = $"doudou"
}); });
modelBuilder.Entity<Product>().HasData(new Product { modelBuilder.Entity<ProductModel>().HasData(new ProductModel {
Id = 17, Id = 17,
Title = $"Mitaines pas de doigts", Title = $"Mitaines pas de doigts",
Category = $"Vêtements d'extérieur", Category = $"Vêtements d'extérieur",
Description = $"Pour avoir l'air thug en hiver.", Description = $"Pour avoir l'air thug en hiver.",
Status = Product.States.Available, Status = ProductModel.States.Available,
Price = 9.45M, Price = 9.45M,
PromoPrice = 8.99M, PromoPrice = 8.99M,
Quantity = 16, Quantity = 16,
ImageName = $"mitaines2" ImageName = $"mitaines2"
}); });
InventoryUser admin = new InventoryUser() { NormalizedUserName = "admin", UserName = "Admin", NormalizedEmail = "admin@admin.com", Email = "admin@admin.com" }; // Source: Notre TP Web 4DW.
string AdministrateurID = "c9e08b20-d8a5-473f-9f52-572eb23c12af";
string ClientID = "1b7b9c55-c746-493a-a24f-3d5ca937298e";
string AdminID = "ecf7503a-591c-454e-a824-048e10bd0474";
InventoryUser admin = new InventoryUser() {
FirstName = "Roger",
LastName = "Admin",
NormalizedUserName = "ADMIN",
UserName = "Admin",
Id = AdminID,
NormalizedEmail = "ADMIN@ADMIN.COM",
Email = "admin@admin.com"
};
//admin.Adresses.Add(new AddressModel() {
// CivicNumber = 1234,
// Appartment = "B",
// Street = "Rue Pierre-Falardeau",
// City = "Saint-Chrysostome",
// PostalCode = "H0H0H0",
// Province = "QC",
// Country = "Canada"
//});
admin.PasswordHash = new PasswordHasher<InventoryUser>().HashPassword(admin, "Qwerty123!"); admin.PasswordHash = new PasswordHasher<InventoryUser>().HashPassword(admin, "Qwerty123!");
modelBuilder.Entity<InventoryUser>().HasData(admin); modelBuilder.Entity<InventoryUser>().HasData(admin);
modelBuilder.Entity<IdentityRole>().HasData(
new IdentityRole { Id = AdministrateurID, Name = "Administrateur", NormalizedName = "ADMINISTRATEUR" },
new IdentityRole { Id = ClientID, Name = "Client", NormalizedName = "CLIENT" }
);
modelBuilder.Entity<IdentityUserRole<string>>().HasData(
new IdentityUserRole<string> { RoleId = AdministrateurID, UserId = AdminID },
new IdentityUserRole<string> { RoleId = ClientID, UserId = AdminID }
);
base.OnModelCreating(modelBuilder);
} }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
var configuration = new ConfigurationBuilder() var configuration = new ConfigurationBuilder()

View File

@ -1,28 +1,13 @@
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using GrossesMitainesAPI.Models;
namespace GrossesMitainesAPI.Data; namespace GrossesMitainesAPI.Data;
public class InventoryUser : IdentityUser { public class InventoryUser : IdentityUser {
public class Address { [Required, MinLength(2), MaxLength(30)]
[Key] public string FirstName { get; set; }
public int Id { get; set; } [Required, MinLength(1), MaxLength(30)]
[Required, Range(1, int.MaxValue)] public string LastName { get; set; }
public int CivicNumber { get; set; } public List<AddressModel> Adresses { get; set; }
public string? Appartment { get; set; }
[Required, MinLength(3), MaxLength(50)]
public string Street { get; set; }
[Required, MinLength(4), MaxLength(50)]
public string City { get; set; }
[Required, MaxLength(2)]
public string Province { get; set; }
[Required, MinLength(4), MaxLength(30)]
public string Country { get; set; }
// Source pour regex: https://stackoverflow.com/questions/15774555/efficient-regex-for-canadian-postal-code-function
[Required, RegularExpression(@"/^[ABCEGHJ-NPRSTVXY]\d[ABCEGHJ-NPRSTV-Z][ -]?\d[ABCEGHJ-NPRSTV-Z]\d$/i")]
public string PostalCode { get; set; }
}
public List<Address> Adresses { get; set; }
} }

View File

@ -12,7 +12,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace GrossesMitainesAPI.Migrations namespace GrossesMitainesAPI.Migrations
{ {
[DbContext(typeof(InventoryContext))] [DbContext(typeof(InventoryContext))]
[Migration("20221030195130_Initial-Db")] [Migration("20221101172005_Initial.Db")]
partial class InitialDb partial class InitialDb
{ {
protected override void BuildTargetModel(ModelBuilder modelBuilder) protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -43,6 +43,16 @@ namespace GrossesMitainesAPI.Migrations
b.Property<bool>("EmailConfirmed") b.Property<bool>("EmailConfirmed")
.HasColumnType("bit"); .HasColumnType("bit");
b.Property<string>("FirstName")
.IsRequired()
.HasMaxLength(30)
.HasColumnType("nvarchar(30)");
b.Property<string>("LastName")
.IsRequired()
.HasMaxLength(30)
.HasColumnType("nvarchar(30)");
b.Property<bool>("LockoutEnabled") b.Property<bool>("LockoutEnabled")
.HasColumnType("bit"); .HasColumnType("bit");
@ -91,23 +101,143 @@ namespace GrossesMitainesAPI.Migrations
b.HasData( b.HasData(
new new
{ {
Id = "809dc5fd-c62f-4d93-abe4-275a55aabd33", Id = "ecf7503a-591c-454e-a824-048e10bd0474",
AccessFailedCount = 0, AccessFailedCount = 0,
ConcurrencyStamp = "f2e55d24-1ab5-441c-87e0-808f32045ab4", ConcurrencyStamp = "6259a59c-35df-4662-84e5-a783e653a643",
Email = "admin@admin.com", Email = "admin@admin.com",
EmailConfirmed = false, EmailConfirmed = false,
FirstName = "Roger",
LastName = "Admin",
LockoutEnabled = false, LockoutEnabled = false,
NormalizedEmail = "admin@admin.com", NormalizedEmail = "ADMIN@ADMIN.COM",
NormalizedUserName = "admin", NormalizedUserName = "ADMIN",
PasswordHash = "AQAAAAEAACcQAAAAEKJtYKQ3fXHUA67KkZbzgDtxdg1UlFk3kFeNj3QSm3OfgsSoMtFExAq16W5arTVmRA==", PasswordHash = "AQAAAAEAACcQAAAAELHpALZdYcW7KzDcU2ovqwdWsfSx68md+LnjvL5ZgJ2OWuTFwJM3gPzQ1yP3RHCn9g==",
PhoneNumberConfirmed = false, PhoneNumberConfirmed = false,
SecurityStamp = "56fc4076-5e9d-4bd1-91e7-b53abcc41fda", SecurityStamp = "c43f4d48-f1bb-4a24-8cd3-78422556cf85",
TwoFactorEnabled = false, TwoFactorEnabled = false,
UserName = "Admin" UserName = "Admin"
}); });
}); });
modelBuilder.Entity("GrossesMitainesAPI.Models.Product", b => modelBuilder.Entity("GrossesMitainesAPI.Models.AddressModel", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1);
b.Property<string>("Appartment")
.HasColumnType("nvarchar(max)");
b.Property<string>("City")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<int>("CivicNumber")
.HasColumnType("int");
b.Property<string>("Country")
.IsRequired()
.HasMaxLength(30)
.HasColumnType("nvarchar(30)");
b.Property<string>("InventoryUserId")
.HasColumnType("nvarchar(450)");
b.Property<string>("PostalCode")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Province")
.IsRequired()
.HasMaxLength(3)
.HasColumnType("nvarchar(3)");
b.Property<string>("Street")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.HasKey("Id");
b.HasIndex("InventoryUserId");
b.ToTable("Addresses");
});
modelBuilder.Entity("GrossesMitainesAPI.Models.InvoiceModel", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1);
b.Property<bool>("Canceled")
.HasColumnType("bit");
b.Property<string>("EmailAddress")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("FirstName")
.IsRequired()
.HasMaxLength(30)
.HasColumnType("nvarchar(30)");
b.Property<string>("LastName")
.IsRequired()
.HasMaxLength(30)
.HasColumnType("nvarchar(30)");
b.Property<string>("LinkedAccountId")
.HasColumnType("nvarchar(450)");
b.Property<string>("PhoneNumber")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("ShippingAddressId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("LinkedAccountId");
b.HasIndex("ShippingAddressId");
b.ToTable("Invoices");
});
modelBuilder.Entity("GrossesMitainesAPI.Models.InvoiceModel+ProductInvoice", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1);
b.Property<int?>("InvoiceModelId")
.HasColumnType("int");
b.Property<int>("ProductId")
.HasColumnType("int");
b.Property<int>("Quantity")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("InvoiceModelId");
b.HasIndex("ProductId");
b.ToTable("ProductInvoice");
});
modelBuilder.Entity("GrossesMitainesAPI.Models.ProductModel", b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
@ -425,6 +555,22 @@ namespace GrossesMitainesAPI.Migrations
.HasFilter("[NormalizedName] IS NOT NULL"); .HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("AspNetRoles", (string)null); b.ToTable("AspNetRoles", (string)null);
b.HasData(
new
{
Id = "c9e08b20-d8a5-473f-9f52-572eb23c12af",
ConcurrencyStamp = "56321382-1bb3-4dfe-87bf-6919c0791765",
Name = "Administrateur",
NormalizedName = "ADMINISTRATEUR"
},
new
{
Id = "1b7b9c55-c746-493a-a24f-3d5ca937298e",
ConcurrencyStamp = "0e3b1bc2-f632-4f63-9bea-ac995e2e95a7",
Name = "Client",
NormalizedName = "CLIENT"
});
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
@ -512,6 +658,18 @@ namespace GrossesMitainesAPI.Migrations
b.HasIndex("RoleId"); b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null); b.ToTable("AspNetUserRoles", (string)null);
b.HasData(
new
{
UserId = "ecf7503a-591c-454e-a824-048e10bd0474",
RoleId = "c9e08b20-d8a5-473f-9f52-572eb23c12af"
},
new
{
UserId = "ecf7503a-591c-454e-a824-048e10bd0474",
RoleId = "1b7b9c55-c746-493a-a24f-3d5ca937298e"
});
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
@ -533,6 +691,45 @@ namespace GrossesMitainesAPI.Migrations
b.ToTable("AspNetUserTokens", (string)null); b.ToTable("AspNetUserTokens", (string)null);
}); });
modelBuilder.Entity("GrossesMitainesAPI.Models.AddressModel", b =>
{
b.HasOne("GrossesMitainesAPI.Data.InventoryUser", null)
.WithMany("Adresses")
.HasForeignKey("InventoryUserId");
});
modelBuilder.Entity("GrossesMitainesAPI.Models.InvoiceModel", b =>
{
b.HasOne("GrossesMitainesAPI.Data.InventoryUser", "LinkedAccount")
.WithMany()
.HasForeignKey("LinkedAccountId");
b.HasOne("GrossesMitainesAPI.Models.AddressModel", "ShippingAddress")
.WithMany()
.HasForeignKey("ShippingAddressId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("LinkedAccount");
b.Navigation("ShippingAddress");
});
modelBuilder.Entity("GrossesMitainesAPI.Models.InvoiceModel+ProductInvoice", b =>
{
b.HasOne("GrossesMitainesAPI.Models.InvoiceModel", null)
.WithMany("Products")
.HasForeignKey("InvoiceModelId");
b.HasOne("GrossesMitainesAPI.Models.ProductModel", "Product")
.WithMany()
.HasForeignKey("ProductId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Product");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{ {
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
@ -583,6 +780,16 @@ namespace GrossesMitainesAPI.Migrations
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
.IsRequired(); .IsRequired();
}); });
modelBuilder.Entity("GrossesMitainesAPI.Data.InventoryUser", b =>
{
b.Navigation("Adresses");
});
modelBuilder.Entity("GrossesMitainesAPI.Models.InvoiceModel", b =>
{
b.Navigation("Products");
});
#pragma warning restore 612, 618 #pragma warning restore 612, 618
} }
} }

View File

@ -28,6 +28,8 @@ namespace GrossesMitainesAPI.Migrations
columns: table => new columns: table => new
{ {
Id = table.Column<string>(type: "nvarchar(450)", nullable: false), Id = table.Column<string>(type: "nvarchar(450)", nullable: false),
FirstName = table.Column<string>(type: "nvarchar(30)", maxLength: 30, nullable: false),
LastName = table.Column<string>(type: "nvarchar(30)", maxLength: 30, nullable: false),
UserName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true), UserName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
NormalizedUserName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true), NormalizedUserName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
Email = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true), Email = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
@ -93,6 +95,31 @@ namespace GrossesMitainesAPI.Migrations
onDelete: ReferentialAction.Cascade); onDelete: ReferentialAction.Cascade);
}); });
migrationBuilder.CreateTable(
name: "Addresses",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
CivicNumber = table.Column<int>(type: "int", nullable: false),
Appartment = table.Column<string>(type: "nvarchar(max)", nullable: true),
Street = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
City = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
Province = table.Column<string>(type: "nvarchar(3)", maxLength: 3, nullable: false),
Country = table.Column<string>(type: "nvarchar(30)", maxLength: 30, nullable: false),
PostalCode = table.Column<string>(type: "nvarchar(max)", nullable: false),
InventoryUserId = table.Column<string>(type: "nvarchar(450)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Addresses", x => x.Id);
table.ForeignKey(
name: "FK_Addresses_AspNetUsers_InventoryUserId",
column: x => x.InventoryUserId,
principalTable: "AspNetUsers",
principalColumn: "Id");
});
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "AspNetUserClaims", name: "AspNetUserClaims",
columns: table => new columns: table => new
@ -178,10 +205,75 @@ namespace GrossesMitainesAPI.Migrations
onDelete: ReferentialAction.Cascade); onDelete: ReferentialAction.Cascade);
}); });
migrationBuilder.CreateTable(
name: "Invoices",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
FirstName = table.Column<string>(type: "nvarchar(30)", maxLength: 30, nullable: false),
LastName = table.Column<string>(type: "nvarchar(30)", maxLength: 30, nullable: false),
PhoneNumber = table.Column<string>(type: "nvarchar(max)", nullable: false),
EmailAddress = table.Column<string>(type: "nvarchar(max)", nullable: false),
LinkedAccountId = table.Column<string>(type: "nvarchar(450)", nullable: true),
ShippingAddressId = table.Column<int>(type: "int", nullable: false),
Canceled = table.Column<bool>(type: "bit", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Invoices", x => x.Id);
table.ForeignKey(
name: "FK_Invoices_Addresses_ShippingAddressId",
column: x => x.ShippingAddressId,
principalTable: "Addresses",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_Invoices_AspNetUsers_LinkedAccountId",
column: x => x.LinkedAccountId,
principalTable: "AspNetUsers",
principalColumn: "Id");
});
migrationBuilder.CreateTable(
name: "ProductInvoice",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
ProductId = table.Column<int>(type: "int", nullable: false),
Quantity = table.Column<int>(type: "int", nullable: false),
InvoiceModelId = table.Column<int>(type: "int", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_ProductInvoice", x => x.Id);
table.ForeignKey(
name: "FK_ProductInvoice_Invoices_InvoiceModelId",
column: x => x.InvoiceModelId,
principalTable: "Invoices",
principalColumn: "Id");
table.ForeignKey(
name: "FK_ProductInvoice_Products_ProductId",
column: x => x.ProductId,
principalTable: "Products",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.InsertData(
table: "AspNetRoles",
columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" },
values: new object[,]
{
{ "1b7b9c55-c746-493a-a24f-3d5ca937298e", "0e3b1bc2-f632-4f63-9bea-ac995e2e95a7", "Client", "CLIENT" },
{ "c9e08b20-d8a5-473f-9f52-572eb23c12af", "56321382-1bb3-4dfe-87bf-6919c0791765", "Administrateur", "ADMINISTRATEUR" }
});
migrationBuilder.InsertData( migrationBuilder.InsertData(
table: "AspNetUsers", table: "AspNetUsers",
columns: new[] { "Id", "AccessFailedCount", "ConcurrencyStamp", "Email", "EmailConfirmed", "LockoutEnabled", "LockoutEnd", "NormalizedEmail", "NormalizedUserName", "PasswordHash", "PhoneNumber", "PhoneNumberConfirmed", "SecurityStamp", "TwoFactorEnabled", "UserName" }, columns: new[] { "Id", "AccessFailedCount", "ConcurrencyStamp", "Email", "EmailConfirmed", "FirstName", "LastName", "LockoutEnabled", "LockoutEnd", "NormalizedEmail", "NormalizedUserName", "PasswordHash", "PhoneNumber", "PhoneNumberConfirmed", "SecurityStamp", "TwoFactorEnabled", "UserName" },
values: new object[] { "809dc5fd-c62f-4d93-abe4-275a55aabd33", 0, "f2e55d24-1ab5-441c-87e0-808f32045ab4", "admin@admin.com", false, false, null, "admin@admin.com", "admin", "AQAAAAEAACcQAAAAEKJtYKQ3fXHUA67KkZbzgDtxdg1UlFk3kFeNj3QSm3OfgsSoMtFExAq16W5arTVmRA==", null, false, "56fc4076-5e9d-4bd1-91e7-b53abcc41fda", false, "Admin" }); values: new object[] { "ecf7503a-591c-454e-a824-048e10bd0474", 0, "6259a59c-35df-4662-84e5-a783e653a643", "admin@admin.com", false, "Roger", "Admin", false, null, "ADMIN@ADMIN.COM", "ADMIN", "AQAAAAEAACcQAAAAELHpALZdYcW7KzDcU2ovqwdWsfSx68md+LnjvL5ZgJ2OWuTFwJM3gPzQ1yP3RHCn9g==", null, false, "c43f4d48-f1bb-4a24-8cd3-78422556cf85", false, "Admin" });
migrationBuilder.InsertData( migrationBuilder.InsertData(
table: "Products", table: "Products",
@ -207,6 +299,21 @@ namespace GrossesMitainesAPI.Migrations
{ 17, "Vêtements d'extérieur", "Pour avoir l'air thug en hiver.", 0L, "mitaines2", null, null, 9.45m, 8.99m, 16L, 0L, 0, "Mitaines pas de doigts" } { 17, "Vêtements d'extérieur", "Pour avoir l'air thug en hiver.", 0L, "mitaines2", null, null, 9.45m, 8.99m, 16L, 0L, 0, "Mitaines pas de doigts" }
}); });
migrationBuilder.InsertData(
table: "AspNetUserRoles",
columns: new[] { "RoleId", "UserId" },
values: new object[] { "1b7b9c55-c746-493a-a24f-3d5ca937298e", "ecf7503a-591c-454e-a824-048e10bd0474" });
migrationBuilder.InsertData(
table: "AspNetUserRoles",
columns: new[] { "RoleId", "UserId" },
values: new object[] { "c9e08b20-d8a5-473f-9f52-572eb23c12af", "ecf7503a-591c-454e-a824-048e10bd0474" });
migrationBuilder.CreateIndex(
name: "IX_Addresses_InventoryUserId",
table: "Addresses",
column: "InventoryUserId");
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(
name: "IX_AspNetRoleClaims_RoleId", name: "IX_AspNetRoleClaims_RoleId",
table: "AspNetRoleClaims", table: "AspNetRoleClaims",
@ -245,6 +352,26 @@ namespace GrossesMitainesAPI.Migrations
column: "NormalizedUserName", column: "NormalizedUserName",
unique: true, unique: true,
filter: "[NormalizedUserName] IS NOT NULL"); filter: "[NormalizedUserName] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "IX_Invoices_LinkedAccountId",
table: "Invoices",
column: "LinkedAccountId");
migrationBuilder.CreateIndex(
name: "IX_Invoices_ShippingAddressId",
table: "Invoices",
column: "ShippingAddressId");
migrationBuilder.CreateIndex(
name: "IX_ProductInvoice_InvoiceModelId",
table: "ProductInvoice",
column: "InvoiceModelId");
migrationBuilder.CreateIndex(
name: "IX_ProductInvoice_ProductId",
table: "ProductInvoice",
column: "ProductId");
} }
protected override void Down(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder)
@ -265,11 +392,20 @@ namespace GrossesMitainesAPI.Migrations
name: "AspNetUserTokens"); name: "AspNetUserTokens");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "Products"); name: "ProductInvoice");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "AspNetRoles"); name: "AspNetRoles");
migrationBuilder.DropTable(
name: "Invoices");
migrationBuilder.DropTable(
name: "Products");
migrationBuilder.DropTable(
name: "Addresses");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "AspNetUsers"); name: "AspNetUsers");
} }

View File

@ -41,6 +41,16 @@ namespace GrossesMitainesAPI.Migrations
b.Property<bool>("EmailConfirmed") b.Property<bool>("EmailConfirmed")
.HasColumnType("bit"); .HasColumnType("bit");
b.Property<string>("FirstName")
.IsRequired()
.HasMaxLength(30)
.HasColumnType("nvarchar(30)");
b.Property<string>("LastName")
.IsRequired()
.HasMaxLength(30)
.HasColumnType("nvarchar(30)");
b.Property<bool>("LockoutEnabled") b.Property<bool>("LockoutEnabled")
.HasColumnType("bit"); .HasColumnType("bit");
@ -89,23 +99,143 @@ namespace GrossesMitainesAPI.Migrations
b.HasData( b.HasData(
new new
{ {
Id = "809dc5fd-c62f-4d93-abe4-275a55aabd33", Id = "ecf7503a-591c-454e-a824-048e10bd0474",
AccessFailedCount = 0, AccessFailedCount = 0,
ConcurrencyStamp = "f2e55d24-1ab5-441c-87e0-808f32045ab4", ConcurrencyStamp = "6259a59c-35df-4662-84e5-a783e653a643",
Email = "admin@admin.com", Email = "admin@admin.com",
EmailConfirmed = false, EmailConfirmed = false,
FirstName = "Roger",
LastName = "Admin",
LockoutEnabled = false, LockoutEnabled = false,
NormalizedEmail = "admin@admin.com", NormalizedEmail = "ADMIN@ADMIN.COM",
NormalizedUserName = "admin", NormalizedUserName = "ADMIN",
PasswordHash = "AQAAAAEAACcQAAAAEKJtYKQ3fXHUA67KkZbzgDtxdg1UlFk3kFeNj3QSm3OfgsSoMtFExAq16W5arTVmRA==", PasswordHash = "AQAAAAEAACcQAAAAELHpALZdYcW7KzDcU2ovqwdWsfSx68md+LnjvL5ZgJ2OWuTFwJM3gPzQ1yP3RHCn9g==",
PhoneNumberConfirmed = false, PhoneNumberConfirmed = false,
SecurityStamp = "56fc4076-5e9d-4bd1-91e7-b53abcc41fda", SecurityStamp = "c43f4d48-f1bb-4a24-8cd3-78422556cf85",
TwoFactorEnabled = false, TwoFactorEnabled = false,
UserName = "Admin" UserName = "Admin"
}); });
}); });
modelBuilder.Entity("GrossesMitainesAPI.Models.Product", b => modelBuilder.Entity("GrossesMitainesAPI.Models.AddressModel", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1);
b.Property<string>("Appartment")
.HasColumnType("nvarchar(max)");
b.Property<string>("City")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<int>("CivicNumber")
.HasColumnType("int");
b.Property<string>("Country")
.IsRequired()
.HasMaxLength(30)
.HasColumnType("nvarchar(30)");
b.Property<string>("InventoryUserId")
.HasColumnType("nvarchar(450)");
b.Property<string>("PostalCode")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Province")
.IsRequired()
.HasMaxLength(3)
.HasColumnType("nvarchar(3)");
b.Property<string>("Street")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.HasKey("Id");
b.HasIndex("InventoryUserId");
b.ToTable("Addresses");
});
modelBuilder.Entity("GrossesMitainesAPI.Models.InvoiceModel", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1);
b.Property<bool>("Canceled")
.HasColumnType("bit");
b.Property<string>("EmailAddress")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("FirstName")
.IsRequired()
.HasMaxLength(30)
.HasColumnType("nvarchar(30)");
b.Property<string>("LastName")
.IsRequired()
.HasMaxLength(30)
.HasColumnType("nvarchar(30)");
b.Property<string>("LinkedAccountId")
.HasColumnType("nvarchar(450)");
b.Property<string>("PhoneNumber")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("ShippingAddressId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("LinkedAccountId");
b.HasIndex("ShippingAddressId");
b.ToTable("Invoices");
});
modelBuilder.Entity("GrossesMitainesAPI.Models.InvoiceModel+ProductInvoice", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1);
b.Property<int?>("InvoiceModelId")
.HasColumnType("int");
b.Property<int>("ProductId")
.HasColumnType("int");
b.Property<int>("Quantity")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("InvoiceModelId");
b.HasIndex("ProductId");
b.ToTable("ProductInvoice");
});
modelBuilder.Entity("GrossesMitainesAPI.Models.ProductModel", b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
@ -423,6 +553,22 @@ namespace GrossesMitainesAPI.Migrations
.HasFilter("[NormalizedName] IS NOT NULL"); .HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("AspNetRoles", (string)null); b.ToTable("AspNetRoles", (string)null);
b.HasData(
new
{
Id = "c9e08b20-d8a5-473f-9f52-572eb23c12af",
ConcurrencyStamp = "56321382-1bb3-4dfe-87bf-6919c0791765",
Name = "Administrateur",
NormalizedName = "ADMINISTRATEUR"
},
new
{
Id = "1b7b9c55-c746-493a-a24f-3d5ca937298e",
ConcurrencyStamp = "0e3b1bc2-f632-4f63-9bea-ac995e2e95a7",
Name = "Client",
NormalizedName = "CLIENT"
});
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
@ -510,6 +656,18 @@ namespace GrossesMitainesAPI.Migrations
b.HasIndex("RoleId"); b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null); b.ToTable("AspNetUserRoles", (string)null);
b.HasData(
new
{
UserId = "ecf7503a-591c-454e-a824-048e10bd0474",
RoleId = "c9e08b20-d8a5-473f-9f52-572eb23c12af"
},
new
{
UserId = "ecf7503a-591c-454e-a824-048e10bd0474",
RoleId = "1b7b9c55-c746-493a-a24f-3d5ca937298e"
});
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
@ -531,6 +689,45 @@ namespace GrossesMitainesAPI.Migrations
b.ToTable("AspNetUserTokens", (string)null); b.ToTable("AspNetUserTokens", (string)null);
}); });
modelBuilder.Entity("GrossesMitainesAPI.Models.AddressModel", b =>
{
b.HasOne("GrossesMitainesAPI.Data.InventoryUser", null)
.WithMany("Adresses")
.HasForeignKey("InventoryUserId");
});
modelBuilder.Entity("GrossesMitainesAPI.Models.InvoiceModel", b =>
{
b.HasOne("GrossesMitainesAPI.Data.InventoryUser", "LinkedAccount")
.WithMany()
.HasForeignKey("LinkedAccountId");
b.HasOne("GrossesMitainesAPI.Models.AddressModel", "ShippingAddress")
.WithMany()
.HasForeignKey("ShippingAddressId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("LinkedAccount");
b.Navigation("ShippingAddress");
});
modelBuilder.Entity("GrossesMitainesAPI.Models.InvoiceModel+ProductInvoice", b =>
{
b.HasOne("GrossesMitainesAPI.Models.InvoiceModel", null)
.WithMany("Products")
.HasForeignKey("InvoiceModelId");
b.HasOne("GrossesMitainesAPI.Models.ProductModel", "Product")
.WithMany()
.HasForeignKey("ProductId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Product");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{ {
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
@ -581,6 +778,16 @@ namespace GrossesMitainesAPI.Migrations
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
.IsRequired(); .IsRequired();
}); });
modelBuilder.Entity("GrossesMitainesAPI.Data.InventoryUser", b =>
{
b.Navigation("Adresses");
});
modelBuilder.Entity("GrossesMitainesAPI.Models.InvoiceModel", b =>
{
b.Navigation("Products");
});
#pragma warning restore 612, 618 #pragma warning restore 612, 618
} }
} }

View File

@ -0,0 +1,22 @@
using System.ComponentModel.DataAnnotations;
namespace GrossesMitainesAPI.Models;
public class AddressModel {
[Key]
public int Id { get; set; }
[Required, Range(1, int.MaxValue)]
public int CivicNumber { get; set; }
public string? Appartment { get; set; }
[Required, MinLength(3), MaxLength(50)]
public string Street { get; set; }
[Required, MinLength(4), MaxLength(50)]
public string City { get; set; }
[Required, MaxLength(3)]
public string Province { get; set; }
[Required, MinLength(4), MaxLength(30)]
public string Country { get; set; }
// Source pour regex: https://stackoverflow.com/questions/15774555/efficient-regex-for-canadian-postal-code-function
[Required, RegularExpression(@"/^[ABCEGHJ-NPRSTVXY]\d[ABCEGHJ-NPRSTV-Z][ -]?\d[ABCEGHJ-NPRSTV-Z]\d$/i")]
public string PostalCode { get; set; }
}

View File

@ -1,12 +1,36 @@
using GrossesMitainesAPI.Data; using GrossesMitainesAPI.Data;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace GrossesMitainesAPI.Models; namespace GrossesMitainesAPI.Models;
public class InvoiceModel { public class InvoiceModel {
public InventoryUser Account { get; set; } public class ProductInvoice {
public DateTime PurchaseDate { get; set; } [Key]
public Dictionary<Product, int> Products { get; set; } public int Id { get; set; }
public InventoryUser.Address BillingAddress { get; set; } public ProductModel Product { get; set; }
public InventoryUser.Address ShippingAddress { get; set; } public int Quantity { get; set; }
}
[Key]
public int Id { get; set; }
[Required, MinLength(2), MaxLength(30)]
public string FirstName { get; set; }
[Required, MinLength(1), MaxLength(30)]
public string LastName { get; set; }
[Required, Phone]
public string PhoneNumber { get; set; }
[Required, EmailAddress]
public string EmailAddress { get; set; }
public InventoryUser? LinkedAccount { get; set; }
public DateTime PurchaseDate { get; } = DateTime.Now;
[Required]
public List<ProductInvoice> Products { get; set; }
//[Required, Column("BillingAddress")]
//public AddressModel BillingAddress { get; set; }
[Required]
public AddressModel ShippingAddress { get; set; }
public bool Canceled { get; set; } = false;
} }

View File

@ -4,7 +4,7 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace GrossesMitainesAPI.Models; namespace GrossesMitainesAPI.Models;
public class Product { public class ProductModel {
public enum States { public enum States {
Available, Available,
BackOrder, BackOrder,

View File

@ -1,4 +1,4 @@
using static GrossesMitainesAPI.Models.Product; using static GrossesMitainesAPI.Models.ProductModel;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
namespace GrossesMitainesAPI.Models; namespace GrossesMitainesAPI.Models;
@ -13,7 +13,7 @@ public class ProductViewModel {
public States Status { get; set; } = States.Available; public States Status { get; set; } = States.Available;
public string? ImageName { get; set; } public string? ImageName { get; set; }
public ProductViewModel(Product prod) { public ProductViewModel(ProductModel prod) {
this.Id = prod.Id; this.Id = prod.Id;
this.Title = prod.Title; this.Title = prod.Title;
this.Category = prod.Category; this.Category = prod.Category;

View File

@ -1,20 +1,27 @@
using GrossesMitainesAPI.Data; using GrossesMitainesAPI.Data;
using Microsoft.AspNetCore.Identity;
namespace GrossesMitainesAPI.Models; namespace GrossesMitainesAPI.Models;
public class ReturnUserViewModel { public class ReturnUserViewModel {
public string Username { get; set; } public string Username { get; set; }
public string Email { get; set; } public string Email { get; set; }
public string Phone { get; set; } public string Phone { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public bool EmailConfirmed { get; set; } public bool EmailConfirmed { get; set; }
public bool PhoneConfirmed { get; set; } public bool PhoneConfirmed { get; set; }
public bool TwoFactorEnable { get; set; } public bool TwoFactorEnable { get; set; }
public string Role { get; set; }
public ReturnUserViewModel(InventoryUser user) { public ReturnUserViewModel(InventoryUser user, string role) {
Username = user.UserName; Username = user.UserName;
Email = user.Email; Email = user.Email;
Phone = user.PhoneNumber; Phone = user.PhoneNumber;
EmailConfirmed = user.EmailConfirmed; EmailConfirmed = user.EmailConfirmed;
PhoneConfirmed = user.PhoneNumberConfirmed; PhoneConfirmed = user.PhoneNumberConfirmed;
TwoFactorEnable = user.TwoFactorEnabled; TwoFactorEnable = user.TwoFactorEnabled;
FirstName = user.FirstName;
LastName = user.LastName;
Role = role;
} }
} }

View File

@ -23,8 +23,6 @@ builder.Services.AddCors(options => {
}); });
builder.Services.AddControllers(); builder.Services.AddControllers();
builder.Services.AddAuthorization();
builder.Services.AddAuthentication().AddIdentityCookies();
// Source: https://github.com/dotnet/aspnetcore/issues/9039 // Source: https://github.com/dotnet/aspnetcore/issues/9039
builder.Services.ConfigureApplicationCookie(o => { builder.Services.ConfigureApplicationCookie(o => {
@ -48,6 +46,9 @@ builder.Services.AddIdentityCore<InventoryUser>()
.AddRoles<IdentityRole>() .AddRoles<IdentityRole>()
.AddEntityFrameworkStores<InventoryContext>() .AddEntityFrameworkStores<InventoryContext>()
.AddSignInManager(); .AddSignInManager();
builder.Services.AddAuthorization();
builder.Services.AddAuthentication().AddIdentityCookies();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer(); builder.Services.AddEndpointsApiExplorer();

View File

@ -19,7 +19,7 @@ public class DatabaseCacheService {
#endregion #endregion
#region Fields #region Fields
private Product[] _cache = new Product[1]; private ProductModel[] _cache = new ProductModel[1];
private Dictionary<uint, uint> _hits = new(); private Dictionary<uint, uint> _hits = new();
private bool _ok = false, _needUpd = true; private bool _ok = false, _needUpd = true;
private PeriodicTimer _timer = new PeriodicTimer(TimeSpan.FromSeconds(10)); private PeriodicTimer _timer = new PeriodicTimer(TimeSpan.FromSeconds(10));
@ -51,7 +51,7 @@ public class DatabaseCacheService {
} }
private bool UpdateCache() { private bool UpdateCache() {
try { try {
Product[] prods; ProductModel[] prods;
using (var scope = _contextFactory.CreateScope()) { using (var scope = _contextFactory.CreateScope()) {
var db = scope.ServiceProvider.GetRequiredService<InventoryContext>(); var db = scope.ServiceProvider.GetRequiredService<InventoryContext>();
prods = db.Products.ToArray(); prods = db.Products.ToArray();
@ -75,7 +75,7 @@ public class DatabaseCacheService {
List<uint> ids = hits.Keys.ToList(); List<uint> ids = hits.Keys.ToList();
using (var scope = _contextFactory.CreateScope()) { using (var scope = _contextFactory.CreateScope()) {
var db = scope.ServiceProvider.GetRequiredService<InventoryContext>(); var db = scope.ServiceProvider.GetRequiredService<InventoryContext>();
List<Product> lst = db.Products.Where(x => ids.Contains((uint)x.Id)).ToList(); List<ProductModel> lst = db.Products.Where(x => ids.Contains((uint)x.Id)).ToList();
foreach (var x in hits) foreach (var x in hits)
lst.First(x => x.Id == x.Id).Hits += x.Value; lst.First(x => x.Id == x.Id).Hits += x.Value;
db.UpdateRange(lst); db.UpdateRange(lst);
@ -101,13 +101,13 @@ public class DatabaseCacheService {
} }
} }
public Product[]? GetCacheCopy() { public ProductModel[]? GetCacheCopy() {
if (!_ok) if (!_ok)
return null; return null;
Product[] copy; ProductModel[] copy;
try { try {
lock (_cache) { lock (_cache) {
copy = new Product[_cache.Length]; copy = new ProductModel[_cache.Length];
_cache.CopyTo(copy, 0); _cache.CopyTo(copy, 0);
} }
} catch (Exception e) { } catch (Exception e) {
@ -116,7 +116,7 @@ public class DatabaseCacheService {
} }
return copy; return copy;
} }
public IQueryable<Product> queryCache() { public IQueryable<ProductModel> queryCache() {
if (!_ok) if (!_ok)
return null; return null;
try { try {

View File

@ -3,7 +3,7 @@ import { Button } from "react-bootstrap";
const Login = () => { const Login = () => {
const [adresse, setAdresse] = useState(""); const [email, setEmail] = useState("");
const [password, setPassword] = useState(""); const [password, setPassword] = useState("");
const [returnmess, returnMessage] = useState(""); const [returnmess, returnMessage] = useState("");
@ -17,14 +17,13 @@ const Login = () => {
'Accept': 'text/json', 'Accept': 'text/json',
'Content-Type': 'text/json' 'Content-Type': 'text/json'
}, },
//body: JSON.stringify({ username, password }) body: JSON.stringify({ email, password })
}); });
// Partie de display d'erreur ou de redirection (faudrait checker pour se faire un state de connexion avec un cookie pour react).
if (response.status === 200) { if (response.status === 200) {
var rep = await response.json(); var rep = await response.json();
if (rep.succeeded === true) { if (rep.succeeded === true) {
const confirm = await fetch(`https://localhost:7292/api/Login`, { const confirm = await fetch(`https://localhost:7292/api/WhoAmI`, {
method: 'GET', method: 'GET',
credentials: 'include', credentials: 'include',
headers: { headers: {
@ -32,6 +31,7 @@ const Login = () => {
'Content-Type': 'text/json' 'Content-Type': 'text/json'
} }
}); });
// TODO: Redirection vers Home et ajout du cookie pour React pour le layout.
console.log(await confirm.json()) console.log(await confirm.json())
returnMessage("WOOHOO!"); returnMessage("WOOHOO!");
} }
@ -52,8 +52,8 @@ const Login = () => {
<label>Adresse courriel </label> <label>Adresse courriel </label>
<input className="form-control form-input" type='text' <input className="form-control form-input" type='text'
placeholder="Adresse..." placeholder="Adresse..."
value={adresse} value={email}
onChange={(e) => setAdresse(e.target.value)} /> onChange={(e) => setEmail(e.target.value)} />
</div> </div>
<div className="form-group"> <div className="form-group">
<label>Mot de passe: </label> <label>Mot de passe: </label>