diff --git a/src/Domain/Entities/Companies/Company.cs b/src/Domain/Entities/Companies/Company.cs index 0769f7f5..69e17db1 100644 --- a/src/Domain/Entities/Companies/Company.cs +++ b/src/Domain/Entities/Companies/Company.cs @@ -108,15 +108,12 @@ public void ApproveReview( } review.Approve(); - var relevantReviews = GetRelevantReviews(); + var newRating = RecalculateRating(); - Rating = relevantReviews - .Select(x => x.TotalRating) - .DefaultIfEmpty(0) - .Average(); - - ReviewsCount = relevantReviews.Count; - RatingHistory.Add(new CompanyRatingHistory(Rating, this)); + RatingHistory.Add( + new CompanyRatingHistory( + newRating, + this)); } public void MarkReviewAsOutdated( @@ -136,6 +133,16 @@ public void MarkReviewAsOutdated( } review.MarkAsOutdated(); + var newRating = RecalculateRating(); + + RatingHistory.Add( + new CompanyRatingHistory( + newRating, + this)); + } + + public double RecalculateRating() + { var relevantReviews = GetRelevantReviews(); Rating = relevantReviews @@ -144,7 +151,7 @@ public void MarkReviewAsOutdated( .Average(); ReviewsCount = relevantReviews.Count; - RatingHistory.Add(new CompanyRatingHistory(Rating, this)); + return Rating; } public void Delete() @@ -165,6 +172,32 @@ public void NotDeletedOrFail() } } + public void Update( + string name, + string description, + string logoUrl, + List links) + { + name = name?.Trim(); + description = description?.Trim(); + + if (string.IsNullOrWhiteSpace(name)) + { + throw new BadRequestException("Company name cannot be empty."); + } + + if (string.IsNullOrWhiteSpace(description)) + { + throw new BadRequestException("Company description cannot be empty."); + } + + Name = name; + NormalizedName = name.ToUpperInvariant(); + Description = description; + LogoUrl = logoUrl; + Links = links ?? new List(); + } + protected Company() { } diff --git a/src/Domain/Validation/Exceptions/EntityInvalidException.cs b/src/Domain/Validation/Exceptions/EntityInvalidException.cs index 1b1466a5..3440af4b 100644 --- a/src/Domain/Validation/Exceptions/EntityInvalidException.cs +++ b/src/Domain/Validation/Exceptions/EntityInvalidException.cs @@ -11,16 +11,17 @@ public EntityInvalidException(string message) { } - public static EntityInvalidException FromInstance(ICollection errors) + public static EntityInvalidException FromInstance( + ICollection errors) { errors.ThrowIfNull(nameof(errors)); - if (!errors.Any()) + if (errors.Count == 0) { throw new InvalidOperationException("Collection of errors could not be empty"); } var message = $"Instance of {typeof(T).Name} is invalid"; - if (errors.Any()) + if (errors.Count != 0) { message += "\r\n" + errors.Aggregate((result, item) => result + item + "\r\n"); } diff --git a/src/Web.Api/Features/Companies/CompaniesController.cs b/src/Web.Api/Features/Companies/CompaniesController.cs index 4e380d65..423229c5 100644 --- a/src/Web.Api/Features/Companies/CompaniesController.cs +++ b/src/Web.Api/Features/Companies/CompaniesController.cs @@ -14,6 +14,7 @@ using Web.Api.Features.Companies.SearchCompanies; using Web.Api.Features.Companies.SearchReviewsToBeApproved; using Web.Api.Features.Companies.SoftDeleteCompany; +using Web.Api.Features.Companies.UpdateCompany; using Web.Api.Setup.Attributes; namespace Web.Api.Features.Companies; @@ -44,7 +45,7 @@ await _mediator.Send( [HttpPost("")] [HasAnyRole(Role.Admin)] public async Task CreateCompany( - [FromBody] CreateCompanyBodyRequest request, + [FromBody] EditCompanyBodyRequest request, CancellationToken cancellationToken) { return Ok( @@ -53,6 +54,21 @@ await _mediator.Send( cancellationToken)); } + [HttpPost("{companyId:guid}")] + [HasAnyRole(Role.Admin)] + public async Task UpdateCompany( + [FromRoute] Guid companyId, + [FromBody] EditCompanyBodyRequest request, + CancellationToken cancellationToken) + { + return Ok( + await _mediator.Send( + new UpdateCompanyCommand( + companyId, + request), + cancellationToken)); + } + [HttpGet("{companyId:guid}")] public async Task GetCompany( Guid companyId, diff --git a/src/Web.Api/Features/Companies/CreateCompany/CreateCompanyCommand.cs b/src/Web.Api/Features/Companies/CreateCompany/CreateCompanyCommand.cs index 271b35d5..1c4e0235 100644 --- a/src/Web.Api/Features/Companies/CreateCompany/CreateCompanyCommand.cs +++ b/src/Web.Api/Features/Companies/CreateCompany/CreateCompanyCommand.cs @@ -5,10 +5,10 @@ namespace Web.Api.Features.Companies.CreateCompany; -public record CreateCompanyCommand : CreateCompanyBodyRequest, IRequest +public record CreateCompanyCommand : EditCompanyBodyRequest, IRequest { public CreateCompanyCommand( - CreateCompanyBodyRequest request) + EditCompanyBodyRequest request) { request.ThrowIfInvalid(); diff --git a/src/Web.Api/Features/Companies/CreateCompany/CreateCompanyBodyRequest.cs b/src/Web.Api/Features/Companies/CreateCompany/EditCompanyBodyRequest.cs similarity index 90% rename from src/Web.Api/Features/Companies/CreateCompany/CreateCompanyBodyRequest.cs rename to src/Web.Api/Features/Companies/CreateCompany/EditCompanyBodyRequest.cs index 1f1d0ef7..78ef8f9e 100644 --- a/src/Web.Api/Features/Companies/CreateCompany/CreateCompanyBodyRequest.cs +++ b/src/Web.Api/Features/Companies/CreateCompany/EditCompanyBodyRequest.cs @@ -3,7 +3,7 @@ namespace Web.Api.Features.Companies.CreateCompany; -public record CreateCompanyBodyRequest +public record EditCompanyBodyRequest { [Required] public string Name { get; init; } diff --git a/src/Web.Api/Features/Companies/UpdateCompany/UpdateCompanyCommand.cs b/src/Web.Api/Features/Companies/UpdateCompany/UpdateCompanyCommand.cs new file mode 100644 index 00000000..6299d6e9 --- /dev/null +++ b/src/Web.Api/Features/Companies/UpdateCompany/UpdateCompanyCommand.cs @@ -0,0 +1,20 @@ +using System; +using MediatR; +using Web.Api.Features.Companies.CreateCompany; + +namespace Web.Api.Features.Companies.UpdateCompany; + +public record UpdateCompanyCommand : IRequest +{ + public UpdateCompanyCommand( + Guid companyId, + EditCompanyBodyRequest body) + { + CompanyId = companyId; + Body = body; + } + + public Guid CompanyId { get; } + + public EditCompanyBodyRequest Body { get; } +} \ No newline at end of file diff --git a/src/Web.Api/Features/Companies/UpdateCompany/UpdateCompanyHandler.cs b/src/Web.Api/Features/Companies/UpdateCompany/UpdateCompanyHandler.cs new file mode 100644 index 00000000..7a4ccd81 --- /dev/null +++ b/src/Web.Api/Features/Companies/UpdateCompany/UpdateCompanyHandler.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Domain.Entities.Companies; +using Domain.Validation; +using Domain.Validation.Exceptions; +using Infrastructure.Database; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace Web.Api.Features.Companies.UpdateCompany; + +public record UpdateCompanyHandler : IRequestHandler +{ + private readonly DatabaseContext _context; + + public UpdateCompanyHandler( + DatabaseContext context) + { + _context = context; + } + + public async Task Handle( + UpdateCompanyCommand request, + CancellationToken cancellationToken) + { + request.Body.ThrowIfInvalid(); + + var company = await _context.Companies.FirstOrDefaultAsync( + c => c.Id == request.CompanyId, + cancellationToken) + ?? throw NotFoundException.CreateFromEntity(request.CompanyId); + + company.Update( + request.Body.Name, + request.Body.Description, + request.Body.LogoUrl, + request.Body.Links); + + await _context.TrySaveChangesAsync(cancellationToken); + return Unit.Value; + } +} \ No newline at end of file diff --git a/src/Web.Api/Features/Telegram/GetTelegramInlineUsageStats/TelegramInlineUsageChatDataItem.cs b/src/Web.Api/Features/Telegram/GetTelegramInlineUsageStats/TelegramInlineUsageChatDataItem.cs index cc5bf592..93deb8f4 100644 --- a/src/Web.Api/Features/Telegram/GetTelegramInlineUsageStats/TelegramInlineUsageChatDataItem.cs +++ b/src/Web.Api/Features/Telegram/GetTelegramInlineUsageStats/TelegramInlineUsageChatDataItem.cs @@ -1,6 +1,6 @@ namespace Web.Api.Features.Telegram.GetTelegramInlineUsageStats; -public class TelegramInlineUsageChatDataItem +public record TelegramInlineUsageChatDataItem { public TelegramInlineUsageChatDataItem( long? chatId, diff --git a/src/Web.Api/Middlewares/LoggingMiddleware.cs b/src/Web.Api/Middlewares/LoggingMiddleware.cs index 0460231f..47ad9f96 100644 --- a/src/Web.Api/Middlewares/LoggingMiddleware.cs +++ b/src/Web.Api/Middlewares/LoggingMiddleware.cs @@ -19,9 +19,13 @@ public class LoggingMiddleware { typeof(AuthenticationException), typeof(BadRequestException), + typeof(EntityInvalidException), + typeof(NotFoundException), }; - public LoggingMiddleware(ILoggerFactory loggerFactory, RequestDelegate next) + public LoggingMiddleware( + ILoggerFactory loggerFactory, + RequestDelegate next) { _next = next; _logger = loggerFactory.CreateLogger(); @@ -37,7 +41,10 @@ public async Task InvokeAsync(HttpContext context) { if (!Ignore(exception)) { - _logger.LogError(exception, exception.Message); + _logger.LogError( + exception, + "Unhandled error occured. Message {Message}", + exception.Message); } throw;