update apis to minimal-api

This commit is contained in:
meysamhadeli 2022-11-16 00:39:05 +03:30
parent 29d9daf544
commit 27a0b74a9f
30 changed files with 652 additions and 322 deletions

View File

@ -33,19 +33,19 @@ grant_type=password
### ###
# @name Register_New_User # @name Register_New_User
POST {{api-gateway}}/identity/register-user POST {{api-gateway}}/api/v1/identity/register-user
accept: application/json accept: application/json
Content-Type: application/json Content-Type: application/json
authorization: bearer {{Authenticate.response.body.access_token}} authorization: bearer {{Authenticate.response.body.access_token}}
{ {
"firstName": "John", "firstName": "4John",
"lastName": "Do", "lastName": "4Do",
"username": "admin", "username": "4admin",
"passportNumber": "12900000000", "passportNumber": "412900000000",
"email": "admin@admin.com", "email": "4admin@admin.com",
"password": "Admin@12345", "password": "4Admin@12345",
"confirmPassword": "Admin@12345" "confirmPassword": "4Admin@12345"
} }
### ###
@ -64,7 +64,7 @@ Content-Type: application/json
authorization: bearer {{Authenticate.response.body.access_token}} authorization: bearer {{Authenticate.response.body.access_token}}
{ {
"seatNumber": "12H9", "seatNumber": "1255",
"type": 1, "type": 1,
"class": 1, "class": 1,
"flightId": 1 "flightId": 1
@ -81,7 +81,7 @@ authorization: bearer {{Authenticate.response.body.access_token}}
{ {
"flightId": 1, "flightId": 1,
"seatNumber": "12H9" "seatNumber": "1255"
} }
### ###
@ -161,14 +161,10 @@ authorization: bearer {{Authenticate.response.body.access_token}}
### ###
# @name Delete_Flights # @name Delete_Flights
DELETE {{api-gateway}}/api/v1/flight DELETE {{api-gateway}}/api/v1/flight/{{flightid}}
accept: application/json accept: application/json
Content-Type: application/json Content-Type: application/json
authorization: bearer {{Authenticate.response.body.access_token}} authorization: bearer {{Authenticate.response.body.access_token}}
{
"id": 1
}
### ###
@ -219,7 +215,7 @@ Content-Type: application/json
authorization: bearer {{Authenticate.response.body.access_token}} authorization: bearer {{Authenticate.response.body.access_token}}
{ {
"passportNumber": "12900000000", "passportNumber": "412900000000",
"passengerType": 1, "passengerType": 1,
"age": 30 "age": 30
} }
@ -251,7 +247,7 @@ Content-Type: application/json
authorization: bearer {{Authenticate.response.body.access_token}} authorization: bearer {{Authenticate.response.body.access_token}}
{ {
"passengerId": 7201620062109696, "passengerId": 7225627535474688,
"flightId": 1, "flightId": 1,
"description": "I want to fly to iran" "description": "I want to fly to iran"
} }

View File

@ -8,6 +8,10 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Ardalis.GuardClauses" Version="4.0.1" /> <PackageReference Include="Ardalis.GuardClauses" Version="4.0.1" />
<PackageReference Include="Asp.Versioning.Abstractions" Version="7.0.0-preview.1" />
<PackageReference Include="Asp.Versioning.Http" Version="7.0.0-preview.1" />
<PackageReference Include="Asp.Versioning.Mvc" Version="7.0.0-preview.1" />
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="7.0.0-preview.1" />
<PackageReference Include="AspNetCore.HealthChecks.Elasticsearch" Version="6.0.2" /> <PackageReference Include="AspNetCore.HealthChecks.Elasticsearch" Version="6.0.2" />
<PackageReference Include="AspNetCore.HealthChecks.EventStore" Version="6.0.3" /> <PackageReference Include="AspNetCore.HealthChecks.EventStore" Version="6.0.3" />
<PackageReference Include="AspNetCore.HealthChecks.Rabbitmq" Version="6.0.2" /> <PackageReference Include="AspNetCore.HealthChecks.Rabbitmq" Version="6.0.2" />
@ -22,6 +26,7 @@
<PackageReference Include="FluentValidation" Version="11.3.0" /> <PackageReference Include="FluentValidation" Version="11.3.0" />
<PackageReference Include="FluentValidation.AspNetCore" Version="11.2.2" /> <PackageReference Include="FluentValidation.AspNetCore" Version="11.2.2" />
<PackageReference Include="Grpc.Core.Testing" Version="2.46.5" /> <PackageReference Include="Grpc.Core.Testing" Version="2.46.5" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyModel" Version="7.0.0" />
<PackageReference Include="Mongo2Go" Version="3.1.3" /> <PackageReference Include="Mongo2Go" Version="3.1.3" />
@ -41,7 +46,6 @@
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="11.0.0" /> <PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="11.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" /> <PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer" Version="5.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="7.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="7.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.0" />
@ -67,10 +71,10 @@
<PackageReference Include="Serilog.Sinks.Seq" Version="5.2.1" /> <PackageReference Include="Serilog.Sinks.Seq" Version="5.2.1" />
<PackageReference Include="Serilog.Sinks.SpectreConsole" Version="0.3.3" /> <PackageReference Include="Serilog.Sinks.SpectreConsole" Version="0.3.3" />
<PackageReference Include="Serilog.Sinks.XUnit" Version="3.0.3" /> <PackageReference Include="Serilog.Sinks.XUnit" Version="3.0.3" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.4.0" /> <PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.4.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.4.0" /> <PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.4.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.4.0" /> <PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.4.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
<PackageReference Include="AspNetCore.HealthChecks.UI" Version="6.0.5" /> <PackageReference Include="AspNetCore.HealthChecks.UI" Version="6.0.5" />
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="6.0.5" /> <PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="6.0.5" />
<PackageReference Include="AspNetCore.HealthChecks.UI.InMemory.Storage" Version="6.0.5" /> <PackageReference Include="AspNetCore.HealthChecks.UI.InMemory.Storage" Version="6.0.5" />

View File

@ -1,48 +1,90 @@
using System; using System.Text;
using Microsoft.AspNetCore.Mvc.ApiExplorer; using Asp.Versioning;
using Asp.Versioning.ApiExplorer;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen; using Swashbuckle.AspNetCore.SwaggerGen;
namespace BuildingBlocks.Swagger namespace BuildingBlocks.Swagger;
public class ConfigureSwaggerOptions : IConfigureOptions<SwaggerGenOptions>
{ {
public class ConfigureSwaggerOptions : IConfigureOptions<SwaggerGenOptions> private readonly IApiVersionDescriptionProvider provider;
private readonly SwaggerOptions? _options;
/// <summary>
/// Initializes a new instance of the <see cref="ConfigureSwaggerOptions"/> class.
/// </summary>
/// <param name="provider">The <see cref="IApiVersionDescriptionProvider">provider</see> used to generate Swagger documents.</param>
public ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider, IOptions<SwaggerOptions> options)
{ {
private readonly IApiVersionDescriptionProvider _provider; this.provider = provider;
private readonly SwaggerOptions _options; _options = options.Value;
}
public ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider, IOptions<SwaggerOptions> options) /// <inheritdoc />
public void Configure(SwaggerGenOptions options)
{
// add a swagger document for each discovered API version
// note: you might choose to skip or document deprecated API versions differently
foreach (var description in provider.ApiVersionDescriptions)
{ {
_provider = provider; options.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description));
_options = options.Value;
}
public void Configure(SwaggerGenOptions options)
{
foreach (var description in _provider.ApiVersionDescriptions)
{
options.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description));
}
}
private OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description)
{
var info = new OpenApiInfo
{
Title = _options.Title ?? "APIs",
Version =_options.Version ?? description.ApiVersion.ToString(),
Description = "An application with Swagger, Swashbuckle, and API versioning.",
Contact = new OpenApiContact { Name = "", Email = "" },
License = new OpenApiLicense { Name = "MIT", Url = new Uri("https://opensource.org/licenses/MIT") }
};
if (description.IsDeprecated)
{
info.Description += " This API version has been deprecated.";
}
return info;
} }
} }
private OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description)
{
var text = new StringBuilder("An example application with OpenAPI, Swashbuckle, and API versioning.");
var info = new OpenApiInfo
{
Version = description.ApiVersion.ToString(),
Title = _options?.Title ?? "APIs",
Description = "An application with Swagger, Swashbuckle, and API versioning.",
Contact = new OpenApiContact {Name = "", Email = ""},
License = new OpenApiLicense {Name = "MIT", Url = new Uri("https://opensource.org/licenses/MIT")}
};
if (description.IsDeprecated)
{
text.Append("This API version has been deprecated.");
}
if (description.SunsetPolicy is SunsetPolicy policy)
{
if (policy.Date is DateTimeOffset when)
{
text.Append(" The API will be sunset on ")
.Append(when.Date.ToShortDateString())
.Append('.');
}
if (policy.HasLinks)
{
text.AppendLine();
for (var i = 0; i < policy.Links.Count; i++)
{
var link = policy.Links[i];
if (link.Type == "text/html")
{
text.AppendLine();
if (link.Title.HasValue)
{
text.Append(link.Title.Value).Append(": ");
}
text.Append(link.LinkTarget.OriginalString);
}
}
}
}
info.Description = text.ToString();
return info;
}
} }

