diff --git a/ShareBook/ShareBook.Api/AutoMapper/DomainToViewModelMappingProfile.cs b/ShareBook/ShareBook.Api/AutoMapper/DomainToViewModelMappingProfile.cs index 129b3d18..f47b25d0 100644 --- a/ShareBook/ShareBook.Api/AutoMapper/DomainToViewModelMappingProfile.cs +++ b/ShareBook/ShareBook.Api/AutoMapper/DomainToViewModelMappingProfile.cs @@ -15,24 +15,35 @@ protected DomainToViewModelMappingProfile(string profileName) : base(profileName { #region [ Book ] - CreateMap() + CreateMap() .ForMember(dest => dest.Donor, opt => opt.MapFrom(src => src.User.Name)) + .ForMember(dest => dest.City, opt => opt.MapFrom(src => src.User.Address.City)) + .ForMember(dest => dest.State, opt => opt.MapFrom(src => src.User.Address.State)) .ForMember(dest => dest.Facilitator, opt => opt.MapFrom(src => src.UserFacilitator.Name)) .ForMember(dest => dest.FacilitatorNotes, opt => opt.MapFrom(src => src.FacilitatorNotes)) - .ForMember(dest => dest.Donated, opt => opt.MapFrom(src => src.Donated())) .ForMember(dest => dest.PhoneDonor, opt => opt.MapFrom(src => src.User.Phone)) .ForMember(dest => dest.DaysInShowcase, opt => opt.MapFrom(src => src.DaysInShowcase())) .ForMember(dest => dest.TotalInterested, opt => opt.MapFrom(src => src.TotalInterested())) - .ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.Status().Description())) + .ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.Status.ToString())) + .ForMember(dest => dest.FreightOption, opt => opt.MapFrom(src => src.FreightOption.ToString())) .ForMember(dest => dest.CreationDate, opt => opt.MapFrom(src => src.CreationDate)) .ForMember(dest => dest.ChooseDate, opt => opt.MapFrom(src => src.ChooseDate)) .ForMember(dest => dest.Winner, opt => opt.MapFrom(src => src.Winner())) - .ForMember(dest => dest.TrackingNumber, opt => opt.MapFrom(src => src.TrackingNumber)); + .ForMember(dest => dest.TrackingNumber, opt => opt.MapFrom(src => src.TrackingNumber)) + .ForMember(dest => dest.Category, opt => opt.MapFrom(src => src.Category.Name)); + + CreateMap() + .ForMember(dest => dest.City, opt => opt.MapFrom(src => src.User.Address.City)) + .ForMember(dest => dest.State, opt => opt.MapFrom(src => src.User.Address.State)) + .ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.Status.ToString())) + .ForMember(dest => dest.FreightOption, opt => opt.MapFrom(src => src.FreightOption.ToString())) + .ForMember(dest => dest.CreationDate, opt => opt.MapFrom(src => src.CreationDate)) + .ForMember(dest => dest.Category, opt => opt.MapFrom(src => src.Category.Name)); CreateMap() .ForMember(dest => dest.Author, opt => opt.MapFrom(src => src.Book.Author)) .ForMember(dest => dest.Title, opt => opt.MapFrom(src => src.Book.Title)) - .ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.Status.Description())) + .ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.Status.ToString())) .ForMember(dest => dest.RequestId, opt => opt.MapFrom(src => src.Id)); #endregion [ Book ] diff --git a/ShareBook/ShareBook.Api/Configuration/ServiceRepositoryCollectionExtensions.cs b/ShareBook/ShareBook.Api/Configuration/ServiceRepositoryCollectionExtensions.cs index e27a9165..d34a8db1 100644 --- a/ShareBook/ShareBook.Api/Configuration/ServiceRepositoryCollectionExtensions.cs +++ b/ShareBook/ShareBook.Api/Configuration/ServiceRepositoryCollectionExtensions.cs @@ -11,6 +11,7 @@ using Sharebook.Jobs; using ShareBook.Service.Notification; using ShareBook.Service.Muambator; +using ShareBook.Service.AWSSQS; namespace ShareBook.Api.Configuration { @@ -30,6 +31,7 @@ public static IServiceCollection RegisterRepositoryServices( services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddSingleton(); //repositories services.AddScoped(); @@ -63,6 +65,7 @@ public static IServiceCollection RegisterRepositoryServices( services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); //notification services.AddScoped(); diff --git a/ShareBook/ShareBook.Api/Controllers/AccountController.cs b/ShareBook/ShareBook.Api/Controllers/AccountController.cs index 262f014f..a9579b71 100644 --- a/ShareBook/ShareBook.Api/Controllers/AccountController.cs +++ b/ShareBook/ShareBook.Api/Controllers/AccountController.cs @@ -5,10 +5,12 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; using ShareBook.Api.Filters; using ShareBook.Api.ViewModels; using ShareBook.Domain; using ShareBook.Domain.Common; +using ShareBook.Domain.Exceptions; using ShareBook.Infra.CrossCutting.Identity; using ShareBook.Infra.CrossCutting.Identity.Interfaces; using ShareBook.Service; @@ -23,14 +25,17 @@ public class AccountController : ControllerBase private readonly IUserService _userService; private readonly IApplicationSignInManager _signManager; private readonly IMapper _mapper; + private readonly IConfiguration _configuration; public AccountController(IUserService userService, IApplicationSignInManager signManager, - IMapper mapper) + IMapper mapper, + IConfiguration configuration) { _userService = userService; _signManager = signManager; _mapper = mapper; + _configuration = configuration; } #region GET @@ -90,13 +95,22 @@ public IActionResult Post([FromBody] RegisterUserVM registerUserVM, [FromService [HttpPost("Login")] [ProducesResponseType(typeof(object), 200)] [ProducesResponseType(404)] - public IActionResult Login([FromBody] LoginUserVM loginUserVM, [FromServices] SigningConfigurations signingConfigurations, [FromServices] TokenConfigurations tokenConfigurations) + public IActionResult Login( + [FromBody] LoginUserVM loginUserVM, + [FromServices] SigningConfigurations signingConfigurations, + [FromServices] TokenConfigurations tokenConfigurations, + [FromHeader(Name = "x-requested-with")] string client, + [FromHeader(Name = "client-version")] string clientVersion) { + if (!ModelState.IsValid) - return BadRequest(); + return BadRequest(ModelState); - var user = _mapper.Map(loginUserVM); + // mensagem amigável para usuários mobile antigos + if (!IsValidClientVersion(client, clientVersion)) + throw new ShareBookException("Não é possível fazer login porque seu app está desatualizado. Por favor atualize seu app na loja do Google Play."); + var user = _mapper.Map(loginUserVM); var result = _userService.AuthenticationByEmailAndPassword(user); if (result.Success) @@ -180,5 +194,22 @@ public IActionResult ChangeUserPasswordByHashCode([FromBody] ChangeUserPasswordB } #endregion PUT + + private bool IsValidClientVersion(string client, string clientVersion) + { + switch (client) + { + case "web": + return true; + + // mobile android + case "com.makeztec.sharebook": + var minVersion = _configuration["ClientSettings:AndroidMinVersion"]; + return Helper.ClientVersionValidation.IsValidVersion(clientVersion, minVersion); + + default: + return false; + } + } } } \ No newline at end of file diff --git a/ShareBook/ShareBook.Api/Controllers/BookController.cs b/ShareBook/ShareBook.Api/Controllers/BookController.cs index caa849dc..93bbe022 100644 --- a/ShareBook/ShareBook.Api/Controllers/BookController.cs +++ b/ShareBook/ShareBook.Api/Controllers/BookController.cs @@ -46,19 +46,19 @@ protected void SetDefault(Expression> defaultOrder) [HttpGet()] [Authorize("Bearer")] [AuthorizationFilter(Permissions.Permission.DonateBook)] - public PagedList GetAll() => Paged(1, 15); + public PagedList GetAll() => Paged(1, 15); [HttpGet("{page}/{items}")] [Authorize("Bearer")] [AuthorizationFilter(Permissions.Permission.DonateBook)] - public PagedList Paged(int page, int items) + public PagedList Paged(int page, int items) { // TODO: parar de usar esse get complicado e fazer uma query linq/ef tradicional usando // ThenInclude(). fonte: https://stackoverflow.com/questions/10822656/entity-framework-include-multiple-levels-of-properties var books = _service.Get(x => x.Title, page, items, new IncludeList(x => x.User, x => x.BookUsers, x => x.UserFacilitator)); - var responseVM = _mapper.Map>(books.Items); + var responseVM = _mapper.Map>(books.Items); - return new PagedList() + return new PagedList() { Page = page, TotalItems = books.TotalItems, @@ -67,13 +67,14 @@ public PagedList Paged(int page, int items) }; } - [HttpGet("{id}")] - public Book GetById(string id) => _service.Find(new Guid(id)); - [Authorize("Bearer")] [HttpPost("Approve/{id}")] [AuthorizationFilter(Permissions.Permission.ApproveBook)] - public Result Approve(string id, [FromBody] ApproveBookVM model) => _service.Approve(new Guid(id), model?.ChooseDate); + public Result Approve(string id, [FromBody] ApproveBookVM model) + { + _service.Approve(new Guid(id), model?.ChooseDate); + return new Result("Livro aprovado com sucesso."); + } [Authorize("Bearer")] [HttpPost("Cancel/{id}")] @@ -107,28 +108,49 @@ public IActionResult GetRequestersList(Guid bookId) } [HttpGet("Slug/{slug}")] + [ProducesResponseType(typeof(BookVM), 200)] public IActionResult Get(string slug) { var book = _service.BySlug(slug); - return book != null ? (IActionResult)Ok(book) : NotFound(); + var bookVM = _mapper.Map(book); + return book != null ? (IActionResult)Ok(bookVM) : NotFound(); } - [HttpGet("Top15NewBooks")] - public IList Top15NewBooks() => _service.Top15NewBooks(); - - [HttpGet("Random15Books")] - public IList Random15Books() => _service.Random15Books(); - [Authorize("Bearer")] - [HttpGet("Title/{title}/{page}/{items}")] - public PagedList ByTitle(string title, int page, int items) => _service.ByTitle(title, page, items); + [AuthorizationFilter(Permissions.Permission.ApproveBook)] // apenas adms + [ProducesResponseType(typeof(BookVM), 200)] + [HttpGet("{id}")] + public IActionResult GetById(string id) + { + var book = _service.Find(new Guid(id)); + var bookVM = _mapper.Map(book); + return bookVM != null ? (IActionResult)Ok(bookVM) : NotFound(); + } - [Authorize("Bearer")] - [HttpGet("Author/{author}/{page}/{items}")] - public PagedList ByAuthor(string author, int page, int items) => _service.ByAuthor(author, page, items); + [HttpGet("AvailableBooks")] + public IList AvailableBooks() { + var books = _service.AvailableBooks(); + return _mapper.Map>(books); + } + + [HttpGet("Random15Books")] + public IList Random15Books() { + var books = _service.Random15Books(); + return _mapper.Map>(books); + } [HttpGet("FullSearch/{criteria}/{page}/{items}")] - public PagedList FullSearch(string criteria, int page, int items) => _service.FullSearch(criteria, page, items); + public PagedList FullSearch(string criteria, int page, int items) { + var books = _service.FullSearch(criteria, page, items); + var booksVM = _mapper.Map>(books.Items); + return new PagedList() + { + Page = page, + TotalItems = books.TotalItems, + ItemsPerPage = items, + Items = booksVM + }; + } [Authorize("Bearer")] [HttpGet("FullSearchAdmin/{criteria}")] @@ -140,7 +162,20 @@ public PagedList FullSearchAdmin(string criteria, int page, int items) } [HttpGet("Category/{categoryId}/{page}/{items}")] - public PagedList ByCategoryId(Guid categoryId, int page, int items) => _service.ByCategoryId(categoryId, page, items); + public PagedList ByCategoryId(Guid categoryId, int page, int items) + { + var booksPaged = _service.ByCategoryId(categoryId, page, items); + var books = booksPaged.Items; + var booksVM = _mapper.Map>(books); + + return new PagedList() + { + Page = page, + ItemsPerPage = items, + TotalItems = booksPaged.TotalItems, + Items = booksVM + }; + } [Authorize("Bearer")] [HttpPost("Request")] @@ -223,11 +258,11 @@ public PagedList MyRequests(int page, int items) [Authorize("Bearer")] [HttpGet("MyDonations")] - public IList MyDonations() + public IList MyDonations() { Guid userId = new Guid(Thread.CurrentPrincipal?.Identity?.Name); var donations = _service.GetUserDonations(userId); - return _mapper.Map>(donations); + return _mapper.Map>(donations); } [Authorize("Bearer")] diff --git a/ShareBook/ShareBook.Api/Controllers/OperationsController.cs b/ShareBook/ShareBook.Api/Controllers/OperationsController.cs index c7df5155..fa46aa47 100644 --- a/ShareBook/ShareBook.Api/Controllers/OperationsController.cs +++ b/ShareBook/ShareBook.Api/Controllers/OperationsController.cs @@ -4,7 +4,9 @@ using Microsoft.Extensions.Options; using Sharebook.Jobs; using ShareBook.Api.Filters; +using ShareBook.Api.ViewModels; using ShareBook.Helper; +using ShareBook.Service; using ShareBook.Service.Authorization; using ShareBook.Service.Server; using System; @@ -18,11 +20,13 @@ public class OperationsController : Controller protected IJobExecutor _executor; protected string _validToken; + IEmailService _emailService; - public OperationsController(IJobExecutor executor, IOptions settings) + public OperationsController(IJobExecutor executor, IOptions settings, IEmailService emailService) { _executor = executor; _validToken = settings.Value.JobExecutorToken; + _emailService = emailService; } [HttpGet] @@ -59,6 +63,18 @@ public IActionResult Executor() return Ok(_executor.Execute()); } + [HttpPost("EmailTest")] + [Authorize("Bearer")] + [AuthorizationFilter(Permissions.Permission.ApproveBook)] // adm + public IActionResult EmailTest([FromBody] EmailTestVM emailVM) + { + if (!ModelState.IsValid) + return BadRequest(ModelState); + + _emailService.Test(emailVM.Email, emailVM.Name); + return Ok(); + } + protected bool _IsValidJobToken() => Request.Headers["Authorization"].ToString() == _validToken; } } diff --git a/ShareBook/ShareBook.Api/Middleware/ExceptionHandlerMiddleware.cs b/ShareBook/ShareBook.Api/Middleware/ExceptionHandlerMiddleware.cs index 22462811..d845ccea 100644 --- a/ShareBook/ShareBook.Api/Middleware/ExceptionHandlerMiddleware.cs +++ b/ShareBook/ShareBook.Api/Middleware/ExceptionHandlerMiddleware.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; using Rollbar; using ShareBook.Api.Services; using ShareBook.Domain.Common; @@ -29,7 +30,7 @@ public async Task Invoke(HttpContext httpContext) { var result = new Result(); result.Messages.Add(ex.Message); - var jsonResponse = JsonConvert.SerializeObject(result); + var jsonResponse = ToJson(result); httpContext.Response.Clear(); httpContext.Response.StatusCode = (int)ex.ErrorType; @@ -42,14 +43,15 @@ public async Task Invoke(HttpContext httpContext) { SendErrorToRollbar(ex); } + var result = new Result(); - result.Messages.Add(ex.Message); + result.Messages.Add(ex.ToString()); // detalhes do erro real pra facilitar o desenvolvimento. if (ex is AggregateException) result.Messages.Add(ex.InnerException.ToString()); - var jsonResponse = JsonConvert.SerializeObject(result); + var jsonResponse = ToJson(result); httpContext.Response.Clear(); httpContext.Response.StatusCode = StatusCodes.Status500InternalServerError; @@ -58,6 +60,22 @@ public async Task Invoke(HttpContext httpContext) } } + private string ToJson(Object obj) + { + DefaultContractResolver contractResolver = new DefaultContractResolver + { + NamingStrategy = new CamelCaseNamingStrategy() + }; + + string json = JsonConvert.SerializeObject(obj, new JsonSerializerSettings + { + ContractResolver = contractResolver, + Formatting = Formatting.Indented + }); + + return json; + } + private void SendErrorToRollbar(Exception ex) { object error = new diff --git a/ShareBook/ShareBook.Api/ShareBook.Api.csproj b/ShareBook/ShareBook.Api/ShareBook.Api.csproj index 524ca978..505f9542 100644 --- a/ShareBook/ShareBook.Api/ShareBook.Api.csproj +++ b/ShareBook/ShareBook.Api/ShareBook.Api.csproj @@ -21,8 +21,8 @@ - - + + diff --git a/ShareBook/ShareBook.Api/Startup.cs b/ShareBook/ShareBook.Api/Startup.cs index 04231b78..ed79ac4e 100644 --- a/ShareBook/ShareBook.Api/Startup.cs +++ b/ShareBook/ShareBook.Api/Startup.cs @@ -15,6 +15,7 @@ using ShareBook.Api.Services; using ShareBook.Repository; using ShareBook.Service; +using ShareBook.Service.AWSSQS; using ShareBook.Service.Muambator; using ShareBook.Service.Notification; using ShareBook.Service.Server; @@ -61,6 +62,8 @@ public void ConfigureServices(IServiceCollection services) services.Configure(options => Configuration.GetSection("NotificationSettings").Bind(options)); + services.Configure(options => Configuration.GetSection("AWSSQSSettings").Bind(options)); + services.AddHttpContextAccessor(); JWTConfig.RegisterJWT(services, Configuration); @@ -101,7 +104,6 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) } app.UseHealthChecks("/hc"); - app.UseDeveloperExceptionPage(); app.UseExceptionHandlerMiddleware(); app.UseStaticFiles(new StaticFileOptions() diff --git a/ShareBook/ShareBook.Api/ViewModels/BooksVM.cs b/ShareBook/ShareBook.Api/ViewModels/BooksVM.cs index 4fcb3f4f..0bb7f444 100644 --- a/ShareBook/ShareBook.Api/ViewModels/BooksVM.cs +++ b/ShareBook/ShareBook.Api/ViewModels/BooksVM.cs @@ -1,17 +1,16 @@ using System; namespace ShareBook.Api.ViewModels { - public class BooksVM + public class BookVMAdm { public Guid Id { get; set; } public string Title { get; set; } public string Author { get; set; } - public bool Donated { get; set; } public string Winner { get; set; } public string Donor { get; set; } + public Guid? UserIdFacilitator { get; set; } public string Facilitator { get; set; } public string FacilitatorNotes { get; set; } - public bool Approved { get; set; } public string PhoneDonor { get; set; } public int DaysInShowcase { get; set; } public int TotalInterested { get; set; } @@ -19,5 +18,34 @@ public class BooksVM public string TrackingNumber { get; set; } public DateTime CreationDate { get; set; } public DateTime? ChooseDate { get; set; } + public string FreightOption { get; set; } + public Guid CategoryId { get; set; } + public string Category { get; set; } + public string ImageSlug { get; set; } + public string ImageUrl { get; set; } + public string City { get; set; } + public string State { get; set; } + public string Synopsis { get; set; } + public string Slug { get; set; } + public Guid? UserId { get; set; } + } + + public class BookVM + { + public Guid Id { get; set; } + public string Title { get; set; } + public string Author { get; set; } + public string Status { get; set; } + public DateTime CreationDate { get; set; } + public DateTime? ChooseDate { get; set; } + public string FreightOption { get; set; } + public Guid CategoryId { get; set; } + public string Category { get; set; } + public string ImageSlug { get; set; } + public string ImageUrl { get; set; } + public string City { get; set; } + public string State { get; set; } + public string Synopsis { get; set; } + public string Slug { get; set; } } } diff --git a/ShareBook/ShareBook.Api/ViewModels/EmailTestVM.cs b/ShareBook/ShareBook.Api/ViewModels/EmailTestVM.cs new file mode 100644 index 00000000..5dad447e --- /dev/null +++ b/ShareBook/ShareBook.Api/ViewModels/EmailTestVM.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations; + +namespace ShareBook.Api.ViewModels +{ + public class EmailTestVM + { + [Required] + [EmailAddress] + public string Email { get; set; } + + [Required] + public string Name { get; set; } + } +} diff --git a/ShareBook/ShareBook.Api/appsettings.Development.json b/ShareBook/ShareBook.Api/appsettings.Development.json index 92be4a60..1c75b8ee 100644 --- a/ShareBook/ShareBook.Api/appsettings.Development.json +++ b/ShareBook/ShareBook.Api/appsettings.Development.json @@ -5,7 +5,7 @@ //"DefaultConnection": "Server=localhost;Database=ShareBook;Trusted_Connection=True;MultipleActiveResultSets=true" //"DefaultConnection": "Server=localhost,1433;Database=ShareBook;User Id=sa;Password=cz7YkLL09@!0*" // dev.sharebook.com.br - "DefaultConnection": "Data Source=SQL7003.site4now.net;Initial Catalog=DB_A3BA78_sharebookdev;Integrated Security=False;User ID=DB_A3BA78_sharebookdev_admin;Password=teste123@;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False", + "DefaultConnection": "Data Source=SQL7003.site4now.net;Initial Catalog=DB_A3BA78_sharebookdev;Integrated Security=False;User ID=DB_A3BA78_sharebookdev_admin;Password=teste123@;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False" }, "EmailSettings": { "HostName": "mail.sharebook.com.br", @@ -46,5 +46,16 @@ "IsActive": "false", "Token": "", "LogLevel": "Info" // Critical, Error, Warning, Info, or Debug + }, + "AWSSQSSettings": { + "IsActive": false, + "AccessKey": "", + "SecretKey": "", + "QueueUrl": "", + "Region": "", + "MaxDestinationsPerMessage": 5 + }, + "ClientSettings": { + "AndroidMinVersion": "v1.0.0" } } \ No newline at end of file diff --git a/ShareBook/ShareBook.Api/appsettings.Stage.json b/ShareBook/ShareBook.Api/appsettings.Stage.json index f5dfbac6..ffd59144 100644 --- a/ShareBook/ShareBook.Api/appsettings.Stage.json +++ b/ShareBook/ShareBook.Api/appsettings.Stage.json @@ -41,5 +41,16 @@ "IsActive": "true", "Token": "8692e979402a48a5b35ec62daadcabac", "LogLevel": "Warning" // Critical, Error, Warning, Info, or Debug + }, + "AWSSQSSettings": { + "IsActive": false, + "AccessKey": "", + "SecretKey": "", + "QueueUrl": "", + "Region": "", + "MaxDestinationsPerMessage": 5 + }, + "ClientSettings": { + "AndroidMinVersion": "v1.0.0" } } \ No newline at end of file diff --git a/ShareBook/ShareBook.Domain/Book.cs b/ShareBook/ShareBook.Domain/Book.cs index 7133a09e..08ce551b 100644 --- a/ShareBook/ShareBook.Domain/Book.cs +++ b/ShareBook/ShareBook.Domain/Book.cs @@ -38,10 +38,6 @@ public class Book : BaseEntity public Category Category { get; set; } - public bool Approved { get; set; } = false; - - public bool Canceled { get; set; } = false; - public virtual ICollection BookUsers { get; set; } public string ImageUrl { get; set; } @@ -54,8 +50,12 @@ public class Book : BaseEntity public string TrackingNumber { get; set; } - public bool Donated() - => BookUsers?.Any(x => x.Status == DonationStatus.Donated) ?? false; + public BookStatus Status { get; set; } + + public Book() + { + Status = BookStatus.WaitingApproval; + } public string Winner() => BookUsers?.FirstOrDefault(x => x.Status == DonationStatus.Donated)?.User?.Name ?? ""; @@ -63,23 +63,6 @@ public string Winner() public User WinnerUser() => BookUsers?.FirstOrDefault(x => x.Status == DonationStatus.Donated)?.User; - public BookStatus Status() - { - if (Donated()) - return BookStatus.Donated; - - if(Canceled) - return BookStatus.Canceled; - - if (Approved) - return BookStatus.Available; - - if (TotalInterested() == 0) - return BookStatus.WaitingApproval; - - return BookStatus.Invisible; - } - public int TotalInterested() { return BookUsers?.Count ?? 0; diff --git a/ShareBook/ShareBook.Domain/Common/Result.cs b/ShareBook/ShareBook.Domain/Common/Result.cs index af304a8d..01e8b6ae 100644 --- a/ShareBook/ShareBook.Domain/Common/Result.cs +++ b/ShareBook/ShareBook.Domain/Common/Result.cs @@ -7,6 +7,10 @@ namespace ShareBook.Domain.Common public class Result : Result { public Result() : base(null) { } + public Result(string SuccessMessage) : base(null) + { + this.SuccessMessage = SuccessMessage; + } } public class Result where T : class diff --git a/ShareBook/ShareBook.Domain/Enums/BookStatus.cs b/ShareBook/ShareBook.Domain/Enums/BookStatus.cs index 1b4cfbda..b88469d2 100644 --- a/ShareBook/ShareBook.Domain/Enums/BookStatus.cs +++ b/ShareBook/ShareBook.Domain/Enums/BookStatus.cs @@ -9,13 +9,23 @@ namespace ShareBook.Domain.Enums public enum BookStatus { [Description("Aguardando aprovação")] - WaitingApproval, + WaitingApproval,// Status inicial + [Description("Disponível")] - Available, - [Description("Invisível")] - Invisible, - [Description("Doado")] - Donated, + Available,// Status é usado quando o admin aprova o livro + + [Description("Aguardando decisão do doador")] + AwaitingDonorDecision,// Status para quando já expirou a qt de dias que o livro pode ficar na vitrine tendo pessoas ja interessadas em ganha-lo e o doador ainda nao escolheu o ganhador + + [Description("Aguardando envio")] + WaitingSend,// Status é usado a partir do momento que o doador escolhe um ganhador + + [Description("Enviado")] + Sent,// Status para quando o doador envia o livro + + [Description("Recebido")] + Received,// Status para quando o ganhador recebe o livro + [Description("Cancelado")] Canceled } diff --git a/ShareBook/ShareBook.Domain/Enums/JobInterval.cs b/ShareBook/ShareBook.Domain/Enums/JobInterval.cs index c14ba496..49aa6767 100644 --- a/ShareBook/ShareBook.Domain/Enums/JobInterval.cs +++ b/ShareBook/ShareBook.Domain/Enums/JobInterval.cs @@ -5,6 +5,8 @@ public enum Interval { Weekly, Dayly, - Hourly + Hourly, + Each30Minutes, + Each5Minutes, } } diff --git a/ShareBook/ShareBook.Helper/ClientVersionValidation/ClientVersionValidation.cs b/ShareBook/ShareBook.Helper/ClientVersionValidation/ClientVersionValidation.cs new file mode 100644 index 00000000..8907565b --- /dev/null +++ b/ShareBook/ShareBook.Helper/ClientVersionValidation/ClientVersionValidation.cs @@ -0,0 +1,47 @@ +using System; +using System.Text.RegularExpressions; + +namespace ShareBook.Helper +{ + static public class ClientVersionValidation + { + static public bool IsValidVersion(string version, string minVersion) + { + try + { + (int majorMin, int minorMin, int patchMin) = VersionDeconstructor(minVersion); + (int major, int minor, int patch) = VersionDeconstructor(version); + + if (major < majorMin) return false; + if (major > majorMin) return true; + + if (minor < minorMin) return false; + if (minor > minorMin) return true; + + if (patch < patchMin) return false; + else return true; + } + catch (Exception) + { + return false; + } + + } + + static private Tuple VersionDeconstructor(string version) + { + string pattern = @"v([0-9]{1,2})\.([0-9]{1,2})\.([0-9]{1,2})$"; + Regex rg = new Regex(pattern); + + MatchCollection matches = rg.Matches(version); + + if (matches.Count != 1) throw new Exception("Formato inválido"); + + var major = int.Parse(matches[0].Groups[1].Value); + var minor = int.Parse(matches[0].Groups[2].Value); + var patch = int.Parse(matches[0].Groups[3].Value); + + return Tuple.Create(major, minor, patch); + } + } +} diff --git a/ShareBook/ShareBook.Helper/ShareBook.Helper.csproj b/ShareBook/ShareBook.Helper/ShareBook.Helper.csproj index 26966507..79b820d8 100644 --- a/ShareBook/ShareBook.Helper/ShareBook.Helper.csproj +++ b/ShareBook/ShareBook.Helper/ShareBook.Helper.csproj @@ -8,4 +8,7 @@ + + + \ No newline at end of file diff --git a/ShareBook/ShareBook.Repository/Migrations/20200509195647_Refatoracao_para_Book_ter_apenas_um_campo_status.Designer.cs b/ShareBook/ShareBook.Repository/Migrations/20200509195647_Refatoracao_para_Book_ter_apenas_um_campo_status.Designer.cs new file mode 100644 index 00000000..d43d6ad8 --- /dev/null +++ b/ShareBook/ShareBook.Repository/Migrations/20200509195647_Refatoracao_para_Book_ter_apenas_um_campo_status.Designer.cs @@ -0,0 +1,374 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using ShareBook.Repository; + +namespace ShareBook.Repository.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20200509195647_Refatoracao_para_Book_ter_apenas_um_campo_status")] + partial class Refatoracao_para_Book_ter_apenas_um_campo_status + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("ShareBook.Domain.Address", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("City") + .HasColumnType("varchar(50)") + .HasMaxLength(30); + + b.Property("Complement") + .HasColumnType("varchar(50)") + .HasMaxLength(30); + + b.Property("Country") + .HasColumnType("varchar(50)") + .HasMaxLength(30); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("Neighborhood") + .HasColumnType("varchar(50)") + .HasMaxLength(30); + + b.Property("Number") + .HasColumnType("varchar(10)") + .HasMaxLength(10); + + b.Property("PostalCode") + .HasColumnType("varchar(15)") + .HasMaxLength(15); + + b.Property("State") + .HasColumnType("varchar(30)") + .HasMaxLength(30); + + b.Property("Street") + .HasColumnType("varchar(80)") + .HasMaxLength(50); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("Addresses"); + }); + + modelBuilder.Entity("ShareBook.Domain.Book", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Author") + .IsRequired() + .HasColumnType("varchar(200)") + .HasMaxLength(50); + + b.Property("CategoryId") + .HasColumnType("uniqueidentifier"); + + b.Property("ChooseDate") + .HasColumnType("datetime2"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("FacilitatorNotes") + .HasColumnType("varchar(2000)") + .HasMaxLength(2000); + + b.Property("FreightOption") + .HasColumnType("int"); + + b.Property("ImageSlug") + .IsRequired() + .HasColumnType("varchar(100)") + .HasMaxLength(100); + + b.Property("Slug") + .HasColumnType("varchar(100)") + .HasMaxLength(100); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("Synopsis") + .HasColumnType("varchar(2000)") + .HasMaxLength(2000); + + b.Property("Title") + .IsRequired() + .HasColumnType("varchar(200)") + .HasMaxLength(50); + + b.Property("TrackingNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserIdFacilitator") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("CategoryId"); + + b.HasIndex("UserId"); + + b.HasIndex("UserIdFacilitator"); + + b.ToTable("Books"); + }); + + modelBuilder.Entity("ShareBook.Domain.BookUser", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("BookId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("NickName") + .HasColumnType("varchar(64)") + .HasMaxLength(64); + + b.Property("Note") + .HasColumnType("varchar(2000)") + .HasMaxLength(2000); + + b.Property("Reason") + .HasColumnType("varchar(2000)") + .HasMaxLength(2000); + + b.Property("Status") + .HasColumnType("int"); + + b.HasKey("Id", "BookId", "UserId"); + + b.HasIndex("BookId"); + + b.HasIndex("UserId"); + + b.ToTable("BookUser"); + }); + + modelBuilder.Entity("ShareBook.Domain.Category", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(100)") + .HasMaxLength(100); + + b.HasKey("Id"); + + b.ToTable("Categories"); + }); + + modelBuilder.Entity("ShareBook.Domain.JobHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("Details") + .HasColumnType("varchar(max)"); + + b.Property("IsSuccess") + .HasColumnType("bit"); + + b.Property("JobName") + .IsRequired() + .HasColumnType("varchar(200)") + .HasMaxLength(200); + + b.Property("LastResult") + .HasColumnType("varchar(200)") + .HasMaxLength(200); + + b.Property("TimeSpentSeconds") + .HasColumnType("float"); + + b.HasKey("Id"); + + b.ToTable("JobHistories"); + }); + + modelBuilder.Entity("ShareBook.Domain.LogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("EntityId") + .HasColumnType("uniqueidentifier"); + + b.Property("EntityName") + .HasColumnType("nvarchar(max)"); + + b.Property("LogDateTime") + .HasColumnType("datetime2"); + + b.Property("Operation") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("ValuesChanges") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("LogEntries"); + }); + + modelBuilder.Entity("ShareBook.Domain.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Active") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValueSql("1"); + + b.Property("AllowSendingEmail") + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("Email") + .IsRequired() + .HasColumnType("varchar(100)") + .HasMaxLength(100); + + b.Property("HashCodePassword") + .HasColumnType("varchar(200)") + .HasMaxLength(200); + + b.Property("HashCodePasswordExpiryDate") + .HasColumnType("datetime2(7)"); + + b.Property("LastLogin") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("getdate()"); + + b.Property("Linkedin") + .HasColumnType("varchar(100)") + .HasMaxLength(100); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(200)") + .HasMaxLength(100); + + b.Property("Password") + .IsRequired() + .HasColumnType("varchar(50)") + .HasMaxLength(50); + + b.Property("PasswordSalt") + .IsRequired() + .HasColumnType("varchar(50)") + .HasMaxLength(50); + + b.Property("Phone") + .HasColumnType("varchar(30)") + .HasMaxLength(30); + + b.Property("Profile") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("ShareBook.Domain.Address", b => + { + b.HasOne("ShareBook.Domain.User", null) + .WithOne("Address") + .HasForeignKey("ShareBook.Domain.Address", "UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ShareBook.Domain.Book", b => + { + b.HasOne("ShareBook.Domain.Category", "Category") + .WithMany() + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ShareBook.Domain.User", "User") + .WithMany("BooksDonated") + .HasForeignKey("UserId"); + + b.HasOne("ShareBook.Domain.User", "UserFacilitator") + .WithMany() + .HasForeignKey("UserIdFacilitator"); + }); + + modelBuilder.Entity("ShareBook.Domain.BookUser", b => + { + b.HasOne("ShareBook.Domain.Book", "Book") + .WithMany("BookUsers") + .HasForeignKey("BookId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ShareBook.Domain.User", "User") + .WithMany("BookUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ShareBook/ShareBook.Repository/Migrations/20200509195647_Refatoracao_para_Book_ter_apenas_um_campo_status.cs b/ShareBook/ShareBook.Repository/Migrations/20200509195647_Refatoracao_para_Book_ter_apenas_um_campo_status.cs new file mode 100644 index 00000000..9f82bae6 --- /dev/null +++ b/ShareBook/ShareBook.Repository/Migrations/20200509195647_Refatoracao_para_Book_ter_apenas_um_campo_status.cs @@ -0,0 +1,43 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ShareBook.Repository.Migrations +{ + public partial class Refatoracao_para_Book_ter_apenas_um_campo_status : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Approved", + table: "Books"); + + migrationBuilder.DropColumn( + name: "Canceled", + table: "Books"); + + migrationBuilder.AddColumn( + name: "Status", + table: "Books", + nullable: false, + defaultValue: 0); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Status", + table: "Books"); + + migrationBuilder.AddColumn( + name: "Approved", + table: "Books", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "Canceled", + table: "Books", + nullable: false, + defaultValue: false); + } + } +} diff --git a/ShareBook/ShareBook.Repository/Migrations/ApplicationDbContextModelSnapshot.cs b/ShareBook/ShareBook.Repository/Migrations/ApplicationDbContextModelSnapshot.cs index f2be92ff..6d32cabe 100644 --- a/ShareBook/ShareBook.Repository/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/ShareBook/ShareBook.Repository/Migrations/ApplicationDbContextModelSnapshot.cs @@ -15,14 +15,15 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "2.2.4-servicing-10062") + .HasAnnotation("ProductVersion", "3.1.3") .HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); modelBuilder.Entity("ShareBook.Domain.Address", b => { b.Property("Id") - .ValueGeneratedOnAdd(); + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); b.Property("City") .HasColumnType("varchar(50)") @@ -36,7 +37,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("varchar(50)") .HasMaxLength(30); - b.Property("CreationDate"); + b.Property("CreationDate") + .HasColumnType("datetime2"); b.Property("Neighborhood") .HasColumnType("varchar(50)") @@ -58,7 +60,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("varchar(80)") .HasMaxLength(50); - b.Property("UserId"); + b.Property("UserId") + .HasColumnType("uniqueidentifier"); b.HasKey("Id"); @@ -71,28 +74,29 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("ShareBook.Domain.Book", b => { b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("Approved"); + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); b.Property("Author") .IsRequired() .HasColumnType("varchar(200)") .HasMaxLength(50); - b.Property("Canceled"); + b.Property("CategoryId") + .HasColumnType("uniqueidentifier"); - b.Property("CategoryId"); + b.Property("ChooseDate") + .HasColumnType("datetime2"); - b.Property("ChooseDate"); - - b.Property("CreationDate"); + b.Property("CreationDate") + .HasColumnType("datetime2"); b.Property("FacilitatorNotes") .HasColumnType("varchar(2000)") .HasMaxLength(2000); - b.Property("FreightOption"); + b.Property("FreightOption") + .HasColumnType("int"); b.Property("ImageSlug") .IsRequired() @@ -103,6 +107,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("varchar(100)") .HasMaxLength(100); + b.Property("Status") + .HasColumnType("int"); + b.Property("Synopsis") .HasColumnType("varchar(2000)") .HasMaxLength(2000); @@ -112,11 +119,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("varchar(200)") .HasMaxLength(50); - b.Property("TrackingNumber"); + b.Property("TrackingNumber") + .HasColumnType("nvarchar(max)"); - b.Property("UserId"); + b.Property("UserId") + .HasColumnType("uniqueidentifier"); - b.Property("UserIdFacilitator"); + b.Property("UserIdFacilitator") + .HasColumnType("uniqueidentifier"); b.HasKey("Id"); @@ -131,13 +141,17 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("ShareBook.Domain.BookUser", b => { - b.Property("Id"); + b.Property("Id") + .HasColumnType("uniqueidentifier"); - b.Property("BookId"); + b.Property("BookId") + .HasColumnType("uniqueidentifier"); - b.Property("UserId"); + b.Property("UserId") + .HasColumnType("uniqueidentifier"); - b.Property("CreationDate"); + b.Property("CreationDate") + .HasColumnType("datetime2"); b.Property("NickName") .HasColumnType("varchar(64)") @@ -151,7 +165,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("varchar(2000)") .HasMaxLength(2000); - b.Property("Status"); + b.Property("Status") + .HasColumnType("int"); b.HasKey("Id", "BookId", "UserId"); @@ -165,9 +180,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("ShareBook.Domain.Category", b => { b.Property("Id") - .ValueGeneratedOnAdd(); + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); - b.Property("CreationDate"); + b.Property("CreationDate") + .HasColumnType("datetime2"); b.Property("Name") .IsRequired() @@ -182,14 +199,17 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("ShareBook.Domain.JobHistory", b => { b.Property("Id") - .ValueGeneratedOnAdd(); + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); - b.Property("CreationDate"); + b.Property("CreationDate") + .HasColumnType("datetime2"); b.Property("Details") .HasColumnType("varchar(max)"); - b.Property("IsSuccess"); + b.Property("IsSuccess") + .HasColumnType("bit"); b.Property("JobName") .IsRequired() @@ -200,7 +220,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("varchar(200)") .HasMaxLength(200); - b.Property("TimeSpentSeconds"); + b.Property("TimeSpentSeconds") + .HasColumnType("float"); b.HasKey("Id"); @@ -210,21 +231,29 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("ShareBook.Domain.LogEntry", b => { b.Property("Id") - .ValueGeneratedOnAdd(); + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); - b.Property("CreationDate"); + b.Property("CreationDate") + .HasColumnType("datetime2"); - b.Property("EntityId"); + b.Property("EntityId") + .HasColumnType("uniqueidentifier"); - b.Property("EntityName"); + b.Property("EntityName") + .HasColumnType("nvarchar(max)"); - b.Property("LogDateTime"); + b.Property("LogDateTime") + .HasColumnType("datetime2"); - b.Property("Operation"); + b.Property("Operation") + .HasColumnType("nvarchar(max)"); - b.Property("UserId"); + b.Property("UserId") + .HasColumnType("uniqueidentifier"); - b.Property("ValuesChanges"); + b.Property("ValuesChanges") + .HasColumnType("nvarchar(max)"); b.HasKey("Id"); @@ -234,17 +263,20 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("ShareBook.Domain.User", b => { b.Property("Id") - .ValueGeneratedOnAdd(); + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); b.Property("Active") .ValueGeneratedOnAdd() + .HasColumnType("bit") .HasDefaultValueSql("1"); b.Property("AllowSendingEmail") - .ValueGeneratedOnAdd() + .HasColumnType("bit") .HasDefaultValue(true); - b.Property("CreationDate"); + b.Property("CreationDate") + .HasColumnType("datetime2"); b.Property("Email") .IsRequired() @@ -260,6 +292,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("LastLogin") .ValueGeneratedOnAdd() + .HasColumnType("datetime2") .HasDefaultValueSql("getdate()"); b.Property("Linkedin") @@ -285,7 +318,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("varchar(30)") .HasMaxLength(30); - b.Property("Profile"); + b.Property("Profile") + .HasColumnType("int"); b.HasKey("Id"); @@ -294,10 +328,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("ShareBook.Domain.Address", b => { - b.HasOne("ShareBook.Domain.User") + b.HasOne("ShareBook.Domain.User", null) .WithOne("Address") .HasForeignKey("ShareBook.Domain.Address", "UserId") - .OnDelete(DeleteBehavior.Cascade); + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); }); modelBuilder.Entity("ShareBook.Domain.Book", b => @@ -305,7 +340,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasOne("ShareBook.Domain.Category", "Category") .WithMany() .HasForeignKey("CategoryId") - .OnDelete(DeleteBehavior.Cascade); + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); b.HasOne("ShareBook.Domain.User", "User") .WithMany("BooksDonated") @@ -321,12 +357,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasOne("ShareBook.Domain.Book", "Book") .WithMany("BookUsers") .HasForeignKey("BookId") - .OnDelete(DeleteBehavior.Cascade); + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); b.HasOne("ShareBook.Domain.User", "User") .WithMany("BookUsers") .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); }); #pragma warning restore 612, 618 } diff --git a/ShareBook/ShareBook.Repository/ShareBookSeeder.cs b/ShareBook/ShareBook.Repository/ShareBookSeeder.cs index 2625a35f..61927e1c 100644 --- a/ShareBook/ShareBook.Repository/ShareBookSeeder.cs +++ b/ShareBook/ShareBook.Repository/ShareBookSeeder.cs @@ -10,9 +10,9 @@ public class ShareBookSeeder private readonly ApplicationDbContext _context; - // Teste123@ - private const string PASSWORD_HASH = "cWrRhnwyLmSOv3FIn7abuRevvV/GkGc1E/c66s02ujQ="; - private const string PASSWORD_SALT = "xP+CoqfrCbbfIU9HPCd4rA=="; + // 123456 + private const string PASSWORD_HASH = "n71pJuPLLg4EJkRBf+SRDXHD3x5f1sNI+3Fi5bSjdx4="; + private const string PASSWORD_SALT = "Uo5G5EKyKh5GnXy0D57i0w=="; public ShareBookSeeder(ApplicationDbContext context) @@ -134,7 +134,7 @@ public void Seed() ImageSlug = "volta-ao-mundo-em-80-dias.jpg", Slug = "volta-ao-mundo-em-80-dias", User = donor, - Approved = true, + Status = BookStatus.Available, Category = adv, CreationDate = DateTime.Now.AddDays(-1), ChooseDate = DateTime.Now.AddDays(5), @@ -150,7 +150,7 @@ public void Seed() ImageSlug = "teoria-discursiva-do-direito.jpg", Slug = "teoria-discursiva-do-direito", User = donor, - Approved = true, + Status = BookStatus.Available, Category = dir, CreationDate = DateTime.Now.AddDays(-2), ChooseDate = DateTime.Now.AddDays(5), @@ -165,7 +165,7 @@ public void Seed() ImageSlug = "the-book-of-jonah.jpg", Slug = "the-book-of-jonah", User = donor, - Approved = true, + Status = BookStatus.Available, Category = adv, CreationDate = DateTime.Now.AddDays(-2), ChooseDate = DateTime.Now.AddDays(5), @@ -180,7 +180,7 @@ public void Seed() ImageSlug = "the-hobbit.jpg", Slug = "the-hobbit", User = donor, - Approved = true, + Status = BookStatus.Available, Category = adv, CreationDate = DateTime.Now.AddDays(-2), ChooseDate = DateTime.Now.AddDays(5), @@ -195,7 +195,7 @@ public void Seed() ImageSlug = "the-hobbit-there-and-back-again.jpg", Slug = "the-hobbit-there-and-back-again", User = donor, - Approved = true, + Status = BookStatus.Available, Category = adv, CreationDate = DateTime.Now.AddDays(-2), ChooseDate = DateTime.Now.AddDays(5), @@ -210,7 +210,7 @@ public void Seed() ImageSlug = "programando-o-android.jpg", Slug = "programando-o-android", User = donor, - Approved = true, + Status = BookStatus.Available, Category = inf, CreationDate = DateTime.Now.AddDays(-1), ChooseDate = DateTime.Now.AddDays(5), @@ -225,7 +225,7 @@ public void Seed() ImageSlug = "senhor-dos-aneis.jpg", Slug = "senhor-dos-aneis", User = donor, - Approved = true, + Status = BookStatus.Available, Category = adv, CreationDate = DateTime.Now.AddDays(-5), ChooseDate = DateTime.Now.AddDays(5), @@ -240,7 +240,7 @@ public void Seed() ImageSlug = "se-venden-gorras.jpg", Slug = "se-venden-gorras", User = donor, - Approved = true, + Status = BookStatus.Available, Category = adv, CreationDate = DateTime.Now.AddDays(-6), ChooseDate = DateTime.Now.AddDays(5), @@ -255,7 +255,7 @@ public void Seed() ImageSlug = "star-wars.jpg", Slug = "star-wars", User = donor, - Approved = true, + Status = BookStatus.Available, Category = adv, CreationDate = DateTime.Now.AddDays(-8), ChooseDate = DateTime.Now.AddDays(5), @@ -271,7 +271,7 @@ public void Seed() ImageSlug = "programacao-de-redes-com-python.jpg", Slug = "programacao-de-redes-com-python", User = donor, - Approved = true, + Status = BookStatus.Available, Category = inf, CreationDate = DateTime.Now.AddDays(-5), ChooseDate = DateTime.Now.AddDays(5), @@ -286,7 +286,7 @@ public void Seed() ImageSlug = "programacao-de-jogo-android.jpg", Slug = "programacao-de-jogo-android", User = donor, - Approved = true, + Status = BookStatus.Available, Category = inf, CreationDate = DateTime.Now.AddDays(-10), ChooseDate = DateTime.Now.AddDays(5), @@ -301,7 +301,7 @@ public void Seed() ImageSlug = "percy-jackson-e-os-olimpianos.jpg", Slug = "percy-jackson-e-os-olimpianos", User = donor, - Approved = true, + Status = BookStatus.Available, Category = adv, CreationDate = DateTime.Now.AddDays(-2), ChooseDate = DateTime.Now.AddDays(5), @@ -316,7 +316,7 @@ public void Seed() ImageSlug = "os-vingadores.jpg", Slug = "os-vingadores", User = donor, - Approved = true, + Status = BookStatus.Available, Category = adv, CreationDate = DateTime.Now.AddDays(-9), ChooseDate = DateTime.Now.AddDays(5), @@ -331,7 +331,7 @@ public void Seed() ImageSlug = "os-sete.jpg", Slug = "os-sete", User = donor, - Approved = true, + Status = BookStatus.Available, Category = adv, CreationDate = DateTime.Now.AddDays(-1), ChooseDate = DateTime.Now.AddDays(5), @@ -346,7 +346,7 @@ public void Seed() ImageSlug = "o-segredo-das-sombras.jpg", Slug = "o-segredo-das-sombras", User = donor, - Approved = true, + Status = BookStatus.Available, Category = adv, CreationDate = DateTime.Now.AddDays(-2), ChooseDate = DateTime.Now.AddDays(5), @@ -361,7 +361,7 @@ public void Seed() ImageSlug = "orgulho-e-preconceito.jpg", Slug = "orgulho-e-preconceito", User = donor, - Approved = true, + Status = BookStatus.Available, Category = adv, CreationDate = DateTime.Now.AddDays(-3), ChooseDate = DateTime.Now.AddDays(5), @@ -376,7 +376,7 @@ public void Seed() ImageSlug = "o-retorno-do-rei.png", Slug = "o-retorno-do-rei", User = donor, - Approved = true, + Status = BookStatus.Available, Category = adv, CreationDate = DateTime.Now.AddDays(-1), ChooseDate = DateTime.Now.AddDays(5), @@ -391,7 +391,7 @@ public void Seed() ImageSlug = "o-pequeno-principe.jpg", Slug = "o-pequeno-principe", User = donor, - Approved = true, + Status = BookStatus.Available, Category = adv, CreationDate = DateTime.Now.AddDays(-3), ChooseDate = DateTime.Now.AddDays(5), @@ -406,7 +406,7 @@ public void Seed() ImageSlug = "o-cortico.jpg", Slug = "o-cortico", User = donor, - Approved = true, + Status = BookStatus.Available, Category = adv, CreationDate = DateTime.Now.AddDays(-4), ChooseDate = DateTime.Now.AddDays(5), @@ -421,7 +421,7 @@ public void Seed() ImageSlug = "nunca-jamais.jpg", Slug = "nunca-jamais", User = donor, - Approved = true, + Status = BookStatus.Available, Category = adv, CreationDate = DateTime.Now.AddDays(-5), ChooseDate = DateTime.Now.AddDays(5), @@ -436,7 +436,7 @@ public void Seed() ImageSlug = "100-segredos-das-pessoas-felizes.jpg", Slug = "100-segredos-das-pessoas-felizes", User = donor, - Approved = true, + Status = BookStatus.Available, Category = psico, CreationDate = DateTime.Now.AddDays(-2), ChooseDate = DateTime.Now.AddDays(5), @@ -452,7 +452,7 @@ public void Seed() ImageSlug = "a-furia-dos-reis.jpg", Slug = "a-furia-dos-reis", User = donor, - Approved = true, + Status = BookStatus.Available, Category = adv, CreationDate = DateTime.Now.AddDays(-4), ChooseDate = DateTime.Now.AddDays(5), @@ -467,7 +467,7 @@ public void Seed() ImageSlug = "a-menina-que-roubava-livros.jpg", Slug = "a-menina-que-roubava-livros", User = donor, - Approved = true, + Status = BookStatus.Available, Category = adv, CreationDate = DateTime.Now.AddDays(-2), ChooseDate = DateTime.Now.AddDays(5), diff --git a/ShareBook/ShareBook.Service/AWSSQS/AWSSQSService.cs b/ShareBook/ShareBook.Service/AWSSQS/AWSSQSService.cs new file mode 100644 index 00000000..1edda255 --- /dev/null +++ b/ShareBook/ShareBook.Service/AWSSQS/AWSSQSService.cs @@ -0,0 +1,84 @@ +using Amazon.Runtime; +using Amazon.SQS; +using Amazon.SQS.Model; +using Microsoft.Extensions.Options; +using ShareBook.Service.AWSSQS.Dto; +using System; +using System.Threading.Tasks; + +namespace ShareBook.Service.AWSSQS +{ + public class AWSSQSService : IAWSSQSService + { + private readonly AWSSQSSettings _AWSSQSSettings; + private readonly AmazonSQSClient _amazonSQSClient; + + public AWSSQSService(IOptions AWSSQSSettings) + { + _AWSSQSSettings = AWSSQSSettings.Value; + + if (_AWSSQSSettings.IsActive) + { + // usando padrão Reflection + var region = (Amazon.RegionEndpoint)typeof(Amazon.RegionEndpoint).GetField(_AWSSQSSettings.Region).GetValue(null); + + var awsCreds = new BasicAWSCredentials(AWSSQSSettings.Value.AccessKey, AWSSQSSettings.Value.SecretKey); + _amazonSQSClient = new AmazonSQSClient(awsCreds, region); + } + } + + public async Task DeleteNewBookNotifyFromAWSSQSAsync(string receiptHandle) + { + if (!_AWSSQSSettings.IsActive) + { + throw new Exception("Serviço aws está desabilitado no appsettings."); + } + + var deleteMessageRequest = new DeleteMessageRequest(); + + deleteMessageRequest.QueueUrl = _AWSSQSSettings.QueueUrl; + deleteMessageRequest.ReceiptHandle = receiptHandle + "aaa"; + + await _amazonSQSClient.DeleteMessageAsync(deleteMessageRequest); + } + + public async Task GetNewBookNotifyFromAWSSQSAsync() + { + if (!_AWSSQSSettings.IsActive) + { + throw new Exception("Serviço aws está desabilitado no appsettings."); + } + + var receiveMessageRequest = new ReceiveMessageRequest(_AWSSQSSettings.QueueUrl); + + var result = await _amazonSQSClient.ReceiveMessageAsync(receiveMessageRequest); + + if (result.Messages.Count > 0) + { + var firstMessageTemp = result.Messages[0].Body; + var firstMessage = System.Text.Json.JsonSerializer.Deserialize(firstMessageTemp); + firstMessage.ReceiptHandle = result.Messages[0].ReceiptHandle; + return firstMessage; + } + else + { + return null; + } + } + + public async Task SendNewBookNotifyToAWSSQSAsync(AWSSQSMessageNewBookNotifyRequest message) + { + if (_AWSSQSSettings.IsActive) + { + var request = new SendMessageRequest + { + DelaySeconds = (int)TimeSpan.FromSeconds(5).TotalSeconds, + MessageBody = System.Text.Json.JsonSerializer.Serialize(message), + QueueUrl = _AWSSQSSettings.QueueUrl + }; + + await _amazonSQSClient.SendMessageAsync(request); + } + } + } +} \ No newline at end of file diff --git a/ShareBook/ShareBook.Service/AWSSQS/AWSSQSSettings.cs b/ShareBook/ShareBook.Service/AWSSQS/AWSSQSSettings.cs new file mode 100644 index 00000000..438dca3a --- /dev/null +++ b/ShareBook/ShareBook.Service/AWSSQS/AWSSQSSettings.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ShareBook.Service.AWSSQS +{ + public class AWSSQSSettings + { + public bool IsActive { get; set; } + public string AccessKey { get; set; } + public string SecretKey { get; set; } + public string QueueUrl { get; set; } + public string Region { get; set; } + } +} diff --git a/ShareBook/ShareBook.Service/AWSSQS/Dto/AWSSQSMessageNewBookNotifyRequest.cs b/ShareBook/ShareBook.Service/AWSSQS/Dto/AWSSQSMessageNewBookNotifyRequest.cs new file mode 100644 index 00000000..772890a1 --- /dev/null +++ b/ShareBook/ShareBook.Service/AWSSQS/Dto/AWSSQSMessageNewBookNotifyRequest.cs @@ -0,0 +1,20 @@ +using AutoMapper.Configuration.Conventions; +using System; +using System.Collections.Generic; +using System.Text; + +namespace ShareBook.Service.AWSSQS.Dto +{ + public class AWSSQSMessageNewBookNotifyRequest + { + public string Subject { get; set; } + public string BodyHTML { get; set; } + public IList Destinations { get; set; } + } + + public class DestinationRequest + { + public string Name { get; set; } + public string Email { get; set; } + } +} \ No newline at end of file diff --git a/ShareBook/ShareBook.Service/AWSSQS/Dto/AWSSQSMessageNewBookNotifyResponse.cs b/ShareBook/ShareBook.Service/AWSSQS/Dto/AWSSQSMessageNewBookNotifyResponse.cs new file mode 100644 index 00000000..69a2c854 --- /dev/null +++ b/ShareBook/ShareBook.Service/AWSSQS/Dto/AWSSQSMessageNewBookNotifyResponse.cs @@ -0,0 +1,21 @@ +using AutoMapper.Configuration.Conventions; +using System; +using System.Collections.Generic; +using System.Text; + +namespace ShareBook.Service.AWSSQS.Dto +{ + public class AWSSQSMessageNewBookNotifyResponse + { + public string ReceiptHandle { get; set; } + public string Subject { get; set; } + public string BodyHTML { get; set; } + public IList Destinations { get; set; } + } + + public class DestinationResponse + { + public string Name { get; set; } + public string Email { get; set; } + } +} \ No newline at end of file diff --git a/ShareBook/ShareBook.Service/AWSSQS/IAWSSQSService.cs b/ShareBook/ShareBook.Service/AWSSQS/IAWSSQSService.cs new file mode 100644 index 00000000..b2700202 --- /dev/null +++ b/ShareBook/ShareBook.Service/AWSSQS/IAWSSQSService.cs @@ -0,0 +1,13 @@ +using ShareBook.Service.AWSSQS.Dto; +using System; +using System.Threading.Tasks; + +namespace ShareBook.Service.AWSSQS +{ + public interface IAWSSQSService + { + Task SendNewBookNotifyToAWSSQSAsync(AWSSQSMessageNewBookNotifyRequest message); + Task GetNewBookNotifyFromAWSSQSAsync(); + Task DeleteNewBookNotifyFromAWSSQSAsync(string receiptHandle); + } +} \ No newline at end of file diff --git a/ShareBook/ShareBook.Service/Book/BookService.cs b/ShareBook/ShareBook.Service/Book/BookService.cs index caf76bc4..23283864 100644 --- a/ShareBook/ShareBook.Service/Book/BookService.cs +++ b/ShareBook/ShareBook.Service/Book/BookService.cs @@ -8,7 +8,6 @@ using ShareBook.Helper.Extensions; using ShareBook.Helper.Image; using ShareBook.Repository; -using ShareBook.Repository.Repository; using ShareBook.Repository.UoW; using ShareBook.Service.Generic; using ShareBook.Service.Upload; @@ -34,28 +33,32 @@ public BookService(IBookRepository bookRepository, _booksEmailService = booksEmailService; } - public Result Approve(Guid bookId, DateTime? chooseDate = null) + public void Approve(Guid bookId, DateTime? chooseDate = null) { - var book = _repository.Find(bookId); + var book = _repository.Get().Include(b => b.Category).FirstOrDefault(b => b.Id == bookId); if (book == null) throw new ShareBookException(ShareBookException.Error.NotFound); - book.Approved = true; + book.Status = BookStatus.Available; book.ChooseDate = chooseDate?.Date ?? DateTime.Today.AddDays(5); _repository.Update(book); - _booksEmailService.SendEmailBookApproved(book).Wait(); + // notifica o doador + _booksEmailService.SendEmailBookApproved(book); - return new Result(book); + // notifica possíveis interessados + _booksEmailService.SendEmailBookToInterestedUsers(book); } - public void HideBook(Guid bookId) + + + public void UpdateBookStatus(Guid bookId, BookStatus bookStatus) { var book = _repository.Find(bookId); if (book == null) throw new ShareBookException(ShareBookException.Error.NotFound); - book.Approved = false; + book.Status = bookStatus; _repository.Update(book); } @@ -73,16 +76,37 @@ public IList FreightOptions() return enumValues; } - // TODO: renomar para um nome mais significativo. Talvez: Showcase (vitrine) - public IList Top15NewBooks() - => SearchBooks(x => x.Approved && !x.Canceled - && !x.BookUsers.Any(y => y.Status == DonationStatus.Donated), 1, 99) // não precisamos de limite. Ainda mais levando em consideração a DOAÇÃO RÁPIDA. - .Items; + public IList AvailableBooks() + { + return SetImageUrl( + _repository.Get() + .Include(b => b.User) + .ThenInclude(u => u.Address) + .Include(b => b.Category) + .Where(b => b.Status == BookStatus.Available) + .ToList() + ); + } public IList Random15Books() - => SearchBooks(x => x.Approved && !x.Canceled - && !x.BookUsers.Any(y => y.Status == DonationStatus.Donated), 1, 15, x => Guid.NewGuid()) - .Items; + { + return SetImageUrl( + _repository.Get() + .Include(b => b.User) + .ThenInclude(u => u.Address) + .Include(b => b.Category) + .Where(b => b.Status == BookStatus.Available) + .OrderBy(x => Guid.NewGuid()) // ordem aleatória + .Take(15) // apenas 15 registros + .ToList() + ); + } + + private IList SetImageUrl(IList books) + { + return books.Select(b => { b.ImageUrl = _uploadService.GetImageUrl(b.ImageSlug, "Books"); return b; }).ToList(); + } + public IList GetAll(int page, int items) => _repository.Get().Include(b => b.User).Include(b => b.BookUsers) @@ -91,7 +115,13 @@ public IList GetAll(int page, int items) public override Book Find(object keyValue) { - var result = _repository.Find(keyValue); + var result = _repository.Get() + .Include(b => b.User) + .ThenInclude(u => u.Address) + .Include(b => b.Category) + .Include(b => b.UserFacilitator) + .Where(b => b.Id == (Guid)keyValue) + .FirstOrDefault(); if (result == null) throw new ShareBookException(ShareBookException.Error.NotFound); @@ -128,7 +158,6 @@ public override Result Update(Book entity) Result result = Validate(entity, x => x.Title, x => x.Author, - x => x.Approved, x => x.FreightOption, x => x.Id); @@ -142,11 +171,6 @@ public override Result Update(Book entity) if (savedBook == null) throw new ShareBookException(ShareBookException.Error.NotFound); - //Como o objeto foi buscado diretamente do banco, removi a consulta e tratei diretamente no objeto - var bookAlreadyApproved = savedBook.Approved; - - if (!bookAlreadyApproved) - entity.Slug = SetSlugByTitleOrIncremental(entity); //imagem eh opcional no update if (!string.IsNullOrEmpty(entity.ImageName) && entity.ImageBytes.Length > 0) @@ -158,12 +182,15 @@ public override Result Update(Book entity) //preparar o book para atualização savedBook.Author = entity.Author; savedBook.FreightOption = entity.FreightOption; - savedBook.Approved = entity.Approved; savedBook.Author = entity.Author; savedBook.ImageSlug = entity.ImageSlug; savedBook.Title = entity.Title; savedBook.CategoryId = entity.CategoryId; - savedBook.Canceled = entity.Canceled; + + // Condição efetuada para evitar busca no BD desnecessariamente por conta do SetSlugByTitleOrIncremental() + if (savedBook.Slug != entity.Slug) + savedBook.Slug = SetSlugByTitleOrIncremental(entity); + savedBook.Synopsis = entity.Synopsis; savedBook.TrackingNumber = entity.TrackingNumber; @@ -174,32 +201,15 @@ public override Result Update(Book entity) result.Value = _repository.UpdateAsync(savedBook).Result; result.Value.ImageBytes = null; - // TODO: pedir pro pessoal de front usar o endpoint correto de aprovação. - if (!bookAlreadyApproved && entity.Approved) - { - this.Approve(entity.Id); - } - return result; } - public PagedList ByTitle(string title, int page, int itemsPerPage) - => SearchBooks(x => (x.Approved - && !x.BookUsers.Any(y => y.Status == DonationStatus.Donated)) - && x.Title.Contains(title), page, itemsPerPage); - - public PagedList ByAuthor(string author, int page, int itemsPerPage) - => SearchBooks(x => (x.Approved - && !x.BookUsers.Any(y => y.Status == DonationStatus.Donated)) - && x.Author.Contains(author), page, itemsPerPage); - public PagedList FullSearch(string criteria, int page, int itemsPerPage, bool isAdmin) { Expression> filter = x => (x.Author.Contains(criteria) || x.Title.Contains(criteria) || x.Category.Name.Contains(criteria)) - && x.Approved - && !x.BookUsers.Any(y => y.Status == DonationStatus.Donated); + && x.Status == BookStatus.Available; if (!isAdmin) filter = x => x.Author.Contains(criteria) || x.Title.Contains(criteria) @@ -209,9 +219,7 @@ public PagedList FullSearch(string criteria, int page, int itemsPerPage, b } public PagedList ByCategoryId(Guid categoryId, int page, int itemsPerPage) - => SearchBooks(x => (x.Approved - && !x.BookUsers.Any(y => y.Status == DonationStatus.Donated)) - && x.CategoryId == categoryId, page, itemsPerPage); + => SearchBooks(x => x.Status == BookStatus.Available && x.CategoryId == categoryId, page, itemsPerPage); public Book BySlug(string slug) { @@ -273,7 +281,7 @@ public IList GetBooksChooseDateIsLate() var booksLate = new List(); foreach (var book in books) { - if (book.Status() == BookStatus.Available || book.Status() == BookStatus.Invisible) + if (book.Status == BookStatus.Available || book.Status == BookStatus.AwaitingDonorDecision) booksLate.Add(book); } @@ -315,7 +323,7 @@ public void RenewChooseDate(Guid bookId) if (!book.MayChooseWinner()) throw new ShareBookException(ShareBookException.Error.BadRequest, "Aguarde a data de decisão."); - book.Approved = true; + book.Status = BookStatus.Available; book.ChooseDate = DateTime.Now.AddDays(10); _repository.Update(book); } @@ -335,8 +343,7 @@ private PagedList SearchBooks(Expression> filter, i Id = u.Id, Title = u.Title, Author = u.Author, - Approved = u.Approved, - Canceled = u.Canceled, + Status = u.Status, FreightOption = u.FreightOption, ImageUrl = _uploadService.GetImageUrl(u.ImageSlug, "Books"), Slug = u.Slug, @@ -366,16 +373,8 @@ private PagedList SearchBooks(Expression> filter, i return FormatPagedList(query, page, itemsPerPage); } - private bool BookAlreadyApproved(Guid bookId) - => _repository.Any(x => x.Approved && x.Id == bookId); - private string SetSlugByTitleOrIncremental(Book entity) { - //var slug = _repository.Get() - // .Where(x => x.Title.ToUpperInvariant().Equals(entity.Title.ToUpperInvariant()) - // && !x.Id.Equals(entity.Id)) - // .OrderByDescending(x => x.CreationDate)?.FirstOrDefault()?.Slug; - var slug = _repository.Get() .Where(x => x.Title.ToUpper().Trim().Equals(entity.Title.ToUpper().Trim()) && !x.Id.Equals(entity.Id)) diff --git a/ShareBook/ShareBook.Service/Book/BooksEmailService.cs b/ShareBook/ShareBook.Service/Book/BooksEmailService.cs index e6fdfa2d..fac3f553 100644 --- a/ShareBook/ShareBook.Service/Book/BooksEmailService.cs +++ b/ShareBook/ShareBook.Service/Book/BooksEmailService.cs @@ -1,4 +1,14 @@ -using ShareBook.Domain; +using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Options; +using Org.BouncyCastle.Math.EC.Rfc7748; +using ShareBook.Domain; +using ShareBook.Service.AWSSQS; +using ShareBook.Service.AWSSQS.Dto; +using ShareBook.Service.Server; +using System; +using System.Linq; +using System.Security.Cryptography; using System.Threading.Tasks; namespace ShareBook.Service @@ -11,17 +21,32 @@ public class BooksEmailService : IBooksEmailService private const string WaitingApprovalTitle = "Aguarde aprovação do livro - Sharebook"; private const string BookApprovedTemplate = "BookApprovedTemplate"; private const string BookApprovedTitle = "Livro aprovado - Sharebook"; + private const string NewBookNotifyTemplate = "NewBookNotifyTemplate"; + private readonly IEmailService _emailService; private readonly IUserService _userService; private readonly IEmailTemplate _emailTemplate; - public BooksEmailService(IEmailService emailService, IUserService userService, IEmailTemplate emailTemplate) + private readonly IAWSSQSService _AWSSQSService; + private readonly ServerSettings _serverSettings; + private readonly IConfiguration _configuration; + + public BooksEmailService( + IEmailService emailService, + IUserService userService, + IEmailTemplate emailTemplate, + IAWSSQSService AWSSQSService, + IOptions serverSettings, + IConfiguration configuration) { _emailService = emailService; _userService = userService; _emailTemplate = emailTemplate; + _AWSSQSService = AWSSQSService; + _serverSettings = serverSettings.Value; + _configuration = configuration; } - public async Task SendEmailBookApproved(Book book) + public void SendEmailBookApproved(Book book) { if (book.User == null) book.User = _userService.Find(book.UserId); @@ -34,8 +59,42 @@ public async Task SendEmailBookApproved(Book book) book.User, ChooseDate = book.ChooseDate?.ToString("dd/MM/yyyy") }; - var html = await _emailTemplate.GenerateHtmlFromTemplateAsync(BookApprovedTemplate, vm); - await _emailService.Send(book.User.Email, book.User.Name, html, BookApprovedTitle, true); + var html = _emailTemplate.GenerateHtmlFromTemplateAsync(BookApprovedTemplate, vm).Result; + _emailService.Send(book.User.Email, book.User.Name, html, BookApprovedTitle, true); + } + } + + public async Task SendEmailBookToInterestedUsers(Book book) + { + int MAX_DESTINATIONS = int.Parse(_configuration["AWSSQSSettings:MaxDestinationsPerMessage"]); + + var vm = new + { + Book = book, + ServerSettings = _serverSettings, + name = "{name}"// recebe {name} pois e o padrao que o servico Consumer reconhece para substituicao para o nome do usuario para o qual ira enviar o email + }; + + var html = await _emailTemplate.GenerateHtmlFromTemplateAsync(NewBookNotifyTemplate, vm); + + var message = new AWSSQSMessageNewBookNotifyRequest + { + Subject = $"Chegou um livro de {book.Category.Name}", + BodyHTML = html + }; + + + var interestedUsers = _userService.GetBySolicitedBookCategory(book.CategoryId); + + + int maxMessages = interestedUsers.Count() % MAX_DESTINATIONS == 0 ? interestedUsers.Count() / MAX_DESTINATIONS : interestedUsers.Count() / MAX_DESTINATIONS + 1; + + for(int i = 1; i <= maxMessages; i++) + { + var destinations = interestedUsers.Skip((i - 1) * MAX_DESTINATIONS).Take(MAX_DESTINATIONS).Select(u => new DestinationRequest { Name = u.Name, Email = u.Email }); + message.Destinations = destinations.ToList(); + + await _AWSSQSService.SendNewBookNotifyToAWSSQSAsync(message); } } diff --git a/ShareBook/ShareBook.Service/Book/IBookService.cs b/ShareBook/ShareBook.Service/Book/IBookService.cs index 48a784ca..ff8c8673 100644 --- a/ShareBook/ShareBook.Service/Book/IBookService.cs +++ b/ShareBook/ShareBook.Service/Book/IBookService.cs @@ -1,5 +1,6 @@ using ShareBook.Domain; using ShareBook.Domain.Common; +using ShareBook.Domain.Enums; using ShareBook.Service.Generic; using System; using System.Collections.Generic; @@ -8,20 +9,16 @@ namespace ShareBook.Service { public interface IBookService : IBaseService { - Result Approve(Guid bookId, DateTime? chooseDate); + void Approve(Guid bookId, DateTime? chooseDate); - void HideBook(Guid bookId); + void UpdateBookStatus(Guid bookId, BookStatus bookStatus); IList FreightOptions(); - IList Top15NewBooks(); + IList AvailableBooks(); IList Random15Books(); - PagedList ByTitle(string title, int page, int itemsPerPage); - - PagedList ByAuthor(string author, int page, int itemsPerPage); - PagedList FullSearch(string criteria, int page, int itemsPerPage, bool isAdmin = false); PagedList ByCategoryId(Guid categoryId, int page, int items); diff --git a/ShareBook/ShareBook.Service/Book/IBooksEmailService.cs b/ShareBook/ShareBook.Service/Book/IBooksEmailService.cs index 3d9d023e..d5b0a1d4 100644 --- a/ShareBook/ShareBook.Service/Book/IBooksEmailService.cs +++ b/ShareBook/ShareBook.Service/Book/IBooksEmailService.cs @@ -7,7 +7,9 @@ public interface IBooksEmailService { Task SendEmailNewBookInserted(Book book); - Task SendEmailBookApproved(Book book); + void SendEmailBookApproved(Book book); + + Task SendEmailBookToInterestedUsers(Book book); } } \ No newline at end of file diff --git a/ShareBook/ShareBook.Service/BookUser/BookUserService.cs b/ShareBook/ShareBook.Service/BookUser/BookUserService.cs index e95fc901..a3401bae 100644 --- a/ShareBook/ShareBook.Service/BookUser/BookUserService.cs +++ b/ShareBook/ShareBook.Service/BookUser/BookUserService.cs @@ -74,7 +74,7 @@ public void Insert(Guid bookId, string reason) if (_bookUserRepository.Any(x => x.UserId == bookUser.UserId && x.BookId == bookUser.BookId)) throw new ShareBookException("O usuário já possui uma requisição para o mesmo livro."); - if (bookRequested.Status() != BookStatus.Available) + if (bookRequested.Status != BookStatus.Available) throw new ShareBookException("Esse livro não está mais disponível para doação."); _bookUserRepository.Insert(bookUser); @@ -108,7 +108,7 @@ public void DonateBook(Guid bookId, Guid userId, string note) DeniedBookUsers(bookId); - _bookService.HideBook(bookId); + _bookService.UpdateBookStatus(bookId, BookStatus.WaitingSend); // não usamos await nas notificações, pra serem assíncronas de verdade e retornar mais rápido. @@ -134,9 +134,8 @@ public Result Cancel(Guid bookId, bool isAdmin = false) if (!isAdmin && bookUsers != null && bookUsers.Count > 0) throw new ShareBookException("Este livro já possui interessados"); - book.Approved = false; book.ChooseDate = null; - book.Canceled = true; + book.Status = BookStatus.Canceled; CancelBookUsersAndSendNotification(book); @@ -226,6 +225,7 @@ public void InformTrackingNumber(Guid bookId, string trackingNumber) if(MuambatorConfigurator.IsActive) _muambatorService.AddPackageToTrackerAsync(book, winnerBookUser.User, trackingNumber); + book.Status = BookStatus.Sent; book.TrackingNumber = trackingNumber; _bookService.Update(book); diff --git a/ShareBook/ShareBook.Service/Email/EmailService.cs b/ShareBook/ShareBook.Service/Email/EmailService.cs index e44f36a6..3e1a7add 100644 --- a/ShareBook/ShareBook.Service/Email/EmailService.cs +++ b/ShareBook/ShareBook.Service/Email/EmailService.cs @@ -94,5 +94,12 @@ private InternetAddressList GetAdminEmails() return list; } + public async void Test(string email, string name) + { + var subject = "Sharebook - teste de email"; + var message = $"

Olá {name},

Esse é um email de teste para verificar se o sharebook consegue fazer contato com você. Por favor avise o facilitador quando esse email chegar. Obrigado.

"; + await this.Send(email, name, message, subject); + } + } } diff --git a/ShareBook/ShareBook.Service/Email/IEmailService.cs b/ShareBook/ShareBook.Service/Email/IEmailService.cs index 3d4af0d1..fc0c8ae4 100644 --- a/ShareBook/ShareBook.Service/Email/IEmailService.cs +++ b/ShareBook/ShareBook.Service/Email/IEmailService.cs @@ -7,5 +7,6 @@ public interface IEmailService Task SendToAdmins(string messageText, string subject); Task Send(string emailRecipient, string nameRecipient, string messageText, string subject); Task Send(string emailRecipient, string nameRecipient, string messageText, string subject, bool copyAdmins = false); + void Test(string email, string name); } } diff --git a/ShareBook/ShareBook.Service/Email/Templates/NewBookNotifyTemplate.html b/ShareBook/ShareBook.Service/Email/Templates/NewBookNotifyTemplate.html new file mode 100644 index 00000000..2fd5167b --- /dev/null +++ b/ShareBook/ShareBook.Service/Email/Templates/NewBookNotifyTemplate.html @@ -0,0 +1,807 @@ + + + + + + + Novo Livro Gratuito + + + + + +
+ + + + +
+ + + + + + + + +
+ + + + +
+ + + + + + +
+ + + + + + + +
+ +

{Book.Title} - Livro grátis.

+ +

+ Olá, {name}!
+
+ Aqui é o Sharebot do sharebook. Tudo bem?
+ Chegou um esse livro novo e eu lembrei de você. Espero que goste. :-)
+
+ +

+ +
+
+ + + + + +
+ + + + + + +
+ +
+
+ + + + + +
+ + + + + + +
+ Eu quero esse livro! +
+
+ + + + + +
+ + + + + + +
+ +
+ +
+ + + + + +
+ + + + + + +
+ +
+ +
+ + + + + +
+ + + + + + +
+ +
+ +
+ + + + + +
+ + + + + + +
+ +
+ +
+ + + + + + +
+ + + + + + + + + +
+ + + + + + + +
+

É um prazer ajudar.

+ +

Tem alguma dúvida ou sugestão? Basta apenas responder esse email que a gente responde de volta. Simples assim. :-)

