refactor all properties and dtos

This commit is contained in:
meysamhadeli 2022-11-21 04:31:21 +03:30
parent 1168fd7bbd
commit acb618a6fd
77 changed files with 253 additions and 511 deletions

View File

@ -39,13 +39,13 @@ Content-Type: application/json
authorization: bearer {{Authenticate.response.body.access_token}} authorization: bearer {{Authenticate.response.body.access_token}}
{ {
"firstName": "4John", "firstName": "John",
"lastName": "4Do", "lastName": "Do",
"username": "4admin", "username": "admin",
"passportNumber": "412900000000", "passportNumber": "412900000000",
"email": "4admin@admin.com", "email": "admin@admin.com",
"password": "4Admin@12345", "password": "Admin@12345",
"confirmPassword": "4Admin@12345" "confirmPassword": "Admin@12345"
} }
### ###

View File

@ -8,28 +8,25 @@ public class CachingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest,
where TRequest : notnull, IRequest<TResponse> where TRequest : notnull, IRequest<TResponse>
where TResponse : notnull where TResponse : notnull
{ {
private readonly ICacheRequest _cacheRequest;
private readonly IEasyCachingProvider _cachingProvider; private readonly IEasyCachingProvider _cachingProvider;
private readonly ILogger<CachingBehavior<TRequest, TResponse>> _logger; private readonly ILogger<CachingBehavior<TRequest, TResponse>> _logger;
private readonly int defaultCacheExpirationInHours = 1; private readonly int defaultCacheExpirationInHours = 1;
public CachingBehavior(IEasyCachingProviderFactory cachingFactory, public CachingBehavior(IEasyCachingProviderFactory cachingFactory,
ILogger<CachingBehavior<TRequest, TResponse>> logger, ILogger<CachingBehavior<TRequest, TResponse>> logger)
ICacheRequest cacheRequest)
{ {
_logger = logger; _logger = logger;
_cachingProvider = cachingFactory.GetCachingProvider("mem"); _cachingProvider = cachingFactory.GetCachingProvider("mem");
_cacheRequest = cacheRequest;
} }
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
if (request is not ICacheRequest || _cacheRequest == null) if (request is not ICacheRequest cacheRequest)
// No cache request found, so just continue through the pipeline // No cache request found, so just continue through the pipeline
return await next(); return await next();
var cacheKey = _cacheRequest.CacheKey; var cacheKey = cacheRequest.CacheKey;
var cachedResponse = await _cachingProvider.GetAsync<TResponse>(cacheKey); var cachedResponse = await _cachingProvider.GetAsync<TResponse>(cacheKey);
if (cachedResponse.Value != null) if (cachedResponse.Value != null)
{ {
@ -40,7 +37,7 @@ public class CachingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest,
var response = await next(); var response = await next();
var expirationTime = _cacheRequest.AbsoluteExpirationRelativeToNow ?? var expirationTime = cacheRequest.AbsoluteExpirationRelativeToNow ??
DateTime.Now.AddHours(defaultCacheExpirationInHours); DateTime.Now.AddHours(defaultCacheExpirationInHours);
await _cachingProvider.SetAsync(cacheKey, response, expirationTime.TimeOfDay); await _cachingProvider.SetAsync(cacheKey, response, expirationTime.TimeOfDay);

View File

@ -1,29 +0,0 @@
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
namespace BuildingBlocks.Caching;
public static class Extensions
{
public static IServiceCollection AddCachingRequest(this IServiceCollection services,
IList<Assembly> assembliesToScan, ServiceLifetime lifetime = ServiceLifetime.Transient)
{
// ICacheRequest discovery and registration
services.Scan(scan => scan
.FromAssemblies(assembliesToScan ?? AppDomain.CurrentDomain.GetAssemblies())
.AddClasses(classes => classes.AssignableTo(typeof(ICacheRequest)),
false)
.AsImplementedInterfaces()
.WithLifetime(lifetime));
// IInvalidateCacheRequest discovery and registration
services.Scan(scan => scan
.FromAssemblies(assembliesToScan ?? AppDomain.CurrentDomain.GetAssemblies())
.AddClasses(classes => classes.AssignableTo(typeof(IInvalidateCacheRequest)),
false)
.AsImplementedInterfaces()
.WithLifetime(lifetime));
return services;
}
}

View File

@ -1,5 +1,3 @@
using MediatR;
namespace BuildingBlocks.Caching; namespace BuildingBlocks.Caching;
public interface ICacheRequest public interface ICacheRequest

View File

@ -1,7 +1,3 @@
using System;
using System.Linq;
using MediatR;
namespace BuildingBlocks.Caching namespace BuildingBlocks.Caching
{ {
public interface IInvalidateCacheRequest public interface IInvalidateCacheRequest

View File

@ -10,27 +10,23 @@ namespace BuildingBlocks.Caching
{ {
private readonly ILogger<InvalidateCachingBehavior<TRequest, TResponse>> _logger; private readonly ILogger<InvalidateCachingBehavior<TRequest, TResponse>> _logger;
private readonly IEasyCachingProvider _cachingProvider; private readonly IEasyCachingProvider _cachingProvider;
private readonly IInvalidateCacheRequest _invalidateCacheRequest;
public InvalidateCachingBehavior(IEasyCachingProviderFactory cachingFactory, public InvalidateCachingBehavior(IEasyCachingProviderFactory cachingFactory,
ILogger<InvalidateCachingBehavior<TRequest, TResponse>> logger, ILogger<InvalidateCachingBehavior<TRequest, TResponse>> logger)
IInvalidateCacheRequest invalidateCacheRequest)
{ {
_logger = logger; _logger = logger;
_cachingProvider = cachingFactory.GetCachingProvider("mem"); _cachingProvider = cachingFactory.GetCachingProvider("mem");
_invalidateCacheRequest = invalidateCacheRequest;
} }
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken) public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
{ {
if (request is not IInvalidateCacheRequest || _invalidateCacheRequest == null) if (request is not IInvalidateCacheRequest invalidateCacheRequest)
{ {
// No cache request found, so just continue through the pipeline // No cache request found, so just continue through the pipeline
return await next(); return await next();
} }
var cacheKey = _invalidateCacheRequest.CacheKey; var cacheKey = invalidateCacheRequest.CacheKey;
var response = await next(); var response = await next();
await _cachingProvider.RemoveAsync(cacheKey); await _cachingProvider.RemoveAsync(cacheKey);

View File

@ -3,7 +3,4 @@ using BuildingBlocks.IdsGenerator;
namespace BuildingBlocks.Core.Event; namespace BuildingBlocks.Core.Event;
public class InternalCommand : IInternalCommand, ICommand public record InternalCommand : IInternalCommand, ICommand;
{
public long Id { get; init; } = SnowFlakIdGenerator.NewId();
}

View File

@ -27,6 +27,6 @@ namespace BuildingBlocks.Core.Model
public long Version { get; set; } = -1; public long Version { get; set; } = -1;
public TId Id { get; protected set; } public TId Id { get; set; }
} }
} }

View File

@ -1,7 +1,5 @@
using System.Data;
using System.Text.Json; using System.Text.Json;
using BuildingBlocks.Core; using BuildingBlocks.Core;
using BuildingBlocks.Core.Event;
using MediatR; using MediatR;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;

View File

@ -1,25 +1,5 @@
using Booking;
using Booking.Data;
using Booking.Extensions;
using Booking.Extensions.Infrastructure; using Booking.Extensions.Infrastructure;
using BuildingBlocks.EventStoreDB;
using BuildingBlocks.HealthCheck;
using BuildingBlocks.IdsGenerator;
using BuildingBlocks.Jwt;
using BuildingBlocks.Logging;
using BuildingBlocks.Mapster;
using BuildingBlocks.MassTransit;
using BuildingBlocks.Mongo;
using BuildingBlocks.OpenTelemetry;
using BuildingBlocks.PersistMessageProcessor;
using BuildingBlocks.Swagger;
using BuildingBlocks.Web; using BuildingBlocks.Web;
using Figgle;
using FluentValidation;
using Hellang.Middleware.ProblemDetails;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Prometheus;
using Serilog;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);

View File

@ -1,16 +1,4 @@
namespace Booking.Booking.Dtos; namespace Booking.Booking.Dtos;
public record BookingResponseDto public record BookingResponseDto(long Id, string Name, string FlightNumber, long AircraftId, decimal Price,
{ DateTime FlightDate, string SeatNumber, long DepartureAirportId, long ArriveAirportId, string Description);
public long Id { get; init; }
public string Name { get; init; }
public string FlightNumber { get; init; }
public long AircraftId { get; init; }
public decimal Price { get; init; }
public DateTime FlightDate { get; init; }
public string SeatNumber { get; init; }
public long DepartureAirportId { get; init; }
public long ArriveAirportId { get; init; }
public string Description { get; init; }
}

View File

@ -1,6 +1,6 @@
using Booking.Booking.Dtos; using Booking.Booking.Dtos;
using Booking.Booking.Models.Reads; using Booking.Booking.Features.CreateBooking.Commands.V1;
using BuildingBlocks.IdsGenerator; using Booking.Booking.Features.CreateBooking.Dtos.V1;
using Mapster; using Mapster;
namespace Booking.Booking.Features; namespace Booking.Booking.Features;
@ -12,15 +12,12 @@ public class BookingMappings : IRegister
config.Default.NameMatchingStrategy(NameMatchingStrategy.Flexible); config.Default.NameMatchingStrategy(NameMatchingStrategy.Flexible);
config.NewConfig<Models.Booking, BookingResponseDto>() config.NewConfig<Models.Booking, BookingResponseDto>()
.Map(d => d.Name, s => s.PassengerInfo.Name) .ConstructUsing(x => new BookingResponseDto(x.Id, x.PassengerInfo.Name, x.Trip.FlightNumber,
.Map(d => d.Description, s => s.Trip.Description) x.Trip.AircraftId, x.Trip.Price, x.Trip.FlightDate, x.Trip.SeatNumber, x.Trip.DepartureAirportId, x.Trip.ArriveAirportId,
.Map(d => d.DepartureAirportId, s => s.Trip.DepartureAirportId) x.Trip.Description));
.Map(d => d.ArriveAirportId, s => s.Trip.ArriveAirportId)
.Map(d => d.FlightNumber, s => s.Trip.FlightNumber)
.Map(d => d.FlightDate, s => s.Trip.FlightDate)
.Map(d => d.Price, s => s.Trip.Price)
.Map(d => d.SeatNumber, s => s.Trip.SeatNumber)
.Map(d => d.AircraftId, s => s.Trip.AircraftId);
}
}
config.NewConfig<CreateBookingRequestDto, CreateBookingCommand>()
.ConstructUsing(x => new CreateBookingCommand(x.PassengerId, x.FlightId, x.Description));
}
}

