Skip to content

Commit

Permalink
initial
Browse files Browse the repository at this point in the history
  • Loading branch information
kubalone committed May 5, 2024
0 parents commit fdf2f26
Show file tree
Hide file tree
Showing 130 changed files with 11,533 additions and 0 deletions.
382 changes: 382 additions & 0 deletions .editorconfig

Large diffs are not rendered by default.

480 changes: 480 additions & 0 deletions .gitignore

Large diffs are not rendered by default.

70 changes: 70 additions & 0 deletions AML.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Domain", "src\Domain\Domain.csproj", "{C7E89A3E-A631-4760-8D61-BD1EAB1C4E69}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Application", "src\Application\Application.csproj", "{34C0FACD-F3D9-400C-8945-554DD6B0819A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Infrastructure", "src\Infrastructure\Infrastructure.csproj", "{117DA02F-5274-4565-ACC6-DA9B6E568B09}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{6ED356A7-8B47-4613-AD01-C85CF28491BD}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{664D406C-2F83-48F0-BFC3-408D5CB53C65}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E2DA20AA-28D1-455C-BF50-C49A8F831633}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
.gitignore = .gitignore
Directory.Build.props = Directory.Build.props
Directory.Packages.props = Directory.Packages.props
global.json = global.json
README.md = README.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Web", "src\Web\Web.csproj", "{4E4EE20C-F06A-4A1B-851F-C5577796941C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Application.FunctionalTests", "tests\Application.FunctionalTests\Application.FunctionalTests.csproj", "{EA6127A5-94C9-4C31-AD11-E6811B92B520}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C7E89A3E-A631-4760-8D61-BD1EAB1C4E69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C7E89A3E-A631-4760-8D61-BD1EAB1C4E69}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C7E89A3E-A631-4760-8D61-BD1EAB1C4E69}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C7E89A3E-A631-4760-8D61-BD1EAB1C4E69}.Release|Any CPU.Build.0 = Release|Any CPU
{34C0FACD-F3D9-400C-8945-554DD6B0819A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{34C0FACD-F3D9-400C-8945-554DD6B0819A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{34C0FACD-F3D9-400C-8945-554DD6B0819A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{34C0FACD-F3D9-400C-8945-554DD6B0819A}.Release|Any CPU.Build.0 = Release|Any CPU
{117DA02F-5274-4565-ACC6-DA9B6E568B09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{117DA02F-5274-4565-ACC6-DA9B6E568B09}.Debug|Any CPU.Build.0 = Debug|Any CPU
{117DA02F-5274-4565-ACC6-DA9B6E568B09}.Release|Any CPU.ActiveCfg = Release|Any CPU
{117DA02F-5274-4565-ACC6-DA9B6E568B09}.Release|Any CPU.Build.0 = Release|Any CPU
{4E4EE20C-F06A-4A1B-851F-C5577796941C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4E4EE20C-F06A-4A1B-851F-C5577796941C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4E4EE20C-F06A-4A1B-851F-C5577796941C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4E4EE20C-F06A-4A1B-851F-C5577796941C}.Release|Any CPU.Build.0 = Release|Any CPU
{EA6127A5-94C9-4C31-AD11-E6811B92B520}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EA6127A5-94C9-4C31-AD11-E6811B92B520}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EA6127A5-94C9-4C31-AD11-E6811B92B520}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EA6127A5-94C9-4C31-AD11-E6811B92B520}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{C7E89A3E-A631-4760-8D61-BD1EAB1C4E69} = {6ED356A7-8B47-4613-AD01-C85CF28491BD}
{34C0FACD-F3D9-400C-8945-554DD6B0819A} = {6ED356A7-8B47-4613-AD01-C85CF28491BD}
{117DA02F-5274-4565-ACC6-DA9B6E568B09} = {6ED356A7-8B47-4613-AD01-C85CF28491BD}
{4E4EE20C-F06A-4A1B-851F-C5577796941C} = {6ED356A7-8B47-4613-AD01-C85CF28491BD}
{EA6127A5-94C9-4C31-AD11-E6811B92B520} = {664D406C-2F83-48F0-BFC3-408D5CB53C65}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3CB609D9-5D54-4C11-A371-DAAC8B74E430}
EndGlobalSection
EndGlobal
10 changes: 10 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!-- See https://aka.ms/dotnet/msbuild/customize for more details on customizing your build -->
<Project>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<!-- <ArtifactsPath>$(MSBuildThisFileDirectory)artifacts</ArtifactsPath> -->
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
44 changes: 44 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<!-- For more info on central package management go to https://devblogs.microsoft.com/nuget/introducing-central-package-management/ -->
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Ardalis.GuardClauses" Version="4.2.0" />
<PackageVersion Include="AutoMapper" Version="13.0.1" />
<PackageVersion Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.3.0" />
<PackageVersion Include="Azure.Identity" Version="1.10.4" />
<PackageVersion Include="coverlet.collector" Version="6.0.0" />
<PackageVersion Include="FluentAssertions" Version="6.12.0" />
<PackageVersion Include="FluentValidation.AspNetCore" Version="11.3.0" />
<PackageVersion Include="FluentValidation.DependencyInjectionExtensions" Version="11.8.1" />
<PackageVersion Include="MediatR" Version="12.2.0" />
<PackageVersion Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="8.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="8.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Identity.UI" Version="8.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.SpaProxy" Version="8.0.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="8.0.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.2" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="8.0.2" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageVersion Include="Moq" Version="4.20.69" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="NSwag.AspNetCore" Version="14.0.7" />
<PackageVersion Include="NSwag.MSBuild" Version="14.0.3" />
<PackageVersion Include="nunit" Version="3.14.0" />
<PackageVersion Include="NUnit.Analyzers" Version="3.9.0" />
<PackageVersion Include="NUnit3TestAdapter" Version="4.5.0" />
<PackageVersion Include="Respawn" Version="6.1.0" />
<PackageVersion Include="Testcontainers.MsSql" Version="3.6.0" />
<PackageVersion Include="Microsoft.Playwright" Version="1.40.0" />
<PackageVersion Include="SpecFlow.Plus.LivingDocPlugin" Version="3.9.57" />
<PackageVersion Include="SpecFlow.NUnit" Version="3.9.74" />
</ItemGroup>
</Project>
1 change: 1 addition & 0 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
https://localhost:5001/api
20 changes: 20 additions & 0 deletions src/Application/Application.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<RootNamespace>AML.Application</RootNamespace>
<AssemblyName>AML.Application</AssemblyName>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Ardalis.GuardClauses" />
<PackageReference Include="AutoMapper" />
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" />
<PackageReference Include="Microsoft.EntityFrameworkCore" />
<PackageReference Include="Newtonsoft.Json" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Domain\Domain.csproj" />
</ItemGroup>