View File

@ -1,46 +1,36 @@
using System.Reflection; using System.Reflection;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.Extensions.PlatformAbstractions;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen; using Swashbuckle.AspNetCore.SwaggerGen;
namespace BuildingBlocks.Swagger; namespace BuildingBlocks.Swagger;
//https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/master/README.md
public static class ServiceCollectionExtensions public static class ServiceCollectionExtensions
{ {
public const string HeaderName = "X-Api-Key"; public const string HeaderName = "X-Api-Key";
public const string HeaderVersion = "api-version";
// https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/master/README.md
// https://github.com/dotnet/aspnet-api-versioning/tree/88323136a97a59fcee24517a514c1a445530c7e2/examples/AspNetCore/WebApi/MinimalOpenApiExample
public static IServiceCollection AddCustomSwagger(this IServiceCollection services, public static IServiceCollection AddCustomSwagger(this IServiceCollection services,
IConfiguration configuration, IConfiguration configuration,
Assembly assembly, string swaggerSectionName = "SwaggerOptions") Assembly assembly)
{ {
services.AddVersionedApiExplorer(options => // https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/openapi
{ services.AddEndpointsApiExplorer();
// add the versioned api explorer, which also adds IApiVersionDescriptionProvider service
// note: the specified format code will format the version as "'v'major[.minor][-status]"
options.GroupNameFormat = "'v'VVV";
// note: this option is only necessary when versioning by url segment. the SubstitutionFormat
// can also be used to control the format of the API version in route templates
options.SubstituteApiVersionInUrl = true;
});
services.AddOptions<SwaggerOptions>().Bind(configuration.GetSection(swaggerSectionName))
.ValidateDataAnnotations();
services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>(); services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();
services.AddOptions<SwaggerOptions>().Bind(configuration.GetSection(nameof(SwaggerOptions)))
.ValidateDataAnnotations();
services.AddSwaggerGen( services.AddSwaggerGen(
options => options =>
{ {
// options.DescribeAllParametersInCamelCase();
options.OperationFilter<SwaggerDefaultValues>(); options.OperationFilter<SwaggerDefaultValues>();
var xmlFile = XmlCommentsFilePath(assembly); var xmlFile = XmlCommentsFilePath(assembly);
if (File.Exists(xmlFile)) options.IncludeXmlComments(xmlFile); if (File.Exists(xmlFile)) options.IncludeXmlComments(xmlFile);
@ -55,7 +45,8 @@ public static class ServiceCollectionExtensions
Scheme = "Bearer" Scheme = "Bearer"
}); });
options.AddSecurityDefinition(HeaderName, options.AddSecurityDefinition(
HeaderName,
new OpenApiSecurityScheme new OpenApiSecurityScheme
{ {
Description = "Api key needed to access the endpoints. X-Api-Key: My_API_Key", Description = "Api key needed to access the endpoints. X-Api-Key: My_API_Key",
@ -82,49 +73,52 @@ public static class ServiceCollectionExtensions
Name = HeaderName, Name = HeaderName,
Type = SecuritySchemeType.ApiKey, Type = SecuritySchemeType.ApiKey,
In = ParameterLocation.Header, In = ParameterLocation.Header,
Reference = new OpenApiReference {Type = ReferenceType.SecurityScheme, Id = HeaderName} Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme, Id = HeaderName
}
}, },
new string[] { } Array.Empty<string>()
} }
}); });
//https://rimdev.io/swagger-grouping-with-controller-name-fallback-using-swashbuckle-aspnetcore/ options.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
options.TagActionsBy(api =>
{
if (api.GroupName != null) return new[] {api.GroupName};
if (api.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor) ////https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/467
return new[] {controllerActionDescriptor.ControllerName}; // options.OperationFilter<TagByApiExplorerSettingsOperationFilter>();
// options.OperationFilter<TagBySwaggerOperationFilter>();
throw new InvalidOperationException("Unable to determine tag for endpoint.");
});
options.DocInclusionPredicate((name, api) => true);
// Enables Swagger annotations (SwaggerOperationAttribute, SwaggerParameterAttribute etc.)
options.EnableAnnotations(); options.EnableAnnotations();
}); });
services.Configure<SwaggerGeneratorOptions>(o => o.InferSecuritySchemes = true);
return services;
static string XmlCommentsFilePath(Assembly assembly) static string XmlCommentsFilePath(Assembly assembly)
{ {
var basePath = PlatformServices.Default.Application.ApplicationBasePath; var basePath = Path.GetDirectoryName(assembly.Location);
var fileName = assembly.GetName().Name + ".xml"; var fileName = assembly.GetName().Name + ".xml";
return Path.Combine(basePath, fileName); return Path.Combine(basePath, fileName);
} }
return services;
} }
public static IApplicationBuilder UseCustomSwagger(this IApplicationBuilder app, public static IApplicationBuilder UseCustomSwagger(this WebApplication app)
IApiVersionDescriptionProvider provider)
{ {
app.UseSwagger(); app.UseSwagger();
app.UseSwaggerUI( app.UseSwaggerUI(
options => options =>
{ {
foreach (var description in provider.ApiVersionDescriptions) var descriptions = app.DescribeApiVersions();
options.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json",
description.GroupName.ToUpperInvariant()); // build a swagger endpoint for each discovered API version
foreach (var description in descriptions)
{
var url = $"/swagger/{description.GroupName}/swagger.json";
var name = description.GroupName.ToUpperInvariant();
options.SwaggerEndpoint(url, name);
}
}); });
return app; return app;

View File

@ -1,34 +1,54 @@
using Microsoft.AspNetCore.Mvc; using Asp.Versioning;
using Microsoft.AspNetCore.Mvc.Versioning;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace BuildingBlocks.Web; namespace BuildingBlocks.Web;
public static class ApiVersioningExtensions public static class ApiVersioningExtensions
{ {
public static void AddCustomVersioning(this IServiceCollection services, public static void AddCustomVersioning(
Action<ApiVersioningOptions> configurator = null) this IServiceCollection services,
Action<ApiVersioningOptions>? configurator = null)
{ {
//https://www.meziantou.net/versioning-an-asp-net-core-api.htm // https://www.meziantou.net/versioning-an-asp-net-core-api.htm
//https://exceptionnotfound.net/overview-of-api-versioning-in-asp-net-core-3-0/ // https://dotnetthoughts.net/aspnetcore-api-versioning-with-net-6-minimal-apis/
// https://im5tu.io/article/2022/10/asp.net-core-versioning-minimal-apis/
// https://www.youtube.com/watch?v=YRJGKyzjFlY
// https://www.nuget.org/packages/Asp.Versioning.Http
// Support versioning in minimal apis with (Asp.Versioning.Http) dll
services.AddApiVersioning(options => services.AddApiVersioning(options =>
{ {
// Add the headers "api-supported-versions" and "api-deprecated-versions" // Add the headers "api-supported-versions" and "api-deprecated-versions"
// This is better for discoverability // This is better for discoverability
options.ReportApiVersions = true; options.ReportApiVersions = true;
// AssumeDefaultVersionWhenUnspecified should only be enabled when supporting legacy services that did not previously // AssumeDefaultVersionWhenUnspecified should only be enabled when supporting legacy services that did not previously
// support API versioning. Forcing existing clients to specify an explicit API version for an // support API versioning. Forcing existing clients to specify an explicit API version for an
// existing service introduces a breaking change. Conceptually, clients in this situation are // existing service introduces a breaking change. Conceptually, clients in this situation are
// bound to some API version of a service, but they don't know what it is and never explicit request it. // bound to some API version of a service, but they don't know what it is and never explicit request it.
options.AssumeDefaultVersionWhenUnspecified = true; options.AssumeDefaultVersionWhenUnspecified = true;
options.DefaultApiVersion = new ApiVersion(1, 0); options.DefaultApiVersion = new ApiVersion(1, 0);
// // Defines how an API version is read from the current HTTP request // Defines how an API version is read from the current HTTP request
options.ApiVersionReader = ApiVersionReader.Combine(new HeaderApiVersionReader("api-version"), options.ApiVersionReader = ApiVersionReader.Combine(
new UrlSegmentApiVersionReader()); new HeaderApiVersionReader("api-version"),
new UrlSegmentApiVersionReader());
configurator?.Invoke(options); configurator?.Invoke(options);
}); })
.AddApiExplorer(
options =>
{
// add the versioned api explorer, which also adds IApiVersionDescriptionProvider service
// note: the specified format code will format the version as "'v'major[.minor][-status]"
options.GroupNameFormat = "'v'VVV";
// note: this option is only necessary when versioning by url segment. the SubstitutionFormat
// can also be used to control the format of the API version in route templates
options.SubstituteApiVersionInUrl = true;
})
// Support versioning in mvc with with (Asp.Versioning.Mvc.ApiExplorer) dll
.AddMvc(); // https://www.nuget.org/packages/Asp.Versioning.Mvc.ApiExplorer
} }
} }

View File

@ -1,4 +1,5 @@
using AutoMapper; using Asp.Versioning;
using AutoMapper;
using MediatR; using MediatR;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;

View File

@ -0,0 +1,9 @@
using Asp.Versioning.Builder;
namespace BuildingBlocks.Web;
public class EndpointConfig
{
public const string BaseApiPath = "api/v{version:apiVersion}";
public static ApiVersionSet VersionSet { get; private set; } = default!;
}

View File

@ -0,0 +1,8 @@
using Microsoft.AspNetCore.Routing;
namespace BuildingBlocks.Web;
public interface IMinimalEndpoint
{
IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder builder);
}