View File

@ -0,0 +1,3 @@
namespace Booking.Booking.Features.CreateBooking.Dtos.V1;
public record CreateBookingRequestDto(long PassengerId, long FlightId, string Description);

View File

@ -1,5 +1,7 @@
using Booking.Booking.Features.CreateBooking.Commands.V1; using Booking.Booking.Features.CreateBooking.Commands.V1;
using Booking.Booking.Features.CreateBooking.Dtos.V1;
using BuildingBlocks.Web; using BuildingBlocks.Web;
using MapsterMapper;
using MediatR; using MediatR;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
@ -9,6 +11,7 @@ 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 public class CreateBookingEndpoint : IMinimalEndpoint
{ {
public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder endpoints) public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder endpoints)
@ -27,8 +30,11 @@ public class CreateBookingEndpoint : IMinimalEndpoint
return endpoints; return endpoints;
} }
private async Task<IResult> CreateBooking(CreateBookingCommand command, IMediator mediator, CancellationToken cancellationToken) private async Task<IResult> CreateBooking(CreateBookingRequestDto request, IMediator mediator, IMapper mapper,
CancellationToken cancellationToken)
{ {
var command = mapper.Map<CreateBookingCommand>(request);
var result = await mediator.Send(command, cancellationToken); var result = await mediator.Send(command, cancellationToken);
return Results.Ok(result); return Results.Ok(result);

View File

@ -1,8 +1,6 @@
using Booking.Booking.Features.CreateBooking.Events.Domain.V1; using Booking.Booking.Features.CreateBooking.Events.Domain.V1;
using Booking.Booking.Models.ValueObjects; using Booking.Booking.Models.ValueObjects;
using BuildingBlocks.EventStoreDB.Events; using BuildingBlocks.EventStoreDB.Events;
using BuildingBlocks.Utils;
using Microsoft.AspNetCore.Http;
namespace Booking.Booking.Models; namespace Booking.Booking.Models;

View File

@ -92,13 +92,12 @@ public static class InfrastructureExtensions
var env = app.Environment; var env = app.Environment;
var appOptions = app.GetOptions<AppOptions>("AppOptions"); var appOptions = app.GetOptions<AppOptions>("AppOptions");
app.UseProblemDetails();
app.UseSerilogRequestLogging(); app.UseSerilogRequestLogging();
app.UseCorrelationId(); app.UseCorrelationId();
app.UseRouting(); app.UseRouting();
app.UseHttpMetrics(); app.UseHttpMetrics();
app.UseHttpsRedirection(); app.UseHttpsRedirection();
app.UseProblemDetails();
app.UseCustomHealthCheck(); app.UseCustomHealthCheck();
app.MapMetrics(); app.MapMetrics();
app.MapGet("/", x => x.Response.WriteAsync(appOptions.Name)); app.MapGet("/", x => x.Response.WriteAsync(appOptions.Name));

View File

@ -10,8 +10,8 @@ public static class MediatRExtensions
public static IServiceCollection AddCustomMediatR(this IServiceCollection services) public static IServiceCollection AddCustomMediatR(this IServiceCollection services)
{ {
services.AddMediatR(typeof(BookingRoot).Assembly); services.AddMediatR(typeof(BookingRoot).Assembly);
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>)); services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>)); services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>));
return services; return services;
} }

View File

@ -1,9 +1,3 @@
namespace Flight.Aircrafts.Dtos; namespace Flight.Aircrafts.Dtos;
public record AircraftResponseDto public record AircraftResponseDto(long Id, string Name, string Model, int ManufacturingYear);
{
public long Id { get; set; }
public string Name { get; init; }
public string Model { get; init; }
public int ManufacturingYear { get; init; }
}

View File

@ -1,7 +1,10 @@
using BuildingBlocks.IdsGenerator; using BuildingBlocks.IdsGenerator;
using Flight.Aircrafts.Features.CreateAircraft.Commands.V1;
using Flight.Aircrafts.Features.CreateAircraft.Commands.V1.Reads; using Flight.Aircrafts.Features.CreateAircraft.Commands.V1.Reads;
using Flight.Aircrafts.Features.CreateAircraft.Dtos.V1;
using Flight.Aircrafts.Models; using Flight.Aircrafts.Models;
using Flight.Aircrafts.Models.Reads; using Flight.Aircrafts.Models.Reads;
using Flight.Airports.Features.CreateAirport.Commands.V1;
using Mapster; using Mapster;
namespace Flight.Aircrafts.Features; namespace Flight.Aircrafts.Features;
@ -17,5 +20,8 @@ public class AircraftMappings : IRegister
config.NewConfig<Aircraft, AircraftReadModel>() config.NewConfig<Aircraft, AircraftReadModel>()
.Map(d => d.Id, s => SnowFlakIdGenerator.NewId()) .Map(d => d.Id, s => SnowFlakIdGenerator.NewId())
.Map(d => d.AircraftId, s => s.Id); .Map(d => d.AircraftId, s => s.Id);
config.NewConfig<CreateAircraftRequestDto, CreateAircraftCommand>()
.ConstructUsing(x => new CreateAircraftCommand(x.Name, x.Model, x.ManufacturingYear));
} }
} }

View File

