From e406443bd6bd7cc500ca00307976b2c144f2deac Mon Sep 17 00:00:00 2001 From: akovylyaeva Date: Fri, 10 Oct 2025 14:03:10 +0500 Subject: [PATCH] refactor: update editorconfig file --- .editorconfig | 17 +- Api/Configuration/AuthenticationExtensions.cs | 26 +-- Api/Configuration/ConfigurationExtensions.cs | 39 +++-- Api/Configuration/LoggingExtensions.cs | 54 +++--- Api/Configuration/SwaggerExtensions.cs | 79 ++++----- Api/Controllers/DocumentsController.cs | 103 +++++------ .../ExceptionMiddlewareExtensions.cs | 53 +++--- Api/Program.cs | 27 +-- Api/UserClaimsProvider.cs | 12 +- Api/UserExtensions.cs | 30 ++-- Application/DependencyInjection.cs | 14 +- Application/IInnerCircleHttpClient.cs | 4 +- Application/InnerCircleHttpClient.cs | 128 ++++++++------ Application/Services/EmployeeDto.cs | 5 +- Application/Services/IPayslipsValidator.cs | 2 +- .../Services/Options/InnerCircleServiceUrl.cs | 4 +- Application/Services/PayslipsValidator.cs | 82 ++++----- Core/Document.cs | 15 +- Core/Employee.cs | 18 +- Core/MailPayslipsModel.cs | 26 +-- Core/PayslipsItem.cs | 19 +- DataAccess/DependencyInjection.cs | 24 +-- DataAccess/DocumentsDbContext.cs | 22 +-- DataAccess/Mapping/DocumentsMapping.cs | 16 +- Tests/Api/DocumentsControllerTests.cs | 108 ++++++------ Tests/Application/PayslipsValidatorTests.cs | 163 +++++++++--------- 26 files changed, 562 insertions(+), 528 deletions(-) diff --git a/.editorconfig b/.editorconfig index 1cbac56..0ac7735 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,17 +1,26 @@ -[*.cs] +# command to run: +# dotnet format --exclude "**/Migrations/**" --verbosity detailed + +[*.cs] #### Core EditorConfig Options #### charset = utf-8 # Indentation and spacing -indent_size = 4 +indent_size = 2 indent_style = space -tab_width = 4 +tab_width = 2 # New line preferences end_of_line = lf -insert_final_newline = false +insert_final_newline = true + +# Wrapping preferences +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true +# error unnecessary usings +dotnet_diagnostic.IDE0005.severity = error #### .NET Coding Conventions #### diff --git a/Api/Configuration/AuthenticationExtensions.cs b/Api/Configuration/AuthenticationExtensions.cs index 805b785..7f1f539 100644 --- a/Api/Configuration/AuthenticationExtensions.cs +++ b/Api/Configuration/AuthenticationExtensions.cs @@ -1,18 +1,22 @@ +using DataAccess; using TourmalineCore.AspNetCore.JwtAuthentication.Core; using TourmalineCore.AspNetCore.JwtAuthentication.Core.Options; -using DataAccess; namespace Api.Configuration; public static class AuthenticationExtensions { - public static void AddAppAuthentication(this WebApplicationBuilder builder) - { - var configuration = builder.Configuration; - var authenticationOptions = configuration.GetSection(nameof(AuthenticationOptions)).Get(); - builder.Services.Configure(configuration.GetSection(nameof(AuthenticationOptions))); - builder.Services.AddJwtAuthentication(authenticationOptions) - .WithUserClaimsProvider(UserClaimsProvider.PermissionClaimType); - builder.Services.AddPersistence(configuration); - } -} \ No newline at end of file + public static void AddAppAuthentication(this WebApplicationBuilder builder) + { + var configuration = builder.Configuration; + var authenticationOptions = configuration + .GetSection(nameof(AuthenticationOptions)) + .Get(); + + builder.Services.Configure(configuration.GetSection(nameof(AuthenticationOptions))); + builder.Services + .AddJwtAuthentication(authenticationOptions) + .WithUserClaimsProvider(UserClaimsProvider.PermissionClaimType); + builder.Services.AddPersistence(configuration); + } +} diff --git a/Api/Configuration/ConfigurationExtensions.cs b/Api/Configuration/ConfigurationExtensions.cs index e3d4f4b..eb3e270 100644 --- a/Api/Configuration/ConfigurationExtensions.cs +++ b/Api/Configuration/ConfigurationExtensions.cs @@ -4,26 +4,27 @@ namespace Api.Configuration; public static class ConfigurationExtensions { - public static ConfigurationManager AddAppConfiguration(this WebApplicationBuilder builder, string[] args) - { - var env = builder.Environment; - var configuration = builder.Configuration; - var reloadOnChange = configuration.GetValue("hostBuilder:reloadConfigOnChange", true); + public static ConfigurationManager AddAppConfiguration(this WebApplicationBuilder builder, string[] args) + { + var env = builder.Environment; + var configuration = builder.Configuration; + var reloadOnChange = configuration.GetValue("hostBuilder:reloadConfigOnChange", true); - configuration.AddJsonFile("appsettings.json", true, reloadOnChange) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", true, reloadOnChange) - .AddJsonFile("appsettings.Active.json", true, reloadOnChange) - .AddJsonFile("swagger.json", true, reloadOnChange); + configuration + .AddJsonFile("appsettings.json", true, reloadOnChange) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", true, reloadOnChange) + .AddJsonFile("appsettings.Active.json", true, reloadOnChange) + .AddJsonFile("swagger.json", true, reloadOnChange); - if (env.IsDevelopment() && !string.IsNullOrEmpty(env.ApplicationName)) - { - var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName)); - configuration.AddUserSecrets(appAssembly, true); - } + if (env.IsDevelopment() && !string.IsNullOrEmpty(env.ApplicationName)) + { + var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName)); + configuration.AddUserSecrets(appAssembly, true); + } - configuration.AddEnvironmentVariables(); - configuration.AddCommandLine(args); + configuration.AddEnvironmentVariables(); + configuration.AddCommandLine(args); - return configuration; - } -} \ No newline at end of file + return configuration; + } +} diff --git a/Api/Configuration/LoggingExtensions.cs b/Api/Configuration/LoggingExtensions.cs index 9ae14e0..97f92ed 100644 --- a/Api/Configuration/LoggingExtensions.cs +++ b/Api/Configuration/LoggingExtensions.cs @@ -1,38 +1,38 @@ -using Microsoft.Extensions.Logging.EventLog; using System.Runtime.InteropServices; +using Microsoft.Extensions.Logging.EventLog; namespace Api.Configuration; public static class LoggingExtensions { - public static void AddAppLogging(this WebApplicationBuilder builder) - { - const string LoggingSectionKey = "Logging"; - var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - var logging = builder.Logging; + public static void AddAppLogging(this WebApplicationBuilder builder) + { + const string LoggingSectionKey = "Logging"; + var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + var logging = builder.Logging; - if (isWindows) - { - logging.AddFilter(level => level >= LogLevel.Warning); - logging.AddEventLog(); - } + if (isWindows) + { + logging.AddFilter(level => level >= LogLevel.Warning); + logging.AddEventLog(); + } - logging.AddConfiguration(builder.Configuration.GetSection(LoggingSectionKey)); - logging.AddSimpleConsole(opts => - { - opts.SingleLine = true; - opts.TimestampFormat = "[yyyy-MM-dd HH:mm:ss] "; - opts.UseUtcTimestamp = true; - }); + logging.AddConfiguration(builder.Configuration.GetSection(LoggingSectionKey)); + logging.AddSimpleConsole(opts => + { + opts.SingleLine = true; + opts.TimestampFormat = "[yyyy-MM-dd HH:mm:ss] "; + opts.UseUtcTimestamp = true; + }); - logging.AddDebug(); - logging.AddEventSourceLogger(); + logging.AddDebug(); + logging.AddEventSourceLogger(); - logging.Configure(options => - { - options.ActivityTrackingOptions = ActivityTrackingOptions.SpanId - | ActivityTrackingOptions.TraceId - | ActivityTrackingOptions.ParentId; - }); - } + logging.Configure(options => + { + options.ActivityTrackingOptions = ActivityTrackingOptions.SpanId + | ActivityTrackingOptions.TraceId + | ActivityTrackingOptions.ParentId; + }); + } } diff --git a/Api/Configuration/SwaggerExtensions.cs b/Api/Configuration/SwaggerExtensions.cs index 0e78622..a92a413 100644 --- a/Api/Configuration/SwaggerExtensions.cs +++ b/Api/Configuration/SwaggerExtensions.cs @@ -4,48 +4,49 @@ namespace Api.Configuration; public static class SwaggerExtensions { - public static void AddAppSwagger(this WebApplicationBuilder builder) + public static void AddAppSwagger(this WebApplicationBuilder builder) + { + builder.Services.AddSwaggerGen(c => { - builder.Services.AddSwaggerGen(c => - { - c.SwaggerDoc("v1", new OpenApiInfo - { - Title = "My API", - Version = "v1" - }); + c.SwaggerDoc("v1", new OpenApiInfo + { + Title = "My API", + Version = "v1" + }); - c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme - { - In = ParameterLocation.Header, - Description = "Please insert JWT with Bearer into field", - Name = "Authorization", - Type = SecuritySchemeType.ApiKey - }); + c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme + { + In = ParameterLocation.Header, + Description = "Please insert JWT with Bearer into field", + Name = "Authorization", + Type = SecuritySchemeType.ApiKey + }); - c.AddSecurityRequirement(new OpenApiSecurityRequirement { - { - new OpenApiSecurityScheme - { - Reference = new OpenApiReference - { - Type = ReferenceType.SecurityScheme, - Id = "Bearer" - } - }, - new string[] { } - } - }); - }); - } + c.AddSecurityRequirement(new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "Bearer" + } + }, + new string[] { } + } + }); + }); + } - public static void UseAppSwagger(this WebApplication app) + public static void UseAppSwagger(this WebApplication app) + { + app.UseSwagger(); + app.UseSwaggerUI(); + app.UseSwaggerUI(options => { - app.UseSwagger(); - app.UseSwaggerUI(); - app.UseSwaggerUI(options => - { - options.SwaggerEndpoint("/swagger/v1/swagger.json", "v1"); - options.RoutePrefix = string.Empty; - }); - } + options.SwaggerEndpoint("/swagger/v1/swagger.json", "v1"); + options.RoutePrefix = string.Empty; + }); + } } diff --git a/Api/Controllers/DocumentsController.cs b/Api/Controllers/DocumentsController.cs index e270474..c0bbcb2 100644 --- a/Api/Controllers/DocumentsController.cs +++ b/Api/Controllers/DocumentsController.cs @@ -1,4 +1,4 @@ -using Application; +using Application; using Application.Services; using Core; using Microsoft.AspNetCore.Authorization; @@ -13,64 +13,67 @@ namespace Api.Controllers; [Consumes("multipart/form-data")] public class DocumentsController : Controller { - private readonly IInnerCircleHttpClient _client; - private readonly IPayslipsValidator _payslipsValidator; - private readonly ILogger _logger; + private readonly IInnerCircleHttpClient _client; + private readonly IPayslipsValidator _payslipsValidator; + private readonly ILogger _logger; - public DocumentsController(IInnerCircleHttpClient client, IPayslipsValidator payslipsValidator, ILogger logger) + public DocumentsController( + IInnerCircleHttpClient client, + IPayslipsValidator payslipsValidator, + ILogger logger + ) + { + _client = client; + _payslipsValidator = payslipsValidator; + _logger = logger; + } + + [RequiresPermission(UserClaimsProvider.CanManageDocuments)] + [HttpPost("sendMailingPayslips")] + public async Task SendMailingPayslips([FromForm] List payslips) + { + var employees = await GetEmployeesAsyncFromEmployeeService(); + + await _payslipsValidator.ValidateAsync(payslips, employees); + + try { - _client = client; - _payslipsValidator = payslipsValidator; - _logger = logger; + await _client.SendMailingPayslips(payslips, employees); } - - [RequiresPermission(UserClaimsProvider.CanManageDocuments)] - [HttpPost("sendMailingPayslips")] - public async Task SendMailingPayslips([FromForm] List payslips) + catch (Exception ex) { - var employees = await GetEmployeesAsyncFromEmployeeService(); + _logger.LogError(ex.Message); + throw new Exception("Email sender service is not available"); + } + } - await _payslipsValidator.ValidateAsync(payslips, employees); + [RequiresPermission(UserClaimsProvider.CanManageDocuments)] + [HttpGet("getEmployees")] + public async Task GetEmployees() + { + var employees = await GetEmployeesAsyncFromEmployeeService(); - try - { - await _client.SendMailingPayslips(payslips, employees); - } - catch(Exception ex) + return new EmployeesDto + { + Employees = employees + .Select(employee => new EmployeeDto { - _logger.LogError(ex.Message); - throw new Exception("Email sender service is not available"); - } - - } + LastName = employee.LastName + }) + .ToList() + }; + } - [RequiresPermission(UserClaimsProvider.CanManageDocuments)] - [HttpGet("getEmployees")] - public async Task GetEmployees() + private async Task> GetEmployeesAsyncFromEmployeeService() + { + try { - var employees = await GetEmployeesAsyncFromEmployeeService(); - - return new EmployeesDto - { - Employees = employees - .Select(employee => new EmployeeDto - { - LastName = employee.LastName - }) - .ToList() - }; + return await _client.GetEmployeesAsync(); } - - private async Task> GetEmployeesAsyncFromEmployeeService() + catch (Exception ex) { - try - { - return await _client.GetEmployeesAsync(); - } - catch(Exception ex) - { - _logger.LogError(ex.Message); - throw new Exception("Employees service is not available"); - } + _logger.LogError(ex.Message); + throw new Exception("Employees service is not available"); } -} \ No newline at end of file + } +} diff --git a/Api/Middlewares/ExceptionMiddlewareExtensions.cs b/Api/Middlewares/ExceptionMiddlewareExtensions.cs index 1ea3893..0f2c8b5 100644 --- a/Api/Middlewares/ExceptionMiddlewareExtensions.cs +++ b/Api/Middlewares/ExceptionMiddlewareExtensions.cs @@ -1,39 +1,40 @@ -using Microsoft.AspNetCore.Diagnostics; using System.Net; using System.Text.Json; +using Microsoft.AspNetCore.Diagnostics; namespace Api; public static class ExceptionMiddlewareExtensions { - public static void ConfigureExceptionHandler(this IApplicationBuilder app) + public static void ConfigureExceptionHandler(this IApplicationBuilder app) + { + app.UseExceptionHandler(appError => { - app.UseExceptionHandler(appError => + appError.Run(async context => + { + context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; + context.Response.ContentType = "application/json"; + var contextFeature = context.Features.Get(); + if (contextFeature != null) { - appError.Run(async context => - { - context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; - context.Response.ContentType = "application/json"; - var contextFeature = context.Features.Get(); - if (contextFeature != null) - { - await context.Response.WriteAsync(new ErrorDetails() - { - StatusCode = context.Response.StatusCode, - Message = contextFeature.Error.Message - }.ToString()); - } - }); - }); - } + await context.Response.WriteAsync(new ErrorDetails() + { + StatusCode = context.Response.StatusCode, + Message = contextFeature.Error.Message + } + .ToString()); + } + }); + }); + } - public class ErrorDetails + public class ErrorDetails + { + public int StatusCode { get; set; } + public string Message { get; set; } + public override string ToString() { - public int StatusCode { get; set; } - public string Message { get; set; } - public override string ToString() - { - return JsonSerializer.Serialize(this); - } + return JsonSerializer.Serialize(this); } + } } diff --git a/Api/Program.cs b/Api/Program.cs index 9021000..ff7188b 100644 --- a/Api/Program.cs +++ b/Api/Program.cs @@ -1,10 +1,10 @@ -using Microsoft.EntityFrameworkCore; +using Api; +using Api.Configuration; using Application; +using Application.Services.Options; using DataAccess; -using Api; +using Microsoft.EntityFrameworkCore; using TourmalineCore.AspNetCore.JwtAuthentication.Core; -using Application.Services.Options; -using Api.Configuration; const string CorsPolicyName = "DocumentsSpecificOrigins"; @@ -12,13 +12,14 @@ builder.Services.AddCors(options => { - options.AddPolicy(CorsPolicyName, - policy => - { - policy.WithOrigins("*") - .AllowAnyHeader() - .AllowAnyMethod(); - }); + options.AddPolicy(CorsPolicyName, + policy => + { + policy + .WithOrigins("*") + .AllowAnyHeader() + .AllowAnyMethod(); + }); }); builder.Services.AddControllers(); @@ -45,8 +46,8 @@ using (var serviceScope = app.Services.CreateScope()) { - var context = serviceScope.ServiceProvider.GetRequiredService(); - await context.Database.MigrateAsync(); + var context = serviceScope.ServiceProvider.GetRequiredService(); + await context.Database.MigrateAsync(); } app.UseRouting(); diff --git a/Api/UserClaimsProvider.cs b/Api/UserClaimsProvider.cs index a50e3ed..08c38bc 100644 --- a/Api/UserClaimsProvider.cs +++ b/Api/UserClaimsProvider.cs @@ -5,12 +5,12 @@ namespace Api; public class UserClaimsProvider : IUserClaimsProvider { - public const string PermissionClaimType = "permissions"; + public const string PermissionClaimType = "permissions"; - public const string CanManageDocuments = "CanManageDocuments"; + public const string CanManageDocuments = "CanManageDocuments"; - public Task> GetUserClaimsAsync(string login) - { - throw new NotImplementedException(); - } + public Task> GetUserClaimsAsync(string login) + { + throw new NotImplementedException(); + } } diff --git a/Api/UserExtensions.cs b/Api/UserExtensions.cs index f7cec0b..af4d906 100644 --- a/Api/UserExtensions.cs +++ b/Api/UserExtensions.cs @@ -4,24 +4,24 @@ namespace Api; public static class UserExtensions { - private const string CorporateEmailClaimType = "corporateEmail"; + private const string CorporateEmailClaimType = "corporateEmail"; - private const string TenantIdClaimType = "tenantId"; + private const string TenantIdClaimType = "tenantId"; - public static string GetCorporateEmail(this ClaimsPrincipal context) - { - return context.FindFirstValue(CorporateEmailClaimType); - } - - public static long GetTenantId(this ClaimsPrincipal context) - { - var tenantId = context.FindFirstValue(TenantIdClaimType); + public static string GetCorporateEmail(this ClaimsPrincipal context) + { + return context.FindFirstValue(CorporateEmailClaimType); + } - if (string.IsNullOrEmpty(tenantId)) - { - throw new NullReferenceException("Tenant id is null or empty"); - } + public static long GetTenantId(this ClaimsPrincipal context) + { + var tenantId = context.FindFirstValue(TenantIdClaimType); - return long.Parse(tenantId); + if (string.IsNullOrEmpty(tenantId)) + { + throw new NullReferenceException("Tenant id is null or empty"); } + + return long.Parse(tenantId); + } } diff --git a/Application/DependencyInjection.cs b/Application/DependencyInjection.cs index d90735b..c5716cd 100644 --- a/Application/DependencyInjection.cs +++ b/Application/DependencyInjection.cs @@ -1,13 +1,13 @@ -using Microsoft.Extensions.DependencyInjection; using Application.Services; +using Microsoft.Extensions.DependencyInjection; namespace Application; public static class DependencyInjection { - public static void AddApplication(this IServiceCollection services) - { - services.AddTransient(); - services.AddTransient(); - } -} \ No newline at end of file + public static void AddApplication(this IServiceCollection services) + { + services.AddTransient(); + services.AddTransient(); + } +} diff --git a/Application/IInnerCircleHttpClient.cs b/Application/IInnerCircleHttpClient.cs index 2a3ac3f..c564489 100644 --- a/Application/IInnerCircleHttpClient.cs +++ b/Application/IInnerCircleHttpClient.cs @@ -4,6 +4,6 @@ namespace Application; public interface IInnerCircleHttpClient { - Task> GetEmployeesAsync(); - Task SendMailingPayslips(List payslips, List employees); + Task> GetEmployeesAsync(); + Task SendMailingPayslips(List payslips, List employees); } diff --git a/Application/InnerCircleHttpClient.cs b/Application/InnerCircleHttpClient.cs index 82ff160..ec85e36 100644 --- a/Application/InnerCircleHttpClient.cs +++ b/Application/InnerCircleHttpClient.cs @@ -9,73 +9,89 @@ namespace Application; public class InnerCircleHttpClient : IInnerCircleHttpClient { - private readonly HttpClient _client; - private readonly InnerCircleServiceUrls _urls; - private readonly AuthenticationOptions _authOptions; - private readonly IHttpContextAccessor _httpContextAccessor; + private readonly HttpClient _client; + private readonly InnerCircleServiceUrls _urls; + private readonly AuthenticationOptions _authOptions; + private readonly IHttpContextAccessor _httpContextAccessor; - public InnerCircleHttpClient( - IOptions urls, - IOptions authOptions, - IHttpContextAccessor httpContextAccessor - ) - { - _client = new HttpClient(); - _urls = urls.Value; - _authOptions = authOptions.Value; - _httpContextAccessor = httpContextAccessor; - } + public InnerCircleHttpClient( + IOptions urls, + IOptions authOptions, + IHttpContextAccessor httpContextAccessor + ) + { + _client = new HttpClient(); + _urls = urls.Value; + _authOptions = authOptions.Value; + _httpContextAccessor = httpContextAccessor; + } - public async Task> GetEmployeesAsync() - { - var link = $"{_urls.EmployeesServiceUrl}/internal/get-employees"; + public async Task> GetEmployeesAsync() + { + var link = $"{_urls.EmployeesServiceUrl}/internal/get-employees"; - var headerName = _authOptions.IsDebugTokenEnabled ? "X-DEBUG-TOKEN" : "Authorization"; + var headerName = _authOptions.IsDebugTokenEnabled + ? "X-DEBUG-TOKEN" + : "Authorization"; - var token = _httpContextAccessor - .HttpContext! - .Request - .Headers[headerName] - .ToString(); + var token = _httpContextAccessor + .HttpContext! + .Request + .Headers[headerName] + .ToString(); - _client.DefaultRequestHeaders.Add(headerName, token); + _client.DefaultRequestHeaders.Add(headerName, token); - var response = await _client.GetStringAsync(link); + var response = await _client.GetStringAsync(link); - return JsonConvert.DeserializeObject>(response); - } + return JsonConvert.DeserializeObject>(response); + } + public async Task SendMailingPayslips(List payslips, List employees) + { + var mailsData = payslips.Join( + employees, + payslip => payslip.LastName, + employee => employee.LastName, + (payslip, employee) => new MailPayslipsModel + ( + employee.CorporateEmail, + payslip.File.FileName.Substring(0, payslip.File.FileName.Length - 4), + " ", + payslip.File + ) + ); - public async Task SendMailingPayslips(List payslips, List employees) - { - var mailsData = payslips.Join(employees, - payslip => payslip.LastName, - employee => employee.LastName, - (payslip, employee) => new MailPayslipsModel - ( - employee.CorporateEmail, - payslip.File.FileName.Substring(0, payslip.File.FileName.Length - 4), - " ", - payslip.File - )); - - var link = $"{_urls.EmailSenderServiceUrl}/mail/send-document"; + var link = $"{_urls.EmailSenderServiceUrl}/mail/send-document"; - foreach (var mailData in mailsData) + foreach (var mailData in mailsData) + { + await using var stream = mailData.File.OpenReadStream(); + using var request = new HttpRequestMessage(HttpMethod.Post, link); + using var content = new MultipartFormDataContent + { + { + new StringContent(mailData.To), + "To" + }, + { + new StringContent(mailData.Subject), + "Subject" + }, + { + new StringContent(mailData.Body), + "Body" + }, { - await using var stream = mailData.File.OpenReadStream(); - using var request = new HttpRequestMessage(HttpMethod.Post, link); - using var content = new MultipartFormDataContent - { - { new StringContent(mailData.To), "To" }, - { new StringContent(mailData.Subject), "Subject" }, - { new StringContent(mailData.Body), "Body" }, - { new StreamContent(stream), "File", mailData.File.FileName }, - }; + new StreamContent(stream), + "File", + mailData.File.FileName + }, + }; - request.Content = content; + request.Content = content; - await _client.SendAsync(request); - } + await _client.SendAsync(request); } -} \ No newline at end of file + } +} diff --git a/Application/Services/EmployeeDto.cs b/Application/Services/EmployeeDto.cs index 4dbf660..b0cd7b2 100644 --- a/Application/Services/EmployeeDto.cs +++ b/Application/Services/EmployeeDto.cs @@ -2,11 +2,10 @@ namespace Application.Services; public class EmployeesDto { - public List Employees { get; set; } + public List Employees { get; set; } } public class EmployeeDto { - public string LastName { get; set; } + public string LastName { get; set; } } - diff --git a/Application/Services/IPayslipsValidator.cs b/Application/Services/IPayslipsValidator.cs index 6d1227f..dc9a87b 100644 --- a/Application/Services/IPayslipsValidator.cs +++ b/Application/Services/IPayslipsValidator.cs @@ -4,5 +4,5 @@ namespace Application.Services; public interface IPayslipsValidator { - public Task ValidateAsync(List payslips, List employees); + public Task ValidateAsync(List payslips, List employees); } diff --git a/Application/Services/Options/InnerCircleServiceUrl.cs b/Application/Services/Options/InnerCircleServiceUrl.cs index 6a5c969..d3bbede 100644 --- a/Application/Services/Options/InnerCircleServiceUrl.cs +++ b/Application/Services/Options/InnerCircleServiceUrl.cs @@ -2,7 +2,7 @@ namespace Application.Services.Options; public class InnerCircleServiceUrls { - public string EmployeesServiceUrl { get; set; } + public string EmployeesServiceUrl { get; set; } - public string EmailSenderServiceUrl { get; set; } + public string EmailSenderServiceUrl { get; set; } } diff --git a/Application/Services/PayslipsValidator.cs b/Application/Services/PayslipsValidator.cs index e33e291..19df5fa 100644 --- a/Application/Services/PayslipsValidator.cs +++ b/Application/Services/PayslipsValidator.cs @@ -4,47 +4,47 @@ namespace Application.Services; public class PayslipsValidator : IPayslipsValidator { - public async Task ValidateAsync(List payslips, List employees) + public async Task ValidateAsync(List payslips, List employees) + { + if (payslips.Count == 0) { - if (payslips.Count == 0) - { - throw new Exception("Payslips list is empty"); - } - - - foreach (var payslipsItem in payslips) - { - if (payslipsItem.File.Length == 0) - { - throw new Exception($"'{payslipsItem.File.FileName}' is empty"); - } - - else if (payslipsItem.File.FileName is null) - { - throw new Exception("Payslips name is null"); - } - - else if (!payslipsItem.File.FileName.Contains(payslipsItem.LastName)) - { - throw new Exception($"Last name {payslipsItem.LastName} not contains in file name '{payslipsItem.File.FileName}'"); - } - } - - var employeesNames = employees.Select(x => x.LastName).ToList(); - var notExistentEmployees = new List(); - - foreach (var payslipsItem in payslips) - { - if (!employeesNames.Contains(payslipsItem.LastName)) - { - notExistentEmployees.Add(payslipsItem.LastName); - } - } - - if (notExistentEmployees.Count != 0) - { - throw new Exception($"Employees with last Names {string.Join(", ", notExistentEmployees)} doesn't exist"); - } + throw new Exception("Payslips list is empty"); } -} + foreach (var payslipsItem in payslips) + { + if (payslipsItem.File.Length == 0) + { + throw new Exception($"'{payslipsItem.File.FileName}' is empty"); + } + + else if (payslipsItem.File.FileName is null) + { + throw new Exception("Payslips name is null"); + } + + else if (!payslipsItem.File.FileName.Contains(payslipsItem.LastName)) + { + throw new Exception($"Last name {payslipsItem.LastName} not contains in file name '{payslipsItem.File.FileName}'"); + } + } + + var employeesNames = employees + .Select(x => x.LastName) + .ToList(); + var notExistentEmployees = new List(); + + foreach (var payslipsItem in payslips) + { + if (!employeesNames.Contains(payslipsItem.LastName)) + { + notExistentEmployees.Add(payslipsItem.LastName); + } + } + + if (notExistentEmployees.Count != 0) + { + throw new Exception($"Employees with last Names {string.Join(", ", notExistentEmployees)} doesn't exist"); + } + } +} diff --git a/Core/Document.cs b/Core/Document.cs index c97aa8d..3a64b4e 100644 --- a/Core/Document.cs +++ b/Core/Document.cs @@ -2,15 +2,14 @@ namespace Core; public class Document { - public long Id { get; set; } + public long Id { get; set; } - public string Name { get; set; } + public string Name { get; set; } - public Document(string name) - { - Name = name; - } + public Document(string name) + { + Name = name; + } - private Document() { } + private Document() { } } - diff --git a/Core/Employee.cs b/Core/Employee.cs index a358943..62ceca6 100644 --- a/Core/Employee.cs +++ b/Core/Employee.cs @@ -2,16 +2,16 @@ namespace Core; public class Employee { - public long EmployeeId { get; } + public long EmployeeId { get; } - public string LastName { get; } + public string LastName { get; } - public string CorporateEmail { get; } + public string CorporateEmail { get; } - public Employee(long employeeId, string fullName, string corporateEmail) - { - EmployeeId = employeeId; - LastName = fullName.Split(' ')[0]; - CorporateEmail = corporateEmail; - } + public Employee(long employeeId, string fullName, string corporateEmail) + { + EmployeeId = employeeId; + LastName = fullName.Split(' ')[0]; + CorporateEmail = corporateEmail; + } } diff --git a/Core/MailPayslipsModel.cs b/Core/MailPayslipsModel.cs index d64612a..9eafbd1 100644 --- a/Core/MailPayslipsModel.cs +++ b/Core/MailPayslipsModel.cs @@ -4,17 +4,19 @@ namespace Core; public class MailPayslipsModel { - public string To { get; set; } - public string Subject { get; set; } - public string Body { get; set; } - public IFormFile File { get; set; } + public string To { get; set; } - public MailPayslipsModel(string to, string subject, string body, IFormFile file) - { - To = to; - Subject = subject; - Body = body; - File = file; - } -} + public string Subject { get; set; } + + public string Body { get; set; } + public IFormFile File { get; set; } + + public MailPayslipsModel(string to, string subject, string body, IFormFile file) + { + To = to; + Subject = subject; + Body = body; + File = file; + } +} diff --git a/Core/PayslipsItem.cs b/Core/PayslipsItem.cs index adfd641..87a8d60 100644 --- a/Core/PayslipsItem.cs +++ b/Core/PayslipsItem.cs @@ -4,16 +4,15 @@ namespace Core; public class PayslipsItem { - public IFormFile File { get; set; } - public string LastName { get; set; } + public IFormFile File { get; set; } - public PayslipsItem(string lastName, IFormFile file) - { - LastName = lastName; - File = file; - } - - public PayslipsItem() { } -} + public string LastName { get; set; } + public PayslipsItem(string lastName, IFormFile file) + { + LastName = lastName; + File = file; + } + public PayslipsItem() { } +} diff --git a/DataAccess/DependencyInjection.cs b/DataAccess/DependencyInjection.cs index c828099..041b482 100644 --- a/DataAccess/DependencyInjection.cs +++ b/DataAccess/DependencyInjection.cs @@ -6,19 +6,19 @@ namespace DataAccess; public static class DependencyInjection { - private const string DefaultConnection = "DefaultConnection"; + private const string DefaultConnection = "DefaultConnection"; - public static void AddPersistence(this IServiceCollection services, IConfiguration configuration) - { - var connectionString = configuration.GetConnectionString(DefaultConnection); + public static void AddPersistence(this IServiceCollection services, IConfiguration configuration) + { + var connectionString = configuration.GetConnectionString(DefaultConnection); - services.AddDbContext(options => - { - options.UseNpgsql(connectionString!, - o => o.UseNodaTime() - ); - }); + services.AddDbContext(options => + { + options.UseNpgsql(connectionString!, + o => o.UseNodaTime() + ); + }); - services.AddScoped(); - } + services.AddScoped(); + } } diff --git a/DataAccess/DocumentsDbContext.cs b/DataAccess/DocumentsDbContext.cs index ac95eaf..fcbcd9a 100644 --- a/DataAccess/DocumentsDbContext.cs +++ b/DataAccess/DocumentsDbContext.cs @@ -1,23 +1,19 @@ -using System.Collections.Generic; -using System.Reflection.Emit; using Core; using Microsoft.EntityFrameworkCore; namespace DataAccess; -//Use next command in Package Manager Console to update Dev env DB -//PM> $env:ASPNETCORE_ENVIRONMENT = 'Debug'; Update-Database public class DocumentsDbContext : DbContext { - public DbSet Documents { get; set; } + public DbSet Documents { get; set; } - public DocumentsDbContext(DbContextOptions options) : base(options) - { - } + public DocumentsDbContext(DbContextOptions options) : base(options) + { + } - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - modelBuilder.ApplyConfigurationsFromAssembly(GetType().Assembly); - base.OnModelCreating(modelBuilder); - } + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.ApplyConfigurationsFromAssembly(GetType().Assembly); + base.OnModelCreating(modelBuilder); + } } diff --git a/DataAccess/Mapping/DocumentsMapping.cs b/DataAccess/Mapping/DocumentsMapping.cs index c301cbe..4b49c9f 100644 --- a/DataAccess/Mapping/DocumentsMapping.cs +++ b/DataAccess/Mapping/DocumentsMapping.cs @@ -1,18 +1,18 @@ +using Core; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using NodaTime; -using Core; namespace DataAccess.Mapping; public class DocumentsMapping : IEntityTypeConfiguration { - public void Configure(EntityTypeBuilder builder) - { - var instantConverter = - new ValueConverter(v => - v.ToDateTimeUtc(), - v => Instant.FromDateTimeUtc(v)); - } + public void Configure(EntityTypeBuilder builder) + { + var instantConverter = new ValueConverter(v => + v.ToDateTimeUtc(), + v => Instant.FromDateTimeUtc(v) + ); + } } diff --git a/Tests/Api/DocumentsControllerTests.cs b/Tests/Api/DocumentsControllerTests.cs index 23c3bd6..4e2ca14 100644 --- a/Tests/Api/DocumentsControllerTests.cs +++ b/Tests/Api/DocumentsControllerTests.cs @@ -2,78 +2,78 @@ using Application; using Application.Services; using Core; +using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.Logging; using Moq; using Xunit; -using Microsoft.AspNetCore.Authorization; namespace Tests.Api; public class DocumentsControllerTests { - private readonly Mock _httpClientMock = new(); - private readonly Mock _payslipsValidatorMock = new(); - private readonly DocumentsController _controller; - private readonly List _payslips = It.IsAny>(); - private readonly List _employees = It.IsAny>(); + private readonly Mock _httpClientMock = new(); + private readonly Mock _payslipsValidatorMock = new(); + private readonly DocumentsController _controller; + private readonly List _payslips = It.IsAny>(); + private readonly List _employees = It.IsAny>(); - public DocumentsControllerTests() - { - var logger = LoggerFactory - .Create(_ => { }) - .CreateLogger(); + public DocumentsControllerTests() + { + var logger = LoggerFactory + .Create(_ => { }) + .CreateLogger(); - _controller = new DocumentsController(_httpClientMock.Object, _payslipsValidatorMock.Object, logger); - } + _controller = new DocumentsController(_httpClientMock.Object, _payslipsValidatorMock.Object, logger); + } - [Fact] - public async Task SendMailingPayslips_ShouldRequireAuthorizeAttribute() - { - var authorizeAttribute = _controller - .GetType() - .GetCustomAttributes( - typeof(AuthorizeAttribute), - true - ); + [Fact] + public async Task SendMailingPayslips_ShouldRequireAuthorizeAttribute() + { + var authorizeAttribute = _controller + .GetType() + .GetCustomAttributes( + typeof(AuthorizeAttribute), + true + ); - Assert.True(authorizeAttribute.Any()); - } + Assert.True(authorizeAttribute.Any()); + } - [Fact] - public async Task SendMailingPayslips_WhenEmailSenderServiceIsNotAvailable_ShouldThrowException() - { - _httpClientMock - .Setup(x => x.SendMailingPayslips(_payslips, _employees)) - .ThrowsAsync(new Exception()); + [Fact] + public async Task SendMailingPayslips_WhenEmailSenderServiceIsNotAvailable_ShouldThrowException() + { + _httpClientMock + .Setup(x => x.SendMailingPayslips(_payslips, _employees)) + .ThrowsAsync(new Exception()); - var exception = await Assert.ThrowsAsync(() => _controller.SendMailingPayslips(_payslips)); + var exception = await Assert.ThrowsAsync(() => _controller.SendMailingPayslips(_payslips)); - Assert.Equal("Email sender service is not available", exception.Message); - } + Assert.Equal("Email sender service is not available", exception.Message); + } - [Fact] - public async Task SendMailingPayslips_WhenEmployeesServiceIsNotAvailable_ShouldThrowException() - { - _httpClientMock - .Setup(x => x.GetEmployeesAsync()) - .ThrowsAsync(new Exception()); + [Fact] + public async Task SendMailingPayslips_WhenEmployeesServiceIsNotAvailable_ShouldThrowException() + { + _httpClientMock + .Setup(x => x.GetEmployeesAsync()) + .ThrowsAsync(new Exception()); - var exception = await Assert.ThrowsAsync(() => _controller.SendMailingPayslips(_payslips)); + var exception = await Assert.ThrowsAsync(() => _controller.SendMailingPayslips(_payslips)); - Assert.Equal("Employees service is not available", exception.Message); - } + Assert.Equal("Employees service is not available", exception.Message); + } - [Fact] - public async Task SendMailingPayslips_WhenServicesAreAvailable_NoException() - { - var exception = await Record.ExceptionAsync(() => _controller.SendMailingPayslips(_payslips)); - Assert.Null(exception); - } + [Fact] + public async Task SendMailingPayslips_WhenServicesAreAvailable_NoException() + { + var exception = await Record.ExceptionAsync(() => _controller.SendMailingPayslips(_payslips)); + Assert.Null(exception); + } - [Fact] - public async Task SendMailingPayslips_PayslipsValidatorShouldBeCalledOnce() - { - await _controller.SendMailingPayslips(_payslips); - _payslipsValidatorMock.Verify(x => x.ValidateAsync(_payslips, _employees)); - } + [Fact] + public async Task SendMailingPayslips_PayslipsValidatorShouldBeCalledOnce() + { + await _controller.SendMailingPayslips(_payslips); + _payslipsValidatorMock.Verify(x => x.ValidateAsync(_payslips, _employees)); + } } diff --git a/Tests/Application/PayslipsValidatorTests.cs b/Tests/Application/PayslipsValidatorTests.cs index e4750a7..300164b 100644 --- a/Tests/Application/PayslipsValidatorTests.cs +++ b/Tests/Application/PayslipsValidatorTests.cs @@ -7,110 +7,113 @@ namespace Tests.Application; public class PayslipsValidatorTests { - private readonly IPayslipsValidator _validator = new PayslipsValidator(); - private readonly List _emptyEmployeeList = new(); - - [Fact] - public async Task PayslipsListMustNotBeEmpty() - { - var exception = await Assert.ThrowsAsync(() => _validator.ValidateAsync(new List(), _emptyEmployeeList)); - Assert.Equal("Payslips list is empty", exception.Message); - } - - [Fact] - public async Task PayslipFilesMustNotBeEmpty() + private readonly IPayslipsValidator _validator = new PayslipsValidator(); + private readonly List _emptyEmployeeList = new(); + + [Fact] + public async Task PayslipsListMustNotBeEmpty() + { + var exception = await Assert.ThrowsAsync(() => _validator.ValidateAsync(new List(), _emptyEmployeeList)); + Assert.Equal("Payslips list is empty", exception.Message); + } + + [Fact] + public async Task PayslipFilesMustNotBeEmpty() + { + var formFile = await GetFormFileAsync("Расчетный листок Иванов за ноябрь 2023 года.pdf", ""); + + var payslipsItems = new List { - var formFile = await GetFormFileAsync("Расчетный листок Иванов за ноябрь 2023 года.pdf", ""); + new PayslipsItem("", formFile), + }; - var payslipsItems = new List { - new PayslipsItem("", formFile), - }; + var exception = await Assert.ThrowsAsync(() => _validator.ValidateAsync(payslipsItems, _emptyEmployeeList)); - var exception = await Assert.ThrowsAsync(() => _validator.ValidateAsync(payslipsItems, _emptyEmployeeList)); + Assert.Equal($"'{formFile.FileName}' is empty", exception.Message); + } - Assert.Equal($"'{formFile.FileName}' is empty", exception.Message); - } + [Fact] + public async Task PayslipFileNameMustNotBeNull() + { + var formFile = await GetFormFileAsync(null, "Something"); - - [Fact] - public async Task PayslipFileNameMustNotBeNull() + var payslipsItems = new List { - var formFile = await GetFormFileAsync(null, "Something"); + new PayslipsItem("", formFile), + }; + + var exception = await Assert.ThrowsAsync(() => _validator.ValidateAsync(payslipsItems, _emptyEmployeeList)); - var payslipsItems = new List { - new PayslipsItem("", formFile), - }; + Assert.Equal("Payslips name is null", exception.Message); + } - var exception = await Assert.ThrowsAsync(() => _validator.ValidateAsync(payslipsItems, _emptyEmployeeList)); + [Fact] + public async Task LastNameMustBeIncludedInPayslipFileName() + { + const string lastName = "Петров"; - Assert.Equal("Payslips name is null", exception.Message); - } + var formFile = await GetFormFileAsync("Расчетный листок Иванов за ноябрь 2023 года.pdf", "Something"); - [Fact] - public async Task LastNameMustBeIncludedInPayslipFileName() + var payslipsItems = new List { - const string lastName = "Петров"; + new PayslipsItem(lastName, formFile), + }; - var formFile = await GetFormFileAsync("Расчетный листок Иванов за ноябрь 2023 года.pdf", "Something"); + var exception = await Assert.ThrowsAsync(() => _validator.ValidateAsync(payslipsItems, _emptyEmployeeList)); - var payslipsItems = new List { - new PayslipsItem(lastName, formFile), - }; + Assert.Equal($"Last name {lastName} not contains in file name '{formFile.FileName}'", exception.Message); + } - var exception = await Assert.ThrowsAsync(() => _validator.ValidateAsync(payslipsItems, _emptyEmployeeList)); + [Fact] + public async Task EmployesWithLastNamesShouldBeExist() + { + const string lastName = "Иванов"; - Assert.Equal($"Last name {lastName} not contains in file name '{formFile.FileName}'", exception.Message); - } + var formFile = await GetFormFileAsync("Расчетный листок Иванов за ноябрь 2023 года.pdf", "Something"); - [Fact] - public async Task EmployesWithLastNamesShouldBeExist() + var payslipsItems = new List { - const string lastName = "Иванов"; + new PayslipsItem(lastName, formFile), + }; - var formFile = await GetFormFileAsync("Расчетный листок Иванов за ноябрь 2023 года.pdf", "Something"); + var employees = new List + { + new Employee(21, "Петров Иван Иванович", "test@mail.com") + }; - var payslipsItems = new List { - new PayslipsItem(lastName, formFile), - }; + var exception = await Assert.ThrowsAsync(() => _validator.ValidateAsync(payslipsItems, employees)); - var employees = new List - { - new Employee(21, "Петров Иван Иванович", "test@mail.com") - }; + Assert.Equal($"Employees with last Names {string.Join(", ", lastName)} doesn't exist", exception.Message); + } - var exception = await Assert.ThrowsAsync(() => _validator.ValidateAsync(payslipsItems, employees)); + [Fact] + public async Task NoException() + { + const string lastName = "Иванов"; - Assert.Equal($"Employees with last Names {string.Join(", ", lastName)} doesn't exist", exception.Message); - } + var formFile = await GetFormFileAsync("Расчетный листок Иванов за ноябрь 2023 года.pdf", "Something"); - [Fact] - public async Task NoException() + var payslipsItems = new List { - const string lastName = "Иванов"; + new PayslipsItem(lastName, formFile), + }; - var formFile = await GetFormFileAsync("Расчетный листок Иванов за ноябрь 2023 года.pdf", "Something"); - - var payslipsItems = new List { - new PayslipsItem(lastName, formFile), - }; - - var employees = new List - { - new Employee(21, "Иванов Иван Иванович", "test@mail.com") - }; - - Assert.Null(Record.Exception(() => _validator.ValidateAsync(payslipsItems, employees).Exception)); - } - - private static async Task GetFormFileAsync(string fileName, string content) + var employees = new List { - var stream = new MemoryStream(); - var writer = new StreamWriter(stream); - await writer.WriteAsync(content); - await writer.FlushAsync(); - stream.Position = 0; - - return new FormFile(stream, 0, stream.Length, "file", fileName); - } + new Employee(21, "Иванов Иван Иванович", "test@mail.com") + }; + + Assert.Null(Record.Exception(() => _validator.ValidateAsync(payslipsItems, employees).Exception)); + } + + private static async Task GetFormFileAsync(string fileName, string content) + { + var stream = new MemoryStream(); + var writer = new StreamWriter(stream); + await writer.WriteAsync(content); + await writer.FlushAsync(); + stream.Position = 0; + + return new FormFile(stream, 0, stream.Length, "file", fileName); + } } -