View File

@ -0,0 +1,43 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Scrutor;
namespace BuildingBlocks.Web;
public static class MinimalApiExtensions
{
public static IServiceCollection AddMinimalEndpoints(
this WebApplicationBuilder applicationBuilder,
ServiceLifetime lifetime = ServiceLifetime.Scoped)
{
applicationBuilder.Services.Scan(scan => scan
.FromAssemblies(AppDomain.CurrentDomain.GetAssemblies())
.AddClasses(classes => classes.AssignableTo(typeof(IMinimalEndpoint)))
.UsingRegistrationStrategy(RegistrationStrategy.Append)
.As<IMinimalEndpoint>()
.WithLifetime(lifetime));
return applicationBuilder.Services;
}
/// <summary>
/// Map Minimal Endpoints
/// </summary>
/// <name>builder.</name>
/// <returns>IEndpointRouteBuilder.</returns>
public static IEndpointRouteBuilder MapMinimalEndpoints(this IEndpointRouteBuilder builder)
{
var scope = builder.ServiceProvider.CreateScope();
var endpoints = scope.ServiceProvider.GetServices<IMinimalEndpoint>();
foreach (var endpoint in endpoints)
{
endpoint.MapEndpoint(builder);
}
return builder;
}
}

View File

@ -34,7 +34,6 @@ builder.Services.AddMongoDbContext<BookingReadDbContext>(configuration);
builder.AddCustomSerilog(env); builder.AddCustomSerilog(env);
builder.Services.AddCore(); builder.Services.AddCore();
builder.Services.AddJwt(); builder.Services.AddJwt();
builder.Services.AddControllers();
builder.Services.AddHttpContextAccessor(); builder.Services.AddHttpContextAccessor();
builder.Services.AddCustomSwagger(configuration, typeof(BookingRoot).Assembly); builder.Services.AddCustomSwagger(configuration, typeof(BookingRoot).Assembly);
builder.Services.AddCustomVersioning(); builder.Services.AddCustomVersioning();
@ -55,12 +54,13 @@ builder.Services.AddEventStore(configuration, typeof(BookingRoot).Assembly)
builder.Services.AddGrpcClients(); builder.Services.AddGrpcClients();
builder.AddMinimalEndpoints();
var app = builder.Build(); var app = builder.Build();
if (app.Environment.IsDevelopment()) if (app.Environment.IsDevelopment())
{ {
var provider = app.Services.GetService<IApiVersionDescriptionProvider>(); app.UseCustomSwagger();
app.UseCustomSwagger(provider);
} }
app.UseSerilogRequestLogging(); app.UseSerilogRequestLogging();
@ -73,9 +73,10 @@ app.UseAuthorization();
app.UseProblemDetails(); app.UseProblemDetails();
app.UseCustomHealthCheck(); app.UseCustomHealthCheck();
app.MapMinimalEndpoints();
app.UseEndpoints(endpoints => app.UseEndpoints(endpoints =>
{ {
endpoints.MapControllers();
endpoints.MapMetrics(); endpoints.MapMetrics();
}); });