@ -2,21 +2,4 @@
namespace Flight.Aircrafts.Features.CreateAircraft.Commands.V1.Reads; namespace Flight.Aircrafts.Features.CreateAircraft.Commands.V1.Reads;
public class CreateAircraftMongoCommand : InternalCommand public record CreateAircraftMongoCommand(long Id, string Name, string Model, int ManufacturingYear, bool IsDeleted) : InternalCommand;
{
public CreateAircraftMongoCommand(long id, string name, string model, int manufacturingYear, bool isDeleted)
{
Id = id;
Name = name;
Model = model;
ManufacturingYear = manufacturingYear;
IsDeleted = isDeleted;
}
public long Id { get; }
public string Name { get; }
public string Model { get; }
public int ManufacturingYear { get; }
public bool IsDeleted { get; }
}

View File

@ -32,7 +32,7 @@ public class CreateAircraftMongoCommandHandler : ICommandHandler<CreateAircraftM
var aircraftReadModel = _mapper.Map<AircraftReadModel>(command); var aircraftReadModel = _mapper.Map<AircraftReadModel>(command);
var aircraft = await _flightReadDbContext.Aircraft.AsQueryable() var aircraft = await _flightReadDbContext.Aircraft.AsQueryable()
.FirstOrDefaultAsync(x => x.Id == aircraftReadModel.Id, cancellationToken); .FirstOrDefaultAsync(x => x.AircraftId == aircraftReadModel.AircraftId, cancellationToken);
if (aircraft is not null) if (aircraft is not null)
throw new AircraftAlreadyExistException(); throw new AircraftAlreadyExistException();

View File

@ -0,0 +1,4 @@
namespace Flight.Aircrafts.Features.CreateAircraft.Dtos.V1;
public record CreateAircraftRequestDto(string Name, string Model, int ManufacturingYear);

View File

@ -3,6 +3,8 @@ using System.Threading.Tasks;
using BuildingBlocks.Web; using BuildingBlocks.Web;
using Flight.Aircrafts.Dtos; using Flight.Aircrafts.Dtos;
using Flight.Aircrafts.Features.CreateAircraft.Commands.V1; using Flight.Aircrafts.Features.CreateAircraft.Commands.V1;
using Flight.Aircrafts.Features.CreateAircraft.Dtos.V1;
using MapsterMapper;
using MediatR; using MediatR;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
@ -29,8 +31,11 @@ public class CreateAircraftEndpoint : IMinimalEndpoint
return endpoints; return endpoints;
} }
private async Task<IResult> CreateAircraft(CreateAircraftCommand command, IMediator mediator, CancellationToken cancellationToken) private async Task<IResult> CreateAircraft(CreateAircraftRequestDto request, IMediator mediator, IMapper mapper,
CancellationToken cancellationToken)
{ {
var command = mapper.Map<CreateAircraftCommand>(request);
var result = await mediator.Send(command, cancellationToken); var result = await mediator.Send(command, cancellationToken);
return Results.Ok(result); return Results.Ok(result);

View File

@ -6,10 +6,6 @@ namespace Flight.Aircrafts.Models;
public record Aircraft : Aggregate<long> public record Aircraft : Aggregate<long>
{ {
public Aircraft()
{
}
public string Name { get; private set; } public string Name { get; private set; }
public string Model { get; private set; } public string Model { get; private set; }
public int ManufacturingYear { get; private set; } public int ManufacturingYear { get; private set; }

View File

@ -1,5 +1,7 @@
using BuildingBlocks.IdsGenerator; using BuildingBlocks.IdsGenerator;
using Flight.Airports.Features.CreateAirport.Commands.V1;
using Flight.Airports.Features.CreateAirport.Commands.V1.Reads; using Flight.Airports.Features.CreateAirport.Commands.V1.Reads;
using Flight.Airports.Features.CreateAirport.Dtos.V1;
using Flight.Airports.Models; using Flight.Airports.Models;
using Flight.Airports.Models.Reads; using Flight.Airports.Models.Reads;
using Mapster; using Mapster;
@ -17,5 +19,8 @@ public class AirportMappings : IRegister
config.NewConfig<Airport, AirportReadModel>() config.NewConfig<Airport, AirportReadModel>()
.Map(d => d.Id, s => SnowFlakIdGenerator.NewId()) .Map(d => d.Id, s => SnowFlakIdGenerator.NewId())
.Map(d => d.AirportId, s => s.Id); .Map(d => d.AirportId, s => s.Id);
config.NewConfig<CreateAirportRequestDto, CreateAirportCommand>()
.ConstructUsing(x => new CreateAirportCommand(x.Name, x.Address, x.Code));
} }
} }

View File

@ -1,8 +1,3 @@
namespace Flight.Airports.Dtos; namespace Flight.Airports.Dtos;
public record AirportResponseDto
{ public record AirportResponseDto(long Id, string Name, string Address, string Code);
public long Id { get; set; }
public string Name { get; init; }
public string Address { get; init; }
public string Code { get; init; }
}

View File

@ -2,19 +2,4 @@
namespace Flight.Airports.Features.CreateAirport.Commands.V1.Reads; namespace Flight.Airports.Features.CreateAirport.Commands.V1.Reads;
public class CreateAirportMongoCommand : InternalCommand public record CreateAirportMongoCommand(long Id, string Name, string Address, string Code, bool IsDeleted) : InternalCommand;
{
public CreateAirportMongoCommand(long id, string name, string address, string code, bool isDeleted)
{
Id = id;
Name = name;
Address = address;
Code = code;
IsDeleted = isDeleted;
}
public string Name { get; }
public string Address { get; }
public string Code { get; }
public bool IsDeleted { get; }
}

View File

@ -32,7 +32,7 @@ public class CreateAirportMongoCommandHandler : ICommandHandler<CreateAirportMon
var airportReadModel = _mapper.Map<AirportReadModel>(command); var airportReadModel = _mapper.Map<AirportReadModel>(command);
var aircraft = await _flightReadDbContext.Airport.AsQueryable() var aircraft = await _flightReadDbContext.Airport.AsQueryable()
.FirstOrDefaultAsync(x => x.Id == airportReadModel.Id, cancellationToken); .FirstOrDefaultAsync(x => x.AirportId == airportReadModel.AirportId, cancellationToken);
if (aircraft is not null) if (aircraft is not null)
throw new AirportAlreadyExistException(); throw new AirportAlreadyExistException();

View File

@ -0,0 +1,3 @@
namespace Flight.Airports.Features.CreateAirport.Dtos.V1;
public record CreateAirportRequestDto(string Name, string Address, string Code);

View File