</Project>
35 changes: 35 additions & 0 deletions src/Application/Common/Behaviours/ValidationBehaviour.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using ValidationException = AML.Application.Common.Exceptions.ValidationException;

namespace AML.Application.Common.Behaviours;

public class ValidationBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : notnull
{
private readonly IEnumerable<IValidator<TRequest>> _validators;

public ValidationBehaviour(IEnumerable<IValidator<TRequest>> validators)
{
_validators = validators;
}

public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
{
if (_validators.Any())
{
var context = new ValidationContext<TRequest>(request);

var validationResults = await Task.WhenAll(
_validators.Select(v =>
v.ValidateAsync(context, cancellationToken)));

var failures = validationResults
.Where(r => r.Errors.Any())
.SelectMany(r => r.Errors)
.ToList();

if (failures.Any())
throw new ValidationException(failures);
}
return await next();
}
}
22 changes: 22 additions & 0 deletions src/Application/Common/Exceptions/ValidationException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using FluentValidation.Results;

namespace AML.Application.Common.Exceptions;

public class ValidationException : Exception
{
public ValidationException()
: base("One or more validation failures have occurred.")
{
Errors = new Dictionary<string, string[]>();
}

public ValidationException(IEnumerable<ValidationFailure> failures)
: this()
{
Errors = failures
.GroupBy(e => e.PropertyName, e => e.ErrorMessage)
.ToDictionary(failureGroup => failureGroup.Key, failureGroup => failureGroup.ToArray());
}

public IDictionary<string, string[]> Errors { get; }
}
20 changes: 20 additions & 0 deletions src/Application/Common/Interfaces/IApplicationDbContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using AML.Domain.Entities;

namespace AML.Application.Common.Interfaces;

public interface IApplicationDbContext
{


DbSet<Transaction> Transactions { get; }

DbSet<Address> Addresses { get; }

DbSet<Customer> Customers { get; }

DbSet<TransactionLimit> TransactionLimits { get; }
DbSet<LimitChangeHistory> LimitChangesHistory { get; }
DbSet<CustomerBalance> CustomerBalances { get; }

Task<int> SaveChangesAsync(CancellationToken cancellationToken);
}
12 changes: 12 additions & 0 deletions src/Application/Common/Mappings/MappingExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using AML.Application.Common.Models;