View File

@ -1,25 +1,36 @@
using Booking.Booking.Features.CreateBooking.Commands.V1; using Booking.Booking.Features.CreateBooking.Commands.V1;
using BuildingBlocks.Web; using BuildingBlocks.Web;
using MediatR;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Swashbuckle.AspNetCore.Annotations; using Swashbuckle.AspNetCore.Annotations;
namespace Booking.Booking.Features.CreateBooking.Endpoints.V1; namespace Booking.Booking.Features.CreateBooking.Endpoints.V1;
public class CreateBookingEndpoint : IMinimalEndpoint
[Route(BaseApiPath + "/booking")]
public class CreateBookingEndpoint : BaseController
{ {
[HttpPost] public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder endpoints)
[Authorize]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[SwaggerOperation(Summary = "Create new Reservation", Description = "Create new Reservation")]
public async Task<ActionResult> CreateReservation([FromBody] CreateBookingCommand command,
CancellationToken cancellationToken)
{ {
var result = await Mediator.Send(command, cancellationToken); endpoints.MapPost($"{EndpointConfig.BaseApiPath}/booking", CreateBooking)
.RequireAuthorization()
.WithTags("Booking")
.WithName("Create Booking")
.WithMetadata(new SwaggerOperationAttribute("Create Booking", "Create Booking"))
.WithApiVersionSet(endpoints.NewApiVersionSet("Booking").Build())
.Produces<ulong>()
.Produces(StatusCodes.Status201Created)
.Produces(StatusCodes.Status400BadRequest)
.HasApiVersion(1.0);
return Ok(result); return endpoints;
}
private async Task<IResult> CreateBooking(CreateBookingCommand command, IMediator mediator, CancellationToken cancellationToken)
{
var result = await mediator.Send(command, cancellationToken);
return Results.Ok(result);
} }
} }

View File

@ -18,6 +18,7 @@ using Flight;
using Flight.Data; using Flight.Data;
using Flight.Data.Seed; using Flight.Data.Seed;
using Flight.Extensions; using Flight.Extensions;
using Flight.Flights.Features.CreateFlight.Endpoints.V1;
using Flight.GrpcServer.Services; using Flight.GrpcServer.Services;
using FluentValidation; using FluentValidation;
using Hellang.Middleware.ProblemDetails; using Hellang.Middleware.ProblemDetails;
@ -41,7 +42,6 @@ builder.Services.AddPersistMessage(configuration);
builder.AddCustomSerilog(env); builder.AddCustomSerilog(env);
builder.Services.AddCore(); builder.Services.AddCore();
builder.Services.AddJwt(); builder.Services.AddJwt();
builder.Services.AddControllers();
builder.Services.AddCustomSwagger(configuration, typeof(FlightRoot).Assembly); builder.Services.AddCustomSwagger(configuration, typeof(FlightRoot).Assembly);
builder.Services.AddCustomVersioning(); builder.Services.AddCustomVersioning();
builder.Services.AddCustomMediatR(); builder.Services.AddCustomMediatR();
@ -65,12 +65,13 @@ builder.Services.AddCachingRequest(new List<Assembly> {typeof(FlightRoot).Assemb
builder.Services.AddEasyCaching(options => { options.UseInMemory(configuration, "mem"); }); builder.Services.AddEasyCaching(options => { options.UseInMemory(configuration, "mem"); });
builder.AddMinimalEndpoints();
var app = builder.Build(); var app = builder.Build();
if (app.Environment.IsDevelopment()) if (app.Environment.IsDevelopment())
{ {
var provider = app.Services.GetService<IApiVersionDescriptionProvider>(); app.UseCustomSwagger();
app.UseCustomSwagger(provider);
} }
app.UseSerilogRequestLogging(); app.UseSerilogRequestLogging();
@ -84,9 +85,11 @@ app.UseCustomHealthCheck();
app.UseAuthentication(); app.UseAuthentication();
app.UseAuthorization(); app.UseAuthorization();
app.MapMinimalEndpoints();
app.UseEndpoints(endpoints => app.UseEndpoints(endpoints =>
{ {
endpoints.MapControllers();
endpoints.MapMetrics(); endpoints.MapMetrics();
endpoints.MapGrpcService<FlightGrpcServices>(); endpoints.MapGrpcService<FlightGrpcServices>();
}); });

View File

@ -1,24 +1,36 @@
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using BuildingBlocks.Web; using BuildingBlocks.Web;
using Flight.Aircrafts.Dtos;
using Flight.Aircrafts.Features.CreateAircraft.Commands.V1; using Flight.Aircrafts.Features.CreateAircraft.Commands.V1;
using MediatR;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing;
using Swashbuckle.AspNetCore.Annotations; using Swashbuckle.AspNetCore.Annotations;
namespace Flight.Aircrafts.Features.CreateAircraft.Endpoints.V1; public class CreateAircraftEndpoint : IMinimalEndpoint
[Route(BaseApiPath + "/flight/aircraft")]
public class CreateAircraftEndpoint : BaseController
{ {
[HttpPost] public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder endpoints)
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[SwaggerOperation(Summary = "Create new aircraft", Description = "Create new aircraft")]
public async Task<ActionResult> Create([FromBody] CreateAircraftCommand command, CancellationToken cancellationToken)
{ {
var result = await Mediator.Send(command, cancellationToken); endpoints.MapPost($"{EndpointConfig.BaseApiPath}/flight/aircraft", CreateAircraft)
.RequireAuthorization()
.WithTags("Flight")
.WithName("Create Aircraft")
.WithMetadata(new SwaggerOperationAttribute("Create Aircraft", "Create Aircraft"))
.WithApiVersionSet(endpoints.NewApiVersionSet("Flight").Build())
.Produces<AircraftResponseDto>()
.Produces(StatusCodes.Status201Created)
.Produces(StatusCodes.Status400BadRequest)
.HasApiVersion(1.0);
return Ok(result); return endpoints;
}
private async Task<IResult> CreateAircraft(CreateAircraftCommand command, IMediator mediator, CancellationToken cancellationToken)
{
var result = await mediator.Send(command, cancellationToken);
return Results.Ok(result);
} }
} }

View File

@ -1,24 +1,36 @@
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using BuildingBlocks.Web; using BuildingBlocks.Web;
using Flight.Airports.Dtos;
using Flight.Airports.Features.CreateAirport.Commands.V1; using Flight.Airports.Features.CreateAirport.Commands.V1;
using MediatR;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing;
using Swashbuckle.AspNetCore.Annotations; using Swashbuckle.AspNetCore.Annotations;
namespace Flight.Airports.Features.CreateAirport.Endpoints.V1; public class CreateAirportEndpoint : IMinimalEndpoint
[Route(BaseApiPath + "/flight/airport")]
public class CreateAirportEndpoint : BaseController
{ {
[HttpPost] public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder endpoints)
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[SwaggerOperation(Summary = "Create new airport", Description = "Create new airport")]
public async Task<ActionResult> Create([FromBody] CreateAirportCommand command, CancellationToken cancellationToken)
{ {
var result = await Mediator.Send(command, cancellationToken); endpoints.MapPost($"{EndpointConfig.BaseApiPath}/flight/airport", CreateAirport)
.RequireAuthorization()
.WithTags("Flight")
.WithName("Create Airport")
.WithMetadata(new SwaggerOperationAttribute("Create Airport", "Create Airport"))
.WithApiVersionSet(endpoints.NewApiVersionSet("Flight").Build())
.Produces<AirportResponseDto>()
.Produces(StatusCodes.Status201Created)
.Produces(StatusCodes.Status400BadRequest)
.HasApiVersion(1.0);
return Ok(result); return endpoints;
}
private async Task<IResult> CreateAirport(CreateAirportCommand command, IMediator mediator, CancellationToken cancellationToken)
{
var result = await mediator.Send(command, cancellationToken);
return Results.Ok(result);
} }
} }

