From 23530774047656966deba1b325279c94b3065cb0 Mon Sep 17 00:00:00 2001 From: prtsie Date: Tue, 27 Aug 2024 10:36:00 +0300 Subject: [PATCH 1/5] removed route attributes --- .../Controllers/UsersController.cs | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/SchengenVisaApi/SchengenVisaApi/Controllers/UsersController.cs b/SchengenVisaApi/SchengenVisaApi/Controllers/UsersController.cs index 98d8baf..4ff4df4 100644 --- a/SchengenVisaApi/SchengenVisaApi/Controllers/UsersController.cs +++ b/SchengenVisaApi/SchengenVisaApi/Controllers/UsersController.cs @@ -23,11 +23,10 @@ public class UsersController( IValidator authDataValidator) : ControllerBase { /// Adds applicant with user account to DB - [HttpPost] + [HttpPost("register")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status409Conflict)] [ProducesResponseType(StatusCodes.Status400BadRequest)] - [Route("register")] public async Task Register(RegisterApplicantRequest request, CancellationToken cancellationToken) { await registerApplicantRequestValidator.ValidateAndThrowAsync(request, cancellationToken); @@ -38,13 +37,12 @@ public class UsersController( /// Adds approving authority with user account to DB /// Accessible only for admins - [HttpPost] + [HttpPost("authorities")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status409Conflict)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status400BadRequest)] - [Route("authorities")] [Authorize(policy: PolicyConstants.AdminPolicy)] public async Task RegisterAuthority(RegisterRequest request, CancellationToken cancellationToken) { @@ -55,10 +53,9 @@ public class UsersController( } /// Returns JWT-token for authentication - [HttpGet] + [HttpGet("login")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] - [Route("login")] public async Task Login(string email, string password, CancellationToken cancellationToken) { var result = await loginService.LoginAsync(email, password, cancellationToken); @@ -67,11 +64,10 @@ public class UsersController( /// Returns list of authority accounts /// Accessible only for admins - [HttpGet] + [HttpGet("authorities")] [ProducesResponseType>(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] - [Route("authorities")] [Authorize(policy: PolicyConstants.AdminPolicy)] public async Task GetAuthorityAccounts(CancellationToken cancellationToken) { @@ -81,13 +77,12 @@ public class UsersController( /// Changes authority's account authentication data /// Accessible only for admins - [HttpPut] + [HttpPut("authorities/{authorityAccountId:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status400BadRequest)] - [Route("authorities/{authorityAccountId:guid}")] [Authorize(policy: PolicyConstants.AdminPolicy)] public async Task ChangeAuthorityAuthData(Guid authorityAccountId, AuthData authData, CancellationToken cancellationToken) { @@ -99,16 +94,15 @@ public class UsersController( /// Removes authority's account authentication data /// Accessible only for admins - [HttpDelete] + [HttpDelete("authorities/{authorityAccountId:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] - [Route("authorities/{authorityAccountId:guid}")] [Authorize(policy: PolicyConstants.AdminPolicy)] public async Task RemoveAuthorityAccount(Guid authorityAccountId, CancellationToken cancellationToken) { await usersService.RemoveUserAccount(authorityAccountId, cancellationToken); return Ok(); } -} \ No newline at end of file +} From b63a245c9ed2aeff0486cce79fbcf92ae39c9af2 Mon Sep 17 00:00:00 2001 From: prtsie Date: Tue, 27 Aug 2024 10:46:30 +0300 Subject: [PATCH 2/5] Return pending applications for authorities --- .../Handlers/IVisaApplicationRequestsHandler.cs | 2 +- .../Handlers/VisaApplicationRequestsHandler.cs | 4 ++-- .../NeededServices/IVisaApplicationsRepository.cs | 3 +++ .../Repositories/VisaApplicationsRepository.cs | 6 ++++++ .../Controllers/VisaApplicationController.cs | 8 ++++---- 5 files changed, 16 insertions(+), 7 deletions(-) diff --git a/SchengenVisaApi/ApplicationLayer/Services/VisaApplications/Handlers/IVisaApplicationRequestsHandler.cs b/SchengenVisaApi/ApplicationLayer/Services/VisaApplications/Handlers/IVisaApplicationRequestsHandler.cs index 6d9fdef..80cce41 100644 --- a/SchengenVisaApi/ApplicationLayer/Services/VisaApplications/Handlers/IVisaApplicationRequestsHandler.cs +++ b/SchengenVisaApi/ApplicationLayer/Services/VisaApplications/Handlers/IVisaApplicationRequestsHandler.cs @@ -6,7 +6,7 @@ namespace ApplicationLayer.Services.VisaApplications.Handlers; public interface IVisaApplicationRequestsHandler { /// Returns all applications for approving authorities - Task> GetAllAsync(CancellationToken cancellationToken); + Task> GetPendingAsync(CancellationToken cancellationToken); /// Returns all applications of one applicant Task> GetForApplicantAsync(CancellationToken cancellationToken); diff --git a/SchengenVisaApi/ApplicationLayer/Services/VisaApplications/Handlers/VisaApplicationRequestsHandler.cs b/SchengenVisaApi/ApplicationLayer/Services/VisaApplications/Handlers/VisaApplicationRequestsHandler.cs index 63c3e25..4088eae 100644 --- a/SchengenVisaApi/ApplicationLayer/Services/VisaApplications/Handlers/VisaApplicationRequestsHandler.cs +++ b/SchengenVisaApi/ApplicationLayer/Services/VisaApplications/Handlers/VisaApplicationRequestsHandler.cs @@ -19,9 +19,9 @@ public class VisaApplicationRequestsHandler( IDateTimeProvider dateTimeProvider, IUserIdProvider userIdProvider) : IVisaApplicationRequestsHandler { - async Task> IVisaApplicationRequestsHandler.GetAllAsync(CancellationToken cancellationToken) + async Task> IVisaApplicationRequestsHandler.GetPendingAsync(CancellationToken cancellationToken) { - var applicationsList = await applications.GetAllAsync(cancellationToken); + var applicationsList = await applications.GetPendingApplicationsAsync(cancellationToken); var applicationModels = applicationsList .Select(a => MapVisaApplicationToModelForAuthorities(a, cancellationToken).Result) diff --git a/SchengenVisaApi/ApplicationLayer/Services/VisaApplications/NeededServices/IVisaApplicationsRepository.cs b/SchengenVisaApi/ApplicationLayer/Services/VisaApplications/NeededServices/IVisaApplicationsRepository.cs index 5e132a8..270636d 100644 --- a/SchengenVisaApi/ApplicationLayer/Services/VisaApplications/NeededServices/IVisaApplicationsRepository.cs +++ b/SchengenVisaApi/ApplicationLayer/Services/VisaApplications/NeededServices/IVisaApplicationsRepository.cs @@ -10,4 +10,7 @@ public interface IVisaApplicationsRepository : IGenericRepository GetByApplicantAndApplicationIdAsync(Guid applicantId, Guid applicationId, CancellationToken cancellationToken); + + /// Returns pending applications for approving authorities + Task> GetPendingApplicationsAsync(CancellationToken cancellationToken); } diff --git a/SchengenVisaApi/Infrastructure/Database/VisaApplications/Repositories/VisaApplicationsRepository.cs b/SchengenVisaApi/Infrastructure/Database/VisaApplications/Repositories/VisaApplicationsRepository.cs index 2a40dd7..bfc6224 100644 --- a/SchengenVisaApi/Infrastructure/Database/VisaApplications/Repositories/VisaApplicationsRepository.cs +++ b/SchengenVisaApi/Infrastructure/Database/VisaApplications/Repositories/VisaApplicationsRepository.cs @@ -27,4 +27,10 @@ public sealed class VisaApplicationsRepository(IGenericReader reader, IGenericWr .SingleOrDefaultAsync(va => va.Id == applicationId && va.ApplicantId == applicantId, cancellationToken); return result ?? throw new ApplicationNotFoundByApplicantAndApplicationIdException(applicationId); } + + async Task> IVisaApplicationsRepository.GetPendingApplicationsAsync(CancellationToken cancellationToken) + { + var result = LoadDomain().Where(va => va.Status == ApplicationStatus.Pending); + return await result.ToListAsync(cancellationToken); + } } diff --git a/SchengenVisaApi/SchengenVisaApi/Controllers/VisaApplicationController.cs b/SchengenVisaApi/SchengenVisaApi/Controllers/VisaApplicationController.cs index bbeb650..6bb5bac 100644 --- a/SchengenVisaApi/SchengenVisaApi/Controllers/VisaApplicationController.cs +++ b/SchengenVisaApi/SchengenVisaApi/Controllers/VisaApplicationController.cs @@ -15,16 +15,16 @@ public class VisaApplicationController( IVisaApplicationRequestsHandler visaApplicationRequestsHandler, IValidator visaApplicationCreateRequestValidator) : ControllerBase { - /// Returns all applications from DB + /// Returns pending applications from DB /// Accessible only for approving authorities - [HttpGet] + [HttpGet("pending")] [ProducesResponseType>(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [Authorize(policy: PolicyConstants.ApprovingAuthorityPolicy)] public async Task Get(CancellationToken cancellationToken) { - var result = await visaApplicationRequestsHandler.GetAllAsync(cancellationToken); + var result = await visaApplicationRequestsHandler.GetPendingAsync(cancellationToken); return Ok(result); } @@ -36,7 +36,7 @@ public class VisaApplicationController( [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status404NotFound)] [Authorize(policy: PolicyConstants.ApplicantPolicy)] - [Route("OfApplicant")] + [Route("ofApplicant")] public async Task GetForApplicant(CancellationToken cancellationToken) { var result = await visaApplicationRequestsHandler.GetForApplicantAsync(cancellationToken); From 8ad57afdd05fc31544c4dfdd24de3a56f3672493 Mon Sep 17 00:00:00 2001 From: prtsie Date: Tue, 27 Aug 2024 16:04:08 +0300 Subject: [PATCH 3/5] Edited comments and removed route attributes --- .../Controllers/UsersController.cs | 6 +++--- .../Controllers/VisaApplicationController.cs | 21 ++++++++----------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/SchengenVisaApi/SchengenVisaApi/Controllers/UsersController.cs b/SchengenVisaApi/SchengenVisaApi/Controllers/UsersController.cs index 4ff4df4..fc10d47 100644 --- a/SchengenVisaApi/SchengenVisaApi/Controllers/UsersController.cs +++ b/SchengenVisaApi/SchengenVisaApi/Controllers/UsersController.cs @@ -22,7 +22,7 @@ public class UsersController( IValidator registerApplicantRequestValidator, IValidator authDataValidator) : ControllerBase { - /// Adds applicant with user account to DB + /// Adds applicant with user account [HttpPost("register")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status409Conflict)] @@ -35,7 +35,7 @@ public class UsersController( return Ok(); } - /// Adds approving authority with user account to DB + /// Adds approving authority with user account /// Accessible only for admins [HttpPost("authorities")] [ProducesResponseType(StatusCodes.Status200OK)] @@ -92,7 +92,7 @@ public class UsersController( return Ok(); } - /// Removes authority's account authentication data + /// Removes authority's account /// Accessible only for admins [HttpDelete("authorities/{authorityAccountId:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] diff --git a/SchengenVisaApi/SchengenVisaApi/Controllers/VisaApplicationController.cs b/SchengenVisaApi/SchengenVisaApi/Controllers/VisaApplicationController.cs index 6bb5bac..b415f16 100644 --- a/SchengenVisaApi/SchengenVisaApi/Controllers/VisaApplicationController.cs +++ b/SchengenVisaApi/SchengenVisaApi/Controllers/VisaApplicationController.cs @@ -8,21 +8,21 @@ using SchengenVisaApi.Common; namespace SchengenVisaApi.Controllers; -/// Controller for +/// Controller for visa applications [ApiController] [Route("visaApplications")] public class VisaApplicationController( IVisaApplicationRequestsHandler visaApplicationRequestsHandler, IValidator visaApplicationCreateRequestValidator) : ControllerBase { - /// Returns pending applications from DB + /// Returns pending applications /// Accessible only for approving authorities [HttpGet("pending")] [ProducesResponseType>(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [Authorize(policy: PolicyConstants.ApprovingAuthorityPolicy)] - public async Task Get(CancellationToken cancellationToken) + public async Task GetPending(CancellationToken cancellationToken) { var result = await visaApplicationRequestsHandler.GetPendingAsync(cancellationToken); return Ok(result); @@ -30,20 +30,19 @@ public class VisaApplicationController( /// Returns all applications of one applicant /// Returns applications of authorized applicant - [HttpGet] + [HttpGet("ofApplicant")] [ProducesResponseType>(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status404NotFound)] [Authorize(policy: PolicyConstants.ApplicantPolicy)] - [Route("ofApplicant")] public async Task GetForApplicant(CancellationToken cancellationToken) { var result = await visaApplicationRequestsHandler.GetForApplicantAsync(cancellationToken); return Ok(result); } - /// Adds new application to DB + /// Adds new application /// Adds application for authorized applicant [HttpPost] [ProducesResponseType(StatusCodes.Status200OK)] @@ -52,7 +51,7 @@ public class VisaApplicationController( [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [Authorize(policy: PolicyConstants.ApplicantPolicy)] - public async Task Create(VisaApplicationCreateRequest request, CancellationToken cancellationToken) + public async Task CreateApplication(VisaApplicationCreateRequest request, CancellationToken cancellationToken) { await visaApplicationCreateRequestValidator.ValidateAndThrowAsync(request, cancellationToken); @@ -62,28 +61,26 @@ public class VisaApplicationController( /// Sets application status to closed /// Accessible only for applicant - [HttpPatch] + [HttpPatch("{applicationId:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status404NotFound)] [Authorize(policy: PolicyConstants.ApplicantPolicy)] - [Route("{applicationId:guid}")] public async Task CloseApplication(Guid applicationId, CancellationToken cancellationToken) { await visaApplicationRequestsHandler.HandleCloseRequestAsync(applicationId, cancellationToken); return Ok(); } - /// Allows approving authorities approve or reject applications + /// Approve or reject applications /// Accessible only for authorities - [HttpPatch] + [HttpPatch("approving/{applicationId:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status404NotFound)] [Authorize(policy: PolicyConstants.ApprovingAuthorityPolicy)] - [Route("approving/{applicationId:guid}")] public async Task SetStatusFromAuthority(Guid applicationId, AuthorityRequestStatuses status, CancellationToken cancellationToken) { await visaApplicationRequestsHandler.SetApplicationStatusFromAuthorityAsync(applicationId, status, cancellationToken); From 8700c78f1a3f8b7208e82c1032f77c0eca7c2647 Mon Sep 17 00:00:00 2001 From: prtsie Date: Tue, 27 Aug 2024 21:16:55 +0300 Subject: [PATCH 4/5] Changes for client generation --- .../Domains/VisaApplicationDomain/VisaCategory.cs | 2 +- SchengenVisaApi/SchengenVisaApi/DependencyInjection.cs | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/SchengenVisaApi/Domains/VisaApplicationDomain/VisaCategory.cs b/SchengenVisaApi/Domains/VisaApplicationDomain/VisaCategory.cs index c3690f6..cd41d76 100644 --- a/SchengenVisaApi/Domains/VisaApplicationDomain/VisaCategory.cs +++ b/SchengenVisaApi/Domains/VisaApplicationDomain/VisaCategory.cs @@ -5,4 +5,4 @@ public enum VisaCategory { Transit, ShortDated -} \ No newline at end of file +} diff --git a/SchengenVisaApi/SchengenVisaApi/DependencyInjection.cs b/SchengenVisaApi/SchengenVisaApi/DependencyInjection.cs index 90693bb..edc5c29 100644 --- a/SchengenVisaApi/SchengenVisaApi/DependencyInjection.cs +++ b/SchengenVisaApi/SchengenVisaApi/DependencyInjection.cs @@ -1,6 +1,7 @@ using System.Reflection; using System.Security.Claims; using System.Text; +using System.Text.Json.Serialization; using ApplicationLayer; using Domains.Users; using Infrastructure; @@ -42,7 +43,8 @@ public static class DependencyInjection services.AddProblemDetails(); - services.AddControllers(opts => opts.Filters.Add()); + services.AddControllers(opts => opts.Filters.Add()) + .AddJsonOptions(options => options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter())); } /// Adds authentication, authorization and token generator @@ -95,6 +97,9 @@ public static class DependencyInjection { var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename)); + + options.CustomOperationIds(apiDescription => + apiDescription.TryGetMethodInfo(out MethodInfo methodInfo) ? methodInfo.Name : null); }); } } From 1be599fe1ce1544001ae1ab2c4eee6adfadc2851 Mon Sep 17 00:00:00 2001 From: prtsie Date: Wed, 28 Aug 2024 11:57:36 +0300 Subject: [PATCH 5/5] Moved swagger configuration to one place --- .../Common/ConfigureSwaggerOptions.cs | 37 ------------------- .../SchengenVisaApi/DependencyInjection.cs | 28 +++++++++++++- 2 files changed, 26 insertions(+), 39 deletions(-) delete mode 100644 SchengenVisaApi/SchengenVisaApi/Common/ConfigureSwaggerOptions.cs diff --git a/SchengenVisaApi/SchengenVisaApi/Common/ConfigureSwaggerOptions.cs b/SchengenVisaApi/SchengenVisaApi/Common/ConfigureSwaggerOptions.cs deleted file mode 100644 index 4c7da47..0000000 --- a/SchengenVisaApi/SchengenVisaApi/Common/ConfigureSwaggerOptions.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Microsoft.Extensions.Options; -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; - -namespace SchengenVisaApi.Common; - -/// Adds auth for swagger -public class ConfigureSwaggerOptions : IConfigureOptions -{ - void IConfigureOptions.Configure(SwaggerGenOptions options) - { - options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme - { - In = ParameterLocation.Header, - Description = "Provide a JWT-token.", - Name = "Authorization", - Type = SecuritySchemeType.Http, - BearerFormat = "JWT", - Scheme = "Bearer" - }); - - options.AddSecurityRequirement(new OpenApiSecurityRequirement - { - { - new OpenApiSecurityScheme - { - Reference = new OpenApiReference - { - Type = ReferenceType.SecurityScheme, - Id = "Bearer" - } - }, - Array.Empty() - } - }); - } -} \ No newline at end of file diff --git a/SchengenVisaApi/SchengenVisaApi/DependencyInjection.cs b/SchengenVisaApi/SchengenVisaApi/DependencyInjection.cs index edc5c29..8f1b336 100644 --- a/SchengenVisaApi/SchengenVisaApi/DependencyInjection.cs +++ b/SchengenVisaApi/SchengenVisaApi/DependencyInjection.cs @@ -8,8 +8,8 @@ using Infrastructure; using Infrastructure.Auth; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authorization; -using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; +using Microsoft.OpenApi.Models; using SchengenVisaApi.Common; using SchengenVisaApi.ExceptionFilters; using Swashbuckle.AspNetCore.SwaggerGen; @@ -92,7 +92,6 @@ public static class DependencyInjection /// Add swagger private static void AddSwagger(this IServiceCollection services) { - services.AddTransient, ConfigureSwaggerOptions>(); services.AddSwaggerGen(options => { var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; @@ -100,6 +99,31 @@ public static class DependencyInjection options.CustomOperationIds(apiDescription => apiDescription.TryGetMethodInfo(out MethodInfo methodInfo) ? methodInfo.Name : null); + + options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme + { + In = ParameterLocation.Header, + Description = "Provide a JWT-token.", + Name = "Authorization", + Type = SecuritySchemeType.Http, + BearerFormat = "JWT", + Scheme = "Bearer" + }); + + options.AddSecurityRequirement(new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "Bearer" + } + }, + Array.Empty() + } + }); }); } }