@ -3,6 +3,8 @@ using System.Threading.Tasks;
using BuildingBlocks.Web; using BuildingBlocks.Web;
using Flight.Airports.Dtos; using Flight.Airports.Dtos;
using Flight.Airports.Features.CreateAirport.Commands.V1; using Flight.Airports.Features.CreateAirport.Commands.V1;
using Flight.Airports.Features.CreateAirport.Dtos.V1;
using MapsterMapper;
using MediatR; using MediatR;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
@ -29,8 +31,11 @@ public class CreateAirportEndpoint : IMinimalEndpoint
return endpoints; return endpoints;
} }
private async Task<IResult> CreateAirport(CreateAirportCommand command, IMediator mediator, CancellationToken cancellationToken) private async Task<IResult> CreateAirport(CreateAirportRequestDto request, IMediator mediator, IMapper mapper,
CancellationToken cancellationToken)
{ {
var command = mapper.Map<CreateAirportCommand>(request);
var result = await mediator.Send(command, cancellationToken); var result = await mediator.Send(command, cancellationToken);
return Results.Ok(result); return Results.Ok(result);

View File

@ -6,10 +6,6 @@ namespace Flight.Airports.Models;
public record Airport : Aggregate<long> public record Airport : Aggregate<long>
{ {
public Airport()
{
}
public string Name { get; private set; } public string Name { get; private set; }
public string Address { get; private set; } public string Address { get; private set; }
public string Code { get; private set; } public string Code { get; private set; }

View File

@ -7,5 +7,5 @@ public class AirportReadModel
public string Name { get; init; } public string Name { get; init; }
public string Address { get; init; } public string Address { get; init; }
public string Code { get; init; } public string Code { get; init; }
public bool IsDeleted { get; set; } public bool IsDeleted { get; init; }
} }

View File

@ -90,8 +90,6 @@ public static class InfrastructureExtensions
SnowFlakIdGenerator.Configure(1); SnowFlakIdGenerator.Configure(1);
builder.Services.AddCachingRequest(new List<Assembly> {typeof(FlightRoot).Assembly});
builder.Services.AddEasyCaching(options => { options.UseInMemory(configuration, "mem"); }); builder.Services.AddEasyCaching(options => { options.UseInMemory(configuration, "mem"); });
return builder; return builder;
@ -103,11 +101,11 @@ public static class InfrastructureExtensions
var env = app.Environment; var env = app.Environment;
var appOptions = app.GetOptions<AppOptions>("AppOptions"); var appOptions = app.GetOptions<AppOptions>("AppOptions");
app.UseProblemDetails();
app.UseSerilogRequestLogging(); app.UseSerilogRequestLogging();
app.UseCorrelationId(); app.UseCorrelationId();
app.UseHttpMetrics(); app.UseHttpMetrics();
app.UseMigration<FlightDbContext>(env); app.UseMigration<FlightDbContext>(env);
app.UseProblemDetails();
app.UseHttpsRedirection(); app.UseHttpsRedirection();
app.MapMetrics(); app.MapMetrics();
app.UseCustomHealthCheck(); app.UseCustomHealthCheck();

View File

@ -12,10 +12,11 @@ public static class MediatRExtensions
public static IServiceCollection AddCustomMediatR(this IServiceCollection services) public static IServiceCollection AddCustomMediatR(this IServiceCollection services)
{ {
services.AddMediatR(typeof(FlightRoot).Assembly); services.AddMediatR(typeof(FlightRoot).Assembly);
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>)); services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>)); services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>));
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(EfTxBehavior<,>)); services.AddTransient(typeof(IPipelineBehavior<,>), typeof(EfTxBehavior<,>));
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(CachingBehavior<,>)); services.AddTransient(typeof(IPipelineBehavior<,>), typeof(CachingBehavior<,>));
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(InvalidateCachingBehavior<,>));
return services; return services;
} }

View File

@ -2,18 +2,7 @@ using System;
using Flight.Flights.Models; using Flight.Flights.Models;
namespace Flight.Flights.Dtos; namespace Flight.Flights.Dtos;
public record FlightResponseDto
{ public record FlightResponseDto(long Id, string FlightNumber, long AircraftId, long DepartureAirportId,
public long Id { get; init; } DateTime DepartureDate, DateTime ArriveDate, long ArriveAirportId, decimal DurationMinutes, DateTime FlightDate,
public string FlightNumber { get; init; } Enums.FlightStatus Status, decimal Price);
public long FlightId { get; set; }
public long AircraftId { get; init; }
public long DepartureAirportId { get; init; }
public DateTime DepartureDate { get; init; }
public DateTime ArriveDate { get; init; }
public long ArriveAirportId { get; init; }
public decimal DurationMinutes { get; init; }
public DateTime FlightDate { get; init; }
public Enums.FlightStatus Status { get; init; }
public decimal Price { get; init; }
}

View File

@ -40,6 +40,8 @@ public class CreateFlightCommandHandler : ICommandHandler<CreateFlightCommand, F
var newFlight = await _flightDbContext.Flights.AddAsync(flightEntity, cancellationToken); var newFlight = await _flightDbContext.Flights.AddAsync(flightEntity, cancellationToken);
return _mapper.Map<FlightResponseDto>(newFlight.Entity); var f = _mapper.Map<FlightResponseDto>(newFlight.Entity);
return f;
} }
} }

View File

@ -3,36 +3,6 @@ using BuildingBlocks.Core.Event;
namespace Flight.Flights.Features.CreateFlight.Commands.V1.Reads; namespace Flight.Flights.Features.CreateFlight.Commands.V1.Reads;
public class CreateFlightMongoCommand : InternalCommand public record CreateFlightMongoCommand(long Id, string FlightNumber, long AircraftId, DateTime DepartureDate,
{ long DepartureAirportId, DateTime ArriveDate, long ArriveAirportId, decimal DurationMinutes, DateTime FlightDate,
public CreateFlightMongoCommand(long id, string flightNumber, long aircraftId, DateTime departureDate, Enums.FlightStatus Status, decimal Price, bool IsDeleted) : InternalCommand;
long departureAirportId,
DateTime arriveDate, long arriveAirportId, decimal durationMinutes, DateTime flightDate, Enums.FlightStatus status,
decimal price, bool isDeleted)
{
Id = id;
FlightNumber = flightNumber;
AircraftId = aircraftId;
DepartureDate = departureDate;
DepartureAirportId = departureAirportId;
ArriveDate = arriveDate;
ArriveAirportId = arriveAirportId;
DurationMinutes = durationMinutes;
FlightDate = flightDate;
Status = status;
Price = price;
IsDeleted = isDeleted;
}
public string FlightNumber { get; }
public long AircraftId { get; }
public DateTime DepartureDate { get; }
public long DepartureAirportId { get; }
public DateTime ArriveDate { get; }
public long ArriveAirportId { get; }
public decimal DurationMinutes { get; }
public DateTime FlightDate { get; }
public Enums.FlightStatus Status { get; }
public decimal Price { get; }
public bool IsDeleted { get; }
}

View File

@ -33,7 +33,7 @@ public class CreateFlightMongoCommandHandler : ICommandHandler<CreateFlightMongo
var flightReadModel = _mapper.Map<FlightReadModel>(command); var flightReadModel = _mapper.Map<FlightReadModel>(command);
var flight = await _flightReadDbContext.Flight.AsQueryable() var flight = await _flightReadDbContext.Flight.AsQueryable()
.FirstOrDefaultAsync(x => x.Id == flightReadModel.Id && !x.IsDeleted, cancellationToken); .FirstOrDefaultAsync(x => x.FlightId == flightReadModel.FlightId && !x.IsDeleted, cancellationToken);
if (flight is not null) if (flight is not null)
throw new FlightAlreadyExistException(); throw new FlightAlreadyExistException();

View File

@ -0,0 +1,7 @@
using System;
namespace Flight.Flights.Features.CreateFlight.Dtos.V1;
public record CreateFlightRequestDto(string FlightNumber, long AircraftId, long DepartureAirportId,
DateTime DepartureDate, DateTime ArriveDate, long ArriveAirportId,
decimal DurationMinutes, DateTime FlightDate, Enums.FlightStatus Status, decimal Price);

View File

