diff --git a/backend/.vs/VSWorkspaceState.json b/backend/.vs/VSWorkspaceState.json new file mode 100644 index 00000000..51c17612 --- /dev/null +++ b/backend/.vs/VSWorkspaceState.json @@ -0,0 +1,9 @@ +{ + "ExpandedNodes": [ + "", + "\\BackendAssessment", + "\\BackendAssessment\\Infrastructure" + ], + "SelectedNode": "\\BackendAssessment\\Infrastructure\\Infrastructure.csproj", + "PreviewInSolutionExplorer": false +} \ No newline at end of file diff --git a/backend/.vs/backend/FileContentIndex/504a1ad5-97ec-4b59-b934-3f782a787b96.vsidx b/backend/.vs/backend/FileContentIndex/504a1ad5-97ec-4b59-b934-3f782a787b96.vsidx new file mode 100644 index 00000000..54e0e050 Binary files /dev/null and b/backend/.vs/backend/FileContentIndex/504a1ad5-97ec-4b59-b934-3f782a787b96.vsidx differ diff --git a/backend/.vs/backend/FileContentIndex/a5562604-b092-4b27-ba22-b36b38cf0958.vsidx b/backend/.vs/backend/FileContentIndex/a5562604-b092-4b27-ba22-b36b38cf0958.vsidx new file mode 100644 index 00000000..96eaf11f Binary files /dev/null and b/backend/.vs/backend/FileContentIndex/a5562604-b092-4b27-ba22-b36b38cf0958.vsidx differ diff --git a/backend/.vs/backend/FileContentIndex/read.lock b/backend/.vs/backend/FileContentIndex/read.lock new file mode 100644 index 00000000..e69de29b diff --git a/backend/.vs/backend/v17/.wsuo b/backend/.vs/backend/v17/.wsuo new file mode 100644 index 00000000..4cef5b2b Binary files /dev/null and b/backend/.vs/backend/v17/.wsuo differ diff --git a/backend/.vs/slnx.sqlite b/backend/.vs/slnx.sqlite new file mode 100644 index 00000000..6c45b45e Binary files /dev/null and b/backend/.vs/slnx.sqlite differ diff --git a/backend/BackendAssessment/Application.Tests/Application.Tests.csproj b/backend/BackendAssessment/Application.Tests/Application.Tests.csproj index 37de39f4..e2907571 100644 --- a/backend/BackendAssessment/Application.Tests/Application.Tests.csproj +++ b/backend/BackendAssessment/Application.Tests/Application.Tests.csproj @@ -24,4 +24,8 @@ + + + + diff --git a/backend/BackendAssessment/Application/Application.csproj b/backend/BackendAssessment/Application/Application.csproj index 9c452bbc..e22460aa 100644 --- a/backend/BackendAssessment/Application/Application.csproj +++ b/backend/BackendAssessment/Application/Application.csproj @@ -1,4 +1,4 @@ - + net7.0 @@ -7,10 +7,14 @@ - - - - - + + + + + + + + + diff --git a/backend/BackendAssessment/Application/Contracts/ICategoryRepository.cs b/backend/BackendAssessment/Application/Contracts/ICategoryRepository.cs new file mode 100644 index 00000000..c3e4db62 --- /dev/null +++ b/backend/BackendAssessment/Application/Contracts/ICategoryRepository.cs @@ -0,0 +1,14 @@ +using Domain.Entites; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Persistence.Repositories +{ + public interface ICategoryRepository : IGenericRepository + { + + } +} diff --git a/backend/BackendAssessment/Application/Contracts/IGenericRepository.cs b/backend/BackendAssessment/Application/Contracts/IGenericRepository.cs new file mode 100644 index 00000000..c2c3f0f9 --- /dev/null +++ b/backend/BackendAssessment/Application/Contracts/IGenericRepository.cs @@ -0,0 +1,20 @@ +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Persistence.Repositories +{ + public interface IGenericRepository where T : class + { + Task Get(Guid id); + Task> GetAll(); + Task Add(T entity); + Task Update(T entity); + bool Exists(Guid Id); + void Delete(T entity); + + } +} diff --git a/backend/BackendAssessment/Application/Contracts/IProductRepository.cs b/backend/BackendAssessment/Application/Contracts/IProductRepository.cs new file mode 100644 index 00000000..6a5a153f --- /dev/null +++ b/backend/BackendAssessment/Application/Contracts/IProductRepository.cs @@ -0,0 +1,15 @@ +using Domain.Entites; +using Persistence.Repositories; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Application.Contracts +{ + public interface IProductRepository : IGenericRepository + { + + } +} diff --git a/backend/BackendAssessment/Application/Contracts/IUserRepository.cs b/backend/BackendAssessment/Application/Contracts/IUserRepository.cs new file mode 100644 index 00000000..5839ed19 --- /dev/null +++ b/backend/BackendAssessment/Application/Contracts/IUserRepository.cs @@ -0,0 +1,14 @@ +using Domain.Entites; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Persistence.Repositories +{ + public interface IUserRepository : IGenericRepository + { + + } +} diff --git a/backend/BackendAssessment/Application/DTOs/Category/CategoryRetriveDto.cs b/backend/BackendAssessment/Application/DTOs/Category/CategoryRetriveDto.cs new file mode 100644 index 00000000..433c68b4 --- /dev/null +++ b/backend/BackendAssessment/Application/DTOs/Category/CategoryRetriveDto.cs @@ -0,0 +1,14 @@ +using Application.DTOs.Common; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Application.DTOs.Category +{ + public class CategoryRetriveDto : BaseEntityDto + { + + } +} diff --git a/backend/BackendAssessment/Application/DTOs/Category/CreateCategoryDto.cs b/backend/BackendAssessment/Application/DTOs/Category/CreateCategoryDto.cs new file mode 100644 index 00000000..b076109f --- /dev/null +++ b/backend/BackendAssessment/Application/DTOs/Category/CreateCategoryDto.cs @@ -0,0 +1,14 @@ +using Application.DTOs.Common; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Application.DTOs.Category +{ + public class CreateCategoryDto : BaseEntityDto + { + + } +} diff --git a/backend/BackendAssessment/Application/DTOs/Common/BaseEntityDto.cs b/backend/BackendAssessment/Application/DTOs/Common/BaseEntityDto.cs new file mode 100644 index 00000000..f85c00a4 --- /dev/null +++ b/backend/BackendAssessment/Application/DTOs/Common/BaseEntityDto.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Application.DTOs.Common +{ + public class BaseEntityDto + { + public int Id { get; set; } + public string name { get; set; } + public string description { get; set; } + + } +} diff --git a/backend/BackendAssessment/Application/DTOs/Product/CreateProductDTO.cs b/backend/BackendAssessment/Application/DTOs/Product/CreateProductDTO.cs new file mode 100644 index 00000000..d627a251 --- /dev/null +++ b/backend/BackendAssessment/Application/DTOs/Product/CreateProductDTO.cs @@ -0,0 +1,15 @@ +using Application.DTOs.Common; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Application.DTOs.Product +{ + public class CreateProductDTO : BaseEntityDto + { + public bool isAvailable { get; set; } + public double pricing { get; set; } + } +} diff --git a/backend/BackendAssessment/Application/DTOs/Product/UpdateProductDto.cs b/backend/BackendAssessment/Application/DTOs/Product/UpdateProductDto.cs new file mode 100644 index 00000000..0fe003df --- /dev/null +++ b/backend/BackendAssessment/Application/DTOs/Product/UpdateProductDto.cs @@ -0,0 +1,15 @@ +using Application.DTOs.Common; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Application.DTOs.Product +{ + public class UpdateProductDto : BaseEntityDto + { + public bool isAvailable { get; set; } + public double pricing { get; set; } + } +} diff --git a/backend/BackendAssessment/Application/DTOs/Product/Validator/CreateProductDtoValidator.cs b/backend/BackendAssessment/Application/DTOs/Product/Validator/CreateProductDtoValidator.cs new file mode 100644 index 00000000..08bbcda9 --- /dev/null +++ b/backend/BackendAssessment/Application/DTOs/Product/Validator/CreateProductDtoValidator.cs @@ -0,0 +1,28 @@ +using FluentValidation; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Application.DTOs.Product.Validator +{ + public class CreateProductDtoValidator : AbstractValidator + { + public CreateProductDtoValidator() + { + RuleFor(dto => dto.name) + .NotEmpty() + .WithMessage("name is required."); + + RuleFor(dto => dto.description) + .NotEmpty() + .WithMessage("Reciever description is required."); + + RuleFor(dto => dto.pricing) + .NotEmpty() + .WithMessage("price is required."); + } + + } +} diff --git a/backend/BackendAssessment/Application/DTOs/Product/productRetrieveDTO.cs b/backend/BackendAssessment/Application/DTOs/Product/productRetrieveDTO.cs new file mode 100644 index 00000000..34b26a9b --- /dev/null +++ b/backend/BackendAssessment/Application/DTOs/Product/productRetrieveDTO.cs @@ -0,0 +1,19 @@ +using Application.DTOs.Category; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Application.DTOs.Product +{ + public class productRetrieveDTO + { + public string name { get; set; } + public string description { get; set; } + public string availability { get; set; } + public string pricing { get; set; } + public CategoryRetriveDto category { get; set; } + + } +} diff --git a/backend/BackendAssessment/Application/DTOs/User/CreateUserDto.cs b/backend/BackendAssessment/Application/DTOs/User/CreateUserDto.cs new file mode 100644 index 00000000..9d7475c2 --- /dev/null +++ b/backend/BackendAssessment/Application/DTOs/User/CreateUserDto.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Application.DTOs.User +{ + public class CreateUserDto + { + public string username { get; set; } + public string password { get; set; } + public string email { get; set; } + public bool isAdmin { get; set; } + + } +} diff --git a/backend/BackendAssessment/Application/DTOs/User/Validator/CreateUserDtoValidator.cs b/backend/BackendAssessment/Application/DTOs/User/Validator/CreateUserDtoValidator.cs new file mode 100644 index 00000000..7210e90f --- /dev/null +++ b/backend/BackendAssessment/Application/DTOs/User/Validator/CreateUserDtoValidator.cs @@ -0,0 +1,28 @@ +using FluentValidation; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Application.DTOs.User.Validator +{ + public class CreateUserDtoValidator : AbstractValidator + { + public CreateUserDtoValidator() { + + RuleFor(dto => dto.username) + .NotEmpty() + .WithMessage("name is required."); + + RuleFor(dto => dto.email) + .NotEmpty() + .WithMessage("email is required."); + + RuleFor(dto => dto.password) + .NotEmpty() + .WithMessage("password is required."); + + } + } +} diff --git a/backend/BackendAssessment/Application/Features/Categories/Handler/Command/CreateCategoryCommandHandler.cs b/backend/BackendAssessment/Application/Features/Categories/Handler/Command/CreateCategoryCommandHandler.cs new file mode 100644 index 00000000..3bec065d --- /dev/null +++ b/backend/BackendAssessment/Application/Features/Categories/Handler/Command/CreateCategoryCommandHandler.cs @@ -0,0 +1,36 @@ +using Application.DTOs.Category; +using Application.Features.Categories.Request.Command; +using AutoMapper; +using MediatR; +using Persistence.Repositories; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Domain.Entites; + +namespace Application.Features.Categories.Handler.Command +{ + public class CreateCategoryCommandHandler : IRequestHandler + { + private readonly ICategoryRepository _categoryRepository; + private readonly IMapper _mapper; + + public CreateCategoryCommandHandler(ICategoryRepository i, IMapper mapper) + { + _categoryRepository = i; + _mapper = mapper; + } + + public async Task Handle(CreateCategoryCommand request, CancellationToken cancellationToken) + { + var category = _mapper.Map(request); + var result = await _categoryRepository.Add(category); + + var response = _mapper.Map(result); + return response; + } + } +} + diff --git a/backend/BackendAssessment/Application/Features/Categories/Handler/Command/UpdateCategoryCommandHandler.cs b/backend/BackendAssessment/Application/Features/Categories/Handler/Command/UpdateCategoryCommandHandler.cs new file mode 100644 index 00000000..0d1ecb85 --- /dev/null +++ b/backend/BackendAssessment/Application/Features/Categories/Handler/Command/UpdateCategoryCommandHandler.cs @@ -0,0 +1,42 @@ +using Application.DTOs.Category; +using Application.Features.Categories.Request.Command; +using AutoMapper; +using Domain.Entites; +using MediatR; +using Persistence.Repositories; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static System.Runtime.InteropServices.JavaScript.JSType; + +namespace Application.Features.Categories.Handler.Command +{ + public class UpdateCategoryCommandHandler : IRequestHandler + { + private readonly ICategoryRepository _categoryRepository; + private readonly IMapper _mapper; + + public UpdateCategoryCommandHandler(ICategoryRepository i, IMapper mapper) + { + _categoryRepository = i; + _mapper = mapper; + } + + public async Task Handle(UpdateCategoryCommand request, CancellationToken cancellationToken) + { + var category = await _categoryRepository.Get(request.id)!; + if (category == null) + { + throw new Exception(); + } + + var categoryToUpdate = _mapper.Map(request, category ); + var updatedCategory = await _categoryRepository.Update(categoryToUpdate); + return _mapper.Map(updatedCategory); + } + + + } +} diff --git a/backend/BackendAssessment/Application/Features/Categories/Handler/Queries/GetCategoryRequestHandler.cs b/backend/BackendAssessment/Application/Features/Categories/Handler/Queries/GetCategoryRequestHandler.cs new file mode 100644 index 00000000..a07a7193 --- /dev/null +++ b/backend/BackendAssessment/Application/Features/Categories/Handler/Queries/GetCategoryRequestHandler.cs @@ -0,0 +1,46 @@ +using Application.DTOs.Category; +using Application.Features.Categories.Request.Queries; +using AutoMapper; +using MediatR; +using Persistence.Repositories; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static System.Runtime.InteropServices.JavaScript.JSType; + +namespace Application.Features.Categories.Handler.Queries +{ + public class GetCategoryRequestHandler : IRequestHandler + { + private readonly ICategoryRepository _categoryRepository; + private readonly IMapper _mapper; + + public GetCategoryRequestHandler(ICategoryRepository commentRepository, IMapper mapper) + { + _categoryRepository = commentRepository; + _mapper = mapper; + } + + + + + public async Task Handle(GetCategoryRequest request, CancellationToken cancellationToken) + { + var res = await _categoryRepository.Get(request.id)!; + + if (res == null) + { + throw new Exception(); + } + + var categoryResponse = _mapper.Map(res); + return categoryResponse; + } + + + } + + +} diff --git a/backend/BackendAssessment/Application/Features/Categories/Request/Command/CreateCategoryCommand.cs b/backend/BackendAssessment/Application/Features/Categories/Request/Command/CreateCategoryCommand.cs new file mode 100644 index 00000000..51ab9695 --- /dev/null +++ b/backend/BackendAssessment/Application/Features/Categories/Request/Command/CreateCategoryCommand.cs @@ -0,0 +1,16 @@ +using Application.DTOs.Category; +using MediatR; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Application.Features.Categories.Request.Command +{ + public class CreateCategoryCommand : IRequest + { + public string name { get; set; } + public string description { get; set; } + } +} diff --git a/backend/BackendAssessment/Application/Features/Categories/Request/Command/UpdateCategoryCommand.cs b/backend/BackendAssessment/Application/Features/Categories/Request/Command/UpdateCategoryCommand.cs new file mode 100644 index 00000000..293047c3 --- /dev/null +++ b/backend/BackendAssessment/Application/Features/Categories/Request/Command/UpdateCategoryCommand.cs @@ -0,0 +1,17 @@ +using Application.DTOs.Category; +using MediatR; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Application.Features.Categories.Request.Command +{ + public class UpdateCategoryCommand : IRequest + { + public string name { get; set; } + public string description { get; set; } + public Guid id { get; set; } + } +} diff --git a/backend/BackendAssessment/Application/Features/Categories/Request/Queries/GetCategoryRequest.cs b/backend/BackendAssessment/Application/Features/Categories/Request/Queries/GetCategoryRequest.cs new file mode 100644 index 00000000..94cdad37 --- /dev/null +++ b/backend/BackendAssessment/Application/Features/Categories/Request/Queries/GetCategoryRequest.cs @@ -0,0 +1,15 @@ +using Application.DTOs.Category; +using MediatR; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Application.Features.Categories.Request.Queries +{ + public class GetCategoryRequest : IRequest + { + public Guid id { get; set; } + } +} diff --git a/backend/BackendAssessment/Application/Features/Products/Handler/Command/CreateProductCommandHandler.cs b/backend/BackendAssessment/Application/Features/Products/Handler/Command/CreateProductCommandHandler.cs new file mode 100644 index 00000000..094850d7 --- /dev/null +++ b/backend/BackendAssessment/Application/Features/Products/Handler/Command/CreateProductCommandHandler.cs @@ -0,0 +1,37 @@ +using Application.Contracts; +using Application.DTOs.Product; +using Application.DTOs.User; +using Application.Features.Products.Request.Command; +using AutoMapper; +using Domain.Entites; +using MediatR; +using Persistence.Repositories; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Application.Features.Products.Handler.Command +{ + public class CreateProductCommandHandler : IRequestHandler + { + + private readonly IProductRepository _Repository; + private readonly IMapper _mapper; + + public CreateProductCommandHandler(IProductRepository i, IMapper mapper) + { + _Repository = i; + _mapper = mapper; + } + public async Task Handle(CreateProductCommand request, CancellationToken cancellationToken) + { + var product = _mapper.Map(request); + var result = await _Repository.Add(product); + + var response = _mapper.Map(result); + return response; + } + } +} diff --git a/backend/BackendAssessment/Application/Features/Products/Handler/Command/UpdateProductCommandHandler.cs b/backend/BackendAssessment/Application/Features/Products/Handler/Command/UpdateProductCommandHandler.cs new file mode 100644 index 00000000..fbbe15b1 --- /dev/null +++ b/backend/BackendAssessment/Application/Features/Products/Handler/Command/UpdateProductCommandHandler.cs @@ -0,0 +1,39 @@ +using Application.Contracts; +using Application.DTOs.Product; +using Application.DTOs.User; +using Application.Features.Products.Request.Command; +using AutoMapper; +using MediatR; +using Persistence.Repositories; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Application.Features.Products.Handler.Command +{ + public class UpdateProductCommandHandler : IRequestHandler + { + private readonly IProductRepository _Repository; + private readonly IMapper _mapper; + + public UpdateProductCommandHandler(IProductRepository i, IMapper mapper) + { + _Repository = i; + _mapper = mapper; + } + public async Task Handle(UpdateProductCommand request, CancellationToken cancellationToken) + { + var product = await _Repository.Get(request.id)!; + if (product == null) + { + throw new Exception(); + } + + var categoryToUpdate = _mapper.Map(request, product); + var updatedCategory = await _Repository.Update(categoryToUpdate); + return _mapper.Map(updatedCategory); + } + } +} diff --git a/backend/BackendAssessment/Application/Features/Products/Handler/Queries/GetProductRequestHandler.cs b/backend/BackendAssessment/Application/Features/Products/Handler/Queries/GetProductRequestHandler.cs new file mode 100644 index 00000000..140008ae --- /dev/null +++ b/backend/BackendAssessment/Application/Features/Products/Handler/Queries/GetProductRequestHandler.cs @@ -0,0 +1,39 @@ +using Application.Contracts; +using Application.DTOs.Category; +using Application.DTOs.Product; +using Application.Features.Products.Request.Queries; +using AutoMapper; +using MediatR; +using Persistence.Repositories; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Application.Features.Products.Handler.Queries +{ + public class GetProductRequestHandler : IRequestHandler + { + private readonly IProductRepository _Repository; + private readonly IMapper _mapper; + + public GetProductRequestHandler(IProductRepository i, IMapper mapper) + { + _Repository = i; + _mapper = mapper; + } + public async Task Handle(GetProductRequest request, CancellationToken cancellationToken) + { + var res = await _Repository.Get(request.Id)!; + + if (res == null) + { + throw new Exception(); + } + + var categoryResponse = _mapper.Map(res); + return categoryResponse; + } + } +} diff --git a/backend/BackendAssessment/Application/Features/Products/Request/Command/CreateProductCommand.cs b/backend/BackendAssessment/Application/Features/Products/Request/Command/CreateProductCommand.cs new file mode 100644 index 00000000..3de38de7 --- /dev/null +++ b/backend/BackendAssessment/Application/Features/Products/Request/Command/CreateProductCommand.cs @@ -0,0 +1,18 @@ +using Application.DTOs.Product; +using MediatR; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Application.Features.Products.Request.Command +{ + public class CreateProductCommand : IRequest + { + public string name { get; set; } + public string description { get; set; } + public double pricing { get; set; } + public bool IsAvailable { get; set; } + } +} diff --git a/backend/BackendAssessment/Application/Features/Products/Request/Command/UpdateProductCommand.cs b/backend/BackendAssessment/Application/Features/Products/Request/Command/UpdateProductCommand.cs new file mode 100644 index 00000000..01517cc9 --- /dev/null +++ b/backend/BackendAssessment/Application/Features/Products/Request/Command/UpdateProductCommand.cs @@ -0,0 +1,19 @@ +using Application.DTOs.Product; +using MediatR; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Application.Features.Products.Request.Command +{ + public class UpdateProductCommand : IRequest + { + public Guid id { get; set; } + public string name { get; set; } + public string description { get; set; } + public double pricing { get; set; } + public bool IsAvailable { get; set; } + } +} diff --git a/backend/BackendAssessment/Application/Features/Products/Request/Queries/GetProductRequest.cs b/backend/BackendAssessment/Application/Features/Products/Request/Queries/GetProductRequest.cs new file mode 100644 index 00000000..ac47a7ff --- /dev/null +++ b/backend/BackendAssessment/Application/Features/Products/Request/Queries/GetProductRequest.cs @@ -0,0 +1,15 @@ +using Application.DTOs.Product; +using MediatR; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Application.Features.Products.Request.Queries +{ + public class GetProductRequest : IRequest + { + public Guid Id { get; set; } + } +} diff --git a/backend/BackendAssessment/Application/Features/Users/Handler/Command/CreateUserCommandHandler.cs b/backend/BackendAssessment/Application/Features/Users/Handler/Command/CreateUserCommandHandler.cs new file mode 100644 index 00000000..f5984d54 --- /dev/null +++ b/backend/BackendAssessment/Application/Features/Users/Handler/Command/CreateUserCommandHandler.cs @@ -0,0 +1,35 @@ +using Application.DTOs.Category; +using Application.DTOs.User; +using Application.Features.Users.Request.Command; +using AutoMapper; +using Domain.Entites; +using MediatR; +using Persistence.Repositories; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Application.Features.Users.Handler.Command +{ + public class CreateUserCommandHandler : IRequestHandler + { + private readonly IUserRepository _userRepository; + private readonly IMapper _mapper; + + public CreateUserCommandHandler(IUserRepository i, IMapper mapper) + { + _userRepository = i; + _mapper = mapper; + } + public async Task Handle(CreateUserCommand request, CancellationToken cancellationToken) + { + var user = _mapper.Map(request); + var result = await _userRepository.Add(user); + + var response = _mapper.Map(result); + return response; + } + } +} diff --git a/backend/BackendAssessment/Application/Features/Users/Handler/Command/UpdateUserCommandHandler.cs b/backend/BackendAssessment/Application/Features/Users/Handler/Command/UpdateUserCommandHandler.cs new file mode 100644 index 00000000..f7fcaae2 --- /dev/null +++ b/backend/BackendAssessment/Application/Features/Users/Handler/Command/UpdateUserCommandHandler.cs @@ -0,0 +1,39 @@ +using Application.DTOs.Category; +using Application.DTOs.User; +using Application.Features.Users.Request.Command; +using AutoMapper; +using MediatR; +using Persistence.Repositories; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Application.Features.Users.Handler.Command +{ + internal class UpdateUserCommandHandler : IRequestHandler + { + private readonly IUserRepository _userRepository; + private readonly IMapper _mapper; + + public UpdateUserCommandHandler(IUserRepository i, IMapper mapper) + { + _userRepository = i; + _mapper = mapper; + } + public async Task Handle(UpdateUserCommand request, CancellationToken cancellationToken) + { + var category = await _userRepository.Get(request.Id)!; + if (category == null) + { + throw new Exception(); + } + + var categoryToUpdate = _mapper.Map(request, category); + var updatedCategory = await _userRepository.Update(categoryToUpdate); + return _mapper.Map(updatedCategory); + + } + } +} diff --git a/backend/BackendAssessment/Application/Features/Users/Handler/Queries/GetUserRequestHandler.cs b/backend/BackendAssessment/Application/Features/Users/Handler/Queries/GetUserRequestHandler.cs new file mode 100644 index 00000000..0783957e --- /dev/null +++ b/backend/BackendAssessment/Application/Features/Users/Handler/Queries/GetUserRequestHandler.cs @@ -0,0 +1,37 @@ +using Application.DTOs.Category; +using Application.DTOs.User; +using Application.Features.Users.Request.Queries; +using AutoMapper; +using MediatR; +using Persistence.Repositories; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Application.Features.Users.Handler.Queries +{ + public class GetUserRequestHandler : IRequestHandler + { + private readonly IUserRepository _userRepository; + private readonly IMapper _mapper; + + + + public GetUserRequestHandler(IUserRepository i, IMapper mapper) + { + _userRepository = i; + _mapper = mapper; + } + public async Task Handle(GetUserRequest request, CancellationToken cancellationToken) + { + var user = await _userRepository.Get(request.Id)!; + if (user == null) + { + throw new Exception(); + } + return _mapper.Map(user); + } + } +} diff --git a/backend/BackendAssessment/Application/Features/Users/Request/Command/CreateUserCommand.cs b/backend/BackendAssessment/Application/Features/Users/Request/Command/CreateUserCommand.cs new file mode 100644 index 00000000..251b4a28 --- /dev/null +++ b/backend/BackendAssessment/Application/Features/Users/Request/Command/CreateUserCommand.cs @@ -0,0 +1,19 @@ +using Application.DTOs.User; +using MediatR; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Application.Features.Users.Request.Command +{ + public class CreateUserCommand : IRequest + { + public string name { get; set; } + public string email { get; set; } + public string password { get; set; } + public bool isAdmin { get; set; } + + } +} diff --git a/backend/BackendAssessment/Application/Features/Users/Request/Command/UpdateUserCommand.cs b/backend/BackendAssessment/Application/Features/Users/Request/Command/UpdateUserCommand.cs new file mode 100644 index 00000000..a0052305 --- /dev/null +++ b/backend/BackendAssessment/Application/Features/Users/Request/Command/UpdateUserCommand.cs @@ -0,0 +1,19 @@ +using Application.DTOs.User; +using MediatR; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Application.Features.Users.Request.Command +{ + public class UpdateUserCommand : IRequest + { + public Guid Id { get; set; } + public string UserName { get; set; } + public string Password { get; set; } + public string Email { get; set; } + + } +} diff --git a/backend/BackendAssessment/Application/Features/Users/Request/Queries/GetUserRequest.cs b/backend/BackendAssessment/Application/Features/Users/Request/Queries/GetUserRequest.cs new file mode 100644 index 00000000..e3e14941 --- /dev/null +++ b/backend/BackendAssessment/Application/Features/Users/Request/Queries/GetUserRequest.cs @@ -0,0 +1,15 @@ +using Application.DTOs.User; +using MediatR; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Application.Features.Users.Request.Queries +{ + public class GetUserRequest : IRequest + { + public Guid Id { get; set; } + } +} diff --git a/backend/BackendAssessment/Application/Profiles/MappingProfile.cs b/backend/BackendAssessment/Application/Profiles/MappingProfile.cs new file mode 100644 index 00000000..83c2b1c3 --- /dev/null +++ b/backend/BackendAssessment/Application/Profiles/MappingProfile.cs @@ -0,0 +1,26 @@ +using AutoMapper; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Application.DTOs.Category; +using Application.Features.Categories.Request.Command; +using Application.DTOs.User; +using Application.Features.Users.Request.Command; +using Application.Features.Products.Request.Command; +using Application.DTOs.Product; + +namespace Application.Profiles +{ + public class MappingProfile : Profile + { + public MappingProfile() + { + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + + } + } +} diff --git a/backend/BackendAssessment/Domain/Entites/Category.cs b/backend/BackendAssessment/Domain/Entites/Category.cs new file mode 100644 index 00000000..add9ec01 --- /dev/null +++ b/backend/BackendAssessment/Domain/Entites/Category.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Domain.Entites +{ + public class Category + { + public Guid Id { get; set; } + public Category() + { + products = new HashSet(); + } + public string name { get; set; } + public string description { get; set; } + + public virtual ICollection products { get; set; } + } +} diff --git a/backend/BackendAssessment/Domain/Entites/Product.cs b/backend/BackendAssessment/Domain/Entites/Product.cs new file mode 100644 index 00000000..cedc739e --- /dev/null +++ b/backend/BackendAssessment/Domain/Entites/Product.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Domain.Entites +{ + public class Product + { + public Guid Id { get; set; } + public string name { get; set; } + public string description { get; set; } + public double pricing { get; set; } + public bool availability { get; set; } + public virtual Category category { get; set; } + public virtual User user { get; set; } + } +} diff --git a/backend/BackendAssessment/Domain/Entites/User.cs b/backend/BackendAssessment/Domain/Entites/User.cs new file mode 100644 index 00000000..435f8618 --- /dev/null +++ b/backend/BackendAssessment/Domain/Entites/User.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Domain.Entites +{ + public class User + { + public User() + { + products = new HashSet(); + } + public Guid id { get; set; } + + public string username { get; set; } + public string password { get; set; } + public string email { get; set; } + public bool isAdmin { get; set; } + + public virtual ICollection products { get; set; } + } +} diff --git a/backend/BackendAssessment/Infrastructure/Infrastructure.csproj b/backend/BackendAssessment/Infrastructure/Infrastructure.csproj index b62d9762..b85d13b7 100644 --- a/backend/BackendAssessment/Infrastructure/Infrastructure.csproj +++ b/backend/BackendAssessment/Infrastructure/Infrastructure.csproj @@ -1,4 +1,4 @@ - + net7.0 @@ -8,10 +8,18 @@ + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + diff --git a/backend/BackendAssessment/Persistence/Data/ApiDbContext.cs b/backend/BackendAssessment/Persistence/Data/ApiDbContext.cs new file mode 100644 index 00000000..e5e97506 --- /dev/null +++ b/backend/BackendAssessment/Persistence/Data/ApiDbContext.cs @@ -0,0 +1,56 @@ +using Domain.Entites; +using Microsoft.EntityFrameworkCore; + +using Microsoft.Extensions.Hosting; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Persistence.Data +{ + public class ApiDbContext : DbContext + { + public ApiDbContext(DbContextOptions options) : base(options) + { + + } + + public DbSet users { get; set; } + public DbSet products { get; set; } + public DbSet categories { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.Entity(entity => + { + entity.HasMany(p => p.products) + .WithOne(u => u.user) + .OnDelete(DeleteBehavior.Cascade); + }); + modelBuilder.Entity(entity => + { + entity.HasMany(p => p.products) + .WithOne(c => c.category) + .OnDelete(DeleteBehavior.Cascade); + }); + modelBuilder.Entity(entity => + { + entity.HasOne(p => p.user) + .WithMany(c => c.products); + }); + modelBuilder.Entity(entity => + { + entity.HasOne(c => c.category) + .WithMany(p => p.products) + .OnDelete(DeleteBehavior.Cascade); + }); + + + } + + } +} diff --git a/backend/BackendAssessment/Persistence/Data/ApiDbContextFactory.cs b/backend/BackendAssessment/Persistence/Data/ApiDbContextFactory.cs new file mode 100644 index 00000000..9357568a --- /dev/null +++ b/backend/BackendAssessment/Persistence/Data/ApiDbContextFactory.cs @@ -0,0 +1,20 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.Extensions.Configuration; +using Persistence.Data; + +namespace Persistence +{ + public class ApiDBContextFactory : IDesignTimeDbContextFactory + { + public ApiDbContext CreateDbContext(string[] args) + { + IConfiguration config = new ConfigurationBuilder().SetBasePath(Path.Combine(Directory.GetCurrentDirectory(), "../WebApi")).AddJsonFile("appsettings.json").Build(); + + var connectionString = config.GetConnectionString("DefaultConnection"); + var builder = new DbContextOptionsBuilder(); + builder.UseNpgsql(connectionString); + return new ApiDbContext(builder.Options); + } + } +} \ No newline at end of file diff --git a/backend/BackendAssessment/Persistence/DependencyInjection.cs b/backend/BackendAssessment/Persistence/DependencyInjection.cs new file mode 100644 index 00000000..840b9239 --- /dev/null +++ b/backend/BackendAssessment/Persistence/DependencyInjection.cs @@ -0,0 +1,26 @@ +using Persistence.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Persistence.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Persistence +{ + public static class DependencyInjection + { + public static IServiceCollection AddPersistence(this IServiceCollection services, ConfigurationManager configuration) + { + + var connectionString = configuration.GetConnectionString("DefaultConnection"); + services.AddDbContext(opt => + opt.UseNpgsql(connectionString)); + + return services; + } + } +} diff --git a/backend/BackendAssessment/Persistence/Migrations/20230905142035_Initial.Designer.cs b/backend/BackendAssessment/Persistence/Migrations/20230905142035_Initial.Designer.cs new file mode 100644 index 00000000..41039cd2 --- /dev/null +++ b/backend/BackendAssessment/Persistence/Migrations/20230905142035_Initial.Designer.cs @@ -0,0 +1,139 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Persistence.Data; + +#nullable disable + +namespace Persistence.Migrations +{ + [DbContext(typeof(ApiDbContext))] + [Migration("20230905142035_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entites.Category", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("description") + .IsRequired() + .HasColumnType("text"); + + b.Property("name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("categories"); + }); + + modelBuilder.Entity("Domain.Entites.Product", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("availability") + .HasColumnType("boolean"); + + b.Property("categoryId") + .HasColumnType("uuid"); + + b.Property("description") + .IsRequired() + .HasColumnType("text"); + + b.Property("name") + .IsRequired() + .HasColumnType("text"); + + b.Property("pricing") + .HasColumnType("double precision"); + + b.Property("userid") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("categoryId"); + + b.HasIndex("userid"); + + b.ToTable("products"); + }); + + modelBuilder.Entity("Domain.Entites.User", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("email") + .IsRequired() + .HasColumnType("text"); + + b.Property("isAdmin") + .HasColumnType("boolean"); + + b.Property("password") + .IsRequired() + .HasColumnType("text"); + + b.Property("username") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("id"); + + b.ToTable("users"); + }); + + modelBuilder.Entity("Domain.Entites.Product", b => + { + b.HasOne("Domain.Entites.Category", "category") + .WithMany("products") + .HasForeignKey("categoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entites.User", "user") + .WithMany("products") + .HasForeignKey("userid") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("category"); + + b.Navigation("user"); + }); + + modelBuilder.Entity("Domain.Entites.Category", b => + { + b.Navigation("products"); + }); + + modelBuilder.Entity("Domain.Entites.User", b => + { + b.Navigation("products"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/BackendAssessment/Persistence/Migrations/20230905142035_Initial.cs b/backend/BackendAssessment/Persistence/Migrations/20230905142035_Initial.cs new file mode 100644 index 00000000..98c7dca0 --- /dev/null +++ b/backend/BackendAssessment/Persistence/Migrations/20230905142035_Initial.cs @@ -0,0 +1,95 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Persistence.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "categories", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + name = table.Column(type: "text", nullable: false), + description = table.Column(type: "text", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_categories", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "users", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + username = table.Column(type: "text", nullable: false), + password = table.Column(type: "text", nullable: false), + email = table.Column(type: "text", nullable: false), + isAdmin = table.Column(type: "boolean", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_users", x => x.id); + }); + + migrationBuilder.CreateTable( + name: "products", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + name = table.Column(type: "text", nullable: false), + description = table.Column(type: "text", nullable: false), + pricing = table.Column(type: "double precision", nullable: false), + availability = table.Column(type: "boolean", nullable: false), + categoryId = table.Column(type: "uuid", nullable: false), + userid = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_products", x => x.Id); + table.ForeignKey( + name: "FK_products_categories_categoryId", + column: x => x.categoryId, + principalTable: "categories", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_products_users_userid", + column: x => x.userid, + principalTable: "users", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_products_categoryId", + table: "products", + column: "categoryId"); + + migrationBuilder.CreateIndex( + name: "IX_products_userid", + table: "products", + column: "userid"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "products"); + + migrationBuilder.DropTable( + name: "categories"); + + migrationBuilder.DropTable( + name: "users"); + } + } +} diff --git a/backend/BackendAssessment/Persistence/Migrations/20230905142215_newMigration.Designer.cs b/backend/BackendAssessment/Persistence/Migrations/20230905142215_newMigration.Designer.cs new file mode 100644 index 00000000..61ea468e --- /dev/null +++ b/backend/BackendAssessment/Persistence/Migrations/20230905142215_newMigration.Designer.cs @@ -0,0 +1,139 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Persistence.Data; + +#nullable disable + +namespace Persistence.Migrations +{ + [DbContext(typeof(ApiDbContext))] + [Migration("20230905142215_newMigration")] + partial class newMigration + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entites.Category", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("description") + .IsRequired() + .HasColumnType("text"); + + b.Property("name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("categories"); + }); + + modelBuilder.Entity("Domain.Entites.Product", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("availability") + .HasColumnType("boolean"); + + b.Property("categoryId") + .HasColumnType("uuid"); + + b.Property("description") + .IsRequired() + .HasColumnType("text"); + + b.Property("name") + .IsRequired() + .HasColumnType("text"); + + b.Property("pricing") + .HasColumnType("double precision"); + + b.Property("userid") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("categoryId"); + + b.HasIndex("userid"); + + b.ToTable("products"); + }); + + modelBuilder.Entity("Domain.Entites.User", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("email") + .IsRequired() + .HasColumnType("text"); + + b.Property("isAdmin") + .HasColumnType("boolean"); + + b.Property("password") + .IsRequired() + .HasColumnType("text"); + + b.Property("username") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("id"); + + b.ToTable("users"); + }); + + modelBuilder.Entity("Domain.Entites.Product", b => + { + b.HasOne("Domain.Entites.Category", "category") + .WithMany("products") + .HasForeignKey("categoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entites.User", "user") + .WithMany("products") + .HasForeignKey("userid") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("category"); + + b.Navigation("user"); + }); + + modelBuilder.Entity("Domain.Entites.Category", b => + { + b.Navigation("products"); + }); + + modelBuilder.Entity("Domain.Entites.User", b => + { + b.Navigation("products"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/BackendAssessment/Persistence/Migrations/20230905142215_newMigration.cs b/backend/BackendAssessment/Persistence/Migrations/20230905142215_newMigration.cs new file mode 100644 index 00000000..a65aabee --- /dev/null +++ b/backend/BackendAssessment/Persistence/Migrations/20230905142215_newMigration.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Persistence.Migrations +{ + /// + public partial class newMigration : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/backend/BackendAssessment/Persistence/Migrations/ApiDbContextModelSnapshot.cs b/backend/BackendAssessment/Persistence/Migrations/ApiDbContextModelSnapshot.cs new file mode 100644 index 00000000..57ce86a6 --- /dev/null +++ b/backend/BackendAssessment/Persistence/Migrations/ApiDbContextModelSnapshot.cs @@ -0,0 +1,136 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Persistence.Data; + +#nullable disable + +namespace Persistence.Migrations +{ + [DbContext(typeof(ApiDbContext))] + partial class ApiDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Entites.Category", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("description") + .IsRequired() + .HasColumnType("text"); + + b.Property("name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("categories"); + }); + + modelBuilder.Entity("Domain.Entites.Product", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("availability") + .HasColumnType("boolean"); + + b.Property("categoryId") + .HasColumnType("uuid"); + + b.Property("description") + .IsRequired() + .HasColumnType("text"); + + b.Property("name") + .IsRequired() + .HasColumnType("text"); + + b.Property("pricing") + .HasColumnType("double precision"); + + b.Property("userid") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("categoryId"); + + b.HasIndex("userid"); + + b.ToTable("products"); + }); + + modelBuilder.Entity("Domain.Entites.User", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("email") + .IsRequired() + .HasColumnType("text"); + + b.Property("isAdmin") + .HasColumnType("boolean"); + + b.Property("password") + .IsRequired() + .HasColumnType("text"); + + b.Property("username") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("id"); + + b.ToTable("users"); + }); + + modelBuilder.Entity("Domain.Entites.Product", b => + { + b.HasOne("Domain.Entites.Category", "category") + .WithMany("products") + .HasForeignKey("categoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Domain.Entites.User", "user") + .WithMany("products") + .HasForeignKey("userid") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("category"); + + b.Navigation("user"); + }); + + modelBuilder.Entity("Domain.Entites.Category", b => + { + b.Navigation("products"); + }); + + modelBuilder.Entity("Domain.Entites.User", b => + { + b.Navigation("products"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/BackendAssessment/Persistence/Persistence.csproj b/backend/BackendAssessment/Persistence/Persistence.csproj index c78c297b..45fd79a7 100644 --- a/backend/BackendAssessment/Persistence/Persistence.csproj +++ b/backend/BackendAssessment/Persistence/Persistence.csproj @@ -1,4 +1,4 @@ - + net7.0 @@ -8,11 +8,16 @@ - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all + + + + + diff --git a/backend/BackendAssessment/Persistence/Repositories/CategoryRepository.cs b/backend/BackendAssessment/Persistence/Repositories/CategoryRepository.cs new file mode 100644 index 00000000..c065bfac --- /dev/null +++ b/backend/BackendAssessment/Persistence/Repositories/CategoryRepository.cs @@ -0,0 +1,19 @@ +using Application.Persistence.Repositories; +using Domain.Entites; +using Persistence.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Persistence.Repositories +{ + public class CategoryRepository : GenericRepository + { + private readonly ApiDbContext _context; + public CategoryRepository(ApiDbContext dbContext) : base(dbContext) + { + } + } +} diff --git a/backend/BackendAssessment/Persistence/Repositories/GenericRepository.cs b/backend/BackendAssessment/Persistence/Repositories/GenericRepository.cs new file mode 100644 index 00000000..8da43950 --- /dev/null +++ b/backend/BackendAssessment/Persistence/Repositories/GenericRepository.cs @@ -0,0 +1,57 @@ +using Application.Contracts; +using Microsoft.EntityFrameworkCore; +using Persistence.Data; +using Persistence.Repositories; + +namespace Application.Persistence.Repositories +{ + public class GenericRepository : IGenericRepository where T : class + { + private readonly ApiDbContext _dbContext; + + public GenericRepository(ApiDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Get(Guid id) + { + return await _dbContext.Set().FindAsync(id); + } + + public async Task> GetAll() + { + return await _dbContext.Set().ToListAsync(); + } + + public async Task Add(T entity) + { + await _dbContext.Set().AddAsync(entity); + await _dbContext.SaveChangesAsync(); + return entity; + } + + public async Task Update(T entity) + { + _dbContext.Entry(entity).State = EntityState.Modified; + await _dbContext.SaveChangesAsync(); + return entity; + } + + public bool Exists(Guid id) + { + var entity = Get(id); + return entity != null; + } + + public void Delete(T entity) + { + _dbContext.Set().Remove(entity); + _dbContext.SaveChanges(); + } + + } + +} + + diff --git a/backend/BackendAssessment/Persistence/Repositories/ProductRepository.cs b/backend/BackendAssessment/Persistence/Repositories/ProductRepository.cs new file mode 100644 index 00000000..a7dfd304 --- /dev/null +++ b/backend/BackendAssessment/Persistence/Repositories/ProductRepository.cs @@ -0,0 +1,24 @@ +using Domain.Entites; +using Application.Contracts; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Application.Persistence.Repositories; +using Persistence.Data; + +namespace Persistence.Repositories +{ + public class ProductRepository : GenericRepository + { + private readonly ApiDbContext _dbContext; + public ProductRepository(ApiDbContext dbContext) : base(dbContext) + { + _dbContext = dbContext; + } + + + } +} diff --git a/backend/BackendAssessment/Persistence/Repositories/UserRepository.cs b/backend/BackendAssessment/Persistence/Repositories/UserRepository.cs new file mode 100644 index 00000000..d246541e --- /dev/null +++ b/backend/BackendAssessment/Persistence/Repositories/UserRepository.cs @@ -0,0 +1,34 @@ +using Application.Persistence.Repositories; +using Domain.Entites; +using Persistence.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Persistence.Repositories +{ + public class UserRepository : GenericRepository + { + private readonly ApiDbContext _dbContext; + public UserRepository(ApiDbContext dbContext) : base(dbContext) + { + _dbContext = dbContext; + } + + //public async Task GetByEmail(string email) + //{ + // return await _dbContext.Set() + // .Where(u => u.email == email) + + //} + + //public async Task> GetByNameEmail(string email) + //{ + // return await _dbContext.Set() + // .Where(u => u.username == email || u.email == email) + // .ToListAsync(); + //} + } +} diff --git a/backend/BackendAssessment/WebApi/Controllers/CategoryController.cs b/backend/BackendAssessment/WebApi/Controllers/CategoryController.cs new file mode 100644 index 00000000..a2188e53 --- /dev/null +++ b/backend/BackendAssessment/WebApi/Controllers/CategoryController.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using System.Xml.Linq; +using Application.DTOs.Category; +using Application.Features.Categories.Request.Command; +using Application.Features.Categories.Request.Queries; +using Application.Features.Categories.Request.Queries; +using MediatR; +using Microsoft.AspNetCore.Mvc; + +namespace WebApi.Controllers +{ + [ApiController] + [Route("api/categories")] + public class CategoryController : ControllerBase + { + private readonly IMediator _mediator; + + public CategoryController(IMediator mediator) + { + _mediator = mediator; + } + + [HttpGet("{categoryId}")] + public async Task> GetCategoryById(Guid categoryId) + { + var query = new GetCategoryRequest { id = categoryId }; + var category = await _mediator.Send(query); + + if (category == null) + { + return NotFound(); + } + + return Ok(category); + } + + [HttpPost] + public async Task CreateCategory(CategoryRetriveDto createCategoryDTO) + { + var command = new CreateCategoryCommand { name = createCategoryDTO.name , description = createCategoryDTO.description}; + await _mediator.Send(command); + + return CreatedAtAction(nameof(GetCategoryById), new { categoryId = createCategoryDTO.Id }, createCategoryDTO); + } + + [HttpPut("{categoryId}")] + public async Task UpdateCategory(CategoryRetriveDto updateCategoryDTO) + { + + var command = new UpdateCategoryCommand {name = updateCategoryDTO.name, description = updateCategoryDTO.description }; + await _mediator.Send(command); + + return NoContent(); + } + + } +} diff --git a/backend/BackendAssessment/WebApi/Controllers/ProductController.cs b/backend/BackendAssessment/WebApi/Controllers/ProductController.cs new file mode 100644 index 00000000..2995bcea --- /dev/null +++ b/backend/BackendAssessment/WebApi/Controllers/ProductController.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Application.DTOs.Product; +using Application.Features.Products.Request.Command; +using Application.Features.Products.Request.Queries; +using MediatR; +using Microsoft.AspNetCore.Mvc; + +namespace WebApi.Controllers +{ + [ApiController] + [Route("api/products")] + public class ProductController : ControllerBase + { + private readonly IMediator _mediator; + + public ProductController(IMediator mediator) + { + _mediator = mediator; + } + + [HttpGet("{productId}")] + public async Task> GetProductById(Guid productId) + { + var query = new GetProductRequest { Id = productId }; + var product = await _mediator.Send(query); + + if (product == null) + { + return NotFound(); + } + + return Ok(product); + } + + [HttpPost] + public async Task CreateProduct(CreateProductDTO createProductDTO) + { + var command = new CreateProductCommand { name = createProductDTO.name , description=createProductDTO.description, + IsAvailable = createProductDTO.isAvailable,pricing = createProductDTO.pricing}; + await _mediator.Send(command); + + return CreatedAtAction(nameof(GetProductById), new { productId = createProductDTO.Id }, createProductDTO); + } + + [HttpPut("{productId}")] + public async Task UpdateProduct(CreateProductDTO updateProductDTO) + { + var command = new UpdateProductCommand { + name = updateProductDTO.name, + description = updateProductDTO.description, + IsAvailable = updateProductDTO.isAvailable, + pricing = updateProductDTO.pricing + }; + await _mediator.Send(command); + + return NoContent(); + } + } +} + diff --git a/backend/BackendAssessment/WebApi/Controllers/UserController.cs b/backend/BackendAssessment/WebApi/Controllers/UserController.cs new file mode 100644 index 00000000..ba7e33a3 --- /dev/null +++ b/backend/BackendAssessment/WebApi/Controllers/UserController.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Application.DTOs.User; +using Application.Features.Users.Request.Command; +using Application.Features.Users.Request.Queries; +using MediatR; +using Microsoft.AspNetCore.Mvc; + +namespace WebApi.Controllers +{ + [ApiController] + [Route("api/users")] + public class UserController : ControllerBase + { + private readonly IMediator _mediator; + + public UserController(IMediator mediator) + { + _mediator = mediator; + } + + [HttpGet("{id}")] + public async Task> GetUserById(Guid id) + { + var request = new GetUserRequest { Id = id }; + var result = await _mediator.Send(request); + + if (result == null) + { + return NotFound(); + } + + return Ok(result); + } + + [HttpPut("{id}")] + public async Task UpdateUser(Guid id, CreateUserDto userDTO) + { + + var command = new UpdateUserCommand { UserName = userDTO.username,Password = userDTO.password,Email = userDTO.email }; + await _mediator.Send(command); + + return NoContent(); + } + + [HttpPost] + public async Task> CreateUser(CreateUserDto UserDTO) + { + var command = new CreateUserCommand { name = UserDTO.username, password = UserDTO.password, email = UserDTO.email,isAdmin = UserDTO.isAdmin }; + var result = await _mediator.Send(command); + + return Ok(result); + } + + } +} + diff --git a/backend/BackendAssessment/WebApi/Controllers/WeatherForecastController.cs b/backend/BackendAssessment/WebApi/Controllers/WeatherForecastController.cs deleted file mode 100644 index 7e256fd7..00000000 --- a/backend/BackendAssessment/WebApi/Controllers/WeatherForecastController.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Microsoft.AspNetCore.Mvc; - -namespace WebApi.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/backend/BackendAssessment/WebApi/Program.cs b/backend/BackendAssessment/WebApi/Program.cs index 8264bac5..a21ba9b0 100644 --- a/backend/BackendAssessment/WebApi/Program.cs +++ b/backend/BackendAssessment/WebApi/Program.cs @@ -1,3 +1,4 @@ +using Persistence; var builder = WebApplication.CreateBuilder(args); // Add services to the container. @@ -6,6 +7,7 @@ // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); +builder.Services.AddPersistence(builder.Configuration); var app = builder.Build(); diff --git a/backend/BackendAssessment/WebApi/WebApi.csproj b/backend/BackendAssessment/WebApi/WebApi.csproj index 85ed54c3..a206e0a3 100644 --- a/backend/BackendAssessment/WebApi/WebApi.csproj +++ b/backend/BackendAssessment/WebApi/WebApi.csproj @@ -8,9 +8,13 @@ - - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + @@ -19,4 +23,10 @@ + + + + + + diff --git a/backend/BackendAssessment/WebApi/appsettings.json b/backend/BackendAssessment/WebApi/appsettings.json index 10f68b8c..3b4158ad 100644 --- a/backend/BackendAssessment/WebApi/appsettings.json +++ b/backend/BackendAssessment/WebApi/appsettings.json @@ -5,5 +5,9 @@ "Microsoft.AspNetCore": "Warning" } }, - "AllowedHosts": "*" + "AllowedHosts": "*", + "ConnectionStrings": { + "DefaultConnection": "Username=postgres;database=ProductHub;Password=aymila777;Host=localhost;Port=5432;Integrated Security=false;Pooling=true;" + + } }