mirror of
https://github.com/meysamhadeli/booking-microservices.git
synced 2026-04-11 10:32:09 +08:00
Merge branch 'develop'
This commit is contained in:
commit
e69d3d856c
@ -13,6 +13,10 @@ services:
|
||||
environment:
|
||||
- POSTGRES_USER=postgres
|
||||
- POSTGRES_PASSWORD=postgres
|
||||
command:
|
||||
- "postgres"
|
||||
- "-c"
|
||||
- "wal_level=logical"
|
||||
networks:
|
||||
- booking
|
||||
|
||||
|
||||
@ -27,6 +27,10 @@ services:
|
||||
environment:
|
||||
- POSTGRES_USER=postgres
|
||||
- POSTGRES_PASSWORD=postgres
|
||||
command:
|
||||
- "postgres"
|
||||
- "-c"
|
||||
- "wal_level=logical"
|
||||
networks:
|
||||
- booking
|
||||
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
namespace Booking.Booking.Features.CreatingBook.Commands.V1;
|
||||
|
||||
using BuildingBlocks.Core.Event;
|
||||
using BuildingBlocks.Core.Model;
|
||||
using Models.ValueObjects;
|
||||
|
||||
public record BookingCreatedDomainEvent(Guid Id, PassengerInfo PassengerInfo, Trip Trip) : Audit, IDomainEvent;
|
||||
@ -4,22 +4,65 @@ using Ardalis.GuardClauses;
|
||||
using BuildingBlocks.Core;
|
||||
using BuildingBlocks.Core.CQRS;
|
||||
using BuildingBlocks.Core.Event;
|
||||
using BuildingBlocks.Core.Model;
|
||||
using BuildingBlocks.EventStoreDB.Repository;
|
||||
using BuildingBlocks.Web;
|
||||
using Exceptions;
|
||||
using Flight;
|
||||
using FluentValidation;
|
||||
using MapsterMapper;
|
||||
using MassTransit;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Models.ValueObjects;
|
||||
using Passenger;
|
||||
|
||||
public record CreateBooking(Guid PassengerId, Guid FlightId, string Description) : ICommand<CreateBookingResult>, IInternalCommand
|
||||
public record CreateBooking(Guid PassengerId, Guid FlightId, string Description) : ICommand<CreateBookingResult>,
|
||||
IInternalCommand
|
||||
{
|
||||
public Guid Id { get; init; } = NewId.NextGuid();
|
||||
}
|
||||
|
||||
public record CreateBookingResult(ulong Id);
|
||||
|
||||
public record BookingCreatedDomainEvent(Guid Id, PassengerInfo PassengerInfo, Trip Trip) : Audit, IDomainEvent;
|
||||
|
||||
public record CreateBookingRequestDto(Guid PassengerId, Guid FlightId, string Description);
|
||||
|
||||
public record CreateBookingResponseDto(ulong Id);
|
||||
|
||||
public class CreateBookingEndpoint : IMinimalEndpoint
|
||||
{
|
||||
public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapPost($"{EndpointConfig.BaseApiPath}/booking", async (CreateBookingRequestDto request,
|
||||
IMediator mediator, IMapper mapper,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
var command = mapper.Map<CreateBooking>(request);
|
||||
|
||||
var result = await mediator.Send(command, cancellationToken);
|
||||
|
||||
var response = new CreateBookingResponseDto(result.Id);
|
||||
|
||||
return Results.Ok(response);
|
||||
})
|
||||
.RequireAuthorization()
|
||||
.WithName("CreateBooking")
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Booking").Build())
|
||||
.Produces<CreateBookingResponseDto>()
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.WithSummary("Create Booking")
|
||||
.WithDescription("Create Booking")
|
||||
.WithOpenApi()
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
internal class CreateBookingValidator : AbstractValidator<CreateBooking>
|
||||
{
|
||||
public CreateBookingValidator()
|
||||
@ -54,16 +97,21 @@ internal class CreateBookingCommandHandler : ICommandHandler<CreateBooking, Crea
|
||||
{
|
||||
Guard.Against.Null(command, nameof(command));
|
||||
|
||||
var flight = await _flightGrpcServiceClient.GetByIdAsync(new Flight.GetByIdRequest { Id = command.FlightId.ToString()});
|
||||
var flight =
|
||||
await _flightGrpcServiceClient.GetByIdAsync(new Flight.GetByIdRequest { Id = command.FlightId.ToString() });
|
||||
|
||||
if (flight is null)
|
||||
{
|
||||
throw new FlightNotFoundException();
|
||||
}
|
||||
|
||||
var passenger = await _passengerGrpcServiceClient.GetByIdAsync(new Passenger.GetByIdRequest { Id = command.PassengerId.ToString() });
|
||||
var passenger =
|
||||
await _passengerGrpcServiceClient.GetByIdAsync(
|
||||
new Passenger.GetByIdRequest { Id = command.PassengerId.ToString() });
|
||||
|
||||
var emptySeat = (await _flightGrpcServiceClient.GetAvailableSeatsAsync(new GetAvailableSeatsRequest { FlightId = command.FlightId.ToString() }).ResponseAsync)
|
||||
var emptySeat = (await _flightGrpcServiceClient
|
||||
.GetAvailableSeatsAsync(new GetAvailableSeatsRequest { FlightId = command.FlightId.ToString() })
|
||||
.ResponseAsync)
|
||||
?.SeatDtos?.FirstOrDefault();
|
||||
|
||||
var reservation = await _eventStoreDbRepository.Find(command.Id, cancellationToken);
|
||||
@ -74,8 +122,10 @@ internal class CreateBookingCommandHandler : ICommandHandler<CreateBooking, Crea
|
||||
}
|
||||
|
||||
var aggrigate = Models.Booking.Create(command.Id, new PassengerInfo(passenger.PassengerDto?.Name), new Trip(
|
||||
flight.FlightDto.FlightNumber, new Guid(flight.FlightDto.AircraftId), new Guid(flight.FlightDto.DepartureAirportId),
|
||||
new Guid(flight.FlightDto.ArriveAirportId), flight.FlightDto.FlightDate.ToDateTime(), (decimal)flight.FlightDto.Price, command.Description,
|
||||
flight.FlightDto.FlightNumber, new Guid(flight.FlightDto.AircraftId),
|
||||
new Guid(flight.FlightDto.DepartureAirportId),
|
||||
new Guid(flight.FlightDto.ArriveAirportId), flight.FlightDto.FlightDate.ToDateTime(),
|
||||
(decimal)flight.FlightDto.Price, command.Description,
|
||||
emptySeat?.SeatNumber),
|
||||
false, _currentUserProvider.GetCurrentUserId());
|
||||
|
||||
|
||||
@ -1,41 +0,0 @@
|
||||
namespace Booking.Booking.Features.CreatingBook.Commands.V1;
|
||||
|
||||
using BuildingBlocks.Web;
|
||||
using MapsterMapper;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.OpenApi.Models;
|
||||
|
||||
public record CreateBookingRequestDto(Guid PassengerId, Guid FlightId, string Description);
|
||||
public record CreateBookingResponseDto(ulong Id);
|
||||
|
||||
public class CreateBookingEndpoint : IMinimalEndpoint
|
||||
{
|
||||
public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapPost($"{EndpointConfig.BaseApiPath}/booking", CreateBooking)
|
||||
.RequireAuthorization()
|
||||
.WithName("CreateBooking")
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Booking").Build())
|
||||
.Produces<CreateBookingResponseDto>()
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.WithOpenApi(operation => new OpenApiOperation(operation) { Summary = "Create Booking", Description = "Create Booking" })
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private async Task<IResult> CreateBooking(CreateBookingRequestDto request, IMediator mediator, IMapper mapper,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var command = mapper.Map<CreateBooking>(request);
|
||||
|
||||
var result = await mediator.Send(command, cancellationToken);
|
||||
|
||||
var response = new CreateBookingResponseDto(result.Id);
|
||||
|
||||
return Results.Ok(response);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
# OpenAPI Generator Ignore
|
||||
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
|
||||
|
||||
# Use this file to prevent files from being overwritten by the generator.
|
||||
# The patterns follow closely to .gitignore or .dockerignore.
|
||||
|
||||
# As an example, the C# client generator defines ApiClient.cs.
|
||||
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
|
||||
#ApiClient.cs
|
||||
|
||||
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
|
||||
#foo/*/qux
|
||||
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
|
||||
|
||||
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
|
||||
#foo/**/qux
|
||||
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
|
||||
|
||||
# You can also negate patterns with an exclamation (!).
|
||||
# For example, you can ignore all files in a docs folder with the file extension .md:
|
||||
#docs/*.md
|
||||
# Then explicitly reverse the ignore rule for a single file:
|
||||
#!docs/README.md
|
||||
@ -0,0 +1,3 @@
|
||||
.openapi-generator-ignore
|
||||
README.md
|
||||
script.js
|
||||
@ -0,0 +1 @@
|
||||
6.6.0-SNAPSHOT
|
||||
15
src/Services/Booking/tests/PerformanceTest/README.md
Normal file
15
src/Services/Booking/tests/PerformanceTest/README.md
Normal file
@ -0,0 +1,15 @@
|
||||
# Generated k6 script
|
||||
|
||||
The `script.js` file contains most of the Swagger/OpenAPI specification and you can customize it to your needs.
|
||||
|
||||
Global header variables are defined at the top of the file, like `api_key`. Each path in the specification is converted into a [group](https://docs.k6.io/docs/tags-and-groups) in k6 and each group contains all the request methods related to that path. Path and query parameters are extracted from the specification and put at the start of the group. The URL is constructed from the base URL plus path and query.
|
||||
|
||||
If the Swagger/OpenAPI specification used as the input spec contains examples at parameter level, those will be extracted and utilized as parameter values. The `handleParamValue` custom Mustache lambda registered for use in the K6 `script.mustache` template handles the conditional checks, formatting, and outputting of parameter values. If a given parameter has value specified – either in `example` or `examples` field, defined at the parameter level – that value will be used. For list (`examples`), entire list will be output in the generated script and the first element from that list will be assigned as parameter value. If a given parameter does not have an example defined, a placeholder value with `TODO_EDIT_THE_` prefix will be generated for that parameter, and you will have to assign a value before you can run the script. In other words, you can now generate K6 test scripts which are ready to run, provided the Swagger/OpenAPI specification used as the input spec contains examples for all of the path/query parameters; see `modules/openapi-generator/src/test/resources/3_0/examples.yaml` for an example of such specification, and https://swagger.io/docs/specification/adding-examples/ for more information about adding examples.
|
||||
|
||||
k6 specific parameters are in the [`params`](https://docs.k6.io/docs/params-k6http) object, and `body` contains the [request](https://docs.k6.io/docs/http-requests) body which is in the form of `identifier: type`, which the `type` should be substituted by a proper value. Then goes the request and the check.
|
||||
|
||||
[Check](https://docs.k6.io/docs/checks) are like asserts but differ in that they don't halt execution, instead they just store the result of the check, pass or fail, and let the script execution continue.
|
||||
|
||||
Each request is always followed by a 0.1 second [sleep](https://docs.k6.io/docs/sleep-t-1) to prevent the script execution from flooding the system with too many requests simultaneously.
|
||||
|
||||
Note that the default iteration count and VU count is 1. So each request in each group will be executed once. For more information, see the [k6 options](https://docs.k6.io/docs/options).
|
||||
41
src/Services/Booking/tests/PerformanceTest/script.js
Normal file
41
src/Services/Booking/tests/PerformanceTest/script.js
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* APIs
|
||||
* An example application with OpenAPI, Swashbuckle, and API versioning.
|
||||
*
|
||||
* OpenAPI spec version: 1.0
|
||||
* Contact:
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator.
|
||||
* https://github.com/OpenAPITools/openapi-generator
|
||||
*
|
||||
* OpenAPI generator version: 6.6.0-SNAPSHOT
|
||||
*/
|
||||
|
||||
|
||||
import http from "k6/http";
|
||||
import { group, check, sleep } from "k6";
|
||||
|
||||
const BASE_URL = "/";
|
||||
// Sleep duration between successive requests.
|
||||
// You might want to edit the value of this variable or remove calls to the sleep function on the script.
|
||||
const SLEEP_DURATION = 0.1;
|
||||
// Global variables should be initialized.
|
||||
|
||||
export default function() {
|
||||
group("/api/v1/booking", () => {
|
||||
|
||||
// Request No. 1: CreateBooking
|
||||
{
|
||||
let url = BASE_URL + `/api/v1/booking`;
|
||||
// TODO: edit the parameters of the request body.
|
||||
let body = {"passengerId": "uuid", "flightId": "uuid", "description": "string"};
|
||||
let params = {headers: {"Content-Type": "application/json", "Accept": "application/json"}};
|
||||
let request = http.post(url, JSON.stringify(body), params);
|
||||
|
||||
check(request, {
|
||||
"Success": (r) => r.status === 200
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
namespace Flight.Aircrafts.Features.CreatingAircraft.V1;
|
||||
|
||||
using System;
|
||||
using BuildingBlocks.Core.Event;
|
||||
|
||||
public record AircraftCreatedDomainEvent(Guid Id, string Name, string Model, int ManufacturingYear, bool IsDeleted) : IDomainEvent;
|
||||
@ -6,21 +6,64 @@ using System.Threading.Tasks;
|
||||
using Ardalis.GuardClauses;
|
||||
using BuildingBlocks.Core.CQRS;
|
||||
using BuildingBlocks.Core.Event;
|
||||
using BuildingBlocks.Web;
|
||||
using Exceptions;
|
||||
using Models;
|
||||
using Data;
|
||||
using FluentValidation;
|
||||
using MapsterMapper;
|
||||
using MassTransit;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
public record CreateAircraft(string Name, string Model, int ManufacturingYear) : ICommand<CreateAircraftResult>, IInternalCommand
|
||||
public record CreateAircraft(string Name, string Model, int ManufacturingYear) : ICommand<CreateAircraftResult>,
|
||||
IInternalCommand
|
||||
{
|
||||
public Guid Id { get; init; } = NewId.NextGuid();
|
||||
}
|
||||
|
||||
public record CreateAircraftResult(Guid Id);
|
||||
|
||||
public record AircraftCreatedDomainEvent
|
||||
(Guid Id, string Name, string Model, int ManufacturingYear, bool IsDeleted) : IDomainEvent;
|
||||
|
||||
public record CreateAircraftRequestDto(string Name, string Model, int ManufacturingYear);
|
||||
|
||||
public record CreateAircraftResponseDto(Guid Id);
|
||||
|
||||
public class CreateAircraftEndpoint : IMinimalEndpoint
|
||||
{
|
||||
public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapPost($"{EndpointConfig.BaseApiPath}/flight/aircraft", async (CreateAircraftRequestDto request,
|
||||
IMediator mediator, IMapper mapper,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
var command = mapper.Map<CreateAircraft>(request);
|
||||
|
||||
var result = await mediator.Send(command, cancellationToken);
|
||||
|
||||
var response = new CreateAircraftResponseDto(result.Id);
|
||||
|
||||
return Results.Ok(response);
|
||||
})
|
||||
.RequireAuthorization()
|
||||
.WithName("CreateAircraft")
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Flight").Build())
|
||||
.Produces<CreateAircraftResponseDto>()
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.WithSummary("Create Aircraft")
|
||||
.WithDescription("Create Aircraft")
|
||||
.WithOpenApi()
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
internal class CreateAircraftValidator : AbstractValidator<CreateAircraft>
|
||||
{
|
||||
public CreateAircraftValidator()
|
||||
@ -44,7 +87,8 @@ internal class CreateAircraftHandler : IRequestHandler<CreateAircraft, CreateAir
|
||||
{
|
||||
Guard.Against.Null(request, nameof(request));
|
||||
|
||||
var aircraft = await _flightDbContext.Aircraft.SingleOrDefaultAsync(x => x.Model == request.Model, cancellationToken);
|
||||
var aircraft =
|
||||
await _flightDbContext.Aircraft.SingleOrDefaultAsync(x => x.Model == request.Model, cancellationToken);
|
||||
|
||||
if (aircraft is not null)
|
||||
{
|
||||
|
||||
@ -1,44 +0,0 @@
|
||||
namespace Flight.Aircrafts.Features.CreatingAircraft.V1;
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.Web;
|
||||
using MapsterMapper;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.OpenApi.Models;
|
||||
|
||||
public record CreateAircraftRequestDto(string Name, string Model, int ManufacturingYear);
|
||||
public record CreateAircraftResponseDto(Guid Id);
|
||||
|
||||
public class CreateAircraftEndpoint : IMinimalEndpoint
|
||||
{
|
||||
public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapPost($"{EndpointConfig.BaseApiPath}/flight/aircraft", CreateAircraft)
|
||||
.RequireAuthorization()
|
||||
.WithName("CreateAircraft")
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Flight").Build())
|
||||
.Produces<CreateAircraftResponseDto>()
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.WithOpenApi(operation => new OpenApiOperation(operation) { Summary = "Create Aircraft", Description = "Create Aircraft" })
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private async Task<IResult> CreateAircraft(CreateAircraftRequestDto request, IMediator mediator, IMapper mapper,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var command = mapper.Map<CreateAircraft>(request);
|
||||
|
||||
var result = await mediator.Send(command, cancellationToken);
|
||||
|
||||
var response = new CreateAircraftResponseDto(result.Id);
|
||||
|
||||
return Results.Ok(response);
|
||||
}
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
namespace Flight.Airports.Features.CreatingAirport.V1;
|
||||
|
||||
using System;
|
||||
using BuildingBlocks.Core.Event;
|
||||
|
||||
public record AirportCreatedDomainEvent(Guid Id, string Name, string Address, string Code, bool IsDeleted) : IDomainEvent;
|
||||
@ -6,11 +6,16 @@ using System.Threading.Tasks;
|
||||
using Ardalis.GuardClauses;
|
||||
using BuildingBlocks.Core.CQRS;
|
||||
using BuildingBlocks.Core.Event;
|
||||
using BuildingBlocks.Web;
|
||||
using Exceptions;
|
||||
using Data;
|
||||
using FluentValidation;
|
||||
using MapsterMapper;
|
||||
using MassTransit;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
public record CreateAirport(string Name, string Address, string Code) : ICommand<CreateAirportResult>, IInternalCommand
|
||||
@ -20,6 +25,43 @@ public record CreateAirport(string Name, string Address, string Code) : ICommand
|
||||
|
||||
public record CreateAirportResult(Guid Id);
|
||||
|
||||
public record AirportCreatedDomainEvent
|
||||
(Guid Id, string Name, string Address, string Code, bool IsDeleted) : IDomainEvent;
|
||||
|
||||
public record CreateAirportRequestDto(string Name, string Address, string Code);
|
||||
|
||||
public record CreateAirportResponseDto(Guid Id);
|
||||
|
||||
public class CreateAirportEndpoint : IMinimalEndpoint
|
||||
{
|
||||
public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapPost($"{EndpointConfig.BaseApiPath}/flight/airport", async (CreateAirportRequestDto request,
|
||||
IMediator mediator, IMapper mapper,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
var command = mapper.Map<CreateAirport>(request);
|
||||
|
||||
var result = await mediator.Send(command, cancellationToken);
|
||||
|
||||
var response = new CreateAirportResponseDto(result.Id);
|
||||
|
||||
return Results.Ok(response);
|
||||
})
|
||||
.RequireAuthorization()
|
||||
.WithName("CreateAirport")
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Flight").Build())
|
||||
.Produces<CreateAirportResponseDto>()
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.WithSummary("Create Airport")
|
||||
.WithDescription("Create Airport")
|
||||
.WithOpenApi()
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
internal class CreateAirportValidator : AbstractValidator<CreateAirport>
|
||||
{
|
||||
public CreateAirportValidator()
|
||||
@ -30,7 +72,6 @@ internal class CreateAirportValidator : AbstractValidator<CreateAirport>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal class CreateAirportHandler : IRequestHandler<CreateAirport, CreateAirportResult>
|
||||
{
|
||||
private readonly FlightDbContext _flightDbContext;
|
||||
@ -44,7 +85,8 @@ internal class CreateAirportHandler : IRequestHandler<CreateAirport, CreateAirpo
|
||||
{
|
||||
Guard.Against.Null(request, nameof(request));
|
||||
|
||||
var airport = await _flightDbContext.Airports.SingleOrDefaultAsync(x => x.Code == request.Code, cancellationToken);
|
||||
var airport =
|
||||
await _flightDbContext.Airports.SingleOrDefaultAsync(x => x.Code == request.Code, cancellationToken);
|
||||
|
||||
if (airport is not null)
|
||||
{
|
||||
|
||||
@ -1,44 +0,0 @@
|
||||
namespace Flight.Airports.Features.CreatingAirport.V1;
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.Web;
|
||||
using MapsterMapper;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.OpenApi.Models;
|
||||
|
||||
public record CreateAirportRequestDto(string Name, string Address, string Code);
|
||||
public record CreateAirportResponseDto(Guid Id);
|
||||
|
||||
public class CreateAirportEndpoint : IMinimalEndpoint
|
||||
{
|
||||
public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapPost($"{EndpointConfig.BaseApiPath}/flight/airport", CreateAirport)
|
||||
.RequireAuthorization()
|
||||
.WithName("CreateAirport")
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Flight").Build())
|
||||
.Produces<CreateAirportResponseDto>()
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.WithOpenApi(operation => new OpenApiOperation(operation) { Summary = "Create Airport", Description = "Create Airport" })
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private async Task<IResult> CreateAirport(CreateAirportRequestDto request, IMediator mediator, IMapper mapper,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var command = mapper.Map<CreateAirport>(request);
|
||||
|
||||
var result = await mediator.Send(command, cancellationToken);
|
||||
|
||||
var response = new CreateAirportResponseDto(result.Id);
|
||||
|
||||
return Results.Ok(response);
|
||||
}
|
||||
}
|
||||
@ -6,10 +6,16 @@ using System.Threading.Tasks;
|
||||
using Ardalis.GuardClauses;
|
||||
using BuildingBlocks.Core.CQRS;
|
||||
using BuildingBlocks.Core.Event;
|
||||
using BuildingBlocks.Web;
|
||||
using Data;
|
||||
using Exceptions;
|
||||
using FluentValidation;
|
||||
using MapsterMapper;
|
||||
using MassTransit;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
public record CreateFlight(string FlightNumber, Guid AircraftId, Guid DepartureAirportId,
|
||||
@ -22,6 +28,46 @@ public record CreateFlight(string FlightNumber, Guid AircraftId, Guid DepartureA
|
||||
|
||||
public record CreateFlightResult(Guid Id);
|
||||
|
||||
public record FlightCreatedDomainEvent(Guid Id, string FlightNumber, Guid AircraftId, DateTime DepartureDate,
|
||||
Guid DepartureAirportId, DateTime ArriveDate, Guid ArriveAirportId, decimal DurationMinutes,
|
||||
DateTime FlightDate, Enums.FlightStatus Status, decimal Price, bool IsDeleted) : IDomainEvent;
|
||||
|
||||
public record CreateFlightRequestDto(string FlightNumber, Guid AircraftId, Guid DepartureAirportId,
|
||||
DateTime DepartureDate, DateTime ArriveDate, Guid ArriveAirportId,
|
||||
decimal DurationMinutes, DateTime FlightDate, Enums.FlightStatus Status, decimal Price);
|
||||
|
||||
public record CreateFlightResponseDto(Guid Id);
|
||||
|
||||
public class CreateFlightEndpoint : IMinimalEndpoint
|
||||
{
|
||||
public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapPost($"{EndpointConfig.BaseApiPath}/flight", async (CreateFlightRequestDto request,
|
||||
IMediator mediator, IMapper mapper,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
var command = mapper.Map<CreateFlight>(request);
|
||||
|
||||
var result = await mediator.Send(command, cancellationToken);
|
||||
|
||||
var response = new CreateFlightResponseDto(result.Id);
|
||||
|
||||
return Results.CreatedAtRoute("GetFlightById", new { id = result.Id }, response);
|
||||
})
|
||||
.RequireAuthorization()
|
||||
.WithName("CreateFlight")
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Flight").Build())
|
||||
.Produces<CreateFlightResponseDto>(StatusCodes.Status201Created)
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.WithSummary("Create Flight")
|
||||
.WithDescription("Create Flight")
|
||||
.WithOpenApi()
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
internal class CreateFlightValidator : AbstractValidator<CreateFlight>
|
||||
{
|
||||
public CreateFlightValidator()
|
||||
|
||||
@ -1,47 +0,0 @@
|
||||
namespace Flight.Flights.Features.CreatingFlight.V1;
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.Web;
|
||||
using MapsterMapper;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.OpenApi.Models;
|
||||
|
||||
public record CreateFlightRequestDto(string FlightNumber, Guid AircraftId, Guid DepartureAirportId,
|
||||
DateTime DepartureDate, DateTime ArriveDate, Guid ArriveAirportId,
|
||||
decimal DurationMinutes, DateTime FlightDate, Enums.FlightStatus Status, decimal Price);
|
||||
|
||||
public record CreateFlightResponseDto(Guid Id);
|
||||
|
||||
public class CreateFlightEndpoint : IMinimalEndpoint
|
||||
{
|
||||
public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapPost($"{EndpointConfig.BaseApiPath}/flight", CreateFlight)
|
||||
.RequireAuthorization()
|
||||
.WithName("CreateFlight")
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Flight").Build())
|
||||
.Produces<CreateFlightResponseDto>(StatusCodes.Status201Created)
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.WithOpenApi(operation => new OpenApiOperation(operation) { Summary = "Create Flight", Description = "Create Flight" })
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private async Task<IResult> CreateFlight(CreateFlightRequestDto request, IMediator mediator, IMapper mapper,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var command = mapper.Map<CreateFlight>(request);
|
||||
|
||||
var result = await mediator.Send(command, cancellationToken);
|
||||
|
||||
var response = new CreateFlightResponseDto(result.Id);
|
||||
|
||||
return Results.CreatedAtRoute("GetFlightById", new { id = result.Id }, response);
|
||||
}
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
namespace Flight.Flights.Features.CreatingFlight.V1;
|
||||
|
||||
using System;
|
||||
using BuildingBlocks.Core.Event;
|
||||
|
||||
public record FlightCreatedDomainEvent(Guid Id, string FlightNumber, Guid AircraftId, DateTime DepartureDate,
|
||||
Guid DepartureAirportId, DateTime ArriveDate, Guid ArriveAirportId, decimal DurationMinutes,
|
||||
DateTime FlightDate, Enums.FlightStatus Status, decimal Price, bool IsDeleted) : IDomainEvent;
|
||||
@ -6,15 +6,49 @@ using System.Threading.Tasks;
|
||||
using Ardalis.GuardClauses;
|
||||
using BuildingBlocks.Core.CQRS;
|
||||
using BuildingBlocks.Core.Event;
|
||||
using BuildingBlocks.Web;
|
||||
using Data;
|
||||
using Exceptions;
|
||||
using FluentValidation;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
public record DeleteFlight(Guid Id) : ICommand<DeleteFlightResult>, IInternalCommand;
|
||||
|
||||
public record DeleteFlightResult(Guid Id);
|
||||
|
||||
public record FlightDeletedDomainEvent(Guid Id, string FlightNumber, Guid AircraftId, DateTime DepartureDate,
|
||||
Guid DepartureAirportId, DateTime ArriveDate, Guid ArriveAirportId, decimal DurationMinutes,
|
||||
DateTime FlightDate, Enums.FlightStatus Status, decimal Price, bool IsDeleted) : IDomainEvent;
|
||||
|
||||
public class DeleteFlightEndpoint : IMinimalEndpoint
|
||||
{
|
||||
public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapDelete($"{EndpointConfig.BaseApiPath}/flight/{{id}}",
|
||||
async (Guid id, IMediator mediator, CancellationToken cancellationToken) =>
|
||||
{
|
||||
var result = await mediator.Send(new DeleteFlight(id), cancellationToken);
|
||||
|
||||
return Results.NoContent();
|
||||
})
|
||||
.RequireAuthorization()
|
||||
.WithName("DeleteFlight")
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Flight").Build())
|
||||
.Produces(StatusCodes.Status204NoContent)
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.WithSummary("Delete Flight")
|
||||
.WithDescription("Delete Flight")
|
||||
.WithOpenApi()
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
internal class DeleteFlightValidator : AbstractValidator<DeleteFlight>
|
||||
{
|
||||
public DeleteFlightValidator()
|
||||
|
||||
@ -1,35 +0,0 @@
|
||||
namespace Flight.Flights.Features.DeletingFlight.V1;
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.Web;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.OpenApi.Models;
|
||||
|
||||
public class DeleteFlightEndpoint : IMinimalEndpoint
|
||||
{
|
||||
public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapDelete($"{EndpointConfig.BaseApiPath}/flight/{{id}}", DeleteFlight)
|
||||
.RequireAuthorization()
|
||||
.WithName("DeleteFlight")
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Flight").Build())
|
||||
.Produces(StatusCodes.Status204NoContent)
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.WithOpenApi(operation => new OpenApiOperation(operation) { Summary = "Delete Flight", Description = "Delete Flight" })
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private async Task<IResult> DeleteFlight(Guid id, IMediator mediator, CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new DeleteFlight(id), cancellationToken);
|
||||
|
||||
return Results.NoContent();
|
||||
}
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
namespace Flight.Flights.Features.DeletingFlight.V1;
|
||||
|
||||
using System;
|
||||
using BuildingBlocks.Core.Event;
|
||||
|
||||
public record FlightDeletedDomainEvent(Guid Id, string FlightNumber, Guid AircraftId, DateTime DepartureDate,
|
||||
Guid DepartureAirportId, DateTime ArriveDate, Guid ArriveAirportId, decimal DurationMinutes,
|
||||
DateTime FlightDate, Enums.FlightStatus Status, decimal Price, bool IsDeleted) : IDomainEvent;
|
||||
@ -8,10 +8,15 @@ using System.Threading.Tasks;
|
||||
using Ardalis.GuardClauses;
|
||||
using BuildingBlocks.Caching;
|
||||
using BuildingBlocks.Core.CQRS;
|
||||
using BuildingBlocks.Web;
|
||||
using Data;
|
||||
using Dtos;
|
||||
using Exceptions;
|
||||
using MapsterMapper;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using MongoDB.Driver;
|
||||
|
||||
public record GetAvailableFlights : IQuery<GetAvailableFlightsResult>, ICacheRequest
|
||||
@ -22,6 +27,36 @@ public record GetAvailableFlights : IQuery<GetAvailableFlightsResult>, ICacheReq
|
||||
|
||||
public record GetAvailableFlightsResult(IEnumerable<FlightDto> FlightDtos);
|
||||
|
||||
public record GetAvailableFlightsResponseDto(IEnumerable<FlightDto> FlightDtos);
|
||||
|
||||
public class GetAvailableFlightsEndpoint : IMinimalEndpoint
|
||||
{
|
||||
public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapGet($"{EndpointConfig.BaseApiPath}/flight/get-available-flights",
|
||||
async (IMediator mediator, CancellationToken cancellationToken) =>
|
||||
{
|
||||
var result = await mediator.Send(new GetAvailableFlights(), cancellationToken);
|
||||
|
||||
var response = new GetAvailableFlightsResponseDto(result?.FlightDtos);
|
||||
|
||||
return Results.Ok(response);
|
||||
})
|
||||
.WithOpenApi()
|
||||
.RequireAuthorization()
|
||||
.WithName("GetAvailableFlights")
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Flight").Build())
|
||||
.Produces<GetAvailableFlightsResponseDto>()
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.WithSummary("Get Available Flights")
|
||||
.WithDescription("Get Available Flights")
|
||||
.WithOpenApi()
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
internal class GetAvailableFlightsHandler : IQueryHandler<GetAvailableFlights, GetAvailableFlightsResult>
|
||||
{
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
@ -1,40 +0,0 @@
|
||||
namespace Flight.Flights.Features.GettingAvailableFlights.V1;
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.Web;
|
||||
using Dtos;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.OpenApi.Models;
|
||||
|
||||
public record GetAvailableFlightsResponseDto(IEnumerable<FlightDto> FlightDtos);
|
||||
|
||||
public class GetAvailableFlightsEndpoint : IMinimalEndpoint
|
||||
{
|
||||
public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapGet($"{EndpointConfig.BaseApiPath}/flight/get-available-flights", GetAvailableFlights)
|
||||
.RequireAuthorization()
|
||||
.WithName("GetAvailableFlights")
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Flight").Build())
|
||||
.Produces<GetAvailableFlightsResponseDto>()
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.WithOpenApi(operation => new OpenApiOperation(operation) { Summary = "Get Available Flights", Description = "Get Available Flights" })
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private async Task<IResult> GetAvailableFlights(IMediator mediator, CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetAvailableFlights(), cancellationToken);
|
||||
|
||||
var response = new GetAvailableFlightsResponseDto(result?.FlightDtos);
|
||||
|
||||
return Results.Ok(response);
|
||||
}
|
||||
}
|
||||
@ -5,11 +5,16 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Ardalis.GuardClauses;
|
||||
using BuildingBlocks.Core.CQRS;
|
||||
using BuildingBlocks.Web;
|
||||
using Data;
|
||||
using Dtos;
|
||||
using Exceptions;
|
||||
using FluentValidation;
|
||||
using MapsterMapper;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using MongoDB.Driver;
|
||||
using MongoDB.Driver.Linq;
|
||||
|
||||
@ -17,6 +22,35 @@ public record GetFlightById(Guid Id) : IQuery<GetFlightByIdResult>;
|
||||
|
||||
public record GetFlightByIdResult(FlightDto FlightDto);
|
||||
|
||||
public record GetFlightByIdResponseDto(FlightDto FlightDto);
|
||||
|
||||
public class GetFlightByIdEndpoint : IMinimalEndpoint
|
||||
{
|
||||
public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapGet($"{EndpointConfig.BaseApiPath}/flight/{{id}}",
|
||||
async (Guid id, IMediator mediator, CancellationToken cancellationToken) =>
|
||||
{
|
||||
var result = await mediator.Send(new GetFlightById(id), cancellationToken);
|
||||
|
||||
var response = new GetFlightByIdResponseDto(result?.FlightDto);
|
||||
|
||||
return Results.Ok(response);
|
||||
})
|
||||
.RequireAuthorization()
|
||||
.WithName("GetFlightById")
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Flight").Build())
|
||||
.Produces<GetFlightByIdResponseDto>()
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.WithSummary("Get Flight By Id")
|
||||
.WithDescription("Get Flight By Id")
|
||||
.WithOpenApi()
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
public class GetFlightByIdValidator : AbstractValidator<GetFlightById>
|
||||
{
|
||||
public GetFlightByIdValidator()
|
||||
|
||||
@ -1,40 +0,0 @@
|
||||
namespace Flight.Flights.Features.GettingFlightById.V1;
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.Web;
|
||||
using Dtos;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.OpenApi.Models;
|
||||
|
||||
public record GetFlightByIdResponseDto(FlightDto FlightDto);
|
||||
|
||||
public class GetFlightByIdEndpoint : IMinimalEndpoint
|
||||
{
|
||||
public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapGet($"{EndpointConfig.BaseApiPath}/flight/{{id}}", GetById)
|
||||
.RequireAuthorization()
|
||||
.WithName("GetFlightById")
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Flight").Build())
|
||||
.Produces<GetFlightByIdResponseDto>()
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.WithOpenApi(operation => new OpenApiOperation(operation) { Summary = "Get Flight By Id", Description = "Get Flight By Id" })
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private async Task<IResult> GetById(Guid id, IMediator mediator, CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetFlightById(id), cancellationToken);
|
||||
|
||||
var response = new GetFlightByIdResponseDto(result?.FlightDto);
|
||||
|
||||
return Results.Ok(response);
|
||||
}
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
namespace Flight.Flights.Features.UpdatingFlight.V1;
|
||||
|
||||
using System;
|
||||
using BuildingBlocks.Core.Event;
|
||||
|
||||
public record FlightUpdatedDomainEvent(Guid Id, string FlightNumber, Guid AircraftId, DateTime DepartureDate,
|
||||
Guid DepartureAirportId, DateTime ArriveDate, Guid ArriveAirportId, decimal DurationMinutes,
|
||||
DateTime FlightDate, Enums.FlightStatus Status, decimal Price, bool IsDeleted) : IDomainEvent;
|
||||
@ -7,10 +7,16 @@ using Ardalis.GuardClauses;
|
||||
using BuildingBlocks.Caching;
|
||||
using BuildingBlocks.Core.CQRS;
|
||||
using BuildingBlocks.Core.Event;
|
||||
using BuildingBlocks.Web;
|
||||
using Data;
|
||||
using Exceptions;
|
||||
using Flight.Flights.Features.CreatingFlight.V1;
|
||||
using FluentValidation;
|
||||
using MapsterMapper;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
public record UpdateFlight(Guid Id, string FlightNumber, Guid AircraftId, Guid DepartureAirportId,
|
||||
@ -23,6 +29,43 @@ public record UpdateFlight(Guid Id, string FlightNumber, Guid AircraftId, Guid D
|
||||
|
||||
public record UpdateFlightResult(Guid Id);
|
||||
|
||||
public record FlightUpdatedDomainEvent(Guid Id, string FlightNumber, Guid AircraftId, DateTime DepartureDate,
|
||||
Guid DepartureAirportId, DateTime ArriveDate, Guid ArriveAirportId, decimal DurationMinutes,
|
||||
DateTime FlightDate, Enums.FlightStatus Status, decimal Price, bool IsDeleted) : IDomainEvent;
|
||||
|
||||
public record UpdateFlightRequestDto(Guid Id, string FlightNumber, Guid AircraftId, Guid DepartureAirportId,
|
||||
DateTime DepartureDate, DateTime ArriveDate,
|
||||
Guid ArriveAirportId, decimal DurationMinutes, DateTime FlightDate, Enums.FlightStatus Status, decimal Price,
|
||||
bool IsDeleted);
|
||||
|
||||
public class UpdateFlightEndpoint : IMinimalEndpoint
|
||||
{
|
||||
public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapPut($"{EndpointConfig.BaseApiPath}/flight", async (UpdateFlightRequestDto request,
|
||||
IMediator mediator,
|
||||
IMapper mapper, CancellationToken cancellationToken) =>
|
||||
{
|
||||
var command = mapper.Map<UpdateFlight>(request);
|
||||
|
||||
await mediator.Send(command, cancellationToken);
|
||||
|
||||
return Results.NoContent();
|
||||
})
|
||||
.RequireAuthorization()
|
||||
.WithName("UpdateFlight")
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Flight").Build())
|
||||
.Produces(StatusCodes.Status204NoContent)
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.WithSummary("Update Flight")
|
||||
.WithDescription("Update Flight")
|
||||
.WithOpenApi()
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
internal class UpdateFlightValidator : AbstractValidator<CreateFlight>
|
||||
{
|
||||
public UpdateFlightValidator()
|
||||
|
||||
@ -1,41 +0,0 @@
|
||||
namespace Flight.Flights.Features.UpdatingFlight.V1;
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.Web;
|
||||
using MapsterMapper;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.OpenApi.Models;
|
||||
|
||||
public record UpdateFlightRequestDto(Guid Id, string FlightNumber, Guid AircraftId, Guid DepartureAirportId, DateTime DepartureDate, DateTime ArriveDate,
|
||||
Guid ArriveAirportId, decimal DurationMinutes, DateTime FlightDate, Enums.FlightStatus Status, decimal Price, bool IsDeleted);
|
||||
|
||||
public class UpdateFlightEndpoint : IMinimalEndpoint
|
||||
{
|
||||
public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapPut($"{EndpointConfig.BaseApiPath}/flight", UpdateFlight)
|
||||
.RequireAuthorization()
|
||||
.WithName("UpdateFlight")
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Flight").Build())
|
||||
.Produces(StatusCodes.Status204NoContent)
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.WithOpenApi(operation => new OpenApiOperation(operation) { Summary = "Update Flight", Description = "Update Flight" })
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private async Task<IResult> UpdateFlight(UpdateFlightRequestDto request, IMediator mediator, IMapper mapper, CancellationToken cancellationToken)
|
||||
{
|
||||
var command = mapper.Map<UpdateFlight>(request);
|
||||
|
||||
await mediator.Send(command, cancellationToken);
|
||||
|
||||
return Results.NoContent();
|
||||
}
|
||||
}
|
||||
@ -6,21 +6,66 @@ using System.Threading.Tasks;
|
||||
using Ardalis.GuardClauses;
|
||||
using BuildingBlocks.Core.CQRS;
|
||||
using BuildingBlocks.Core.Event;
|
||||
using BuildingBlocks.Web;
|
||||
using Flight.Data;
|
||||
using Flight.Seats.Exceptions;
|
||||
using Flight.Seats.Models;
|
||||
using FluentValidation;
|
||||
using MapsterMapper;
|
||||
using MassTransit;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
public record CreateSeat(string SeatNumber, Enums.SeatType Type, Enums.SeatClass Class, Guid FlightId) : ICommand<CreateSeatResult>, IInternalCommand
|
||||
public record CreateSeat
|
||||
(string SeatNumber, Enums.SeatType Type, Enums.SeatClass Class, Guid FlightId) : ICommand<CreateSeatResult>,
|
||||
IInternalCommand
|
||||
{
|
||||
public Guid Id { get; init; } = NewId.NextGuid();
|
||||
}
|
||||
|
||||
public record CreateSeatResult(Guid Id);
|
||||
|
||||
public record SeatCreatedDomainEvent(Guid Id, string SeatNumber, Enums.SeatType Type, Enums.SeatClass Class,
|
||||
Guid FlightId, bool IsDeleted) : IDomainEvent;
|
||||
|
||||
public record CreateSeatRequestDto(string SeatNumber, Enums.SeatType Type, Enums.SeatClass Class, Guid FlightId);
|
||||
|
||||
public record CreateSeatResponseDto(Guid Id);
|
||||
|
||||
public class CreateSeatEndpoint : IMinimalEndpoint
|
||||
{
|
||||
public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapPost($"{EndpointConfig.BaseApiPath}/flight/seat", CreateSeat)
|
||||
.RequireAuthorization()
|
||||
.WithName("CreateSeat")
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Flight").Build())
|
||||
.Produces<CreateSeatResponseDto>()
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.WithSummary("Create Seat")
|
||||
.WithDescription("Create Seat")
|
||||
.WithOpenApi()
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private async Task<IResult> CreateSeat(CreateSeatRequestDto request, IMediator mediator, IMapper mapper,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var command = mapper.Map<CreateSeat>(request);
|
||||
|
||||
var result = await mediator.Send(command, cancellationToken);
|
||||
|
||||
var response = new CreateSeatResponseDto(result.Id);
|
||||
|
||||
return Results.Ok(response);
|
||||
}
|
||||
}
|
||||
|
||||
internal class CreateSeatValidator : AbstractValidator<CreateSeat>
|
||||
{
|
||||
public CreateSeatValidator()
|
||||
@ -62,4 +107,3 @@ internal class CreateSeatCommandHandler : IRequestHandler<CreateSeat, CreateSeat
|
||||
return new CreateSeatResult(newSeat.Id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,44 +0,0 @@
|
||||
namespace Flight.Seats.Features.CreatingSeat.V1;
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.Web;
|
||||
using MapsterMapper;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.OpenApi.Models;
|
||||
|
||||
public record CreateSeatRequestDto(string SeatNumber, Enums.SeatType Type, Enums.SeatClass Class, Guid FlightId);
|
||||
public record CreateSeatResponseDto(Guid Id);
|
||||
|
||||
public class CreateSeatEndpoint : IMinimalEndpoint
|
||||
{
|
||||
public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapPost($"{EndpointConfig.BaseApiPath}/flight/seat", CreateSeat)
|
||||
.RequireAuthorization()
|
||||
.WithName("CreateSeat")
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Flight").Build())
|
||||
.Produces<CreateSeatResponseDto>()
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.WithOpenApi(operation => new OpenApiOperation(operation) { Summary = "Create Seat", Description = "Create Seat" })
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private async Task<IResult> CreateSeat(CreateSeatRequestDto request, IMediator mediator, IMapper mapper,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var command = mapper.Map<CreateSeat>(request);
|
||||
|
||||
var result = await mediator.Send(command, cancellationToken);
|
||||
|
||||
var response = new CreateSeatResponseDto(result.Id);
|
||||
|
||||
return Results.Ok(response);
|
||||
}
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
namespace Flight.Seats.Features.CreatingSeat.V1;
|
||||
|
||||
using System;
|
||||
using BuildingBlocks.Core.Event;
|
||||
|
||||
public record SeatCreatedDomainEvent(Guid Id, string SeatNumber, Enums.SeatType Type, Enums.SeatClass Class, Guid FlightId, bool IsDeleted) : IDomainEvent;
|
||||
@ -7,18 +7,52 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Ardalis.GuardClauses;
|
||||
using BuildingBlocks.Core.CQRS;
|
||||
using BuildingBlocks.Web;
|
||||
using Data;
|
||||
using Dtos;
|
||||
using Exceptions;
|
||||
using FluentValidation;
|
||||
using MapsterMapper;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using MongoDB.Driver;
|
||||
|
||||
public record GetAvailableSeats(Guid FlightId) : IQuery<GetAvailableSeatsResult>;
|
||||
|
||||
public record GetAvailableSeatsResult(IEnumerable<SeatDto> SeatDtos);
|
||||
|
||||
public record GetAvailableSeatsResponseDto(IEnumerable<SeatDto> SeatDtos);
|
||||
|
||||
public class GetAvailableSeatsEndpoint : IMinimalEndpoint
|
||||
{
|
||||
public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapGet($"{EndpointConfig.BaseApiPath}/flight/get-available-seats/{{id}}", GetAvailableSeats)
|
||||
.RequireAuthorization()
|
||||
.WithName("GetAvailableSeats")
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Flight").Build())
|
||||
.Produces<GetAvailableSeatsResponseDto>()
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.WithSummary("Get Available Seats")
|
||||
.WithDescription("Get Available Seats")
|
||||
.WithOpenApi()
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private async Task<IResult> GetAvailableSeats(Guid id, IMediator mediator, CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetAvailableSeats(id), cancellationToken);
|
||||
|
||||
var response = new GetAvailableSeatsResponseDto(result?.SeatDtos);
|
||||
|
||||
return Results.Ok(response);
|
||||
}
|
||||
}
|
||||
|
||||
internal class GetAvailableSeatsValidator : AbstractValidator<GetAvailableSeats>
|
||||
{
|
||||
public GetAvailableSeatsValidator()
|
||||
|
||||
@ -1,41 +0,0 @@
|
||||
namespace Flight.Seats.Features.GettingAvailableSeats.V1;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.Web;
|
||||
using Dtos;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.OpenApi.Models;
|
||||
|
||||
public record GetAvailableSeatsResponseDto(IEnumerable<SeatDto> SeatDtos);
|
||||
|
||||
public class GetAvailableSeatsEndpoint : IMinimalEndpoint
|
||||
{
|
||||
public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapGet($"{EndpointConfig.BaseApiPath}/flight/get-available-seats/{{id}}", GetAvailableSeats)
|
||||
.RequireAuthorization()
|
||||
.WithName("GetAvailableSeats")
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Flight").Build())
|
||||
.Produces<GetAvailableSeatsResponseDto>()
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.WithOpenApi(operation => new OpenApiOperation(operation) { Summary = "Get Available Seats", Description = "Get Available Seats" })
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private async Task<IResult> GetAvailableSeats(Guid id, IMediator mediator, CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetAvailableSeats(id), cancellationToken);
|
||||
|
||||
var response = new GetAvailableSeatsResponseDto(result?.SeatDtos);
|
||||
|
||||
return Results.Ok(response);
|
||||
}
|
||||
}
|
||||
@ -6,16 +6,59 @@ using System.Threading.Tasks;
|
||||
using Ardalis.GuardClauses;
|
||||
using BuildingBlocks.Core.CQRS;
|
||||
using BuildingBlocks.Core.Event;
|
||||
using BuildingBlocks.Web;
|
||||
using Data;
|
||||
using Exceptions;
|
||||
using FluentValidation;
|
||||
using MapsterMapper;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
public record ReserveSeat(Guid FlightId, string SeatNumber) : ICommand<ReserveSeatResult>, IInternalCommand;
|
||||
|
||||
public record ReserveSeatResult(Guid Id);
|
||||
|
||||
public record SeatReservedDomainEvent(Guid Id, string SeatNumber, Enums.SeatType Type, Enums.SeatClass Class,
|
||||
Guid FlightId, bool IsDeleted) : IDomainEvent;
|
||||
|
||||
public record ReserveSeatRequestDto(Guid FlightId, string SeatNumber);
|
||||
|
||||
public record ReserveSeatResponseDto(Guid Id);
|
||||
|
||||
public class ReserveSeatEndpoint : IMinimalEndpoint
|
||||
{
|
||||
public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapPost($"{EndpointConfig.BaseApiPath}/flight/reserve-seat", ReserveSeat)
|
||||
.RequireAuthorization()
|
||||
.WithName("ReserveSeat")
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Flight").Build())
|
||||
.Produces<ReserveSeatResponseDto>()
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.WithSummary("Reserve Seat")
|
||||
.WithDescription("Reserve Seat")
|
||||
.WithOpenApi()
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private async Task<IResult> ReserveSeat(ReserveSeatRequestDto request, IMediator mediator, IMapper mapper,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var command = mapper.Map<V1.ReserveSeat>(request);
|
||||
|
||||
var result = await mediator.Send(command, cancellationToken);
|
||||
|
||||
var response = new ReserveSeatResponseDto(result.Id);
|
||||
|
||||
return Results.Ok(response);
|
||||
}
|
||||
}
|
||||
|
||||
internal class ReserveSeatValidator : AbstractValidator<ReserveSeat>
|
||||
{
|
||||
public ReserveSeatValidator()
|
||||
@ -38,7 +81,8 @@ internal class ReserveSeatCommandHandler : IRequestHandler<ReserveSeat, ReserveS
|
||||
{
|
||||
Guard.Against.Null(command, nameof(command));
|
||||
|
||||
var seat = await _flightDbContext.Seats.SingleOrDefaultAsync(x => x.SeatNumber == command.SeatNumber && x.FlightId == command.FlightId, cancellationToken);
|
||||
var seat = await _flightDbContext.Seats.SingleOrDefaultAsync(
|
||||
x => x.SeatNumber == command.SeatNumber && x.FlightId == command.FlightId, cancellationToken);
|
||||
|
||||
if (seat is null)
|
||||
{
|
||||
|
||||
@ -1,43 +0,0 @@
|
||||
namespace Flight.Seats.Features.ReservingSeat.Commands.V1;
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.Web;
|
||||
using MapsterMapper;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.OpenApi.Models;
|
||||
|
||||
public record ReserveSeatRequestDto(Guid FlightId, string SeatNumber);
|
||||
public record ReserveSeatResponseDto(Guid Id);
|
||||
|
||||
public class ReserveSeatEndpoint : IMinimalEndpoint
|
||||
{
|
||||
public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapPost($"{EndpointConfig.BaseApiPath}/flight/reserve-seat", ReserveSeat)
|
||||
.RequireAuthorization()
|
||||
.WithName("ReserveSeat")
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Flight").Build())
|
||||
.Produces<ReserveSeatResponseDto>()
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.WithOpenApi(operation => new OpenApiOperation(operation) { Summary = "Reserve Seat", Description = "Reserve Seat" })
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private async Task<IResult> ReserveSeat(ReserveSeatRequestDto request, IMediator mediator, IMapper mapper, CancellationToken cancellationToken)
|
||||
{
|
||||
var command = mapper.Map<V1.ReserveSeat>(request);
|
||||
|
||||
var result = await mediator.Send(command, cancellationToken);
|
||||
|
||||
var response = new ReserveSeatResponseDto(result.Id);
|
||||
|
||||
return Results.Ok(response);
|
||||
}
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
namespace Flight.Seats.Features.ReservingSeat.Commands.V1;
|
||||
|
||||
using System;
|
||||
using BuildingBlocks.Core.Event;
|
||||
|
||||
public record SeatReservedDomainEvent(Guid Id, string SeatNumber, Enums.SeatType Type, Enums.SeatClass Class, Guid FlightId, bool IsDeleted) : IDomainEvent;
|
||||
@ -0,0 +1,23 @@
|
||||
# OpenAPI Generator Ignore
|
||||
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
|
||||
|
||||
# Use this file to prevent files from being overwritten by the generator.
|
||||
# The patterns follow closely to .gitignore or .dockerignore.
|
||||
|
||||
# As an example, the C# client generator defines ApiClient.cs.
|
||||
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
|
||||
#ApiClient.cs
|
||||
|
||||
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
|
||||
#foo/*/qux
|
||||
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
|
||||
|
||||
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
|
||||
#foo/**/qux
|
||||
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
|
||||
|
||||
# You can also negate patterns with an exclamation (!).
|
||||
# For example, you can ignore all files in a docs folder with the file extension .md:
|
||||
#docs/*.md
|
||||
# Then explicitly reverse the ignore rule for a single file:
|
||||
#!docs/README.md
|
||||
@ -0,0 +1,2 @@
|
||||
README.md
|
||||
script.js
|
||||
@ -0,0 +1 @@
|
||||
6.6.0-SNAPSHOT
|
||||
15
src/Services/Flight/tests/PerformanceTest/README.md
Normal file
15
src/Services/Flight/tests/PerformanceTest/README.md
Normal file
@ -0,0 +1,15 @@
|
||||
# Generated k6 script
|
||||
|
||||
The `script.js` file contains most of the Swagger/OpenAPI specification and you can customize it to your needs.
|
||||
|
||||
Global header variables are defined at the top of the file, like `api_key`. Each path in the specification is converted into a [group](https://docs.k6.io/docs/tags-and-groups) in k6 and each group contains all the request methods related to that path. Path and query parameters are extracted from the specification and put at the start of the group. The URL is constructed from the base URL plus path and query.
|
||||
|
||||
If the Swagger/OpenAPI specification used as the input spec contains examples at parameter level, those will be extracted and utilized as parameter values. The `handleParamValue` custom Mustache lambda registered for use in the K6 `script.mustache` template handles the conditional checks, formatting, and outputting of parameter values. If a given parameter has value specified – either in `example` or `examples` field, defined at the parameter level – that value will be used. For list (`examples`), entire list will be output in the generated script and the first element from that list will be assigned as parameter value. If a given parameter does not have an example defined, a placeholder value with `TODO_EDIT_THE_` prefix will be generated for that parameter, and you will have to assign a value before you can run the script. In other words, you can now generate K6 test scripts which are ready to run, provided the Swagger/OpenAPI specification used as the input spec contains examples for all of the path/query parameters; see `modules/openapi-generator/src/test/resources/3_0/examples.yaml` for an example of such specification, and https://swagger.io/docs/specification/adding-examples/ for more information about adding examples.
|
||||
|
||||
k6 specific parameters are in the [`params`](https://docs.k6.io/docs/params-k6http) object, and `body` contains the [request](https://docs.k6.io/docs/http-requests) body which is in the form of `identifier: type`, which the `type` should be substituted by a proper value. Then goes the request and the check.
|
||||
|
||||
[Check](https://docs.k6.io/docs/checks) are like asserts but differ in that they don't halt execution, instead they just store the result of the check, pass or fail, and let the script execution continue.
|
||||
|
||||
Each request is always followed by a 0.1 second [sleep](https://docs.k6.io/docs/sleep-t-1) to prevent the script execution from flooding the system with too many requests simultaneously.
|
||||
|
||||
Note that the default iteration count and VU count is 1. So each request in each group will be executed once. For more information, see the [k6 options](https://docs.k6.io/docs/options).
|
||||
173
src/Services/Flight/tests/PerformanceTest/script.js
Normal file
173
src/Services/Flight/tests/PerformanceTest/script.js
Normal file
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* APIs
|
||||
* An example application with OpenAPI, Swashbuckle, and API versioning.
|
||||
*
|
||||
* OpenAPI spec version: 1.0
|
||||
* Contact:
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator.
|
||||
* https://github.com/OpenAPITools/openapi-generator
|
||||
*
|
||||
* OpenAPI generator version: 6.6.0-SNAPSHOT
|
||||
*/
|
||||
|
||||
|
||||
import http from "k6/http";
|
||||
import { group, check, sleep } from "k6";
|
||||
|
||||
const BASE_URL = "/";
|
||||
// Sleep duration between successive requests.
|
||||
// You might want to edit the value of this variable or remove calls to the sleep function on the script.
|
||||
const SLEEP_DURATION = 0.1;
|
||||
// Global variables should be initialized.
|
||||
|
||||
export default function() {
|
||||
group("/api/v1/flight/seat", () => {
|
||||
|
||||
// Request No. 1: CreateSeat
|
||||
{
|
||||
let url = BASE_URL + `/api/v1/flight/seat`;
|
||||
// TODO: edit the parameters of the request body.
|
||||
let body = {"seatNumber": "string", "type": "seattype", "class": "seatclass", "flightId": "uuid"};
|
||||
let params = {headers: {"Content-Type": "application/json", "Accept": "application/json"}};
|
||||
let request = http.post(url, JSON.stringify(body), params);
|
||||
|
||||
check(request, {
|
||||
"Success": (r) => r.status === 200
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
group("/api/v1/flight/get-available-flights", () => {
|
||||
|
||||
// Request No. 1: GetAvailableFlights
|
||||
{
|
||||
let url = BASE_URL + `/api/v1/flight/get-available-flights`;
|
||||
let request = http.get(url);
|
||||
|
||||
check(request, {
|
||||
"Success": (r) => r.status === 200
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
group("/api/v1/flight/airport", () => {
|
||||
|
||||
// Request No. 1: CreateAirport
|
||||
{
|
||||
let url = BASE_URL + `/api/v1/flight/airport`;
|
||||
// TODO: edit the parameters of the request body.
|
||||
let body = {"name": "string", "address": "string", "code": "string"};
|
||||
let params = {headers: {"Content-Type": "application/json", "Accept": "application/json"}};
|
||||
let request = http.post(url, JSON.stringify(body), params);
|
||||
|
||||
check(request, {
|
||||
"Success": (r) => r.status === 200
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
group("/api/v1/flight/get-available-seats/{id}", () => {
|
||||
let id = 'TODO_EDIT_THE_ID'; // specify value as there is no example value for this parameter in OpenAPI spec
|
||||
|
||||
// Request No. 1: GetAvailableSeats
|
||||
{
|
||||
let url = BASE_URL + `/api/v1/flight/get-available-seats/${id}`;
|
||||
let request = http.get(url);
|
||||
|
||||
check(request, {
|
||||
"Success": (r) => r.status === 200
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
group("/api/v1/flight", () => {
|
||||
|
||||
// Request No. 1: UpdateFlight
|
||||
{
|
||||
let url = BASE_URL + `/api/v1/flight`;
|
||||
// TODO: edit the parameters of the request body.
|
||||
let body = {"id": "uuid", "flightNumber": "string", "aircraftId": "uuid", "departureAirportId": "uuid", "departureDate": "date", "arriveDate": "date", "arriveAirportId": "uuid", "durationMinutes": "double", "flightDate": "date", "status": "flightstatus", "price": "double", "isDeleted": "boolean"};
|
||||
let params = {headers: {"Content-Type": "application/json", "Accept": "application/problem+json"}};
|
||||
let request = http.put(url, JSON.stringify(body), params);
|
||||
|
||||
check(request, {
|
||||
"Success": (r) => r.status === 204
|
||||
});
|
||||
|
||||
sleep(SLEEP_DURATION);
|
||||
}
|
||||
|
||||
// Request No. 2: CreateFlight
|
||||
{
|
||||
let url = BASE_URL + `/api/v1/flight`;
|
||||
// TODO: edit the parameters of the request body.
|
||||
let body = {"flightNumber": "string", "aircraftId": "uuid", "departureAirportId": "uuid", "departureDate": "date", "arriveDate": "date", "arriveAirportId": "uuid", "durationMinutes": "double", "flightDate": "date", "status": "flightstatus", "price": "double"};
|
||||
let params = {headers: {"Content-Type": "application/json", "Accept": "application/json"}};
|
||||
let request = http.post(url, JSON.stringify(body), params);
|
||||
|
||||
check(request, {
|
||||
"Success": (r) => r.status === 201
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
group("/api/v1/flight/{id}", () => {
|
||||
let id = 'TODO_EDIT_THE_ID'; // specify value as there is no example value for this parameter in OpenAPI spec
|
||||
|
||||
// Request No. 1: GetFlightById
|
||||
{
|
||||
let url = BASE_URL + `/api/v1/flight/${id}`;
|
||||
let request = http.get(url);
|
||||
|
||||
check(request, {
|
||||
"Success": (r) => r.status === 200
|
||||
});
|
||||
|
||||
sleep(SLEEP_DURATION);
|
||||
}
|
||||
|
||||
// Request No. 2: DeleteFlight
|
||||
{
|
||||
let url = BASE_URL + `/api/v1/flight/${id}`;
|
||||
let request = http.del(url);
|
||||
|
||||
check(request, {
|
||||
"Success": (r) => r.status === 204
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
group("/api/v1/flight/reserve-seat", () => {
|
||||
|
||||
// Request No. 1: ReserveSeat
|
||||
{
|
||||
let url = BASE_URL + `/api/v1/flight/reserve-seat`;
|
||||
// TODO: edit the parameters of the request body.
|
||||
let body = {"flightId": "uuid", "seatNumber": "string"};
|
||||
let params = {headers: {"Content-Type": "application/json", "Accept": "application/json"}};
|
||||
let request = http.post(url, JSON.stringify(body), params);
|
||||
|
||||
check(request, {
|
||||
"Success": (r) => r.status === 200
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
group("/api/v1/flight/aircraft", () => {
|
||||
|
||||
// Request No. 1: CreateAircraft
|
||||
{
|
||||
let url = BASE_URL + `/api/v1/flight/aircraft`;
|
||||
// TODO: edit the parameters of the request body.
|
||||
let body = {"name": "string", "model": "string", "manufacturingYear": "integer"};
|
||||
let params = {headers: {"Content-Type": "application/json", "Accept": "application/json"}};
|
||||
let request = http.post(url, JSON.stringify(body), params);
|
||||
|
||||
check(request, {
|
||||
"Success": (r) => r.status === 200
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
@ -8,9 +8,15 @@ using Ardalis.GuardClauses;
|
||||
using BuildingBlocks.Contracts.EventBus.Messages;
|
||||
using BuildingBlocks.Core;
|
||||
using BuildingBlocks.Core.CQRS;
|
||||
using BuildingBlocks.Web;
|
||||
using Exceptions;
|
||||
using FluentValidation;
|
||||
using MapsterMapper;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Models;
|
||||
|
||||
public record RegisterNewUser(string FirstName, string LastName, string Username, string Email,
|
||||
@ -18,6 +24,41 @@ public record RegisterNewUser(string FirstName, string LastName, string Username
|
||||
|
||||
public record RegisterNewUserResult(Guid Id, string FirstName, string LastName, string Username, string PassportNumber);
|
||||
|
||||
public record RegisterNewUserRequestDto(string FirstName, string LastName, string Username, string Email,
|
||||
string Password, string ConfirmPassword, string PassportNumber);
|
||||
|
||||
public record RegisterNewUserResponseDto(Guid Id, string FirstName, string LastName, string Username,
|
||||
string PassportNumber);
|
||||
|
||||
public class RegisterNewUserEndpoint : IMinimalEndpoint
|
||||
{
|
||||
public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapPost($"{EndpointConfig.BaseApiPath}/identity/register-user", async (
|
||||
RegisterNewUserRequestDto request, IMediator mediator, IMapper mapper,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
var command = mapper.Map<RegisterNewUser>(request);
|
||||
|
||||
var result = await mediator.Send(command, cancellationToken);
|
||||
|
||||
var response = mapper.Map<RegisterNewUserResponseDto>(result);
|
||||
|
||||
return Results.Ok(response);
|
||||
})
|
||||
.WithName("RegisterUser")
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Identity").Build())
|
||||
.Produces<RegisterNewUserResponseDto>()
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.WithSummary("Register User")
|
||||
.WithDescription("Register User")
|
||||
.WithOpenApi()
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
internal class RegisterNewUserValidator : AbstractValidator<RegisterNewUser>
|
||||
{
|
||||
public RegisterNewUserValidator()
|
||||
@ -81,8 +122,9 @@ internal class RegisterNewUserHandler : ICommandHandler<RegisterNewUser, Registe
|
||||
throw new RegisterIdentityUserException(string.Join(',', roleResult.Errors.Select(e => e.Description)));
|
||||
}
|
||||
|
||||
await _eventDispatcher.SendAsync(new UserCreated(applicationUser.Id, applicationUser.FirstName + " " + applicationUser.LastName,
|
||||
applicationUser.PassPortNumber),cancellationToken: cancellationToken);
|
||||
await _eventDispatcher.SendAsync(new UserCreated(applicationUser.Id,
|
||||
applicationUser.FirstName + " " + applicationUser.LastName,
|
||||
applicationUser.PassPortNumber), cancellationToken: cancellationToken);
|
||||
|
||||
return new RegisterNewUserResult(applicationUser.Id, applicationUser.FirstName, applicationUser.LastName,
|
||||
applicationUser.UserName, applicationUser.PassPortNumber);
|
||||
|
||||
@ -1,45 +0,0 @@
|
||||
namespace Identity.Identity.Features.RegisteringNewUser.V1;
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.Web;
|
||||
using MapsterMapper;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.OpenApi.Models;
|
||||
|
||||
public record RegisterNewUserRequestDto(string FirstName, string LastName, string Username, string Email,
|
||||
string Password, string ConfirmPassword, string PassportNumber);
|
||||
|
||||
public record RegisterNewUserResponseDto(Guid Id, string FirstName, string LastName, string Username, string PassportNumber);
|
||||
|
||||
public class RegisterNewUserEndpoint : IMinimalEndpoint
|
||||
{
|
||||
public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapPost($"{EndpointConfig.BaseApiPath}/identity/register-user", RegisterNewUser)
|
||||
.WithName("RegisterUser")
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Identity").Build())
|
||||
.Produces<RegisterNewUserResponseDto>()
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.WithOpenApi(operation => new OpenApiOperation(operation) { Summary = "Register User", Description = "Register User" })
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private async Task<IResult> RegisterNewUser(RegisterNewUserRequestDto request, IMediator mediator, IMapper mapper,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var command = mapper.Map<RegisterNewUser>(request);
|
||||
|
||||
var result = await mediator.Send(command, cancellationToken);
|
||||
|
||||
var response = mapper.Map<RegisterNewUserResponseDto>(result);
|
||||
|
||||
return Results.Ok(response);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
# OpenAPI Generator Ignore
|
||||
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
|
||||
|
||||
# Use this file to prevent files from being overwritten by the generator.
|
||||
# The patterns follow closely to .gitignore or .dockerignore.
|
||||
|
||||
# As an example, the C# client generator defines ApiClient.cs.
|
||||
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
|
||||
#ApiClient.cs
|
||||
|
||||
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
|
||||
#foo/*/qux
|
||||
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
|
||||
|
||||
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
|
||||
#foo/**/qux
|
||||
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
|
||||
|
||||
# You can also negate patterns with an exclamation (!).
|
||||
# For example, you can ignore all files in a docs folder with the file extension .md:
|
||||
#docs/*.md
|
||||
# Then explicitly reverse the ignore rule for a single file:
|
||||
#!docs/README.md
|
||||
@ -0,0 +1,3 @@
|
||||
.openapi-generator-ignore
|
||||
README.md
|
||||
script.js
|
||||
@ -0,0 +1 @@
|
||||
6.6.0-SNAPSHOT
|
||||
15
src/Services/Identity/tests/PerformanceTest/README.md
Normal file
15
src/Services/Identity/tests/PerformanceTest/README.md
Normal file
@ -0,0 +1,15 @@
|
||||
# Generated k6 script
|
||||
|
||||
The `script.js` file contains most of the Swagger/OpenAPI specification and you can customize it to your needs.
|
||||
|
||||
Global header variables are defined at the top of the file, like `api_key`. Each path in the specification is converted into a [group](https://docs.k6.io/docs/tags-and-groups) in k6 and each group contains all the request methods related to that path. Path and query parameters are extracted from the specification and put at the start of the group. The URL is constructed from the base URL plus path and query.
|
||||
|
||||
If the Swagger/OpenAPI specification used as the input spec contains examples at parameter level, those will be extracted and utilized as parameter values. The `handleParamValue` custom Mustache lambda registered for use in the K6 `script.mustache` template handles the conditional checks, formatting, and outputting of parameter values. If a given parameter has value specified – either in `example` or `examples` field, defined at the parameter level – that value will be used. For list (`examples`), entire list will be output in the generated script and the first element from that list will be assigned as parameter value. If a given parameter does not have an example defined, a placeholder value with `TODO_EDIT_THE_` prefix will be generated for that parameter, and you will have to assign a value before you can run the script. In other words, you can now generate K6 test scripts which are ready to run, provided the Swagger/OpenAPI specification used as the input spec contains examples for all of the path/query parameters; see `modules/openapi-generator/src/test/resources/3_0/examples.yaml` for an example of such specification, and https://swagger.io/docs/specification/adding-examples/ for more information about adding examples.
|
||||
|
||||
k6 specific parameters are in the [`params`](https://docs.k6.io/docs/params-k6http) object, and `body` contains the [request](https://docs.k6.io/docs/http-requests) body which is in the form of `identifier: type`, which the `type` should be substituted by a proper value. Then goes the request and the check.
|
||||
|
||||
[Check](https://docs.k6.io/docs/checks) are like asserts but differ in that they don't halt execution, instead they just store the result of the check, pass or fail, and let the script execution continue.
|
||||
|
||||
Each request is always followed by a 0.1 second [sleep](https://docs.k6.io/docs/sleep-t-1) to prevent the script execution from flooding the system with too many requests simultaneously.
|
||||
|
||||
Note that the default iteration count and VU count is 1. So each request in each group will be executed once. For more information, see the [k6 options](https://docs.k6.io/docs/options).
|
||||
41
src/Services/Identity/tests/PerformanceTest/script.js
Normal file
41
src/Services/Identity/tests/PerformanceTest/script.js
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* APIs
|
||||
* An example application with OpenAPI, Swashbuckle, and API versioning.
|
||||
*
|
||||
* OpenAPI spec version: 1.0
|
||||
* Contact:
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator.
|
||||
* https://github.com/OpenAPITools/openapi-generator
|
||||
*
|
||||
* OpenAPI generator version: 6.6.0-SNAPSHOT
|
||||
*/
|
||||
|
||||
|
||||
import http from "k6/http";
|
||||
import { group, check, sleep } from "k6";
|
||||
|
||||
const BASE_URL = "/";
|
||||
// Sleep duration between successive requests.
|
||||
// You might want to edit the value of this variable or remove calls to the sleep function on the script.
|
||||
const SLEEP_DURATION = 0.1;
|
||||
// Global variables should be initialized.
|
||||
|
||||
export default function() {
|
||||
group("/api/v1/identity/register-user", () => {
|
||||
|
||||
// Request No. 1: RegisterUser
|
||||
{
|
||||
let url = BASE_URL + `/api/v1/identity/register-user`;
|
||||
// TODO: edit the parameters of the request body.
|
||||
let body = {"firstName": "string", "lastName": "string", "username": "string", "email": "string", "password": "string", "confirmPassword": "string", "passportNumber": "string"};
|
||||
let params = {headers: {"Content-Type": "application/json", "Accept": "application/json"}};
|
||||
let request = http.post(url, JSON.stringify(body), params);
|
||||
|
||||
check(request, {
|
||||
"Success": (r) => r.status === 200
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
@ -3,6 +3,7 @@ namespace Passenger.Passengers.Features.CompletingRegisterPassenger.V1;
|
||||
using Ardalis.GuardClauses;
|
||||
using BuildingBlocks.Core.CQRS;
|
||||
using BuildingBlocks.Core.Event;
|
||||
using BuildingBlocks.Web;
|
||||
using Exceptions;
|
||||
using FluentValidation;
|
||||
using MapsterMapper;
|
||||
@ -10,6 +11,10 @@ using Microsoft.EntityFrameworkCore;
|
||||
using Data;
|
||||
using Dtos;
|
||||
using MassTransit;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
|
||||
public record CompleteRegisterPassenger
|
||||
(string PassportNumber, Enums.PassengerType PassengerType, int Age) : ICommand<CompleteRegisterPassengerResult>,
|
||||
@ -18,8 +23,45 @@ public record CompleteRegisterPassenger
|
||||
public Guid Id { get; init; } = NewId.NextGuid();
|
||||
}
|
||||
|
||||
public record PassengerRegistrationCompletedDomainEvent(Guid Id, string Name, string PassportNumber,
|
||||
Enums.PassengerType PassengerType, int Age, bool IsDeleted = false) : IDomainEvent;
|
||||
|
||||
public record CompleteRegisterPassengerResult(PassengerDto PassengerDto);
|
||||
|
||||
public record CompleteRegisterPassengerRequestDto(string PassportNumber, Enums.PassengerType PassengerType, int Age);
|
||||
|
||||
public record CompleteRegisterPassengerResponseDto(PassengerDto PassengerDto);
|
||||
|
||||
public class CompleteRegisterPassengerEndpoint : IMinimalEndpoint
|
||||
{
|
||||
public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapPost($"{EndpointConfig.BaseApiPath}/passenger/complete-registration", async (
|
||||
CompleteRegisterPassengerRequestDto request, IMapper mapper,
|
||||
IMediator mediator, CancellationToken cancellationToken) =>
|
||||
{
|
||||
var command = mapper.Map<CompleteRegisterPassenger>(request);
|
||||
|
||||
var result = await mediator.Send(command, cancellationToken);
|
||||
|
||||
var response = new CompleteRegisterPassengerResponseDto(result?.PassengerDto);
|
||||
|
||||
return Results.Ok(response);
|
||||
})
|
||||
.RequireAuthorization()
|
||||
.WithName("CompleteRegisterPassenger")
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Passenger").Build())
|
||||
.Produces<CompleteRegisterPassengerResponseDto>()
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.WithSummary("Complete Register Passenger")
|
||||
.WithDescription("Complete Register Passenger")
|
||||
.WithOpenApi()
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
internal class CompleteRegisterPassengerValidator : AbstractValidator<CompleteRegisterPassenger>
|
||||
{
|
||||
public CompleteRegisterPassengerValidator()
|
||||
@ -35,9 +77,8 @@ internal class CompleteRegisterPassengerValidator : AbstractValidator<CompleteRe
|
||||
}
|
||||
}
|
||||
|
||||
internal class
|
||||
CompleteRegisterPassengerCommandHandler : ICommandHandler<CompleteRegisterPassenger,
|
||||
CompleteRegisterPassengerResult>
|
||||
internal class CompleteRegisterPassengerCommandHandler : ICommandHandler<CompleteRegisterPassenger,
|
||||
CompleteRegisterPassengerResult>
|
||||
{
|
||||
private readonly IMapper _mapper;
|
||||
private readonly PassengerDbContext _passengerDbContext;
|
||||
|
||||
@ -1,42 +0,0 @@
|
||||
namespace Passenger.Passengers.Features.CompletingRegisterPassenger.V1;
|
||||
|
||||
using BuildingBlocks.Web;
|
||||
using MapsterMapper;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Dtos;
|
||||
using Microsoft.OpenApi.Models;
|
||||
|
||||
public record CompleteRegisterPassengerRequestDto(string PassportNumber, Enums.PassengerType PassengerType, int Age);
|
||||
public record CompleteRegisterPassengerResponseDto(PassengerDto PassengerDto);
|
||||
|
||||
public class CompleteRegisterPassengerEndpoint : IMinimalEndpoint
|
||||
{
|
||||
public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapPost($"{EndpointConfig.BaseApiPath}/passenger/complete-registration", CompleteRegisterPassenger)
|
||||
.RequireAuthorization()
|
||||
.WithName("CompleteRegisterPassenger")
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Passenger").Build())
|
||||
.Produces<CompleteRegisterPassengerResponseDto>()
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.WithOpenApi(operation => new OpenApiOperation(operation) { Summary = "Complete Register Passenger", Description = "Complete Register Passenger" })
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private async Task<IResult> CompleteRegisterPassenger(CompleteRegisterPassengerRequestDto request, IMapper mapper,
|
||||
IMediator mediator, CancellationToken cancellationToken)
|
||||
{
|
||||
var command = mapper.Map<CompleteRegisterPassenger>(request);
|
||||
|
||||
var result = await mediator.Send(command, cancellationToken);
|
||||
|
||||
var response = new CompleteRegisterPassengerResponseDto(result?.PassengerDto);
|
||||
|
||||
return Results.Ok(response);
|
||||
}
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
namespace Passenger.Passengers.Features.CompletingRegisterPassenger.V1;
|
||||
|
||||
using BuildingBlocks.Core.Event;
|
||||
|
||||
public record PassengerRegistrationCompletedDomainEvent(Guid Id, string Name, string PassportNumber,
|
||||
Enums.PassengerType PassengerType, int Age, bool IsDeleted = false) : IDomainEvent;
|
||||
@ -7,13 +7,47 @@ using FluentValidation;
|
||||
using MapsterMapper;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Ardalis.GuardClauses;
|
||||
using BuildingBlocks.Web;
|
||||
using Exceptions;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
|
||||
public record GetPassengerById(Guid Id) : IQuery<GetPassengerByIdResult>;
|
||||
|
||||
public record GetPassengerByIdResult(PassengerDto PassengerDto);
|
||||
|
||||
internal class GetPassengerByIdValidator: AbstractValidator<GetPassengerById>
|
||||
public record GetPassengerByIdResponseDto(PassengerDto PassengerDto);
|
||||
|
||||
public class GetPassengerByIdEndpoint : IMinimalEndpoint
|
||||
{
|
||||
public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapGet($"{EndpointConfig.BaseApiPath}/passenger/{{id}}",
|
||||
async (Guid id, IMediator mediator, CancellationToken cancellationToken) =>
|
||||
{
|
||||
var result = await mediator.Send(new GetPassengerById(id), cancellationToken);
|
||||
|
||||
var response = new GetPassengerByIdResponseDto(result?.PassengerDto);
|
||||
|
||||
return Results.Ok(response);
|
||||
})
|
||||
.RequireAuthorization()
|
||||
.WithName("GetPassengerById")
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Passenger").Build())
|
||||
.Produces<GetPassengerByIdResponseDto>()
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.WithSummary("Get Passenger By Id")
|
||||
.WithDescription("Get Passenger By Id")
|
||||
.WithOpenApi()
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
internal class GetPassengerByIdValidator : AbstractValidator<GetPassengerById>
|
||||
{
|
||||
public GetPassengerByIdValidator()
|
||||
{
|
||||
|
||||
@ -1,37 +0,0 @@
|
||||
namespace Passenger.Passengers.Features.GettingPassengerById.Queries.V1;
|
||||
|
||||
using BuildingBlocks.Web;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Dtos;
|
||||
using Microsoft.OpenApi.Models;
|
||||
|
||||
public record GetPassengerByIdResponseDto(PassengerDto PassengerDto);
|
||||
|
||||
public class GetPassengerByIdEndpoint : IMinimalEndpoint
|
||||
{
|
||||
public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapGet($"{EndpointConfig.BaseApiPath}/passenger/{{id}}", GetById)
|
||||
.RequireAuthorization()
|
||||
.WithName("GetPassengerById")
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Passenger").Build())
|
||||
.Produces<GetPassengerByIdResponseDto>()
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.WithOpenApi(operation => new OpenApiOperation(operation) { Summary = "Get Passenger By Id", Description = "Get Passenger By Id" })
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private async Task<IResult> GetById(Guid id, IMediator mediator, CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(new GetPassengerById(id), cancellationToken);
|
||||
|
||||
var response = new GetPassengerByIdResponseDto(result?.PassengerDto);
|
||||
|
||||
return Results.Ok(response);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
# OpenAPI Generator Ignore
|
||||
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
|
||||
|
||||
# Use this file to prevent files from being overwritten by the generator.
|
||||
# The patterns follow closely to .gitignore or .dockerignore.
|
||||
|
||||
# As an example, the C# client generator defines ApiClient.cs.
|
||||
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
|
||||
#ApiClient.cs
|
||||
|
||||
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
|
||||
#foo/*/qux
|
||||
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
|
||||
|
||||
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
|
||||
#foo/**/qux
|
||||
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
|
||||
|
||||
# You can also negate patterns with an exclamation (!).
|
||||
# For example, you can ignore all files in a docs folder with the file extension .md:
|
||||
#docs/*.md
|
||||
# Then explicitly reverse the ignore rule for a single file:
|
||||
#!docs/README.md
|
||||
@ -0,0 +1,3 @@
|
||||
.openapi-generator-ignore
|
||||
README.md
|
||||
script.js
|
||||
@ -0,0 +1 @@
|
||||
6.6.0-SNAPSHOT
|
||||
15
src/Services/Passenger/tests/PerformanceTest/README.md
Normal file
15
src/Services/Passenger/tests/PerformanceTest/README.md
Normal file
@ -0,0 +1,15 @@
|
||||
# Generated k6 script
|
||||
|
||||
The `script.js` file contains most of the Swagger/OpenAPI specification and you can customize it to your needs.
|
||||
|
||||
Global header variables are defined at the top of the file, like `api_key`. Each path in the specification is converted into a [group](https://docs.k6.io/docs/tags-and-groups) in k6 and each group contains all the request methods related to that path. Path and query parameters are extracted from the specification and put at the start of the group. The URL is constructed from the base URL plus path and query.
|
||||
|
||||
If the Swagger/OpenAPI specification used as the input spec contains examples at parameter level, those will be extracted and utilized as parameter values. The `handleParamValue` custom Mustache lambda registered for use in the K6 `script.mustache` template handles the conditional checks, formatting, and outputting of parameter values. If a given parameter has value specified – either in `example` or `examples` field, defined at the parameter level – that value will be used. For list (`examples`), entire list will be output in the generated script and the first element from that list will be assigned as parameter value. If a given parameter does not have an example defined, a placeholder value with `TODO_EDIT_THE_` prefix will be generated for that parameter, and you will have to assign a value before you can run the script. In other words, you can now generate K6 test scripts which are ready to run, provided the Swagger/OpenAPI specification used as the input spec contains examples for all of the path/query parameters; see `modules/openapi-generator/src/test/resources/3_0/examples.yaml` for an example of such specification, and https://swagger.io/docs/specification/adding-examples/ for more information about adding examples.
|
||||
|
||||
k6 specific parameters are in the [`params`](https://docs.k6.io/docs/params-k6http) object, and `body` contains the [request](https://docs.k6.io/docs/http-requests) body which is in the form of `identifier: type`, which the `type` should be substituted by a proper value. Then goes the request and the check.
|
||||
|
||||
[Check](https://docs.k6.io/docs/checks) are like asserts but differ in that they don't halt execution, instead they just store the result of the check, pass or fail, and let the script execution continue.
|
||||
|
||||
Each request is always followed by a 0.1 second [sleep](https://docs.k6.io/docs/sleep-t-1) to prevent the script execution from flooding the system with too many requests simultaneously.
|
||||
|
||||
Note that the default iteration count and VU count is 1. So each request in each group will be executed once. For more information, see the [k6 options](https://docs.k6.io/docs/options).
|
||||
55
src/Services/Passenger/tests/PerformanceTest/script.js
Normal file
55
src/Services/Passenger/tests/PerformanceTest/script.js
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* APIs
|
||||
* An example application with OpenAPI, Swashbuckle, and API versioning.
|
||||
*
|
||||
* OpenAPI spec version: 1.0
|
||||
* Contact:
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator.
|
||||
* https://github.com/OpenAPITools/openapi-generator
|
||||
*
|
||||
* OpenAPI generator version: 6.6.0-SNAPSHOT
|
||||
*/
|
||||
|
||||
|
||||
import http from "k6/http";
|
||||
import { group, check, sleep } from "k6";
|
||||
|
||||
const BASE_URL = "/";
|
||||
// Sleep duration between successive requests.
|
||||
// You might want to edit the value of this variable or remove calls to the sleep function on the script.
|
||||
const SLEEP_DURATION = 0.1;
|
||||
// Global variables should be initialized.
|
||||
|
||||
export default function() {
|
||||
group("/api/v1/passenger/{id}", () => {
|
||||
let id = 'TODO_EDIT_THE_ID'; // specify value as there is no example value for this parameter in OpenAPI spec
|
||||
|
||||
// Request No. 1: GetPassengerById
|
||||
{
|
||||
let url = BASE_URL + `/api/v1/passenger/${id}`;
|
||||
let request = http.get(url);
|
||||
|
||||
check(request, {
|
||||
"Success": (r) => r.status === 200
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
group("/api/v1/passenger/complete-registration", () => {
|
||||
|
||||
// Request No. 1: CompleteRegisterPassenger
|
||||
{
|
||||
let url = BASE_URL + `/api/v1/passenger/complete-registration`;
|
||||
// TODO: edit the parameters of the request body.
|
||||
let body = {"passportNumber": "string", "passengerType": "passengertype", "age": "integer"};
|
||||
let params = {headers: {"Content-Type": "application/json", "Accept": "application/json"}};
|
||||
let request = http.post(url, JSON.stringify(body), params);
|
||||
|
||||
check(request, {
|
||||
"Success": (r) => r.status === 200
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user