@ -3,6 +3,9 @@ using System.Threading.Tasks;
using BuildingBlocks.Web; using BuildingBlocks.Web;
using Flight.Flights.Dtos; using Flight.Flights.Dtos;
using Flight.Flights.Features.CreateFlight.Commands.V1; using Flight.Flights.Features.CreateFlight.Commands.V1;
using Flight.Flights.Features.CreateFlight.Dtos.V1;
using Mapster;
using MapsterMapper;
using MediatR; using MediatR;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
@ -29,8 +32,11 @@ public class CreateFlightEndpoint : IMinimalEndpoint
return endpoints; return endpoints;
} }
private async Task<IResult> CreateFlight(CreateFlightCommand command, IMediator mediator, CancellationToken cancellationToken) private async Task<IResult> CreateFlight(CreateFlightRequestDto request, IMediator mediator, IMapper mapper,
CancellationToken cancellationToken)
{ {
var command = mapper.Map<CreateFlightCommand>(request);
var result = await mediator.Send(command, cancellationToken); var result = await mediator.Send(command, cancellationToken);
return Results.Ok(result); return Results.Ok(result);

View File

@ -3,36 +3,6 @@ using BuildingBlocks.Core.Event;
namespace Flight.Flights.Features.DeleteFlight.Commands.V1.Reads; namespace Flight.Flights.Features.DeleteFlight.Commands.V1.Reads;
public class DeleteFlightMongoCommand : InternalCommand public record DeleteFlightMongoCommand(long Id, string FlightNumber, long AircraftId, DateTime DepartureDate,
{ long DepartureAirportId, DateTime ArriveDate, long ArriveAirportId, decimal DurationMinutes, DateTime FlightDate,
public DeleteFlightMongoCommand(long id, string flightNumber, long aircraftId, DateTime departureDate, Enums.FlightStatus Status, decimal Price, bool IsDeleted) : InternalCommand;
long departureAirportId,
DateTime arriveDate, long arriveAirportId, decimal durationMinutes, DateTime flightDate, Enums.FlightStatus status,
decimal price, bool isDeleted)
{
Id = id;
FlightNumber = flightNumber;
AircraftId = aircraftId;
DepartureDate = departureDate;
DepartureAirportId = departureAirportId;
ArriveDate = arriveDate;
ArriveAirportId = arriveAirportId;
DurationMinutes = durationMinutes;
FlightDate = flightDate;
Status = status;
Price = price;
IsDeleted = isDeleted;
}
public string FlightNumber { get; }
public long AircraftId { get; }
public DateTime DepartureDate { get; }
public long DepartureAirportId { get; }
public DateTime ArriveDate { get; }
public long ArriveAirportId { get; }
public decimal DurationMinutes { get; }
public DateTime FlightDate { get; }
public Enums.FlightStatus Status { get; }
public decimal Price { get; }
public bool IsDeleted { get; }
}

View File

@ -1,9 +1,13 @@
using AutoMapper; using AutoMapper;
using BuildingBlocks.IdsGenerator; using BuildingBlocks.IdsGenerator;
using Flight.Flights.Dtos; using Flight.Flights.Dtos;
using Flight.Flights.Features.CreateFlight.Commands.V1;
using Flight.Flights.Features.CreateFlight.Commands.V1.Reads; using Flight.Flights.Features.CreateFlight.Commands.V1.Reads;
using Flight.Flights.Features.CreateFlight.Dtos.V1;
using Flight.Flights.Features.DeleteFlight.Commands.V1.Reads; using Flight.Flights.Features.DeleteFlight.Commands.V1.Reads;
using Flight.Flights.Features.UpdateFlight.Commands.V1;
using Flight.Flights.Features.UpdateFlight.Commands.V1.Reads; using Flight.Flights.Features.UpdateFlight.Commands.V1.Reads;
using Flight.Flights.Features.UpdateFlight.Dtos;
using Flight.Flights.Models.Reads; using Flight.Flights.Models.Reads;
using Mapster; using Mapster;
@ -14,16 +18,30 @@ public class FlightMappings : IRegister
public void Register(TypeAdapterConfig config) public void Register(TypeAdapterConfig config)
{ {
config.NewConfig<Models.Flight, FlightResponseDto>() config.NewConfig<Models.Flight, FlightResponseDto>()
.Map(d => d.FlightId, s => s.Id); .ConstructUsing(x => new FlightResponseDto(x.Id, x.FlightNumber, x.AircraftId, x.DepartureAirportId, x.DepartureDate,
x.ArriveDate, x.ArriveAirportId, x.DurationMinutes, x.FlightDate, x.Status, x.Price));
config.NewConfig<CreateFlightMongoCommand, FlightReadModel>() config.NewConfig<CreateFlightMongoCommand, FlightReadModel>()
.Map(d => d.Id, s => SnowFlakIdGenerator.NewId()) .Map(d => d.Id, s => SnowFlakIdGenerator.NewId())
.Map(d => d.FlightId, s => s.Id); .Map(d => d.FlightId, s => s.Id);
config.NewConfig<Models.Flight, FlightReadModel>() config.NewConfig<Models.Flight, FlightReadModel>()
.Map(d => d.Id, s => SnowFlakIdGenerator.NewId()) .Map(d => d.Id, s => SnowFlakIdGenerator.NewId())
.Map(d => d.FlightId, s => s.Id); .Map(d => d.FlightId, s => s.Id);
config.NewConfig<UpdateFlightMongoCommand, FlightReadModel>() config.NewConfig<UpdateFlightMongoCommand, FlightReadModel>()
.Map(d => d.FlightId, s => s.Id); .Map(d => d.FlightId, s => s.Id);
config.NewConfig<DeleteFlightMongoCommand, FlightReadModel>() config.NewConfig<DeleteFlightMongoCommand, FlightReadModel>()
.Map(d => d.FlightId, s => s.Id); .Map(d => d.FlightId, s => s.Id);
config.NewConfig<CreateFlightRequestDto, CreateFlightCommand>()
.ConstructUsing(x => new CreateFlightCommand(x.FlightNumber, x.AircraftId, x.DepartureAirportId,
x.DepartureDate, x.ArriveDate, x.ArriveAirportId, x.DurationMinutes, x.FlightDate, x.Status, x.Price));
config.NewConfig<UpdateFlightRequestDto, UpdateFlightCommand>()
.ConstructUsing(x => new UpdateFlightCommand(x.Id, x.FlightNumber, x.AircraftId, x.DepartureAirportId, x.DepartureDate,
x.ArriveDate, x.ArriveAirportId, x.DurationMinutes, x.FlightDate, x.Status, x.IsDeleted, x.Price));
} }
} }

View File

@ -1,29 +1,3 @@
// 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.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using BuildingBlocks.Web; using BuildingBlocks.Web;

View File

@ -3,36 +3,6 @@ using BuildingBlocks.Core.Event;
namespace Flight.Flights.Features.UpdateFlight.Commands.V1.Reads; namespace Flight.Flights.Features.UpdateFlight.Commands.V1.Reads;
public class UpdateFlightMongoCommand : InternalCommand public record UpdateFlightMongoCommand(long Id, string FlightNumber, long AircraftId, DateTime DepartureDate,
{ long DepartureAirportId, DateTime ArriveDate, long ArriveAirportId, decimal DurationMinutes, DateTime FlightDate,
public UpdateFlightMongoCommand(long Id, string FlightNumber, long AircraftId, DateTime DepartureDate, Enums.FlightStatus Status, decimal Price, bool IsDeleted) : InternalCommand;
long DepartureAirportId,
DateTime ArriveDate, long ArriveAirportId, decimal DurationMinutes, DateTime FlightDate, Enums.FlightStatus Status,
decimal Price, bool IsDeleted)
{
this.Id = Id;
this.FlightNumber = FlightNumber;
this.AircraftId = AircraftId;
this.DepartureDate = DepartureDate;
this.DepartureAirportId = DepartureAirportId;
this.ArriveDate = ArriveDate;
this.ArriveAirportId = ArriveAirportId;
this.DurationMinutes = DurationMinutes;
this.FlightDate = FlightDate;
this.Status = Status;
this.Price = Price;
this.IsDeleted = IsDeleted;
}
public string FlightNumber { get; }
public long AircraftId { get; }
public DateTime DepartureDate { get; }
public long DepartureAirportId { get; }
public DateTime ArriveDate { get; }
public long ArriveAirportId { get; }
public decimal DurationMinutes { get; }
public DateTime FlightDate { get; }
public Enums.FlightStatus Status { get; }
public decimal Price { get; }
public bool IsDeleted { get; }
}

View File

@ -6,21 +6,9 @@ using Flight.Flights.Dtos;
namespace Flight.Flights.Features.UpdateFlight.Commands.V1; namespace Flight.Flights.Features.UpdateFlight.Commands.V1;
public record UpdateFlightCommand : ICommand<FlightResponseDto>, IInvalidateCacheRequest, IInternalCommand public record UpdateFlightCommand(long Id, string FlightNumber, long AircraftId, long DepartureAirportId,
DateTime DepartureDate, DateTime ArriveDate, long ArriveAirportId, decimal DurationMinutes, DateTime FlightDate,
Enums.FlightStatus Status, bool IsDeleted, decimal Price) : ICommand<FlightResponseDto>, IInternalCommand, IInvalidateCacheRequest
{ {
public long Id { get; init; }
public string FlightNumber { get; init; }
public long AircraftId { get; init; }
public long DepartureAirportId { get; init; }
public DateTime DepartureDate { get; init; }
public DateTime ArriveDate { get; init; }
public long ArriveAirportId { get; init; }
public decimal DurationMinutes { get; init; }
public DateTime FlightDate { get; init; }
public Enums.FlightStatus Status { get; init; }
public bool IsDeleted { get; init; } = false;
public decimal Price { get; init; }
public string CacheKey => "GetAvailableFlightsQuery"; public string CacheKey => "GetAvailableFlightsQuery";
} }

