diff --git a/JendStore.Cart.Service.API/Configurations/MapperInitilizer.cs b/JendStore.Cart.Service.API/Configurations/MapperInitilizer.cs new file mode 100644 index 0000000..b9657f2 --- /dev/null +++ b/JendStore.Cart.Service.API/Configurations/MapperInitilizer.cs @@ -0,0 +1,19 @@ +using AutoMapper; +using JendStore.Cart.Service.API.DTO; +using JendStore.Cart.Service.API.Models; + +namespace JendStore.Cart.Service.API.Configurations +{ + public class MapperInitilizer + { + public static MapperConfiguration RegisterMaps() + { + var config = new MapperConfiguration(cfg => { + cfg.CreateMap().ReverseMap(); + cfg.CreateMap().ReverseMap(); + }); + return config; + } + + } +} diff --git a/JendStore.Cart.Service.API/Controllers/CartController.cs b/JendStore.Cart.Service.API/Controllers/CartController.cs new file mode 100644 index 0000000..095059b --- /dev/null +++ b/JendStore.Cart.Service.API/Controllers/CartController.cs @@ -0,0 +1,66 @@ +using AutoMapper; +using JendStore.Cart.Service.API.DTO; +using JendStore.Cart.Service.API.Models; +using JendStore.Cart.Service.API.Repository.Interface; +using Microsoft.AspNetCore.Mvc; + +namespace JendStore.Cart.Service.API.Controllers +{ + [Route("api/cart")] + [ApiController] + public class CartController : ControllerBase + { + private readonly IUnitOfWork _unitOfWork; + private readonly IMapper _mapper; + private readonly ILogger _logger; + protected ResponseDTOStatus _response; + + public CartController(IUnitOfWork unitOfWork, IMapper mapper1, ILogger logger) + { + _unitOfWork = unitOfWork; + _mapper = mapper1; + _logger = logger; + _response = new(); + } + + [HttpPost("CartUpsert")] + public async Task CartUspsert(CartDto cartDto) + { + var cartHeder = await _unitOfWork.CartHeaders.Get(u => u.UserId == cartDto.CartHeader.UserId); + if (cartHeder == null) + { + //Create header and detail + CartHeader cartHeader = _mapper.Map(cartDto.CartHeader); + await _unitOfWork.CartHeaders.Insert(cartHeader); + await _unitOfWork.Save(); + cartDto.CartDetails.First().CartHeaderId = cartHeader.CartHeaderId; + await _unitOfWork.CartDetails.Insert(_mapper.Map(cartDto.CartDetails.First())); + await _unitOfWork.Save(); + } + else + { + //if header is not null + //check if detail has same product + var cartDetail = await _unitOfWork.CartDetails.Get(u => u.ProductId == cartDto.CartDetails.First().ProductId && u.CartHeaderId == cartHeder.CartHeaderId); + if (cartDetail == null) + { + //create cartDeatil + cartDto.CartDetails.First().CartHeaderId = cartHeder.CartHeaderId; + await _unitOfWork.CartDetails.Insert(_mapper.Map(cartDto.CartDetails.First())); + await _unitOfWork.Save(); + } + else + { + //update quantity in cart detail + cartDto.CartDetails.First().Quantity += cartDetail.Quantity; + cartDto.CartDetails.First().CartHeaderId = cartDetail.CartHeaderId; + cartDto.CartDetails.First().CartDetailId = cartDetail.CartDetailId; + _unitOfWork.CartDetails.Update(_mapper.Map(cartDto.CartDetails.First())); + await _unitOfWork.Save(); + } + } + _response.Result = cartDto; + return Ok(_response); + } + } +} \ No newline at end of file diff --git a/JendStore.Cart.Service.API/DTO/CartDetailDto.cs b/JendStore.Cart.Service.API/DTO/CartDetailDto.cs new file mode 100644 index 0000000..ce57d5a --- /dev/null +++ b/JendStore.Cart.Service.API/DTO/CartDetailDto.cs @@ -0,0 +1,13 @@ + +namespace JendStore.Cart.Service.API.DTO +{ + public class CartDetailDto + { + public int CartDetailId { get; set; } + public int CartHeaderId { get; set; } + public CartHeaderDto? CartHeader { get; set; } + public int ProductId { get; set; } + public ProductDto? Product { get; set; } + public int Quantity { get; set; } + } +} \ No newline at end of file diff --git a/JendStore.Cart.Service.API/DTO/CartDto.cs b/JendStore.Cart.Service.API/DTO/CartDto.cs new file mode 100644 index 0000000..068ac66 --- /dev/null +++ b/JendStore.Cart.Service.API/DTO/CartDto.cs @@ -0,0 +1,8 @@ +namespace JendStore.Cart.Service.API.DTO +{ + public class CartDto + { + public CartHeaderDto CartHeader { get; set; } + public IEnumerable? CartDetails { get; set; } + } +} diff --git a/JendStore.Cart.Service.API/DTO/CartHeaderDto.cs b/JendStore.Cart.Service.API/DTO/CartHeaderDto.cs new file mode 100644 index 0000000..3cca1b9 --- /dev/null +++ b/JendStore.Cart.Service.API/DTO/CartHeaderDto.cs @@ -0,0 +1,11 @@ +namespace JendStore.Cart.Service.API.DTO +{ + public class CartHeaderDto + { + public int CartHeaderId { get; set; } + public string? UserId { get; set; } + public string? Code { get; set; } + public double Discount { get; set; } + public double Total { get; set; } + } +} diff --git a/JendStore.Cart.Service.API/DTO/CouponDTO.cs b/JendStore.Cart.Service.API/DTO/CouponDTO.cs new file mode 100644 index 0000000..6d25e47 --- /dev/null +++ b/JendStore.Cart.Service.API/DTO/CouponDTO.cs @@ -0,0 +1,22 @@ +using System.ComponentModel.DataAnnotations; + +namespace JendStore.Cart.Service.API.DTO +{ + + public class CreateCouponDTO + { + public string Code { get; set; } + public double Discount { get; set; } + public int MinAmount { get; set; } + } + + public class UpdateCouponDTO: CreateCouponDTO + { + + } + + public class CouponDTO : CreateCouponDTO + { + public int CouponId { get; set; } + } +} diff --git a/JendStore.Cart.Service.API/DTO/ProductDto.cs b/JendStore.Cart.Service.API/DTO/ProductDto.cs new file mode 100644 index 0000000..3440c0b --- /dev/null +++ b/JendStore.Cart.Service.API/DTO/ProductDto.cs @@ -0,0 +1,19 @@ +using System.ComponentModel.DataAnnotations; + +namespace JendStore.Cart.Service.API.DTO +{ + public class ProductDto + { + public int ProductId { get; set; } + + public string Name { get; set; } + + public double Price { get; set; } + + public string Description { get; set; } + + public string CategoryName { get; set; } + + public string ImageUrl { get; set; } + } +} diff --git a/JendStore.Cart.Service.API/DTO/ResponseDTOStatus.cs b/JendStore.Cart.Service.API/DTO/ResponseDTOStatus.cs new file mode 100644 index 0000000..35d024d --- /dev/null +++ b/JendStore.Cart.Service.API/DTO/ResponseDTOStatus.cs @@ -0,0 +1,12 @@ +namespace JendStore.Cart.Service.API.DTO +{ + public class ResponseDTOStatus + { + public object? Result { get; set; } + public bool IsSuccess { get; set; } = true; + public string Message { get; set; } = string.Empty; + public int StatusCode { get; set; } + + } +} + diff --git a/JendStore.Cart.Service.API/Data/DatabaseContext.cs b/JendStore.Cart.Service.API/Data/DatabaseContext.cs new file mode 100644 index 0000000..47ced5a --- /dev/null +++ b/JendStore.Cart.Service.API/Data/DatabaseContext.cs @@ -0,0 +1,23 @@ +using JendStore.Cart.Service.API.Models; +using Microsoft.EntityFrameworkCore; + +namespace JendStore.Cart.Service.API.Data +{ + public class DatabaseContext: DbContext + { + + public DatabaseContext(DbContextOptions options) : base(options) + { + + } + + public DbSet CartDetails { get; set; } + public DbSet CartHeaders { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + } + } + +} diff --git a/JendStore.Cart.Service.API/GlobalErrorResponseHandler/ResponseStatus.cs b/JendStore.Cart.Service.API/GlobalErrorResponseHandler/ResponseStatus.cs new file mode 100644 index 0000000..59df5ea --- /dev/null +++ b/JendStore.Cart.Service.API/GlobalErrorResponseHandler/ResponseStatus.cs @@ -0,0 +1,13 @@ +using Newtonsoft.Json; + +namespace JendStore.Cart.Service.API.ResponseHandler +{ + public class ResponseStatus + { + public string? Message { get; set; } = string.Empty; + public int Status { get; set; } + public int StatusCode { get; set; } + + public override string ToString() => JsonConvert.SerializeObject(this); + } +} diff --git a/JendStore.Cart.Service.API/JendStore.Cart.Service.API.csproj b/JendStore.Cart.Service.API/JendStore.Cart.Service.API.csproj new file mode 100644 index 0000000..26e0c75 --- /dev/null +++ b/JendStore.Cart.Service.API/JendStore.Cart.Service.API.csproj @@ -0,0 +1,26 @@ + + + + net7.0 + enable + enable + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + diff --git a/JendStore.Cart.Service.API/Migrations/20240304072330_CartTable.Designer.cs b/JendStore.Cart.Service.API/Migrations/20240304072330_CartTable.Designer.cs new file mode 100644 index 0000000..12f17be --- /dev/null +++ b/JendStore.Cart.Service.API/Migrations/20240304072330_CartTable.Designer.cs @@ -0,0 +1,83 @@ +// +using JendStore.Cart.Service.API.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace JendStore.Cart.Service.API.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20240304072330_CartTable")] + partial class CartTable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.14") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("JendStore.Cart.Service.API.Models.CartDetail", b => + { + b.Property("CartDetailId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("CartDetailId")); + + b.Property("CartHeaderId") + .HasColumnType("int"); + + b.Property("ProductId") + .HasColumnType("int"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.HasKey("CartDetailId"); + + b.HasIndex("CartHeaderId"); + + b.ToTable("CartDetails"); + }); + + modelBuilder.Entity("JendStore.Cart.Service.API.Models.CartHeader", b => + { + b.Property("CartHeaderId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("CartHeaderId")); + + b.Property("Code") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("nvarchar(max)"); + + b.HasKey("CartHeaderId"); + + b.ToTable("CartHeaders"); + }); + + modelBuilder.Entity("JendStore.Cart.Service.API.Models.CartDetail", b => + { + b.HasOne("JendStore.Cart.Service.API.Models.CartHeader", "CartHeader") + .WithMany() + .HasForeignKey("CartHeaderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CartHeader"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/JendStore.Cart.Service.API/Migrations/20240304072330_CartTable.cs b/JendStore.Cart.Service.API/Migrations/20240304072330_CartTable.cs new file mode 100644 index 0000000..ffd525a --- /dev/null +++ b/JendStore.Cart.Service.API/Migrations/20240304072330_CartTable.cs @@ -0,0 +1,64 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace JendStore.Cart.Service.API.Migrations +{ + /// + public partial class CartTable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "CartHeaders", + columns: table => new + { + CartHeaderId = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + UserId = table.Column(type: "nvarchar(max)", nullable: true), + Code = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_CartHeaders", x => x.CartHeaderId); + }); + + migrationBuilder.CreateTable( + name: "CartDetails", + columns: table => new + { + CartDetailId = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + CartHeaderId = table.Column(type: "int", nullable: false), + ProductId = table.Column(type: "int", nullable: false), + Quantity = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_CartDetails", x => x.CartDetailId); + table.ForeignKey( + name: "FK_CartDetails_CartHeaders_CartHeaderId", + column: x => x.CartHeaderId, + principalTable: "CartHeaders", + principalColumn: "CartHeaderId", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_CartDetails_CartHeaderId", + table: "CartDetails", + column: "CartHeaderId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "CartDetails"); + + migrationBuilder.DropTable( + name: "CartHeaders"); + } + } +} diff --git a/JendStore.Cart.Service.API/Migrations/DatabaseContextModelSnapshot.cs b/JendStore.Cart.Service.API/Migrations/DatabaseContextModelSnapshot.cs new file mode 100644 index 0000000..773dd46 --- /dev/null +++ b/JendStore.Cart.Service.API/Migrations/DatabaseContextModelSnapshot.cs @@ -0,0 +1,80 @@ +// +using JendStore.Cart.Service.API.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace JendStore.Cart.Service.API.Migrations +{ + [DbContext(typeof(DatabaseContext))] + partial class DatabaseContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.14") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("JendStore.Cart.Service.API.Models.CartDetail", b => + { + b.Property("CartDetailId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("CartDetailId")); + + b.Property("CartHeaderId") + .HasColumnType("int"); + + b.Property("ProductId") + .HasColumnType("int"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.HasKey("CartDetailId"); + + b.HasIndex("CartHeaderId"); + + b.ToTable("CartDetails"); + }); + + modelBuilder.Entity("JendStore.Cart.Service.API.Models.CartHeader", b => + { + b.Property("CartHeaderId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("CartHeaderId")); + + b.Property("Code") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("nvarchar(max)"); + + b.HasKey("CartHeaderId"); + + b.ToTable("CartHeaders"); + }); + + modelBuilder.Entity("JendStore.Cart.Service.API.Models.CartDetail", b => + { + b.HasOne("JendStore.Cart.Service.API.Models.CartHeader", "CartHeader") + .WithMany() + .HasForeignKey("CartHeaderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CartHeader"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/JendStore.Cart.Service.API/Models/CartDetail.cs b/JendStore.Cart.Service.API/Models/CartDetail.cs new file mode 100644 index 0000000..f5c5b76 --- /dev/null +++ b/JendStore.Cart.Service.API/Models/CartDetail.cs @@ -0,0 +1,21 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using JendStore.Cart.Service.API.DTO; + +namespace JendStore.Cart.Service.API.Models +{ + public class CartDetail + { + [Key] + public int CartDetailId { get; set; } + public int CartHeaderId { get; set; } + + [ForeignKey("CartHeaderId")] + public CartHeader CartHeader { get; set; } + + public int ProductId { get; set; } + [NotMapped] + public ProductDto Product { get; set; } + public int Quantity { get; set; } + } +} diff --git a/JendStore.Cart.Service.API/Models/CartHeader.cs b/JendStore.Cart.Service.API/Models/CartHeader.cs new file mode 100644 index 0000000..7f3e97a --- /dev/null +++ b/JendStore.Cart.Service.API/Models/CartHeader.cs @@ -0,0 +1,19 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace JendStore.Cart.Service.API.Models +{ + public class CartHeader + { + [Key] + public int CartHeaderId { get; set; } + public string? UserId { get; set; } + public string? Code { get; set; } + + [NotMapped] + public double Discount { get; set; } + + [NotMapped] + public double Total { get; set; } + } +} diff --git a/JendStore.Cart.Service.API/Program.cs b/JendStore.Cart.Service.API/Program.cs new file mode 100644 index 0000000..815325f --- /dev/null +++ b/JendStore.Cart.Service.API/Program.cs @@ -0,0 +1,67 @@ +using AutoMapper; +using JendStore.Cart.Service.API.Configurations; +using JendStore.Cart.Service.API.Data; +using JendStore.Cart.Service.API.Repository; +using JendStore.Cart.Service.API.Repository.Interface; +using JendStore.Cart.Service.API.ServiceExtensions; +using Microsoft.EntityFrameworkCore; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. +builder.Services.AddDbContext(options => +{ + options.UseSqlServer(builder.Configuration.GetConnectionString("sqlConnection")); +}); +builder.Services.AddControllers().AddNewtonsoftJson(options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore); +builder.Services.AddTransient(); + + +IMapper mapper = MapperInitilizer.RegisterMaps().CreateMapper(); +builder.Services.AddSingleton(mapper); + + +// Learn more about configuring Swagger/OpenAPI +builder.Services.AddEndpointsApiExplorer(); +var Config = builder.Configuration; + + +builder.Services.AddAuthentication(); +builder.Services.AddAuthorization(); +builder.Services.ConfigureJWT(Config); +builder.Services.ConfigSwagger(Config); + + +var app = builder.Build(); + +//Automatic Migration (checks for any pending migration, and if there's any it automatically apply migration to the database) +void ApplyAutoMigration() +{ + using (var serviceScope = app.Services.CreateScope()) + { + var services = serviceScope.ServiceProvider.GetService(); + + if (services.Database.GetPendingMigrations().Count() > 0) + { + services.Database.Migrate(); + } + } +} + +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "CartAPI v1")); +} + +ApplyAutoMigration(); + +app.UseHttpsRedirection(); + +app.UseAuthentication(); + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); diff --git a/JendStore.Cart.Service.API/Properties/launchSettings.json b/JendStore.Cart.Service.API/Properties/launchSettings.json new file mode 100644 index 0000000..c3725de --- /dev/null +++ b/JendStore.Cart.Service.API/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:24920", + "sslPort": 44385 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5155", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7288;http://localhost:5155", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/JendStore.Cart.Service.API/Repository/GenericRepostory.cs b/JendStore.Cart.Service.API/Repository/GenericRepostory.cs new file mode 100644 index 0000000..78473b0 --- /dev/null +++ b/JendStore.Cart.Service.API/Repository/GenericRepostory.cs @@ -0,0 +1,77 @@ +using JendStore.Cart.Service.API.Data; +using JendStore.Cart.Service.API.Repository.Interface; +using Microsoft.EntityFrameworkCore; +using System.Linq.Expressions; + +namespace JendStore.Cart.Service.API.Repository +{ + public class GenericRopository : IGenericRepository where T : class + { + private readonly DatabaseContext _context; + private readonly DbSet _dbSet; + + public GenericRopository(DatabaseContext context) + { + _context = context; + _dbSet = context.Set(); + } + + public async Task Delete(int id) + { + var entity = await _dbSet.FindAsync(id); + + _dbSet.Remove(entity); + } + + public async void DeleteRamge(IEnumerable entity) + { + _dbSet.RemoveRange(entity); + } + + public async Task Get(Expression> expression) + { + IQueryable query = _dbSet; + + return await query.AsNoTracking().FirstOrDefaultAsync(expression); + } + + public async Task> GetAll(Expression> expression = null, Func, IOrderedQueryable> orderBy = null, List includes = null) + { + IQueryable query = _dbSet; + + if (expression != null) + { + query = query.Where(expression); + } + if (includes != null) + { + foreach (var includeProperty in includes) + { + query = query.Include(includeProperty); + } + } + if (orderBy != null) + { + query = orderBy(query); + } + return await query.AsNoTracking().ToListAsync(); + } + + public async Task Insert(T entity) + { + + await _dbSet.AddAsync(entity); + } + + public async Task InsertRamge(IEnumerable entity) + { + await _dbSet.AddRangeAsync(entity); + } + + public void Update(T entity) + { + _dbSet.Attach(entity); + _context.Entry(entity).State = EntityState.Modified; + } + } +} diff --git a/JendStore.Cart.Service.API/Repository/Interface/IGenericRepository.cs b/JendStore.Cart.Service.API/Repository/Interface/IGenericRepository.cs new file mode 100644 index 0000000..7916d52 --- /dev/null +++ b/JendStore.Cart.Service.API/Repository/Interface/IGenericRepository.cs @@ -0,0 +1,19 @@ +using System.Linq.Expressions; + +namespace JendStore.Cart.Service.API.Repository.Interface +{ + public interface IGenericRepository where T : class + { + Task> GetAll( + Expression> expression = null, + Func, IOrderedQueryable> orderBy = null, + List includes = null); + + Task Get(Expression> expression); + Task Insert(T entity); + Task InsertRamge(IEnumerable entity); + Task Delete(int id); + void DeleteRamge(IEnumerable entity); + void Update(T entity); + } +} diff --git a/JendStore.Cart.Service.API/Repository/Interface/IUnitOfWork.cs b/JendStore.Cart.Service.API/Repository/Interface/IUnitOfWork.cs new file mode 100644 index 0000000..d4c3764 --- /dev/null +++ b/JendStore.Cart.Service.API/Repository/Interface/IUnitOfWork.cs @@ -0,0 +1,13 @@ + +using JendStore.Cart.Service.API.Models; + +namespace JendStore.Cart.Service.API.Repository.Interface +{ + public interface IUnitOfWork : IDisposable + { + IGenericRepository CartDetails { get; } + IGenericRepository CartHeaders { get; } + + Task Save(); + } +} diff --git a/JendStore.Cart.Service.API/Repository/UnitOfWork.cs b/JendStore.Cart.Service.API/Repository/UnitOfWork.cs new file mode 100644 index 0000000..0245d71 --- /dev/null +++ b/JendStore.Cart.Service.API/Repository/UnitOfWork.cs @@ -0,0 +1,32 @@ +using JendStore.Cart.Service.API.Data; +using JendStore.Cart.Service.API.Models; +using JendStore.Cart.Service.API.Repository.Interface; + +namespace JendStore.Cart.Service.API.Repository +{ + public class UnitOfWork : IUnitOfWork + { + private readonly DatabaseContext _context; + private IGenericRepository _cartDetail; + private IGenericRepository _cartHeader; + + public UnitOfWork(DatabaseContext context) + { + _context = context; + } + + public IGenericRepository CartDetails => _cartDetail ??= new GenericRopository(_context); + public IGenericRepository CartHeaders => _cartHeader ??= new GenericRopository(_context); + + void IDisposable.Dispose() + { + _context.Dispose(); + GC.SuppressFinalize(this); + } + + public async Task Save() + { + await _context.SaveChangesAsync(); + } + } +} diff --git a/JendStore.Cart.Service.API/ServiceExtensions/ServiceExtensions.cs b/JendStore.Cart.Service.API/ServiceExtensions/ServiceExtensions.cs new file mode 100644 index 0000000..ee4c1ba --- /dev/null +++ b/JendStore.Cart.Service.API/ServiceExtensions/ServiceExtensions.cs @@ -0,0 +1,100 @@ +using JendStore.Cart.Service.API.ResponseHandler; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Diagnostics; +using Microsoft.IdentityModel.Tokens; +using Microsoft.OpenApi.Models; +using Serilog; +using System.Text; + +namespace JendStore.Cart.Service.API.ServiceExtensions +{ + public static class ServiceExtensions + { + //Configuration method for jwtSecret + public static void ConfigureJWT(this IServiceCollection services, IConfiguration configuration) + { + var jwtSettings = configuration.GetSection("JwtOptions"); + var key = Environment.GetEnvironmentVariable("KEY"); + + services.AddAuthentication(options => + { + options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + }) + .AddJwtBearer(options => + { + options.TokenValidationParameters = new TokenValidationParameters + { + ValidateIssuer = true, + ValidateLifetime = true, + ValidateIssuerSigningKey = true, + ValidateAudience = true, + ValidAudience = jwtSettings.GetSection("Audience").Value, + ValidIssuer = jwtSettings.GetSection("Issuer").Value, + IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key)), + }; + }); + } + + public static void ConfigSwagger(this IServiceCollection services, IConfiguration configuration) + { + + services.AddSwaggerGen(c => + { + c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme + { + Description = @"JWT Authorization header using Bearer scheme. + Enter 'Bearer' [space] and then your token in the input below. + Example: 'Bearer Oboy it worked o'", + Name = "Authorization", + In = ParameterLocation.Header, + Type = SecuritySchemeType.ApiKey, + Scheme = "Bearer" + }); + + c.AddSecurityRequirement(new OpenApiSecurityRequirement() + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "Bearer" + }, + Scheme = "0auth2", + Name = "Bearer", + In = ParameterLocation.Header + }, + new List() + } + } + ); + c.SwaggerDoc("v1", new OpenApiInfo { Title = "CartAPI", Version = "v1" }); + }); + } + + public static void ExceptionHandlerConfiguration(this IApplicationBuilder app) + { + app.UseExceptionHandler(appError => + { + appError.Run(async context => + { + context.Response.StatusCode = StatusCodes.Status500InternalServerError; + context.Response.ContentType = "application/json"; + + var contextFeature = context.Features.Get(); + if (contextFeature != null) + { + Log.Error($"Something Went Wrong In {contextFeature.Error}"); + await context.Response.WriteAsync(new ResponseStatus() + { + StatusCode = context.Response.StatusCode, + Message = "Internal Server Error..." + }.ToString()); + } + }); + }); + } + } +} diff --git a/JendStore.Cart.Service.API/appsettings.Development.json b/JendStore.Cart.Service.API/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/JendStore.Cart.Service.API/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/JendStore.Cart.Service.API/appsettings.json b/JendStore.Cart.Service.API/appsettings.json new file mode 100644 index 0000000..7a056d7 --- /dev/null +++ b/JendStore.Cart.Service.API/appsettings.json @@ -0,0 +1,17 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "ConnectionStrings": { + "sqlConnection": "server=(localdb)\\mssqllocaldb; database=JendStoreCartDB; integrated security=true" + }, + "JwtOptions": { + "Audience": "JendStore", + "Issuer": "SecurityAPI", + "ExpiredTime": 15 + }, + "AllowedHosts": "*" +} \ No newline at end of file diff --git a/JendStore.Client/Models/ProductDto.cs b/JendStore.Client/Models/ProductDto.cs index f500c49..685ebe9 100644 --- a/JendStore.Client/Models/ProductDto.cs +++ b/JendStore.Client/Models/ProductDto.cs @@ -17,6 +17,6 @@ public class ProductDto public string ImageUrl { get; set; } [Range(1,50)] - public int Count { get; set; } + public int Quantity { get; set; } } } \ No newline at end of file diff --git a/JendStore.Client/Views/Home/DetailProduct.cshtml b/JendStore.Client/Views/Home/DetailProduct.cshtml index f8fabf4..636d0c2 100644 --- a/JendStore.Client/Views/Home/DetailProduct.cshtml +++ b/JendStore.Client/Views/Home/DetailProduct.cshtml @@ -14,8 +14,8 @@
$96.00@string.Format("{0:c}", Model.Price)
diff --git a/JendStore.Client/Views/Product/DetailProduct.cshtml b/JendStore.Client/Views/Product/DetailProduct.cshtml new file mode 100644 index 0000000..fc2064b --- /dev/null +++ b/JendStore.Client/Views/Product/DetailProduct.cshtml @@ -0,0 +1,18 @@ +@model ProductDto + +
+
+ +
+
+
@Model.Name
+

@string.Format("{0:c}", Model.Price)

+ view details
+ ADD TO CART +
+
+
+ +
\ No newline at end of file diff --git a/JendStore.Products.Service.API/Migrations/20240212152058_ProductTable.Designer.cs b/JendStore.Products.Service.API/Migrations/20240224170915_AddPrdocutTable.Designer.cs similarity index 95% rename from JendStore.Products.Service.API/Migrations/20240212152058_ProductTable.Designer.cs rename to JendStore.Products.Service.API/Migrations/20240224170915_AddPrdocutTable.Designer.cs index 0db68f2..7ffcf38 100644 --- a/JendStore.Products.Service.API/Migrations/20240212152058_ProductTable.Designer.cs +++ b/JendStore.Products.Service.API/Migrations/20240224170915_AddPrdocutTable.Designer.cs @@ -11,8 +11,8 @@ namespace JendStore.Products.Service.API.Migrations { [DbContext(typeof(DatabaseContext))] - [Migration("20240212152058_ProductTable")] - partial class ProductTable + [Migration("20240224170915_AddPrdocutTable")] + partial class AddPrdocutTable { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) diff --git a/JendStore.Products.Service.API/Migrations/20240212152058_ProductTable.cs b/JendStore.Products.Service.API/Migrations/20240224170915_AddPrdocutTable.cs similarity index 96% rename from JendStore.Products.Service.API/Migrations/20240212152058_ProductTable.cs rename to JendStore.Products.Service.API/Migrations/20240224170915_AddPrdocutTable.cs index 29bbb7f..4e353c3 100644 --- a/JendStore.Products.Service.API/Migrations/20240212152058_ProductTable.cs +++ b/JendStore.Products.Service.API/Migrations/20240224170915_AddPrdocutTable.cs @@ -5,7 +5,7 @@ namespace JendStore.Products.Service.API.Migrations { /// - public partial class ProductTable : Migration + public partial class AddPrdocutTable : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) diff --git a/JendStore.sln b/JendStore.sln index 74fd35f..fa0739a 100644 --- a/JendStore.sln +++ b/JendStore.sln @@ -19,6 +19,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JendStore.Security.Service. EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JendStore.Products.Service.API", "JendStore.Products.Service.API\JendStore.Products.Service.API.csproj", "{4EC5911F-ACF0-4220-BAD7-FF217EAE87A5}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JendStore.Cart.Service.API", "JendStore.Cart.Service.API\JendStore.Cart.Service.API.csproj", "{3AEE5393-8822-4AE5-9F4D-9C1CF796D160}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -41,6 +43,10 @@ Global {4EC5911F-ACF0-4220-BAD7-FF217EAE87A5}.Debug|Any CPU.Build.0 = Debug|Any CPU {4EC5911F-ACF0-4220-BAD7-FF217EAE87A5}.Release|Any CPU.ActiveCfg = Release|Any CPU {4EC5911F-ACF0-4220-BAD7-FF217EAE87A5}.Release|Any CPU.Build.0 = Release|Any CPU + {3AEE5393-8822-4AE5-9F4D-9C1CF796D160}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3AEE5393-8822-4AE5-9F4D-9C1CF796D160}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3AEE5393-8822-4AE5-9F4D-9C1CF796D160}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3AEE5393-8822-4AE5-9F4D-9C1CF796D160}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -50,6 +56,7 @@ Global {0BE25347-3A6A-4989-9D85-0781979A2716} = {06178DF0-38BB-49E0-A583-216B05B53B11} {B73735C0-D7B6-4515-BA48-874BC3A1CCE2} = {D93BF416-230B-431D-84FB-540EE10EE45C} {4EC5911F-ACF0-4220-BAD7-FF217EAE87A5} = {D93BF416-230B-431D-84FB-540EE10EE45C} + {3AEE5393-8822-4AE5-9F4D-9C1CF796D160} = {D93BF416-230B-431D-84FB-540EE10EE45C} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {4602B2CB-9082-4664-8447-C83C1B729F75}