add alpha implementation of route search
This commit is contained in:
parent
806a139d3d
commit
7d3ad666c6
14
AutobusApi.Api/Controllers/RouteSearchController.cs
Normal file
14
AutobusApi.Api/Controllers/RouteSearchController.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using AutobusApi.Application.RouteSearch;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace AutobusApi.Api.Controllers;
|
||||
|
||||
[Route("search")]
|
||||
public class RouteSearchController : BaseController
|
||||
{
|
||||
[HttpPost]
|
||||
public async Task<List<RouteWithTransfersDto>> Search([FromBody] RouteSearchQuery query, CancellationToken cancellationToken)
|
||||
{
|
||||
return await Mediator.Send(query, cancellationToken);
|
||||
}
|
||||
}
|
18
AutobusApi.Api/OpenApi/TimeSpanSchemaFilter.cs
Normal file
18
AutobusApi.Api/OpenApi/TimeSpanSchemaFilter.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
|
||||
namespace AutobusApi.Api.OpenApi;
|
||||
|
||||
public class TimeSpanSchemaFilter : ISchemaFilter
|
||||
{
|
||||
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
|
||||
{
|
||||
if (context.Type == typeof(TimeSpan))
|
||||
{
|
||||
schema.Type = "string";
|
||||
schema.Format = "time-span";
|
||||
schema.Pattern = @"^\d{2}:\d{2}:\d{2}\.\d{3}$";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,8 @@ using AutobusApi.Application;
|
||||
using AutobusApi.Api.Middlewares;
|
||||
using AutoubsApi.Infrastructure.Data;
|
||||
using AutobusApi.Infrastructure.Identity;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using AutobusApi.Api.OpenApi;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
@ -11,7 +13,33 @@ builder.Services.AddApplication();
|
||||
|
||||
builder.Services.AddControllers();
|
||||
|
||||
builder.Services.AddSwaggerGen();
|
||||
builder.Services.AddSwaggerGen(options =>
|
||||
{
|
||||
options.SchemaFilter<TimeSpanSchemaFilter>();
|
||||
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
|
||||
{
|
||||
Scheme = "Bearer",
|
||||
BearerFormat = "Jwt",
|
||||
In = ParameterLocation.Header,
|
||||
Name = "Authorization",
|
||||
Description = "Bearer Authentication With Jwt",
|
||||
Type = SecuritySchemeType.Http
|
||||
});
|
||||
options.AddSecurityRequirement(new OpenApiSecurityRequirement
|
||||
{
|
||||
{
|
||||
new OpenApiSecurityScheme
|
||||
{
|
||||
Reference = new OpenApiReference
|
||||
{
|
||||
Id = "Bearer",
|
||||
Type = ReferenceType.SecurityScheme
|
||||
}
|
||||
},
|
||||
new List<string>()
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
builder.Services.AddTransient<GlobalExceptionHandlerMiddleware>();
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
"Issuer": "",
|
||||
"Audience": "",
|
||||
"IssuerSigningKey": "a2c98dec80787a4e85ffb5bcbc24f7e4cc014d8a4fe43e9520480a50759164bc",
|
||||
"AccessTokenValidityInMinutes": "5",
|
||||
"AccessTokenValidityInMinutes": "60",
|
||||
"RefreshTokenValidityInDays": "15",
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
<Version>7.0.14</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.3.7" />
|
||||
<PackageReference Include="QuikGraph" Version="2.5.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
22
AutobusApi.Application/Common/Behaviours/LoggingBehaviour.cs
Normal file
22
AutobusApi.Application/Common/Behaviours/LoggingBehaviour.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using MediatR.Pipeline;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace AutobusApi.Application.Common.Behaviours;
|
||||
|
||||
public class LoggingBehaviour<TRequest> : IRequestPreProcessor<TRequest> where TRequest : notnull
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public LoggingBehaviour(ILogger<TRequest> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task Process(TRequest request, CancellationToken cancellationToken)
|
||||
{
|
||||
var requestName = typeof(TRequest).Name;
|
||||
|
||||
_logger.LogInformation("Request: {Name} {@UserId} {@UserName} {@Request}",
|
||||
requestName, request);
|
||||
}
|
||||
}
|
@ -13,9 +13,9 @@ public class RegisterCommandHandler : IRequestHandler<RegisterCommand>
|
||||
}
|
||||
|
||||
public async Task Handle(
|
||||
RegisterCommand command,
|
||||
RegisterCommand request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
await _identityService.RegisterAsync(command.Email, command.Password, cancellationToken);
|
||||
await _identityService.RegisterAsync(request.Email, request.Password, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
@ -14,9 +14,9 @@ public class RenewAccessTokenCommandHandler : IRequestHandler<RenewAccessTokenCo
|
||||
}
|
||||
|
||||
public async Task<TokensModel> Handle(
|
||||
RenewAccessTokenCommand command,
|
||||
RenewAccessTokenCommand request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return await _identityService.RenewAccessTokenAsync(command.RefreshToken, cancellationToken);
|
||||
return await _identityService.RenewAccessTokenAsync(request.RefreshToken, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
@ -13,9 +13,9 @@ public class RevokeRefreshTokenCommandHandler : IRequestHandler<RevokeRefreshTok
|
||||
}
|
||||
|
||||
public async Task Handle(
|
||||
RevokeRefreshTokenCommand command,
|
||||
RevokeRefreshTokenCommand request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
await _identityService.RevokeRefreshTokenAsync(command.RefreshToken, cancellationToken);
|
||||
await _identityService.RevokeRefreshTokenAsync(request.RefreshToken, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
@ -14,9 +14,9 @@ public class LoginQueryHandler : IRequestHandler<LoginQuery, TokensModel>
|
||||
}
|
||||
|
||||
public async Task<TokensModel> Handle(
|
||||
LoginQuery query,
|
||||
LoginQuery request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return await _identityService.LoginAsync(query.Email, query.Password, cancellationToken);
|
||||
return await _identityService.LoginAsync(request.Email, request.Password, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,75 @@
|
||||
using AutobusApi.Domain.Entities;
|
||||
|
||||
namespace AutobusApi.Application.RouteSearch;
|
||||
|
||||
public class EnrollmentAddressVertex
|
||||
{
|
||||
public EnrollmentAddressVertex(VehicleEnrollment vehicleEnrollment, Address address)
|
||||
{
|
||||
VehicleEnrollment = vehicleEnrollment;
|
||||
Address = address;
|
||||
}
|
||||
|
||||
public VehicleEnrollment VehicleEnrollment { get; set; }
|
||||
|
||||
public Address Address { get; set; }
|
||||
|
||||
public DateTime GetDepartureDateTimeUtc()
|
||||
{
|
||||
DateTime result = VehicleEnrollment.DepartureDateTimeUtc;
|
||||
|
||||
var rouetAddressDetails = VehicleEnrollment.RouteAddressDetails.OrderBy(rad => rad.RouteAddress.Order);
|
||||
rouetAddressDetails.First().CurrentAddressStopTime = TimeSpan.Zero;
|
||||
|
||||
foreach (var rad in rouetAddressDetails)
|
||||
{
|
||||
result += rad.CurrentAddressStopTime;
|
||||
|
||||
if (rad.RouteAddress.Address.Id == Address.Id)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
result += rad.TimeToNextAddress;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public DateTime GetArrivalDateTimeUtc()
|
||||
{
|
||||
DateTime result = VehicleEnrollment.DepartureDateTimeUtc;
|
||||
|
||||
var rouetAddressDetails = VehicleEnrollment.RouteAddressDetails.OrderBy(rad => rad.RouteAddress.Order);
|
||||
rouetAddressDetails.First().CurrentAddressStopTime = TimeSpan.Zero;
|
||||
|
||||
foreach (var rad in rouetAddressDetails)
|
||||
{
|
||||
if (rad.RouteAddress.Address.Id == Address.Id)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
result += rad.CurrentAddressStopTime;
|
||||
result += rad.TimeToNextAddress;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (obj == null || !(obj is EnrollmentAddressVertex))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
EnrollmentAddressVertex other = (EnrollmentAddressVertex)obj;
|
||||
return this.Address.Id == other.Address.Id;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Address.Id.GetHashCode();
|
||||
}
|
||||
}
|
18
AutobusApi.Application/RouteSearch/RouteSearchQuery.cs
Normal file
18
AutobusApi.Application/RouteSearch/RouteSearchQuery.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using MediatR;
|
||||
|
||||
namespace AutobusApi.Application.RouteSearch;
|
||||
|
||||
public record RouteSearchQuery : IRequest<List<RouteWithTransfersDto>>
|
||||
{
|
||||
public required int DepartureAddressId { get; set; }
|
||||
|
||||
public required int ArrivalAddressId { get; set; }
|
||||
|
||||
public required DateOnly DepartureDate { get; set; }
|
||||
|
||||
public required TimeSpan MinTransferTime { get; set; } = TimeSpan.FromMinutes(15);
|
||||
|
||||
public required TimeSpan MaxTransferTime { get; set; } = TimeSpan.FromHours(12);
|
||||
|
||||
public required double MaxTransferDistanceInMeters { get; set; } = 1000;
|
||||
}
|
220
AutobusApi.Application/RouteSearch/RouteSearchQueryHandler.cs
Normal file
220
AutobusApi.Application/RouteSearch/RouteSearchQueryHandler.cs
Normal file
@ -0,0 +1,220 @@
|
||||
using AutobusApi.Application.Common.Exceptions;
|
||||
using AutobusApi.Application.Common.Interfaces;
|
||||
using AutoMapper;
|
||||
using AutoMapper.QueryableExtensions;
|
||||
using FluentValidation.Results;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using QuikGraph;
|
||||
|
||||
namespace AutobusApi.Application.RouteSearch;
|
||||
|
||||
public class RouteSearchQueryHandler : IRequestHandler<RouteSearchQuery, List<RouteWithTransfersDto>>
|
||||
{
|
||||
private readonly IApplicationDbContext _dbContext;
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
public RouteSearchQueryHandler(
|
||||
IApplicationDbContext dbContext,
|
||||
IMapper mapper)
|
||||
{
|
||||
_dbContext = dbContext;
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
public async Task<List<RouteWithTransfersDto>> Handle(
|
||||
RouteSearchQuery request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var departureEnrollmentAddressVertex = await GetEnrollmentAddressVertexAsync(request.DepartureAddressId);
|
||||
var arrivalEnrollmentAddressVertex = await GetEnrollmentAddressVertexAsync(request.ArrivalAddressId);
|
||||
|
||||
var graph = await InitializeGraphAsync(request.DepartureDate, cancellationToken);
|
||||
|
||||
var paths = FindAllPaths(graph, departureEnrollmentAddressVertex, arrivalEnrollmentAddressVertex,
|
||||
request.MinTransferTime, request.MaxTransferTime, request.MaxTransferDistanceInMeters);
|
||||
|
||||
return paths.AsQueryable().ProjectTo<RouteWithTransfersDto>(_mapper.ConfigurationProvider).ToList();
|
||||
}
|
||||
|
||||
private async Task<AdjacencyGraph<EnrollmentAddressVertex, SEquatableEdge<EnrollmentAddressVertex>>> InitializeGraphAsync(
|
||||
DateOnly departureDate,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var graph = new AdjacencyGraph<EnrollmentAddressVertex, SEquatableEdge<EnrollmentAddressVertex>>();
|
||||
|
||||
var vehicleEnrollmentsArray = await _dbContext.VehicleEnrollments
|
||||
.Include(ve => ve.Route)
|
||||
.ThenInclude(r => r.RouteAddresses)
|
||||
.ThenInclude(ra => ra.Address)
|
||||
.ThenInclude(a => a.City)
|
||||
.ThenInclude(c => c.Region)
|
||||
.ThenInclude(r => r.Country)
|
||||
.Include(ve => ve.Route)
|
||||
.ThenInclude(r => r.RouteAddresses)
|
||||
.ThenInclude(ra => ra.RouteAddressDetails)
|
||||
.Include(ve => ve.Tickets)
|
||||
.ThenInclude(t => t.TicketGroup)
|
||||
.Include(ve => ve.Vehicle)
|
||||
.ThenInclude(v => v.Company)
|
||||
.Where(ve =>
|
||||
DateOnly.FromDateTime(ve.DepartureDateTimeUtc) >= departureDate &&
|
||||
DateOnly.FromDateTime(ve.DepartureDateTimeUtc) < departureDate.AddDays(1))
|
||||
.ToArrayAsync(cancellationToken);
|
||||
|
||||
|
||||
foreach (var enrollment in vehicleEnrollmentsArray)
|
||||
{
|
||||
var routeAddressesArray = enrollment.Route.RouteAddresses.OrderBy(ra => ra.Order).ToArray();
|
||||
|
||||
var previousVertex = new EnrollmentAddressVertex(enrollment, routeAddressesArray[0].Address);
|
||||
graph.AddVertex(previousVertex);
|
||||
|
||||
for (int i = 1; i < routeAddressesArray.Length; i++)
|
||||
{
|
||||
var currentVertex = new EnrollmentAddressVertex(enrollment, routeAddressesArray[i].Address);
|
||||
graph.AddVertex(currentVertex);
|
||||
graph.AddEdge(new SEquatableEdge<EnrollmentAddressVertex>(previousVertex, currentVertex));
|
||||
|
||||
var vertexFromDifferentRoute = graph.Vertices.FirstOrDefault(v => v.Address.Id == currentVertex.Address.Id);
|
||||
|
||||
if (vertexFromDifferentRoute != null)
|
||||
{
|
||||
graph.AddEdge(new SEquatableEdge<EnrollmentAddressVertex>(currentVertex, vertexFromDifferentRoute));
|
||||
}
|
||||
|
||||
previousVertex = currentVertex;
|
||||
}
|
||||
}
|
||||
|
||||
return graph;
|
||||
}
|
||||
|
||||
private async Task<EnrollmentAddressVertex> GetEnrollmentAddressVertexAsync(int addressId)
|
||||
{
|
||||
var routeAddress = await _dbContext.VehicleEnrollments
|
||||
.Include(ve => ve.Route)
|
||||
.ThenInclude(r => r.RouteAddresses)
|
||||
.ThenInclude(ra => ra.Address)
|
||||
.ThenInclude(a => a.City)
|
||||
.ThenInclude(c => c.Region)
|
||||
.ThenInclude(r => r.Country)
|
||||
.Include(ve => ve.Route)
|
||||
.ThenInclude(r => r.RouteAddresses)
|
||||
.ThenInclude(ra => ra.RouteAddressDetails)
|
||||
.Include(ve => ve.Tickets)
|
||||
.ThenInclude(t => t.TicketGroup)
|
||||
.Include(ve => ve.Vehicle)
|
||||
.ThenInclude(v => v.Company)
|
||||
.SelectMany(ve => ve.Route.RouteAddresses)
|
||||
.FirstOrDefaultAsync(ra => ra.Address.Id == addressId);
|
||||
|
||||
if (routeAddress == null)
|
||||
{
|
||||
throw new ValidationException(new ValidationFailure[] { new ValidationFailure("AddressId", "Departure or Arrival Address with given Id is not found") });
|
||||
}
|
||||
|
||||
return new EnrollmentAddressVertex(null, routeAddress.Address);
|
||||
}
|
||||
|
||||
private List<List<EnrollmentAddressVertex>> FindAllPaths(
|
||||
AdjacencyGraph<EnrollmentAddressVertex, SEquatableEdge<EnrollmentAddressVertex>> graph,
|
||||
EnrollmentAddressVertex startVertex,
|
||||
EnrollmentAddressVertex endVertex,
|
||||
TimeSpan minTransferTime,
|
||||
TimeSpan maxTransferTime,
|
||||
double maxTransferDistanceInMeters)
|
||||
{
|
||||
var paths = new List<List<EnrollmentAddressVertex>>();
|
||||
var visited = new HashSet<EnrollmentAddressVertex>();
|
||||
var currentPath = new List<EnrollmentAddressVertex>();
|
||||
|
||||
FindAllPathsDFS(graph, startVertex, endVertex, visited, currentPath, paths, minTransferTime,
|
||||
maxTransferTime, maxTransferDistanceInMeters);
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
private void FindAllPathsDFS(
|
||||
AdjacencyGraph<EnrollmentAddressVertex, SEquatableEdge<EnrollmentAddressVertex>> graph,
|
||||
EnrollmentAddressVertex current,
|
||||
EnrollmentAddressVertex endVertex,
|
||||
HashSet<EnrollmentAddressVertex> visited,
|
||||
List<EnrollmentAddressVertex> currentPath,
|
||||
List<List<EnrollmentAddressVertex>> paths,
|
||||
TimeSpan minTransferTime,
|
||||
TimeSpan maxTransferTime,
|
||||
double maxTransferDistanceInMeters)
|
||||
{
|
||||
visited.Add(current);
|
||||
currentPath.Add(current);
|
||||
|
||||
if (current.Equals(endVertex))
|
||||
{
|
||||
paths.Add(new List<EnrollmentAddressVertex>(currentPath));
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var edge in graph.OutEdges(current))
|
||||
{
|
||||
if (!visited.Contains(edge.Target))
|
||||
{
|
||||
if (current.VehicleEnrollment == null)
|
||||
{
|
||||
current.VehicleEnrollment = edge.Target.VehicleEnrollment;
|
||||
}
|
||||
|
||||
// If transfering to other vehicle enrollment
|
||||
if (current.VehicleEnrollment.Id != edge.Target.VehicleEnrollment.Id)
|
||||
{
|
||||
// Skip paths where there is a singular vertex from any Vehicle Enrollment
|
||||
// A - A - A - _B_ <- - C - C
|
||||
|
||||
var threeLast = currentPath.TakeLast(3).ToArray();
|
||||
|
||||
if (currentPath.Count >= 4 &&
|
||||
threeLast[0].VehicleEnrollment.Id != threeLast[1].VehicleEnrollment.Id &&
|
||||
threeLast[1].VehicleEnrollment.Id != threeLast[2].VehicleEnrollment.Id)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Filter by amount of free time between enrollment 1 arrives and enrollment 2 departures
|
||||
|
||||
var freeTime = edge.Target.GetDepartureDateTimeUtc() - current.GetArrivalDateTimeUtc();
|
||||
|
||||
if (freeTime < minTransferTime ||
|
||||
freeTime > maxTransferTime)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Filter by distance between transfer locations
|
||||
|
||||
var currentLocation = current.Address.Location;
|
||||
var targetLocation = edge.Target.Address.Location;
|
||||
|
||||
const double metersInDegree = 111_111.111;
|
||||
|
||||
var distanceInDegrees = Math.Sqrt(
|
||||
Math.Pow(currentLocation.Latitude - targetLocation.Latitude, 2) +
|
||||
Math.Pow(currentLocation.Longitude - targetLocation.Longitude, 2));
|
||||
|
||||
var distanceInMeters = distanceInDegrees * metersInDegree;
|
||||
|
||||
if (distanceInMeters > maxTransferDistanceInMeters)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
FindAllPathsDFS(graph, edge.Target, endVertex, visited, currentPath, paths,
|
||||
minTransferTime, maxTransferTime, maxTransferDistanceInMeters);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
visited.Remove(current);
|
||||
currentPath.RemoveAt(currentPath.Count - 1);
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
using FluentValidation;
|
||||
|
||||
namespace AutobusApi.Application.RouteSearch;
|
||||
|
||||
public class RouteSearchQueryValidator : AbstractValidator<RouteSearchQuery>
|
||||
{
|
||||
public RouteSearchQueryValidator()
|
||||
{
|
||||
// RuleFor(v => v.Email)
|
||||
// .NotEmpty().WithMessage("Email address is required.")
|
||||
// .EmailAddress().WithMessage("Email address is invalid.");
|
||||
}
|
||||
}
|
143
AutobusApi.Application/RouteSearch/RouteWithTransfersDto.cs
Normal file
143
AutobusApi.Application/RouteSearch/RouteWithTransfersDto.cs
Normal file
@ -0,0 +1,143 @@
|
||||
using AutobusApi.Application.Common.Mappings;
|
||||
using AutoMapper;
|
||||
|
||||
namespace AutobusApi.Application.RouteSearch;
|
||||
|
||||
public class RouteWithTransfersDto : IMapFrom<List<EnrollmentAddressVertex>>
|
||||
{
|
||||
public List<SubRouteDto> Routes { get; set; } = null!;
|
||||
|
||||
public TimeSpan TotalTravelTime { get; set; }
|
||||
|
||||
public double TotalCost { get; set; }
|
||||
|
||||
// public int NumberOfTransfers { get; set; }
|
||||
|
||||
public DateTime DeparuteDateTime { get; set; }
|
||||
|
||||
public DateTime ArrivalDateTime { get; set; }
|
||||
|
||||
public void Mapping(Profile profile)
|
||||
{
|
||||
profile.CreateMap<List<EnrollmentAddressVertex>, RouteWithTransfersDto>()
|
||||
.ForMember(
|
||||
d => d.TotalCost,
|
||||
opt => opt.MapFrom(
|
||||
s => s
|
||||
.SkipLast(1)
|
||||
.Select(eav => eav.VehicleEnrollment)
|
||||
.SelectMany(ve => ve.RouteAddressDetails)
|
||||
.Select(rad => rad.CostToNextAddress)
|
||||
.Aggregate(0.0, (sum, val) => sum + val)
|
||||
)
|
||||
)
|
||||
.ForMember(
|
||||
d => d.TotalTravelTime,
|
||||
opt => opt.MapFrom(
|
||||
s =>
|
||||
s.Last().VehicleEnrollment.GetArrivalDateTimeUtc(s.Last().Address.Id) -
|
||||
s.First().VehicleEnrollment.GetDepartureDateTimeUtc(s.First().Address.Id)
|
||||
)
|
||||
)
|
||||
.ForMember(d => d.DeparuteDateTime, opt => opt.MapFrom(s => s.First().VehicleEnrollment.GetDepartureDateTimeUtc(s.First().Address.Id)))
|
||||
.ForMember(d => d.ArrivalDateTime, opt => opt.MapFrom(s => s.Last().VehicleEnrollment.GetArrivalDateTimeUtc(s.Last().Address.Id)))
|
||||
.ForMember(d => d.Routes, opt => opt.MapFrom(s => CreateRoutes(s)));
|
||||
|
||||
}
|
||||
|
||||
List<SubRouteDto> CreateRoutes(List<EnrollmentAddressVertex> vertices)
|
||||
{
|
||||
var result = new List<SubRouteDto>();
|
||||
|
||||
var groupedByVehicle = vertices.GroupBy(ev => ev.VehicleEnrollment);
|
||||
|
||||
int order = 1;
|
||||
foreach (var group in groupedByVehicle)
|
||||
{
|
||||
|
||||
var subRouteDto = new SubRouteDto
|
||||
{
|
||||
Stations = new List<StationDto>(),
|
||||
VehicleType = "Unknown",//group.Key.Vehicle.Type, // Replace with the actual property to fetch VehicleType
|
||||
CompanyName = group.Key.Vehicle.Company.Name, // Replace with the actual property to fetch CompanyName
|
||||
CompanyId = group.Key.Vehicle.Company.Id, // Replace with the actual property to fetch CompanyId
|
||||
Order = order
|
||||
// Other properties initialization
|
||||
};
|
||||
|
||||
order++;
|
||||
|
||||
foreach (var enrollmentVertex in group)
|
||||
{
|
||||
var stationDto = new StationDto
|
||||
{
|
||||
AddressId = enrollmentVertex.Address.Id,
|
||||
AddressName = enrollmentVertex.Address.Name,
|
||||
CityId = enrollmentVertex.Address.CityId,
|
||||
CityName = enrollmentVertex.Address.City.Name,
|
||||
RegionId = enrollmentVertex.Address.City.RegionId,
|
||||
RegionName = enrollmentVertex.Address.City.Region.Name,
|
||||
CountryId = enrollmentVertex.Address.City.Region.CountryId,
|
||||
CountryName = enrollmentVertex.Address.City.Region.Country.Name,
|
||||
DeparureTime = enrollmentVertex.GetDepartureDateTimeUtc(),
|
||||
ArrivalTime = enrollmentVertex.GetArrivalDateTimeUtc(),
|
||||
StopTime = enrollmentVertex.GetDepartureDateTimeUtc() - enrollmentVertex.GetArrivalDateTimeUtc(),
|
||||
Latitude = enrollmentVertex.Address.Location.Latitude,
|
||||
Longitude = enrollmentVertex.Address.Location.Longitude
|
||||
};
|
||||
|
||||
subRouteDto.Stations.Add(stationDto);
|
||||
}
|
||||
|
||||
result.Add(subRouteDto);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public class SubRouteDto
|
||||
{
|
||||
public List<StationDto> Stations { get; set; } = null!;
|
||||
|
||||
public string VehicleType { get; set; } = null!;
|
||||
|
||||
public string CompanyName { get; set; } = null!;
|
||||
|
||||
public int CompanyId { get; set; }
|
||||
|
||||
public int TicketsAvailable { get; set; }
|
||||
|
||||
public int TicketsBought { get; set; }
|
||||
|
||||
public int Order { get; set; }
|
||||
}
|
||||
|
||||
public class StationDto : IMapFrom<EnrollmentAddressVertex>
|
||||
{
|
||||
public int AddressId { get; set; }
|
||||
|
||||
public string AddressName { get; set; } = null!;
|
||||
|
||||
public int CityId { get; set; }
|
||||
|
||||
public string CityName { get; set; } = null!;
|
||||
|
||||
public int RegionId { get; set; }
|
||||
|
||||
public string RegionName { get; set; } = null!;
|
||||
|
||||
public int CountryId { get; set; }
|
||||
|
||||
public string CountryName { get; set; } = null!;
|
||||
|
||||
public DateTime DeparureTime { get; set; }
|
||||
|
||||
public DateTime ArrivalTime { get; set; }
|
||||
|
||||
public TimeSpan StopTime { get; set; }
|
||||
|
||||
public double Latitude { get; set; }
|
||||
|
||||
public double Longitude { get; set; }
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
using AutobusApi.Domain.Entities;
|
||||
using AutobusApi.Domain.Enums;
|
||||
using AutobusApi.Infrastructure.Data.Entities;
|
||||
using AutobusApi.Infrastructure.Identity;
|
||||
using AutoubsApi.Infrastructure.Data;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
@ -46,6 +48,454 @@ public static class DbInitializer
|
||||
private static void InitializeDomain(ApplicationDbContext dbContext)
|
||||
{
|
||||
|
||||
var country1 = new Country()
|
||||
{
|
||||
Name = "Country A"
|
||||
};
|
||||
|
||||
dbContext.Countries.AddRange(new[] { country1 });
|
||||
dbContext.SaveChanges();
|
||||
|
||||
var region1 = new Region()
|
||||
{
|
||||
Name = "Region A",
|
||||
Country = country1
|
||||
};
|
||||
|
||||
dbContext.Regions.AddRange(new[] { region1 });
|
||||
dbContext.SaveChanges();
|
||||
|
||||
var city1 = new City()
|
||||
{
|
||||
Name = "City A",
|
||||
Region = region1
|
||||
};
|
||||
|
||||
var city2 = new City()
|
||||
{
|
||||
Name = "City B",
|
||||
Region = region1
|
||||
};
|
||||
|
||||
var city3 = new City()
|
||||
{
|
||||
Name = "City C",
|
||||
Region = region1
|
||||
};
|
||||
|
||||
var city4 = new City()
|
||||
{
|
||||
Name = "City D",
|
||||
Region = region1
|
||||
};
|
||||
|
||||
var city5 = new City()
|
||||
{
|
||||
Name = "City E",
|
||||
Region = region1
|
||||
};
|
||||
|
||||
var city6 = new City()
|
||||
{
|
||||
Name = "City F",
|
||||
Region = region1
|
||||
};
|
||||
|
||||
var city7 = new City()
|
||||
{
|
||||
Name = "City G",
|
||||
Region = region1
|
||||
};
|
||||
|
||||
var city8 = new City()
|
||||
{
|
||||
Name = "City H",
|
||||
Region = region1
|
||||
};
|
||||
|
||||
var city9 = new City()
|
||||
{
|
||||
Name = "City I",
|
||||
Region = region1
|
||||
};
|
||||
|
||||
var city10 = new City()
|
||||
{
|
||||
Name = "City J",
|
||||
Region = region1
|
||||
};
|
||||
|
||||
dbContext.Cities.AddRange(new[] { city1, city2, city3, city4, city5, city6, city7, city8, city9, city10 });
|
||||
dbContext.SaveChanges();
|
||||
|
||||
var address1 = new Address()
|
||||
{
|
||||
Name = "address A",
|
||||
Location = new Coordinates(11, 11),
|
||||
VehicleType = VehicleType.Bus,
|
||||
City = city1
|
||||
};
|
||||
|
||||
var address2 = new Address()
|
||||
{
|
||||
Name = "address B",
|
||||
Location = new Coordinates(22, 22),
|
||||
VehicleType = VehicleType.Bus,
|
||||
City = city2
|
||||
};
|
||||
|
||||
var address3 = new Address()
|
||||
{
|
||||
Name = "address C",
|
||||
Location = new Coordinates(33, 33),
|
||||
VehicleType = VehicleType.Bus,
|
||||
City = city3
|
||||
};
|
||||
|
||||
var address4 = new Address()
|
||||
{
|
||||
Name = "address E",
|
||||
Location = new Coordinates(44, 44),
|
||||
VehicleType = VehicleType.Bus,
|
||||
City = city4
|
||||
};
|
||||
|
||||
var address5 = new Address()
|
||||
{
|
||||
Name = "address F",
|
||||
Location = new Coordinates(55, 55),
|
||||
VehicleType = VehicleType.Bus,
|
||||
City = city5
|
||||
};
|
||||
|
||||
var address6 = new Address()
|
||||
{
|
||||
Name = "address G",
|
||||
Location = new Coordinates(66, 66),
|
||||
VehicleType = VehicleType.Bus,
|
||||
City = city6
|
||||
};
|
||||
|
||||
var address7 = new Address()
|
||||
{
|
||||
Name = "address H",
|
||||
Location = new Coordinates(77, 77),
|
||||
VehicleType = VehicleType.Bus,
|
||||
City = city7
|
||||
};
|
||||
|
||||
var address8 = new Address()
|
||||
{
|
||||
Name = "address I",
|
||||
Location = new Coordinates(88, 88),
|
||||
VehicleType = VehicleType.Bus,
|
||||
City = city8
|
||||
};
|
||||
|
||||
var address9 = new Address()
|
||||
{
|
||||
Name = "address J",
|
||||
Location = new Coordinates(99, 99),
|
||||
VehicleType = VehicleType.Bus,
|
||||
City = city9
|
||||
};
|
||||
|
||||
var address10 = new Address()
|
||||
{
|
||||
Name = "address K",
|
||||
Location = new Coordinates(110, 110),
|
||||
VehicleType = VehicleType.Bus,
|
||||
City = city10
|
||||
};
|
||||
|
||||
dbContext.Addresses.AddRange(new[] { address1, address2, address3, address4, address5, address6, address7, address8, address9, address10 });
|
||||
dbContext.SaveChanges();
|
||||
|
||||
var route1 = new Route()
|
||||
{
|
||||
RouteAddresses = new RouteAddress[]
|
||||
{
|
||||
new RouteAddress
|
||||
{
|
||||
Order = 1,
|
||||
Address = address1
|
||||
},
|
||||
new RouteAddress
|
||||
{
|
||||
Order = 2,
|
||||
Address = address2
|
||||
},
|
||||
new RouteAddress
|
||||
{
|
||||
Order = 3,
|
||||
Address = address3
|
||||
},
|
||||
new RouteAddress
|
||||
{
|
||||
Order = 4,
|
||||
Address = address4
|
||||
},
|
||||
new RouteAddress
|
||||
{
|
||||
Order = 5,
|
||||
Address = address5
|
||||
},
|
||||
new RouteAddress
|
||||
{
|
||||
Order = 6,
|
||||
Address = address8
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var route2 = new Route()
|
||||
{
|
||||
RouteAddresses = new RouteAddress[]
|
||||
{
|
||||
new RouteAddress
|
||||
{
|
||||
Order = 1,
|
||||
Address = address2
|
||||
},
|
||||
new RouteAddress
|
||||
{
|
||||
Order = 2,
|
||||
Address = address3
|
||||
},
|
||||
new RouteAddress
|
||||
{
|
||||
Order = 3,
|
||||
Address = address6
|
||||
},
|
||||
new RouteAddress
|
||||
{
|
||||
Order = 4,
|
||||
Address = address7
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var route3 = new Route()
|
||||
{
|
||||
RouteAddresses = new RouteAddress[]
|
||||
{
|
||||
new RouteAddress
|
||||
{
|
||||
Order = 1,
|
||||
Address = address6
|
||||
},
|
||||
new RouteAddress
|
||||
{
|
||||
Order = 2,
|
||||
Address = address8
|
||||
},
|
||||
new RouteAddress
|
||||
{
|
||||
Order = 3,
|
||||
Address = address9
|
||||
},
|
||||
new RouteAddress
|
||||
{
|
||||
Order = 4,
|
||||
Address = address10
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
dbContext.Routes.AddRange(new[] { route1, route2, route3 });
|
||||
dbContext.SaveChanges();
|
||||
|
||||
var company1 = new Company
|
||||
{
|
||||
Name = "Smartlines",
|
||||
LegalAddress = "1234 Spectrum Avenue, Metroville, XYZ 98765, United States",
|
||||
ContactEmail = "support@smartlines.com",
|
||||
ContactPhoneNumber = "+00000000000"
|
||||
};
|
||||
|
||||
var company2 = new Company
|
||||
{
|
||||
Name = "ecobus",
|
||||
LegalAddress = "5678 Galaxy Street, Cosmic City, ABC 54321, United States",
|
||||
ContactEmail = "support@ecobus.com",
|
||||
ContactPhoneNumber = "+00000000000"
|
||||
};
|
||||
|
||||
dbContext.Companies.AddRange(new[] { company1, company2 });
|
||||
dbContext.SaveChanges();
|
||||
|
||||
var vehicle1 = new Bus()
|
||||
{
|
||||
Number = "AC8236HK",
|
||||
Model = "Mercedes-Benz Citaro",
|
||||
Capacity = 20,
|
||||
HasWC = true,
|
||||
HasWiFi = false,
|
||||
HasOutlets = true,
|
||||
HasMultimedia = false,
|
||||
HasClimateControl = true,
|
||||
Company = company1
|
||||
};
|
||||
|
||||
var vehicle2 = new Bus()
|
||||
{
|
||||
Number = "EP2566MK",
|
||||
Model = "Van Hool TDX25",
|
||||
Capacity = 25,
|
||||
HasWC = true,
|
||||
HasWiFi = false,
|
||||
HasOutlets = true,
|
||||
HasMultimedia = false,
|
||||
HasClimateControl = true,
|
||||
Company = company2
|
||||
};
|
||||
|
||||
var vehicle3 = new Bus()
|
||||
{
|
||||
Number = "OK8634MC",
|
||||
Model = "New Flyer Xcelsior",
|
||||
Capacity = 30,
|
||||
HasWC = true,
|
||||
HasWiFi = false,
|
||||
HasOutlets = true,
|
||||
HasMultimedia = false,
|
||||
HasClimateControl = true,
|
||||
Company = company1
|
||||
};
|
||||
|
||||
dbContext.Vehicles.AddRange(new[] { vehicle1, vehicle2, vehicle3 });
|
||||
dbContext.SaveChanges();
|
||||
|
||||
var vehicleEnrollment1 = new VehicleEnrollment()
|
||||
{
|
||||
DepartureDateTimeUtc = DateTime.UtcNow,
|
||||
Vehicle = vehicle1,
|
||||
Route = route1,
|
||||
RouteAddressDetails = new RouteAddressDetails[]
|
||||
{
|
||||
new RouteAddressDetails()
|
||||
{
|
||||
TimeToNextAddress = TimeSpan.FromMinutes(50),
|
||||
CostToNextAddress = 25,
|
||||
CurrentAddressStopTime = TimeSpan.FromMinutes(5),
|
||||
RouteAddress = route1.RouteAddresses.ToArray()[0],
|
||||
},
|
||||
new RouteAddressDetails()
|
||||
{
|
||||
TimeToNextAddress = TimeSpan.FromMinutes(40),
|
||||
CostToNextAddress = 20,
|
||||
CurrentAddressStopTime = TimeSpan.FromMinutes(5),
|
||||
RouteAddress = route1.RouteAddresses.ToArray()[1],
|
||||
},
|
||||
new RouteAddressDetails()
|
||||
{
|
||||
TimeToNextAddress = TimeSpan.FromMinutes(45),
|
||||
CostToNextAddress = 23.33,
|
||||
CurrentAddressStopTime = TimeSpan.FromMinutes(5),
|
||||
RouteAddress = route1.RouteAddresses.ToArray()[2],
|
||||
},
|
||||
new RouteAddressDetails()
|
||||
{
|
||||
TimeToNextAddress = TimeSpan.FromMinutes(25),
|
||||
CostToNextAddress = 15,
|
||||
CurrentAddressStopTime = TimeSpan.FromMinutes(5),
|
||||
RouteAddress = route1.RouteAddresses.ToArray()[3],
|
||||
},
|
||||
new RouteAddressDetails()
|
||||
{
|
||||
TimeToNextAddress = TimeSpan.FromMinutes(30),
|
||||
CostToNextAddress = 17.5,
|
||||
CurrentAddressStopTime = TimeSpan.FromMinutes(5),
|
||||
RouteAddress = route1.RouteAddresses.ToArray()[4],
|
||||
},
|
||||
new RouteAddressDetails()
|
||||
{
|
||||
TimeToNextAddress = TimeSpan.FromMinutes(35),
|
||||
CostToNextAddress = 19,
|
||||
CurrentAddressStopTime = TimeSpan.FromMinutes(5),
|
||||
RouteAddress = route1.RouteAddresses.ToArray()[5],
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var vehicleEnrollment2 = new VehicleEnrollment()
|
||||
{
|
||||
DepartureDateTimeUtc = DateTime.UtcNow.AddMinutes(30),
|
||||
Vehicle = vehicle2,
|
||||
Route = route2,
|
||||
RouteAddressDetails = new RouteAddressDetails[]
|
||||
{
|
||||
new RouteAddressDetails()
|
||||
{
|
||||
TimeToNextAddress = TimeSpan.FromMinutes(40),
|
||||
CostToNextAddress = 25,
|
||||
CurrentAddressStopTime = TimeSpan.FromMinutes(5),
|
||||
RouteAddress = route2.RouteAddresses.ToArray()[0],
|
||||
},
|
||||
new RouteAddressDetails()
|
||||
{
|
||||
TimeToNextAddress = TimeSpan.FromMinutes(50),
|
||||
CostToNextAddress = 20,
|
||||
CurrentAddressStopTime = TimeSpan.FromMinutes(5),
|
||||
RouteAddress = route2.RouteAddresses.ToArray()[1],
|
||||
},
|
||||
new RouteAddressDetails()
|
||||
{
|
||||
TimeToNextAddress = TimeSpan.FromMinutes(45),
|
||||
CostToNextAddress = 23.33,
|
||||
CurrentAddressStopTime = TimeSpan.FromMinutes(5),
|
||||
RouteAddress = route2.RouteAddresses.ToArray()[2],
|
||||
},
|
||||
new RouteAddressDetails()
|
||||
{
|
||||
TimeToNextAddress = TimeSpan.FromMinutes(25),
|
||||
CostToNextAddress = 15,
|
||||
CurrentAddressStopTime = TimeSpan.FromMinutes(5),
|
||||
RouteAddress = route2.RouteAddresses.ToArray()[3],
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var vehicleEnrollment3 = new VehicleEnrollment()
|
||||
{
|
||||
DepartureDateTimeUtc = DateTime.UtcNow.AddMinutes(175),
|
||||
Vehicle = vehicle3,
|
||||
Route = route3,
|
||||
RouteAddressDetails = new RouteAddressDetails[]
|
||||
{
|
||||
new RouteAddressDetails()
|
||||
{
|
||||
TimeToNextAddress = TimeSpan.FromMinutes(50),
|
||||
CostToNextAddress = 25,
|
||||
CurrentAddressStopTime = TimeSpan.FromMinutes(5),
|
||||
RouteAddress = route2.RouteAddresses.ToArray()[0],
|
||||
},
|
||||
new RouteAddressDetails()
|
||||
{
|
||||
TimeToNextAddress = TimeSpan.FromMinutes(40),
|
||||
CostToNextAddress = 20,
|
||||
CurrentAddressStopTime = TimeSpan.FromMinutes(5),
|
||||
RouteAddress = route2.RouteAddresses.ToArray()[1],
|
||||
},
|
||||
new RouteAddressDetails()
|
||||
{
|
||||
TimeToNextAddress = TimeSpan.FromMinutes(45),
|
||||
CostToNextAddress = 23.33,
|
||||
CurrentAddressStopTime = TimeSpan.FromMinutes(5),
|
||||
RouteAddress = route2.RouteAddresses.ToArray()[2],
|
||||
},
|
||||
new RouteAddressDetails()
|
||||
{
|
||||
TimeToNextAddress = TimeSpan.FromMinutes(25),
|
||||
CostToNextAddress = 15,
|
||||
CurrentAddressStopTime = TimeSpan.FromMinutes(5),
|
||||
RouteAddress = route2.RouteAddresses.ToArray()[3],
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
dbContext.VehicleEnrollments.AddRange(new[] { vehicleEnrollment1, vehicleEnrollment2/* , vehicleEnrollment3 */ });
|
||||
dbContext.SaveChanges();
|
||||
}
|
||||
|
||||
private static void InitializeIdentity(ApplicationIdentityDbContext identityDbContext)
|
||||
|
@ -1,5 +1,9 @@
|
||||
using AutobusApi.Domain.Entities;
|
||||
using AutobusApi.Domain.Enums;
|
||||
using AutobusApi.Infrastructure.Data.Entities;
|
||||
using AutobusApi.Infrastructure.Identity;
|
||||
using AutoubsApi.Infrastructure.Data;
|
||||
using Route = AutobusApi.Domain.Entities.Route;
|
||||
|
||||
namespace AutobusApi.IntegrationTests;
|
||||
|
||||
@ -14,7 +18,454 @@ public static class DbInitializer
|
||||
|
||||
private static void InitializeDomain(ApplicationDbContext dbContext)
|
||||
{
|
||||
var country1 = new Country()
|
||||
{
|
||||
Name = "Country A"
|
||||
};
|
||||
|
||||
dbContext.Countries.AddRange(new[] { country1 });
|
||||
dbContext.SaveChanges();
|
||||
|
||||
var region1 = new Region()
|
||||
{
|
||||
Name = "Region A",
|
||||
Country = country1
|
||||
};
|
||||
|
||||
dbContext.Regions.AddRange(new[] { region1 });
|
||||
dbContext.SaveChanges();
|
||||
|
||||
var city1 = new City()
|
||||
{
|
||||
Name = "City A",
|
||||
Region = region1
|
||||
};
|
||||
|
||||
var city2 = new City()
|
||||
{
|
||||
Name = "City B",
|
||||
Region = region1
|
||||
};
|
||||
|
||||
var city3 = new City()
|
||||
{
|
||||
Name = "City C",
|
||||
Region = region1
|
||||
};
|
||||
|
||||
var city4 = new City()
|
||||
{
|
||||
Name = "City D",
|
||||
Region = region1
|
||||
};
|
||||
|
||||
var city5 = new City()
|
||||
{
|
||||
Name = "City E",
|
||||
Region = region1
|
||||
};
|
||||
|
||||
var city6 = new City()
|
||||
{
|
||||
Name = "City F",
|
||||
Region = region1
|
||||
};
|
||||
|
||||
var city7 = new City()
|
||||
{
|
||||
Name = "City G",
|
||||
Region = region1
|
||||
};
|
||||
|
||||
var city8 = new City()
|
||||
{
|
||||
Name = "City H",
|
||||
Region = region1
|
||||
};
|
||||
|
||||
var city9 = new City()
|
||||
{
|
||||
Name = "City I",
|
||||
Region = region1
|
||||
};
|
||||
|
||||
var city10 = new City()
|
||||
{
|
||||
Name = "City J",
|
||||
Region = region1
|
||||
};
|
||||
|
||||
dbContext.Cities.AddRange(new[] { city1, city2, city3, city4, city5, city6, city7, city8, city9, city10 });
|
||||
dbContext.SaveChanges();
|
||||
|
||||
var address1 = new Address()
|
||||
{
|
||||
Name = "address A",
|
||||
Location = new Coordinates(11, 11),
|
||||
VehicleType = VehicleType.Bus,
|
||||
City = city1
|
||||
};
|
||||
|
||||
var address2 = new Address()
|
||||
{
|
||||
Name = "address B",
|
||||
Location = new Coordinates(22, 22),
|
||||
VehicleType = VehicleType.Bus,
|
||||
City = city2
|
||||
};
|
||||
|
||||
var address3 = new Address()
|
||||
{
|
||||
Name = "address C",
|
||||
Location = new Coordinates(33, 33),
|
||||
VehicleType = VehicleType.Bus,
|
||||
City = city3
|
||||
};
|
||||
|
||||
var address4 = new Address()
|
||||
{
|
||||
Name = "address E",
|
||||
Location = new Coordinates(44, 44),
|
||||
VehicleType = VehicleType.Bus,
|
||||
City = city4
|
||||
};
|
||||
|
||||
var address5 = new Address()
|
||||
{
|
||||
Name = "address F",
|
||||
Location = new Coordinates(55, 55),
|
||||
VehicleType = VehicleType.Bus,
|
||||
City = city5
|
||||
};
|
||||
|
||||
var address6 = new Address()
|
||||
{
|
||||
Name = "address G",
|
||||
Location = new Coordinates(66, 66),
|
||||
VehicleType = VehicleType.Bus,
|
||||
City = city6
|
||||
};
|
||||
|
||||
var address7 = new Address()
|
||||
{
|
||||
Name = "address H",
|
||||
Location = new Coordinates(77, 77),
|
||||
VehicleType = VehicleType.Bus,
|
||||
City = city7
|
||||
};
|
||||
|
||||
var address8 = new Address()
|
||||
{
|
||||
Name = "address I",
|
||||
Location = new Coordinates(88, 88),
|
||||
VehicleType = VehicleType.Bus,
|
||||
City = city8
|
||||
};
|
||||
|
||||
var address9 = new Address()
|
||||
{
|
||||
Name = "address J",
|
||||
Location = new Coordinates(99, 99),
|
||||
VehicleType = VehicleType.Bus,
|
||||
City = city9
|
||||
};
|
||||
|
||||
var address10 = new Address()
|
||||
{
|
||||
Name = "address K",
|
||||
Location = new Coordinates(110, 110),
|
||||
VehicleType = VehicleType.Bus,
|
||||
City = city10
|
||||
};
|
||||
|
||||
dbContext.Addresses.AddRange(new[] { address1, address2, address3, address4, address5, address6, address7, address8, address9, address10 });
|
||||
dbContext.SaveChanges();
|
||||
|
||||
var route1 = new Route()
|
||||
{
|
||||
RouteAddresses = new RouteAddress[]
|
||||
{
|
||||
new RouteAddress
|
||||
{
|
||||
Order = 1,
|
||||
Address = address1
|
||||
},
|
||||
new RouteAddress
|
||||
{
|
||||
Order = 2,
|
||||
Address = address2
|
||||
},
|
||||
new RouteAddress
|
||||
{
|
||||
Order = 3,
|
||||
Address = address3
|
||||
},
|
||||
new RouteAddress
|
||||
{
|
||||
Order = 4,
|
||||
Address = address4
|
||||
},
|
||||
new RouteAddress
|
||||
{
|
||||
Order = 5,
|
||||
Address = address5
|
||||
},
|
||||
new RouteAddress
|
||||
{
|
||||
Order = 6,
|
||||
Address = address8
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var route2 = new Route()
|
||||
{
|
||||
RouteAddresses = new RouteAddress[]
|
||||
{
|
||||
new RouteAddress
|
||||
{
|
||||
Order = 1,
|
||||
Address = address2
|
||||
},
|
||||
new RouteAddress
|
||||
{
|
||||
Order = 2,
|
||||
Address = address3
|
||||
},
|
||||
new RouteAddress
|
||||
{
|
||||
Order = 3,
|
||||
Address = address6
|
||||
},
|
||||
new RouteAddress
|
||||
{
|
||||
Order = 4,
|
||||
Address = address7
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var route3 = new Route()
|
||||
{
|
||||
RouteAddresses = new RouteAddress[]
|
||||
{
|
||||
new RouteAddress
|
||||
{
|
||||
Order = 1,
|
||||
Address = address6
|
||||
},
|
||||
new RouteAddress
|
||||
{
|
||||
Order = 2,
|
||||
Address = address8
|
||||
},
|
||||
new RouteAddress
|
||||
{
|
||||
Order = 3,
|
||||
Address = address9
|
||||
},
|
||||
new RouteAddress
|
||||
{
|
||||
Order = 4,
|
||||
Address = address10
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
dbContext.Routes.AddRange(new[] { route1, route2, route3 });
|
||||
dbContext.SaveChanges();
|
||||
|
||||
var company1 = new Company
|
||||
{
|
||||
Name = "Smartlines",
|
||||
LegalAddress = "1234 Spectrum Avenue, Metroville, XYZ 98765, United States",
|
||||
ContactEmail = "support@smartlines.com",
|
||||
ContactPhoneNumber = "+00000000000"
|
||||
};
|
||||
|
||||
var company2 = new Company
|
||||
{
|
||||
Name = "ecobus",
|
||||
LegalAddress = "5678 Galaxy Street, Cosmic City, ABC 54321, United States",
|
||||
ContactEmail = "support@ecobus.com",
|
||||
ContactPhoneNumber = "+00000000000"
|
||||
};
|
||||
|
||||
dbContext.Companies.AddRange(new[] { company1, company2 });
|
||||
dbContext.SaveChanges();
|
||||
|
||||
var vehicle1 = new Bus()
|
||||
{
|
||||
Number = "AC8236HK",
|
||||
Model = "Mercedes-Benz Citaro",
|
||||
Capacity = 20,
|
||||
HasWC = true,
|
||||
HasWiFi = false,
|
||||
HasOutlets = true,
|
||||
HasMultimedia = false,
|
||||
HasClimateControl = true,
|
||||
Company = company1
|
||||
};
|
||||
|
||||
var vehicle2 = new Bus()
|
||||
{
|
||||
Number = "EP2566MK",
|
||||
Model = "Van Hool TDX25",
|
||||
Capacity = 25,
|
||||
HasWC = true,
|
||||
HasWiFi = false,
|
||||
HasOutlets = true,
|
||||
HasMultimedia = false,
|
||||
HasClimateControl = true,
|
||||
Company = company2
|
||||
};
|
||||
|
||||
var vehicle3 = new Bus()
|
||||
{
|
||||
Number = "OK8634MC",
|
||||
Model = "New Flyer Xcelsior",
|
||||
Capacity = 30,
|
||||
HasWC = true,
|
||||
HasWiFi = false,
|
||||
HasOutlets = true,
|
||||
HasMultimedia = false,
|
||||
HasClimateControl = true,
|
||||
Company = company1
|
||||
};
|
||||
|
||||
dbContext.Vehicles.AddRange(new[] { vehicle1, vehicle2, vehicle3 });
|
||||
dbContext.SaveChanges();
|
||||
|
||||
var vehicleEnrollment1 = new VehicleEnrollment()
|
||||
{
|
||||
DepartureDateTimeUtc = DateTime.UtcNow,
|
||||
Vehicle = vehicle1,
|
||||
Route = route1,
|
||||
RouteAddressDetails = new RouteAddressDetails[]
|
||||
{
|
||||
new RouteAddressDetails()
|
||||
{
|
||||
TimeToNextAddress = TimeSpan.FromMinutes(50),
|
||||
CostToNextAddress = 25,
|
||||
CurrentAddressStopTime = TimeSpan.FromMinutes(5),
|
||||
RouteAddress = route1.RouteAddresses.ToArray()[0],
|
||||
},
|
||||
new RouteAddressDetails()
|
||||
{
|
||||
TimeToNextAddress = TimeSpan.FromMinutes(40),
|
||||
CostToNextAddress = 20,
|
||||
CurrentAddressStopTime = TimeSpan.FromMinutes(5),
|
||||
RouteAddress = route1.RouteAddresses.ToArray()[1],
|
||||
},
|
||||
new RouteAddressDetails()
|
||||
{
|
||||
TimeToNextAddress = TimeSpan.FromMinutes(45),
|
||||
CostToNextAddress = 23.33,
|
||||
CurrentAddressStopTime = TimeSpan.FromMinutes(5),
|
||||
RouteAddress = route1.RouteAddresses.ToArray()[2],
|
||||
},
|
||||
new RouteAddressDetails()
|
||||
{
|
||||
TimeToNextAddress = TimeSpan.FromMinutes(25),
|
||||
CostToNextAddress = 15,
|
||||
CurrentAddressStopTime = TimeSpan.FromMinutes(5),
|
||||
RouteAddress = route1.RouteAddresses.ToArray()[3],
|
||||
},
|
||||
new RouteAddressDetails()
|
||||
{
|
||||
TimeToNextAddress = TimeSpan.FromMinutes(30),
|
||||
CostToNextAddress = 17.5,
|
||||
CurrentAddressStopTime = TimeSpan.FromMinutes(5),
|
||||
RouteAddress = route1.RouteAddresses.ToArray()[4],
|
||||
},
|
||||
new RouteAddressDetails()
|
||||
{
|
||||
TimeToNextAddress = TimeSpan.FromMinutes(35),
|
||||
CostToNextAddress = 19,
|
||||
CurrentAddressStopTime = TimeSpan.FromMinutes(5),
|
||||
RouteAddress = route1.RouteAddresses.ToArray()[5],
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var vehicleEnrollment2 = new VehicleEnrollment()
|
||||
{
|
||||
DepartureDateTimeUtc = DateTime.UtcNow.AddMinutes(30),
|
||||
Vehicle = vehicle2,
|
||||
Route = route2,
|
||||
RouteAddressDetails = new RouteAddressDetails[]
|
||||
{
|
||||
new RouteAddressDetails()
|
||||
{
|
||||
TimeToNextAddress = TimeSpan.FromMinutes(40),
|
||||
CostToNextAddress = 25,
|
||||
CurrentAddressStopTime = TimeSpan.FromMinutes(5),
|
||||
RouteAddress = route2.RouteAddresses.ToArray()[0],
|
||||
},
|
||||
new RouteAddressDetails()
|
||||
{
|
||||
TimeToNextAddress = TimeSpan.FromMinutes(50),
|
||||
CostToNextAddress = 20,
|
||||
CurrentAddressStopTime = TimeSpan.FromMinutes(5),
|
||||
RouteAddress = route2.RouteAddresses.ToArray()[1],
|
||||
},
|
||||
new RouteAddressDetails()
|
||||
{
|
||||
TimeToNextAddress = TimeSpan.FromMinutes(45),
|
||||
CostToNextAddress = 23.33,
|
||||
CurrentAddressStopTime = TimeSpan.FromMinutes(5),
|
||||
RouteAddress = route2.RouteAddresses.ToArray()[2],
|
||||
},
|
||||
new RouteAddressDetails()
|
||||
{
|
||||
TimeToNextAddress = TimeSpan.FromMinutes(25),
|
||||
CostToNextAddress = 15,
|
||||
CurrentAddressStopTime = TimeSpan.FromMinutes(5),
|
||||
RouteAddress = route2.RouteAddresses.ToArray()[3],
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var vehicleEnrollment3 = new VehicleEnrollment()
|
||||
{
|
||||
DepartureDateTimeUtc = DateTime.UtcNow.AddMinutes(175),
|
||||
Vehicle = vehicle3,
|
||||
Route = route3,
|
||||
RouteAddressDetails = new RouteAddressDetails[]
|
||||
{
|
||||
new RouteAddressDetails()
|
||||
{
|
||||
TimeToNextAddress = TimeSpan.FromMinutes(50),
|
||||
CostToNextAddress = 25,
|
||||
CurrentAddressStopTime = TimeSpan.FromMinutes(5),
|
||||
RouteAddress = route2.RouteAddresses.ToArray()[0],
|
||||
},
|
||||
new RouteAddressDetails()
|
||||
{
|
||||
TimeToNextAddress = TimeSpan.FromMinutes(40),
|
||||
CostToNextAddress = 20,
|
||||
CurrentAddressStopTime = TimeSpan.FromMinutes(5),
|
||||
RouteAddress = route2.RouteAddresses.ToArray()[1],
|
||||
},
|
||||
new RouteAddressDetails()
|
||||
{
|
||||
TimeToNextAddress = TimeSpan.FromMinutes(45),
|
||||
CostToNextAddress = 23.33,
|
||||
CurrentAddressStopTime = TimeSpan.FromMinutes(5),
|
||||
RouteAddress = route2.RouteAddresses.ToArray()[2],
|
||||
},
|
||||
new RouteAddressDetails()
|
||||
{
|
||||
TimeToNextAddress = TimeSpan.FromMinutes(25),
|
||||
CostToNextAddress = 15,
|
||||
CurrentAddressStopTime = TimeSpan.FromMinutes(5),
|
||||
RouteAddress = route2.RouteAddresses.ToArray()[3],
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
dbContext.VehicleEnrollments.AddRange(new[] { vehicleEnrollment1, vehicleEnrollment2, vehicleEnrollment3 });
|
||||
dbContext.SaveChanges();
|
||||
}
|
||||
|
||||
private static void InitializeIdentity(ApplicationIdentityDbContext identityDbContext)
|
||||
|
31
AutobusApi.IntegrationTests/Tests/RouteSearchTests.cs
Normal file
31
AutobusApi.IntegrationTests/Tests/RouteSearchTests.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using System.Net;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace AutobusApi.IntegrationTests.Tests;
|
||||
|
||||
public class RouteSearchTests : TestsBase
|
||||
{
|
||||
public RouteSearchTests(CustomWebApplicationFactory<Program> factory)
|
||||
: base(factory) {}
|
||||
|
||||
[Theory]
|
||||
[InlineData(1, 10)]
|
||||
public async Task RouteSearch_ValidStationsAndDate_Returns200OK(int fromId, int toId)
|
||||
{
|
||||
var query = new
|
||||
{
|
||||
DepartureAddressId = fromId,
|
||||
ArrivalAddressId = toId,
|
||||
DepartureDate = DateOnly.FromDateTime(DateTime.UtcNow.Date),
|
||||
MinTransferTime = TimeSpan.FromMinutes(15),
|
||||
MaxTransferTime = TimeSpan.FromHours(12),
|
||||
MaxTransferDistanceInMeters = 1000
|
||||
};
|
||||
|
||||
var response = await _httpClient.PostAsJsonAsync("search", query);
|
||||
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
|
||||
var content = JsonConvert.DeserializeObject<dynamic>(await response.Content.ReadAsStringAsync());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user