View File

@ -0,0 +1,6 @@
using System;
namespace Flight.Flights.Features.UpdateFlight.Dtos;
public record UpdateFlightRequestDto(long Id, string FlightNumber, long AircraftId, long DepartureAirportId, DateTime DepartureDate, DateTime ArriveDate,
long ArriveAirportId, decimal DurationMinutes, DateTime FlightDate, Enums.FlightStatus Status, decimal Price, bool IsDeleted);

View File

@ -3,6 +3,8 @@ using System.Threading.Tasks;
using BuildingBlocks.Web; using BuildingBlocks.Web;
using Flight.Flights.Dtos; using Flight.Flights.Dtos;
using Flight.Flights.Features.UpdateFlight.Commands.V1; using Flight.Flights.Features.UpdateFlight.Commands.V1;
using Flight.Flights.Features.UpdateFlight.Dtos;
using MapsterMapper;
using MediatR; using MediatR;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
@ -29,8 +31,10 @@ public class UpdateFlightEndpoint : IMinimalEndpoint
return endpoints; return endpoints;
} }
private async Task<IResult> UpdateFlight(UpdateFlightCommand command, IMediator mediator, CancellationToken cancellationToken) private async Task<IResult> UpdateFlight(UpdateFlightRequestDto request, IMediator mediator, IMapper mapper, CancellationToken cancellationToken)
{ {
var command = mapper.Map<UpdateFlightCommand>(request);
var result = await mediator.Send(command, cancellationToken); var result = await mediator.Send(command, cancellationToken);
return Results.Ok(result); return Results.Ok(result);

View File

@ -1,6 +1,4 @@
using System; using System;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace Flight.Flights.Models.Reads; namespace Flight.Flights.Models.Reads;

View File

@ -1,12 +1,3 @@
using Flight.Seats.Models;
namespace Flight.Seats.Dtos; namespace Flight.Seats.Dtos;
public record SeatResponseDto public record SeatResponseDto(long Id, string SeatNumber, Enums.SeatType Type, Enums.SeatClass Class, long FlightId);
{
public long Id { get; set; }
public string SeatNumber { get; init; }
public Enums.SeatType Type { get; init; }
public Enums.SeatClass Class { get; init; }
public long FlightId { get; init; }
}

View File

@ -2,23 +2,5 @@
namespace Flight.Seats.Features.CreateSeat.Commands.V1.Reads; namespace Flight.Seats.Features.CreateSeat.Commands.V1.Reads;
public class CreateSeatMongoCommand : InternalCommand public record CreateSeatMongoCommand(long Id, string SeatNumber, Enums.SeatType Type,
{ Enums.SeatClass Class, long FlightId, bool IsDeleted) : InternalCommand;
public CreateSeatMongoCommand(long id, string seatNumber, Enums.SeatType type, Enums.SeatClass @class,
long flightId, bool isDeleted)
{
Id = id;
SeatNumber = seatNumber;
Type = type;
Class = @class;
FlightId = flightId;
IsDeleted = isDeleted;
}
public long Id { get; }
public string SeatNumber { get; }
public Enums.SeatType Type { get; }
public Enums.SeatClass Class { get; }
public long FlightId { get; }
public bool IsDeleted { get; }
}

View File

@ -32,7 +32,7 @@ public class CreateSeatMongoCommandHandler : ICommandHandler<CreateSeatMongoComm
var seatReadModel = _mapper.Map<SeatReadModel>(command); var seatReadModel = _mapper.Map<SeatReadModel>(command);
var seat = await _flightReadDbContext.Seat.AsQueryable() var seat = await _flightReadDbContext.Seat.AsQueryable()
.FirstOrDefaultAsync(x => x.Id == seatReadModel.Id, cancellationToken); .FirstOrDefaultAsync(x => x.SeatId == seatReadModel.SeatId, cancellationToken);
if (seat is not null) if (seat is not null)
throw new SeatAlreadyExistException(); throw new SeatAlreadyExistException();

View File

@ -0,0 +1,3 @@
namespace Flight.Seats.Features.CreateSeat.Dtos.V1;
public record CreateSeatRequestDto(string SeatNumber, Enums.SeatType Type, Enums.SeatClass Class, long FlightId);

View File

@ -1,34 +1,10 @@
// 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.Dtos;
using Flight.Seats.Features.CreateSeat.Commands.V1; using Flight.Seats.Features.CreateSeat.Commands.V1;
using Flight.Seats.Features.CreateSeat.Dtos.V1;
using MapsterMapper;
using MediatR; using MediatR;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
@ -55,8 +31,11 @@ public class CreateSeatEndpoint : IMinimalEndpoint
return endpoints; return endpoints;
} }
private async Task<IResult> CreateSeat(CreateSeatCommand command, IMediator mediator, CancellationToken cancellationToken) private async Task<IResult> CreateSeat(CreateSeatRequestDto request, IMediator mediator, IMapper mapper,
CancellationToken cancellationToken)
{ {
var command = mapper.Map<CreateSeatCommand>(request);
var result = await mediator.Send(command, cancellationToken); var result = await mediator.Send(command, cancellationToken);
return Results.Ok(result); return Results.Ok(result);

View File

@ -2,22 +2,5 @@
namespace Flight.Seats.Features.ReserveSeat.Commands.V1.Reads; namespace Flight.Seats.Features.ReserveSeat.Commands.V1.Reads;
public class ReserveSeatMongoCommand : InternalCommand public record ReserveSeatMongoCommand(long Id, string SeatNumber, Enums.SeatType Type,
{ Enums.SeatClass Class, long FlightId, bool IsDeleted) : InternalCommand;
public ReserveSeatMongoCommand(long id, string seatNumber, Enums.SeatType type, Enums.SeatClass @class, long flightId,
bool isDeleted)
{
Id = id;
SeatNumber = seatNumber;
Type = type;
Class = @class;
FlightId = flightId;
IsDeleted = isDeleted;
}
public string SeatNumber { get; }
public Enums.SeatType Type { get; }
public Enums.SeatClass Class { get; }
public long FlightId { get; }
public bool IsDeleted { get; }
}

View File

@ -0,0 +1,4 @@
namespace Flight.Seats.Features.ReserveSeat.Dtos.V1;
public record ReserveSeatRequestDto(long FlightId, string SeatNumber);

View File

@ -3,6 +3,8 @@ using System.Threading.Tasks;
using BuildingBlocks.Web; using BuildingBlocks.Web;
using Flight.Seats.Dtos; using Flight.Seats.Dtos;
using Flight.Seats.Features.ReserveSeat.Commands.V1; using Flight.Seats.Features.ReserveSeat.Commands.V1;
using Flight.Seats.Features.ReserveSeat.Dtos.V1;
using MapsterMapper;
using MediatR; using MediatR;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
@ -29,8 +31,10 @@ public class ReserveSeatEndpoint : IMinimalEndpoint
return endpoints; return endpoints;
} }
private async Task<IResult> ReserveSeat(ReserveSeatCommand command, IMediator mediator, CancellationToken cancellationToken) private async Task<IResult> ReserveSeat(ReserveSeatRequestDto request, IMediator mediator, IMapper mapper, CancellationToken cancellationToken)
{ {
var command = mapper.Map<ReserveSeatCommand>(request);
var result = await mediator.Send(command, cancellationToken); var result = await mediator.Send(command, cancellationToken);
return Results.Ok(result); return Results.Ok(result);

View File

@ -1,7 +1,11 @@
using BuildingBlocks.IdsGenerator; using BuildingBlocks.IdsGenerator;
using Flight.Seats.Dtos; using Flight.Seats.Dtos;
using Flight.Seats.Features.CreateSeat.Commands.V1;
using Flight.Seats.Features.CreateSeat.Commands.V1.Reads; using Flight.Seats.Features.CreateSeat.Commands.V1.Reads;
using Flight.Seats.Features.CreateSeat.Dtos.V1;
using Flight.Seats.Features.ReserveSeat.Commands.V1;
using Flight.Seats.Features.ReserveSeat.Commands.V1.Reads; using Flight.Seats.Features.ReserveSeat.Commands.V1.Reads;
using Flight.Seats.Features.ReserveSeat.Dtos.V1;
using Flight.Seats.Models; using Flight.Seats.Models;
using Flight.Seats.Models.Reads; using Flight.Seats.Models.Reads;
using Mapster; using Mapster;
@ -12,14 +16,24 @@ public class SeatMappings : IRegister
{ {
public void Register(TypeAdapterConfig config) public void Register(TypeAdapterConfig config)
{ {
config.NewConfig<Seat, SeatResponseDto>(); config.NewConfig<Seat, SeatResponseDto>()
.ConstructUsing(x => new SeatResponseDto(x.Id, x.SeatNumber, x.Type, x.Class, x.FlightId));
config.NewConfig<CreateSeatMongoCommand, SeatReadModel>() config.NewConfig<CreateSeatMongoCommand, SeatReadModel>()
.Map(d => d.Id, s => SnowFlakIdGenerator.NewId()) .Map(d => d.Id, s => SnowFlakIdGenerator.NewId())
.Map(d => d.SeatId, s => s.Id); .Map(d => d.SeatId, s => s.Id);
config.NewConfig<Seat, SeatReadModel>() config.NewConfig<Seat, SeatReadModel>()
.Map(d => d.Id, s => SnowFlakIdGenerator.NewId()) .Map(d => d.Id, s => SnowFlakIdGenerator.NewId())
.Map(d => d.SeatId, s => s.Id); .Map(d => d.SeatId, s => s.Id);
config.NewConfig<ReserveSeatMongoCommand, SeatReadModel>() config.NewConfig<ReserveSeatMongoCommand, SeatReadModel>()
.Map(d => d.SeatId, s => s.Id); .Map(d => d.SeatId, s => s.Id);
config.NewConfig<CreateSeatRequestDto, CreateSeatCommand>()
.ConstructUsing(x => new CreateSeatCommand(x.SeatNumber, x.Type, x.Class, x.FlightId));
config.NewConfig<ReserveSeatRequestDto, ReserveSeatCommand>()
.ConstructUsing(x => new ReserveSeatCommand(x.FlightId, x.SeatNumber));
} }
} }

View File

@ -1,5 +1,4 @@
using Flight.Flights.Features.CreateFlight; using Flight.Flights.Features.CreateFlight.Commands.V1;
using Flight.Flights.Features.CreateFlight.Commands.V1;
namespace Integration.Test.Fakes; namespace Integration.Test.Fakes;

View File

@ -39,7 +39,7 @@ public class GetFlightByIdTests : IntegrationTestBase<Program, FlightDbContext,
// Assert // Assert
response.Should().NotBeNull(); response.Should().NotBeNull();
response?.FlightId.Should().Be(command.Id); response?.Id.Should().Be(command.Id);
} }
[Fact] [Fact]

