Skip to content

Commit

Permalink
Merge branch 'main' into revert-63-revert-56-update-expo-packages
Browse files Browse the repository at this point in the history
  • Loading branch information
raymens committed Jun 25, 2024
2 parents 022153e + 5df70eb commit a558858
Show file tree
Hide file tree
Showing 93 changed files with 5,246 additions and 3,184 deletions.
14 changes: 7 additions & 7 deletions backend/core/src/Core.API/Core.API.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,19 @@

<ItemGroup>
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.9.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.2" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.2">
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.4" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.2">
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.6" />
<PackageReference Include="Quantoz.Nexus.Sdk.Shared" Version="1.4.0" />
<PackageReference Include="Quantoz.Nexus.Sdk.Token" Version="1.4.0" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.20.1" />
<PackageReference Include="Quantoz.Nexus.Sdk.Shared" Version="1.10.0" />
<PackageReference Include="Quantoz.Nexus.Sdk.Token" Version="1.10.0" />
<PackageReference Include="Quartz.Extensions.DependencyInjection" Version="3.8.1" />
<PackageReference Include="Quartz.Extensions.Hosting" Version="3.8.1" />
<PackageReference Include="ReHackt.Extensions.Options.Validation" Version="8.0.2" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using Core.Domain.Abstractions;
using Core.Domain.Repositories;
using Core.Infrastructure.AzureB2CGraphService;
using Core.Infrastructure.Compliance;
using Core.Infrastructure.Compliance.IPLocator;
using Core.Infrastructure.Compliance.Sanctionlist;
Expand All @@ -14,6 +15,7 @@
using Core.Infrastructure.Nexus.Repositories;
using Core.Infrastructure.Nexus.SigningService;
using Microsoft.Extensions.Options;
using Microsoft.Graph;
using Nexus.Sdk.Shared.Http;
using Nexus.Sdk.Token.Extensions;
using Quartz;
Expand All @@ -25,6 +27,7 @@ public static class InfrastructureInjection
public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
{
services
.AddB2CGraphService(configuration)
.AddNexus(configuration)
.AddIPLocator(configuration)
.AddSanctionlist(configuration)
Expand Down Expand Up @@ -83,6 +86,23 @@ private static IServiceCollection AddIPLocator(this IServiceCollection services,
return services;
}

public static IServiceCollection AddB2CGraphService(this IServiceCollection services, IConfiguration configuration)
{
services.AddOptions<B2CServiceOptions>()
.Bind(configuration.GetSection("B2CServiceOptions"))
.ValidateDataAnnotationsRecursively()
.ValidateOnStart();

services.AddSingleton(sp => sp.GetRequiredService<IOptions<B2CServiceOptions>>().Value);

services.AddHttpClient<IB2CGraphService, B2CGraphService>();

services.AddScoped<IB2CGraphService, B2CGraphService>();
services.AddScoped<GraphServiceClient>();

return services;
}

private static IServiceCollection AddSanctionlist(this IServiceCollection services, IConfiguration configuration)
{
services.AddOptions<SanctionlistOptions>()
Expand Down
2 changes: 1 addition & 1 deletion backend/core/src/Core.API/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@

app.ConfigureCustomAuthenticationMiddleware();
app.ConfigureCustomExceptionMiddleware();
app.ConfigureSignatureVerificationMiddleware();

app.UseAuthentication();
app.UseRouting();
app.UseAuthorization();

app.ConfigureSignatureVerificationMiddleware();
app.UseMiddleware<PublicKeyLinkedMiddleware>();

app.MapControllers();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,18 @@ namespace Core.API.ResponseHandling;
/// </summary>
public class PublicKeyLinkedMiddleware
{
private readonly ICustomerDeviceRepository _customerDeviceRepository;
private readonly RequestDelegate _next;
private readonly ILogger<PublicKeyLinkedMiddleware> _logger;

public PublicKeyLinkedMiddleware(
ICustomerDeviceRepository customerDeviceRepository,
RequestDelegate next,
ILogger<PublicKeyLinkedMiddleware> logger)
{
_customerDeviceRepository = customerDeviceRepository;
_next = next;
_logger = logger;
}

public async Task Invoke(HttpContext context)
public async Task Invoke(HttpContext context, ICustomerDeviceRepository customerDeviceRepository)
{
// skip the middleware for specific endpoints
if (!SkipEndpoint(context))
Expand All @@ -40,7 +37,7 @@ public async Task Invoke(HttpContext context)
var pubKey = context.Request.Headers["x-public-key"];

// get all public keys linked to the user
var customerDevices = await _customerDeviceRepository.GetAsync(userId, context.RequestAborted);
var customerDevices = await customerDeviceRepository.GetAsync(userId, context.RequestAborted);

// if the user does not have any public keys or the public key is not linked to the user, return forbidden
if (customerDevices is null
Expand Down Expand Up @@ -82,7 +79,7 @@ private static bool SkipEndpoint(HttpContext context)
var endpoint = context.GetEndpoint();
var endpointName = endpoint?.Metadata.GetMetadata<EndpointNameMetadata>()?.EndpointName;

var excludeList = new[] { "DeviceAuthentication" };
var excludeList = new[] { "DeviceAuthentication", "SendOTPCodeEmail" };

return context.Request.Path.StartsWithSegments("/health")
|| excludeList.Contains(endpointName);
Expand Down
10 changes: 8 additions & 2 deletions backend/core/src/Core.API/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
"Audience": ""
}
],
"B2CServiceOptions": {
"ClientId": "",
"ClientSecret": "",
"Tenant": ""
},
"NexusOptions": {
"ApiUrl": "",
"PaymentMethodOptions": {
Expand Down Expand Up @@ -60,12 +65,13 @@
"StorageConnectionString": "",
"ContainerName": ""
},
"SendGridMailServiceOptions": {
"SendGridMailServiceOptions": {
"ApiKey": "",
"Sender": "",
"Templates": {
"WithdrawalTemplateID": "",
"FundingtemplateID": ""
"FundingtemplateID": "",
"OTPCodeTemplateID": ""
}
},
"ConnectionStrings": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright 2023 Quantoz Technology B.V. and contributors. Licensed
// under the Apache License, Version 2.0. See the NOTICE file at the root
// of this distribution or at http://www.apache.org/licenses/LICENSE-2.0

using Core.Domain.Repositories;
using MediatR;

namespace Core.Application.Commands.CustomerCommands
{
public class DeleteCustomerCommand : IRequest
{
public string CustomerCode { get; set; }

public string IP { get; set; }

public DeleteCustomerCommand(string customerCode, string iP)
{
CustomerCode = customerCode;
IP = iP;
}
}

public class DeleteCustomerCommandHandler : IRequestHandler<DeleteCustomerCommand>
{
private readonly ICustomerRepository _customerRepository;

public DeleteCustomerCommandHandler(
ICustomerRepository customerRepository)
{
_customerRepository = customerRepository;
}

public async Task Handle(DeleteCustomerCommand request, CancellationToken cancellationToken)
{
var customer = await _customerRepository.GetAsync(request.CustomerCode, cancellationToken);

await _customerRepository.DeleteAsync(customer, request.IP);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright 2023 Quantoz Technology B.V. and contributors. Licensed
// under the Apache License, Version 2.0. See the NOTICE file at the root
// of this distribution or at http://www.apache.org/licenses/LICENSE-2.0

using Core.Domain;
using Core.Domain.Abstractions;
using Core.Domain.Exceptions;
using Core.Domain.Repositories;
using MediatR;

namespace Core.Application.Commands.CustomerCommands
{
public class OTPCodeByEmailCommand : IRequest
{
public string CustomerCode { get; set; }

public string IP { get; set; }

public OTPCodeByEmailCommand(string customerCode, string ip)
{
CustomerCode = customerCode;
IP = ip;
}
}

public class OTPCodeByEmailCommandHandler : IRequestHandler<OTPCodeByEmailCommand>
{
private readonly ICustomerRepository _customerRepository;
private readonly ICustomerDeviceRepository _customerDeviceRepository;
private readonly ICustomerOTPGenerator _otpGenerator;
private readonly ISendGridMailService _sendGridMailService;
private readonly IUnitOfWork _unitOfWork;

public OTPCodeByEmailCommandHandler(
ICustomerRepository customerRepository,
ICustomerDeviceRepository customerDeviceRepository,
ICustomerOTPGenerator otpGenerator,
ISendGridMailService sendGridMailService,
IUnitOfWork unitOfWork)
{
_customerRepository = customerRepository;
_customerDeviceRepository = customerDeviceRepository;
_otpGenerator = otpGenerator;
_sendGridMailService = sendGridMailService;
_unitOfWork = unitOfWork;
}

public async Task Handle(OTPCodeByEmailCommand request, CancellationToken cancellationToken)
{
var customer = await _customerRepository.GetAsync(request.CustomerCode, cancellationToken);

if (customer == null)
{
throw new CustomErrorsException(DomainErrorCode.CustomerNotFoundError.ToString(), request.CustomerCode, "Customer not found.");
}

if (customer.Status != CustomerStatus.ACTIVE.ToString())
{
throw new CustomErrorsException(DomainErrorCode.CustomerNotActiveError.ToString(), request.CustomerCode, "Customer is not ACTIVE.");
}

var customerDevice = await _customerDeviceRepository.GetAsync(request.CustomerCode, cancellationToken);

// rare case of exception
if (customerDevice == null || string.IsNullOrEmpty(customerDevice.OTPKey))
{
throw new CustomErrorsException(DomainErrorCode.CustomerNotFoundError.ToString(), request.CustomerCode, "Customer device key not found.");
}

// Generate OTPCode
var otpCode = _otpGenerator.GenerateOTPCode(customerDevice.OTPKey);

// Send OTPCode to customer email
await _sendGridMailService.SendOTPCodeMailAsync(customer, otpCode);

await _unitOfWork.SaveChangesAsync(cancellationToken);
}
}
}
2 changes: 1 addition & 1 deletion backend/core/src/Core.Application/Core.Application.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<PackageReference Include="FluentValidation" Version="11.9.0" />
<PackageReference Include="MediatR" Version="12.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Http.Features" Version="5.0.17" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" />
</ItemGroup>

<ItemGroup>
Expand Down
3 changes: 2 additions & 1 deletion backend/core/src/Core.Application/Enums.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public enum ApplicationErrorCode
{
InvalidStatusError,
InvalidPropertyError,
ExistingPropertyError
ExistingPropertyError,
NotFoundError
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
// under the Apache License, Version 2.0. See the NOTICE file at the root
// of this distribution or at http://www.apache.org/licenses/LICENSE-2.0

using Core.Domain;
using Core.Domain.Entities.CustomerAggregate;
using Core.Domain.Exceptions;
using Core.Domain.Repositories;
using MediatR;

Expand Down Expand Up @@ -33,11 +31,6 @@ public async Task<Customer> Handle(GetCustomerQuery request, CancellationToken c
{
var customer = await _customerRepository.GetAsync(request.CustomerCode);

if (customer.Status != CustomerStatus.ACTIVE.ToString() && customer.Status != CustomerStatus.UNDERREVIEW.ToString())
{
throw new CustomErrorsException(ApplicationErrorCode.InvalidStatusError.ToString(), request.CustomerCode, "Customer status with this reference is not valid");
}

return customer;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,7 @@ namespace Core.Domain.Abstractions
public interface ISendGridMailService
{
public Task SendMailAsync(Mail mail, Customer customer, Transaction transaction);

public Task SendOTPCodeMailAsync(Customer customer, string otpCode);
}
}
4 changes: 3 additions & 1 deletion backend/core/src/Core.Domain/Enums.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ public enum DomainErrorCode
InvalidStatusError,
InvalidPropertyError,
ExistingKeyError,
SecurityCheckError
SecurityCheckError,
CustomerNotFoundError,
CustomerNotActiveError,
}

public enum PaymentRequestStatus
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,7 @@ public interface ICustomerRepository
public Task<IEnumerable<CustomerLimit>> GetLimitsAsync(string customerCode, CancellationToken cancellationToken = default);

public Task UpdateAsync(Customer customer, CancellationToken cancellationToken = default);

public Task DeleteAsync(Customer customer, string? ip = null, CancellationToken cancellationToken = default);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public interface ITransactionRepository

public Task<Paged<Transaction>> GetAsync(string publicKey, int page, int pageSize, CancellationToken cancellationToken = default);

public Task<Paged<Transaction>> GetByCodeAsync(string code, CancellationToken cancellationToken = default);
public Task<Paged<Transaction>> GetAsync(Dictionary<string, string> parameters, int page, int pageSize, CancellationToken cancellationToken = default);

public Task<WithdrawFees> GetWithdrawFeesAsync(Withdraw withdraw, CancellationToken cancellationToken = default);
}
Expand Down
Loading

0 comments on commit a558858

Please sign in to comment.