From 2ea5ca4ce16b77f9accd46bde75fb26861232ecb Mon Sep 17 00:00:00 2001 From: Thomas Andersen Date: Thu, 19 Oct 2023 12:29:44 +0200 Subject: [PATCH 001/112] SW-47 * Setup api --- Controllers/ItemController.cs | 6 - Controllers/OrderController.cs | 123 +++++++++ Controllers/UserController.cs | 123 +++++++++ Controllers/WinesController.cs | 119 -------- Dtos/AuthenticationResultDto.cs | 6 +- Enums/Roles.cs | 8 + .../20231018112535_Updated data.Designer.cs | 175 ++++++++++++ Migrations/20231018112535_Updated data.cs | 260 ++++++++++++++++++ Migrations/DBContextModelSnapshot.cs | 128 +++++++-- Models/Authentication/Company.cs | 7 - Models/Authentication/User.cs | 14 +- Models/Common/Item.cs | 9 - Models/Common/Wine.cs | 7 - Models/DBContext.cs | 8 +- Models/Item/Chocolate.cs | 6 + Models/Item/Item.cs | 11 + Models/Item/Liquor.cs | 6 + Models/Item/Wine.cs | 6 + Models/Order/CustomerPurchaseOrder.cs | 6 + Models/Order/InboundOrder.cs | 7 + Models/Order/Order.cs | 7 + Models/Schedule/Schedule.cs | 6 + Models/Supplier/Supplier.cs | 7 + Services/AuthenticationService.cs | 3 +- Services/IAuthenticationService.cs | 4 +- 25 files changed, 874 insertions(+), 188 deletions(-) create mode 100644 Controllers/OrderController.cs create mode 100644 Controllers/UserController.cs delete mode 100644 Controllers/WinesController.cs create mode 100644 Enums/Roles.cs create mode 100644 Migrations/20231018112535_Updated data.Designer.cs create mode 100644 Migrations/20231018112535_Updated data.cs delete mode 100644 Models/Authentication/Company.cs delete mode 100644 Models/Common/Item.cs delete mode 100644 Models/Common/Wine.cs create mode 100644 Models/Item/Chocolate.cs create mode 100644 Models/Item/Item.cs create mode 100644 Models/Item/Liquor.cs create mode 100644 Models/Item/Wine.cs create mode 100644 Models/Order/CustomerPurchaseOrder.cs create mode 100644 Models/Order/InboundOrder.cs create mode 100644 Models/Order/Order.cs create mode 100644 Models/Schedule/Schedule.cs create mode 100644 Models/Supplier/Supplier.cs diff --git a/Controllers/ItemController.cs b/Controllers/ItemController.cs index 69aadc9..2f7dbb0 100644 --- a/Controllers/ItemController.cs +++ b/Controllers/ItemController.cs @@ -1,12 +1,6 @@ -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 API.Models; -using API.Models.Common; namespace API.Controllers { diff --git a/Controllers/OrderController.cs b/Controllers/OrderController.cs new file mode 100644 index 0000000..78e5b89 --- /dev/null +++ b/Controllers/OrderController.cs @@ -0,0 +1,123 @@ +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 API.Models; + +namespace API.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class OrderController : ControllerBase + { + private readonly DBContext _context; + + public OrderController(DBContext context) + { + _context = context; + } + + // GET: api/Order + [HttpGet] + public async Task>> GetOrder() + { + if (_context.Order == null) + { + return NotFound(); + } + return await _context.Order.ToListAsync(); + } + + // GET: api/Order/5 + [HttpGet("{id}")] + public async Task> GetOrder(int id) + { + if (_context.Order == null) + { + return NotFound(); + } + var order = await _context.Order.FindAsync(id); + + if (order == null) + { + return NotFound(); + } + + return order; + } + + // PUT: api/Order/5 + // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754 + [HttpPut("{id}")] + public async Task PutOrder(int id, Order order) + { + if (id != order.Id) + { + return BadRequest(); + } + + _context.Entry(order).State = EntityState.Modified; + + try + { + await _context.SaveChangesAsync(); + } + catch (DbUpdateConcurrencyException) + { + if (!OrderExists(id)) + { + return NotFound(); + } + else + { + throw; + } + } + + return NoContent(); + } + + // POST: api/Order + // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754 + [HttpPost] + public async Task> PostOrder(Order order) + { + if (_context.Order == null) + { + return Problem("Entity set 'DBContext.Order' is null."); + } + _context.Order.Add(order); + await _context.SaveChangesAsync(); + + return CreatedAtAction("GetOrder", new { id = order.Id }, order); + } + + // DELETE: api/Order/5 + [HttpDelete("{id}")] + public async Task DeleteOrder(int id) + { + if (_context.Order == null) + { + return NotFound(); + } + var order = await _context.Order.FindAsync(id); + if (order == null) + { + return NotFound(); + } + + _context.Order.Remove(order); + await _context.SaveChangesAsync(); + + return NoContent(); + } + + private bool OrderExists(int id) + { + return (_context.Order?.Any(e => e.Id == id)).GetValueOrDefault(); + } + } +} diff --git a/Controllers/UserController.cs b/Controllers/UserController.cs new file mode 100644 index 0000000..5c5d04b --- /dev/null +++ b/Controllers/UserController.cs @@ -0,0 +1,123 @@ +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 API.Models; + +namespace API.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class UserController : ControllerBase + { + private readonly DBContext _context; + + public UserController(DBContext context) + { + _context = context; + } + + // GET: api/User + [HttpGet] + public async Task>> GetUsers() + { + if (_context.Users == null) + { + return NotFound(); + } + return await _context.Users.ToListAsync(); + } + + // GET: api/User/5 + [HttpGet("{id}")] + public async Task> GetUser(int id) + { + if (_context.Users == null) + { + return NotFound(); + } + var user = await _context.Users.FindAsync(id); + + if (user == null) + { + return NotFound(); + } + + return user; + } + + // PUT: api/User/5 + // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754 + [HttpPut("{id}")] + public async Task PutUser(int id, User user) + { + if (id != user.Id) + { + return BadRequest(); + } + + _context.Entry(user).State = EntityState.Modified; + + try + { + await _context.SaveChangesAsync(); + } + catch (DbUpdateConcurrencyException) + { + if (!UserExists(id)) + { + return NotFound(); + } + else + { + throw; + } + } + + return NoContent(); + } + + // POST: api/User + // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754 + [HttpPost] + public async Task> PostUser(User user) + { + if (_context.Users == null) + { + return Problem("Entity set 'DBContext.Users' is null."); + } + _context.Users.Add(user); + await _context.SaveChangesAsync(); + + return CreatedAtAction("GetUser", new { id = user.Id }, user); + } + + // DELETE: api/User/5 + [HttpDelete("{id}")] + public async Task DeleteUser(int id) + { + if (_context.Users == null) + { + return NotFound(); + } + var user = await _context.Users.FindAsync(id); + if (user == null) + { + return NotFound(); + } + + _context.Users.Remove(user); + await _context.SaveChangesAsync(); + + return NoContent(); + } + + private bool UserExists(int id) + { + return (_context.Users?.Any(e => e.Id == id)).GetValueOrDefault(); + } + } +} diff --git a/Controllers/WinesController.cs b/Controllers/WinesController.cs deleted file mode 100644 index 8fedee5..0000000 --- a/Controllers/WinesController.cs +++ /dev/null @@ -1,119 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using API.Models; -using API.Models.Common; - -namespace API.Controllers -{ - [Route("api/[controller]")] - [ApiController] - public class WinesController : ControllerBase - { - private readonly DBContext _context; - - public WinesController(DBContext context) - { - _context = context; - } - - // GET: api/Wines - [HttpGet] - public async Task>> GetWines() - { - if (_context.Wines == null) - { - return NotFound(); - } - return await _context.Wines.ToListAsync(); - } - - // GET: api/Wines/5 - [HttpGet("{id}")] - public async Task> GetWine(int? id) - { - if (_context.Wines == null) - { - return NotFound(); - } - var wine = await _context.Wines.FindAsync(id); - - if (wine == null) - { - return NotFound(); - } - - return wine; - } - - // PUT: api/Wines/5 - // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754 - [HttpPut("{id}")] - public async Task PutWine(int? id, Wine wine) - { - if (id != wine.Id) - { - return BadRequest(); - } - - _context.Entry(wine).State = EntityState.Modified; - - try - { - await _context.SaveChangesAsync(); - } - catch (DbUpdateConcurrencyException) - { - if (!WineExists(id)) - { - return NotFound(); - } - else - { - throw; - } - } - - return NoContent(); - } - - // POST: api/Wines - // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754 - [HttpPost] - public async Task> PostWine(Wine wine) - { - if (_context.Wines == null) - { - return Problem("Entity set 'DBContext.Wines' is null."); - } - _context.Wines.Add(wine); - await _context.SaveChangesAsync(); - - return CreatedAtAction("GetWine", new { id = wine.Id }, wine); - } - - // DELETE: api/Wines/5 - [HttpDelete("{id}")] - public async Task DeleteWine(int? id) - { - if (_context.Wines == null) - { - return NotFound(); - } - var wine = await _context.Wines.FindAsync(id); - if (wine == null) - { - return NotFound(); - } - - _context.Wines.Remove(wine); - await _context.SaveChangesAsync(); - - return NoContent(); - } - - private bool WineExists(int? id) - { - return (_context.Wines?.Any(e => e.Id == id)).GetValueOrDefault(); - } - } -} diff --git a/Dtos/AuthenticationResultDto.cs b/Dtos/AuthenticationResultDto.cs index f472e35..33f7773 100644 --- a/Dtos/AuthenticationResultDto.cs +++ b/Dtos/AuthenticationResultDto.cs @@ -1,3 +1,5 @@ +using API.Enums; + namespace API.Dtos; public class AuthenticationResultDto @@ -5,5 +7,5 @@ public class AuthenticationResultDto public string Token { get; set; } public DateTime TokenExpiration { get; set; } - public string UserRole { get; set; } -} \ No newline at end of file + public Roles UserRole { get; set; } +} diff --git a/Enums/Roles.cs b/Enums/Roles.cs new file mode 100644 index 0000000..ee336cd --- /dev/null +++ b/Enums/Roles.cs @@ -0,0 +1,8 @@ +namespace API.Enums; + +public enum Roles +{ + Customer, + Employee, + Admin +} diff --git a/Migrations/20231018112535_Updated data.Designer.cs b/Migrations/20231018112535_Updated data.Designer.cs new file mode 100644 index 0000000..41cba28 --- /dev/null +++ b/Migrations/20231018112535_Updated data.Designer.cs @@ -0,0 +1,175 @@ +// +using System; +using API.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace API.Migrations +{ + [DbContext(typeof(DBContext))] + [Migration("20231018112535_Updated data")] + partial class Updateddata + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("API.Models.Item", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("BarCode") + .HasColumnType("longtext"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrderId") + .HasColumnType("int"); + + b.Property("Price") + .HasColumnType("float"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.Property("SupplierId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("OrderId"); + + b.HasIndex("SupplierId"); + + b.ToTable("Items"); + + b.HasDiscriminator("Discriminator").HasValue("Item"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("API.Models.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Order"); + }); + + modelBuilder.Entity("API.Models.Supplier", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Suppliers"); + }); + + modelBuilder.Entity("API.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Email") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Role") + .HasColumnType("int"); + + b.Property("Token") + .HasColumnType("longtext"); + + b.Property("TokenExpiration") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("API.Models.Chocolate", b => + { + b.HasBaseType("API.Models.Item"); + + b.HasDiscriminator().HasValue("Chocolate"); + }); + + modelBuilder.Entity("API.Models.Liquor", b => + { + b.HasBaseType("API.Models.Item"); + + b.Property("LiquorType") + .HasColumnType("longtext"); + + b.HasDiscriminator().HasValue("Liquor"); + }); + + modelBuilder.Entity("API.Models.Wine", b => + { + b.HasBaseType("API.Models.Item"); + + b.HasDiscriminator().HasValue("Wine"); + }); + + modelBuilder.Entity("API.Models.Item", b => + { + b.HasOne("API.Models.Order", null) + .WithMany("Items") + .HasForeignKey("OrderId"); + + b.HasOne("API.Models.Supplier", null) + .WithMany("Items") + .HasForeignKey("SupplierId"); + }); + + modelBuilder.Entity("API.Models.Order", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("API.Models.Supplier", b => + { + b.Navigation("Items"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Migrations/20231018112535_Updated data.cs b/Migrations/20231018112535_Updated data.cs new file mode 100644 index 0000000..5a45eef --- /dev/null +++ b/Migrations/20231018112535_Updated data.cs @@ -0,0 +1,260 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace API.Migrations +{ + /// + public partial class Updateddata : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Wines"); + + migrationBuilder.RenameColumn( + name: "WineType", + table: "Items", + newName: "LiquorType"); + + migrationBuilder.AlterColumn( + name: "Role", + table: "Users", + type: "int", + nullable: false, + defaultValue: 0, + oldClrType: typeof(string), + oldType: "longtext", + oldNullable: true) + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.UpdateData( + table: "Users", + keyColumn: "Password", + keyValue: null, + column: "Password", + value: ""); + + migrationBuilder.AlterColumn( + name: "Password", + table: "Users", + type: "longtext", + nullable: false, + oldClrType: typeof(string), + oldType: "longtext", + oldNullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.UpdateData( + table: "Users", + keyColumn: "LastName", + keyValue: null, + column: "LastName", + value: ""); + + migrationBuilder.AlterColumn( + name: "LastName", + table: "Users", + type: "longtext", + nullable: false, + oldClrType: typeof(string), + oldType: "longtext", + oldNullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "BarCode", + table: "Items", + type: "longtext", + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "Discriminator", + table: "Items", + type: "longtext", + nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "ExpirationDate", + table: "Items", + type: "datetime(6)", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.AddColumn( + name: "OrderId", + table: "Items", + type: "int", + nullable: true); + + migrationBuilder.AddColumn( + name: "Price", + table: "Items", + type: "float", + nullable: false, + defaultValue: 0f); + + migrationBuilder.AddColumn( + name: "SupplierId", + table: "Items", + type: "int", + nullable: true); + + migrationBuilder.CreateTable( + name: "Order", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_Order", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Suppliers", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_Suppliers", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_Items_OrderId", + table: "Items", + column: "OrderId"); + + migrationBuilder.CreateIndex( + name: "IX_Items_SupplierId", + table: "Items", + column: "SupplierId"); + + migrationBuilder.AddForeignKey( + name: "FK_Items_Order_OrderId", + table: "Items", + column: "OrderId", + principalTable: "Order", + principalColumn: "Id"); + + migrationBuilder.AddForeignKey( + name: "FK_Items_Suppliers_SupplierId", + table: "Items", + column: "SupplierId", + principalTable: "Suppliers", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Items_Order_OrderId", + table: "Items"); + + migrationBuilder.DropForeignKey( + name: "FK_Items_Suppliers_SupplierId", + table: "Items"); + + migrationBuilder.DropTable( + name: "Order"); + + migrationBuilder.DropTable( + name: "Suppliers"); + + migrationBuilder.DropIndex( + name: "IX_Items_OrderId", + table: "Items"); + + migrationBuilder.DropIndex( + name: "IX_Items_SupplierId", + table: "Items"); + + migrationBuilder.DropColumn( + name: "BarCode", + table: "Items"); + + migrationBuilder.DropColumn( + name: "Discriminator", + table: "Items"); + + migrationBuilder.DropColumn( + name: "ExpirationDate", + table: "Items"); + + migrationBuilder.DropColumn( + name: "OrderId", + table: "Items"); + + migrationBuilder.DropColumn( + name: "Price", + table: "Items"); + + migrationBuilder.DropColumn( + name: "SupplierId", + table: "Items"); + + migrationBuilder.RenameColumn( + name: "LiquorType", + table: "Items", + newName: "WineType"); + + migrationBuilder.AlterColumn( + name: "Role", + table: "Users", + type: "longtext", + nullable: true, + oldClrType: typeof(int), + oldType: "int") + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "Password", + table: "Users", + type: "longtext", + nullable: true, + oldClrType: typeof(string), + oldType: "longtext") + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "LastName", + table: "Users", + type: "longtext", + nullable: true, + oldClrType: typeof(string), + oldType: "longtext") + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Wines", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Name = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_Wines", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + } + } +} diff --git a/Migrations/DBContextModelSnapshot.cs b/Migrations/DBContextModelSnapshot.cs index 36da01c..f9a1987 100644 --- a/Migrations/DBContextModelSnapshot.cs +++ b/Migrations/DBContextModelSnapshot.cs @@ -19,9 +19,76 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasAnnotation("ProductVersion", "7.0.11") .HasAnnotation("Relational:MaxIdentifierLength", 64); - modelBuilder.Entity("API.Models.Authentication.User", b => + modelBuilder.Entity("API.Models.Item", b => { - b.Property("Id") + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("BarCode") + .HasColumnType("longtext"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrderId") + .HasColumnType("int"); + + b.Property("Price") + .HasColumnType("float"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.Property("SupplierId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("OrderId"); + + b.HasIndex("SupplierId"); + + b.ToTable("Items"); + + b.HasDiscriminator("Discriminator").HasValue("Item"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("API.Models.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Order"); + }); + + modelBuilder.Entity("API.Models.Supplier", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Suppliers"); + }); + + modelBuilder.Entity("API.Models.User", b => + { + b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int"); @@ -34,13 +101,15 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("longtext"); b.Property("LastName") + .IsRequired() .HasColumnType("longtext"); b.Property("Password") + .IsRequired() .HasColumnType("longtext"); - b.Property("Role") - .HasColumnType("longtext"); + b.Property("Role") + .HasColumnType("int"); b.Property("Token") .HasColumnType("longtext"); @@ -53,40 +122,49 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("Users"); }); - modelBuilder.Entity("API.Models.Common.Item", b => + modelBuilder.Entity("API.Models.Chocolate", b => { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); + b.HasBaseType("API.Models.Item"); - b.Property("Name") - .IsRequired() - .HasColumnType("longtext"); + b.HasDiscriminator().HasValue("Chocolate"); + }); - b.Property("Quantity") - .HasColumnType("int"); + modelBuilder.Entity("API.Models.Liquor", b => + { + b.HasBaseType("API.Models.Item"); - b.Property("WineType") + b.Property("LiquorType") .HasColumnType("longtext"); - b.HasKey("Id"); + b.HasDiscriminator().HasValue("Liquor"); + }); - b.ToTable("Items"); + modelBuilder.Entity("API.Models.Wine", b => + { + b.HasBaseType("API.Models.Item"); + + b.HasDiscriminator().HasValue("Wine"); }); - modelBuilder.Entity("API.Models.Common.Wine", b => + modelBuilder.Entity("API.Models.Item", b => { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); + b.HasOne("API.Models.Order", null) + .WithMany("Items") + .HasForeignKey("OrderId"); - b.Property("Name") - .IsRequired() - .HasColumnType("longtext"); + b.HasOne("API.Models.Supplier", null) + .WithMany("Items") + .HasForeignKey("SupplierId"); + }); - b.HasKey("Id"); + modelBuilder.Entity("API.Models.Order", b => + { + b.Navigation("Items"); + }); - b.ToTable("Wines"); + modelBuilder.Entity("API.Models.Supplier", b => + { + b.Navigation("Items"); }); #pragma warning restore 612, 618 } diff --git a/Models/Authentication/Company.cs b/Models/Authentication/Company.cs deleted file mode 100644 index ee67eb0..0000000 --- a/Models/Authentication/Company.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace API.Models.Authentication; - -public class Company -{ - public Int32 Id { get; set; } - public string Name { get; set; } -} \ No newline at end of file diff --git a/Models/Authentication/User.cs b/Models/Authentication/User.cs index c4b83de..b73b5c2 100644 --- a/Models/Authentication/User.cs +++ b/Models/Authentication/User.cs @@ -1,13 +1,15 @@ -namespace API.Models.Authentication; +using API.Enums; + +namespace API.Models; public class User { - public Int32? Id { get; set; } + public int Id { get; set; } public string FirstName { get; set; } - public string? LastName { get; set; } + public string LastName { get; set; } public string Email { get; set; } - public string? Password { get; set; } - public string? Role { get; set; } + public string Password { get; set; } + public Roles Role { get; set; } public string? Token { get; set; } public DateTime? TokenExpiration { get; set; } -} \ No newline at end of file +} diff --git a/Models/Common/Item.cs b/Models/Common/Item.cs deleted file mode 100644 index 89c60dc..0000000 --- a/Models/Common/Item.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace API.Models.Common; - -public class Item -{ - public Int32 Id { get; set; } - public string Name { get; set; } - public string? WineType { get; set; } - public int Quantity { get; set; } -} diff --git a/Models/Common/Wine.cs b/Models/Common/Wine.cs deleted file mode 100644 index 9d59871..0000000 --- a/Models/Common/Wine.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace API.Models.Common; - -public class Wine -{ - public Int32 Id { get; set; } - public string Name { get; set; } -} diff --git a/Models/DBContext.cs b/Models/DBContext.cs index 05f073a..9ff0f45 100644 --- a/Models/DBContext.cs +++ b/Models/DBContext.cs @@ -1,5 +1,3 @@ -using API.Models.Authentication; -using API.Models.Common; using Microsoft.EntityFrameworkCore; namespace API.Models; @@ -10,7 +8,11 @@ public DBContext(DbContextOptions options) : base(options) { } - public DbSet Wines { get; set; } = null!; public DbSet Users { get; set; } = null!; + public DbSet Liquors { get; set; } = null!; + public DbSet Chocolates { get; set; } = null!; public DbSet Items { get; set; } = null!; + public DbSet Wines { get; set; } = null!; + public DbSet Suppliers { get; set; } = null!; + public DbSet Order { get; set; } = null!; } diff --git a/Models/Item/Chocolate.cs b/Models/Item/Chocolate.cs new file mode 100644 index 0000000..3da64a1 --- /dev/null +++ b/Models/Item/Chocolate.cs @@ -0,0 +1,6 @@ +namespace API.Models; + +public class Chocolate: Item +{ + +} diff --git a/Models/Item/Item.cs b/Models/Item/Item.cs new file mode 100644 index 0000000..cf9e063 --- /dev/null +++ b/Models/Item/Item.cs @@ -0,0 +1,11 @@ +namespace API.Models; + +public class Item +{ + public int Id { get; set; } + public string Name { get; set; } + public string? BarCode { get; set; } + public int Quantity { get; set; } + public float Price { get; set; } + public DateTime ExpirationDate { get; set; } +} diff --git a/Models/Item/Liquor.cs b/Models/Item/Liquor.cs new file mode 100644 index 0000000..ef3d181 --- /dev/null +++ b/Models/Item/Liquor.cs @@ -0,0 +1,6 @@ +namespace API.Models; + +public class Liquor: Item +{ + public string? LiquorType { get; set; } +} diff --git a/Models/Item/Wine.cs b/Models/Item/Wine.cs new file mode 100644 index 0000000..eb48e0a --- /dev/null +++ b/Models/Item/Wine.cs @@ -0,0 +1,6 @@ +namespace API.Models; + +public class Wine : Item +{ + +} diff --git a/Models/Order/CustomerPurchaseOrder.cs b/Models/Order/CustomerPurchaseOrder.cs new file mode 100644 index 0000000..b76b7dd --- /dev/null +++ b/Models/Order/CustomerPurchaseOrder.cs @@ -0,0 +1,6 @@ +namespace API.Models; + +public class CustomerPurchaseOrder: Order +{ + +} diff --git a/Models/Order/InboundOrder.cs b/Models/Order/InboundOrder.cs new file mode 100644 index 0000000..a02e097 --- /dev/null +++ b/Models/Order/InboundOrder.cs @@ -0,0 +1,7 @@ +namespace API.Models; + +public class InboundOrder: Order +{ + public int Id { get; set; } + public Supplier Supplier { get; set; } +} diff --git a/Models/Order/Order.cs b/Models/Order/Order.cs new file mode 100644 index 0000000..0fdc464 --- /dev/null +++ b/Models/Order/Order.cs @@ -0,0 +1,7 @@ +namespace API.Models; + +public class Order +{ + public int Id { get; set; } + public Item[]? Items { get; set; } +} diff --git a/Models/Schedule/Schedule.cs b/Models/Schedule/Schedule.cs new file mode 100644 index 0000000..ada9bca --- /dev/null +++ b/Models/Schedule/Schedule.cs @@ -0,0 +1,6 @@ +namespace API.Models; + +public class Schedule +{ + +} diff --git a/Models/Supplier/Supplier.cs b/Models/Supplier/Supplier.cs new file mode 100644 index 0000000..f8a1efe --- /dev/null +++ b/Models/Supplier/Supplier.cs @@ -0,0 +1,7 @@ +namespace API.Models; + +public class Supplier +{ + public int Id { get; set; } + public Item[]? Items { get; set; } +} diff --git a/Services/AuthenticationService.cs b/Services/AuthenticationService.cs index f96788c..d10c8f8 100644 --- a/Services/AuthenticationService.cs +++ b/Services/AuthenticationService.cs @@ -4,7 +4,6 @@ using Microsoft.IdentityModel.Tokens; using API.Dtos; using API.Models; -using API.Models.Authentication; namespace API.Services; @@ -77,7 +76,7 @@ private string GenerateToken(User user) { new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), new Claim(ClaimTypes.Email, user.Email), - new Claim(ClaimTypes.Role, user.Role), + new Claim(ClaimTypes.Role, user.Role.ToString()), // Add any other claims you want to include in the token }), Expires = DateTime.UtcNow.AddHours(24), diff --git a/Services/IAuthenticationService.cs b/Services/IAuthenticationService.cs index b831134..0730d75 100644 --- a/Services/IAuthenticationService.cs +++ b/Services/IAuthenticationService.cs @@ -1,5 +1,5 @@ using API.Dtos; -using API.Models.Authentication; +using API.Models; namespace API.Services; @@ -8,4 +8,4 @@ public interface IAuthenticationService Task UserSignedUp(String email); Task AuthenticateUser(AuthenticationDto user); -} \ No newline at end of file +} From 824903041e214b58fb19d16b0f7138a8c5e39a8d Mon Sep 17 00:00:00 2001 From: Thomas Andersen Date: Thu, 19 Oct 2023 12:31:34 +0200 Subject: [PATCH 002/112] SW-47 * Fixed worthless migrations --- API.csproj | 3 + Migrations/20231010173626_Initial.Designer.cs | 76 ----- Migrations/20231010173626_Initial.cs | 70 ----- .../20231016091417_Added items.Designer.cs | 97 ------- Migrations/20231016091417_Added items.cs | 40 --- Migrations/20231018112535_Updated data.cs | 260 ------------------ ....cs => 20231019103017_Initial.Designer.cs} | 4 +- Migrations/20231019103017_Initial.cs | 132 +++++++++ 8 files changed, 137 insertions(+), 545 deletions(-) delete mode 100644 Migrations/20231010173626_Initial.Designer.cs delete mode 100644 Migrations/20231010173626_Initial.cs delete mode 100644 Migrations/20231016091417_Added items.Designer.cs delete mode 100644 Migrations/20231016091417_Added items.cs delete mode 100644 Migrations/20231018112535_Updated data.cs rename Migrations/{20231018112535_Updated data.Designer.cs => 20231019103017_Initial.Designer.cs} (98%) create mode 100644 Migrations/20231019103017_Initial.cs diff --git a/API.csproj b/API.csproj index 72c6629..78f047c 100644 --- a/API.csproj +++ b/API.csproj @@ -23,5 +23,8 @@ + + + diff --git a/Migrations/20231010173626_Initial.Designer.cs b/Migrations/20231010173626_Initial.Designer.cs deleted file mode 100644 index 04c6ca5..0000000 --- a/Migrations/20231010173626_Initial.Designer.cs +++ /dev/null @@ -1,76 +0,0 @@ -// -using System; -using API.Models; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace API.Migrations -{ - [DbContext(typeof(DBContext))] - [Migration("20231010173626_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "7.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - modelBuilder.Entity("API.Models.Authentication.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("Email") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("FirstName") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("LastName") - .HasColumnType("longtext"); - - b.Property("Password") - .HasColumnType("longtext"); - - b.Property("Role") - .HasColumnType("longtext"); - - b.Property("Token") - .HasColumnType("longtext"); - - b.Property("TokenExpiration") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.ToTable("Users"); - }); - - modelBuilder.Entity("API.Models.Common.Wine", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("Name") - .IsRequired() - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("Wines"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Migrations/20231010173626_Initial.cs b/Migrations/20231010173626_Initial.cs deleted file mode 100644 index f5bf9c5..0000000 --- a/Migrations/20231010173626_Initial.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace API.Migrations -{ - /// - public partial class Initial : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "Users", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - FirstName = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - LastName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Email = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Password = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Role = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Token = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - TokenExpiration = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Users", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "Wines", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - Name = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK_Wines", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Users"); - - migrationBuilder.DropTable( - name: "Wines"); - } - } -} diff --git a/Migrations/20231016091417_Added items.Designer.cs b/Migrations/20231016091417_Added items.Designer.cs deleted file mode 100644 index c0f37b9..0000000 --- a/Migrations/20231016091417_Added items.Designer.cs +++ /dev/null @@ -1,97 +0,0 @@ -// -using System; -using API.Models; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace API.Migrations -{ - [DbContext(typeof(DBContext))] - [Migration("20231016091417_Added items")] - partial class Addeditems - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "7.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - modelBuilder.Entity("API.Models.Authentication.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("Email") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("FirstName") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("LastName") - .HasColumnType("longtext"); - - b.Property("Password") - .HasColumnType("longtext"); - - b.Property("Role") - .HasColumnType("longtext"); - - b.Property("Token") - .HasColumnType("longtext"); - - b.Property("TokenExpiration") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.ToTable("Users"); - }); - - modelBuilder.Entity("API.Models.Common.Item", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("Name") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("Quantity") - .HasColumnType("int"); - - b.Property("WineType") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("Items"); - }); - - modelBuilder.Entity("API.Models.Common.Wine", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("Name") - .IsRequired() - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("Wines"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Migrations/20231016091417_Added items.cs b/Migrations/20231016091417_Added items.cs deleted file mode 100644 index 2fcc1ab..0000000 --- a/Migrations/20231016091417_Added items.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace API.Migrations -{ - /// - public partial class Addeditems : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "Items", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - Name = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - WineType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Quantity = table.Column(type: "int", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Items", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Items"); - } - } -} diff --git a/Migrations/20231018112535_Updated data.cs b/Migrations/20231018112535_Updated data.cs deleted file mode 100644 index 5a45eef..0000000 --- a/Migrations/20231018112535_Updated data.cs +++ /dev/null @@ -1,260 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace API.Migrations -{ - /// - public partial class Updateddata : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Wines"); - - migrationBuilder.RenameColumn( - name: "WineType", - table: "Items", - newName: "LiquorType"); - - migrationBuilder.AlterColumn( - name: "Role", - table: "Users", - type: "int", - nullable: false, - defaultValue: 0, - oldClrType: typeof(string), - oldType: "longtext", - oldNullable: true) - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.UpdateData( - table: "Users", - keyColumn: "Password", - keyValue: null, - column: "Password", - value: ""); - - migrationBuilder.AlterColumn( - name: "Password", - table: "Users", - type: "longtext", - nullable: false, - oldClrType: typeof(string), - oldType: "longtext", - oldNullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.UpdateData( - table: "Users", - keyColumn: "LastName", - keyValue: null, - column: "LastName", - value: ""); - - migrationBuilder.AlterColumn( - name: "LastName", - table: "Users", - type: "longtext", - nullable: false, - oldClrType: typeof(string), - oldType: "longtext", - oldNullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AddColumn( - name: "BarCode", - table: "Items", - type: "longtext", - nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AddColumn( - name: "Discriminator", - table: "Items", - type: "longtext", - nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AddColumn( - name: "ExpirationDate", - table: "Items", - type: "datetime(6)", - nullable: false, - defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); - - migrationBuilder.AddColumn( - name: "OrderId", - table: "Items", - type: "int", - nullable: true); - - migrationBuilder.AddColumn( - name: "Price", - table: "Items", - type: "float", - nullable: false, - defaultValue: 0f); - - migrationBuilder.AddColumn( - name: "SupplierId", - table: "Items", - type: "int", - nullable: true); - - migrationBuilder.CreateTable( - name: "Order", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_Order", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "Suppliers", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_Suppliers", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateIndex( - name: "IX_Items_OrderId", - table: "Items", - column: "OrderId"); - - migrationBuilder.CreateIndex( - name: "IX_Items_SupplierId", - table: "Items", - column: "SupplierId"); - - migrationBuilder.AddForeignKey( - name: "FK_Items_Order_OrderId", - table: "Items", - column: "OrderId", - principalTable: "Order", - principalColumn: "Id"); - - migrationBuilder.AddForeignKey( - name: "FK_Items_Suppliers_SupplierId", - table: "Items", - column: "SupplierId", - principalTable: "Suppliers", - principalColumn: "Id"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_Items_Order_OrderId", - table: "Items"); - - migrationBuilder.DropForeignKey( - name: "FK_Items_Suppliers_SupplierId", - table: "Items"); - - migrationBuilder.DropTable( - name: "Order"); - - migrationBuilder.DropTable( - name: "Suppliers"); - - migrationBuilder.DropIndex( - name: "IX_Items_OrderId", - table: "Items"); - - migrationBuilder.DropIndex( - name: "IX_Items_SupplierId", - table: "Items"); - - migrationBuilder.DropColumn( - name: "BarCode", - table: "Items"); - - migrationBuilder.DropColumn( - name: "Discriminator", - table: "Items"); - - migrationBuilder.DropColumn( - name: "ExpirationDate", - table: "Items"); - - migrationBuilder.DropColumn( - name: "OrderId", - table: "Items"); - - migrationBuilder.DropColumn( - name: "Price", - table: "Items"); - - migrationBuilder.DropColumn( - name: "SupplierId", - table: "Items"); - - migrationBuilder.RenameColumn( - name: "LiquorType", - table: "Items", - newName: "WineType"); - - migrationBuilder.AlterColumn( - name: "Role", - table: "Users", - type: "longtext", - nullable: true, - oldClrType: typeof(int), - oldType: "int") - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "Password", - table: "Users", - type: "longtext", - nullable: true, - oldClrType: typeof(string), - oldType: "longtext") - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "LastName", - table: "Users", - type: "longtext", - nullable: true, - oldClrType: typeof(string), - oldType: "longtext") - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "Wines", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - Name = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK_Wines", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - } - } -} diff --git a/Migrations/20231018112535_Updated data.Designer.cs b/Migrations/20231019103017_Initial.Designer.cs similarity index 98% rename from Migrations/20231018112535_Updated data.Designer.cs rename to Migrations/20231019103017_Initial.Designer.cs index 41cba28..3147b4c 100644 --- a/Migrations/20231018112535_Updated data.Designer.cs +++ b/Migrations/20231019103017_Initial.Designer.cs @@ -11,8 +11,8 @@ namespace API.Migrations { [DbContext(typeof(DBContext))] - [Migration("20231018112535_Updated data")] - partial class Updateddata + [Migration("20231019103017_Initial")] + partial class Initial { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) diff --git a/Migrations/20231019103017_Initial.cs b/Migrations/20231019103017_Initial.cs new file mode 100644 index 0000000..80366c6 --- /dev/null +++ b/Migrations/20231019103017_Initial.cs @@ -0,0 +1,132 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace API.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Order", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_Order", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Suppliers", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_Suppliers", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Users", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + FirstName = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + LastName = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Email = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Password = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Role = table.Column(type: "int", nullable: false), + Token = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + TokenExpiration = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Users", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Items", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Name = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + BarCode = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Quantity = table.Column(type: "int", nullable: false), + Price = table.Column(type: "float", nullable: false), + ExpirationDate = table.Column(type: "datetime(6)", nullable: false), + Discriminator = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + OrderId = table.Column(type: "int", nullable: true), + SupplierId = table.Column(type: "int", nullable: true), + LiquorType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_Items", x => x.Id); + table.ForeignKey( + name: "FK_Items_Order_OrderId", + column: x => x.OrderId, + principalTable: "Order", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_Items_Suppliers_SupplierId", + column: x => x.SupplierId, + principalTable: "Suppliers", + principalColumn: "Id"); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_Items_OrderId", + table: "Items", + column: "OrderId"); + + migrationBuilder.CreateIndex( + name: "IX_Items_SupplierId", + table: "Items", + column: "SupplierId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Items"); + + migrationBuilder.DropTable( + name: "Users"); + + migrationBuilder.DropTable( + name: "Order"); + + migrationBuilder.DropTable( + name: "Suppliers"); + } + } +} From 7f0b7627f675503e1676a5dc1173581f6870cb5e Mon Sep 17 00:00:00 2001 From: Thomas Andersen Date: Sun, 29 Oct 2023 12:38:33 +0100 Subject: [PATCH 003/112] SW-65 * added authentication --- API.csproj | 3 -- Controllers/AuthenticationController.cs | 48 +++++++++++++++++++ Controllers/ItemController.cs | 2 + Controllers/OrderController.cs | 2 + Controllers/UserController.cs | 1 + Controllers/WeatherForecastController.cs | 32 ------------- .../AuthenticationDto.cs | 0 .../AuthenticationResultDto.cs | 2 +- {Dtos => DataTransferObjects}/ItemDto.cs | 0 ....cs => 20231028092822_Initial.Designer.cs} | 2 +- ...7_Initial.cs => 20231028092822_Initial.cs} | 0 Services/AuthenticationService.cs | 26 +++++++++- Services/IAuthenticationService.cs | 3 ++ 13 files changed, 83 insertions(+), 38 deletions(-) create mode 100644 Controllers/AuthenticationController.cs delete mode 100644 Controllers/WeatherForecastController.cs rename {Dtos => DataTransferObjects}/AuthenticationDto.cs (100%) rename {Dtos => DataTransferObjects}/AuthenticationResultDto.cs (76%) rename {Dtos => DataTransferObjects}/ItemDto.cs (100%) rename Migrations/{20231019103017_Initial.Designer.cs => 20231028092822_Initial.Designer.cs} (99%) rename Migrations/{20231019103017_Initial.cs => 20231028092822_Initial.cs} (100%) diff --git a/API.csproj b/API.csproj index 78f047c..72c6629 100644 --- a/API.csproj +++ b/API.csproj @@ -23,8 +23,5 @@ - - - diff --git a/Controllers/AuthenticationController.cs b/Controllers/AuthenticationController.cs new file mode 100644 index 0000000..f73bf31 --- /dev/null +++ b/Controllers/AuthenticationController.cs @@ -0,0 +1,48 @@ +using API.Dtos; +using API.Services; +using Microsoft.AspNetCore.Mvc; + +namespace API.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class AuthenticationController : ControllerBase + { + private readonly IAuthenticationService _authenticationService; + + public AuthenticationController(IAuthenticationService authService) + { + _authenticationService = authService; + } + + [HttpPost] + public async Task> Login(AuthenticationDto user) + { + var authenticationResult = await _authenticationService.AuthenticateUser(user); + + if (authenticationResult == null) + { + return BadRequest("Incorrect username or password"); + } + + return Ok(authenticationResult); + } + + [HttpPost("LogOut")] + public async Task> Logout([FromBody] string token) + { + if (string.IsNullOrEmpty(token)) + { + return BadRequest("Token is missing"); + } + var logoutResult = await _authenticationService.LogOut(token); + + if (logoutResult) + { + return Ok(true); + } + + return BadRequest("Logout failed"); + } + } +} diff --git a/Controllers/ItemController.cs b/Controllers/ItemController.cs index 2f7dbb0..64c210e 100644 --- a/Controllers/ItemController.cs +++ b/Controllers/ItemController.cs @@ -1,11 +1,13 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using API.Models; +using Microsoft.AspNetCore.Authorization; namespace API.Controllers { [Route("api/[controller]")] [ApiController] + [Authorize] public class ItemController : ControllerBase { private readonly DBContext _context; diff --git a/Controllers/OrderController.cs b/Controllers/OrderController.cs index 78e5b89..750c5af 100644 --- a/Controllers/OrderController.cs +++ b/Controllers/OrderController.cs @@ -6,11 +6,13 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using API.Models; +using Microsoft.AspNetCore.Authorization; namespace API.Controllers { [Route("api/[controller]")] [ApiController] + [Authorize] public class OrderController : ControllerBase { private readonly DBContext _context; diff --git a/Controllers/UserController.cs b/Controllers/UserController.cs index 5c5d04b..20f8218 100644 --- a/Controllers/UserController.cs +++ b/Controllers/UserController.cs @@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using API.Models; +using Microsoft.AspNetCore.Authorization; namespace API.Controllers { diff --git a/Controllers/WeatherForecastController.cs b/Controllers/WeatherForecastController.cs deleted file mode 100644 index 1be4ece..0000000 --- a/Controllers/WeatherForecastController.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Microsoft.AspNetCore.Mvc; - -namespace API.Controllers; - -[ApiController] -[Route("[controller]")] -public class WeatherForecastController : ControllerBase -{ - private static readonly string[] Summaries = new[] - { - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" - }; - - private readonly ILogger _logger; - - public WeatherForecastController(ILogger logger) - { - _logger = logger; - } - - [HttpGet(Name = "GetWeatherForecast")] - public IEnumerable Get() - { - return Enumerable.Range(1, 5).Select(index => new WeatherForecast - { - Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), - TemperatureC = Random.Shared.Next(-20, 55), - Summary = Summaries[Random.Shared.Next(Summaries.Length)] - }) - .ToArray(); - } -} \ No newline at end of file diff --git a/Dtos/AuthenticationDto.cs b/DataTransferObjects/AuthenticationDto.cs similarity index 100% rename from Dtos/AuthenticationDto.cs rename to DataTransferObjects/AuthenticationDto.cs diff --git a/Dtos/AuthenticationResultDto.cs b/DataTransferObjects/AuthenticationResultDto.cs similarity index 76% rename from Dtos/AuthenticationResultDto.cs rename to DataTransferObjects/AuthenticationResultDto.cs index 33f7773..f04b1a9 100644 --- a/Dtos/AuthenticationResultDto.cs +++ b/DataTransferObjects/AuthenticationResultDto.cs @@ -5,7 +5,7 @@ namespace API.Dtos; public class AuthenticationResultDto { public string Token { get; set; } - public DateTime TokenExpiration { get; set; } + public double TokenExpiration { get; set; } public Roles UserRole { get; set; } } diff --git a/Dtos/ItemDto.cs b/DataTransferObjects/ItemDto.cs similarity index 100% rename from Dtos/ItemDto.cs rename to DataTransferObjects/ItemDto.cs diff --git a/Migrations/20231019103017_Initial.Designer.cs b/Migrations/20231028092822_Initial.Designer.cs similarity index 99% rename from Migrations/20231019103017_Initial.Designer.cs rename to Migrations/20231028092822_Initial.Designer.cs index 3147b4c..d2ac656 100644 --- a/Migrations/20231019103017_Initial.Designer.cs +++ b/Migrations/20231028092822_Initial.Designer.cs @@ -11,7 +11,7 @@ namespace API.Migrations { [DbContext(typeof(DBContext))] - [Migration("20231019103017_Initial")] + [Migration("20231028092822_Initial")] partial class Initial { /// diff --git a/Migrations/20231019103017_Initial.cs b/Migrations/20231028092822_Initial.cs similarity index 100% rename from Migrations/20231019103017_Initial.cs rename to Migrations/20231028092822_Initial.cs diff --git a/Services/AuthenticationService.cs b/Services/AuthenticationService.cs index d10c8f8..1097131 100644 --- a/Services/AuthenticationService.cs +++ b/Services/AuthenticationService.cs @@ -4,6 +4,8 @@ using Microsoft.IdentityModel.Tokens; using API.Dtos; using API.Models; +using Microsoft.AspNetCore.Http.HttpResults; +using Microsoft.AspNetCore.Mvc; namespace API.Services; @@ -53,11 +55,33 @@ public async Task AuthenticateUser(AuthenticationDto us return new AuthenticationResultDto { Token = token, - TokenExpiration = tokenExpiration, + TokenExpiration = new DateTimeOffset(tokenExpiration).ToUnixTimeMilliseconds(), UserRole = dbUser.Role }; } + /// + /// Log outs the user + /// + /// The token of the user + /// + public async Task LogOut(string token) + { + var dbUser = await _context.Users.FirstOrDefaultAsync(u => u.Token == token); + + if (dbUser == null) + { + return false; + } + + dbUser.Token = null; + dbUser.TokenExpiration = null; + + await _context.SaveChangesAsync(); + + return true; + } + /** * Generates a token */ diff --git a/Services/IAuthenticationService.cs b/Services/IAuthenticationService.cs index 0730d75..16d4304 100644 --- a/Services/IAuthenticationService.cs +++ b/Services/IAuthenticationService.cs @@ -1,5 +1,6 @@ using API.Dtos; using API.Models; +using Microsoft.AspNetCore.Mvc; namespace API.Services; @@ -8,4 +9,6 @@ public interface IAuthenticationService Task UserSignedUp(String email); Task AuthenticateUser(AuthenticationDto user); + + Task LogOut(String token); } From 8d314ecb75328f3f67e6c24a26653913f60a587e Mon Sep 17 00:00:00 2001 From: Thomas Andersen Date: Sun, 29 Oct 2023 15:08:44 +0100 Subject: [PATCH 004/112] SW-65 * Trying me fucking hardest on authorization --- Controllers/AuthenticationController.cs | 2 +- DataTransferObjects/AuthenticationResultDto.cs | 4 +--- Services/AuthenticationService.cs | 3 --- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/Controllers/AuthenticationController.cs b/Controllers/AuthenticationController.cs index f73bf31..089b178 100644 --- a/Controllers/AuthenticationController.cs +++ b/Controllers/AuthenticationController.cs @@ -29,7 +29,7 @@ public async Task> Login(AuthenticationDto } [HttpPost("LogOut")] - public async Task> Logout([FromBody] string token) + public async Task> Logout([FromQuery] string token) { if (string.IsNullOrEmpty(token)) { diff --git a/DataTransferObjects/AuthenticationResultDto.cs b/DataTransferObjects/AuthenticationResultDto.cs index f04b1a9..0987d7f 100644 --- a/DataTransferObjects/AuthenticationResultDto.cs +++ b/DataTransferObjects/AuthenticationResultDto.cs @@ -5,7 +5,5 @@ namespace API.Dtos; public class AuthenticationResultDto { public string Token { get; set; } - public double TokenExpiration { get; set; } - - public Roles UserRole { get; set; } + public Roles? UserRole { get; set; } } diff --git a/Services/AuthenticationService.cs b/Services/AuthenticationService.cs index 1097131..3f1ae5b 100644 --- a/Services/AuthenticationService.cs +++ b/Services/AuthenticationService.cs @@ -4,8 +4,6 @@ using Microsoft.IdentityModel.Tokens; using API.Dtos; using API.Models; -using Microsoft.AspNetCore.Http.HttpResults; -using Microsoft.AspNetCore.Mvc; namespace API.Services; @@ -55,7 +53,6 @@ public async Task AuthenticateUser(AuthenticationDto us return new AuthenticationResultDto { Token = token, - TokenExpiration = new DateTimeOffset(tokenExpiration).ToUnixTimeMilliseconds(), UserRole = dbUser.Role }; } From 87dc8139afea42e23c7679d0b3b6bafa4e9f8ac9 Mon Sep 17 00:00:00 2001 From: Thomas Andersen Date: Fri, 3 Nov 2023 11:57:47 +0100 Subject: [PATCH 005/112] =?UTF-8?q?SW-65=20*=20Jeg=20vil=20hellere=20d?= =?UTF-8?q?=C3=B8=20end=20at=20fikse=20det=20her?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Controllers/AuthenticationController.cs | 48 ------- Controllers/ItemController.cs | 120 ------------------ .../Shared/AuthenticationController.cs | 81 ++++++++++++ Controllers/Shared/ItemController.cs | 11 ++ Models/Authentication/Admin.cs | 6 + .../Authentication/AuthModel.cs | 2 +- Models/Authentication/Employee.cs | 6 + Models/Authentication/User.cs | 69 ++++++++-- Models/Item/Item.cs | 29 ++++- Models/Item/Liquor.cs | 5 + Services/Authentication/ITokenService.cs | 8 ++ Services/Authentication/TokenService.cs | 44 +++++++ Services/AuthenticationService.cs | 76 ++++++----- Services/IAuthenticationService.cs | 13 +- 14 files changed, 298 insertions(+), 220 deletions(-) delete mode 100644 Controllers/AuthenticationController.cs delete mode 100644 Controllers/ItemController.cs create mode 100644 Controllers/Shared/AuthenticationController.cs create mode 100644 Controllers/Shared/ItemController.cs create mode 100644 Models/Authentication/Admin.cs rename DataTransferObjects/AuthenticationResultDto.cs => Models/Authentication/AuthModel.cs (76%) create mode 100644 Models/Authentication/Employee.cs create mode 100644 Services/Authentication/ITokenService.cs create mode 100644 Services/Authentication/TokenService.cs diff --git a/Controllers/AuthenticationController.cs b/Controllers/AuthenticationController.cs deleted file mode 100644 index 089b178..0000000 --- a/Controllers/AuthenticationController.cs +++ /dev/null @@ -1,48 +0,0 @@ -using API.Dtos; -using API.Services; -using Microsoft.AspNetCore.Mvc; - -namespace API.Controllers -{ - [Route("api/[controller]")] - [ApiController] - public class AuthenticationController : ControllerBase - { - private readonly IAuthenticationService _authenticationService; - - public AuthenticationController(IAuthenticationService authService) - { - _authenticationService = authService; - } - - [HttpPost] - public async Task> Login(AuthenticationDto user) - { - var authenticationResult = await _authenticationService.AuthenticateUser(user); - - if (authenticationResult == null) - { - return BadRequest("Incorrect username or password"); - } - - return Ok(authenticationResult); - } - - [HttpPost("LogOut")] - public async Task> Logout([FromQuery] string token) - { - if (string.IsNullOrEmpty(token)) - { - return BadRequest("Token is missing"); - } - var logoutResult = await _authenticationService.LogOut(token); - - if (logoutResult) - { - return Ok(true); - } - - return BadRequest("Logout failed"); - } - } -} diff --git a/Controllers/ItemController.cs b/Controllers/ItemController.cs deleted file mode 100644 index 64c210e..0000000 --- a/Controllers/ItemController.cs +++ /dev/null @@ -1,120 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using API.Models; -using Microsoft.AspNetCore.Authorization; - -namespace API.Controllers -{ - [Route("api/[controller]")] - [ApiController] - [Authorize] - public class ItemController : ControllerBase - { - private readonly DBContext _context; - - public ItemController(DBContext context) - { - _context = context; - } - - // GET: api/Item - [HttpGet] - public async Task>> GetItems() - { - if (_context.Items == null) - { - return NotFound(); - } - return await _context.Items.ToListAsync(); - } - - // GET: api/Item/5 - [HttpGet("{id}")] - public async Task> GetItem(int id) - { - if (_context.Items == null) - { - return NotFound(); - } - var item = await _context.Items.FindAsync(id); - - if (item == null) - { - return NotFound(); - } - - return item; - } - - // PUT: api/Item/5 - // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754 - [HttpPut("{id}")] - public async Task PutItem(int id, Item item) - { - if (id != item.Id) - { - return BadRequest(); - } - - _context.Entry(item).State = EntityState.Modified; - - try - { - await _context.SaveChangesAsync(); - } - catch (DbUpdateConcurrencyException) - { - if (!ItemExists(id)) - { - return NotFound(); - } - else - { - throw; - } - } - - return NoContent(); - } - - // POST: api/Item - // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754 - [HttpPost] - public async Task> PostItem(Item item) - { - if (_context.Items == null) - { - return Problem("Entity set 'DBContext.Items' is null."); - } - _context.Items.Add(item); - await _context.SaveChangesAsync(); - - return CreatedAtAction("GetItem", new { id = item.Id }, item); - } - - // DELETE: api/Item/5 - [HttpDelete("{id}")] - public async Task DeleteItem(int id) - { - if (_context.Items == null) - { - return NotFound(); - } - var item = await _context.Items.FindAsync(id); - if (item == null) - { - return NotFound(); - } - - _context.Items.Remove(item); - await _context.SaveChangesAsync(); - - return NoContent(); - } - - private bool ItemExists(int id) - { - return (_context.Items?.Any(e => e.Id == id)).GetValueOrDefault(); - } - } -} diff --git a/Controllers/Shared/AuthenticationController.cs b/Controllers/Shared/AuthenticationController.cs new file mode 100644 index 0000000..9e566f2 --- /dev/null +++ b/Controllers/Shared/AuthenticationController.cs @@ -0,0 +1,81 @@ +using API.Dtos; +using API.Models; +using API.Services; +using Microsoft.AspNetCore.Mvc; + +namespace API.Controllers.Shared +{ + [Route("api/[controller]")] + [ApiController] + public class AuthenticationController : ControllerBase + { + private readonly IAuthenticationService _authenticationService; + + public AuthenticationController(IAuthenticationService authService) + { + _authenticationService = authService; + } + + [HttpPost] + public async Task> Login(AuthenticationDto user) + { + if(user == null) + { + return BadRequest("User is missing"); + } + + var authenticationResult = await _authenticationService.Login(user); + + return authenticationResult; + } + + [HttpPost("Signup")] + public async Task> Signup(User user) + { + var authenticationResult = await _authenticationService.SignUpUser(user); + + if (authenticationResult == null) + { + return BadRequest("Incorrect username or password"); + } + + return Ok(authenticationResult); + } + + [HttpPost("LogOut")] + public async Task> Logout([FromBody] AuthModel model) + { + if (string.IsNullOrEmpty(model?.Token)) + { + return BadRequest("Token is missing"); + } + + var logoutResult = await _authenticationService.LogOut(model.Token); + + if (logoutResult) + { + return Ok(true); + } + + return BadRequest("Logout failed"); + } + + [HttpPost("Verify")] + public async Task> VerifyToken([FromBody] AuthModel model) + { + if (string.IsNullOrEmpty(model?.Token)) + { + return BadRequest("Token is missing"); + } + + var verified = await _authenticationService.VerifyToken(model.Token); + + if (verified) + { + return Ok(true); + } + + return BadRequest("Verification failed"); + } + } +} diff --git a/Controllers/Shared/ItemController.cs b/Controllers/Shared/ItemController.cs new file mode 100644 index 0000000..2bd8b20 --- /dev/null +++ b/Controllers/Shared/ItemController.cs @@ -0,0 +1,11 @@ +using Microsoft.AspNetCore.Mvc; + +namespace API.Controllers.Shared +{ + [Route("api/[controller]")] + [ApiController] + public class ItemController + { + + } +} diff --git a/Models/Authentication/Admin.cs b/Models/Authentication/Admin.cs new file mode 100644 index 0000000..bf64975 --- /dev/null +++ b/Models/Authentication/Admin.cs @@ -0,0 +1,6 @@ +namespace API.Models.Authentication; + +public class Admin : User +{ + +} diff --git a/DataTransferObjects/AuthenticationResultDto.cs b/Models/Authentication/AuthModel.cs similarity index 76% rename from DataTransferObjects/AuthenticationResultDto.cs rename to Models/Authentication/AuthModel.cs index 0987d7f..54fb078 100644 --- a/DataTransferObjects/AuthenticationResultDto.cs +++ b/Models/Authentication/AuthModel.cs @@ -2,7 +2,7 @@ namespace API.Dtos; -public class AuthenticationResultDto +public class AuthModel { public string Token { get; set; } public Roles? UserRole { get; set; } diff --git a/Models/Authentication/Employee.cs b/Models/Authentication/Employee.cs new file mode 100644 index 0000000..efa7e59 --- /dev/null +++ b/Models/Authentication/Employee.cs @@ -0,0 +1,6 @@ +namespace API.Models.Authentication; + +public class Employee : User +{ + +} diff --git a/Models/Authentication/User.cs b/Models/Authentication/User.cs index b73b5c2..64a215b 100644 --- a/Models/Authentication/User.cs +++ b/Models/Authentication/User.cs @@ -2,14 +2,65 @@ namespace API.Models; -public class User +public abstract class User { - public int Id { get; set; } - public string FirstName { get; set; } - public string LastName { get; set; } - public string Email { get; set; } - public string Password { get; set; } - public Roles Role { get; set; } - public string? Token { get; set; } - public DateTime? TokenExpiration { get; set; } + private int? Id; + private string Name; + private int Phone; + private string Email; + public string Password; + public Roles? Role; + public string? Token; + public DateTime? TokenExpiration; + + public static List Users = new List(); + + public int GetId() + { + return this.Id; + } + + public void SetToken(string token) + { + this.Token = token; + } + + public string GetToken() + { + return this.Token; + } + + public void SetEmail(string email) + { + this.Email = email; + } + + public string GetEmail() + { + return this.Email; + } + + public User(string name, int phone, string email, string password, Roles role) + { + this.Name = name; + this.Phone = phone; + this.Email = email; + this.Password = password; + this.Role = role; + } + + public static List GetUsers(DBContext context) + { + return context.Users.ToList(); + } + + public static User? GetUserById(DBContext context, int id) + { + return context.Users.FirstOrDefault(u => u.Id == id); + } + + public static User? LoginUser(DBContext context, string email, string password) + { + return context.Users.FirstOrDefault(u => u.Email == email && u.Password == password); + } } diff --git a/Models/Item/Item.cs b/Models/Item/Item.cs index cf9e063..b30e2a7 100644 --- a/Models/Item/Item.cs +++ b/Models/Item/Item.cs @@ -2,10 +2,27 @@ namespace API.Models; public class Item { - public int Id { get; set; } - public string Name { get; set; } - public string? BarCode { get; set; } - public int Quantity { get; set; } - public float Price { get; set; } - public DateTime ExpirationDate { get; set; } + private int Id; + private string Name; + private string? BarCode; + private int Quantity; + private float Price; + private DateTime ExpirationDate; + + + public static List Items = new List(); + + + + public int getId() + { + return this.Id; + } + + public static List GetItems(DBContext context) + { + return context.Items.ToList(); + } } + + diff --git a/Models/Item/Liquor.cs b/Models/Item/Liquor.cs index ef3d181..a44ffd4 100644 --- a/Models/Item/Liquor.cs +++ b/Models/Item/Liquor.cs @@ -3,4 +3,9 @@ namespace API.Models; public class Liquor: Item { public string? LiquorType { get; set; } + + public string SellLiquor() + { + + } } diff --git a/Services/Authentication/ITokenService.cs b/Services/Authentication/ITokenService.cs new file mode 100644 index 0000000..efa7a69 --- /dev/null +++ b/Services/Authentication/ITokenService.cs @@ -0,0 +1,8 @@ +using API.Models; + +namespace API.Services.Authentication; + +public interface ITokenService +{ + public string GenerateToken(User user); +} diff --git a/Services/Authentication/TokenService.cs b/Services/Authentication/TokenService.cs new file mode 100644 index 0000000..8edf964 --- /dev/null +++ b/Services/Authentication/TokenService.cs @@ -0,0 +1,44 @@ +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using Microsoft.IdentityModel.Tokens; +using API.Models; + + +namespace API.Services.Authentication; + +public class TokenService : ITokenService +{ + private readonly DBContext _context; + private readonly IConfiguration _configuration; + + public TokenService(DBContext context, IConfiguration configuration) + { + _context = context; + _configuration = configuration; + } + + public string GenerateToken(User user) + { + var tokenHandler = new JwtSecurityTokenHandler(); + var key = "b0493a0d-f88e-4b0b-94eb-665f7207c92c"u8.ToArray(); // Use the same secret key as in your JWT configuration + var tokenDescriptor = new SecurityTokenDescriptor + { + Claims = new Dictionary + { + { "aud", "ApiAppAudience" }, // Use the same audience name as in your JWT configuration + { "iss", "ApiAppIssuer" } + }, + Subject = new ClaimsIdentity(new Claim[] + { + new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), + new Claim(ClaimTypes.Email, user.GetEmail()), + new Claim(ClaimTypes.Role, user.Role.ToString()), + // Add any other claims you want to include in the token + }), + Expires = DateTime.UtcNow.AddHours(24), + SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) + }; + var token = tokenHandler.CreateToken(tokenDescriptor); + return tokenHandler.WriteToken(token); + } +} diff --git a/Services/AuthenticationService.cs b/Services/AuthenticationService.cs index 3f1ae5b..9490cba 100644 --- a/Services/AuthenticationService.cs +++ b/Services/AuthenticationService.cs @@ -28,14 +28,9 @@ public async Task UserSignedUp(string email) return await _context.Users.FirstOrDefaultAsync(u => u.Email == email); } - /// - /// Authenticates the user with email and password - /// - /// - /// A Token and an Expiration - public async Task AuthenticateUser(AuthenticationDto user) + public async Task Login(AuthenticationDto user) { - var dbUser = await _context.Users.FirstOrDefaultAsync(u => u.Email == user.Email && u.Password == user.Password); + var dbUser = User.LoginUser(_context, user.Email, user.Password); if (dbUser == null) { @@ -50,13 +45,23 @@ public async Task AuthenticateUser(AuthenticationDto us await _context.SaveChangesAsync(); - return new AuthenticationResultDto + return new AuthModel { Token = token, UserRole = dbUser.Role }; } + /// + /// Authenticates the user with email and password + /// + /// + /// A Token and an Expiration + public async Task AuthenticateUser(AuthenticationDto user) + { + + } + /// /// Log outs the user /// @@ -79,31 +84,38 @@ public async Task LogOut(string token) return true; } - /** - * Generates a token - */ - private string GenerateToken(User user) + /// + /// Verifies a given token + /// + /// Token to verify + /// + public async Task VerifyToken(string token) { - var tokenHandler = new JwtSecurityTokenHandler(); - var key = "b0493a0d-f88e-4b0b-94eb-665f7207c92c"u8.ToArray(); // Use the same secret key as in your JWT configuration - var tokenDescriptor = new SecurityTokenDescriptor + var dbUser = await _context.Users.FirstOrDefaultAsync(u => u.Token == token); + + if (dbUser == null) { - Claims = new Dictionary - { - { "aud", "ApiAppAudience" }, // Use the same audience name as in your JWT configuration - { "iss", "ApiAppIssuer" } - }, - Subject = new ClaimsIdentity(new Claim[] - { - new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), - new Claim(ClaimTypes.Email, user.Email), - new Claim(ClaimTypes.Role, user.Role.ToString()), - // Add any other claims you want to include in the token - }), - Expires = DateTime.UtcNow.AddHours(24), - SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) - }; - var token = tokenHandler.CreateToken(tokenDescriptor); - return tokenHandler.WriteToken(token); + return false; + } + + if (dbUser.TokenExpiration == null || dbUser.TokenExpiration <= DateTime.Now) + { + dbUser.Token = null; + dbUser.TokenExpiration = null; + await _context.SaveChangesAsync(); + return false; + } + + return true; + } + + public Task SignupUser(string token) + { + throw new NotImplementedException(); } + + /** + * Generates a token + */ + } diff --git a/Services/IAuthenticationService.cs b/Services/IAuthenticationService.cs index 16d4304..93a9637 100644 --- a/Services/IAuthenticationService.cs +++ b/Services/IAuthenticationService.cs @@ -1,14 +1,19 @@ using API.Dtos; using API.Models; -using Microsoft.AspNetCore.Mvc; namespace API.Services; public interface IAuthenticationService { - Task UserSignedUp(String email); + Task UserSignedUp(string email); - Task AuthenticateUser(AuthenticationDto user); + Task Login(AuthenticationDto user); - Task LogOut(String token); + Task AuthenticateUser(AuthenticationDto user); + + Task LogOut(string token); + + Task VerifyToken(string token); + + Task SignupUser(string token); } From b5ba26b1bb61671c5504802909cc9f47223468a9 Mon Sep 17 00:00:00 2001 From: ThomasSteenAndersen Date: Mon, 6 Nov 2023 10:49:46 +0100 Subject: [PATCH 006/112] in progress --- API.csproj | 3 ++ Enums/Roles.cs | 8 ---- Models/Authentication/User.cs | 68 ++++--------------------------- Services/AuthenticationService.cs | 2 +- 4 files changed, 11 insertions(+), 70 deletions(-) delete mode 100644 Enums/Roles.cs diff --git a/API.csproj b/API.csproj index 72c6629..5b6067a 100644 --- a/API.csproj +++ b/API.csproj @@ -23,5 +23,8 @@ + + + diff --git a/Enums/Roles.cs b/Enums/Roles.cs deleted file mode 100644 index ee336cd..0000000 --- a/Enums/Roles.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace API.Enums; - -public enum Roles -{ - Customer, - Employee, - Admin -} diff --git a/Models/Authentication/User.cs b/Models/Authentication/User.cs index 64a215b..3efacf4 100644 --- a/Models/Authentication/User.cs +++ b/Models/Authentication/User.cs @@ -1,66 +1,12 @@ -using API.Enums; - namespace API.Models; public abstract class User { - private int? Id; - private string Name; - private int Phone; - private string Email; - public string Password; - public Roles? Role; - public string? Token; - public DateTime? TokenExpiration; - - public static List Users = new List(); - - public int GetId() - { - return this.Id; - } - - public void SetToken(string token) - { - this.Token = token; - } - - public string GetToken() - { - return this.Token; - } - - public void SetEmail(string email) - { - this.Email = email; - } - - public string GetEmail() - { - return this.Email; - } - - public User(string name, int phone, string email, string password, Roles role) - { - this.Name = name; - this.Phone = phone; - this.Email = email; - this.Password = password; - this.Role = role; - } - - public static List GetUsers(DBContext context) - { - return context.Users.ToList(); - } - - public static User? GetUserById(DBContext context, int id) - { - return context.Users.FirstOrDefault(u => u.Id == id); - } - - public static User? LoginUser(DBContext context, string email, string password) - { - return context.Users.FirstOrDefault(u => u.Email == email && u.Password == password); - } + public int Id { get; set; } + public string Name { get; set; } + public int Phone { get; set; } + public string Email { get; set; } + public string Password { get; set; } + public string? Token { get; set; } + public DateTime? TokenExpiration { get; set; } } diff --git a/Services/AuthenticationService.cs b/Services/AuthenticationService.cs index 9490cba..ec6bd70 100644 --- a/Services/AuthenticationService.cs +++ b/Services/AuthenticationService.cs @@ -31,7 +31,7 @@ public async Task UserSignedUp(string email) public async Task Login(AuthenticationDto user) { var dbUser = User.LoginUser(_context, user.Email, user.Password); - + dbUser if (dbUser == null) { return null; From ddf1032ab1b961bf503ebd86844831dec62a4630 Mon Sep 17 00:00:00 2001 From: Thomas Andersen Date: Mon, 6 Nov 2023 13:58:40 +0100 Subject: [PATCH 007/112] SW-65 * Fixed some of the public private --- .idea/.idea.API/.idea/dataSources.xml | 12 ++ .idea/.idea.API/.idea/sqldialects.xml | 6 + API.csproj | 5 + .../Shared/AuthenticationController.cs | 21 +-- Controllers/Shared/UserController.cs | 26 ++++ Controllers/UserController.cs | 124 ------------------ DataTransferObjects/AuthPas.cs | 19 +++ DataTransferObjects/AuthenticationDto.cs | 2 +- DataTransferObjects/UserDto.cs | 13 ++ Enums/Roles.cs | 8 -- ....cs => 20231106111910_Initial.Designer.cs} | 35 ++++- ...2_Initial.cs => 20231106111910_Initial.cs} | 10 +- Migrations/DBContextModelSnapshot.cs | 33 ++++- Models/Authentication/Admin.cs | 12 ++ Models/Authentication/AuthModel.cs | 9 -- Models/Authentication/Customer.cs | 22 ++++ Models/Authentication/Employee.cs | 20 +++ Models/Authentication/User.cs | 70 ++-------- Models/DBContext.cs | 4 + Models/Item/Item.cs | 28 +--- Models/Item/Liquor.cs | 5 - Program.cs | 5 +- Services/Authentication/TokenService.cs | 8 +- Services/AuthenticationService.cs | 58 ++++---- Services/IAuthenticationService.cs | 8 +- 25 files changed, 283 insertions(+), 280 deletions(-) create mode 100644 .idea/.idea.API/.idea/dataSources.xml create mode 100644 .idea/.idea.API/.idea/sqldialects.xml create mode 100644 Controllers/Shared/UserController.cs delete mode 100644 Controllers/UserController.cs create mode 100644 DataTransferObjects/AuthPas.cs create mode 100644 DataTransferObjects/UserDto.cs delete mode 100644 Enums/Roles.cs rename Migrations/{20231028092822_Initial.Designer.cs => 20231106111910_Initial.Designer.cs} (82%) rename Migrations/{20231028092822_Initial.cs => 20231106111910_Initial.cs} (94%) delete mode 100644 Models/Authentication/AuthModel.cs create mode 100644 Models/Authentication/Customer.cs diff --git a/.idea/.idea.API/.idea/dataSources.xml b/.idea/.idea.API/.idea/dataSources.xml new file mode 100644 index 0000000..a656ac8 --- /dev/null +++ b/.idea/.idea.API/.idea/dataSources.xml @@ -0,0 +1,12 @@ + + + + + mysql.8 + true + com.mysql.cj.jdbc.Driver + jdbc:mysql://localhost:3306/Eventilope + $ProjectFileDir$ + + + \ No newline at end of file diff --git a/.idea/.idea.API/.idea/sqldialects.xml b/.idea/.idea.API/.idea/sqldialects.xml new file mode 100644 index 0000000..52a92ab --- /dev/null +++ b/.idea/.idea.API/.idea/sqldialects.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/API.csproj b/API.csproj index 72c6629..229da36 100644 --- a/API.csproj +++ b/API.csproj @@ -10,6 +10,7 @@ + @@ -23,5 +24,9 @@ + + + + diff --git a/Controllers/Shared/AuthenticationController.cs b/Controllers/Shared/AuthenticationController.cs index 9e566f2..63483d5 100644 --- a/Controllers/Shared/AuthenticationController.cs +++ b/Controllers/Shared/AuthenticationController.cs @@ -16,8 +16,8 @@ public AuthenticationController(IAuthenticationService authService) _authenticationService = authService; } - [HttpPost] - public async Task> Login(AuthenticationDto user) + [HttpPost("Login")] + public async Task> Login(AuthenticationDto user) { if(user == null) { @@ -30,9 +30,9 @@ public async Task> Login(AuthenticationDto user) } [HttpPost("Signup")] - public async Task> Signup(User user) + public async Task> Signup(UserDto user) { - var authenticationResult = await _authenticationService.SignUpUser(user); + var authenticationResult = await _authenticationService.CreateNewUser(user); if (authenticationResult == null) { @@ -40,17 +40,18 @@ public async Task> Signup(User user) } return Ok(authenticationResult); + } [HttpPost("LogOut")] - public async Task> Logout([FromBody] AuthModel model) + public async Task> Logout([FromBody] AuthPas pas) { - if (string.IsNullOrEmpty(model?.Token)) + if (string.IsNullOrEmpty(pas?.Token)) { return BadRequest("Token is missing"); } - var logoutResult = await _authenticationService.LogOut(model.Token); + var logoutResult = await _authenticationService.LogOut(pas.Token); if (logoutResult) { @@ -61,14 +62,14 @@ public async Task> Logout([FromBody] AuthModel model) } [HttpPost("Verify")] - public async Task> VerifyToken([FromBody] AuthModel model) + public async Task> VerifyToken([FromBody] AuthPas pas) { - if (string.IsNullOrEmpty(model?.Token)) + if (string.IsNullOrEmpty(pas?.Token)) { return BadRequest("Token is missing"); } - var verified = await _authenticationService.VerifyToken(model.Token); + var verified = await _authenticationService.VerifyToken(pas.Token); if (verified) { diff --git a/Controllers/Shared/UserController.cs b/Controllers/Shared/UserController.cs new file mode 100644 index 0000000..e602ca7 --- /dev/null +++ b/Controllers/Shared/UserController.cs @@ -0,0 +1,26 @@ +using API.Models; +using API.Services; +using Microsoft.AspNetCore.Mvc; + +namespace API.Controllers.Shared +{ + [Route("api/[controller]")] + [ApiController] + public class UserController + { + private readonly IAuthenticationService _authenticationService; + + public UserController(IAuthenticationService authService) + { + _authenticationService = authService; + } + + [HttpGet("Users")] + public async Task>> GetUsers() + { + var users = await _authenticationService.GetUsers(); + + return users; + } + } +} diff --git a/Controllers/UserController.cs b/Controllers/UserController.cs deleted file mode 100644 index 20f8218..0000000 --- a/Controllers/UserController.cs +++ /dev/null @@ -1,124 +0,0 @@ -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 API.Models; -using Microsoft.AspNetCore.Authorization; - -namespace API.Controllers -{ - [Route("api/[controller]")] - [ApiController] - public class UserController : ControllerBase - { - private readonly DBContext _context; - - public UserController(DBContext context) - { - _context = context; - } - - // GET: api/User - [HttpGet] - public async Task>> GetUsers() - { - if (_context.Users == null) - { - return NotFound(); - } - return await _context.Users.ToListAsync(); - } - - // GET: api/User/5 - [HttpGet("{id}")] - public async Task> GetUser(int id) - { - if (_context.Users == null) - { - return NotFound(); - } - var user = await _context.Users.FindAsync(id); - - if (user == null) - { - return NotFound(); - } - - return user; - } - - // PUT: api/User/5 - // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754 - [HttpPut("{id}")] - public async Task PutUser(int id, User user) - { - if (id != user.Id) - { - return BadRequest(); - } - - _context.Entry(user).State = EntityState.Modified; - - try - { - await _context.SaveChangesAsync(); - } - catch (DbUpdateConcurrencyException) - { - if (!UserExists(id)) - { - return NotFound(); - } - else - { - throw; - } - } - - return NoContent(); - } - - // POST: api/User - // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754 - [HttpPost] - public async Task> PostUser(User user) - { - if (_context.Users == null) - { - return Problem("Entity set 'DBContext.Users' is null."); - } - _context.Users.Add(user); - await _context.SaveChangesAsync(); - - return CreatedAtAction("GetUser", new { id = user.Id }, user); - } - - // DELETE: api/User/5 - [HttpDelete("{id}")] - public async Task DeleteUser(int id) - { - if (_context.Users == null) - { - return NotFound(); - } - var user = await _context.Users.FindAsync(id); - if (user == null) - { - return NotFound(); - } - - _context.Users.Remove(user); - await _context.SaveChangesAsync(); - - return NoContent(); - } - - private bool UserExists(int id) - { - return (_context.Users?.Any(e => e.Id == id)).GetValueOrDefault(); - } - } -} diff --git a/DataTransferObjects/AuthPas.cs b/DataTransferObjects/AuthPas.cs new file mode 100644 index 0000000..544ef98 --- /dev/null +++ b/DataTransferObjects/AuthPas.cs @@ -0,0 +1,19 @@ +namespace API.Dtos; + +public class AuthPas +{ + public string Token { get; protected set; } + public DateTime ExpirationDate { get; protected set; } + + public AuthPas(string token, DateTime? expirationDate) + { + Token = token; + + if (expirationDate != null) + { + ExpirationDate = expirationDate.Value; + } + + ExpirationDate = DateTime.Now.AddHours(24); + } +} diff --git a/DataTransferObjects/AuthenticationDto.cs b/DataTransferObjects/AuthenticationDto.cs index 51042d5..cea088f 100644 --- a/DataTransferObjects/AuthenticationDto.cs +++ b/DataTransferObjects/AuthenticationDto.cs @@ -4,4 +4,4 @@ public class AuthenticationDto { public string Email { get; set; } public string Password { get; set; } -} \ No newline at end of file +} diff --git a/DataTransferObjects/UserDto.cs b/DataTransferObjects/UserDto.cs new file mode 100644 index 0000000..da77534 --- /dev/null +++ b/DataTransferObjects/UserDto.cs @@ -0,0 +1,13 @@ +namespace API.Dtos; + +public class UserDto +{ + public int Id { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + public int Phone { get; set; } + public string Email { get; set; } + public string Password { get; set; } + public string Token { get; set; } + public DateTime? TokenExpiration { get; set; } +} diff --git a/Enums/Roles.cs b/Enums/Roles.cs deleted file mode 100644 index ee336cd..0000000 --- a/Enums/Roles.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace API.Enums; - -public enum Roles -{ - Customer, - Employee, - Admin -} diff --git a/Migrations/20231028092822_Initial.Designer.cs b/Migrations/20231106111910_Initial.Designer.cs similarity index 82% rename from Migrations/20231028092822_Initial.Designer.cs rename to Migrations/20231106111910_Initial.Designer.cs index d2ac656..49e46a9 100644 --- a/Migrations/20231028092822_Initial.Designer.cs +++ b/Migrations/20231106111910_Initial.Designer.cs @@ -11,7 +11,7 @@ namespace API.Migrations { [DbContext(typeof(DBContext))] - [Migration("20231028092822_Initial")] + [Migration("20231106111910_Initial")] partial class Initial { /// @@ -29,6 +29,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("int"); b.Property("BarCode") + .IsRequired() .HasColumnType("longtext"); b.Property("Discriminator") @@ -95,6 +96,10 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .ValueGeneratedOnAdd() .HasColumnType("int"); + b.Property("Discriminator") + .IsRequired() + .HasColumnType("longtext"); + b.Property("Email") .IsRequired() .HasColumnType("longtext"); @@ -111,10 +116,11 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("longtext"); - b.Property("Role") + b.Property("Phone") .HasColumnType("int"); b.Property("Token") + .IsRequired() .HasColumnType("longtext"); b.Property("TokenExpiration") @@ -123,6 +129,10 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.HasKey("Id"); b.ToTable("Users"); + + b.HasDiscriminator("Discriminator").HasValue("User"); + + b.UseTphMappingStrategy(); }); modelBuilder.Entity("API.Models.Chocolate", b => @@ -149,6 +159,27 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.HasDiscriminator().HasValue("Wine"); }); + modelBuilder.Entity("API.Models.Authentication.Admin", b => + { + b.HasBaseType("API.Models.User"); + + b.HasDiscriminator().HasValue("Admin"); + }); + + modelBuilder.Entity("API.Models.Authentication.Customer", b => + { + b.HasBaseType("API.Models.User"); + + b.HasDiscriminator().HasValue("Customer"); + }); + + modelBuilder.Entity("API.Models.Authentication.Employee", b => + { + b.HasBaseType("API.Models.User"); + + b.HasDiscriminator().HasValue("Employee"); + }); + modelBuilder.Entity("API.Models.Item", b => { b.HasOne("API.Models.Order", null) diff --git a/Migrations/20231028092822_Initial.cs b/Migrations/20231106111910_Initial.cs similarity index 94% rename from Migrations/20231028092822_Initial.cs rename to Migrations/20231106111910_Initial.cs index 80366c6..6158e47 100644 --- a/Migrations/20231028092822_Initial.cs +++ b/Migrations/20231106111910_Initial.cs @@ -51,14 +51,16 @@ protected override void Up(MigrationBuilder migrationBuilder) .Annotation("MySql:CharSet", "utf8mb4"), LastName = table.Column(type: "longtext", nullable: false) .Annotation("MySql:CharSet", "utf8mb4"), + Phone = table.Column(type: "int", nullable: false), Email = table.Column(type: "longtext", nullable: false) .Annotation("MySql:CharSet", "utf8mb4"), Password = table.Column(type: "longtext", nullable: false) .Annotation("MySql:CharSet", "utf8mb4"), - Role = table.Column(type: "int", nullable: false), - Token = table.Column(type: "longtext", nullable: true) + Token = table.Column(type: "longtext", nullable: false) .Annotation("MySql:CharSet", "utf8mb4"), - TokenExpiration = table.Column(type: "datetime(6)", nullable: true) + TokenExpiration = table.Column(type: "datetime(6)", nullable: true), + Discriminator = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") }, constraints: table => { @@ -74,7 +76,7 @@ protected override void Up(MigrationBuilder migrationBuilder) .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), Name = table.Column(type: "longtext", nullable: false) .Annotation("MySql:CharSet", "utf8mb4"), - BarCode = table.Column(type: "longtext", nullable: true) + BarCode = table.Column(type: "longtext", nullable: false) .Annotation("MySql:CharSet", "utf8mb4"), Quantity = table.Column(type: "int", nullable: false), Price = table.Column(type: "float", nullable: false), diff --git a/Migrations/DBContextModelSnapshot.cs b/Migrations/DBContextModelSnapshot.cs index f9a1987..57ab2da 100644 --- a/Migrations/DBContextModelSnapshot.cs +++ b/Migrations/DBContextModelSnapshot.cs @@ -26,6 +26,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("int"); b.Property("BarCode") + .IsRequired() .HasColumnType("longtext"); b.Property("Discriminator") @@ -92,6 +93,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) .ValueGeneratedOnAdd() .HasColumnType("int"); + b.Property("Discriminator") + .IsRequired() + .HasColumnType("longtext"); + b.Property("Email") .IsRequired() .HasColumnType("longtext"); @@ -108,10 +113,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("longtext"); - b.Property("Role") + b.Property("Phone") .HasColumnType("int"); b.Property("Token") + .IsRequired() .HasColumnType("longtext"); b.Property("TokenExpiration") @@ -120,6 +126,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); b.ToTable("Users"); + + b.HasDiscriminator("Discriminator").HasValue("User"); + + b.UseTphMappingStrategy(); }); modelBuilder.Entity("API.Models.Chocolate", b => @@ -146,6 +156,27 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasDiscriminator().HasValue("Wine"); }); + modelBuilder.Entity("API.Models.Authentication.Admin", b => + { + b.HasBaseType("API.Models.User"); + + b.HasDiscriminator().HasValue("Admin"); + }); + + modelBuilder.Entity("API.Models.Authentication.Customer", b => + { + b.HasBaseType("API.Models.User"); + + b.HasDiscriminator().HasValue("Customer"); + }); + + modelBuilder.Entity("API.Models.Authentication.Employee", b => + { + b.HasBaseType("API.Models.User"); + + b.HasDiscriminator().HasValue("Employee"); + }); + modelBuilder.Entity("API.Models.Item", b => { b.HasOne("API.Models.Order", null) diff --git a/Models/Authentication/Admin.cs b/Models/Authentication/Admin.cs index bf64975..9c816af 100644 --- a/Models/Authentication/Admin.cs +++ b/Models/Authentication/Admin.cs @@ -2,5 +2,17 @@ namespace API.Models.Authentication; public class Admin : User { + private readonly DBContext _context; + + public Admin(DBContext context) + { + _context = context; + } + + override + public string GetClassName() + { + return "Admin"; + } } diff --git a/Models/Authentication/AuthModel.cs b/Models/Authentication/AuthModel.cs deleted file mode 100644 index 54fb078..0000000 --- a/Models/Authentication/AuthModel.cs +++ /dev/null @@ -1,9 +0,0 @@ -using API.Enums; - -namespace API.Dtos; - -public class AuthModel -{ - public string Token { get; set; } - public Roles? UserRole { get; set; } -} diff --git a/Models/Authentication/Customer.cs b/Models/Authentication/Customer.cs new file mode 100644 index 0000000..9d359af --- /dev/null +++ b/Models/Authentication/Customer.cs @@ -0,0 +1,22 @@ +namespace API.Models.Authentication; + +public class Customer : User +{ + public Customer(int id, string firstName, string lastName, int phone, string email, string password, string token, DateTime? tokenExpiration) + { + Id = id; + FirstName = firstName; + LastName = lastName; + Phone = phone; + Email = email; + Password = password; + Token = token; + TokenExpiration = tokenExpiration; + } + + override + public string GetClassName() + { + return "Customer"; + } +} diff --git a/Models/Authentication/Employee.cs b/Models/Authentication/Employee.cs index efa7e59..a424b32 100644 --- a/Models/Authentication/Employee.cs +++ b/Models/Authentication/Employee.cs @@ -1,6 +1,26 @@ +using Microsoft.EntityFrameworkCore; + namespace API.Models.Authentication; public class Employee : User { + private readonly DBContext _context; + + public Employee(int id, string firstName, string lastName, int phone, string email, string password, string token, DateTime? tokenExpiration) + { + Id = id; + FirstName = firstName; + LastName = lastName; + Phone = phone; + Email = email; + Password = password; + Token = token; + TokenExpiration = tokenExpiration; + } + override + public string GetClassName() + { + return "Employee"; + } } diff --git a/Models/Authentication/User.cs b/Models/Authentication/User.cs index 64a215b..91ab42e 100644 --- a/Models/Authentication/User.cs +++ b/Models/Authentication/User.cs @@ -1,66 +1,24 @@ -using API.Enums; +using API.Dtos; namespace API.Models; public abstract class User { - private int? Id; - private string Name; - private int Phone; - private string Email; - public string Password; - public Roles? Role; - public string? Token; - public DateTime? TokenExpiration; + public int Id { get; protected set; } + public string FirstName { get; protected set; } + public string LastName { get; protected set; } + public int Phone { get; protected set; } + public string Email { get; protected set; } + public string Password { get; protected set; } + public string Token { get; protected set; } + public DateTime? TokenExpiration { get; protected set; } - public static List Users = new List(); - - public int GetId() - { - return this.Id; - } - - public void SetToken(string token) - { - this.Token = token; - } - - public string GetToken() - { - return this.Token; - } - - public void SetEmail(string email) - { - this.Email = email; - } - - public string GetEmail() - { - return this.Email; - } - - public User(string name, int phone, string email, string password, Roles role) - { - this.Name = name; - this.Phone = phone; - this.Email = email; - this.Password = password; - this.Role = role; - } - - public static List GetUsers(DBContext context) - { - return context.Users.ToList(); - } - - public static User? GetUserById(DBContext context, int id) + public AuthPas SetToken(DBContext dbContext,string token, DateTime tokenExpiration) { - return context.Users.FirstOrDefault(u => u.Id == id); + Token = token; + TokenExpiration = tokenExpiration; + return new AuthPas(token, tokenExpiration); } - public static User? LoginUser(DBContext context, string email, string password) - { - return context.Users.FirstOrDefault(u => u.Email == email && u.Password == password); - } + public abstract string GetClassName(); } diff --git a/Models/DBContext.cs b/Models/DBContext.cs index 9ff0f45..0b369c8 100644 --- a/Models/DBContext.cs +++ b/Models/DBContext.cs @@ -1,3 +1,4 @@ +using API.Models.Authentication; using Microsoft.EntityFrameworkCore; namespace API.Models; @@ -8,6 +9,9 @@ public DBContext(DbContextOptions options) : base(options) { } + public DbSet Employees { get; set; } = null!; + public DbSet Admins { get; set; } = null!; + public DbSet Customers { get; set; } = null!; public DbSet Users { get; set; } = null!; public DbSet Liquors { get; set; } = null!; public DbSet Chocolates { get; set; } = null!; diff --git a/Models/Item/Item.cs b/Models/Item/Item.cs index b30e2a7..6768e25 100644 --- a/Models/Item/Item.cs +++ b/Models/Item/Item.cs @@ -1,28 +1,14 @@ namespace API.Models; -public class Item +public abstract class Item { - private int Id; - private string Name; - private string? BarCode; - private int Quantity; - private float Price; - private DateTime ExpirationDate; + public int Id { get; set; } + public string Name { get; set; } + public string BarCode { get; set; } + public int Quantity { get; set; } + public float Price { get; set; } + public DateTime ExpirationDate { get; set; } - - public static List Items = new List(); - - - - public int getId() - { - return this.Id; - } - - public static List GetItems(DBContext context) - { - return context.Items.ToList(); - } } diff --git a/Models/Item/Liquor.cs b/Models/Item/Liquor.cs index a44ffd4..ef3d181 100644 --- a/Models/Item/Liquor.cs +++ b/Models/Item/Liquor.cs @@ -3,9 +3,4 @@ namespace API.Models; public class Liquor: Item { public string? LiquorType { get; set; } - - public string SellLiquor() - { - - } } diff --git a/Program.cs b/Program.cs index df95005..6305ed7 100644 --- a/Program.cs +++ b/Program.cs @@ -2,6 +2,7 @@ using Microsoft.IdentityModel.Tokens; using API.Models; using API.Services; +using API.Services.Authentication; using Microsoft.AspNetCore.Authentication.JwtBearer; var builder = WebApplication.CreateBuilder(args); @@ -49,6 +50,8 @@ new MySqlServerVersion(new Version(8, 0, 26))); // Specify the MySQL server version here }); builder.Services.AddScoped(); +builder.Services.AddScoped(); + // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); @@ -74,4 +77,4 @@ app.MapControllers(); -app.Run(); \ No newline at end of file +app.Run(); diff --git a/Services/Authentication/TokenService.cs b/Services/Authentication/TokenService.cs index 8edf964..54ff9af 100644 --- a/Services/Authentication/TokenService.cs +++ b/Services/Authentication/TokenService.cs @@ -8,12 +8,10 @@ namespace API.Services.Authentication; public class TokenService : ITokenService { - private readonly DBContext _context; private readonly IConfiguration _configuration; - public TokenService(DBContext context, IConfiguration configuration) + public TokenService(IConfiguration configuration) { - _context = context; _configuration = configuration; } @@ -31,8 +29,8 @@ public string GenerateToken(User user) Subject = new ClaimsIdentity(new Claim[] { new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), - new Claim(ClaimTypes.Email, user.GetEmail()), - new Claim(ClaimTypes.Role, user.Role.ToString()), + new Claim(ClaimTypes.Email, user.Email), + new Claim(ClaimTypes.Role, user.GetClassName()), // Add any other claims you want to include in the token }), Expires = DateTime.UtcNow.AddHours(24), diff --git a/Services/AuthenticationService.cs b/Services/AuthenticationService.cs index 9490cba..d700dcb 100644 --- a/Services/AuthenticationService.cs +++ b/Services/AuthenticationService.cs @@ -1,9 +1,8 @@ -using System.IdentityModel.Tokens.Jwt; -using System.Security.Claims; -using Microsoft.EntityFrameworkCore; -using Microsoft.IdentityModel.Tokens; using API.Dtos; using API.Models; +using API.Models.Authentication; +using API.Services.Authentication; +using Microsoft.EntityFrameworkCore; namespace API.Services; @@ -11,13 +10,17 @@ public class AuthenticationService : IAuthenticationService { private readonly DBContext _context; private readonly IConfiguration _configuration; + private readonly ITokenService _tokenService; - public AuthenticationService(DBContext context, IConfiguration configuration) + public AuthenticationService(DBContext dbContext, IConfiguration configuration, ITokenService tokenService) { - _context = context; + _context = dbContext; _configuration = configuration; + _tokenService = tokenService; } + + /// /// Checks if email is signed up /// @@ -28,28 +31,22 @@ public async Task UserSignedUp(string email) return await _context.Users.FirstOrDefaultAsync(u => u.Email == email); } - public async Task Login(AuthenticationDto user) + public async Task Login(AuthenticationDto user) { - var dbUser = User.LoginUser(_context, user.Email, user.Password); + var dbUser = await _context.Users.FirstOrDefaultAsync(u => u.Email == user.Email && u.Password == user.Password); if (dbUser == null) { return null; } - var token = GenerateToken(dbUser); - var tokenExpiration = DateTime.UtcNow.AddHours(24); + var token = _tokenService.GenerateToken(dbUser); - dbUser.Token = token; - dbUser.TokenExpiration = tokenExpiration; + var authPas = dbUser.SetToken(_context, token, DateTime.Now.AddHours(24)); await _context.SaveChangesAsync(); - return new AuthModel - { - Token = token, - UserRole = dbUser.Role - }; + return authPas; } /// @@ -57,9 +54,9 @@ public async Task Login(AuthenticationDto user) /// /// /// A Token and an Expiration - public async Task AuthenticateUser(AuthenticationDto user) + public async Task AuthenticateUser(AuthenticationDto user) { - + return null; } /// @@ -76,8 +73,7 @@ public async Task LogOut(string token) return false; } - dbUser.Token = null; - dbUser.TokenExpiration = null; + dbUser.SetToken(_context, null, new DateTime()); await _context.SaveChangesAsync(); @@ -100,8 +96,7 @@ public async Task VerifyToken(string token) if (dbUser.TokenExpiration == null || dbUser.TokenExpiration <= DateTime.Now) { - dbUser.Token = null; - dbUser.TokenExpiration = null; + dbUser.SetToken(_context,null, new DateTime()); await _context.SaveChangesAsync(); return false; } @@ -109,13 +104,18 @@ public async Task VerifyToken(string token) return true; } - public Task SignupUser(string token) + public async Task CreateNewUser(UserDto user) { - throw new NotImplementedException(); - } + var customer = new Customer(user.Id, user.FirstName, user.LastName, user.Phone, user.Email, user.Password, user.Token, user.TokenExpiration); - /** - * Generates a token - */ + await _context.Customers.AddAsync(customer); + await _context.SaveChangesAsync(); + + return true; + } + public async Task> GetUsers() + { + return await _context.Users.ToListAsync(); + } } diff --git a/Services/IAuthenticationService.cs b/Services/IAuthenticationService.cs index 93a9637..14c7abc 100644 --- a/Services/IAuthenticationService.cs +++ b/Services/IAuthenticationService.cs @@ -5,15 +5,15 @@ namespace API.Services; public interface IAuthenticationService { - Task UserSignedUp(string email); + Task CreateNewUser(UserDto user); - Task Login(AuthenticationDto user); + Task Login(AuthenticationDto user); - Task AuthenticateUser(AuthenticationDto user); + Task AuthenticateUser(AuthenticationDto user); Task LogOut(string token); Task VerifyToken(string token); - Task SignupUser(string token); + Task> GetUsers(); } From 7d23229fa522f7a5737b4ef58fca12935ea92192 Mon Sep 17 00:00:00 2001 From: Thomas Andersen Date: Mon, 6 Nov 2023 20:39:41 +0100 Subject: [PATCH 008/112] SW-65 * Fixed public private fields and dbcontext --- .../Shared/AuthenticationController.cs | 6 ++-- Controllers/Shared/UserController.cs | 4 ++- DataTransferObjects/AuthPas.cs | 4 +-- .../{AuthenticationDto.cs => LoginDto.cs} | 2 +- .../{UserDto.cs => SignupDto.cs} | 6 ++-- Models/Authentication/Admin.cs | 21 +++++++++++--- Models/Authentication/Customer.cs | 16 ++++++++--- Models/Authentication/Employee.cs | 18 ++++++++---- Models/Authentication/User.cs | 28 ++++++++++++++++++- Models/DBContext.cs | 20 ++++++------- Program.cs | 1 - Services/AuthenticationService.cs | 15 +++++----- Services/IAuthenticationService.cs | 6 ++-- 13 files changed, 100 insertions(+), 47 deletions(-) rename DataTransferObjects/{AuthenticationDto.cs => LoginDto.cs} (77%) rename DataTransferObjects/{UserDto.cs => SignupDto.cs} (60%) diff --git a/Controllers/Shared/AuthenticationController.cs b/Controllers/Shared/AuthenticationController.cs index 63483d5..b9e12ba 100644 --- a/Controllers/Shared/AuthenticationController.cs +++ b/Controllers/Shared/AuthenticationController.cs @@ -17,7 +17,7 @@ public AuthenticationController(IAuthenticationService authService) } [HttpPost("Login")] - public async Task> Login(AuthenticationDto user) + public async Task> Login(LoginDto user) { if(user == null) { @@ -30,9 +30,9 @@ public async Task> Login(AuthenticationDto user) } [HttpPost("Signup")] - public async Task> Signup(UserDto user) + public async Task> Signup(SignupDto signup) { - var authenticationResult = await _authenticationService.CreateNewUser(user); + var authenticationResult = await _authenticationService.CreateNewUser(signup); if (authenticationResult == null) { diff --git a/Controllers/Shared/UserController.cs b/Controllers/Shared/UserController.cs index e602ca7..e771ffe 100644 --- a/Controllers/Shared/UserController.cs +++ b/Controllers/Shared/UserController.cs @@ -1,5 +1,6 @@ using API.Models; using API.Services; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace API.Controllers.Shared @@ -15,7 +16,8 @@ public UserController(IAuthenticationService authService) _authenticationService = authService; } - [HttpGet("Users")] + [HttpGet] + [Authorize(Roles = "Admin")] public async Task>> GetUsers() { var users = await _authenticationService.GetUsers(); diff --git a/DataTransferObjects/AuthPas.cs b/DataTransferObjects/AuthPas.cs index 544ef98..99869b9 100644 --- a/DataTransferObjects/AuthPas.cs +++ b/DataTransferObjects/AuthPas.cs @@ -2,8 +2,8 @@ namespace API.Dtos; public class AuthPas { - public string Token { get; protected set; } - public DateTime ExpirationDate { get; protected set; } + public string Token { get; set; } + public DateTime? ExpirationDate { get; set; } public AuthPas(string token, DateTime? expirationDate) { diff --git a/DataTransferObjects/AuthenticationDto.cs b/DataTransferObjects/LoginDto.cs similarity index 77% rename from DataTransferObjects/AuthenticationDto.cs rename to DataTransferObjects/LoginDto.cs index cea088f..9e4f431 100644 --- a/DataTransferObjects/AuthenticationDto.cs +++ b/DataTransferObjects/LoginDto.cs @@ -1,6 +1,6 @@ namespace API.Dtos; -public class AuthenticationDto +public class LoginDto { public string Email { get; set; } public string Password { get; set; } diff --git a/DataTransferObjects/UserDto.cs b/DataTransferObjects/SignupDto.cs similarity index 60% rename from DataTransferObjects/UserDto.cs rename to DataTransferObjects/SignupDto.cs index da77534..45fc664 100644 --- a/DataTransferObjects/UserDto.cs +++ b/DataTransferObjects/SignupDto.cs @@ -1,13 +1,11 @@ namespace API.Dtos; -public class UserDto +public class SignupDto { - public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public int Phone { get; set; } public string Email { get; set; } public string Password { get; set; } - public string Token { get; set; } - public DateTime? TokenExpiration { get; set; } + public int DesiredRole { get; set; } } diff --git a/Models/Authentication/Admin.cs b/Models/Authentication/Admin.cs index 9c816af..2730d65 100644 --- a/Models/Authentication/Admin.cs +++ b/Models/Authentication/Admin.cs @@ -1,12 +1,25 @@ namespace API.Models.Authentication; -public class Admin : User +public class Admin : Employee { - private readonly DBContext _context; + public Admin(string firstName, string lastName, int phone, string email, string password) + { + FirstName = firstName; + LastName = lastName; + Phone = phone; + Email = email; + Password = password; + Token = ""; + } - public Admin(DBContext context) + public Admin(User user) { - _context = context; + FirstName = user.FirstName; + LastName = user.LastName; + Phone = user.Phone; + Email = user.Email; + Password = user.Password; + Token = ""; } override diff --git a/Models/Authentication/Customer.cs b/Models/Authentication/Customer.cs index 9d359af..8c87526 100644 --- a/Models/Authentication/Customer.cs +++ b/Models/Authentication/Customer.cs @@ -2,16 +2,24 @@ namespace API.Models.Authentication; public class Customer : User { - public Customer(int id, string firstName, string lastName, int phone, string email, string password, string token, DateTime? tokenExpiration) + public Customer(string firstName, string lastName, int phone, string email, string password) { - Id = id; FirstName = firstName; LastName = lastName; Phone = phone; Email = email; Password = password; - Token = token; - TokenExpiration = tokenExpiration; + Token = ""; + } + + public Customer(User user) + { + FirstName = user.FirstName; + LastName = user.LastName; + Phone = user.Phone; + Email = user.Email; + Password = user.Password; + Token = ""; } override diff --git a/Models/Authentication/Employee.cs b/Models/Authentication/Employee.cs index a424b32..93f17fc 100644 --- a/Models/Authentication/Employee.cs +++ b/Models/Authentication/Employee.cs @@ -4,18 +4,26 @@ namespace API.Models.Authentication; public class Employee : User { - private readonly DBContext _context; + public Employee(){} - public Employee(int id, string firstName, string lastName, int phone, string email, string password, string token, DateTime? tokenExpiration) + public Employee(string firstName, string lastName, int phone, string email, string password) { - Id = id; FirstName = firstName; LastName = lastName; Phone = phone; Email = email; Password = password; - Token = token; - TokenExpiration = tokenExpiration; + Token = ""; + } + + public Employee(User user) + { + FirstName = user.FirstName; + LastName = user.LastName; + Phone = user.Phone; + Email = user.Email; + Password = user.Password; + Token = ""; } override diff --git a/Models/Authentication/User.cs b/Models/Authentication/User.cs index 91ab42e..0850de2 100644 --- a/Models/Authentication/User.cs +++ b/Models/Authentication/User.cs @@ -1,4 +1,5 @@ using API.Dtos; +using API.Models.Authentication; namespace API.Models; @@ -13,7 +14,32 @@ public abstract class User public string Token { get; protected set; } public DateTime? TokenExpiration { get; protected set; } - public AuthPas SetToken(DBContext dbContext,string token, DateTime tokenExpiration) + public static User CreateNewUser(DBContext context,SignupDto signupDto) + { + User newUser; + + switch (signupDto.DesiredRole) + { + case 1: + newUser = new Employee(signupDto.FirstName, signupDto.LastName, signupDto.Phone, signupDto.Email, + signupDto.Password); + context.Employees.Add(newUser as Employee); + break; + case 2: + newUser = new Admin(signupDto.FirstName, signupDto.LastName, signupDto.Phone, signupDto.Email, + signupDto.Password); + context.Admins.Add(newUser as Admin); + break; + default: + newUser = new Customer(signupDto.FirstName, signupDto.LastName, signupDto.Phone, signupDto.Email, + signupDto.Password); + context.Customers.Add(newUser as Customer); + break; + } + return newUser; + } + + public AuthPas SetToken(string token, DateTime tokenExpiration) { Token = token; TokenExpiration = tokenExpiration; diff --git a/Models/DBContext.cs b/Models/DBContext.cs index 0b369c8..5f00439 100644 --- a/Models/DBContext.cs +++ b/Models/DBContext.cs @@ -9,14 +9,14 @@ public DBContext(DbContextOptions options) : base(options) { } - public DbSet Employees { get; set; } = null!; - public DbSet Admins { get; set; } = null!; - public DbSet Customers { get; set; } = null!; - public DbSet Users { get; set; } = null!; - public DbSet Liquors { get; set; } = null!; - public DbSet Chocolates { get; set; } = null!; - public DbSet Items { get; set; } = null!; - public DbSet Wines { get; set; } = null!; - public DbSet Suppliers { get; set; } = null!; - public DbSet Order { get; set; } = null!; + public DbSet Employees { get; set; } + public DbSet Admins { get; set; } + public DbSet Customers { get; set; } + public DbSet Users { get; set; } + public DbSet Liquors { get; set; } + public DbSet Chocolates { get; set; } + public DbSet Items { get; set; } + public DbSet Wines { get; set; } + public DbSet Suppliers { get; set; } + public DbSet Order { get; set; } } diff --git a/Program.cs b/Program.cs index 6305ed7..2565c74 100644 --- a/Program.cs +++ b/Program.cs @@ -41,7 +41,6 @@ { options.AddPolicy("RequireAdminRole", policy => policy.RequireRole("Admin")); - // Add more policies as needed }); builder.Services.AddDbContext(opt => diff --git a/Services/AuthenticationService.cs b/Services/AuthenticationService.cs index d700dcb..3b8449e 100644 --- a/Services/AuthenticationService.cs +++ b/Services/AuthenticationService.cs @@ -31,7 +31,7 @@ public async Task UserSignedUp(string email) return await _context.Users.FirstOrDefaultAsync(u => u.Email == email); } - public async Task Login(AuthenticationDto user) + public async Task Login(LoginDto user) { var dbUser = await _context.Users.FirstOrDefaultAsync(u => u.Email == user.Email && u.Password == user.Password); @@ -42,7 +42,7 @@ public async Task Login(AuthenticationDto user) var token = _tokenService.GenerateToken(dbUser); - var authPas = dbUser.SetToken(_context, token, DateTime.Now.AddHours(24)); + var authPas = dbUser.SetToken(token, DateTime.Now.AddHours(24)); await _context.SaveChangesAsync(); @@ -54,7 +54,7 @@ public async Task Login(AuthenticationDto user) /// /// /// A Token and an Expiration - public async Task AuthenticateUser(AuthenticationDto user) + public async Task AuthenticateUser(LoginDto user) { return null; } @@ -73,7 +73,7 @@ public async Task LogOut(string token) return false; } - dbUser.SetToken(_context, null, new DateTime()); + dbUser.SetToken("", new DateTime()); await _context.SaveChangesAsync(); @@ -96,7 +96,7 @@ public async Task VerifyToken(string token) if (dbUser.TokenExpiration == null || dbUser.TokenExpiration <= DateTime.Now) { - dbUser.SetToken(_context,null, new DateTime()); + dbUser.SetToken(null, new DateTime()); await _context.SaveChangesAsync(); return false; } @@ -104,11 +104,10 @@ public async Task VerifyToken(string token) return true; } - public async Task CreateNewUser(UserDto user) + public async Task CreateNewUser(SignupDto signup) { - var customer = new Customer(user.Id, user.FirstName, user.LastName, user.Phone, user.Email, user.Password, user.Token, user.TokenExpiration); + User.CreateNewUser(_context, signup); - await _context.Customers.AddAsync(customer); await _context.SaveChangesAsync(); return true; diff --git a/Services/IAuthenticationService.cs b/Services/IAuthenticationService.cs index 14c7abc..2f708ed 100644 --- a/Services/IAuthenticationService.cs +++ b/Services/IAuthenticationService.cs @@ -5,11 +5,11 @@ namespace API.Services; public interface IAuthenticationService { - Task CreateNewUser(UserDto user); + Task CreateNewUser(SignupDto signup); - Task Login(AuthenticationDto user); + Task Login(LoginDto user); - Task AuthenticateUser(AuthenticationDto user); + Task AuthenticateUser(LoginDto user); Task LogOut(string token); From 043dbf1131ae6d5dfbf1d8485b697223aa830906 Mon Sep 17 00:00:00 2001 From: Thomas Andersen Date: Wed, 8 Nov 2023 10:18:16 +0100 Subject: [PATCH 009/112] SW-65 * Look minor changes --- API.csproj | 1 - Controllers/OrderController.cs | 105 ------------------ .../Shared/AuthenticationController.cs | 58 ++++------ Controllers/Shared/UserController.cs | 14 +-- DataTransferObjects/AuthPas.cs | 2 +- DataTransferObjects/ItemDto.cs | 2 +- DataTransferObjects/LoginDto.cs | 2 +- DataTransferObjects/SignupDto.cs | 2 +- Models/Authentication/Employee.cs | 2 - Models/Authentication/User.cs | 13 ++- Program.cs | 4 +- Services/IAuthenticationService.cs | 19 ---- .../{ => Shared}/AuthenticationService.cs | 78 ++++++------- Services/Shared/IAuthenticationService.cs | 13 +++ .../ITokenService.cs | 4 +- Services/Shared/IUserService.cs | 8 ++ .../TokenService.cs | 19 ++-- Services/Shared/UserService.cs | 20 ++++ 18 files changed, 126 insertions(+), 240 deletions(-) delete mode 100644 Services/IAuthenticationService.cs rename Services/{ => Shared}/AuthenticationService.cs (50%) create mode 100644 Services/Shared/IAuthenticationService.cs rename Services/{Authentication => Shared}/ITokenService.cs (55%) create mode 100644 Services/Shared/IUserService.cs rename Services/{Authentication => Shared}/TokenService.cs (68%) create mode 100644 Services/Shared/UserService.cs diff --git a/API.csproj b/API.csproj index 229da36..93a1edb 100644 --- a/API.csproj +++ b/API.csproj @@ -26,7 +26,6 @@ - diff --git a/Controllers/OrderController.cs b/Controllers/OrderController.cs index 750c5af..8e6bb61 100644 --- a/Controllers/OrderController.cs +++ b/Controllers/OrderController.cs @@ -1,8 +1,3 @@ -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 API.Models; @@ -21,105 +16,5 @@ public OrderController(DBContext context) { _context = context; } - - // GET: api/Order - [HttpGet] - public async Task>> GetOrder() - { - if (_context.Order == null) - { - return NotFound(); - } - return await _context.Order.ToListAsync(); - } - - // GET: api/Order/5 - [HttpGet("{id}")] - public async Task> GetOrder(int id) - { - if (_context.Order == null) - { - return NotFound(); - } - var order = await _context.Order.FindAsync(id); - - if (order == null) - { - return NotFound(); - } - - return order; - } - - // PUT: api/Order/5 - // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754 - [HttpPut("{id}")] - public async Task PutOrder(int id, Order order) - { - if (id != order.Id) - { - return BadRequest(); - } - - _context.Entry(order).State = EntityState.Modified; - - try - { - await _context.SaveChangesAsync(); - } - catch (DbUpdateConcurrencyException) - { - if (!OrderExists(id)) - { - return NotFound(); - } - else - { - throw; - } - } - - return NoContent(); - } - - // POST: api/Order - // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754 - [HttpPost] - public async Task> PostOrder(Order order) - { - if (_context.Order == null) - { - return Problem("Entity set 'DBContext.Order' is null."); - } - _context.Order.Add(order); - await _context.SaveChangesAsync(); - - return CreatedAtAction("GetOrder", new { id = order.Id }, order); - } - - // DELETE: api/Order/5 - [HttpDelete("{id}")] - public async Task DeleteOrder(int id) - { - if (_context.Order == null) - { - return NotFound(); - } - var order = await _context.Order.FindAsync(id); - if (order == null) - { - return NotFound(); - } - - _context.Order.Remove(order); - await _context.SaveChangesAsync(); - - return NoContent(); - } - - private bool OrderExists(int id) - { - return (_context.Order?.Any(e => e.Id == id)).GetValueOrDefault(); - } } } diff --git a/Controllers/Shared/AuthenticationController.cs b/Controllers/Shared/AuthenticationController.cs index b9e12ba..f2dfe85 100644 --- a/Controllers/Shared/AuthenticationController.cs +++ b/Controllers/Shared/AuthenticationController.cs @@ -1,6 +1,5 @@ -using API.Dtos; -using API.Models; -using API.Services; +using API.DataTransferObjects; +using API.Services.Shared; using Microsoft.AspNetCore.Mvc; namespace API.Controllers.Shared @@ -17,59 +16,44 @@ public AuthenticationController(IAuthenticationService authService) } [HttpPost("Login")] - public async Task> Login(LoginDto user) + public async Task> Login(LoginDto loginDto) { - if(user == null) + try { - return BadRequest("User is missing"); + var authPas = await _authenticationService.Login(loginDto); + return authPas; + } + catch (Exception e) + { + Console.WriteLine(e.Message); + return BadRequest(e.Message); } - - var authenticationResult = await _authenticationService.Login(user); - - return authenticationResult; } [HttpPost("Signup")] - public async Task> Signup(SignupDto signup) + public async Task> Signup(SignupDto signupDto) { - var authenticationResult = await _authenticationService.CreateNewUser(signup); - - if (authenticationResult == null) - { - return BadRequest("Incorrect username or password"); - } - - return Ok(authenticationResult); - + return Ok(await _authenticationService.CreateNewUser(signupDto)); } [HttpPost("LogOut")] - public async Task> Logout([FromBody] AuthPas pas) + public async Task> Logout([FromBody] AuthPas authPas) { - if (string.IsNullOrEmpty(pas?.Token)) + try { - return BadRequest("Token is missing"); + return Ok(await _authenticationService.LogOut(authPas)); } - - var logoutResult = await _authenticationService.LogOut(pas.Token); - - if (logoutResult) + catch (Exception e) { - return Ok(true); + Console.WriteLine(e); + return BadRequest(e.Message); } - - return BadRequest("Logout failed"); } [HttpPost("Verify")] - public async Task> VerifyToken([FromBody] AuthPas pas) + public async Task> VerifyToken([FromBody] AuthPas authPas) { - if (string.IsNullOrEmpty(pas?.Token)) - { - return BadRequest("Token is missing"); - } - - var verified = await _authenticationService.VerifyToken(pas.Token); + var verified = await _authenticationService.VerifyToken(authPas); if (verified) { diff --git a/Controllers/Shared/UserController.cs b/Controllers/Shared/UserController.cs index e771ffe..310771a 100644 --- a/Controllers/Shared/UserController.cs +++ b/Controllers/Shared/UserController.cs @@ -1,5 +1,5 @@ -using API.Models; -using API.Services; +using API.Models.Authentication; +using API.Services.Shared; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -9,20 +9,18 @@ namespace API.Controllers.Shared [ApiController] public class UserController { - private readonly IAuthenticationService _authenticationService; + private readonly IUserService _userService; - public UserController(IAuthenticationService authService) + public UserController(IUserService userService) { - _authenticationService = authService; + _userService = userService; } [HttpGet] [Authorize(Roles = "Admin")] public async Task>> GetUsers() { - var users = await _authenticationService.GetUsers(); - - return users; + return await _userService.GetAllUsers(); } } } diff --git a/DataTransferObjects/AuthPas.cs b/DataTransferObjects/AuthPas.cs index 99869b9..30c3242 100644 --- a/DataTransferObjects/AuthPas.cs +++ b/DataTransferObjects/AuthPas.cs @@ -1,4 +1,4 @@ -namespace API.Dtos; +namespace API.DataTransferObjects; public class AuthPas { diff --git a/DataTransferObjects/ItemDto.cs b/DataTransferObjects/ItemDto.cs index b14e7d0..11d5f1a 100644 --- a/DataTransferObjects/ItemDto.cs +++ b/DataTransferObjects/ItemDto.cs @@ -1,4 +1,4 @@ -namespace API.Dtos; +namespace API.DataTransferObjects; public class ItemDto { diff --git a/DataTransferObjects/LoginDto.cs b/DataTransferObjects/LoginDto.cs index 9e4f431..7634928 100644 --- a/DataTransferObjects/LoginDto.cs +++ b/DataTransferObjects/LoginDto.cs @@ -1,4 +1,4 @@ -namespace API.Dtos; +namespace API.DataTransferObjects; public class LoginDto { diff --git a/DataTransferObjects/SignupDto.cs b/DataTransferObjects/SignupDto.cs index 45fc664..66a5752 100644 --- a/DataTransferObjects/SignupDto.cs +++ b/DataTransferObjects/SignupDto.cs @@ -1,4 +1,4 @@ -namespace API.Dtos; +namespace API.DataTransferObjects; public class SignupDto { diff --git a/Models/Authentication/Employee.cs b/Models/Authentication/Employee.cs index 93f17fc..27bf433 100644 --- a/Models/Authentication/Employee.cs +++ b/Models/Authentication/Employee.cs @@ -1,5 +1,3 @@ -using Microsoft.EntityFrameworkCore; - namespace API.Models.Authentication; public class Employee : User diff --git a/Models/Authentication/User.cs b/Models/Authentication/User.cs index 0850de2..37dcefe 100644 --- a/Models/Authentication/User.cs +++ b/Models/Authentication/User.cs @@ -1,7 +1,6 @@ -using API.Dtos; -using API.Models.Authentication; +using API.DataTransferObjects; -namespace API.Models; +namespace API.Models.Authentication; public abstract class User { @@ -39,11 +38,15 @@ public static User CreateNewUser(DBContext context,SignupDto signupDto) return newUser; } - public AuthPas SetToken(string token, DateTime tokenExpiration) + public void SetToken(string token, DateTime? tokenExpiration) { Token = token; TokenExpiration = tokenExpiration; - return new AuthPas(token, tokenExpiration); + } + + public AuthPas GetTokenAuthPas() + { + return new AuthPas(Token, TokenExpiration); } public abstract string GetClassName(); diff --git a/Program.cs b/Program.cs index 2565c74..a367f52 100644 --- a/Program.cs +++ b/Program.cs @@ -1,8 +1,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; using API.Models; -using API.Services; -using API.Services.Authentication; +using API.Services.Shared; using Microsoft.AspNetCore.Authentication.JwtBearer; var builder = WebApplication.CreateBuilder(args); @@ -50,6 +49,7 @@ }); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); diff --git a/Services/IAuthenticationService.cs b/Services/IAuthenticationService.cs deleted file mode 100644 index 2f708ed..0000000 --- a/Services/IAuthenticationService.cs +++ /dev/null @@ -1,19 +0,0 @@ -using API.Dtos; -using API.Models; - -namespace API.Services; - -public interface IAuthenticationService -{ - Task CreateNewUser(SignupDto signup); - - Task Login(LoginDto user); - - Task AuthenticateUser(LoginDto user); - - Task LogOut(string token); - - Task VerifyToken(string token); - - Task> GetUsers(); -} diff --git a/Services/AuthenticationService.cs b/Services/Shared/AuthenticationService.cs similarity index 50% rename from Services/AuthenticationService.cs rename to Services/Shared/AuthenticationService.cs index 3b8449e..c60e3fa 100644 --- a/Services/AuthenticationService.cs +++ b/Services/Shared/AuthenticationService.cs @@ -1,10 +1,9 @@ -using API.Dtos; +using API.DataTransferObjects; using API.Models; using API.Models.Authentication; -using API.Services.Authentication; using Microsoft.EntityFrameworkCore; -namespace API.Services; +namespace API.Services.Shared; public class AuthenticationService : IAuthenticationService { @@ -19,61 +18,46 @@ public AuthenticationService(DBContext dbContext, IConfiguration configuration, _tokenService = tokenService; } - - /// - /// Checks if email is signed up + /// Finds a user in the database and generates a token for them /// - /// Email to check - /// User if email is signed up or null - public async Task UserSignedUp(string email) - { - return await _context.Users.FirstOrDefaultAsync(u => u.Email == email); - } - - public async Task Login(LoginDto user) + /// The login data + /// An authentication pas for the users + /// If the email or password is incorrect + public async Task Login(LoginDto loginDto) { - var dbUser = await _context.Users.FirstOrDefaultAsync(u => u.Email == user.Email && u.Password == user.Password); + var dbUser = await _context.Users.FirstOrDefaultAsync(u => u.Email == loginDto.Email && u.Password == loginDto.Password); if (dbUser == null) { - return null; + throw new Exception("Incorrect email or password"); } var token = _tokenService.GenerateToken(dbUser); - var authPas = dbUser.SetToken(token, DateTime.Now.AddHours(24)); + dbUser.SetToken(token, DateTime.Now.AddHours(24)); await _context.SaveChangesAsync(); - return authPas; + return dbUser.GetTokenAuthPas(); } /// - /// Authenticates the user with email and password + /// Logs out a user given their token /// - /// - /// A Token and an Expiration - public async Task AuthenticateUser(LoginDto user) - { - return null; - } - - /// - /// Log outs the user - /// - /// The token of the user + /// The pas with the token to logout /// - public async Task LogOut(string token) + /// + public async Task LogOut(AuthPas authPas) { - var dbUser = await _context.Users.FirstOrDefaultAsync(u => u.Token == token); + var dbUser = await _context.Users.FirstOrDefaultAsync(u => u.Token == authPas.Token); if (dbUser == null) { - return false; + throw new Exception("Failed trying to logout incorrect token"); } - dbUser.SetToken("", new DateTime()); + dbUser.SetToken("", null); await _context.SaveChangesAsync(); @@ -81,22 +65,22 @@ public async Task LogOut(string token) } /// - /// Verifies a given token + /// Verifies a user by a given token /// - /// Token to verify + /// The pas with token to verify /// - public async Task VerifyToken(string token) + public async Task VerifyToken(AuthPas authPas) { - var dbUser = await _context.Users.FirstOrDefaultAsync(u => u.Token == token); + var dbUser = await _context.Users.FirstOrDefaultAsync(u => u.Token == authPas.Token); if (dbUser == null) { - return false; + throw new Exception("Failed trying to verify incorrect token"); } if (dbUser.TokenExpiration == null || dbUser.TokenExpiration <= DateTime.Now) { - dbUser.SetToken(null, new DateTime()); + dbUser.SetToken("", null); await _context.SaveChangesAsync(); return false; } @@ -104,17 +88,17 @@ public async Task VerifyToken(string token) return true; } - public async Task CreateNewUser(SignupDto signup) + /// + /// Creates a new user with a signupDto + /// + /// The sign up data + /// A success boolean + public async Task CreateNewUser(SignupDto signupDto) { - User.CreateNewUser(_context, signup); + User.CreateNewUser(_context, signupDto); await _context.SaveChangesAsync(); return true; } - - public async Task> GetUsers() - { - return await _context.Users.ToListAsync(); - } } diff --git a/Services/Shared/IAuthenticationService.cs b/Services/Shared/IAuthenticationService.cs new file mode 100644 index 0000000..0c9a618 --- /dev/null +++ b/Services/Shared/IAuthenticationService.cs @@ -0,0 +1,13 @@ +using API.DataTransferObjects; + +namespace API.Services.Shared; + +public interface IAuthenticationService +{ + Task CreateNewUser(SignupDto signupDto); + + Task Login(LoginDto loginDto); + + Task LogOut(AuthPas authPas); + + Task VerifyToken(AuthPas authPas); } diff --git a/Services/Authentication/ITokenService.cs b/Services/Shared/ITokenService.cs similarity index 55% rename from Services/Authentication/ITokenService.cs rename to Services/Shared/ITokenService.cs index efa7a69..4b5eb57 100644 --- a/Services/Authentication/ITokenService.cs +++ b/Services/Shared/ITokenService.cs @@ -1,6 +1,6 @@ -using API.Models; +using API.Models.Authentication; -namespace API.Services.Authentication; +namespace API.Services.Shared; public interface ITokenService { diff --git a/Services/Shared/IUserService.cs b/Services/Shared/IUserService.cs new file mode 100644 index 0000000..66bfc7c --- /dev/null +++ b/Services/Shared/IUserService.cs @@ -0,0 +1,8 @@ +using API.Models.Authentication; + +namespace API.Services.Shared; + +public interface IUserService +{ + public Task> GetAllUsers(); +} diff --git a/Services/Authentication/TokenService.cs b/Services/Shared/TokenService.cs similarity index 68% rename from Services/Authentication/TokenService.cs rename to Services/Shared/TokenService.cs index 54ff9af..219b885 100644 --- a/Services/Authentication/TokenService.cs +++ b/Services/Shared/TokenService.cs @@ -1,10 +1,10 @@ using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using Microsoft.IdentityModel.Tokens; -using API.Models; +using API.Models.Authentication; -namespace API.Services.Authentication; +namespace API.Services.Shared; public class TokenService : ITokenService { @@ -15,23 +15,26 @@ public TokenService(IConfiguration configuration) _configuration = configuration; } + /// + /// Generates a token for a given user + /// + /// The user to generate a token for + /// Token as a string public string GenerateToken(User user) { var tokenHandler = new JwtSecurityTokenHandler(); - var key = "b0493a0d-f88e-4b0b-94eb-665f7207c92c"u8.ToArray(); // Use the same secret key as in your JWT configuration + var key = "b0493a0d-f88e-4b0b-94eb-665f7207c92c"u8.ToArray(); var tokenDescriptor = new SecurityTokenDescriptor { Claims = new Dictionary { - { "aud", "ApiAppAudience" }, // Use the same audience name as in your JWT configuration + { "aud", "ApiAppAudience" }, { "iss", "ApiAppIssuer" } }, Subject = new ClaimsIdentity(new Claim[] { - new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), - new Claim(ClaimTypes.Email, user.Email), - new Claim(ClaimTypes.Role, user.GetClassName()), - // Add any other claims you want to include in the token + new (ClaimTypes.Email, user.Email), + new (ClaimTypes.Role, user.GetClassName()), }), Expires = DateTime.UtcNow.AddHours(24), SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) diff --git a/Services/Shared/UserService.cs b/Services/Shared/UserService.cs new file mode 100644 index 0000000..838675b --- /dev/null +++ b/Services/Shared/UserService.cs @@ -0,0 +1,20 @@ +using API.Models; +using API.Models.Authentication; +using Microsoft.EntityFrameworkCore; + +namespace API.Services.Shared; + +public class UserService : IUserService +{ + private readonly DBContext _context; + + public UserService(DBContext dbContext) + { + _context = dbContext; + } + + public async Task> GetAllUsers() + { + return await _context.Users.ToListAsync(); + } +} From 90464e4ee7f7a0d15b0808f0ae9a8b10cfa4b5e8 Mon Sep 17 00:00:00 2001 From: Thomas Andersen Date: Wed, 8 Nov 2023 10:23:07 +0100 Subject: [PATCH 010/112] SW-65 * Fixed verifytoken to use exceptions --- Controllers/Shared/AuthenticationController.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Controllers/Shared/AuthenticationController.cs b/Controllers/Shared/AuthenticationController.cs index f2dfe85..01a7c81 100644 --- a/Controllers/Shared/AuthenticationController.cs +++ b/Controllers/Shared/AuthenticationController.cs @@ -53,14 +53,15 @@ public async Task> Logout([FromBody] AuthPas authPas) [HttpPost("Verify")] public async Task> VerifyToken([FromBody] AuthPas authPas) { - var verified = await _authenticationService.VerifyToken(authPas); - - if (verified) + try { - return Ok(true); + return Ok(await _authenticationService.VerifyToken(authPas)); + } + catch (Exception e) + { + Console.WriteLine(e); + return BadRequest(e.Message); } - - return BadRequest("Verification failed"); } } } From d76dd634f8bd65f2c8bc4a612eb9a21cb61ac7e6 Mon Sep 17 00:00:00 2001 From: Thomas Andersen Date: Wed, 8 Nov 2023 10:35:02 +0100 Subject: [PATCH 011/112] SW-65 * Fixed exceptions --- Services/Shared/AuthenticationService.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Services/Shared/AuthenticationService.cs b/Services/Shared/AuthenticationService.cs index c60e3fa..7b1c58e 100644 --- a/Services/Shared/AuthenticationService.cs +++ b/Services/Shared/AuthenticationService.cs @@ -23,7 +23,7 @@ public AuthenticationService(DBContext dbContext, IConfiguration configuration, /// /// The login data /// An authentication pas for the users - /// If the email or password is incorrect + /// public async Task Login(LoginDto loginDto) { var dbUser = await _context.Users.FirstOrDefaultAsync(u => u.Email == loginDto.Email && u.Password == loginDto.Password); @@ -64,11 +64,13 @@ public async Task LogOut(AuthPas authPas) return true; } + /// /// Verifies a user by a given token /// /// The pas with token to verify - /// + /// Success boolean + /// public async Task VerifyToken(AuthPas authPas) { var dbUser = await _context.Users.FirstOrDefaultAsync(u => u.Token == authPas.Token); From 658f4ac15d054ea45610728c095ef8c5390a7378 Mon Sep 17 00:00:00 2001 From: ThomasSteenAndersen Date: Wed, 15 Nov 2023 22:14:10 +0100 Subject: [PATCH 012/112] Defining classes --- Models/Authentication/Admin.cs | 11 +++++++++ Models/Authentication/Customer.cs | 26 ++++++++++++++++++++++ Models/Authentication/Employee.cs | 11 +++++++++ Models/Authentication/User.cs | 2 ++ Models/DBContext.cs | 5 ++++- Models/Item/Chocolate.cs | 6 ----- Models/Item/Item.cs | 14 ------------ Models/Item/Liquor.cs | 6 ----- Models/Item/Wine.cs | 6 ----- Models/Items/DefaultItem.cs | 14 ++++++++++++ Models/Items/Item.cs | 14 ++++++++++++ Models/Items/Liquor.cs | 17 ++++++++++++++ Models/Items/Wine.cs | 14 ++++++++++++ Models/Order/CustomerPurchaseOrder.cs | 6 ----- Models/Order/Order.cs | 7 ------ Models/Orders/CustomerPurchaseOrder.cs | 16 +++++++++++++ Models/{Order => Orders}/InboundOrder.cs | 4 +++- Models/Orders/Order.cs | 10 +++++++++ Models/{Supplier => Suppliers}/Supplier.cs | 4 +++- 19 files changed, 145 insertions(+), 48 deletions(-) delete mode 100644 Models/Item/Chocolate.cs delete mode 100644 Models/Item/Item.cs delete mode 100644 Models/Item/Liquor.cs delete mode 100644 Models/Item/Wine.cs create mode 100644 Models/Items/DefaultItem.cs create mode 100644 Models/Items/Item.cs create mode 100644 Models/Items/Liquor.cs create mode 100644 Models/Items/Wine.cs delete mode 100644 Models/Order/CustomerPurchaseOrder.cs delete mode 100644 Models/Order/Order.cs create mode 100644 Models/Orders/CustomerPurchaseOrder.cs rename Models/{Order => Orders}/InboundOrder.cs (66%) create mode 100644 Models/Orders/Order.cs rename Models/{Supplier => Suppliers}/Supplier.cs (63%) diff --git a/Models/Authentication/Admin.cs b/Models/Authentication/Admin.cs index 2730d65..d5970cd 100644 --- a/Models/Authentication/Admin.cs +++ b/Models/Authentication/Admin.cs @@ -1,3 +1,5 @@ +using API.Models.Items; + namespace API.Models.Authentication; public class Admin : Employee @@ -28,4 +30,13 @@ public string GetClassName() return "Admin"; } + public void EditUser(User user, string firstName, string lastName, int phone, string email, string password, int? phoneNumber) + { + user.ChangeUser(firstName, lastName, phone, email, password, phoneNumber); + } + + public Item CreateItem() + { + + } } diff --git a/Models/Authentication/Customer.cs b/Models/Authentication/Customer.cs index 8c87526..448d506 100644 --- a/Models/Authentication/Customer.cs +++ b/Models/Authentication/Customer.cs @@ -1,7 +1,14 @@ +using API.Models.Items; +using API.Models.Orders; + namespace API.Models.Authentication; public class Customer : User { + public int? PhoneNumber { get; protected set; } = null; + + public List CustomerPurchaseOrders { get; protected set; } + public Customer(string firstName, string lastName, int phone, string email, string password) { FirstName = firstName; @@ -27,4 +34,23 @@ public string GetClassName() { return "Customer"; } + + override + public void ChangeUser(string firstName, string lastName, int phone, string email, string password, int? phoneNumber) + { + FirstName = firstName; + LastName = lastName; + Phone = phone; + Email = email; + Password = password; + Token = ""; + PhoneNumber = phoneNumber; + } + + public CustomerPurchaseOrder PlaceCustomerPurchaseOrder(Item[] items, string comment) + { + var customerPurchaseOrder = new CustomerPurchaseOrder(this, items, comment); + this.CustomerPurchaseOrders.Add(customerPurchaseOrder); + return customerPurchaseOrder; + } } diff --git a/Models/Authentication/Employee.cs b/Models/Authentication/Employee.cs index 27bf433..924d707 100644 --- a/Models/Authentication/Employee.cs +++ b/Models/Authentication/Employee.cs @@ -29,4 +29,15 @@ public string GetClassName() { return "Employee"; } + + override + public void ChangeUser(string firstName, string lastName, int phone, string email, string password, int? phoneNumber) + { + FirstName = firstName; + LastName = lastName; + Phone = phone; + Email = email; + Password = password; + Token = ""; + } } diff --git a/Models/Authentication/User.cs b/Models/Authentication/User.cs index 37dcefe..72ba373 100644 --- a/Models/Authentication/User.cs +++ b/Models/Authentication/User.cs @@ -49,5 +49,7 @@ public AuthPas GetTokenAuthPas() return new AuthPas(Token, TokenExpiration); } + public abstract void ChangeUser(string firstName, string lastName, int phone, string email, string password, int? phoneNumber); + public abstract string GetClassName(); } diff --git a/Models/DBContext.cs b/Models/DBContext.cs index 5f00439..631c2d7 100644 --- a/Models/DBContext.cs +++ b/Models/DBContext.cs @@ -1,4 +1,7 @@ using API.Models.Authentication; +using API.Models.Items; +using API.Models.Orders; +using API.Models.Suppliers; using Microsoft.EntityFrameworkCore; namespace API.Models; @@ -14,7 +17,7 @@ public DBContext(DbContextOptions options) : base(options) public DbSet Customers { get; set; } public DbSet Users { get; set; } public DbSet Liquors { get; set; } - public DbSet Chocolates { get; set; } + public DbSet Chocolates { get; set; } public DbSet Items { get; set; } public DbSet Wines { get; set; } public DbSet Suppliers { get; set; } diff --git a/Models/Item/Chocolate.cs b/Models/Item/Chocolate.cs deleted file mode 100644 index 3da64a1..0000000 --- a/Models/Item/Chocolate.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace API.Models; - -public class Chocolate: Item -{ - -} diff --git a/Models/Item/Item.cs b/Models/Item/Item.cs deleted file mode 100644 index 6768e25..0000000 --- a/Models/Item/Item.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace API.Models; - -public abstract class Item -{ - public int Id { get; set; } - public string Name { get; set; } - public string BarCode { get; set; } - public int Quantity { get; set; } - public float Price { get; set; } - public DateTime ExpirationDate { get; set; } - -} - - diff --git a/Models/Item/Liquor.cs b/Models/Item/Liquor.cs deleted file mode 100644 index ef3d181..0000000 --- a/Models/Item/Liquor.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace API.Models; - -public class Liquor: Item -{ - public string? LiquorType { get; set; } -} diff --git a/Models/Item/Wine.cs b/Models/Item/Wine.cs deleted file mode 100644 index eb48e0a..0000000 --- a/Models/Item/Wine.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace API.Models; - -public class Wine : Item -{ - -} diff --git a/Models/Items/DefaultItem.cs b/Models/Items/DefaultItem.cs new file mode 100644 index 0000000..ed59f61 --- /dev/null +++ b/Models/Items/DefaultItem.cs @@ -0,0 +1,14 @@ +namespace API.Models.Items; + +public class DefaultItem: Item +{ + public DefaultItem(string name, string ean, int quantity, float price, string imageUrl, DateTime expirationDate) + { + this.Name = name; + this.Ean = ean; + this.Quantity = quantity; + this.Price = price; + this.ImageUrl = imageUrl; + this.ExpirationDate = expirationDate; + } +} diff --git a/Models/Items/Item.cs b/Models/Items/Item.cs new file mode 100644 index 0000000..8a408e3 --- /dev/null +++ b/Models/Items/Item.cs @@ -0,0 +1,14 @@ +namespace API.Models.Items; + +public abstract class Item +{ + public int Id { get; protected set; } + public string Name { get; protected set; } + public string Ean { get; protected set; } + public int Quantity { get; protected set; } + public float Price { get; protected set; } + public string ImageUrl { get; protected set; } + public DateTime ExpirationDate { get; protected set; } +} + + diff --git a/Models/Items/Liquor.cs b/Models/Items/Liquor.cs new file mode 100644 index 0000000..1655db2 --- /dev/null +++ b/Models/Items/Liquor.cs @@ -0,0 +1,17 @@ +namespace API.Models.Items; + +public class Liquor: Item +{ + public string? LiquorType { get; set; } + + public Liquor(string name, string ean, int quantity, float price, string imageUrl, DateTime expirationDate, string liquorType) + { + this.Name = name; + this.Ean = ean; + this.Quantity = quantity; + this.Price = price; + this.ImageUrl = imageUrl; + this.ExpirationDate = expirationDate; + this.LiquorType = liquorType; + } +} diff --git a/Models/Items/Wine.cs b/Models/Items/Wine.cs new file mode 100644 index 0000000..4fa2b0c --- /dev/null +++ b/Models/Items/Wine.cs @@ -0,0 +1,14 @@ +namespace API.Models.Items; + +public class Wine : Item +{ + public Wine(string name, string ean, int quantity, float price, string imageUrl, DateTime expirationDate, string liquorType) + { + this.Name = name; + this.Ean = ean; + this.Quantity = quantity; + this.Price = price; + this.ImageUrl = imageUrl; + this.ExpirationDate = expirationDate; + } +} diff --git a/Models/Order/CustomerPurchaseOrder.cs b/Models/Order/CustomerPurchaseOrder.cs deleted file mode 100644 index b76b7dd..0000000 --- a/Models/Order/CustomerPurchaseOrder.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace API.Models; - -public class CustomerPurchaseOrder: Order -{ - -} diff --git a/Models/Order/Order.cs b/Models/Order/Order.cs deleted file mode 100644 index 0fdc464..0000000 --- a/Models/Order/Order.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace API.Models; - -public class Order -{ - public int Id { get; set; } - public Item[]? Items { get; set; } -} diff --git a/Models/Orders/CustomerPurchaseOrder.cs b/Models/Orders/CustomerPurchaseOrder.cs new file mode 100644 index 0000000..4b2b442 --- /dev/null +++ b/Models/Orders/CustomerPurchaseOrder.cs @@ -0,0 +1,16 @@ +using API.Models.Authentication; +using API.Models.Items; + +namespace API.Models.Orders; + +public class CustomerPurchaseOrder: Order +{ + public Customer Customer { get; set; } + + public CustomerPurchaseOrder(Customer customer, Item[] items, string? comment) + { + this.Customer = customer; + this.Items = items; + this.Comment = comment; + } +} diff --git a/Models/Order/InboundOrder.cs b/Models/Orders/InboundOrder.cs similarity index 66% rename from Models/Order/InboundOrder.cs rename to Models/Orders/InboundOrder.cs index a02e097..a1bbdec 100644 --- a/Models/Order/InboundOrder.cs +++ b/Models/Orders/InboundOrder.cs @@ -1,4 +1,6 @@ -namespace API.Models; +using API.Models.Suppliers; + +namespace API.Models.Orders; public class InboundOrder: Order { diff --git a/Models/Orders/Order.cs b/Models/Orders/Order.cs new file mode 100644 index 0000000..f4fc224 --- /dev/null +++ b/Models/Orders/Order.cs @@ -0,0 +1,10 @@ +using API.Models.Items; + +namespace API.Models.Orders; + +public class Order +{ + public int Id { get; protected set; } + public Item[]? Items { get; protected set; } + public string? Comment { get; protected set; } +} diff --git a/Models/Supplier/Supplier.cs b/Models/Suppliers/Supplier.cs similarity index 63% rename from Models/Supplier/Supplier.cs rename to Models/Suppliers/Supplier.cs index f8a1efe..46feb19 100644 --- a/Models/Supplier/Supplier.cs +++ b/Models/Suppliers/Supplier.cs @@ -1,4 +1,6 @@ -namespace API.Models; +using API.Models.Items; + +namespace API.Models.Suppliers; public class Supplier { From 5736dff0ae57b9c2eb51b852b4d7562fd57080fe Mon Sep 17 00:00:00 2001 From: ThomasSteenAndersen Date: Fri, 17 Nov 2023 11:45:20 +0100 Subject: [PATCH 013/112] SW-103 * CreateItem start --- Models/Authentication/Admin.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Models/Authentication/Admin.cs b/Models/Authentication/Admin.cs index d5970cd..8b8244f 100644 --- a/Models/Authentication/Admin.cs +++ b/Models/Authentication/Admin.cs @@ -35,8 +35,8 @@ public void EditUser(User user, string firstName, string lastName, int phone, st user.ChangeUser(firstName, lastName, phone, email, password, phoneNumber); } - public Item CreateItem() + public Item CreateItem(string name, string ean, int quantity, float price, string imageUrl, DateTime expirationDate) { - + return new DefaultItem(name, ean, quantity, price, imageUrl, expirationDate); } } From 1c784e88794d3aa5774376641ec1dfb15e03fb24 Mon Sep 17 00:00:00 2001 From: Thomas Andersen Date: Fri, 17 Nov 2023 14:06:15 +0100 Subject: [PATCH 014/112] SW-103 * Fixed alot of services --- API.csproj | 3 - Controllers/OrderController.cs | 7 - Controllers/Shared/UserController.cs | 20 ++- DataTransferObjects/ItemDto.cs | 2 +- DataTransferObjects/SignupDto.cs | 4 +- DataTransferObjects/UserStandardDto.cs | 10 ++ Enums/Roles.cs | 8 ++ Models/Authentication/Admin.cs | 12 +- Models/Authentication/Customer.cs | 14 +- Models/Authentication/Employee.cs | 3 +- Models/Authentication/User.cs | 43 +++--- Program.cs | 3 + Services/Shared/AuthService.cs | 37 ++++++ Services/Shared/AuthenticationService.cs | 153 +++++++++++----------- Services/Shared/IAuthService.cs | 8 ++ Services/Shared/IAuthenticationService.cs | 3 +- Services/Shared/IUserService.cs | 4 + Services/Shared/UserService.cs | 19 ++- 18 files changed, 231 insertions(+), 122 deletions(-) create mode 100644 DataTransferObjects/UserStandardDto.cs create mode 100644 Enums/Roles.cs create mode 100644 Services/Shared/AuthService.cs create mode 100644 Services/Shared/IAuthService.cs diff --git a/API.csproj b/API.csproj index 93a1edb..03ee93d 100644 --- a/API.csproj +++ b/API.csproj @@ -24,8 +24,5 @@ - - - diff --git a/Controllers/OrderController.cs b/Controllers/OrderController.cs index 8e6bb61..7adc9b5 100644 --- a/Controllers/OrderController.cs +++ b/Controllers/OrderController.cs @@ -1,6 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using API.Models; using Microsoft.AspNetCore.Authorization; namespace API.Controllers @@ -10,11 +8,6 @@ namespace API.Controllers [Authorize] public class OrderController : ControllerBase { - private readonly DBContext _context; - public OrderController(DBContext context) - { - _context = context; - } } } diff --git a/Controllers/Shared/UserController.cs b/Controllers/Shared/UserController.cs index 310771a..c4d78bf 100644 --- a/Controllers/Shared/UserController.cs +++ b/Controllers/Shared/UserController.cs @@ -1,3 +1,4 @@ +using API.DataTransferObjects; using API.Models.Authentication; using API.Services.Shared; using Microsoft.AspNetCore.Authorization; @@ -7,7 +8,7 @@ namespace API.Controllers.Shared { [Route("api/[controller]")] [ApiController] - public class UserController + public class UserController : ControllerBase { private readonly IUserService _userService; @@ -22,5 +23,22 @@ public async Task>> GetUsers() { return await _userService.GetAllUsers(); } + + [HttpPost("/edit")] + [Authorize] + public async Task EditSelf(UserStandardDto user) + { + try + { + await _userService.EditUser(user); + } + catch (Exception e) + { + Console.WriteLine(e.Message); + BadRequest(e.Message); + throw; + } + return Ok(); + } } } diff --git a/DataTransferObjects/ItemDto.cs b/DataTransferObjects/ItemDto.cs index 11d5f1a..da2a94f 100644 --- a/DataTransferObjects/ItemDto.cs +++ b/DataTransferObjects/ItemDto.cs @@ -3,5 +3,5 @@ namespace API.DataTransferObjects; public class ItemDto { public Int32 Id { get; set; } - public string Name { get; set; } + public string? Name { get; set; } } diff --git a/DataTransferObjects/SignupDto.cs b/DataTransferObjects/SignupDto.cs index 66a5752..8fb44d1 100644 --- a/DataTransferObjects/SignupDto.cs +++ b/DataTransferObjects/SignupDto.cs @@ -1,3 +1,5 @@ +using API.Enums; + namespace API.DataTransferObjects; public class SignupDto @@ -7,5 +9,5 @@ public class SignupDto public int Phone { get; set; } public string Email { get; set; } public string Password { get; set; } - public int DesiredRole { get; set; } + public Roles DesiredRole { get; set; } } diff --git a/DataTransferObjects/UserStandardDto.cs b/DataTransferObjects/UserStandardDto.cs new file mode 100644 index 0000000..ef6a99d --- /dev/null +++ b/DataTransferObjects/UserStandardDto.cs @@ -0,0 +1,10 @@ +namespace API.DataTransferObjects; + +public class UserStandardDto +{ + public string FirstName { get; set; } + public string LastName { get; set; } + public int Phone { get; set; } + public string Email { get; set; } + public string Password { get; set; } +} diff --git a/Enums/Roles.cs b/Enums/Roles.cs new file mode 100644 index 0000000..ee336cd --- /dev/null +++ b/Enums/Roles.cs @@ -0,0 +1,8 @@ +namespace API.Enums; + +public enum Roles +{ + Customer, + Employee, + Admin +} diff --git a/Models/Authentication/Admin.cs b/Models/Authentication/Admin.cs index 8b8244f..b7d2a15 100644 --- a/Models/Authentication/Admin.cs +++ b/Models/Authentication/Admin.cs @@ -30,9 +30,19 @@ public string GetClassName() return "Admin"; } + protected void ChangeAdmin(string firstName, string lastName, int phone, string email, string password, int? phoneNumber) + { + FirstName = firstName; + LastName = lastName; + Phone = phone; + Email = email; + Password = password; + Token = ""; + } + public void EditUser(User user, string firstName, string lastName, int phone, string email, string password, int? phoneNumber) { - user.ChangeUser(firstName, lastName, phone, email, password, phoneNumber); + Console.WriteLine(user.GetType()); } public Item CreateItem(string name, string ean, int quantity, float price, string imageUrl, DateTime expirationDate) diff --git a/Models/Authentication/Customer.cs b/Models/Authentication/Customer.cs index 448d506..9a5d61c 100644 --- a/Models/Authentication/Customer.cs +++ b/Models/Authentication/Customer.cs @@ -17,16 +17,7 @@ public Customer(string firstName, string lastName, int phone, string email, stri Email = email; Password = password; Token = ""; - } - - public Customer(User user) - { - FirstName = user.FirstName; - LastName = user.LastName; - Phone = user.Phone; - Email = user.Email; - Password = user.Password; - Token = ""; + CustomerPurchaseOrders = new List(); } override @@ -35,8 +26,7 @@ public string GetClassName() return "Customer"; } - override - public void ChangeUser(string firstName, string lastName, int phone, string email, string password, int? phoneNumber) + protected void ChangeCustomer(string firstName, string lastName, int phone, string email, string password, int? phoneNumber) { FirstName = firstName; LastName = lastName; diff --git a/Models/Authentication/Employee.cs b/Models/Authentication/Employee.cs index 924d707..fddbaff 100644 --- a/Models/Authentication/Employee.cs +++ b/Models/Authentication/Employee.cs @@ -30,8 +30,7 @@ public string GetClassName() return "Employee"; } - override - public void ChangeUser(string firstName, string lastName, int phone, string email, string password, int? phoneNumber) + protected void ChangeEmployee(string firstName, string lastName, int phone, string email, string password, int? phoneNumber) { FirstName = firstName; LastName = lastName; diff --git a/Models/Authentication/User.cs b/Models/Authentication/User.cs index 72ba373..63ed3dd 100644 --- a/Models/Authentication/User.cs +++ b/Models/Authentication/User.cs @@ -1,4 +1,5 @@ using API.DataTransferObjects; +using API.Enums; namespace API.Models.Authentication; @@ -15,27 +16,26 @@ public abstract class User public static User CreateNewUser(DBContext context,SignupDto signupDto) { - User newUser; - switch (signupDto.DesiredRole) { - case 1: - newUser = new Employee(signupDto.FirstName, signupDto.LastName, signupDto.Phone, signupDto.Email, - signupDto.Password); - context.Employees.Add(newUser as Employee); - break; - case 2: - newUser = new Admin(signupDto.FirstName, signupDto.LastName, signupDto.Phone, signupDto.Email, + case Roles.Customer: + Customer newCustomer = new Customer(signupDto.FirstName, signupDto.LastName, signupDto.Phone, + signupDto.Email, signupDto.Password); + context.Customers.Add(newCustomer); + return newCustomer; + case Roles.Employee: + Employee newEmployee = new Employee(signupDto.FirstName, signupDto.LastName, signupDto.Phone, + signupDto.Email, signupDto.Password); + context.Employees.Add(newEmployee); + return newEmployee; + case Roles.Admin: + Admin newAdmin = new Admin(signupDto.FirstName, signupDto.LastName, signupDto.Phone, signupDto.Email, signupDto.Password); - context.Admins.Add(newUser as Admin); - break; + context.Admins.Add(newAdmin); + return newAdmin; default: - newUser = new Customer(signupDto.FirstName, signupDto.LastName, signupDto.Phone, signupDto.Email, - signupDto.Password); - context.Customers.Add(newUser as Customer); - break; + throw new Exception("Invalid role"); } - return newUser; } public void SetToken(string token, DateTime? tokenExpiration) @@ -49,7 +49,16 @@ public AuthPas GetTokenAuthPas() return new AuthPas(Token, TokenExpiration); } - public abstract void ChangeUser(string firstName, string lastName, int phone, string email, string password, int? phoneNumber); + public void ChangeUserStandardProperties(string firstName, string lastName, int phone, string email, string password) + { + FirstName = firstName; + LastName = lastName; + Phone = phone; + Email = email; + Password = password; + Token = ""; + TokenExpiration = null; + } public abstract string GetClassName(); } diff --git a/Program.cs b/Program.cs index a367f52..9964ffb 100644 --- a/Program.cs +++ b/Program.cs @@ -42,6 +42,8 @@ policy.RequireRole("Admin")); }); +builder.Services.AddHttpContextAccessor(); + builder.Services.AddDbContext(opt => { opt.UseMySql("server=localhost;port=3306;database=EVENTILOPE;user=THOMAS;password=password;", @@ -50,6 +52,7 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); diff --git a/Services/Shared/AuthService.cs b/Services/Shared/AuthService.cs new file mode 100644 index 0000000..440c8b5 --- /dev/null +++ b/Services/Shared/AuthService.cs @@ -0,0 +1,37 @@ +using System.Security.Claims; +using API.Models; +using API.Models.Authentication; +using Microsoft.EntityFrameworkCore; + +namespace API.Services.Shared +{ + public class AuthService : IAuthService + { + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly DBContext _context; + + + public AuthService(IHttpContextAccessor httpContextAccessor, DBContext context) + { + _httpContextAccessor = httpContextAccessor; + _context = context; + } + + public async Task GetActiveUser() + { + var userEmail = _httpContextAccessor.HttpContext?.User.FindFirst(ClaimTypes.Email)?.Value; + if (userEmail == null) + { + throw new Exception("User not found"); + } + + var user = await _context.Users.FirstOrDefaultAsync(user => user.Email == userEmail); + if (user == null) + { + throw new Exception("User not found"); + } + + return user; + } + } +} diff --git a/Services/Shared/AuthenticationService.cs b/Services/Shared/AuthenticationService.cs index 7b1c58e..b486f19 100644 --- a/Services/Shared/AuthenticationService.cs +++ b/Services/Shared/AuthenticationService.cs @@ -3,104 +3,107 @@ using API.Models.Authentication; using Microsoft.EntityFrameworkCore; -namespace API.Services.Shared; - -public class AuthenticationService : IAuthenticationService +namespace API.Services.Shared { - private readonly DBContext _context; - private readonly IConfiguration _configuration; - private readonly ITokenService _tokenService; - - public AuthenticationService(DBContext dbContext, IConfiguration configuration, ITokenService tokenService) - { - _context = dbContext; - _configuration = configuration; - _tokenService = tokenService; - } - - /// - /// Finds a user in the database and generates a token for them - /// - /// The login data - /// An authentication pas for the users - /// - public async Task Login(LoginDto loginDto) + public class AuthenticationService : IAuthenticationService { - var dbUser = await _context.Users.FirstOrDefaultAsync(u => u.Email == loginDto.Email && u.Password == loginDto.Password); + private readonly DBContext _context; + private readonly IConfiguration _configuration; + private readonly ITokenService _tokenService; - if (dbUser == null) + public AuthenticationService(DBContext dbContext, IConfiguration configuration, ITokenService tokenService) { - throw new Exception("Incorrect email or password"); + _context = dbContext; + _configuration = configuration; + _tokenService = tokenService; } - var token = _tokenService.GenerateToken(dbUser); + /// + /// Finds a user in the database and generates a token for them + /// + /// The login data + /// An authentication pas for the users + /// + public async Task Login(LoginDto loginDto) + { + var dbUser = + await _context.Users.FirstOrDefaultAsync(u => + u.Email == loginDto.Email && u.Password == loginDto.Password); - dbUser.SetToken(token, DateTime.Now.AddHours(24)); + if (dbUser == null) + { + throw new Exception("Incorrect email or password"); + } - await _context.SaveChangesAsync(); + var token = _tokenService.GenerateToken(dbUser); - return dbUser.GetTokenAuthPas(); - } + dbUser.SetToken(token, DateTime.Now.AddHours(24)); - /// - /// Logs out a user given their token - /// - /// The pas with the token to logout - /// - /// - public async Task LogOut(AuthPas authPas) - { - var dbUser = await _context.Users.FirstOrDefaultAsync(u => u.Token == authPas.Token); + await _context.SaveChangesAsync(); - if (dbUser == null) - { - throw new Exception("Failed trying to logout incorrect token"); + return dbUser.GetTokenAuthPas(); } - dbUser.SetToken("", null); + /// + /// Logs out a user given their token + /// + /// The pas with the token to logout + /// + /// + public async Task LogOut(AuthPas authPas) + { + var dbUser = await _context.Users.FirstOrDefaultAsync(u => u.Token == authPas.Token); - await _context.SaveChangesAsync(); - - return true; - } + if (dbUser == null) + { + throw new Exception("Failed trying to logout incorrect token"); + } + dbUser.SetToken("", null); - /// - /// Verifies a user by a given token - /// - /// The pas with token to verify - /// Success boolean - /// - public async Task VerifyToken(AuthPas authPas) - { - var dbUser = await _context.Users.FirstOrDefaultAsync(u => u.Token == authPas.Token); + await _context.SaveChangesAsync(); - if (dbUser == null) - { - throw new Exception("Failed trying to verify incorrect token"); + return true; } - if (dbUser.TokenExpiration == null || dbUser.TokenExpiration <= DateTime.Now) + + /// + /// Verifies a user by a given token + /// + /// The pas with token to verify + /// Success boolean + /// + public async Task VerifyToken(AuthPas authPas) { - dbUser.SetToken("", null); - await _context.SaveChangesAsync(); - return false; - } + var dbUser = await _context.Users.FirstOrDefaultAsync(u => u.Token == authPas.Token); - return true; - } + if (dbUser == null) + { + throw new Exception("Failed trying to verify incorrect token"); + } - /// - /// Creates a new user with a signupDto - /// - /// The sign up data - /// A success boolean - public async Task CreateNewUser(SignupDto signupDto) - { - User.CreateNewUser(_context, signupDto); + if (dbUser.TokenExpiration == null || dbUser.TokenExpiration <= DateTime.Now) + { + dbUser.SetToken("", null); + await _context.SaveChangesAsync(); + return false; + } + + return true; + } - await _context.SaveChangesAsync(); + /// + /// Creates a new user with a signupDto + /// + /// The sign up data + /// A success boolean + public async Task CreateNewUser(SignupDto signupDto) + { + User.CreateNewUser(_context, signupDto); - return true; + await _context.SaveChangesAsync(); + + return true; + } } } diff --git a/Services/Shared/IAuthService.cs b/Services/Shared/IAuthService.cs new file mode 100644 index 0000000..0ca1b40 --- /dev/null +++ b/Services/Shared/IAuthService.cs @@ -0,0 +1,8 @@ +using API.Models.Authentication; + +namespace API.Services.Shared; + +public interface IAuthService +{ + Task GetActiveUser(); +} diff --git a/Services/Shared/IAuthenticationService.cs b/Services/Shared/IAuthenticationService.cs index 0c9a618..80e8eb1 100644 --- a/Services/Shared/IAuthenticationService.cs +++ b/Services/Shared/IAuthenticationService.cs @@ -10,4 +10,5 @@ public interface IAuthenticationService Task LogOut(AuthPas authPas); - Task VerifyToken(AuthPas authPas); } + Task VerifyToken(AuthPas authPas); +} diff --git a/Services/Shared/IUserService.cs b/Services/Shared/IUserService.cs index 66bfc7c..818c4e0 100644 --- a/Services/Shared/IUserService.cs +++ b/Services/Shared/IUserService.cs @@ -1,8 +1,12 @@ +using API.DataTransferObjects; using API.Models.Authentication; +using Microsoft.AspNetCore.Mvc; namespace API.Services.Shared; public interface IUserService { public Task> GetAllUsers(); + + public Task EditUser(UserStandardDto user); } diff --git a/Services/Shared/UserService.cs b/Services/Shared/UserService.cs index 838675b..87d5e1c 100644 --- a/Services/Shared/UserService.cs +++ b/Services/Shared/UserService.cs @@ -1,5 +1,7 @@ +using API.DataTransferObjects; using API.Models; using API.Models.Authentication; +using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace API.Services.Shared; @@ -7,14 +9,29 @@ namespace API.Services.Shared; public class UserService : IUserService { private readonly DBContext _context; + private readonly IAuthService _authService; - public UserService(DBContext dbContext) + public UserService(DBContext dbContext, IAuthService authService) { _context = dbContext; + _authService = authService; } public async Task> GetAllUsers() { return await _context.Users.ToListAsync(); } + + public async Task EditUser(UserStandardDto user) + { + var requestingUser = await _authService.GetActiveUser(); + if (requestingUser.Email != user.Email) + { + throw new Exception("You can only edit your own user"); + } + + Console.WriteLine(user.GetType()); + Console.WriteLine(requestingUser.GetType()); + throw new NotImplementedException(); + } } From d382bc73ce7dabd0b4929060893a6a24c9450c8b Mon Sep 17 00:00:00 2001 From: Thomas Andersen Date: Sun, 19 Nov 2023 19:56:22 +0100 Subject: [PATCH 015/112] SW-108 * Fixed issues with the itemtypes --- .../.idea/efCoreCommonOptions.xml | 11 + .../.idea/efCoreDialogsState.xml | 14 + ...tedUsersAndItemsAndAddedOrders.Designer.cs | 252 ++++++++++++++++++ ...1528_UpdatedUsersAndItemsAndAddedOrders.cs | 102 +++++++ Migrations/DBContextModelSnapshot.cs | 174 +++++++----- Models/Authentication/Customer.cs | 2 +- Models/Items/Wine.cs | 2 +- Models/Orders/CustomerPurchaseOrder.cs | 12 +- Models/Orders/Order.cs | 4 +- Services/Shared/AuthenticationService.cs | 4 +- 10 files changed, 503 insertions(+), 74 deletions(-) create mode 100644 .idea/.idea.API.dir/.idea/efCoreCommonOptions.xml create mode 100644 .idea/.idea.API.dir/.idea/efCoreDialogsState.xml create mode 100644 Migrations/20231117191528_UpdatedUsersAndItemsAndAddedOrders.Designer.cs create mode 100644 Migrations/20231117191528_UpdatedUsersAndItemsAndAddedOrders.cs diff --git a/.idea/.idea.API.dir/.idea/efCoreCommonOptions.xml b/.idea/.idea.API.dir/.idea/efCoreCommonOptions.xml new file mode 100644 index 0000000..944e0ce --- /dev/null +++ b/.idea/.idea.API.dir/.idea/efCoreCommonOptions.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/.idea/.idea.API.dir/.idea/efCoreDialogsState.xml b/.idea/.idea.API.dir/.idea/efCoreDialogsState.xml new file mode 100644 index 0000000..f5622c3 --- /dev/null +++ b/.idea/.idea.API.dir/.idea/efCoreDialogsState.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/Migrations/20231117191528_UpdatedUsersAndItemsAndAddedOrders.Designer.cs b/Migrations/20231117191528_UpdatedUsersAndItemsAndAddedOrders.Designer.cs new file mode 100644 index 0000000..327c62e --- /dev/null +++ b/Migrations/20231117191528_UpdatedUsersAndItemsAndAddedOrders.Designer.cs @@ -0,0 +1,252 @@ +// +using System; +using API.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace API.Migrations +{ + [DbContext(typeof(DBContext))] + [Migration("20231117191528_UpdatedUsersAndItemsAndAddedOrders")] + partial class UpdatedUsersAndItemsAndAddedOrders + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("API.Models.Authentication.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Email") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Phone") + .HasColumnType("int"); + + b.Property("Token") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TokenExpiration") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.ToTable("Users"); + + b.HasDiscriminator("Discriminator").HasValue("User"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("API.Models.Items.Item", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Ean") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("ImageUrl") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrderId") + .HasColumnType("int"); + + b.Property("Price") + .HasColumnType("float"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.Property("SupplierId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("OrderId"); + + b.HasIndex("SupplierId"); + + b.ToTable("Items"); + + b.HasDiscriminator("Discriminator").HasValue("Item"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("API.Models.Orders.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Comment") + .HasColumnType("longtext"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Order"); + + b.HasDiscriminator("Discriminator").HasValue("Order"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("API.Models.Suppliers.Supplier", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Suppliers"); + }); + + modelBuilder.Entity("API.Models.Authentication.Customer", b => + { + b.HasBaseType("API.Models.Authentication.User"); + + b.Property("PhoneNumber") + .HasColumnType("int"); + + b.HasDiscriminator().HasValue("Customer"); + }); + + modelBuilder.Entity("API.Models.Authentication.Employee", b => + { + b.HasBaseType("API.Models.Authentication.User"); + + b.HasDiscriminator().HasValue("Employee"); + }); + + modelBuilder.Entity("API.Models.Items.DefaultItem", b => + { + b.HasBaseType("API.Models.Items.Item"); + + b.HasDiscriminator().HasValue("DefaultItem"); + }); + + modelBuilder.Entity("API.Models.Items.Liquor", b => + { + b.HasBaseType("API.Models.Items.Item"); + + b.Property("LiquorType") + .HasColumnType("longtext"); + + b.HasDiscriminator().HasValue("Liquor"); + }); + + modelBuilder.Entity("API.Models.Items.Wine", b => + { + b.HasBaseType("API.Models.Items.Item"); + + b.HasDiscriminator().HasValue("Wine"); + }); + + modelBuilder.Entity("API.Models.Orders.CustomerPurchaseOrder", b => + { + b.HasBaseType("API.Models.Orders.Order"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.HasIndex("CustomerId"); + + b.HasDiscriminator().HasValue("CustomerPurchaseOrder"); + }); + + modelBuilder.Entity("API.Models.Authentication.Admin", b => + { + b.HasBaseType("API.Models.Authentication.Employee"); + + b.HasDiscriminator().HasValue("Admin"); + }); + + modelBuilder.Entity("API.Models.Items.Item", b => + { + b.HasOne("API.Models.Orders.Order", null) + .WithMany("Items") + .HasForeignKey("OrderId"); + + b.HasOne("API.Models.Suppliers.Supplier", null) + .WithMany("Items") + .HasForeignKey("SupplierId"); + }); + + modelBuilder.Entity("API.Models.Orders.CustomerPurchaseOrder", b => + { + b.HasOne("API.Models.Authentication.Customer", "Customer") + .WithMany("CustomerPurchaseOrders") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Customer"); + }); + + modelBuilder.Entity("API.Models.Orders.Order", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("API.Models.Suppliers.Supplier", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("API.Models.Authentication.Customer", b => + { + b.Navigation("CustomerPurchaseOrders"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Migrations/20231117191528_UpdatedUsersAndItemsAndAddedOrders.cs b/Migrations/20231117191528_UpdatedUsersAndItemsAndAddedOrders.cs new file mode 100644 index 0000000..d0e5941 --- /dev/null +++ b/Migrations/20231117191528_UpdatedUsersAndItemsAndAddedOrders.cs @@ -0,0 +1,102 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace API.Migrations +{ + /// + public partial class UpdatedUsersAndItemsAndAddedOrders : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "BarCode", + table: "Items", + newName: "ImageUrl"); + + migrationBuilder.AddColumn( + name: "PhoneNumber", + table: "Users", + type: "int", + nullable: true); + + migrationBuilder.AddColumn( + name: "Comment", + table: "Order", + type: "longtext", + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "CustomerId", + table: "Order", + type: "int", + nullable: true); + + migrationBuilder.AddColumn( + name: "Discriminator", + table: "Order", + type: "longtext", + nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "Ean", + table: "Items", + type: "longtext", + nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_Order_CustomerId", + table: "Order", + column: "CustomerId"); + + migrationBuilder.AddForeignKey( + name: "FK_Order_Users_CustomerId", + table: "Order", + column: "CustomerId", + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Order_Users_CustomerId", + table: "Order"); + + migrationBuilder.DropIndex( + name: "IX_Order_CustomerId", + table: "Order"); + + migrationBuilder.DropColumn( + name: "PhoneNumber", + table: "Users"); + + migrationBuilder.DropColumn( + name: "Comment", + table: "Order"); + + migrationBuilder.DropColumn( + name: "CustomerId", + table: "Order"); + + migrationBuilder.DropColumn( + name: "Discriminator", + table: "Order"); + + migrationBuilder.DropColumn( + name: "Ean", + table: "Items"); + + migrationBuilder.RenameColumn( + name: "ImageUrl", + table: "Items", + newName: "BarCode"); + } + } +} diff --git a/Migrations/DBContextModelSnapshot.cs b/Migrations/DBContextModelSnapshot.cs index 57ab2da..aa6aa0b 100644 --- a/Migrations/DBContextModelSnapshot.cs +++ b/Migrations/DBContextModelSnapshot.cs @@ -19,23 +19,72 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasAnnotation("ProductVersion", "7.0.11") .HasAnnotation("Relational:MaxIdentifierLength", 64); - modelBuilder.Entity("API.Models.Item", b => + modelBuilder.Entity("API.Models.Authentication.User", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int"); - b.Property("BarCode") + b.Property("Discriminator") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Email") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Password") .IsRequired() .HasColumnType("longtext"); + b.Property("Phone") + .HasColumnType("int"); + + b.Property("Token") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TokenExpiration") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.ToTable("Users"); + + b.HasDiscriminator("Discriminator").HasValue("User"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("API.Models.Items.Item", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + b.Property("Discriminator") .IsRequired() .HasColumnType("longtext"); + b.Property("Ean") + .IsRequired() + .HasColumnType("longtext"); + b.Property("ExpirationDate") .HasColumnType("datetime(6)"); + b.Property("ImageUrl") + .IsRequired() + .HasColumnType("longtext"); + b.Property("Name") .IsRequired() .HasColumnType("longtext"); @@ -65,18 +114,29 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.UseTphMappingStrategy(); }); - modelBuilder.Entity("API.Models.Order", b => + modelBuilder.Entity("API.Models.Orders.Order", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int"); + b.Property("Comment") + .HasColumnType("longtext"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("longtext"); + b.HasKey("Id"); b.ToTable("Order"); + + b.HasDiscriminator("Discriminator").HasValue("Order"); + + b.UseTphMappingStrategy(); }); - modelBuilder.Entity("API.Models.Supplier", b => + modelBuilder.Entity("API.Models.Suppliers.Supplier", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -87,61 +147,33 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("Suppliers"); }); - modelBuilder.Entity("API.Models.User", b => + modelBuilder.Entity("API.Models.Authentication.Customer", b => { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("Discriminator") - .IsRequired() - .HasColumnType("longtext"); + b.HasBaseType("API.Models.Authentication.User"); - b.Property("Email") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("FirstName") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("LastName") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("Password") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("Phone") + b.Property("PhoneNumber") .HasColumnType("int"); - b.Property("Token") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("TokenExpiration") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.ToTable("Users"); + b.HasDiscriminator().HasValue("Customer"); + }); - b.HasDiscriminator("Discriminator").HasValue("User"); + modelBuilder.Entity("API.Models.Authentication.Employee", b => + { + b.HasBaseType("API.Models.Authentication.User"); - b.UseTphMappingStrategy(); + b.HasDiscriminator().HasValue("Employee"); }); - modelBuilder.Entity("API.Models.Chocolate", b => + modelBuilder.Entity("API.Models.Items.DefaultItem", b => { - b.HasBaseType("API.Models.Item"); + b.HasBaseType("API.Models.Items.Item"); - b.HasDiscriminator().HasValue("Chocolate"); + b.HasDiscriminator().HasValue("DefaultItem"); }); - modelBuilder.Entity("API.Models.Liquor", b => + modelBuilder.Entity("API.Models.Items.Liquor", b => { - b.HasBaseType("API.Models.Item"); + b.HasBaseType("API.Models.Items.Item"); b.Property("LiquorType") .HasColumnType("longtext"); @@ -149,54 +181,68 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasDiscriminator().HasValue("Liquor"); }); - modelBuilder.Entity("API.Models.Wine", b => + modelBuilder.Entity("API.Models.Items.Wine", b => { - b.HasBaseType("API.Models.Item"); + b.HasBaseType("API.Models.Items.Item"); b.HasDiscriminator().HasValue("Wine"); }); - modelBuilder.Entity("API.Models.Authentication.Admin", b => + modelBuilder.Entity("API.Models.Orders.CustomerPurchaseOrder", b => { - b.HasBaseType("API.Models.User"); + b.HasBaseType("API.Models.Orders.Order"); - b.HasDiscriminator().HasValue("Admin"); - }); + b.Property("CustomerId") + .HasColumnType("int"); - modelBuilder.Entity("API.Models.Authentication.Customer", b => - { - b.HasBaseType("API.Models.User"); + b.HasIndex("CustomerId"); - b.HasDiscriminator().HasValue("Customer"); + b.HasDiscriminator().HasValue("CustomerPurchaseOrder"); }); - modelBuilder.Entity("API.Models.Authentication.Employee", b => + modelBuilder.Entity("API.Models.Authentication.Admin", b => { - b.HasBaseType("API.Models.User"); + b.HasBaseType("API.Models.Authentication.Employee"); - b.HasDiscriminator().HasValue("Employee"); + b.HasDiscriminator().HasValue("Admin"); }); - modelBuilder.Entity("API.Models.Item", b => + modelBuilder.Entity("API.Models.Items.Item", b => { - b.HasOne("API.Models.Order", null) + b.HasOne("API.Models.Orders.Order", null) .WithMany("Items") .HasForeignKey("OrderId"); - b.HasOne("API.Models.Supplier", null) + b.HasOne("API.Models.Suppliers.Supplier", null) .WithMany("Items") .HasForeignKey("SupplierId"); }); - modelBuilder.Entity("API.Models.Order", b => + modelBuilder.Entity("API.Models.Orders.CustomerPurchaseOrder", b => + { + b.HasOne("API.Models.Authentication.Customer", "Customer") + .WithMany("CustomerPurchaseOrders") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Customer"); + }); + + modelBuilder.Entity("API.Models.Orders.Order", b => { b.Navigation("Items"); }); - modelBuilder.Entity("API.Models.Supplier", b => + modelBuilder.Entity("API.Models.Suppliers.Supplier", b => { b.Navigation("Items"); }); + + modelBuilder.Entity("API.Models.Authentication.Customer", b => + { + b.Navigation("CustomerPurchaseOrders"); + }); #pragma warning restore 612, 618 } } diff --git a/Models/Authentication/Customer.cs b/Models/Authentication/Customer.cs index 9a5d61c..978e6de 100644 --- a/Models/Authentication/Customer.cs +++ b/Models/Authentication/Customer.cs @@ -37,7 +37,7 @@ protected void ChangeCustomer(string firstName, string lastName, int phone, stri PhoneNumber = phoneNumber; } - public CustomerPurchaseOrder PlaceCustomerPurchaseOrder(Item[] items, string comment) + public CustomerPurchaseOrder PlaceCustomerPurchaseOrder(List items, string comment) { var customerPurchaseOrder = new CustomerPurchaseOrder(this, items, comment); this.CustomerPurchaseOrders.Add(customerPurchaseOrder); diff --git a/Models/Items/Wine.cs b/Models/Items/Wine.cs index 4fa2b0c..b24545a 100644 --- a/Models/Items/Wine.cs +++ b/Models/Items/Wine.cs @@ -2,7 +2,7 @@ namespace API.Models.Items; public class Wine : Item { - public Wine(string name, string ean, int quantity, float price, string imageUrl, DateTime expirationDate, string liquorType) + public Wine(string name, string ean, int quantity, float price, string imageUrl, DateTime expirationDate) { this.Name = name; this.Ean = ean; diff --git a/Models/Orders/CustomerPurchaseOrder.cs b/Models/Orders/CustomerPurchaseOrder.cs index 4b2b442..9734bd7 100644 --- a/Models/Orders/CustomerPurchaseOrder.cs +++ b/Models/Orders/CustomerPurchaseOrder.cs @@ -5,12 +5,18 @@ namespace API.Models.Orders; public class CustomerPurchaseOrder: Order { - public Customer Customer { get; set; } + public Customer Customer { get; protected set; } - public CustomerPurchaseOrder(Customer customer, Item[] items, string? comment) + // Parameterless constructor for EF Core + protected CustomerPurchaseOrder() + { + } + + + public CustomerPurchaseOrder(Customer customer, List items, string? comment) { this.Customer = customer; - this.Items = items; + this.Items.AddRange(items); this.Comment = comment; } } diff --git a/Models/Orders/Order.cs b/Models/Orders/Order.cs index f4fc224..4d06192 100644 --- a/Models/Orders/Order.cs +++ b/Models/Orders/Order.cs @@ -2,9 +2,9 @@ namespace API.Models.Orders; -public class Order +public abstract class Order { public int Id { get; protected set; } - public Item[]? Items { get; protected set; } + public List Items { get; protected set; } = new List(); public string? Comment { get; protected set; } } diff --git a/Services/Shared/AuthenticationService.cs b/Services/Shared/AuthenticationService.cs index b486f19..1d3b747 100644 --- a/Services/Shared/AuthenticationService.cs +++ b/Services/Shared/AuthenticationService.cs @@ -26,9 +26,7 @@ public AuthenticationService(DBContext dbContext, IConfiguration configuration, /// public async Task Login(LoginDto loginDto) { - var dbUser = - await _context.Users.FirstOrDefaultAsync(u => - u.Email == loginDto.Email && u.Password == loginDto.Password); + var dbUser = await _context.Users.FirstOrDefaultAsync(u => u.Email == loginDto.Email && u.Password == loginDto.Password); if (dbUser == null) { From fcbf33ab9374cc34e1e7c1aab7c7cc78d5b9e3b1 Mon Sep 17 00:00:00 2001 From: ThomasSteenAndersen Date: Tue, 21 Nov 2023 11:30:44 +0100 Subject: [PATCH 016/112] SW-108 * Added wine spec --- Models/Items/Wine.cs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/Models/Items/Wine.cs b/Models/Items/Wine.cs index b24545a..e666b81 100644 --- a/Models/Items/Wine.cs +++ b/Models/Items/Wine.cs @@ -2,7 +2,18 @@ namespace API.Models.Items; public class Wine : Item { - public Wine(string name, string ean, int quantity, float price, string imageUrl, DateTime expirationDate) + public int? Year { get; protected set; } + public double? Volume { get; protected set; } + public double? AlcoholPercentage { get; protected set; } + public string? Country { get; protected set; } + public string? Region { get; protected set; } + public string? GrapeSort { get; protected set; } + public string? Winery { get; protected set; } + public List? TastingNotes { get; protected set; } + public List? SuitableFor { get; protected set; } + public List? WineType { get; protected set; } + + public Wine(string name, string ean, int quantity, float price, string imageUrl, DateTime expirationDate, int? year, double? volume, double? alcoholPercentage, string? country, string? region, string? grapeSort, string? winery, List? tastingNotes, List? suitableFor, List? wineType) { this.Name = name; this.Ean = ean; @@ -10,5 +21,15 @@ public Wine(string name, string ean, int quantity, float price, string imageUrl, this.Price = price; this.ImageUrl = imageUrl; this.ExpirationDate = expirationDate; + this.Year = year; + this.Volume = volume; + this.AlcoholPercentage = alcoholPercentage; + this.Country = country; + this.Region = region; + this.GrapeSort = grapeSort; + this.Winery = winery; + this.TastingNotes = tastingNotes; + this.SuitableFor = suitableFor; + this.WineType = wineType; } } From 5125663198d12fe646421c74128dcaf3ba522bc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoffer=20Gam=C3=A9l?= Date: Tue, 21 Nov 2023 13:51:22 +0100 Subject: [PATCH 017/112] API set up for getitems --- Controllers/Shared/ItemController.cs | 22 ++++++++++++++++++++-- Models/Items/Wine.cs | 17 +++++++++++------ Program.cs | 1 + Services/Shared/IItemService.cs | 9 +++++++++ Services/Shared/ItemService.cs | 25 +++++++++++++++++++++++++ bin/Debug/net7.0/API | Bin 148208 -> 181016 bytes obj/Debug/net7.0/apphost | Bin 148208 -> 181016 bytes 7 files changed, 66 insertions(+), 8 deletions(-) create mode 100644 Services/Shared/IItemService.cs create mode 100644 Services/Shared/ItemService.cs diff --git a/Controllers/Shared/ItemController.cs b/Controllers/Shared/ItemController.cs index 2bd8b20..2c1edeb 100644 --- a/Controllers/Shared/ItemController.cs +++ b/Controllers/Shared/ItemController.cs @@ -1,11 +1,29 @@ +using API.Models.Items; +using API.Services.Shared; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace API.Controllers.Shared { [Route("api/[controller]")] [ApiController] - public class ItemController + public class ItemController : ControllerBase { - + private readonly IItemService _itemService; + public ItemController(IItemService itemService) + { + _itemService = itemService; + } + [HttpGet] + public async Task>> GetItems() + { + return await _itemService.GetAllItems(); + } + + [HttpGet("{id}")] + public async Task> GetItemById(int id) + { + return await _itemService.GetItemById(id); + } } } diff --git a/Models/Items/Wine.cs b/Models/Items/Wine.cs index e666b81..2140afa 100644 --- a/Models/Items/Wine.cs +++ b/Models/Items/Wine.cs @@ -9,10 +9,15 @@ public class Wine : Item public string? Region { get; protected set; } public string? GrapeSort { get; protected set; } public string? Winery { get; protected set; } - public List? TastingNotes { get; protected set; } - public List? SuitableFor { get; protected set; } - public List? WineType { get; protected set; } + // public List? TastingNotes { get; protected set; } + // public List? SuitableFor { get; protected set; } + // public List? WineType { get; protected set; } + public Wine() + { + + } + public Wine(string name, string ean, int quantity, float price, string imageUrl, DateTime expirationDate, int? year, double? volume, double? alcoholPercentage, string? country, string? region, string? grapeSort, string? winery, List? tastingNotes, List? suitableFor, List? wineType) { this.Name = name; @@ -28,8 +33,8 @@ public Wine(string name, string ean, int quantity, float price, string imageUrl, this.Region = region; this.GrapeSort = grapeSort; this.Winery = winery; - this.TastingNotes = tastingNotes; - this.SuitableFor = suitableFor; - this.WineType = wineType; + // this.TastingNotes = tastingNotes; + // this.SuitableFor = suitableFor; + // this.WineType = wineType; } } diff --git a/Program.cs b/Program.cs index 9964ffb..efc3cb2 100644 --- a/Program.cs +++ b/Program.cs @@ -53,6 +53,7 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); diff --git a/Services/Shared/IItemService.cs b/Services/Shared/IItemService.cs new file mode 100644 index 0000000..0dc0708 --- /dev/null +++ b/Services/Shared/IItemService.cs @@ -0,0 +1,9 @@ +using API.Models.Items; + +namespace API.Services.Shared; + +public interface IItemService +{ + public Task> GetAllItems(); + public Task GetItemById(int id); +} \ No newline at end of file diff --git a/Services/Shared/ItemService.cs b/Services/Shared/ItemService.cs new file mode 100644 index 0000000..526113e --- /dev/null +++ b/Services/Shared/ItemService.cs @@ -0,0 +1,25 @@ +using API.Models; +using API.Models.Items; +using Microsoft.EntityFrameworkCore; + +namespace API.Services.Shared; + +public class ItemService : IItemService +{ + private readonly DBContext _context; + + public ItemService(DBContext dbContext) + { + _context = dbContext; + } + + public async Task> GetAllItems() + { + return await _context.Items.ToListAsync(); + } + + public async Task GetItemById(int id) + { + return await _context.Items.FirstOrDefaultAsync(i => i.Id == id); + } +} \ No newline at end of file diff --git a/bin/Debug/net7.0/API b/bin/Debug/net7.0/API index 457cb8722515302f23ba9f45f4eecc5be9f12c0f..a2f979c7fef1f37eb6093d3a97458bf8957e8daf 100755 GIT binary patch literal 181016 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 Z(2yBFt@$@a!BLJmn`M5hzAWmr{a-KozFz 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 Z(2yBFt@$@a!BLJmn`M5hzAWmr{a-KozFz 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 Date: Tue, 21 Nov 2023 14:31:25 +0100 Subject: [PATCH 018/112] SW-99 * Fixed model and migrations --- Controllers/Shared/ItemController.cs | 3 +- ...0231121132304_AddedStateToWine.Designer.cs | 282 ++++++++++++++++++ Migrations/20231121132304_AddedStateToWine.cs | 125 ++++++++ ...1132822_ExpirationDateNullable.Designer.cs | 282 ++++++++++++++++++ .../20231121132822_ExpirationDateNullable.cs | 37 +++ Migrations/DBContextModelSnapshot.cs | 32 +- Models/Authentication/Admin.cs | 4 +- Models/Items/DefaultItem.cs | 3 +- Models/Items/Item.cs | 2 +- Models/Items/Liquor.cs | 3 +- Models/Items/Wine.cs | 18 +- 11 files changed, 772 insertions(+), 19 deletions(-) create mode 100644 Migrations/20231121132304_AddedStateToWine.Designer.cs create mode 100644 Migrations/20231121132304_AddedStateToWine.cs create mode 100644 Migrations/20231121132822_ExpirationDateNullable.Designer.cs create mode 100644 Migrations/20231121132822_ExpirationDateNullable.cs diff --git a/Controllers/Shared/ItemController.cs b/Controllers/Shared/ItemController.cs index 2c1edeb..69f2649 100644 --- a/Controllers/Shared/ItemController.cs +++ b/Controllers/Shared/ItemController.cs @@ -1,6 +1,5 @@ using API.Models.Items; using API.Services.Shared; -using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace API.Controllers.Shared @@ -19,7 +18,7 @@ public async Task>> GetItems() { return await _itemService.GetAllItems(); } - + [HttpGet("{id}")] public async Task> GetItemById(int id) { diff --git a/Migrations/20231121132304_AddedStateToWine.Designer.cs b/Migrations/20231121132304_AddedStateToWine.Designer.cs new file mode 100644 index 0000000..75adff6 --- /dev/null +++ b/Migrations/20231121132304_AddedStateToWine.Designer.cs @@ -0,0 +1,282 @@ +// +using System; +using API.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace API.Migrations +{ + [DbContext(typeof(DBContext))] + [Migration("20231121132304_AddedStateToWine")] + partial class AddedStateToWine + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("API.Models.Authentication.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Email") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Phone") + .HasColumnType("int"); + + b.Property("Token") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TokenExpiration") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.ToTable("Users"); + + b.HasDiscriminator("Discriminator").HasValue("User"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("API.Models.Items.Item", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Ean") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("ImageUrl") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrderId") + .HasColumnType("int"); + + b.Property("Price") + .HasColumnType("float"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.Property("SupplierId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("OrderId"); + + b.HasIndex("SupplierId"); + + b.ToTable("Items"); + + b.HasDiscriminator("Discriminator").HasValue("Item"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("API.Models.Orders.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Comment") + .HasColumnType("longtext"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Order"); + + b.HasDiscriminator("Discriminator").HasValue("Order"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("API.Models.Suppliers.Supplier", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Suppliers"); + }); + + modelBuilder.Entity("API.Models.Authentication.Customer", b => + { + b.HasBaseType("API.Models.Authentication.User"); + + b.Property("PhoneNumber") + .HasColumnType("int"); + + b.HasDiscriminator().HasValue("Customer"); + }); + + modelBuilder.Entity("API.Models.Authentication.Employee", b => + { + b.HasBaseType("API.Models.Authentication.User"); + + b.HasDiscriminator().HasValue("Employee"); + }); + + modelBuilder.Entity("API.Models.Items.DefaultItem", b => + { + b.HasBaseType("API.Models.Items.Item"); + + b.HasDiscriminator().HasValue("DefaultItem"); + }); + + modelBuilder.Entity("API.Models.Items.Liquor", b => + { + b.HasBaseType("API.Models.Items.Item"); + + b.Property("LiquorType") + .HasColumnType("longtext"); + + b.HasDiscriminator().HasValue("Liquor"); + }); + + modelBuilder.Entity("API.Models.Items.Wine", b => + { + b.HasBaseType("API.Models.Items.Item"); + + b.Property("AlcoholPercentage") + .HasColumnType("double"); + + b.Property("Country") + .HasColumnType("longtext"); + + b.Property("GrapeSort") + .HasColumnType("longtext"); + + b.Property("Region") + .HasColumnType("longtext"); + + b.Property("SuitableFor") + .HasColumnType("longtext"); + + b.Property("TastingNotes") + .HasColumnType("longtext"); + + b.Property("Volume") + .HasColumnType("double"); + + b.Property("WineType") + .HasColumnType("longtext"); + + b.Property("Winery") + .HasColumnType("longtext"); + + b.Property("Year") + .HasColumnType("int"); + + b.HasDiscriminator().HasValue("Wine"); + }); + + modelBuilder.Entity("API.Models.Orders.CustomerPurchaseOrder", b => + { + b.HasBaseType("API.Models.Orders.Order"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.HasIndex("CustomerId"); + + b.HasDiscriminator().HasValue("CustomerPurchaseOrder"); + }); + + modelBuilder.Entity("API.Models.Authentication.Admin", b => + { + b.HasBaseType("API.Models.Authentication.Employee"); + + b.HasDiscriminator().HasValue("Admin"); + }); + + modelBuilder.Entity("API.Models.Items.Item", b => + { + b.HasOne("API.Models.Orders.Order", null) + .WithMany("Items") + .HasForeignKey("OrderId"); + + b.HasOne("API.Models.Suppliers.Supplier", null) + .WithMany("Items") + .HasForeignKey("SupplierId"); + }); + + modelBuilder.Entity("API.Models.Orders.CustomerPurchaseOrder", b => + { + b.HasOne("API.Models.Authentication.Customer", "Customer") + .WithMany("CustomerPurchaseOrders") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Customer"); + }); + + modelBuilder.Entity("API.Models.Orders.Order", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("API.Models.Suppliers.Supplier", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("API.Models.Authentication.Customer", b => + { + b.Navigation("CustomerPurchaseOrders"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Migrations/20231121132304_AddedStateToWine.cs b/Migrations/20231121132304_AddedStateToWine.cs new file mode 100644 index 0000000..a298c68 --- /dev/null +++ b/Migrations/20231121132304_AddedStateToWine.cs @@ -0,0 +1,125 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace API.Migrations +{ + /// + public partial class AddedStateToWine : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "AlcoholPercentage", + table: "Items", + type: "double", + nullable: true); + + migrationBuilder.AddColumn( + name: "Country", + table: "Items", + type: "longtext", + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "GrapeSort", + table: "Items", + type: "longtext", + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "Region", + table: "Items", + type: "longtext", + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "SuitableFor", + table: "Items", + type: "longtext", + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "TastingNotes", + table: "Items", + type: "longtext", + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "Volume", + table: "Items", + type: "double", + nullable: true); + + migrationBuilder.AddColumn( + name: "WineType", + table: "Items", + type: "longtext", + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "Winery", + table: "Items", + type: "longtext", + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "Year", + table: "Items", + type: "int", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "AlcoholPercentage", + table: "Items"); + + migrationBuilder.DropColumn( + name: "Country", + table: "Items"); + + migrationBuilder.DropColumn( + name: "GrapeSort", + table: "Items"); + + migrationBuilder.DropColumn( + name: "Region", + table: "Items"); + + migrationBuilder.DropColumn( + name: "SuitableFor", + table: "Items"); + + migrationBuilder.DropColumn( + name: "TastingNotes", + table: "Items"); + + migrationBuilder.DropColumn( + name: "Volume", + table: "Items"); + + migrationBuilder.DropColumn( + name: "WineType", + table: "Items"); + + migrationBuilder.DropColumn( + name: "Winery", + table: "Items"); + + migrationBuilder.DropColumn( + name: "Year", + table: "Items"); + } + } +} diff --git a/Migrations/20231121132822_ExpirationDateNullable.Designer.cs b/Migrations/20231121132822_ExpirationDateNullable.Designer.cs new file mode 100644 index 0000000..07eea1e --- /dev/null +++ b/Migrations/20231121132822_ExpirationDateNullable.Designer.cs @@ -0,0 +1,282 @@ +// +using System; +using API.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace API.Migrations +{ + [DbContext(typeof(DBContext))] + [Migration("20231121132822_ExpirationDateNullable")] + partial class ExpirationDateNullable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("API.Models.Authentication.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Email") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Phone") + .HasColumnType("int"); + + b.Property("Token") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TokenExpiration") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.ToTable("Users"); + + b.HasDiscriminator("Discriminator").HasValue("User"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("API.Models.Items.Item", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Ean") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("ImageUrl") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrderId") + .HasColumnType("int"); + + b.Property("Price") + .HasColumnType("float"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.Property("SupplierId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("OrderId"); + + b.HasIndex("SupplierId"); + + b.ToTable("Items"); + + b.HasDiscriminator("Discriminator").HasValue("Item"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("API.Models.Orders.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Comment") + .HasColumnType("longtext"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Order"); + + b.HasDiscriminator("Discriminator").HasValue("Order"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("API.Models.Suppliers.Supplier", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Suppliers"); + }); + + modelBuilder.Entity("API.Models.Authentication.Customer", b => + { + b.HasBaseType("API.Models.Authentication.User"); + + b.Property("PhoneNumber") + .HasColumnType("int"); + + b.HasDiscriminator().HasValue("Customer"); + }); + + modelBuilder.Entity("API.Models.Authentication.Employee", b => + { + b.HasBaseType("API.Models.Authentication.User"); + + b.HasDiscriminator().HasValue("Employee"); + }); + + modelBuilder.Entity("API.Models.Items.DefaultItem", b => + { + b.HasBaseType("API.Models.Items.Item"); + + b.HasDiscriminator().HasValue("DefaultItem"); + }); + + modelBuilder.Entity("API.Models.Items.Liquor", b => + { + b.HasBaseType("API.Models.Items.Item"); + + b.Property("LiquorType") + .HasColumnType("longtext"); + + b.HasDiscriminator().HasValue("Liquor"); + }); + + modelBuilder.Entity("API.Models.Items.Wine", b => + { + b.HasBaseType("API.Models.Items.Item"); + + b.Property("AlcoholPercentage") + .HasColumnType("double"); + + b.Property("Country") + .HasColumnType("longtext"); + + b.Property("GrapeSort") + .HasColumnType("longtext"); + + b.Property("Region") + .HasColumnType("longtext"); + + b.Property("SuitableFor") + .HasColumnType("longtext"); + + b.Property("TastingNotes") + .HasColumnType("longtext"); + + b.Property("Volume") + .HasColumnType("double"); + + b.Property("WineType") + .HasColumnType("longtext"); + + b.Property("Winery") + .HasColumnType("longtext"); + + b.Property("Year") + .HasColumnType("int"); + + b.HasDiscriminator().HasValue("Wine"); + }); + + modelBuilder.Entity("API.Models.Orders.CustomerPurchaseOrder", b => + { + b.HasBaseType("API.Models.Orders.Order"); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.HasIndex("CustomerId"); + + b.HasDiscriminator().HasValue("CustomerPurchaseOrder"); + }); + + modelBuilder.Entity("API.Models.Authentication.Admin", b => + { + b.HasBaseType("API.Models.Authentication.Employee"); + + b.HasDiscriminator().HasValue("Admin"); + }); + + modelBuilder.Entity("API.Models.Items.Item", b => + { + b.HasOne("API.Models.Orders.Order", null) + .WithMany("Items") + .HasForeignKey("OrderId"); + + b.HasOne("API.Models.Suppliers.Supplier", null) + .WithMany("Items") + .HasForeignKey("SupplierId"); + }); + + modelBuilder.Entity("API.Models.Orders.CustomerPurchaseOrder", b => + { + b.HasOne("API.Models.Authentication.Customer", "Customer") + .WithMany("CustomerPurchaseOrders") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Customer"); + }); + + modelBuilder.Entity("API.Models.Orders.Order", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("API.Models.Suppliers.Supplier", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("API.Models.Authentication.Customer", b => + { + b.Navigation("CustomerPurchaseOrders"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Migrations/20231121132822_ExpirationDateNullable.cs b/Migrations/20231121132822_ExpirationDateNullable.cs new file mode 100644 index 0000000..a72e234 --- /dev/null +++ b/Migrations/20231121132822_ExpirationDateNullable.cs @@ -0,0 +1,37 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace API.Migrations +{ + /// + public partial class ExpirationDateNullable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "ExpirationDate", + table: "Items", + type: "datetime(6)", + nullable: true, + oldClrType: typeof(DateTime), + oldType: "datetime(6)"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "ExpirationDate", + table: "Items", + type: "datetime(6)", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + oldClrType: typeof(DateTime), + oldType: "datetime(6)", + oldNullable: true); + } + } +} diff --git a/Migrations/DBContextModelSnapshot.cs b/Migrations/DBContextModelSnapshot.cs index aa6aa0b..0461812 100644 --- a/Migrations/DBContextModelSnapshot.cs +++ b/Migrations/DBContextModelSnapshot.cs @@ -78,7 +78,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("longtext"); - b.Property("ExpirationDate") + b.Property("ExpirationDate") .HasColumnType("datetime(6)"); b.Property("ImageUrl") @@ -185,6 +185,36 @@ protected override void BuildModel(ModelBuilder modelBuilder) { b.HasBaseType("API.Models.Items.Item"); + b.Property("AlcoholPercentage") + .HasColumnType("double"); + + b.Property("Country") + .HasColumnType("longtext"); + + b.Property("GrapeSort") + .HasColumnType("longtext"); + + b.Property("Region") + .HasColumnType("longtext"); + + b.Property("SuitableFor") + .HasColumnType("longtext"); + + b.Property("TastingNotes") + .HasColumnType("longtext"); + + b.Property("Volume") + .HasColumnType("double"); + + b.Property("WineType") + .HasColumnType("longtext"); + + b.Property("Winery") + .HasColumnType("longtext"); + + b.Property("Year") + .HasColumnType("int"); + b.HasDiscriminator().HasValue("Wine"); }); diff --git a/Models/Authentication/Admin.cs b/Models/Authentication/Admin.cs index b7d2a15..7293a67 100644 --- a/Models/Authentication/Admin.cs +++ b/Models/Authentication/Admin.cs @@ -45,8 +45,8 @@ public void EditUser(User user, string firstName, string lastName, int phone, st Console.WriteLine(user.GetType()); } - public Item CreateItem(string name, string ean, int quantity, float price, string imageUrl, DateTime expirationDate) + public Item CreateItem(string name, string ean, int quantity, float price, string imageUrl) { - return new DefaultItem(name, ean, quantity, price, imageUrl, expirationDate); + return new DefaultItem(name, ean, quantity, price, imageUrl); } } diff --git a/Models/Items/DefaultItem.cs b/Models/Items/DefaultItem.cs index ed59f61..5b3aad9 100644 --- a/Models/Items/DefaultItem.cs +++ b/Models/Items/DefaultItem.cs @@ -2,13 +2,12 @@ namespace API.Models.Items; public class DefaultItem: Item { - public DefaultItem(string name, string ean, int quantity, float price, string imageUrl, DateTime expirationDate) + public DefaultItem(string name, string ean, int quantity, float price, string imageUrl) { this.Name = name; this.Ean = ean; this.Quantity = quantity; this.Price = price; this.ImageUrl = imageUrl; - this.ExpirationDate = expirationDate; } } diff --git a/Models/Items/Item.cs b/Models/Items/Item.cs index 8a408e3..b0b6f71 100644 --- a/Models/Items/Item.cs +++ b/Models/Items/Item.cs @@ -8,7 +8,7 @@ public abstract class Item public int Quantity { get; protected set; } public float Price { get; protected set; } public string ImageUrl { get; protected set; } - public DateTime ExpirationDate { get; protected set; } + public DateTime? ExpirationDate { get; protected set; } } diff --git a/Models/Items/Liquor.cs b/Models/Items/Liquor.cs index 1655db2..5140fc8 100644 --- a/Models/Items/Liquor.cs +++ b/Models/Items/Liquor.cs @@ -4,14 +4,13 @@ public class Liquor: Item { public string? LiquorType { get; set; } - public Liquor(string name, string ean, int quantity, float price, string imageUrl, DateTime expirationDate, string liquorType) + public Liquor(string name, string ean, int quantity, float price, string imageUrl, string liquorType) { this.Name = name; this.Ean = ean; this.Quantity = quantity; this.Price = price; this.ImageUrl = imageUrl; - this.ExpirationDate = expirationDate; this.LiquorType = liquorType; } } diff --git a/Models/Items/Wine.cs b/Models/Items/Wine.cs index 2140afa..0477fa2 100644 --- a/Models/Items/Wine.cs +++ b/Models/Items/Wine.cs @@ -9,16 +9,16 @@ public class Wine : Item public string? Region { get; protected set; } public string? GrapeSort { get; protected set; } public string? Winery { get; protected set; } - // public List? TastingNotes { get; protected set; } - // public List? SuitableFor { get; protected set; } - // public List? WineType { get; protected set; } + public string? TastingNotes { get; protected set; } + public string? SuitableFor { get; protected set; } + public string? WineType { get; protected set; } public Wine() { - + } - - public Wine(string name, string ean, int quantity, float price, string imageUrl, DateTime expirationDate, int? year, double? volume, double? alcoholPercentage, string? country, string? region, string? grapeSort, string? winery, List? tastingNotes, List? suitableFor, List? wineType) + + public Wine(string name, string ean, int quantity, float price, string imageUrl, DateTime expirationDate, int? year, double? volume, double? alcoholPercentage, string? country, string? region, string? grapeSort, string? winery, string? tastingNotes, string? suitableFor, string? wineType) { this.Name = name; this.Ean = ean; @@ -33,8 +33,8 @@ public Wine(string name, string ean, int quantity, float price, string imageUrl, this.Region = region; this.GrapeSort = grapeSort; this.Winery = winery; - // this.TastingNotes = tastingNotes; - // this.SuitableFor = suitableFor; - // this.WineType = wineType; + this.TastingNotes = tastingNotes; + this.SuitableFor = suitableFor; + this.WineType = wineType; } } From ce97c6e98c053e83f05e422fb513bf8005b61729 Mon Sep 17 00:00:00 2001 From: Thomas Andersen Date: Wed, 22 Nov 2023 10:31:28 +0100 Subject: [PATCH 019/112] SW-108 * Added the getSelf and editSelf --- Controllers/Shared/UserController.cs | 11 +++++++++-- Models/Items/Wine.cs | 18 ++++-------------- Services/Shared/IUserService.cs | 4 ++++ Services/Shared/UserService.cs | 23 +++++++++++++++-------- 4 files changed, 32 insertions(+), 24 deletions(-) diff --git a/Controllers/Shared/UserController.cs b/Controllers/Shared/UserController.cs index c4d78bf..5067b2a 100644 --- a/Controllers/Shared/UserController.cs +++ b/Controllers/Shared/UserController.cs @@ -24,13 +24,20 @@ public async Task>> GetUsers() return await _userService.GetAllUsers(); } - [HttpPost("/edit")] + [HttpGet("self")] + [Authorize] + public async Task> GetSelf() + { + return await _userService.GetSelf(); + } + + [HttpPut("edit")] [Authorize] public async Task EditSelf(UserStandardDto user) { try { - await _userService.EditUser(user); + await _userService.EditSelf(user); } catch (Exception e) { diff --git a/Models/Items/Wine.cs b/Models/Items/Wine.cs index e666b81..3147614 100644 --- a/Models/Items/Wine.cs +++ b/Models/Items/Wine.cs @@ -9,11 +9,11 @@ public class Wine : Item public string? Region { get; protected set; } public string? GrapeSort { get; protected set; } public string? Winery { get; protected set; } - public List? TastingNotes { get; protected set; } - public List? SuitableFor { get; protected set; } - public List? WineType { get; protected set; } + public string? TastingNotes { get; protected set; } + public string? SuitableFor { get; protected set; } + public string? WineType { get; protected set; } - public Wine(string name, string ean, int quantity, float price, string imageUrl, DateTime expirationDate, int? year, double? volume, double? alcoholPercentage, string? country, string? region, string? grapeSort, string? winery, List? tastingNotes, List? suitableFor, List? wineType) + public Wine(string name, string ean, int quantity, float price, string imageUrl, DateTime expirationDate) { this.Name = name; this.Ean = ean; @@ -21,15 +21,5 @@ public Wine(string name, string ean, int quantity, float price, string imageUrl, this.Price = price; this.ImageUrl = imageUrl; this.ExpirationDate = expirationDate; - this.Year = year; - this.Volume = volume; - this.AlcoholPercentage = alcoholPercentage; - this.Country = country; - this.Region = region; - this.GrapeSort = grapeSort; - this.Winery = winery; - this.TastingNotes = tastingNotes; - this.SuitableFor = suitableFor; - this.WineType = wineType; } } diff --git a/Services/Shared/IUserService.cs b/Services/Shared/IUserService.cs index 818c4e0..4b41e7d 100644 --- a/Services/Shared/IUserService.cs +++ b/Services/Shared/IUserService.cs @@ -8,5 +8,9 @@ public interface IUserService { public Task> GetAllUsers(); + public Task GetSelf(); + public Task EditUser(UserStandardDto user); + + public Task EditSelf(UserStandardDto user); } diff --git a/Services/Shared/UserService.cs b/Services/Shared/UserService.cs index 87d5e1c..c04bf98 100644 --- a/Services/Shared/UserService.cs +++ b/Services/Shared/UserService.cs @@ -21,17 +21,24 @@ public async Task> GetAllUsers() { return await _context.Users.ToListAsync(); } + + public async Task GetSelf() + { + return await _authService.GetActiveUser(); + } + + public Task EditUser(UserStandardDto user) + { + throw new NotImplementedException(); + } - public async Task EditUser(UserStandardDto user) + public async Task EditSelf(UserStandardDto user) { var requestingUser = await _authService.GetActiveUser(); - if (requestingUser.Email != user.Email) - { - throw new Exception("You can only edit your own user"); - } - Console.WriteLine(user.GetType()); - Console.WriteLine(requestingUser.GetType()); - throw new NotImplementedException(); + requestingUser.ChangeUserStandardProperties(user.FirstName, user.LastName, user.Phone, user.Email, user.Password); + + await _context.SaveChangesAsync(); + return requestingUser; } } From 4abe615b3f1e8c0a638f3bd5035ef3b149e9dd66 Mon Sep 17 00:00:00 2001 From: Thomas Andersen Date: Wed, 22 Nov 2023 10:35:38 +0100 Subject: [PATCH 020/112] SW-108 * Formatering --- Services/Shared/UserService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Services/Shared/UserService.cs b/Services/Shared/UserService.cs index c04bf98..e6eda41 100644 --- a/Services/Shared/UserService.cs +++ b/Services/Shared/UserService.cs @@ -21,7 +21,7 @@ public async Task> GetAllUsers() { return await _context.Users.ToListAsync(); } - + public async Task GetSelf() { return await _authService.GetActiveUser(); From ff6db0025dcd31f5c3d55b46c127ee1fe206aeda Mon Sep 17 00:00:00 2001 From: Thomas Andersen Date: Wed, 22 Nov 2023 11:18:39 +0100 Subject: [PATCH 021/112] SW-108 * Renamed variables --- API.csproj | 2 +- DataTransferObjects/ItemDto.cs | 2 +- Migrations/20231106111910_Initial.Designer.cs | 2 +- ..._UpdatedUsersAndItemsAndAddedOrders.Designer.cs | 2 +- .../20231121132304_AddedStateToWine.Designer.cs | 2 +- ...231121132822_ExpirationDateNullable.Designer.cs | 2 +- Migrations/DBContextModelSnapshot.cs | 4 ++-- Models/Authentication/Customer.cs | 2 +- Models/Authentication/User.cs | 2 +- Models/DBContext.cs | 5 +++-- Models/Items/Liquor.cs | 2 +- Models/Items/Wine.cs | 14 +++++++------- Models/Orders/CustomerPurchaseOrder.cs | 2 +- Models/Orders/InboundOrder.cs | 1 - Models/Orders/Order.cs | 2 +- Models/Schedule/Schedule.cs | 2 +- Models/Suppliers/Supplier.cs | 2 +- Program.cs | 2 +- Services/Shared/AuthService.cs | 4 ++-- Services/Shared/AuthenticationService.cs | 6 ++---- Services/Shared/ItemService.cs | 6 +++--- Services/Shared/TokenService.cs | 7 ------- Services/Shared/UserService.cs | 4 ++-- WeatherForecast.cs | 4 ++-- 24 files changed, 37 insertions(+), 46 deletions(-) diff --git a/API.csproj b/API.csproj index 03ee93d..a137f01 100644 --- a/API.csproj +++ b/API.csproj @@ -2,7 +2,7 @@ net7.0 - enable + disable enable Windows diff --git a/DataTransferObjects/ItemDto.cs b/DataTransferObjects/ItemDto.cs index da2a94f..11d5f1a 100644 --- a/DataTransferObjects/ItemDto.cs +++ b/DataTransferObjects/ItemDto.cs @@ -3,5 +3,5 @@ namespace API.DataTransferObjects; public class ItemDto { public Int32 Id { get; set; } - public string? Name { get; set; } + public string Name { get; set; } } diff --git a/Migrations/20231106111910_Initial.Designer.cs b/Migrations/20231106111910_Initial.Designer.cs index 49e46a9..b3a0cb7 100644 --- a/Migrations/20231106111910_Initial.Designer.cs +++ b/Migrations/20231106111910_Initial.Designer.cs @@ -10,7 +10,7 @@ namespace API.Migrations { - [DbContext(typeof(DBContext))] + [DbContext(typeof(Context))] [Migration("20231106111910_Initial")] partial class Initial { diff --git a/Migrations/20231117191528_UpdatedUsersAndItemsAndAddedOrders.Designer.cs b/Migrations/20231117191528_UpdatedUsersAndItemsAndAddedOrders.Designer.cs index 327c62e..278bc21 100644 --- a/Migrations/20231117191528_UpdatedUsersAndItemsAndAddedOrders.Designer.cs +++ b/Migrations/20231117191528_UpdatedUsersAndItemsAndAddedOrders.Designer.cs @@ -10,7 +10,7 @@ namespace API.Migrations { - [DbContext(typeof(DBContext))] + [DbContext(typeof(Context))] [Migration("20231117191528_UpdatedUsersAndItemsAndAddedOrders")] partial class UpdatedUsersAndItemsAndAddedOrders { diff --git a/Migrations/20231121132304_AddedStateToWine.Designer.cs b/Migrations/20231121132304_AddedStateToWine.Designer.cs index 75adff6..c43ae44 100644 --- a/Migrations/20231121132304_AddedStateToWine.Designer.cs +++ b/Migrations/20231121132304_AddedStateToWine.Designer.cs @@ -10,7 +10,7 @@ namespace API.Migrations { - [DbContext(typeof(DBContext))] + [DbContext(typeof(Context))] [Migration("20231121132304_AddedStateToWine")] partial class AddedStateToWine { diff --git a/Migrations/20231121132822_ExpirationDateNullable.Designer.cs b/Migrations/20231121132822_ExpirationDateNullable.Designer.cs index 07eea1e..942c3f8 100644 --- a/Migrations/20231121132822_ExpirationDateNullable.Designer.cs +++ b/Migrations/20231121132822_ExpirationDateNullable.Designer.cs @@ -10,7 +10,7 @@ namespace API.Migrations { - [DbContext(typeof(DBContext))] + [DbContext(typeof(Context))] [Migration("20231121132822_ExpirationDateNullable")] partial class ExpirationDateNullable { diff --git a/Migrations/DBContextModelSnapshot.cs b/Migrations/DBContextModelSnapshot.cs index 0461812..17ae75e 100644 --- a/Migrations/DBContextModelSnapshot.cs +++ b/Migrations/DBContextModelSnapshot.cs @@ -9,8 +9,8 @@ namespace API.Migrations { - [DbContext(typeof(DBContext))] - partial class DBContextModelSnapshot : ModelSnapshot + [DbContext(typeof(Context))] + partial class ContextModelSnapshot : ModelSnapshot { protected override void BuildModel(ModelBuilder modelBuilder) { diff --git a/Models/Authentication/Customer.cs b/Models/Authentication/Customer.cs index 978e6de..8411209 100644 --- a/Models/Authentication/Customer.cs +++ b/Models/Authentication/Customer.cs @@ -5,7 +5,7 @@ namespace API.Models.Authentication; public class Customer : User { - public int? PhoneNumber { get; protected set; } = null; + public int? PhoneNumber { get; protected set; } public List CustomerPurchaseOrders { get; protected set; } diff --git a/Models/Authentication/User.cs b/Models/Authentication/User.cs index 63ed3dd..e1d8fed 100644 --- a/Models/Authentication/User.cs +++ b/Models/Authentication/User.cs @@ -14,7 +14,7 @@ public abstract class User public string Token { get; protected set; } public DateTime? TokenExpiration { get; protected set; } - public static User CreateNewUser(DBContext context,SignupDto signupDto) + public static User CreateNewUser(Context context,SignupDto signupDto) { switch (signupDto.DesiredRole) { diff --git a/Models/DBContext.cs b/Models/DBContext.cs index 631c2d7..2d02916 100644 --- a/Models/DBContext.cs +++ b/Models/DBContext.cs @@ -6,10 +6,11 @@ namespace API.Models; -public class DBContext : DbContext +public class Context : DbContext { - public DBContext(DbContextOptions options) : base(options) + public Context(DbContextOptions options) : base(options) { + } public DbSet Employees { get; set; } diff --git a/Models/Items/Liquor.cs b/Models/Items/Liquor.cs index 5140fc8..acae0a3 100644 --- a/Models/Items/Liquor.cs +++ b/Models/Items/Liquor.cs @@ -2,7 +2,7 @@ namespace API.Models.Items; public class Liquor: Item { - public string? LiquorType { get; set; } + public string LiquorType { get; set; } public Liquor(string name, string ean, int quantity, float price, string imageUrl, string liquorType) { diff --git a/Models/Items/Wine.cs b/Models/Items/Wine.cs index 3147614..bcc0bb7 100644 --- a/Models/Items/Wine.cs +++ b/Models/Items/Wine.cs @@ -5,13 +5,13 @@ public class Wine : Item public int? Year { get; protected set; } public double? Volume { get; protected set; } public double? AlcoholPercentage { get; protected set; } - public string? Country { get; protected set; } - public string? Region { get; protected set; } - public string? GrapeSort { get; protected set; } - public string? Winery { get; protected set; } - public string? TastingNotes { get; protected set; } - public string? SuitableFor { get; protected set; } - public string? WineType { get; protected set; } + public string Country { get; protected set; } + public string Region { get; protected set; } + public string GrapeSort { get; protected set; } + public string Winery { get; protected set; } + public string TastingNotes { get; protected set; } + public string SuitableFor { get; protected set; } + public string WineType { get; protected set; } public Wine(string name, string ean, int quantity, float price, string imageUrl, DateTime expirationDate) { diff --git a/Models/Orders/CustomerPurchaseOrder.cs b/Models/Orders/CustomerPurchaseOrder.cs index 9734bd7..602c1ef 100644 --- a/Models/Orders/CustomerPurchaseOrder.cs +++ b/Models/Orders/CustomerPurchaseOrder.cs @@ -13,7 +13,7 @@ protected CustomerPurchaseOrder() } - public CustomerPurchaseOrder(Customer customer, List items, string? comment) + public CustomerPurchaseOrder(Customer customer, List items, string comment) { this.Customer = customer; this.Items.AddRange(items); diff --git a/Models/Orders/InboundOrder.cs b/Models/Orders/InboundOrder.cs index a1bbdec..20c3276 100644 --- a/Models/Orders/InboundOrder.cs +++ b/Models/Orders/InboundOrder.cs @@ -4,6 +4,5 @@ namespace API.Models.Orders; public class InboundOrder: Order { - public int Id { get; set; } public Supplier Supplier { get; set; } } diff --git a/Models/Orders/Order.cs b/Models/Orders/Order.cs index 4d06192..efea01d 100644 --- a/Models/Orders/Order.cs +++ b/Models/Orders/Order.cs @@ -6,5 +6,5 @@ public abstract class Order { public int Id { get; protected set; } public List Items { get; protected set; } = new List(); - public string? Comment { get; protected set; } + public string Comment { get; protected set; } } diff --git a/Models/Schedule/Schedule.cs b/Models/Schedule/Schedule.cs index ada9bca..8d3e7d1 100644 --- a/Models/Schedule/Schedule.cs +++ b/Models/Schedule/Schedule.cs @@ -1,4 +1,4 @@ -namespace API.Models; +namespace API.Models.Schedule; public class Schedule { diff --git a/Models/Suppliers/Supplier.cs b/Models/Suppliers/Supplier.cs index 46feb19..e7faf11 100644 --- a/Models/Suppliers/Supplier.cs +++ b/Models/Suppliers/Supplier.cs @@ -5,5 +5,5 @@ namespace API.Models.Suppliers; public class Supplier { public int Id { get; set; } - public Item[]? Items { get; set; } + public Item[] Items { get; set; } } diff --git a/Program.cs b/Program.cs index efc3cb2..2985675 100644 --- a/Program.cs +++ b/Program.cs @@ -44,7 +44,7 @@ builder.Services.AddHttpContextAccessor(); -builder.Services.AddDbContext(opt => +builder.Services.AddDbContext(opt => { opt.UseMySql("server=localhost;port=3306;database=EVENTILOPE;user=THOMAS;password=password;", new MySqlServerVersion(new Version(8, 0, 26))); // Specify the MySQL server version here diff --git a/Services/Shared/AuthService.cs b/Services/Shared/AuthService.cs index 440c8b5..f526096 100644 --- a/Services/Shared/AuthService.cs +++ b/Services/Shared/AuthService.cs @@ -8,10 +8,10 @@ namespace API.Services.Shared public class AuthService : IAuthService { private readonly IHttpContextAccessor _httpContextAccessor; - private readonly DBContext _context; + private readonly Context _context; - public AuthService(IHttpContextAccessor httpContextAccessor, DBContext context) + public AuthService(IHttpContextAccessor httpContextAccessor, Context context) { _httpContextAccessor = httpContextAccessor; _context = context; diff --git a/Services/Shared/AuthenticationService.cs b/Services/Shared/AuthenticationService.cs index 1d3b747..c8d8ffe 100644 --- a/Services/Shared/AuthenticationService.cs +++ b/Services/Shared/AuthenticationService.cs @@ -7,14 +7,12 @@ namespace API.Services.Shared { public class AuthenticationService : IAuthenticationService { - private readonly DBContext _context; - private readonly IConfiguration _configuration; + private readonly Context _context; private readonly ITokenService _tokenService; - public AuthenticationService(DBContext dbContext, IConfiguration configuration, ITokenService tokenService) + public AuthenticationService(Context dbContext, ITokenService tokenService) { _context = dbContext; - _configuration = configuration; _tokenService = tokenService; } diff --git a/Services/Shared/ItemService.cs b/Services/Shared/ItemService.cs index 526113e..342b6f5 100644 --- a/Services/Shared/ItemService.cs +++ b/Services/Shared/ItemService.cs @@ -6,9 +6,9 @@ namespace API.Services.Shared; public class ItemService : IItemService { - private readonly DBContext _context; + private readonly Context _context; - public ItemService(DBContext dbContext) + public ItemService(Context dbContext) { _context = dbContext; } @@ -22,4 +22,4 @@ public async Task GetItemById(int id) { return await _context.Items.FirstOrDefaultAsync(i => i.Id == id); } -} \ No newline at end of file +} diff --git a/Services/Shared/TokenService.cs b/Services/Shared/TokenService.cs index 219b885..9937e8c 100644 --- a/Services/Shared/TokenService.cs +++ b/Services/Shared/TokenService.cs @@ -8,13 +8,6 @@ namespace API.Services.Shared; public class TokenService : ITokenService { - private readonly IConfiguration _configuration; - - public TokenService(IConfiguration configuration) - { - _configuration = configuration; - } - /// /// Generates a token for a given user /// diff --git a/Services/Shared/UserService.cs b/Services/Shared/UserService.cs index e6eda41..d4d4459 100644 --- a/Services/Shared/UserService.cs +++ b/Services/Shared/UserService.cs @@ -8,10 +8,10 @@ namespace API.Services.Shared; public class UserService : IUserService { - private readonly DBContext _context; + private readonly Context _context; private readonly IAuthService _authService; - public UserService(DBContext dbContext, IAuthService authService) + public UserService(Context dbContext, IAuthService authService) { _context = dbContext; _authService = authService; diff --git a/WeatherForecast.cs b/WeatherForecast.cs index 3e5e783..f9a4e59 100644 --- a/WeatherForecast.cs +++ b/WeatherForecast.cs @@ -8,5 +8,5 @@ public class WeatherForecast public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); - public string? Summary { get; set; } -} \ No newline at end of file + public string Summary { get; set; } +} From 11705ce1995c9e16be3529f55874711f6026cbb6 Mon Sep 17 00:00:00 2001 From: AndreasBM1 Date: Wed, 22 Nov 2023 11:41:07 +0100 Subject: [PATCH 022/112] SW-109 * Fixed merge conflict --- Models/Items/Item.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Models/Items/Item.cs b/Models/Items/Item.cs index 8a408e3..7f9ad20 100644 --- a/Models/Items/Item.cs +++ b/Models/Items/Item.cs @@ -7,8 +7,8 @@ public abstract class Item public string Ean { get; protected set; } public int Quantity { get; protected set; } public float Price { get; protected set; } - public string ImageUrl { get; protected set; } - public DateTime ExpirationDate { get; protected set; } + public string ImageUrl { get; protected set; } + public DateTime? ExpirationDate { get; protected set; } } From 900688d9d2e9bca90981d07520e28057b2062a32 Mon Sep 17 00:00:00 2001 From: Thomas Andersen Date: Wed, 22 Nov 2023 12:51:39 +0100 Subject: [PATCH 023/112] Fixed wine constructor --- .../.idea/efCoreDialogsState.xml | 2 +- Models/Items/Wine.cs | 2 +- bin/Debug/net7.0/API | Bin 181016 -> 148208 bytes obj/Debug/net7.0/apphost | Bin 181016 -> 148208 bytes 4 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.idea/.idea.API.dir/.idea/efCoreDialogsState.xml b/.idea/.idea.API.dir/.idea/efCoreDialogsState.xml index f5622c3..204222e 100644 --- a/.idea/.idea.API.dir/.idea/efCoreDialogsState.xml +++ b/.idea/.idea.API.dir/.idea/efCoreDialogsState.xml @@ -3,7 +3,7 @@