View File

@ -84,6 +84,7 @@ public static class InfrastructureExtensions
var env = app.Environment; var env = app.Environment;
var appOptions = app.GetOptions<AppOptions>("AppOptions"); var appOptions = app.GetOptions<AppOptions>("AppOptions");
app.UseProblemDetails();
app.UseSerilogRequestLogging(); app.UseSerilogRequestLogging();
app.UseMigration<IdentityContext>(env); app.UseMigration<IdentityContext>(env);
app.UseCorrelationId(); app.UseCorrelationId();

View File

@ -10,8 +10,8 @@ public static class MediatRExtensions
public static IServiceCollection AddCustomMediatR(this IServiceCollection services) public static IServiceCollection AddCustomMediatR(this IServiceCollection services)
{ {
services.AddMediatR(typeof(IdentityRoot).Assembly); services.AddMediatR(typeof(IdentityRoot).Assembly);
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>)); services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>)); services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>));
return services; return services;
} }

View File

@ -1,13 +1,3 @@
using System;
namespace Identity.Identity.Dtos; namespace Identity.Identity.Dtos;
public record RegisterNewUserResponseDto public record RegisterNewUserResponseDto(long Id, string FirstName, string LastName, string Username, string PassportNumber);
{
public long Id { get; init; }
public string FirstName { get; init; }
public string LastName { get; init; }
public string Username { get; init; }
public string PassportNumber { get; set; }
}

View File

@ -0,0 +1,15 @@
using Identity.Identity.Features.RegisterNewUser.Commands.V1;
using Identity.Identity.Features.RegisterNewUser.Dtos.V1;
using Mapster;
namespace Identity.Identity.Features;
public class IdentityMappings : IRegister
{
public void Register(TypeAdapterConfig config)
{
config.NewConfig<RegisterNewUserRequestDto, RegisterNewUserCommand>()
.ConstructUsing(x => new RegisterNewUserCommand(x.FirstName, x.LastName, x.Username, x.Email,
x.Password, x.ConfirmPassword, x.PassportNumber));
}
}

View File

@ -29,7 +29,7 @@ public class RegisterNewUserCommandHandler : ICommandHandler<RegisterNewUserComm
{ {
Guard.Against.Null(command, nameof(command)); Guard.Against.Null(command, nameof(command));
var applicationUser = new ApplicationUser var applicationUser = new ApplicationUser()
{ {
FirstName = command.FirstName, FirstName = command.FirstName,
LastName = command.LastName, LastName = command.LastName,
@ -51,13 +51,7 @@ public class RegisterNewUserCommandHandler : ICommandHandler<RegisterNewUserComm
await _eventDispatcher.SendAsync(new UserCreated(applicationUser.Id, applicationUser.FirstName + " " + applicationUser.LastName, await _eventDispatcher.SendAsync(new UserCreated(applicationUser.Id, applicationUser.FirstName + " " + applicationUser.LastName,
applicationUser.PassPortNumber),cancellationToken: cancellationToken); applicationUser.PassPortNumber),cancellationToken: cancellationToken);
return new RegisterNewUserResponseDto return new RegisterNewUserResponseDto(applicationUser.Id, applicationUser.FirstName, applicationUser.LastName,
{ applicationUser.UserName, applicationUser.PassPortNumber);
Id = applicationUser.Id,
FirstName = applicationUser.FirstName,
LastName = applicationUser.LastName,
Username = applicationUser.UserName,
PassportNumber = applicationUser.PassPortNumber
};
} }
} }

View File

@ -0,0 +1,4 @@
namespace Identity.Identity.Features.RegisterNewUser.Dtos.V1;
public record RegisterNewUserRequestDto(string FirstName, string LastName, string Username, string Email,
string Password, string ConfirmPassword, string PassportNumber);

View File

