diff --git a/deployments/docker-compose/docker-compose.yaml b/deployments/docker-compose/docker-compose.yaml index cc3d7bf..2151bb6 100644 --- a/deployments/docker-compose/docker-compose.yaml +++ b/deployments/docker-compose/docker-compose.yaml @@ -13,6 +13,10 @@ services: environment: - POSTGRES_USER=postgres - POSTGRES_PASSWORD=postgres + command: + - "postgres" + - "-c" + - "wal_level=logical" networks: - booking diff --git a/deployments/docker-compose/infrastracture.yaml b/deployments/docker-compose/infrastracture.yaml index 7152e57..24041dd 100644 --- a/deployments/docker-compose/infrastracture.yaml +++ b/deployments/docker-compose/infrastracture.yaml @@ -27,6 +27,10 @@ services: environment: - POSTGRES_USER=postgres - POSTGRES_PASSWORD=postgres + command: + - "postgres" + - "-c" + - "wal_level=logical" networks: - booking diff --git a/src/Services/Booking/src/Booking/Booking/Features/CreatingBook/Commands/V1/BookingCreatedDomainEvent.cs b/src/Services/Booking/src/Booking/Booking/Features/CreatingBook/Commands/V1/BookingCreatedDomainEvent.cs deleted file mode 100644 index d919c60..0000000 --- a/src/Services/Booking/src/Booking/Booking/Features/CreatingBook/Commands/V1/BookingCreatedDomainEvent.cs +++ /dev/null @@ -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; diff --git a/src/Services/Booking/src/Booking/Booking/Features/CreatingBook/Commands/V1/CreateBooking.cs b/src/Services/Booking/src/Booking/Booking/Features/CreatingBook/Commands/V1/CreateBooking.cs index 2e854d7..ac6aad4 100644 --- a/src/Services/Booking/src/Booking/Booking/Features/CreatingBook/Commands/V1/CreateBooking.cs +++ b/src/Services/Booking/src/Booking/Booking/Features/CreatingBook/Commands/V1/CreateBooking.cs @@ -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, IInternalCommand +public record CreateBooking(Guid PassengerId, Guid FlightId, string Description) : ICommand, + 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(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() + .ProducesProblem(StatusCodes.Status400BadRequest) + .WithSummary("Create Booking") + .WithDescription("Create Booking") + .WithOpenApi() + .HasApiVersion(1.0); + + return builder; + } +} + internal class CreateBookingValidator : AbstractValidator { public CreateBookingValidator() @@ -54,16 +97,21 @@ internal class CreateBookingCommandHandler : ICommandHandler() - .ProducesProblem(StatusCodes.Status400BadRequest) - .WithOpenApi(operation => new OpenApiOperation(operation) { Summary = "Create Booking", Description = "Create Booking" }) - .HasApiVersion(1.0); - - return builder; - } - - private async Task CreateBooking(CreateBookingRequestDto request, IMediator mediator, IMapper mapper, - CancellationToken cancellationToken) - { - var command = mapper.Map(request); - - var result = await mediator.Send(command, cancellationToken); - - var response = new CreateBookingResponseDto(result.Id); - - return Results.Ok(response); - } -} diff --git a/src/Services/Booking/tests/PerformanceTest/.openapi-generator-ignore b/src/Services/Booking/tests/PerformanceTest/.openapi-generator-ignore new file mode 100644 index 0000000..7484ee5 --- /dev/null +++ b/src/Services/Booking/tests/PerformanceTest/.openapi-generator-ignore @@ -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 diff --git a/src/Services/Booking/tests/PerformanceTest/.openapi-generator/FILES b/src/Services/Booking/tests/PerformanceTest/.openapi-generator/FILES new file mode 100644 index 0000000..297b5aa --- /dev/null +++ b/src/Services/Booking/tests/PerformanceTest/.openapi-generator/FILES @@ -0,0 +1,3 @@ +.openapi-generator-ignore +README.md +script.js diff --git a/src/Services/Booking/tests/PerformanceTest/.openapi-generator/VERSION b/src/Services/Booking/tests/PerformanceTest/.openapi-generator/VERSION new file mode 100644 index 0000000..ba8a874 --- /dev/null +++ b/src/Services/Booking/tests/PerformanceTest/.openapi-generator/VERSION @@ -0,0 +1 @@ +6.6.0-SNAPSHOT \ No newline at end of file diff --git a/src/Services/Booking/tests/PerformanceTest/README.md b/src/Services/Booking/tests/PerformanceTest/README.md new file mode 100644 index 0000000..64a7521 --- /dev/null +++ b/src/Services/Booking/tests/PerformanceTest/README.md @@ -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). diff --git a/src/Services/Booking/tests/PerformanceTest/script.js b/src/Services/Booking/tests/PerformanceTest/script.js new file mode 100644 index 0000000..d07f84a --- /dev/null +++ b/src/Services/Booking/tests/PerformanceTest/script.js @@ -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 + }); + } + }); + +} diff --git a/src/Services/Flight/src/Flight/Aircrafts/Features/CreatingAircraft/V1/AircraftCreatedDomainEvent.cs b/src/Services/Flight/src/Flight/Aircrafts/Features/CreatingAircraft/V1/AircraftCreatedDomainEvent.cs deleted file mode 100644 index d296895..0000000 --- a/src/Services/Flight/src/Flight/Aircrafts/Features/CreatingAircraft/V1/AircraftCreatedDomainEvent.cs +++ /dev/null @@ -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; diff --git a/src/Services/Flight/src/Flight/Aircrafts/Features/CreatingAircraft/V1/CreateAircraft.cs b/src/Services/Flight/src/Flight/Aircrafts/Features/CreatingAircraft/V1/CreateAircraft.cs index f06e33f..bcbf296 100644 --- a/src/Services/Flight/src/Flight/Aircrafts/Features/CreatingAircraft/V1/CreateAircraft.cs +++ b/src/Services/Flight/src/Flight/Aircrafts/Features/CreatingAircraft/V1/CreateAircraft.cs @@ -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, IInternalCommand +public record CreateAircraft(string Name, string Model, int ManufacturingYear) : ICommand, + 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(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() + .ProducesProblem(StatusCodes.Status400BadRequest) + .WithSummary("Create Aircraft") + .WithDescription("Create Aircraft") + .WithOpenApi() + .HasApiVersion(1.0); + + return builder; + } +} + internal class CreateAircraftValidator : AbstractValidator { public CreateAircraftValidator() @@ -44,7 +87,8 @@ internal class CreateAircraftHandler : IRequestHandler x.Model == request.Model, cancellationToken); + var aircraft = + await _flightDbContext.Aircraft.SingleOrDefaultAsync(x => x.Model == request.Model, cancellationToken); if (aircraft is not null) { diff --git a/src/Services/Flight/src/Flight/Aircrafts/Features/CreatingAircraft/V1/CreateAircraftEndpoint.cs b/src/Services/Flight/src/Flight/Aircrafts/Features/CreatingAircraft/V1/CreateAircraftEndpoint.cs deleted file mode 100644 index b7e4733..0000000 --- a/src/Services/Flight/src/Flight/Aircrafts/Features/CreatingAircraft/V1/CreateAircraftEndpoint.cs +++ /dev/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() - .ProducesProblem(StatusCodes.Status400BadRequest) - .WithOpenApi(operation => new OpenApiOperation(operation) { Summary = "Create Aircraft", Description = "Create Aircraft" }) - .HasApiVersion(1.0); - - return builder; - } - - private async Task CreateAircraft(CreateAircraftRequestDto request, IMediator mediator, IMapper mapper, - CancellationToken cancellationToken) - { - var command = mapper.Map(request); - - var result = await mediator.Send(command, cancellationToken); - - var response = new CreateAircraftResponseDto(result.Id); - - return Results.Ok(response); - } -} diff --git a/src/Services/Flight/src/Flight/Airports/Features/CreatingAirport/V1/AirportCreatedDomainEvent.cs b/src/Services/Flight/src/Flight/Airports/Features/CreatingAirport/V1/AirportCreatedDomainEvent.cs deleted file mode 100644 index d0ea78d..0000000 --- a/src/Services/Flight/src/Flight/Airports/Features/CreatingAirport/V1/AirportCreatedDomainEvent.cs +++ /dev/null @@ -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; diff --git a/src/Services/Flight/src/Flight/Airports/Features/CreatingAirport/V1/CreateAirport.cs b/src/Services/Flight/src/Flight/Airports/Features/CreatingAirport/V1/CreateAirport.cs index 0f7f802..6f10fb1 100644 --- a/src/Services/Flight/src/Flight/Airports/Features/CreatingAirport/V1/CreateAirport.cs +++ b/src/Services/Flight/src/Flight/Airports/Features/CreatingAirport/V1/CreateAirport.cs @@ -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, 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(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() + .ProducesProblem(StatusCodes.Status400BadRequest) + .WithSummary("Create Airport") + .WithDescription("Create Airport") + .WithOpenApi() + .HasApiVersion(1.0); + + return builder; + } +} + internal class CreateAirportValidator : AbstractValidator { public CreateAirportValidator() @@ -30,7 +72,6 @@ internal class CreateAirportValidator : AbstractValidator } } - internal class CreateAirportHandler : IRequestHandler { private readonly FlightDbContext _flightDbContext; @@ -44,7 +85,8 @@ internal class CreateAirportHandler : IRequestHandler x.Code == request.Code, cancellationToken); + var airport = + await _flightDbContext.Airports.SingleOrDefaultAsync(x => x.Code == request.Code, cancellationToken); if (airport is not null) { diff --git a/src/Services/Flight/src/Flight/Airports/Features/CreatingAirport/V1/CreateAirportEndpoint.cs b/src/Services/Flight/src/Flight/Airports/Features/CreatingAirport/V1/CreateAirportEndpoint.cs deleted file mode 100644 index 0e924ab..0000000 --- a/src/Services/Flight/src/Flight/Airports/Features/CreatingAirport/V1/CreateAirportEndpoint.cs +++ /dev/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() - .ProducesProblem(StatusCodes.Status400BadRequest) - .WithOpenApi(operation => new OpenApiOperation(operation) { Summary = "Create Airport", Description = "Create Airport" }) - .HasApiVersion(1.0); - - return builder; - } - - private async Task CreateAirport(CreateAirportRequestDto request, IMediator mediator, IMapper mapper, - CancellationToken cancellationToken) - { - var command = mapper.Map(request); - - var result = await mediator.Send(command, cancellationToken); - - var response = new CreateAirportResponseDto(result.Id); - - return Results.Ok(response); - } -} diff --git a/src/Services/Flight/src/Flight/Flights/Features/CreatingFlight/V1/CreateFlight.cs b/src/Services/Flight/src/Flight/Flights/Features/CreatingFlight/V1/CreateFlight.cs index 8572ce8..bf1a8e4 100644 --- a/src/Services/Flight/src/Flight/Flights/Features/CreatingFlight/V1/CreateFlight.cs +++ b/src/Services/Flight/src/Flight/Flights/Features/CreatingFlight/V1/CreateFlight.cs @@ -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(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(StatusCodes.Status201Created) + .ProducesProblem(StatusCodes.Status400BadRequest) + .WithSummary("Create Flight") + .WithDescription("Create Flight") + .WithOpenApi() + .HasApiVersion(1.0); + + return builder; + } +} + internal class CreateFlightValidator : AbstractValidator { public CreateFlightValidator() diff --git a/src/Services/Flight/src/Flight/Flights/Features/CreatingFlight/V1/CreateFlightEndpoint.cs b/src/Services/Flight/src/Flight/Flights/Features/CreatingFlight/V1/CreateFlightEndpoint.cs deleted file mode 100644 index 3fdacb1..0000000 --- a/src/Services/Flight/src/Flight/Flights/Features/CreatingFlight/V1/CreateFlightEndpoint.cs +++ /dev/null @@ -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(StatusCodes.Status201Created) - .ProducesProblem(StatusCodes.Status400BadRequest) - .WithOpenApi(operation => new OpenApiOperation(operation) { Summary = "Create Flight", Description = "Create Flight" }) - .HasApiVersion(1.0); - - return builder; - } - - private async Task CreateFlight(CreateFlightRequestDto request, IMediator mediator, IMapper mapper, - CancellationToken cancellationToken) - { - var command = mapper.Map(request); - - var result = await mediator.Send(command, cancellationToken); - - var response = new CreateFlightResponseDto(result.Id); - - return Results.CreatedAtRoute("GetFlightById", new { id = result.Id }, response); - } -} diff --git a/src/Services/Flight/src/Flight/Flights/Features/CreatingFlight/V1/FlightCreatedDomainEvent.cs b/src/Services/Flight/src/Flight/Flights/Features/CreatingFlight/V1/FlightCreatedDomainEvent.cs deleted file mode 100644 index 0d5b31d..0000000 --- a/src/Services/Flight/src/Flight/Flights/Features/CreatingFlight/V1/FlightCreatedDomainEvent.cs +++ /dev/null @@ -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; diff --git a/src/Services/Flight/src/Flight/Flights/Features/DeletingFlight/V1/DeleteFlight.cs b/src/Services/Flight/src/Flight/Flights/Features/DeletingFlight/V1/DeleteFlight.cs index 55b5f2c..81abe99 100644 --- a/src/Services/Flight/src/Flight/Flights/Features/DeletingFlight/V1/DeleteFlight.cs +++ b/src/Services/Flight/src/Flight/Flights/Features/DeletingFlight/V1/DeleteFlight.cs @@ -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, 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 { public DeleteFlightValidator() diff --git a/src/Services/Flight/src/Flight/Flights/Features/DeletingFlight/V1/DeleteFlightEndpoint.cs b/src/Services/Flight/src/Flight/Flights/Features/DeletingFlight/V1/DeleteFlightEndpoint.cs deleted file mode 100644 index ba91cb2..0000000 --- a/src/Services/Flight/src/Flight/Flights/Features/DeletingFlight/V1/DeleteFlightEndpoint.cs +++ /dev/null @@ -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 DeleteFlight(Guid id, IMediator mediator, CancellationToken cancellationToken) - { - var result = await mediator.Send(new DeleteFlight(id), cancellationToken); - - return Results.NoContent(); - } -} diff --git a/src/Services/Flight/src/Flight/Flights/Features/DeletingFlight/V1/FlightDeletedDomainEvent.cs b/src/Services/Flight/src/Flight/Flights/Features/DeletingFlight/V1/FlightDeletedDomainEvent.cs deleted file mode 100644 index 06ed2dc..0000000 --- a/src/Services/Flight/src/Flight/Flights/Features/DeletingFlight/V1/FlightDeletedDomainEvent.cs +++ /dev/null @@ -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; diff --git a/src/Services/Flight/src/Flight/Flights/Features/GettingAvailableFlights/V1/GetAvailableFlights.cs b/src/Services/Flight/src/Flight/Flights/Features/GettingAvailableFlights/V1/GetAvailableFlights.cs index f6dcb57..605a8a6 100644 --- a/src/Services/Flight/src/Flight/Flights/Features/GettingAvailableFlights/V1/GetAvailableFlights.cs +++ b/src/Services/Flight/src/Flight/Flights/Features/GettingAvailableFlights/V1/GetAvailableFlights.cs @@ -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, ICacheRequest @@ -22,6 +27,36 @@ public record GetAvailableFlights : IQuery, ICacheReq public record GetAvailableFlightsResult(IEnumerable FlightDtos); +public record GetAvailableFlightsResponseDto(IEnumerable 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() + .ProducesProblem(StatusCodes.Status400BadRequest) + .WithSummary("Get Available Flights") + .WithDescription("Get Available Flights") + .WithOpenApi() + .HasApiVersion(1.0); + + return builder; + } +} + internal class GetAvailableFlightsHandler : IQueryHandler { private readonly IMapper _mapper; diff --git a/src/Services/Flight/src/Flight/Flights/Features/GettingAvailableFlights/V1/GetAvailableFlightsEndpoint.cs b/src/Services/Flight/src/Flight/Flights/Features/GettingAvailableFlights/V1/GetAvailableFlightsEndpoint.cs deleted file mode 100644 index 899dac5..0000000 --- a/src/Services/Flight/src/Flight/Flights/Features/GettingAvailableFlights/V1/GetAvailableFlightsEndpoint.cs +++ /dev/null @@ -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 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() - .ProducesProblem(StatusCodes.Status400BadRequest) - .WithOpenApi(operation => new OpenApiOperation(operation) { Summary = "Get Available Flights", Description = "Get Available Flights" }) - .HasApiVersion(1.0); - - return builder; - } - - private async Task GetAvailableFlights(IMediator mediator, CancellationToken cancellationToken) - { - var result = await mediator.Send(new GetAvailableFlights(), cancellationToken); - - var response = new GetAvailableFlightsResponseDto(result?.FlightDtos); - - return Results.Ok(response); - } -} diff --git a/src/Services/Flight/src/Flight/Flights/Features/GettingFlightById/V1/GetFlightById.cs b/src/Services/Flight/src/Flight/Flights/Features/GettingFlightById/V1/GetFlightById.cs index e7d7a68..793ecf5 100644 --- a/src/Services/Flight/src/Flight/Flights/Features/GettingFlightById/V1/GetFlightById.cs +++ b/src/Services/Flight/src/Flight/Flights/Features/GettingFlightById/V1/GetFlightById.cs @@ -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; 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() + .ProducesProblem(StatusCodes.Status400BadRequest) + .WithSummary("Get Flight By Id") + .WithDescription("Get Flight By Id") + .WithOpenApi() + .HasApiVersion(1.0); + + return builder; + } +} + public class GetFlightByIdValidator : AbstractValidator { public GetFlightByIdValidator() diff --git a/src/Services/Flight/src/Flight/Flights/Features/GettingFlightById/V1/GetFlightByIdEndpoint.cs b/src/Services/Flight/src/Flight/Flights/Features/GettingFlightById/V1/GetFlightByIdEndpoint.cs deleted file mode 100644 index 3d0bd2e..0000000 --- a/src/Services/Flight/src/Flight/Flights/Features/GettingFlightById/V1/GetFlightByIdEndpoint.cs +++ /dev/null @@ -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() - .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 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); - } -} diff --git a/src/Services/Flight/src/Flight/Flights/Features/UpdatingFlight/V1/FlightUpdatedDomainEvent.cs b/src/Services/Flight/src/Flight/Flights/Features/UpdatingFlight/V1/FlightUpdatedDomainEvent.cs deleted file mode 100644 index 6739a70..0000000 --- a/src/Services/Flight/src/Flight/Flights/Features/UpdatingFlight/V1/FlightUpdatedDomainEvent.cs +++ /dev/null @@ -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; diff --git a/src/Services/Flight/src/Flight/Flights/Features/UpdatingFlight/V1/UpdateFlight.cs b/src/Services/Flight/src/Flight/Flights/Features/UpdatingFlight/V1/UpdateFlight.cs index 2dde4fd..a9ea7e7 100644 --- a/src/Services/Flight/src/Flight/Flights/Features/UpdatingFlight/V1/UpdateFlight.cs +++ b/src/Services/Flight/src/Flight/Flights/Features/UpdatingFlight/V1/UpdateFlight.cs @@ -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(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 { public UpdateFlightValidator() diff --git a/src/Services/Flight/src/Flight/Flights/Features/UpdatingFlight/V1/UpdateFlightEndpoint.cs b/src/Services/Flight/src/Flight/Flights/Features/UpdatingFlight/V1/UpdateFlightEndpoint.cs deleted file mode 100644 index 0bccebc..0000000 --- a/src/Services/Flight/src/Flight/Flights/Features/UpdatingFlight/V1/UpdateFlightEndpoint.cs +++ /dev/null @@ -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 UpdateFlight(UpdateFlightRequestDto request, IMediator mediator, IMapper mapper, CancellationToken cancellationToken) - { - var command = mapper.Map(request); - - await mediator.Send(command, cancellationToken); - - return Results.NoContent(); - } -} diff --git a/src/Services/Flight/src/Flight/Seats/Features/CreatingSeat/V1/CreateSeat.cs b/src/Services/Flight/src/Flight/Seats/Features/CreatingSeat/V1/CreateSeat.cs index 5034ad0..e5ae475 100644 --- a/src/Services/Flight/src/Flight/Seats/Features/CreatingSeat/V1/CreateSeat.cs +++ b/src/Services/Flight/src/Flight/Seats/Features/CreatingSeat/V1/CreateSeat.cs @@ -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, IInternalCommand +public record CreateSeat + (string SeatNumber, Enums.SeatType Type, Enums.SeatClass Class, Guid FlightId) : ICommand, + 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() + .ProducesProblem(StatusCodes.Status400BadRequest) + .WithSummary("Create Seat") + .WithDescription("Create Seat") + .WithOpenApi() + .HasApiVersion(1.0); + + return builder; + } + + private async Task CreateSeat(CreateSeatRequestDto request, IMediator mediator, IMapper mapper, + CancellationToken cancellationToken) + { + var command = mapper.Map(request); + + var result = await mediator.Send(command, cancellationToken); + + var response = new CreateSeatResponseDto(result.Id); + + return Results.Ok(response); + } +} + internal class CreateSeatValidator : AbstractValidator { public CreateSeatValidator() @@ -62,4 +107,3 @@ internal class CreateSeatCommandHandler : IRequestHandler() - .ProducesProblem(StatusCodes.Status400BadRequest) - .WithOpenApi(operation => new OpenApiOperation(operation) { Summary = "Create Seat", Description = "Create Seat" }) - .HasApiVersion(1.0); - - return builder; - } - - private async Task CreateSeat(CreateSeatRequestDto request, IMediator mediator, IMapper mapper, - CancellationToken cancellationToken) - { - var command = mapper.Map(request); - - var result = await mediator.Send(command, cancellationToken); - - var response = new CreateSeatResponseDto(result.Id); - - return Results.Ok(response); - } -} diff --git a/src/Services/Flight/src/Flight/Seats/Features/CreatingSeat/V1/SeatCreatedDomainEvent.cs b/src/Services/Flight/src/Flight/Seats/Features/CreatingSeat/V1/SeatCreatedDomainEvent.cs deleted file mode 100644 index 1a1e04a..0000000 --- a/src/Services/Flight/src/Flight/Seats/Features/CreatingSeat/V1/SeatCreatedDomainEvent.cs +++ /dev/null @@ -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; diff --git a/src/Services/Flight/src/Flight/Seats/Features/GettingAvailableSeats/V1/GetAvailableSeats.cs b/src/Services/Flight/src/Flight/Seats/Features/GettingAvailableSeats/V1/GetAvailableSeats.cs index 3c2c07f..5ca14af 100644 --- a/src/Services/Flight/src/Flight/Seats/Features/GettingAvailableSeats/V1/GetAvailableSeats.cs +++ b/src/Services/Flight/src/Flight/Seats/Features/GettingAvailableSeats/V1/GetAvailableSeats.cs @@ -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; public record GetAvailableSeatsResult(IEnumerable SeatDtos); +public record GetAvailableSeatsResponseDto(IEnumerable 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() + .ProducesProblem(StatusCodes.Status400BadRequest) + .WithSummary("Get Available Seats") + .WithDescription("Get Available Seats") + .WithOpenApi() + .HasApiVersion(1.0); + + return builder; + } + + private async Task 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 { public GetAvailableSeatsValidator() diff --git a/src/Services/Flight/src/Flight/Seats/Features/GettingAvailableSeats/V1/GetAvailableSeatsEndpoint.cs b/src/Services/Flight/src/Flight/Seats/Features/GettingAvailableSeats/V1/GetAvailableSeatsEndpoint.cs deleted file mode 100644 index 84e49c8..0000000 --- a/src/Services/Flight/src/Flight/Seats/Features/GettingAvailableSeats/V1/GetAvailableSeatsEndpoint.cs +++ /dev/null @@ -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 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() - .ProducesProblem(StatusCodes.Status400BadRequest) - .WithOpenApi(operation => new OpenApiOperation(operation) { Summary = "Get Available Seats", Description = "Get Available Seats" }) - .HasApiVersion(1.0); - - return builder; - } - - private async Task 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); - } -} diff --git a/src/Services/Flight/src/Flight/Seats/Features/ReservingSeat/Commands/V1/ReserveSeat.cs b/src/Services/Flight/src/Flight/Seats/Features/ReservingSeat/Commands/V1/ReserveSeat.cs index 5cbf019..1b19d5f 100644 --- a/src/Services/Flight/src/Flight/Seats/Features/ReservingSeat/Commands/V1/ReserveSeat.cs +++ b/src/Services/Flight/src/Flight/Seats/Features/ReservingSeat/Commands/V1/ReserveSeat.cs @@ -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, 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() + .ProducesProblem(StatusCodes.Status400BadRequest) + .WithSummary("Reserve Seat") + .WithDescription("Reserve Seat") + .WithOpenApi() + .HasApiVersion(1.0); + + return builder; + } + + private async Task ReserveSeat(ReserveSeatRequestDto request, IMediator mediator, IMapper mapper, + CancellationToken cancellationToken) + { + var command = mapper.Map(request); + + var result = await mediator.Send(command, cancellationToken); + + var response = new ReserveSeatResponseDto(result.Id); + + return Results.Ok(response); + } +} + internal class ReserveSeatValidator : AbstractValidator { public ReserveSeatValidator() @@ -38,7 +81,8 @@ internal class ReserveSeatCommandHandler : IRequestHandler 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) { diff --git a/src/Services/Flight/src/Flight/Seats/Features/ReservingSeat/Commands/V1/ReserveSeatEndpoint.cs b/src/Services/Flight/src/Flight/Seats/Features/ReservingSeat/Commands/V1/ReserveSeatEndpoint.cs deleted file mode 100644 index dd5bfb0..0000000 --- a/src/Services/Flight/src/Flight/Seats/Features/ReservingSeat/Commands/V1/ReserveSeatEndpoint.cs +++ /dev/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() - .ProducesProblem(StatusCodes.Status400BadRequest) - .WithOpenApi(operation => new OpenApiOperation(operation) { Summary = "Reserve Seat", Description = "Reserve Seat" }) - .HasApiVersion(1.0); - - return builder; - } - - private async Task ReserveSeat(ReserveSeatRequestDto request, IMediator mediator, IMapper mapper, CancellationToken cancellationToken) - { - var command = mapper.Map(request); - - var result = await mediator.Send(command, cancellationToken); - - var response = new ReserveSeatResponseDto(result.Id); - - return Results.Ok(response); - } -} diff --git a/src/Services/Flight/src/Flight/Seats/Features/ReservingSeat/Commands/V1/SeatReservedDomainEvent.cs b/src/Services/Flight/src/Flight/Seats/Features/ReservingSeat/Commands/V1/SeatReservedDomainEvent.cs deleted file mode 100644 index f83b721..0000000 --- a/src/Services/Flight/src/Flight/Seats/Features/ReservingSeat/Commands/V1/SeatReservedDomainEvent.cs +++ /dev/null @@ -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; diff --git a/src/Services/Flight/tests/PerformanceTest/.openapi-generator-ignore b/src/Services/Flight/tests/PerformanceTest/.openapi-generator-ignore new file mode 100644 index 0000000..7484ee5 --- /dev/null +++ b/src/Services/Flight/tests/PerformanceTest/.openapi-generator-ignore @@ -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 diff --git a/src/Services/Flight/tests/PerformanceTest/.openapi-generator/FILES b/src/Services/Flight/tests/PerformanceTest/.openapi-generator/FILES new file mode 100644 index 0000000..343cd1b --- /dev/null +++ b/src/Services/Flight/tests/PerformanceTest/.openapi-generator/FILES @@ -0,0 +1,2 @@ +README.md +script.js diff --git a/src/Services/Flight/tests/PerformanceTest/.openapi-generator/VERSION b/src/Services/Flight/tests/PerformanceTest/.openapi-generator/VERSION new file mode 100644 index 0000000..ba8a874 --- /dev/null +++ b/src/Services/Flight/tests/PerformanceTest/.openapi-generator/VERSION @@ -0,0 +1 @@ +6.6.0-SNAPSHOT \ No newline at end of file diff --git a/src/Services/Flight/tests/PerformanceTest/README.md b/src/Services/Flight/tests/PerformanceTest/README.md new file mode 100644 index 0000000..64a7521 --- /dev/null +++ b/src/Services/Flight/tests/PerformanceTest/README.md @@ -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). diff --git a/src/Services/Flight/tests/PerformanceTest/script.js b/src/Services/Flight/tests/PerformanceTest/script.js new file mode 100644 index 0000000..48348e1 --- /dev/null +++ b/src/Services/Flight/tests/PerformanceTest/script.js @@ -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 + }); + } + }); + +} diff --git a/src/Services/Identity/src/Identity/Identity/Features/RegisteringNewUser/V1/RegisterNewUser.cs b/src/Services/Identity/src/Identity/Identity/Features/RegisteringNewUser/V1/RegisterNewUser.cs index b3fddd1..b3358aa 100644 --- a/src/Services/Identity/src/Identity/Identity/Features/RegisteringNewUser/V1/RegisterNewUser.cs +++ b/src/Services/Identity/src/Identity/Identity/Features/RegisteringNewUser/V1/RegisterNewUser.cs @@ -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(request); + + var result = await mediator.Send(command, cancellationToken); + + var response = mapper.Map(result); + + return Results.Ok(response); + }) + .WithName("RegisterUser") + .WithApiVersionSet(builder.NewApiVersionSet("Identity").Build()) + .Produces() + .ProducesProblem(StatusCodes.Status400BadRequest) + .WithSummary("Register User") + .WithDescription("Register User") + .WithOpenApi() + .HasApiVersion(1.0); + + return builder; + } +} + internal class RegisterNewUserValidator : AbstractValidator { public RegisterNewUserValidator() @@ -81,8 +122,9 @@ internal class RegisterNewUserHandler : ICommandHandler 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); diff --git a/src/Services/Identity/src/Identity/Identity/Features/RegisteringNewUser/V1/RegisterNewUserEndpoint.cs b/src/Services/Identity/src/Identity/Identity/Features/RegisteringNewUser/V1/RegisterNewUserEndpoint.cs deleted file mode 100644 index 9f25ae7..0000000 --- a/src/Services/Identity/src/Identity/Identity/Features/RegisteringNewUser/V1/RegisterNewUserEndpoint.cs +++ /dev/null @@ -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() - .ProducesProblem(StatusCodes.Status400BadRequest) - .WithOpenApi(operation => new OpenApiOperation(operation) { Summary = "Register User", Description = "Register User" }) - .HasApiVersion(1.0); - - return builder; - } - - private async Task RegisterNewUser(RegisterNewUserRequestDto request, IMediator mediator, IMapper mapper, - CancellationToken cancellationToken) - { - var command = mapper.Map(request); - - var result = await mediator.Send(command, cancellationToken); - - var response = mapper.Map(result); - - return Results.Ok(response); - } -} diff --git a/src/Services/Identity/tests/PerformanceTest/.openapi-generator-ignore b/src/Services/Identity/tests/PerformanceTest/.openapi-generator-ignore new file mode 100644 index 0000000..7484ee5 --- /dev/null +++ b/src/Services/Identity/tests/PerformanceTest/.openapi-generator-ignore @@ -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 diff --git a/src/Services/Identity/tests/PerformanceTest/.openapi-generator/FILES b/src/Services/Identity/tests/PerformanceTest/.openapi-generator/FILES new file mode 100644 index 0000000..297b5aa --- /dev/null +++ b/src/Services/Identity/tests/PerformanceTest/.openapi-generator/FILES @@ -0,0 +1,3 @@ +.openapi-generator-ignore +README.md +script.js diff --git a/src/Services/Identity/tests/PerformanceTest/.openapi-generator/VERSION b/src/Services/Identity/tests/PerformanceTest/.openapi-generator/VERSION new file mode 100644 index 0000000..ba8a874 --- /dev/null +++ b/src/Services/Identity/tests/PerformanceTest/.openapi-generator/VERSION @@ -0,0 +1 @@ +6.6.0-SNAPSHOT \ No newline at end of file diff --git a/src/Services/Identity/tests/PerformanceTest/README.md b/src/Services/Identity/tests/PerformanceTest/README.md new file mode 100644 index 0000000..64a7521 --- /dev/null +++ b/src/Services/Identity/tests/PerformanceTest/README.md @@ -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). diff --git a/src/Services/Identity/tests/PerformanceTest/script.js b/src/Services/Identity/tests/PerformanceTest/script.js new file mode 100644 index 0000000..2ed5474 --- /dev/null +++ b/src/Services/Identity/tests/PerformanceTest/script.js @@ -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 + }); + } + }); + +} diff --git a/src/Services/Passenger/src/Passenger/Passengers/Features/CompletingRegisterPassenger/V1/CompleteRegisterPassenger.cs b/src/Services/Passenger/src/Passenger/Passengers/Features/CompletingRegisterPassenger/V1/CompleteRegisterPassenger.cs index 12179a1..d9474dc 100644 --- a/src/Services/Passenger/src/Passenger/Passengers/Features/CompletingRegisterPassenger/V1/CompleteRegisterPassenger.cs +++ b/src/Services/Passenger/src/Passenger/Passengers/Features/CompletingRegisterPassenger/V1/CompleteRegisterPassenger.cs @@ -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, @@ -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(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() + .ProducesProblem(StatusCodes.Status400BadRequest) + .WithSummary("Complete Register Passenger") + .WithDescription("Complete Register Passenger") + .WithOpenApi() + .HasApiVersion(1.0); + + return builder; + } +} + internal class CompleteRegisterPassengerValidator : AbstractValidator { public CompleteRegisterPassengerValidator() @@ -35,9 +77,8 @@ internal class CompleteRegisterPassengerValidator : AbstractValidator +internal class CompleteRegisterPassengerCommandHandler : ICommandHandler { private readonly IMapper _mapper; private readonly PassengerDbContext _passengerDbContext; diff --git a/src/Services/Passenger/src/Passenger/Passengers/Features/CompletingRegisterPassenger/V1/CompleteRegisterPassengerEndpoint.cs b/src/Services/Passenger/src/Passenger/Passengers/Features/CompletingRegisterPassenger/V1/CompleteRegisterPassengerEndpoint.cs deleted file mode 100644 index dfe1d92..0000000 --- a/src/Services/Passenger/src/Passenger/Passengers/Features/CompletingRegisterPassenger/V1/CompleteRegisterPassengerEndpoint.cs +++ /dev/null @@ -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() - .ProducesProblem(StatusCodes.Status400BadRequest) - .WithOpenApi(operation => new OpenApiOperation(operation) { Summary = "Complete Register Passenger", Description = "Complete Register Passenger" }) - .HasApiVersion(1.0); - - return builder; - } - - private async Task CompleteRegisterPassenger(CompleteRegisterPassengerRequestDto request, IMapper mapper, - IMediator mediator, CancellationToken cancellationToken) - { - var command = mapper.Map(request); - - var result = await mediator.Send(command, cancellationToken); - - var response = new CompleteRegisterPassengerResponseDto(result?.PassengerDto); - - return Results.Ok(response); - } -} diff --git a/src/Services/Passenger/src/Passenger/Passengers/Features/CompletingRegisterPassenger/V1/PassengerRegistrationCompletedDomainEvent.cs b/src/Services/Passenger/src/Passenger/Passengers/Features/CompletingRegisterPassenger/V1/PassengerRegistrationCompletedDomainEvent.cs deleted file mode 100644 index 0e95529..0000000 --- a/src/Services/Passenger/src/Passenger/Passengers/Features/CompletingRegisterPassenger/V1/PassengerRegistrationCompletedDomainEvent.cs +++ /dev/null @@ -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; diff --git a/src/Services/Passenger/src/Passenger/Passengers/Features/GettingPassengerById/Queries/V1/GetPassengerById.cs b/src/Services/Passenger/src/Passenger/Passengers/Features/GettingPassengerById/Queries/V1/GetPassengerById.cs index 3390841..323f2e2 100644 --- a/src/Services/Passenger/src/Passenger/Passengers/Features/GettingPassengerById/Queries/V1/GetPassengerById.cs +++ b/src/Services/Passenger/src/Passenger/Passengers/Features/GettingPassengerById/Queries/V1/GetPassengerById.cs @@ -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; public record GetPassengerByIdResult(PassengerDto PassengerDto); -internal class GetPassengerByIdValidator: AbstractValidator +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() + .ProducesProblem(StatusCodes.Status400BadRequest) + .WithSummary("Get Passenger By Id") + .WithDescription("Get Passenger By Id") + .WithOpenApi() + .HasApiVersion(1.0); + + return builder; + } +} + +internal class GetPassengerByIdValidator : AbstractValidator { public GetPassengerByIdValidator() { diff --git a/src/Services/Passenger/src/Passenger/Passengers/Features/GettingPassengerById/Queries/V1/GetPassengerByIdEndpoint.cs b/src/Services/Passenger/src/Passenger/Passengers/Features/GettingPassengerById/Queries/V1/GetPassengerByIdEndpoint.cs deleted file mode 100644 index e4f7678..0000000 --- a/src/Services/Passenger/src/Passenger/Passengers/Features/GettingPassengerById/Queries/V1/GetPassengerByIdEndpoint.cs +++ /dev/null @@ -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() - .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 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); - } -} diff --git a/src/Services/Passenger/tests/PerformanceTest/.openapi-generator-ignore b/src/Services/Passenger/tests/PerformanceTest/.openapi-generator-ignore new file mode 100644 index 0000000..7484ee5 --- /dev/null +++ b/src/Services/Passenger/tests/PerformanceTest/.openapi-generator-ignore @@ -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 diff --git a/src/Services/Passenger/tests/PerformanceTest/.openapi-generator/FILES b/src/Services/Passenger/tests/PerformanceTest/.openapi-generator/FILES new file mode 100644 index 0000000..297b5aa --- /dev/null +++ b/src/Services/Passenger/tests/PerformanceTest/.openapi-generator/FILES @@ -0,0 +1,3 @@ +.openapi-generator-ignore +README.md +script.js diff --git a/src/Services/Passenger/tests/PerformanceTest/.openapi-generator/VERSION b/src/Services/Passenger/tests/PerformanceTest/.openapi-generator/VERSION new file mode 100644 index 0000000..ba8a874 --- /dev/null +++ b/src/Services/Passenger/tests/PerformanceTest/.openapi-generator/VERSION @@ -0,0 +1 @@ +6.6.0-SNAPSHOT \ No newline at end of file diff --git a/src/Services/Passenger/tests/PerformanceTest/README.md b/src/Services/Passenger/tests/PerformanceTest/README.md new file mode 100644 index 0000000..64a7521 --- /dev/null +++ b/src/Services/Passenger/tests/PerformanceTest/README.md @@ -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). diff --git a/src/Services/Passenger/tests/PerformanceTest/script.js b/src/Services/Passenger/tests/PerformanceTest/script.js new file mode 100644 index 0000000..e99a85f --- /dev/null +++ b/src/Services/Passenger/tests/PerformanceTest/script.js @@ -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 + }); + } + }); + +}