View File

@ -14,6 +14,6 @@ public record FlightResponseDto
public long ArriveAirportId { get; init; } public long ArriveAirportId { get; init; }
public decimal DurationMinutes { get; init; } public decimal DurationMinutes { get; init; }
public DateTime FlightDate { get; init; } public DateTime FlightDate { get; init; }
public FlightStatus Status { get; init; } public Enums.FlightStatus Status { get; init; }
public decimal Price { get; init; } public decimal Price { get; init; }
} }

View File

@ -1,26 +1,38 @@
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using BuildingBlocks.Web; using BuildingBlocks.Web;
using Flight.Flights.Dtos;
using Flight.Flights.Features.CreateFlight.Commands.V1; using Flight.Flights.Features.CreateFlight.Commands.V1;
using Microsoft.AspNetCore.Authorization; using MediatR;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing;
using Swashbuckle.AspNetCore.Annotations; using Swashbuckle.AspNetCore.Annotations;
namespace Flight.Flights.Features.CreateFlight.Endpoints.V1; namespace Flight.Flights.Features.CreateFlight.Endpoints.V1;
[Route(BaseApiPath + "/flight")] public class CreateFlightEndpoint : IMinimalEndpoint
public class CreateFlightEndpoint : BaseController
{ {
[Authorize] public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder endpoints)
[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[SwaggerOperation(Summary = "Create new flight", Description = "Create new flight")]
public async Task<ActionResult> Create([FromBody] CreateFlightCommand command, CancellationToken cancellationToken)
{ {
var result = await Mediator.Send(command, cancellationToken); endpoints.MapPost($"{EndpointConfig.BaseApiPath}/flight", CreateFlight)
.RequireAuthorization()
.WithTags("Flight")
.WithName("Create Flight")
.WithMetadata(new SwaggerOperationAttribute("Create Flight", "Create Flight"))
.WithApiVersionSet(endpoints.NewApiVersionSet("Flight").Build())
.Produces<FlightResponseDto>()
.Produces(StatusCodes.Status201Created)
.Produces(StatusCodes.Status400BadRequest)
.HasApiVersion(1.0);
return Ok(result); return endpoints;
}
private async Task<IResult> CreateFlight(CreateFlightCommand command, IMediator mediator, CancellationToken cancellationToken)
{
var result = await mediator.Send(command, cancellationToken);
return Results.Ok(result);
} }
} }

View File

@ -1,27 +1,38 @@
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using BuildingBlocks.Web; using BuildingBlocks.Web;
using Flight.Flights.Dtos;
using Flight.Flights.Features.DeleteFlight.Commands.V1; using Flight.Flights.Features.DeleteFlight.Commands.V1;
using Microsoft.AspNetCore.Authorization; using MediatR;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing;
using Swashbuckle.AspNetCore.Annotations; using Swashbuckle.AspNetCore.Annotations;
namespace Flight.Flights.Features.DeleteFlight.Endpoints.V1; namespace Flight.Flights.Features.DeleteFlight.Endpoints.V1;
[Route(BaseApiPath + "/flight")] public class DeleteFlightEndpoint : IMinimalEndpoint
public class DeleteFlightEndpoint : BaseController
{ {
[Authorize] public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder endpoints)
[HttpDelete]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[SwaggerOperation(Summary = "Delete flight", Description = "Delete flight")]
public async Task<ActionResult> Update(DeleteFlightCommand command, CancellationToken cancellationToken)
{ {
var result = await Mediator.Send(command, cancellationToken); endpoints.MapDelete($"{EndpointConfig.BaseApiPath}/flight/{{id}}", DeleteFlight)
.RequireAuthorization()
.WithTags("Flight")
.WithName("Delete Flight")
.WithMetadata(new SwaggerOperationAttribute("Delete Flight", "Delete Flight"))
.WithApiVersionSet(endpoints.NewApiVersionSet("Flight").Build())
.Produces<FlightResponseDto>()
.Produces(StatusCodes.Status201Created)
.Produces(StatusCodes.Status400BadRequest)
.HasApiVersion(1.0);
return Ok(result); return endpoints;
}
private async Task<IResult> DeleteFlight(long id, IMediator mediator, CancellationToken cancellationToken)
{
var result = await mediator.Send(new DeleteFlightCommand(id), cancellationToken);
return Results.Ok(result);
} }
} }

View File

@ -1,26 +1,37 @@
using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using BuildingBlocks.Web; using BuildingBlocks.Web;
using Flight.Flights.Dtos;
using Flight.Flights.Features.GetAvailableFlights.Queries.V1; using Flight.Flights.Features.GetAvailableFlights.Queries.V1;
using Microsoft.AspNetCore.Authorization; using MediatR;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing;
using Swashbuckle.AspNetCore.Annotations; using Swashbuckle.AspNetCore.Annotations;
namespace Flight.Flights.Features.GetAvailableFlights.Endpoints.V1; public class GetAvailableFlightsEndpoint : IMinimalEndpoint
[Route(BaseApiPath + "/flight/get-available-flights")]
public class GetAvailableFlightsEndpoint : BaseController
{ {
[Authorize] public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder endpoints)
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[SwaggerOperation(Summary = "Get available flights", Description = "Get available flights")]
public async Task<ActionResult> GetAvailableFlights([FromRoute] GetAvailableFlightsQuery query, CancellationToken cancellationToken)
{ {
var result = await Mediator.Send(query, cancellationToken); endpoints.MapGet($"{EndpointConfig.BaseApiPath}/flight/get-available-flights", GetAvailableFlights)
.RequireAuthorization()
.WithTags("Flight")
.WithName("Get Available Flights")
.WithMetadata(new SwaggerOperationAttribute("Get Available Flights", "Get Available Flights"))
.WithApiVersionSet(endpoints.NewApiVersionSet("Flight").Build())
.Produces<IEnumerable<FlightResponseDto>>()
.Produces(StatusCodes.Status200OK)
.Produces(StatusCodes.Status400BadRequest)
.HasApiVersion(1.0);
return Ok(result); return endpoints;
}
private async Task<IResult> GetAvailableFlights(IMediator mediator, CancellationToken cancellationToken)
{
var result = await mediator.Send(new GetAvailableFlightsQuery(), cancellationToken);
return Results.Ok(result);
} }
} }

View File