namespace AML.Application.Common.Mappings;

public static class MappingExtensions
{
public static Task<PaginatedList<TDestination>> PaginatedListAsync<TDestination>(this IQueryable<TDestination> queryable, int pageNumber, int pageSize) where TDestination : class
=> PaginatedList<TDestination>.CreateAsync(queryable.AsNoTracking(), pageNumber, pageSize);

public static Task<List<TDestination>> ProjectToListAsync<TDestination>(this IQueryable queryable, IConfigurationProvider configuration) where TDestination : class
=> queryable.ProjectTo<TDestination>(configuration).AsNoTracking().ToListAsync();
}
29 changes: 29 additions & 0 deletions src/Application/Common/Models/PaginatedList.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
namespace AML.Application.Common.Models;

public class PaginatedList<T>
{
public IReadOnlyCollection<T> Items { get; }
public int PageNumber { get; }
public int TotalPages { get; }
public int TotalCount { get; }

public PaginatedList(IReadOnlyCollection<T> items, int count, int pageNumber, int pageSize)
{
PageNumber = pageNumber;
TotalPages = (int)Math.Ceiling(count / (double)pageSize);
TotalCount = count;
Items = items;
}

public bool HasPreviousPage => PageNumber > 1;

public bool HasNextPage => PageNumber < TotalPages;

public static async Task<PaginatedList<T>> CreateAsync(IQueryable<T> source, int pageNumber, int pageSize)
{
var count = await source.CountAsync();
var items = await source.Skip((pageNumber - 1) * pageSize).Take(pageSize).ToListAsync();

return new PaginatedList<T>(items, count, pageNumber, pageSize);
}
}
24 changes: 24 additions & 0 deletions src/Application/Common/Models/Result.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace AML.Application.Common.Models;

public class Result
{
internal Result(bool succeeded, IEnumerable<string> errors)
{
Succeeded = succeeded;
Errors = errors.ToArray();
}

public bool Succeeded { get; init; }

public string[] Errors { get; init; }

public static Result Success()
{
return new Result(true, Array.Empty<string>());
}

public static Result Failure(IEnumerable<string> errors)
{
return new Result(false, errors);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using System.Text.Json.Serialization;
using AML.Application.Common.Interfaces;
using AML.Domain.Entities;
using AML.Domain.Enums;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;

namespace AML.Application.Customers.Commands.CreateCustomer;

public record CreateCustomerCommand : IRequest<int>
{
public string? FirstName { get; set; }
public string? LastName { get; set; }
public DateTime DateOfBirth { get; set; }
public string? Street { get; set; }
public string? City { get; set; }
public string? ZipCode { get; set; }
public string? Country { get; set; }
public ICollection<CustomerBalanceDto>? Balances { get; init; }

}

public record CustomerBalanceDto
{
public CurrencyType Currency { get; set; }
public decimal Balance { get; set; }
}

public class CreateCustomerCommandHandler(IApplicationDbContext context) : IRequestHandler<CreateCustomerCommand, int>
{


public async Task<int> Handle(CreateCustomerCommand command, CancellationToken cancellationToken)
{
var customer = await CreateCustomerAsync(command, cancellationToken);
await AddCustomerBalancesAsync(command, customer.Id, cancellationToken);
return customer.Id;

}
private async Task<Customer> CreateCustomerAsync(CreateCustomerCommand command, CancellationToken cancellationToken)
{
var customer = new Customer
{
CustomerIdentifier= Guid.NewGuid(),
FirstName = command.FirstName,
LastName = command.LastName,
DateOfBirth = command.DateOfBirth,
Address = new Address
{
Street = command.Street,
City = command.City,
ZipCode = command.ZipCode,
Country = command.Country
}
};

context.Customers.Add(customer);
await context.SaveChangesAsync(cancellationToken);
return customer;
}

private async Task AddCustomerBalancesAsync(CreateCustomerCommand command, int customerId, CancellationToken cancellationToken)
{
if (command.Balances != null)
{
foreach (var balanceDto in command.Balances)
{
var customerBalance = new CustomerBalance
{
CustomerId = customerId,
Currency = balanceDto.Currency,
Balance = balanceDto.Balance
};

context.CustomerBalances.Add(customerBalance);
}

await context.SaveChangesAsync(cancellationToken);
}
}
}
Loading

0 comments on commit fdf2f26

Please sign in to comment.