+ +
+
+ +
+
+ +
+ + + + + +
+ + + + + + +
+ + + + + + +
+ + + + + + +
+ + + + + + +
+ + + + + + + + + +
+ + + + + + +
+ + + + + + + + + +
+ +
+
+
+ + + + + + + + + +
+ + + + + + +
+ + + + + + + + + +
+ +
+
+
+ + + + + + + + + + +
+ + + + + + +
+ + + + + + + + + +
+ +
+
+
+ + +
+
+
+ +
+ + + + + +
+ + + + + + +
+ +
+ +
+ + + + + +
+ + + + + + + + +
+ + + Copyleft © 2020 Sharebook, All rights open source. + +
+ + +
+ +
+
+ +
+ +
+
+ + \ No newline at end of file diff --git a/ShareBook/ShareBook.Service/ShareBook.Service.csproj b/ShareBook/ShareBook.Service/ShareBook.Service.csproj index a3739fe3..9969a927 100644 --- a/ShareBook/ShareBook.Service/ShareBook.Service.csproj +++ b/ShareBook/ShareBook.Service/ShareBook.Service.csproj @@ -6,6 +6,8 @@ + + @@ -22,6 +24,9 @@ + + PreserveNewest + PreserveNewest diff --git a/ShareBook/ShareBook.Service/User/IUserService.cs b/ShareBook/ShareBook.Service/User/IUserService.cs index 99bdfb30..98534e91 100644 --- a/ShareBook/ShareBook.Service/User/IUserService.cs +++ b/ShareBook/ShareBook.Service/User/IUserService.cs @@ -15,5 +15,6 @@ public interface IUserService : IBaseService Result GenerateHashCodePasswordAndSendEmailToUser(string email); Result ConfirmHashCodePassword(string hashCodePassword); IList GetFacilitators(Guid userIdDonator); + IList GetBySolicitedBookCategory(Guid BookCategoryId); } } diff --git a/ShareBook/ShareBook.Service/User/UserService.cs b/ShareBook/ShareBook.Service/User/UserService.cs index 0d9764f3..8d86150e 100644 --- a/ShareBook/ShareBook.Service/User/UserService.cs +++ b/ShareBook/ShareBook.Service/User/UserService.cs @@ -41,6 +41,12 @@ public Result AuthenticationByEmailAndPassword(User user) user = _repository.Find(e => e.Email == user.Email); + if (user == null) + { + result.Messages.Add("Não encontramos esse email no Sharebook. Você já se cadastrou?"); + return result; + } + if (user.IsBruteForceLogin()) { result.Messages.Add("Login bloqueado por 30 segundos, para proteger sua conta."); @@ -52,7 +58,7 @@ public Result AuthenticationByEmailAndPassword(User user) user.LastLogin = DateTime.Now; _userRepository.Update(user); - if (user == null || !IsValidPassword(user, decryptedPass)) + if (!IsValidPassword(user, decryptedPass)) { result.Messages.Add("Email ou senha incorretos"); return result; @@ -251,6 +257,10 @@ private User UserCleanup(User user) return user; } + public IList GetBySolicitedBookCategory(Guid bookCategoryId) => + _userRepository.Get().Where(u => u.AllowSendingEmail && u.BookUsers.Any(bu => bu.Book.CategoryId == bookCategoryId)).ToList(); + + #endregion Private } } \ No newline at end of file diff --git a/ShareBook/ShareBook.Test.Unit/Domain/BookTests.cs b/ShareBook/ShareBook.Test.Unit/Domain/BookTests.cs index dece11ff..8c8f541b 100644 --- a/ShareBook/ShareBook.Test.Unit/Domain/BookTests.cs +++ b/ShareBook/ShareBook.Test.Unit/Domain/BookTests.cs @@ -13,52 +13,7 @@ public class BookTests public void BookStatusWaitingApproval() { var book = new Book(); - Assert.Equal(BookStatus.WaitingApproval, book.Status()); - } - - [Fact] - public void BookStatusAvailable() - { - var book = new Book - { - BookUsers = new List(), - Approved = true - }; - - Assert.Equal(BookStatus.Available, book.Status()); - } - - [Fact] - public void BookStatusInvisible() - { - var bookUsers = new List - { - new BookUser() - }; - - var book = new Book - { - BookUsers = bookUsers - }; - - Assert.Equal(BookStatus.Invisible, book.Status()); - } - - [Fact] - public void BookStatusDonated() - { - var bookUsers = new List(); - var bookUser = new BookUser(); - bookUser.UpdateBookUser(DonationStatus.Donated, null); - - bookUsers.Add(bookUser); - - var book = new Book - { - BookUsers = bookUsers - }; - - Assert.Equal(BookStatus.Donated, book.Status()); + Assert.Equal(BookStatus.WaitingApproval, book.Status); } [Fact] diff --git a/ShareBook/ShareBook.Test.Unit/Helpers/HelperTests.cs b/ShareBook/ShareBook.Test.Unit/Helpers/HelperTests.cs index 422f8450..9a07e0c5 100644 --- a/ShareBook/ShareBook.Test.Unit/Helpers/HelperTests.cs +++ b/ShareBook/ShareBook.Test.Unit/Helpers/HelperTests.cs @@ -92,5 +92,51 @@ public void IncrementalIsValidCopy100() Assert.Equal(expected, actual); } + + [Fact] + public void ClientVersionValidation() + { + var minVersion = "v0.3.1"; + + bool result = Helper.ClientVersionValidation.IsValidVersion("v1.2.3", minVersion); + Assert.True(result); + + result = Helper.ClientVersionValidation.IsValidVersion("v1.0.0", minVersion); + Assert.True(result); + + result = Helper.ClientVersionValidation.IsValidVersion("v0.4.0", minVersion); + Assert.True(result); + + result = Helper.ClientVersionValidation.IsValidVersion("v0.3.2", minVersion); + Assert.True(result); + + result = Helper.ClientVersionValidation.IsValidVersion("v0.3.1", minVersion); + Assert.True(result); + + result = Helper.ClientVersionValidation.IsValidVersion("v0.3.10", minVersion); + Assert.True(result); + + result = Helper.ClientVersionValidation.IsValidVersion("v0.3", minVersion); + Assert.False(result); + + result = Helper.ClientVersionValidation.IsValidVersion("v0.3.0", minVersion); + Assert.False(result); + + result = Helper.ClientVersionValidation.IsValidVersion("", minVersion); + Assert.False(result); + + result = Helper.ClientVersionValidation.IsValidVersion("aaa", minVersion); + Assert.False(result); + + result = Helper.ClientVersionValidation.IsValidVersion("0.3.1", minVersion); + Assert.False(result); + + result = Helper.ClientVersionValidation.IsValidVersion("v0x3x1", minVersion); + Assert.False(result); + + result = Helper.ClientVersionValidation.IsValidVersion("v0.20.0", minVersion); + Assert.True(result); + + } } } diff --git a/ShareBook/ShareBook.Test.Unit/Mocks/BookMock.cs b/ShareBook/ShareBook.Test.Unit/Mocks/BookMock.cs index 4a7cadc3..96a7f147 100644 --- a/ShareBook/ShareBook.Test.Unit/Mocks/BookMock.cs +++ b/ShareBook/ShareBook.Test.Unit/Mocks/BookMock.cs @@ -17,8 +17,7 @@ public static Book GetLordTheRings(User user) ImageBytes = Encoding.UTF8.GetBytes("STRINGBASE64"), User = user, CategoryId = Guid.NewGuid(), - Approved = true, - Canceled = false + Status = ShareBook.Domain.Enums.BookStatus.Available }; } @@ -33,8 +32,7 @@ public static Book GetLordTheRings() ImageBytes = Encoding.UTF8.GetBytes("STRINGBASE64"), CategoryId = Guid.NewGuid(), UserId = new Guid("5489A967-9320-4350-E6FC-08D5CC8498F3"), - Approved = true, - Canceled = false + Status = ShareBook.Domain.Enums.BookStatus.Available }; } } diff --git a/ShareBook/Sharebook.Jobs/Executor/JobExecutor.cs b/ShareBook/Sharebook.Jobs/Executor/JobExecutor.cs index 99f932f1..d831b2aa 100644 --- a/ShareBook/Sharebook.Jobs/Executor/JobExecutor.cs +++ b/ShareBook/Sharebook.Jobs/Executor/JobExecutor.cs @@ -4,6 +4,7 @@ using ShareBook.Domain.Common; using ShareBook.Repository; using System.Diagnostics; +using Rollbar; namespace Sharebook.Jobs { @@ -19,7 +20,8 @@ public class JobExecutor : IJobExecutor public JobExecutor(IJobHistoryRepository jobHistoryRepo, ChooseDateReminder job1, LateDonationNotification job2, - RemoveBookFromShowcase job3) + RemoveBookFromShowcase job3, + NewBookNotify job4) { _jobHistoryRepo = jobHistoryRepo; @@ -27,7 +29,8 @@ public JobExecutor(IJobHistoryRepository jobHistoryRepo, { job1, job2, - job3 + job3, + job4 }; } @@ -75,7 +78,7 @@ public JobExecutorResult Execute() { success = false; messages.Add(string.Format("Executor: ocorreu um erro fatal. {0}", ex.Message)); - // TODO: logar no rollbar. + SendErrorToRollbar(ex); } // Executor também loga seu histórico. Precisamos de rastreabilidade. @@ -102,5 +105,19 @@ private void LogExecutorAddHistory(bool success, string details) _jobHistoryRepo.Insert(history); } + + // TODO: criar um service pro rollbar e reaproveitar aqui + // e no ExceptionHandlerMiddleware. + private void SendErrorToRollbar(Exception ex) + { + object error = new + { + Message = ex.Message, + StackTrace = ex.StackTrace, + Source = ex.Source + }; + + RollbarLocator.RollbarInstance.Error(error); + } } } diff --git a/ShareBook/Sharebook.Jobs/Jobs/1 - ChooseDateReminder.cs b/ShareBook/Sharebook.Jobs/Jobs/1 - ChooseDateReminder.cs index d895c9e4..ddc11103 100644 --- a/ShareBook/Sharebook.Jobs/Jobs/1 - ChooseDateReminder.cs +++ b/ShareBook/Sharebook.Jobs/Jobs/1 - ChooseDateReminder.cs @@ -42,7 +42,7 @@ public override JobHistory Work() foreach (var book in books) { - if (book.Status() == BookStatus.Available && book.BookUsers.Count > 0) + if (book.Status == BookStatus.Available && book.BookUsers.Count > 0) { SendEmail(book); messages.Add(string.Format("Lembrete amigável enviado para '{0}' referente ao livro '{1}'.", book.User.Name, book.Title)); diff --git a/ShareBook/Sharebook.Jobs/Jobs/2 - LateDonationNotification.cs b/ShareBook/Sharebook.Jobs/Jobs/2 - LateDonationNotification.cs index f6e52e38..c1dff40b 100644 --- a/ShareBook/Sharebook.Jobs/Jobs/2 - LateDonationNotification.cs +++ b/ShareBook/Sharebook.Jobs/Jobs/2 - LateDonationNotification.cs @@ -56,7 +56,7 @@ private void SendEmail(IList books) htmlTable += string.Format("{0}
{1}{2}{3}{4}
{5}
{6}
{7}{8}
{9}
{10}
{11}{12}", book.Title, - book.Status(), + book.Status, book.DaysInShowcase(), book.TotalInterested(), book.User.Name, book.User.Email, book.User.Phone, book.User.Linkedin, diff --git a/ShareBook/Sharebook.Jobs/Jobs/3 - RemoveBookFromShowcase.cs b/ShareBook/Sharebook.Jobs/Jobs/3 - RemoveBookFromShowcase.cs index 7cbeb50a..c57dea12 100644 --- a/ShareBook/Sharebook.Jobs/Jobs/3 - RemoveBookFromShowcase.cs +++ b/ShareBook/Sharebook.Jobs/Jobs/3 - RemoveBookFromShowcase.cs @@ -43,7 +43,7 @@ public override JobHistory Work() foreach (var book in books) { // Só trata livros disponíves - if (book.Status() != BookStatus.Available) + if (book.Status != BookStatus.Available) { messages.Add(string.Format("Livro '{0}' não foi processado porque não está disponível.", book.Title)); continue; @@ -51,7 +51,7 @@ public override JobHistory Work() if (book.BookUsers.Count > 0) { - book.Approved = false; + book.Status = BookStatus.AwaitingDonorDecision; messages.Add(string.Format("Livro '{0}' removido da vitrine.", book.Title)); } else diff --git a/ShareBook/Sharebook.Jobs/Jobs/4 - NewBookNotify.cs b/ShareBook/Sharebook.Jobs/Jobs/4 - NewBookNotify.cs new file mode 100644 index 00000000..bcf12052 --- /dev/null +++ b/ShareBook/Sharebook.Jobs/Jobs/4 - NewBookNotify.cs @@ -0,0 +1,65 @@ +using ShareBook.Domain; +using ShareBook.Domain.Enums; +using ShareBook.Repository; +using ShareBook.Service; +using ShareBook.Service.AWSSQS; +using System; +using System.Linq; +using System.Threading; + +namespace Sharebook.Jobs +{ + public class NewBookNotify : GenericJob, IJob + { + private readonly IEmailService _emailService; + private readonly IAWSSQSService _AWSSQSService; + + public NewBookNotify( + IJobHistoryRepository jobHistoryRepo, + IEmailService emailService, + IAWSSQSService AWSSQSService) : base(jobHistoryRepo) + { + + JobName = "NewBookNotify"; + Description = "Assim que um novo livro é aprovado, notifica, por e-mail, os usuários que já solicitaram algum livro da mesma categoria do novo. " + + "Para isso é utilizada a leitura de uma fila da Amazon SQS."; + Interval = Interval.Each5Minutes; + Active = true; + BestTimeToExecute = new TimeSpan(9, 0, 0); + + _emailService = emailService; + _AWSSQSService = AWSSQSService; + } + + public override JobHistory Work() + { + int qtDestinations = 0; + + var message = _AWSSQSService.GetNewBookNotifyFromAWSSQSAsync().Result; + + if (message != null) + { + foreach (var destination in message.Destinations) + { + _emailService.Send(destination.Email, destination.Name, message.BodyHTML.Replace("{name}", destination.Name), message.Subject).Wait(); + + // freio lógico + Thread.Sleep(1000); + } + + var receiptHandle = message.ReceiptHandle; + _AWSSQSService.DeleteNewBookNotifyFromAWSSQSAsync(receiptHandle).Wait(); + + qtDestinations = message.Destinations.Count(); + } + + return new JobHistory() + { + JobName = JobName, + IsSuccess = true, + Details = String.Join("\n", $"{qtDestinations} e-mails enviados.") + }; + } + } + +} diff --git a/ShareBook/Sharebook.Jobs/Jobs/GenericJob.cs b/ShareBook/Sharebook.Jobs/Jobs/GenericJob.cs index 9fda2505..02257332 100644 --- a/ShareBook/Sharebook.Jobs/Jobs/GenericJob.cs +++ b/ShareBook/Sharebook.Jobs/Jobs/GenericJob.cs @@ -52,20 +52,30 @@ public DateTime GetDateLimitByInterval(Interval i) switch (i) { case Interval.Dayly: - { - result = DateTimeHelper.GetTodaySaoPaulo(); - break; - } + { + result = DateTimeHelper.GetTodaySaoPaulo(); + break; + } case Interval.Hourly: - { - result = result.AddHours(-1); - break; - } + { + result = result.AddHours(-1); + break; + } case Interval.Weekly: - { - result = result.AddDays(-7); - break; - } + { + result = result.AddDays(-7); + break; + } + case Interval.Each30Minutes: + { + result = result.AddMinutes(-30); + break; + } + case Interval.Each5Minutes: + { + result = result.AddMinutes(-5); + break; + } } return result; diff --git a/ShareBook/Sharebook.Jobs/Sharebook.Jobs.csproj b/ShareBook/Sharebook.Jobs/Sharebook.Jobs.csproj index 98b01965..8d63be96 100644 --- a/ShareBook/Sharebook.Jobs/Sharebook.Jobs.csproj +++ b/ShareBook/Sharebook.Jobs/Sharebook.Jobs.csproj @@ -12,4 +12,8 @@
+ + + + \ No newline at end of file