@ -1,25 +1,63 @@
// using System.Threading;
// using System.Threading.Tasks;
// using BuildingBlocks.Web;
// using Flight.Flights.Features.GetFlightById.Queries.V1;
// using Microsoft.AspNetCore.Authorization;
// using Microsoft.AspNetCore.Http;
// using Microsoft.AspNetCore.Mvc;
// using Swashbuckle.AspNetCore.Annotations;
//
// namespace Flight.Flights.Features.GetFlightById.Endpoints.V1;
//
// [Route(BaseApiPath + "/flight")]
// public class GetFlightByIdEndpoint : BaseController
// {
// [Authorize]
// [HttpGet("{id}")]
// [ProducesResponseType(StatusCodes.Status200OK)]
// [ProducesResponseType(StatusCodes.Status400BadRequest)]
// [SwaggerOperation(Summary = "Get flight by id", Description = "Get flight by id")]
// public async Task<ActionResult> GetById([FromRoute] GetFlightByIdQuery query, CancellationToken cancellationToken)
// {
// var result = await Mediator.Send(query, cancellationToken);
// return Ok(result);
// }
// }
using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using BuildingBlocks.Web; using BuildingBlocks.Web;
using Flight.Flights.Dtos;
using Flight.Flights.Features.GetFlightById.Queries.V1; using Flight.Flights.Features.GetFlightById.Queries.V1;
using Microsoft.AspNetCore.Authorization; using MediatR;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing;
using Swashbuckle.AspNetCore.Annotations; using Swashbuckle.AspNetCore.Annotations;
namespace Flight.Flights.Features.GetFlightById.Endpoints.V1; public class GetFlightByIdEndpoint : IMinimalEndpoint
[Route(BaseApiPath + "/flight")]
public class GetFlightByIdEndpoint : BaseController
{ {
[Authorize] public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder endpoints)
[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[SwaggerOperation(Summary = "Get flight by id", Description = "Get flight by id")]
public async Task<ActionResult> GetById([FromRoute] GetFlightByIdQuery query, CancellationToken cancellationToken)
{ {
var result = await Mediator.Send(query, cancellationToken); endpoints.MapGet($"{EndpointConfig.BaseApiPath}/flight/{{id}}", GetById)
return Ok(result); .RequireAuthorization()
.WithTags("Flight")
.WithName("Get Flight By Id")
.WithMetadata(new SwaggerOperationAttribute("Get Flight By Id", "Get Flight By Id"))
.WithApiVersionSet(endpoints.NewApiVersionSet("Flight").Build())
.Produces<FlightResponseDto>()
.Produces(StatusCodes.Status200OK)
.Produces(StatusCodes.Status400BadRequest)
.HasApiVersion(1.0);
return endpoints;
}
private async Task<IResult> GetById(long id, IMediator mediator, CancellationToken cancellationToken)
{
var result = await mediator.Send(new GetFlightByIdQuery(id), cancellationToken);
return Results.Ok(result);
} }
} }

View File

@ -1,26 +1,36 @@
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using BuildingBlocks.Web; using BuildingBlocks.Web;
using Flight.Flights.Dtos;
using Flight.Flights.Features.UpdateFlight.Commands.V1; using Flight.Flights.Features.UpdateFlight.Commands.V1;
using Microsoft.AspNetCore.Authorization; using MediatR;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing;
using Swashbuckle.AspNetCore.Annotations; using Swashbuckle.AspNetCore.Annotations;
namespace Flight.Flights.Features.UpdateFlight.Endpoints.V1; public class UpdateFlightEndpoint : IMinimalEndpoint
[Route(BaseApiPath + "/flight")]
public class UpdateFlightEndpoint : BaseController
{ {
[Authorize] public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder endpoints)
[HttpPut]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[SwaggerOperation(Summary = "Update flight", Description = "Update flight")]
public async Task<ActionResult> Update(UpdateFlightCommand command, CancellationToken cancellationToken)
{ {
var result = await Mediator.Send(command, cancellationToken); endpoints.MapPut($"{EndpointConfig.BaseApiPath}/flight", UpdateFlight)
.RequireAuthorization()
.WithTags("Flight")
.WithName("Update Flight")
.WithMetadata(new SwaggerOperationAttribute("Update Flight", "Update Flight"))
.WithApiVersionSet(endpoints.NewApiVersionSet("Flight").Build())
.Produces<FlightResponseDto>()
.Produces(StatusCodes.Status201Created)
.Produces(StatusCodes.Status400BadRequest)
.HasApiVersion(1.0);
return Ok(result); return endpoints;
}
private async Task<IResult> UpdateFlight(UpdateFlightCommand command, IMediator mediator, CancellationToken cancellationToken)
{
var result = await mediator.Send(command, cancellationToken);
return Results.Ok(result);
} }
} }

View File

@ -6,7 +6,7 @@ public record SeatResponseDto
{ {
public long Id { get; set; } public long Id { get; set; }
public string SeatNumber { get; init; } public string SeatNumber { get; init; }
public SeatType Type { get; init; } public Enums.SeatType Type { get; init; }
public SeatClass Class { get; init; } public Enums.SeatClass Class { get; init; }
public long FlightId { get; init; } public long FlightId { get; init; }
} }

View File

@ -1,24 +1,62 @@
// using System.Threading;
// using System.Threading.Tasks;
// using BuildingBlocks.Web;
// using Flight.Seats.Features.CreateSeat.Commands.V1;
// using Microsoft.AspNetCore.Http;
// using Microsoft.AspNetCore.Mvc;
// using Swashbuckle.AspNetCore.Annotations;
//
// namespace Flight.Seats.Features.CreateSeat.Endpoints.V1;
//
// [Route(BaseApiPath + "/flight/seat")]
// public class CreateSeatEndpoint : BaseController
// {
// [HttpPost]
// [ProducesResponseType(StatusCodes.Status201Created)]
// [ProducesResponseType(StatusCodes.Status400BadRequest)]
// [SwaggerOperation(Summary = "Create new seat", Description = "Create new seat")]
// public async Task<ActionResult> Create(CreateSeatCommand command, CancellationToken cancellationToken)
// {
// var result = await Mediator.Send(command, cancellationToken);
//
// return Ok(result);
// }
// }
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using BuildingBlocks.Web; using BuildingBlocks.Web;
using Flight.Seats.Dtos;
using Flight.Seats.Features.CreateSeat.Commands.V1; using Flight.Seats.Features.CreateSeat.Commands.V1;
using MediatR;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing;
using Swashbuckle.AspNetCore.Annotations; using Swashbuckle.AspNetCore.Annotations;
namespace Flight.Seats.Features.CreateSeat.Endpoints.V1; public class CreateSeatEndpoint : IMinimalEndpoint
[Route(BaseApiPath + "/flight/seat")]
public class CreateSeatEndpoint : BaseController
{ {
[HttpPost] public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder endpoints)
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[SwaggerOperation(Summary = "Create new seat", Description = "Create new seat")]
public async Task<ActionResult> Create(CreateSeatCommand command, CancellationToken cancellationToken)
{ {
var result = await Mediator.Send(command, cancellationToken); endpoints.MapPost($"{EndpointConfig.BaseApiPath}/flight/seat", CreateSeat)
.RequireAuthorization()
.WithTags("Flight")
.WithName("Create Seat")
.WithMetadata(new SwaggerOperationAttribute("Create Seat", "Create Seat"))
.WithApiVersionSet(endpoints.NewApiVersionSet("Flight").Build())
.Produces<SeatResponseDto>()
.Produces(StatusCodes.Status201Created)
.Produces(StatusCodes.Status400BadRequest)
.HasApiVersion(1.0);
return Ok(result); return endpoints;
}
private async Task<IResult> CreateSeat(CreateSeatCommand command, IMediator mediator, CancellationToken cancellationToken)
{
var result = await mediator.Send(command, cancellationToken);
return Results.Ok(result);
} }
} }