@ -3,6 +3,8 @@ using System.Threading.Tasks;
using BuildingBlocks.Web; using BuildingBlocks.Web;
using Identity.Identity.Dtos; using Identity.Identity.Dtos;
using Identity.Identity.Features.RegisterNewUser.Commands.V1; using Identity.Identity.Features.RegisterNewUser.Commands.V1;
using Identity.Identity.Features.RegisterNewUser.Dtos.V1;
using MapsterMapper;
using MediatR; using MediatR;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
@ -28,8 +30,11 @@ public class RegisterNewUserEndpoint : IMinimalEndpoint
return endpoints; return endpoints;
} }
private async Task<IResult> RegisterNewUser(RegisterNewUserCommand command, IMediator mediator, CancellationToken cancellationToken) private async Task<IResult> RegisterNewUser(RegisterNewUserRequestDto request, IMediator mediator, IMapper mapper,
CancellationToken cancellationToken)
{ {
var command = mapper.Map<RegisterNewUserCommand>(request);
var result = await mediator.Send(command, cancellationToken); var result = await mediator.Send(command, cancellationToken);
return Results.Ok(result); return Results.Ok(result);

View File

@ -4,7 +4,7 @@ namespace Identity.Identity.Models;
public class ApplicationUser : IdentityUser<long> public class ApplicationUser : IdentityUser<long>
{ {
public string FirstName { get; set; } public string FirstName { get; init; }
public string LastName { get; set; } public string LastName { get; init; }
public string PassPortNumber { get; set; } public string PassPortNumber { get; init; }
} }

View File

@ -1,27 +1,5 @@
using BuildingBlocks.EFCore;
using BuildingBlocks.Exception;
using BuildingBlocks.HealthCheck;
using BuildingBlocks.IdsGenerator;
using BuildingBlocks.Jwt;
using BuildingBlocks.Logging;
using BuildingBlocks.Mapster;
using BuildingBlocks.MassTransit;
using BuildingBlocks.Mongo;
using BuildingBlocks.OpenTelemetry;
using BuildingBlocks.PersistMessageProcessor;
using BuildingBlocks.Swagger;
using BuildingBlocks.Web; using BuildingBlocks.Web;
using Figgle;
using FluentValidation;
using Hellang.Middleware.ProblemDetails;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Passenger;
using Passenger.Data;
using Passenger.Extensions;
using Passenger.Extensions.Infrastructure; using Passenger.Extensions.Infrastructure;
using Passenger.GrpcServer.Services;
using Prometheus;
using Serilog;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);

View File

@ -91,11 +91,11 @@ public static class InfrastructureExtensions
var env = app.Environment; var env = app.Environment;
var appOptions = app.GetOptions<AppOptions>("AppOptions"); var appOptions = app.GetOptions<AppOptions>("AppOptions");
app.UseProblemDetails();
app.UseSerilogRequestLogging(); app.UseSerilogRequestLogging();
app.UseMigration<PassengerDbContext>(env); app.UseMigration<PassengerDbContext>(env);
app.UseCorrelationId(); app.UseCorrelationId();
app.UseHttpMetrics(); app.UseHttpMetrics();
app.UseProblemDetails();
app.UseHttpsRedirection(); app.UseHttpsRedirection();
app.UseCustomHealthCheck(); app.UseCustomHealthCheck();
app.MapMetrics(); app.MapMetrics();

View File

@ -11,9 +11,9 @@ public static class MediatRExtensions
public static IServiceCollection AddCustomMediatR(this IServiceCollection services) public static IServiceCollection AddCustomMediatR(this IServiceCollection services)
{ {
services.AddMediatR(typeof(PassengerRoot).Assembly); services.AddMediatR(typeof(PassengerRoot).Assembly);
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>)); services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>)); services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>));
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(EfTxBehavior<,>)); services.AddTransient(typeof(IPipelineBehavior<,>), typeof(EfTxBehavior<,>));
return services; return services;
} }

View File

@ -2,22 +2,5 @@
namespace Passenger.Passengers.Features.CompleteRegisterPassenger.Commands.V1.Reads; namespace Passenger.Passengers.Features.CompleteRegisterPassenger.Commands.V1.Reads;
public class CompleteRegisterPassengerMongoCommand : InternalCommand public record CompleteRegisterPassengerMongoCommand(long Id, string PassportNumber, string Name,
{ Enums.PassengerType PassengerType, int Age, bool IsDeleted) : InternalCommand;
public CompleteRegisterPassengerMongoCommand(long id, string passportNumber, string name,
Enums.PassengerType passengerType, int age, bool isDeleted)
{
Id = id;
PassportNumber = passportNumber;
Name = name;
PassengerType = passengerType;
Age = age;
IsDeleted = isDeleted;
}
public string PassportNumber { get; }
public string Name { get; }
public Enums.PassengerType PassengerType { get; }
public int Age { get; }
public bool IsDeleted { get; }
}

View File

@ -29,7 +29,7 @@ public class CompleteRegisterPassengerMongoCommandHandler : ICommandHandler<Comp
var passengerReadModel = _mapper.Map<PassengerReadModel>(command); var passengerReadModel = _mapper.Map<PassengerReadModel>(command);
var passenger = await _passengerReadDbContext.Passenger.AsQueryable() var passenger = await _passengerReadDbContext.Passenger.AsQueryable()
.FirstOrDefaultAsync(x => x.PassengerId == command.Id && !x.IsDeleted, cancellationToken); .FirstOrDefaultAsync(x => x.PassengerId == passengerReadModel.PassengerId && !x.IsDeleted, cancellationToken);
if (passenger is not null) if (passenger is not null)
{ {

View File

@ -0,0 +1,3 @@
namespace Passenger.Passengers.Features.CompleteRegisterPassenger.Dtos.V1;
public record CompleteRegisterPassengerRequestDto(string PassportNumber, Enums.PassengerType PassengerType, int Age);

View File

@ -1,12 +1,12 @@
using BuildingBlocks.Web; using BuildingBlocks.Web;
using MapsterMapper;
using MediatR; using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing;
using Passenger.Passengers.Dtos; using Passenger.Passengers.Dtos;
using Passenger.Passengers.Features.CompleteRegisterPassenger.Commands.V1; using Passenger.Passengers.Features.CompleteRegisterPassenger.Commands.V1;
using Passenger.Passengers.Features.CompleteRegisterPassenger.Dtos.V1;
using Swashbuckle.AspNetCore.Annotations; using Swashbuckle.AspNetCore.Annotations;
namespace Passenger.Passengers.Features.CompleteRegisterPassenger.Endpoints.V1; namespace Passenger.Passengers.Features.CompleteRegisterPassenger.Endpoints.V1;
@ -29,8 +29,11 @@ public class CompleteRegisterPassengerEndpoint : IMinimalEndpoint
return endpoints; return endpoints;
} }
private async Task<IResult> CompleteRegisterPassenger(CompleteRegisterPassengerCommand command, IMediator mediator, CancellationToken cancellationToken) private async Task<IResult> CompleteRegisterPassenger(CompleteRegisterPassengerRequestDto request, IMapper mapper,
IMediator mediator, CancellationToken cancellationToken)
{ {
var command = mapper.Map<CompleteRegisterPassengerCommand>(request);
var result = await mediator.Send(command, cancellationToken); var result = await mediator.Send(command, cancellationToken);
return Results.Ok(result); return Results.Ok(result);

View File

@ -3,7 +3,9 @@ using BuildingBlocks.Contracts.EventBus.Messages;
using BuildingBlocks.IdsGenerator; using BuildingBlocks.IdsGenerator;
using Mapster; using Mapster;
using Passenger.Passengers.Dtos; using Passenger.Passengers.Dtos;
using Passenger.Passengers.Features.CompleteRegisterPassenger.Commands.V1;
using Passenger.Passengers.Features.CompleteRegisterPassenger.Commands.V1.Reads; using Passenger.Passengers.Features.CompleteRegisterPassenger.Commands.V1.Reads;
using Passenger.Passengers.Features.CompleteRegisterPassenger.Dtos.V1;
using Passenger.Passengers.Models.Reads; using Passenger.Passengers.Models.Reads;
namespace Passenger.Passengers.Features; namespace Passenger.Passengers.Features;
@ -15,5 +17,8 @@ public class PassengerMappings : IRegister
config.NewConfig<CompleteRegisterPassengerMongoCommand, PassengerReadModel>() config.NewConfig<CompleteRegisterPassengerMongoCommand, PassengerReadModel>()
.Map(d => d.Id, s => SnowFlakIdGenerator.NewId()) .Map(d => d.Id, s => SnowFlakIdGenerator.NewId())
.Map(d => d.PassengerId, s => s.Id); .Map(d => d.PassengerId, s => s.Id);
config.NewConfig<CompleteRegisterPassengerRequestDto, CompleteRegisterPassengerCommand>()
.ConstructUsing(x => new CompleteRegisterPassengerCommand(x.PassportNumber, x.PassengerType, x.Age));
} }
} }

View File

@ -4,9 +4,9 @@ public class PassengerReadModel
{ {
public long Id { get; init; } public long Id { get; init; }
public long PassengerId { get; init; } public long PassengerId { get; init; }
public string PassportNumber { get; private set; } public string PassportNumber { get; init; }
public string Name { get; private set; } public string Name { get; init; }
public Enums.PassengerType PassengerType { get; private set; } public Enums.PassengerType PassengerType { get; init; }
public int Age { get; private set; } public int Age { get; init; }
public bool IsDeleted { get; init; } public bool IsDeleted { get; init; }
} }