mirror of
https://github.com/meysamhadeli/booking-microservices.git
synced 2026-04-11 02:20:20 +08:00
Merge pull request #251 from meysamhadeli/refactor/refactor-core-domain
refactor: Refactor core domain in building-blocks
This commit is contained in:
commit
c93bd2902a
@ -16,11 +16,24 @@ services:
|
||||
command:
|
||||
- "postgres"
|
||||
- "-c"
|
||||
- "wal_level=logical"
|
||||
- "wal_level=logical"
|
||||
networks:
|
||||
- booking
|
||||
|
||||
|
||||
#######################################################
|
||||
# SqlServer
|
||||
#######################################################
|
||||
# sql:
|
||||
# container_name: sql
|
||||
# image: mcr.microsoft.com/mssql/server
|
||||
# ports:
|
||||
# - "1433:1433"
|
||||
# environment:
|
||||
# SA_PASSWORD: "Password@1234"
|
||||
# ACCEPT_EULA: "Y"
|
||||
|
||||
|
||||
#######################################################
|
||||
# Rabbitmq
|
||||
#######################################################
|
||||
|
||||
@ -30,10 +30,24 @@ services:
|
||||
command:
|
||||
- "postgres"
|
||||
- "-c"
|
||||
- "wal_level=logical"
|
||||
- "wal_level=logical"
|
||||
networks:
|
||||
- booking
|
||||
|
||||
|
||||
#######################################################
|
||||
# SqlServer
|
||||
#######################################################
|
||||
# sql:
|
||||
# container_name: sql
|
||||
# image: mcr.microsoft.com/mssql/server
|
||||
# ports:
|
||||
# - "1433:1433"
|
||||
# environment:
|
||||
# SA_PASSWORD: "Password@1234"
|
||||
# ACCEPT_EULA: "Y"
|
||||
|
||||
|
||||
#######################################################
|
||||
# Jaeger
|
||||
#######################################################
|
||||
@ -141,8 +155,6 @@ services:
|
||||
ports:
|
||||
- 6379:6379
|
||||
|
||||
|
||||
|
||||
networks:
|
||||
booking:
|
||||
|
||||
|
||||
@ -77,6 +77,7 @@
|
||||
<PackageReference Include="Serilog.Sinks.Seq" Version="5.2.2" />
|
||||
<PackageReference Include="Serilog.Sinks.SpectreConsole" Version="0.3.3" />
|
||||
<PackageReference Include="Serilog.Sinks.XUnit" Version="3.0.3" />
|
||||
<PackageReference Include="Sieve" Version="2.5.5" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.5.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.5.0" />
|
||||
@ -99,6 +100,8 @@
|
||||
<PackageReference Include="Duende.IdentityServer.EntityFramework" Version="6.2.2" />
|
||||
<PackageReference Include="Duende.IdentityServer.EntityFramework.Storage" Version="6.2.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.2" />
|
||||
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
|
||||
<PackageReference Include="System.Linq.Async.Queryable" Version="6.0.1" />
|
||||
<PackageReference Include="Testcontainers" Version="3.0.0" />
|
||||
<PackageReference Include="Testcontainers.EventStoreDb" Version="3.0.0" />
|
||||
<PackageReference Include="Testcontainers.MongoDb" Version="3.0.0" />
|
||||
@ -141,10 +144,9 @@
|
||||
<PackageReference Include="Google.Protobuf" Version="3.21.12" />
|
||||
<PackageReference Include="Grpc.Net.Client" Version="2.51.0" />
|
||||
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.51.0" />
|
||||
<PackageReference Update="AsyncFixer" Version="1.6.0" />
|
||||
|
||||
<PackageReference Update="Meziantou.Analyzer" Version="1.0.758" />
|
||||
<PackageReference Remove="Microsoft.VisualStudio.Threading.Analyzers" />
|
||||
<PackageReference Update="Microsoft.VisualStudio.Threading.Analyzers" Version="17.4.27" />
|
||||
<PackageReference Update="AsyncFixer" Version="1.6.0" />
|
||||
<PackageReference Update="Roslynator.Analyzers" Version="4.2.0" />
|
||||
<PackageReference Update="Roslynator.CodeAnalysis.Analyzers" Version="4.2.0" />
|
||||
<PackageReference Update="Roslynator.Formatting.Analyzers" Version="4.2.0" />
|
||||
@ -154,6 +156,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Contracts" />
|
||||
<Folder Include="Core\Pagination" />
|
||||
<Folder Include="EventStoreDB\BackgroundWorkers" />
|
||||
<Folder Include="PersistMessageProcessor\Data\Configurations" />
|
||||
<Folder Include="PersistMessageProcessor\Data\Migrations" />
|
||||
|
||||
@ -2,9 +2,7 @@
|
||||
|
||||
namespace BuildingBlocks.Core.Model;
|
||||
|
||||
public abstract record Aggregate : Aggregate<long>;
|
||||
|
||||
public abstract record Aggregate<TId> : Audit, IAggregate<TId>
|
||||
public abstract record Aggregate<TId> : Entity<TId>, IAggregate<TId>
|
||||
{
|
||||
private readonly List<IDomainEvent> _domainEvents = new();
|
||||
public IReadOnlyList<IDomainEvent> DomainEvents => _domainEvents.AsReadOnly();
|
||||
@ -22,8 +20,4 @@ public abstract record Aggregate<TId> : Audit, IAggregate<TId>
|
||||
|
||||
return dequeuedEvents;
|
||||
}
|
||||
|
||||
public long Version { get; set; }
|
||||
|
||||
public required TId Id { get; set; }
|
||||
}
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
namespace BuildingBlocks.Core.Model;
|
||||
namespace BuildingBlocks.Core.Model;
|
||||
|
||||
public interface IAudit
|
||||
public abstract record Entity<T> : IEntity<T>
|
||||
{
|
||||
public T Id { get; set; }
|
||||
public DateTime? CreatedAt { get; set; }
|
||||
public long? CreatedBy { get; set; }
|
||||
public DateTime? LastModified { get; set; }
|
||||
public long? LastModifiedBy { get; set; }
|
||||
public bool IsDeleted { get; set; }
|
||||
public long Version { get; set; }
|
||||
}
|
||||
@ -2,18 +2,12 @@
|
||||
|
||||
namespace BuildingBlocks.Core.Model;
|
||||
|
||||
public interface IAggregate : IAudit, IVersion
|
||||
public interface IAggregate<T> : IAggregate, IEntity<T>
|
||||
{
|
||||
}
|
||||
|
||||
public interface IAggregate : IEntity
|
||||
{
|
||||
IReadOnlyList<IDomainEvent> DomainEvents { get; }
|
||||
IEvent[] ClearDomainEvents();
|
||||
}
|
||||
|
||||
public interface IAggregate<out T> : IAggregate
|
||||
{
|
||||
T Id { get; }
|
||||
}
|
||||
|
||||
public interface IVersion
|
||||
{
|
||||
long Version { get; set; }
|
||||
}
|
||||
|
||||
@ -1,6 +1,11 @@
|
||||
namespace BuildingBlocks.Core.Model;
|
||||
namespace BuildingBlocks.Core.Model;
|
||||
|
||||
public abstract record Audit : IAudit
|
||||
public interface IEntity<T> : IEntity
|
||||
{
|
||||
public T Id { get; set; }
|
||||
}
|
||||
|
||||
public interface IEntity: IVersion
|
||||
{
|
||||
public DateTime? CreatedAt { get; set; }
|
||||
public long? CreatedBy { get; set; }
|
||||
6
src/BuildingBlocks/Core/Model/IVersion.cs
Normal file
6
src/BuildingBlocks/Core/Model/IVersion.cs
Normal file
@ -0,0 +1,6 @@
|
||||
namespace BuildingBlocks.Core.Model;
|
||||
|
||||
public interface IVersion
|
||||
{
|
||||
long Version { get; set; }
|
||||
}
|
||||
36
src/BuildingBlocks/Core/Pagination/Extensions.cs
Normal file
36
src/BuildingBlocks/Core/Pagination/Extensions.cs
Normal file
@ -0,0 +1,36 @@
|
||||
namespace BuildingBlocks.Core.Pagination;
|
||||
|
||||
using Sieve.Models;
|
||||
using Sieve.Services;
|
||||
|
||||
public static class Extensions
|
||||
{
|
||||
public static async Task<IPageList<TEntity>> ApplyPagingAsync<TEntity>(
|
||||
this IQueryable<TEntity> queryable,
|
||||
IPageRequest pageRequest,
|
||||
ISieveProcessor sieveProcessor,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
where TEntity : class
|
||||
{
|
||||
var sieveModel = new SieveModel
|
||||
{
|
||||
PageSize = pageRequest.PageSize,
|
||||
Page = pageRequest.PageNumber,
|
||||
Sorts = pageRequest.SortOrder,
|
||||
Filters = pageRequest.Filters
|
||||
};
|
||||
|
||||
// https://github.com/Biarity/Sieve/issues/34#issuecomment-403817573
|
||||
var result = sieveProcessor.Apply(sieveModel, queryable, applyPagination: false);
|
||||
var total = result.Count();
|
||||
result = sieveProcessor.Apply(sieveModel, queryable, applyFiltering: false,
|
||||
applySorting: false); // Only applies pagination
|
||||
|
||||
var items = await result
|
||||
.ToAsyncEnumerable()
|
||||
.ToListAsync(cancellationToken: cancellationToken);
|
||||
|
||||
return PageList<TEntity>.Create(items.AsReadOnly(), pageRequest.PageNumber, pageRequest.PageSize, total);
|
||||
}
|
||||
}
|
||||
16
src/BuildingBlocks/Core/Pagination/IPageList.cs
Normal file
16
src/BuildingBlocks/Core/Pagination/IPageList.cs
Normal file
@ -0,0 +1,16 @@
|
||||
namespace BuildingBlocks.Core.Pagination;
|
||||
|
||||
public interface IPageList<T>
|
||||
where T : class
|
||||
{
|
||||
int CurrentPageSize { get; }
|
||||
int CurrentStartIndex { get; }
|
||||
int CurrentEndIndex { get; }
|
||||
int TotalPages { get; }
|
||||
bool HasPrevious { get; }
|
||||
bool HasNext { get; }
|
||||
IReadOnlyList<T> Items { get; init; }
|
||||
int TotalCount { get; init; }
|
||||
int PageNumber { get; init; }
|
||||
int PageSize { get; init; }
|
||||
}
|
||||
6
src/BuildingBlocks/Core/Pagination/IPageQuery.cs
Normal file
6
src/BuildingBlocks/Core/Pagination/IPageQuery.cs
Normal file
@ -0,0 +1,6 @@
|
||||
namespace BuildingBlocks.Core.Pagination;
|
||||
|
||||
using MediatR;
|
||||
|
||||
public interface IPageQuery<out TResponse> : IPageRequest, IRequest<TResponse>
|
||||
where TResponse : class { }
|
||||
9
src/BuildingBlocks/Core/Pagination/IPageRequest.cs
Normal file
9
src/BuildingBlocks/Core/Pagination/IPageRequest.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace BuildingBlocks.Core.Pagination;
|
||||
|
||||
public interface IPageRequest
|
||||
{
|
||||
int PageNumber { get; init; }
|
||||
int PageSize { get; init; }
|
||||
string? Filters { get; init; }
|
||||
string? SortOrder { get; init; }
|
||||
}
|
||||
19
src/BuildingBlocks/Core/Pagination/PageList.cs
Normal file
19
src/BuildingBlocks/Core/Pagination/PageList.cs
Normal file
@ -0,0 +1,19 @@
|
||||
namespace BuildingBlocks.Core.Pagination;
|
||||
|
||||
public record PageList<T>(IReadOnlyList<T> Items, int PageNumber, int PageSize, int TotalCount) : IPageList<T>
|
||||
where T : class
|
||||
{
|
||||
public int CurrentPageSize => Items.Count;
|
||||
public int CurrentStartIndex => TotalCount == 0 ? 0 : ((PageNumber - 1) * PageSize) + 1;
|
||||
public int CurrentEndIndex => TotalCount == 0 ? 0 : CurrentStartIndex + CurrentPageSize - 1;
|
||||
public int TotalPages => (int)Math.Ceiling(TotalCount / (double)PageSize);
|
||||
public bool HasPrevious => PageNumber > 1;
|
||||
public bool HasNext => PageNumber < TotalPages;
|
||||
|
||||
public static PageList<T> Empty => new(Enumerable.Empty<T>().ToList(), 0, 0, 0);
|
||||
|
||||
public static PageList<T> Create(IReadOnlyList<T> items, int pageNumber, int pageSize, int totalItems)
|
||||
{
|
||||
return new PageList<T>(items, pageNumber, pageSize, totalItems);
|
||||
}
|
||||
}
|
||||
@ -50,12 +50,16 @@ public class EfTxBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TRe
|
||||
nameof(EfTxBehavior<TRequest, TResponse>),
|
||||
typeof(TRequest).FullName);
|
||||
|
||||
var domainEvents = _dbContextBase.GetDomainEvents();
|
||||
while (true)
|
||||
{
|
||||
var domainEvents = _dbContextBase.GetDomainEvents();
|
||||
|
||||
await _eventDispatcher.SendAsync(domainEvents.ToArray(), typeof(TRequest), cancellationToken);
|
||||
if (domainEvents is null || !domainEvents.Any())
|
||||
return response;
|
||||
|
||||
await _dbContextBase.ExecuteTransactionalAsync(cancellationToken);
|
||||
await _dbContextBase.ExecuteTransactionalAsync(cancellationToken);
|
||||
|
||||
return response;
|
||||
await _eventDispatcher.SendAsync(domainEvents.ToArray(), typeof(TRequest), cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,7 +66,7 @@ public static class Extensions
|
||||
{
|
||||
Expression<Func<IAggregate, bool>> filterExpr = e => !e.IsDeleted;
|
||||
foreach (var mutableEntityType in modelBuilder.Model.GetEntityTypes()
|
||||
.Where(m => m.ClrType.IsAssignableTo(typeof(IAudit))))
|
||||
.Where(m => m.ClrType.IsAssignableTo(typeof(IEntity))))
|
||||
{
|
||||
// modify expression to handle correct child type
|
||||
var parameter = Expression.Parameter(mutableEntityType.ClrType);
|
||||
|
||||
@ -3,7 +3,7 @@ using BuildingBlocks.Core.Model;
|
||||
|
||||
namespace BuildingBlocks.EventStoreDB.Events
|
||||
{
|
||||
public abstract record AggregateEventSourcing<TId> : Audit, IAggregateEventSourcing<TId>
|
||||
public abstract record AggregateEventSourcing<TId> : Entity<TId>, IAggregateEventSourcing<TId>
|
||||
{
|
||||
private readonly List<IDomainEvent> _domainEvents = new();
|
||||
public IReadOnlyList<IDomainEvent> DomainEvents => _domainEvents.AsReadOnly();
|
||||
@ -23,10 +23,6 @@ namespace BuildingBlocks.EventStoreDB.Events
|
||||
}
|
||||
|
||||
public virtual void When(object @event) { }
|
||||
|
||||
public long Version { get; protected set; } = -1;
|
||||
|
||||
public TId Id { get; protected set; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -3,17 +3,15 @@ using BuildingBlocks.Core.Model;
|
||||
|
||||
namespace BuildingBlocks.EventStoreDB.Events
|
||||
{
|
||||
public interface IAggregateEventSourcing : IProjection, IAudit
|
||||
using Microsoft.FSharp.Control;
|
||||
|
||||
public interface IAggregateEventSourcing : IProjection, IEntity
|
||||
{
|
||||
IReadOnlyList<IDomainEvent> DomainEvents { get; }
|
||||
IDomainEvent[] ClearDomainEvents();
|
||||
long Version { get; }
|
||||
}
|
||||
|
||||
public interface IAggregateEventSourcing<out T> : IAggregateEventSourcing
|
||||
public interface IAggregateEventSourcing<T> : IAggregateEventSourcing, IEntity<T>
|
||||
{
|
||||
T Id { get; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -398,11 +398,17 @@ public class TestWriteFixture<TEntryPoint, TWContext> : TestFixture<TEntryPoint>
|
||||
});
|
||||
}
|
||||
|
||||
public Task<T> FindAsync<T>(Guid id)
|
||||
where T : class, IAudit
|
||||
public Task<T> FindAsync<T, TKey>(TKey id)
|
||||
where T : class, IEntity
|
||||
{
|
||||
return ExecuteDbContextAsync(db => db.Set<T>().FindAsync(id).AsTask());
|
||||
}
|
||||
|
||||
public Task<T> FirstOrDefaultAsync<T>()
|
||||
where T : class, IEntity
|
||||
{
|
||||
return ExecuteDbContextAsync(db => db.Set<T>().FirstOrDefaultAsync());
|
||||
}
|
||||
}
|
||||
|
||||
public class TestReadFixture<TEntryPoint, TRContext> : TestFixture<TEntryPoint>
|
||||
|
||||
@ -7,6 +7,7 @@ using BuildingBlocks.Core.Event;
|
||||
using BuildingBlocks.Core.Model;
|
||||
using BuildingBlocks.EventStoreDB.Repository;
|
||||
using BuildingBlocks.Web;
|
||||
using Elasticsearch.Net;
|
||||
using Exceptions;
|
||||
using Flight;
|
||||
using FluentValidation;
|
||||
@ -27,7 +28,7 @@ public record CreateBooking(Guid PassengerId, Guid FlightId, string Description)
|
||||
|
||||
public record CreateBookingResult(ulong Id);
|
||||
|
||||
public record BookingCreatedDomainEvent(Guid Id, PassengerInfo PassengerInfo, Trip Trip) : Audit, IDomainEvent;
|
||||
public record BookingCreatedDomainEvent(Guid Id, PassengerInfo PassengerInfo, Trip Trip) : Entity<Guid>, IDomainEvent;
|
||||
|
||||
public record CreateBookingRequestDto(Guid PassengerId, Guid FlightId, string Description);
|
||||
|
||||
|
||||
@ -54,7 +54,7 @@ public class CreateFlightEndpoint : IMinimalEndpoint
|
||||
|
||||
return Results.CreatedAtRoute("GetFlightById", new { id = result.Id }, response);
|
||||
})
|
||||
.RequireAuthorization()
|
||||
// .RequireAuthorization()
|
||||
.WithName("CreateFlight")
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Flight").Build())
|
||||
.Produces<CreateFlightResponseDto>(StatusCodes.Status201Created)
|
||||
@ -68,7 +68,7 @@ public class CreateFlightEndpoint : IMinimalEndpoint
|
||||
}
|
||||
}
|
||||
|
||||
internal class CreateFlightValidator : AbstractValidator<CreateFlight>
|
||||
public class CreateFlightValidator : AbstractValidator<CreateFlight>
|
||||
{
|
||||
public CreateFlightValidator()
|
||||
{
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
namespace Flight.Identity.Consumers.RegisterNewUser.V1;
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.Contracts.EventBus.Messages;
|
||||
using BuildingBlocks.Web;
|
||||
@ -6,8 +8,6 @@ using MassTransit;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Flight.Identity.Consumers.RegisterNewUser.Consumes.V1;
|
||||
|
||||
public class RegisterNewUserConsumerHandler : IConsumer<UserCreated>
|
||||
{
|
||||
private readonly AppOptions _options;
|
||||
@ -10,8 +10,10 @@ using Xunit;
|
||||
|
||||
namespace Integration.Test.Flight.Features;
|
||||
|
||||
using System;
|
||||
using global::Flight.Data.Seed;
|
||||
using global::Flight.Flights.Features.DeletingFlight.V1;
|
||||
using global::Flight.Flights.Models;
|
||||
|
||||
public class DeleteFlightTests : FlightIntegrationTestBase
|
||||
{
|
||||
@ -24,7 +26,7 @@ public class DeleteFlightTests : FlightIntegrationTestBase
|
||||
public async Task should_delete_flight_from_db()
|
||||
{
|
||||
// Arrange
|
||||
var flightEntity = await Fixture.FindAsync<global::Flight.Flights.Models.Flight>( InitialData.Flights.First().Id);
|
||||
var flightEntity = await Fixture.FindAsync<Flight, Guid>( InitialData.Flights.First().Id);
|
||||
var command = new DeleteFlight(flightEntity.Id);
|
||||
|
||||
// Act
|
||||
|
||||
@ -9,9 +9,11 @@ using Xunit;
|
||||
|
||||
namespace Integration.Test.Flight.Features;
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using global::Flight.Data.Seed;
|
||||
using global::Flight.Flights.Features.UpdatingFlight.V1;
|
||||
using global::Flight.Flights.Models;
|
||||
|
||||
public class UpdateFlightTests : FlightIntegrationTestBase
|
||||
{
|
||||
@ -24,7 +26,7 @@ public class UpdateFlightTests : FlightIntegrationTestBase
|
||||
public async Task should_update_flight_to_db_and_publish_message_to_broker()
|
||||
{
|
||||
// Arrange
|
||||
var flightEntity = await Fixture.FindAsync<global::Flight.Flights.Models.Flight>( InitialData.Flights.First().Id);
|
||||
var flightEntity = await Fixture.FindAsync<Flight, Guid>( InitialData.Flights.First().Id);
|
||||
var command = new FakeUpdateFlightCommand(flightEntity).Generate();
|
||||
|
||||
// Act
|
||||
|
||||
@ -5,80 +5,88 @@ using Flight.Flights.Enums;
|
||||
using Flight.Seats.Enums;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Unit.Test.Common
|
||||
namespace Unit.Test.Common;
|
||||
|
||||
using MassTransit;
|
||||
|
||||
public static class DbContextFactory
|
||||
{
|
||||
using MassTransit;
|
||||
private static readonly Guid _airportId1 = NewId.NextGuid();
|
||||
private static readonly Guid _airportId2 = NewId.NextGuid();
|
||||
private static readonly Guid _aircraft1 = NewId.NextGuid();
|
||||
private static readonly Guid _aircraft2 = NewId.NextGuid();
|
||||
private static readonly Guid _aircraft3 = NewId.NextGuid();
|
||||
private static readonly Guid _flightId1 = NewId.NextGuid();
|
||||
|
||||
public static class DbContextFactory
|
||||
public static FlightDbContext Create()
|
||||
{
|
||||
private static readonly Guid _airportId1 = NewId.NextGuid();
|
||||
private static readonly Guid _airportId2 = NewId.NextGuid();
|
||||
private static readonly Guid _aircraft1 = NewId.NextGuid();
|
||||
private static readonly Guid _aircraft2 = NewId.NextGuid();
|
||||
private static readonly Guid _aircraft3 = NewId.NextGuid();
|
||||
private static readonly Guid _flightId1 = NewId.NextGuid();
|
||||
var options = new DbContextOptionsBuilder<FlightDbContext>()
|
||||
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()).Options;
|
||||
|
||||
public static FlightDbContext Create()
|
||||
var context = new FlightDbContext(options, currentUserProvider: null);
|
||||
|
||||
// Seed our data
|
||||
FlightDataSeeder(context);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
private static void FlightDataSeeder(FlightDbContext context)
|
||||
{
|
||||
var airports = new List<global::Flight.Airports.Models.Airport>
|
||||
{
|
||||
var options = new DbContextOptionsBuilder<FlightDbContext>()
|
||||
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()).Options;
|
||||
global::Flight.Airports.Models.Airport.Create(_airportId1, "Lisbon International Airport", "LIS",
|
||||
"12988"),
|
||||
global::Flight.Airports.Models.Airport.Create(_airportId2, "Sao Paulo International Airport", "BRZ",
|
||||
"11200")
|
||||
};
|
||||
|
||||
var context = new FlightDbContext(options, currentUserProvider: null);
|
||||
context.Airports.AddRange(airports);
|
||||
|
||||
// Seed our data
|
||||
FlightDataSeeder(context);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
private static void FlightDataSeeder(FlightDbContext context)
|
||||
var aircrafts = new List<global::Flight.Aircrafts.Models.Aircraft>
|
||||
{
|
||||
var airports = new List<global::Flight.Airports.Models.Airport>
|
||||
{
|
||||
global::Flight.Airports.Models.Airport.Create(_airportId1, "Lisbon International Airport", "LIS", "12988"),
|
||||
global::Flight.Airports.Models.Airport.Create(_airportId2, "Sao Paulo International Airport", "BRZ", "11200")
|
||||
};
|
||||
global::Flight.Aircrafts.Models.Aircraft.Create(_aircraft1, "Boeing 737", "B737", 2005),
|
||||
global::Flight.Aircrafts.Models.Aircraft.Create(_aircraft2, "Airbus 300", "A300", 2000),
|
||||
global::Flight.Aircrafts.Models.Aircraft.Create(_aircraft3, "Airbus 320", "A320", 2003)
|
||||
};
|
||||
|
||||
context.Airports.AddRange(airports);
|
||||
context.Aircraft.AddRange(aircrafts);
|
||||
|
||||
var aircrafts = new List<global::Flight.Aircrafts.Models.Aircraft>
|
||||
{
|
||||
global::Flight.Aircrafts.Models.Aircraft.Create(_aircraft1, "Boeing 737", "B737", 2005),
|
||||
global::Flight.Aircrafts.Models.Aircraft.Create(_aircraft2, "Airbus 300", "A300", 2000),
|
||||
global::Flight.Aircrafts.Models.Aircraft.Create(_aircraft3, "Airbus 320", "A320", 2003)
|
||||
};
|
||||
|
||||
context.Aircraft.AddRange(aircrafts);
|
||||
|
||||
var flights = new List<global::Flight.Flights.Models.Flight>
|
||||
{
|
||||
global::Flight.Flights.Models.Flight.Create(_flightId1, "BD467", _aircraft1, _airportId1, new DateTime(2022, 1, 31, 12, 0, 0),
|
||||
new DateTime(2022, 1, 31, 14, 0, 0),
|
||||
_airportId2, 120m,
|
||||
new DateTime(2022, 1, 31), FlightStatus.Completed,
|
||||
8000)
|
||||
};
|
||||
context.Flights.AddRange(flights);
|
||||
|
||||
var seats = new List<global::Flight.Seats.Models.Seat>
|
||||
{
|
||||
global::Flight.Seats.Models.Seat.Create(NewId.NextGuid(), "12A", SeatType.Window, SeatClass.Economy, _flightId1),
|
||||
global::Flight.Seats.Models.Seat.Create(NewId.NextGuid(), "12B", SeatType.Window, SeatClass.Economy, _flightId1),
|
||||
global::Flight.Seats.Models.Seat.Create(NewId.NextGuid(), "12C", SeatType.Middle, SeatClass.Economy, _flightId1),
|
||||
global::Flight.Seats.Models.Seat.Create(NewId.NextGuid(), "12D", SeatType.Middle, SeatClass.Economy, _flightId1),
|
||||
global::Flight.Seats.Models.Seat.Create(NewId.NextGuid(), "12E", SeatType.Aisle, SeatClass.Economy, _flightId1),
|
||||
global::Flight.Seats.Models.Seat.Create(NewId.NextGuid(), "12F", SeatType.Aisle, SeatClass.Economy, _flightId1)
|
||||
};
|
||||
|
||||
context.Seats.AddRange(seats);
|
||||
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
public static void Destroy(FlightDbContext context)
|
||||
var flights = new List<global::Flight.Flights.Models.Flight>
|
||||
{
|
||||
context.Database.EnsureDeleted();
|
||||
context.Dispose();
|
||||
}
|
||||
global::Flight.Flights.Models.Flight.Create(_flightId1, "BD467", _aircraft1, _airportId1,
|
||||
new DateTime(2022, 1, 31, 12, 0, 0),
|
||||
new DateTime(2022, 1, 31, 14, 0, 0),
|
||||
_airportId2, 120m,
|
||||
new DateTime(2022, 1, 31), FlightStatus.Completed,
|
||||
8000)
|
||||
};
|
||||
context.Flights.AddRange(flights);
|
||||
|
||||
var seats = new List<global::Flight.Seats.Models.Seat>
|
||||
{
|
||||
global::Flight.Seats.Models.Seat.Create(NewId.NextGuid(), "12A", SeatType.Window, SeatClass.Economy,
|
||||
_flightId1),
|
||||
global::Flight.Seats.Models.Seat.Create(NewId.NextGuid(), "12B", SeatType.Window, SeatClass.Economy,
|
||||
_flightId1),
|
||||
global::Flight.Seats.Models.Seat.Create(NewId.NextGuid(), "12C", SeatType.Middle, SeatClass.Economy,
|
||||
_flightId1),
|
||||
global::Flight.Seats.Models.Seat.Create(NewId.NextGuid(), "12D", SeatType.Middle, SeatClass.Economy,
|
||||
_flightId1),
|
||||
global::Flight.Seats.Models.Seat.Create(NewId.NextGuid(), "12E", SeatType.Aisle, SeatClass.Economy,
|
||||
_flightId1),
|
||||
global::Flight.Seats.Models.Seat.Create(NewId.NextGuid(), "12F", SeatType.Aisle, SeatClass.Economy,
|
||||
_flightId1)
|
||||
};
|
||||
|
||||
context.Seats.AddRange(seats);
|
||||
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
public static void Destroy(FlightDbContext context)
|
||||
{
|
||||
context.Database.EnsureDeleted();
|
||||
context.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
namespace Unit.Test.Flight.Features.Domain
|
||||
namespace Unit.Test.Flight.Features.Domains
|
||||
{
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
@ -1,4 +1,4 @@
|
||||
namespace Unit.Test.Flight.Features.Domain;
|
||||
namespace Unit.Test.Flight.Features.Domains;
|
||||
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
@ -1,13 +1,12 @@
|
||||
namespace Unit.Test.Flight.Features.Commands.CreateFlight;
|
||||
namespace Unit.Test.Flight.Features.Handlers.CreateFlight;
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using global::Flight.Flights.Dtos;
|
||||
using global::Flight.Flights.Features.CreatingFlight.V1;
|
||||
using Common;
|
||||
using Fakes;
|
||||
using Unit.Test.Common;
|
||||
using Unit.Test.Fakes;
|
||||
using Xunit;
|
||||
|
||||
[Collection(nameof(UnitTestFixture))]
|
||||
@ -1,4 +1,4 @@
|
||||
namespace Unit.Test.Flight.Features.Commands.CreateFlight;
|
||||
namespace Unit.Test.Flight.Features.Handlers.CreateFlight;
|
||||
|
||||
using FluentAssertions;
|
||||
using FluentValidation.TestHelper;
|
||||
@ -63,7 +63,7 @@ public sealed class IdentityContext : IdentityDbContext<User, Role, Guid,
|
||||
public IReadOnlyList<IDomainEvent> GetDomainEvents()
|
||||
{
|
||||
var domainEntities = ChangeTracker
|
||||
.Entries<Aggregate>()
|
||||
.Entries<IAggregate>()
|
||||
.Where(x => x.Entity.DomainEvents.Any())
|
||||
.Select(x => x.Entity)
|
||||
.ToList();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user