View File

@ -1,27 +1,38 @@
using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using BuildingBlocks.Web; using BuildingBlocks.Web;
using Flight.Flights.Dtos;
using Flight.Seats.Dtos;
using Flight.Seats.Features.GetAvailableSeats.Queries.V1; using Flight.Seats.Features.GetAvailableSeats.Queries.V1;
using Microsoft.AspNetCore.Authorization; using MediatR;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing;
using Swashbuckle.AspNetCore.Annotations; using Swashbuckle.AspNetCore.Annotations;
namespace Flight.Seats.Features.GetAvailableSeats.Endpoints.V1; public class GetAvailableSeatsEndpoint : IMinimalEndpoint
[Route(BaseApiPath + "/flight/get-available-seats")]
public class GetAvailableSeatsEndpoint : BaseController
{ {
[Authorize] public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder endpoints)
[HttpGet("{flightId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[SwaggerOperation(Summary = "Get available seats", Description = "Get available seats")]
public async Task<ActionResult> GetAvailableSeats([FromRoute] GetAvailableSeatsQuery query,
CancellationToken cancellationToken)
{ {
var result = await Mediator.Send(query, cancellationToken); endpoints.MapGet($"{EndpointConfig.BaseApiPath}/flight/get-available-seats/{{id}}", GetAvailableSeats)
.RequireAuthorization()
.WithTags("Flight")
.WithName("Get Available Seats")
.WithMetadata(new SwaggerOperationAttribute("Get Available Seats", "Get Available Seats"))
.WithApiVersionSet(endpoints.NewApiVersionSet("Flight").Build())
.Produces<IEnumerable<SeatResponseDto>>()
.Produces(StatusCodes.Status200OK)
.Produces(StatusCodes.Status400BadRequest)
.HasApiVersion(1.0);
return Ok(result); return endpoints;
}
private async Task<IResult> GetAvailableSeats(long id, IMediator mediator, CancellationToken cancellationToken)
{
var result = await mediator.Send(new GetAvailableSeatsQuery(id), cancellationToken);
return Results.Ok(result);
} }
} }

View File

@ -1,26 +1,36 @@
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using BuildingBlocks.Web; using BuildingBlocks.Web;
using Flight.Seats.Dtos;
using Flight.Seats.Features.ReserveSeat.Commands.V1; using Flight.Seats.Features.ReserveSeat.Commands.V1;
using Microsoft.AspNetCore.Authorization; using MediatR;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing;
using Swashbuckle.AspNetCore.Annotations; using Swashbuckle.AspNetCore.Annotations;
namespace Flight.Seats.Features.ReserveSeat.Endpoints.V1; public class ReserveSeatEndpoint : IMinimalEndpoint
[Route(BaseApiPath + "/flight/reserve-seat")]
public class ReserveSeatEndpoint : BaseController
{ {
[Authorize] public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder endpoints)
[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[SwaggerOperation(Summary = "Reserve seat", Description = "Reserve seat")]
public async Task<ActionResult> ReserveSeat([FromBody] ReserveSeatCommand command, CancellationToken cancellationToken)
{ {
var result = await Mediator.Send(command, cancellationToken); endpoints.MapPost($"{EndpointConfig.BaseApiPath}/flight/reserve-seat", ReserveSeat)
.RequireAuthorization()
.WithTags("Flight")
.WithName("Reserve Seat")
.WithMetadata(new SwaggerOperationAttribute("Reserve Seat", "Reserve Seat"))
.WithApiVersionSet(endpoints.NewApiVersionSet("Flight").Build())
.Produces<SeatResponseDto>()
.Produces(StatusCodes.Status201Created)
.Produces(StatusCodes.Status400BadRequest)
.HasApiVersion(1.0);
return Ok(result); return endpoints;
}
private async Task<IResult> ReserveSeat(ReserveSeatCommand command, IMediator mediator, CancellationToken cancellationToken)
{
var result = await mediator.Send(command, cancellationToken);
return Results.Ok(result);
} }
} }

View File

@ -30,8 +30,8 @@ builder.Services.AddPersistMessage(configuration);
builder.Services.AddCustomDbContext<IdentityContext>(configuration); builder.Services.AddCustomDbContext<IdentityContext>(configuration);
builder.Services.AddScoped<IDataSeeder, IdentityDataSeeder>(); builder.Services.AddScoped<IDataSeeder, IdentityDataSeeder>();
builder.Services.AddCore(); builder.Services.AddCore();
builder.AddCustomSerilog(env);
builder.Services.AddControllers(); builder.Services.AddControllers();
builder.AddCustomSerilog(env);
builder.Services.AddCustomSwagger(configuration, typeof(IdentityRoot).Assembly); builder.Services.AddCustomSwagger(configuration, typeof(IdentityRoot).Assembly);
builder.Services.AddCustomVersioning(); builder.Services.AddCustomVersioning();
builder.Services.AddCustomMediatR(); builder.Services.AddCustomMediatR();
@ -47,12 +47,13 @@ SnowFlakIdGenerator.Configure(4);
builder.Services.AddIdentityServer(env); builder.Services.AddIdentityServer(env);
builder.AddMinimalEndpoints();
var app = builder.Build(); var app = builder.Build();
if (app.Environment.IsDevelopment()) if (app.Environment.IsDevelopment())
{ {
var provider = app.Services.GetService<IApiVersionDescriptionProvider>(); app.UseCustomSwagger();
app.UseCustomSwagger(provider);
} }
app.UseSerilogRequestLogging(); app.UseSerilogRequestLogging();
@ -67,6 +68,8 @@ app.UseAuthentication();
app.UseAuthorization(); app.UseAuthorization();
app.UseIdentityServer(); app.UseIdentityServer();
app.MapMinimalEndpoints();
app.UseEndpoints(endpoints => app.UseEndpoints(endpoints =>
{ {
endpoints.MapControllers(); endpoints.MapControllers();

View File

@ -1,34 +1,39 @@
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using BuildingBlocks.Web;
using Identity.Identity.Dtos;
using Identity.Identity.Features.RegisterNewUser.Commands.V1; using Identity.Identity.Features.RegisterNewUser.Commands.V1;
using MediatR; using MediatR;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Swashbuckle.AspNetCore.Annotations; using Swashbuckle.AspNetCore.Annotations;
namespace Identity.Identity.Features.RegisterNewUser.Endpoints.V1; namespace Identity.Identity.Features.RegisterNewUser.Endpoints.V1;
[Route("identity/register-user")] public class RegisterNewUserEndpoint : IMinimalEndpoint
[ApiController]
public class LoginEndpoint : ControllerBase
{ {
private readonly IMediator _mediator; public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder endpoints)
public LoginEndpoint(IMediator mediator)
{ {
_mediator = mediator; endpoints.MapPost($"{EndpointConfig.BaseApiPath}/identity/register-user", RegisterNewUser)
.RequireAuthorization()
.WithTags("Identity")
.WithName("Register User")
.WithMetadata(new SwaggerOperationAttribute("Register User", "Register User"))
.WithApiVersionSet(endpoints.NewApiVersionSet("Identity").Build())
.Produces<RegisterNewUserResponseDto>()
.Produces(StatusCodes.Status201Created)
.Produces(StatusCodes.Status400BadRequest)
.HasApiVersion(1.0);
return endpoints;
} }
// [Authorize] private async Task<IResult> RegisterNewUser(RegisterNewUserCommand command, IMediator mediator, CancellationToken cancellationToken)
[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[SwaggerOperation(Summary = "Register new user", Description = "Register new user")]
public async Task<ActionResult> RegisterNewUser([FromBody] RegisterNewUserCommand command,
CancellationToken cancellationToken)
{ {
var result = await _mediator.Send(command, cancellationToken); var result = await mediator.Send(command, cancellationToken);
return Ok(result); return Results.Ok(result);
} }
} }

View File

@ -36,7 +36,6 @@ builder.Services.AddPersistMessage(configuration);
builder.AddCustomSerilog(env); builder.AddCustomSerilog(env);
builder.Services.AddCore(); builder.Services.AddCore();
builder.Services.AddJwt(); builder.Services.AddJwt();
builder.Services.AddControllers();
builder.Services.AddCustomSwagger(configuration, typeof(PassengerRoot).Assembly); builder.Services.AddCustomSwagger(configuration, typeof(PassengerRoot).Assembly);
builder.Services.AddCustomVersioning(); builder.Services.AddCustomVersioning();
builder.Services.AddCustomMediatR(); builder.Services.AddCustomMediatR();
@ -54,12 +53,13 @@ builder.Services.AddGrpc(options =>
SnowFlakIdGenerator.Configure(2); SnowFlakIdGenerator.Configure(2);
builder.AddMinimalEndpoints();
var app = builder.Build(); var app = builder.Build();
if (app.Environment.IsDevelopment()) if (app.Environment.IsDevelopment())
{ {
var provider = app.Services.GetService<IApiVersionDescriptionProvider>(); app.UseCustomSwagger();
app.UseCustomSwagger(provider);
} }
app.UseSerilogRequestLogging(); app.UseSerilogRequestLogging();
@ -73,9 +73,10 @@ app.UseAuthentication();
app.UseAuthorization(); app.UseAuthorization();
app.UseCustomHealthCheck(); app.UseCustomHealthCheck();
app.MapMinimalEndpoints();
app.UseEndpoints(endpoints => app.UseEndpoints(endpoints =>
{ {
endpoints.MapControllers();
endpoints.MapMetrics(); endpoints.MapMetrics();
endpoints.MapGrpcService<PassengerGrpcServices>(); endpoints.MapGrpcService<PassengerGrpcServices>();
}); });

View File

@ -1,5 +1,3 @@
using Passenger.Passengers.Models;
namespace Passenger.Passengers.Dtos; namespace Passenger.Passengers.Dtos;
public record PassengerResponseDto public record PassengerResponseDto
@ -7,6 +5,6 @@ public record PassengerResponseDto
public long Id { get; init; } public long Id { get; init; }
public string Name { get; init; } public string Name { get; init; }
public string PassportNumber { get; init; } public string PassportNumber { get; init; }
public PassengerType PassengerType { get; init; } public Enums.PassengerType PassengerType { get; init; }
public int Age { get; init; } public int Age { get; init; }
} }

View File

@ -1,25 +1,38 @@
using BuildingBlocks.Web; using BuildingBlocks.Web;
using MediatR;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Passenger.Passengers.Dtos;
using Passenger.Passengers.Features.CompleteRegisterPassenger.Commands.V1; using Passenger.Passengers.Features.CompleteRegisterPassenger.Commands.V1;
using Swashbuckle.AspNetCore.Annotations; using Swashbuckle.AspNetCore.Annotations;
namespace Passenger.Passengers.Features.CompleteRegisterPassenger.Endpoints.V1; namespace Passenger.Passengers.Features.CompleteRegisterPassenger.Endpoints.V1;
[Route(BaseApiPath + "/passenger/complete-registration")] public class CompleteRegisterPassengerEndpoint : IMinimalEndpoint
public class CompleteRegisterPassengerEndpoint : BaseController
{ {
[Authorize] public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder endpoints)
[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[SwaggerOperation(Summary = "Complete Register Passenger", Description = "Complete Register Passenger")]
public async Task<ActionResult> CompleteRegisterPassenger([FromBody] CompleteRegisterPassengerCommand command,
CancellationToken cancellationToken)
{ {
var result = await Mediator.Send(command, cancellationToken); endpoints.MapPost($"{EndpointConfig.BaseApiPath}/passenger/complete-registration", CompleteRegisterPassenger)
.RequireAuthorization()
.WithTags("Passenger")
.WithName("Complete Register Passenger")
.WithMetadata(new SwaggerOperationAttribute("Complete Register Passenger", "Complete Register Passenger"))
.WithApiVersionSet(endpoints.NewApiVersionSet("Passenger").Build())
.Produces<PassengerResponseDto>()
.Produces(StatusCodes.Status201Created)
.Produces(StatusCodes.Status400BadRequest)
.HasApiVersion(1.0);
return Ok(result); return endpoints;
}
private async Task<IResult> CompleteRegisterPassenger(CompleteRegisterPassengerCommand command, IMediator mediator, CancellationToken cancellationToken)
{
var result = await mediator.Send(command, cancellationToken);
return Results.Ok(result);
} }
} }

View File

@ -1,24 +1,37 @@
using BuildingBlocks.Web; using BuildingBlocks.Web;
using MediatR;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Passenger.Passengers.Dtos;
using Passenger.Passengers.Features.GetPassengerById.Queries.V1; using Passenger.Passengers.Features.GetPassengerById.Queries.V1;
using Swashbuckle.AspNetCore.Annotations; using Swashbuckle.AspNetCore.Annotations;
namespace Passenger.Passengers.Features.GetPassengerById.Endpoints.V1; namespace Passenger.Passengers.Features.GetPassengerById.Endpoints.V1;
public class GetPassengerByIdEndpoint : IMinimalEndpoint
[Route(BaseApiPath + "/passenger")]
public class GetPassengerByIdEndpoint : BaseController
{ {
[HttpGet("{id}")] public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder endpoints)
[Authorize]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[SwaggerOperation(Summary = "Get passenger by id", Description = "Get passenger by id")]
public async Task<ActionResult> GetById([FromRoute] GetPassengerQueryById query, CancellationToken cancellationToken)
{ {
var result = await Mediator.Send(query, cancellationToken); endpoints.MapGet($"{EndpointConfig.BaseApiPath}/passenger/{{id}}", GetById)
.RequireAuthorization()
.WithTags("Passenger")
.WithName("Get Passenger By Id")
.WithMetadata(new SwaggerOperationAttribute("Get Passenger By Id", "Get Passenger By Id"))
.WithApiVersionSet(endpoints.NewApiVersionSet("Passenger").Build())
.Produces<PassengerResponseDto>()
.Produces(StatusCodes.Status200OK)
.Produces(StatusCodes.Status400BadRequest)
.HasApiVersion(1.0);
return Ok(result); return endpoints;
}
private async Task<IResult> GetById(long id, IMediator mediator, CancellationToken cancellationToken)
{
var result = await mediator.Send(new GetPassengerQueryById(id), cancellationToken);
return Results.Ok(result);
} }
} }