From f4719a8044aa0ca42ced19d71f16b89c717412a5 Mon Sep 17 00:00:00 2001 From: nagarwal4 <107046041+nagarwal4@users.noreply.github.com> Date: Fri, 19 Apr 2024 13:42:42 +0200 Subject: [PATCH] Delete customer functionality (#81) * Initial draft * Some more initial changes * Delete user funnctionality * Prepend comments to source files * Fixed tests * Removed hardcoded code for testing --------- Co-authored-by: GitHub Action --- backend/core/src/Core.API/Core.API.csproj | 4 +- .../InfrastructureInjection.cs | 20 +++ backend/core/src/Core.API/appsettings.json | 5 + .../CustomerCommands/DeleteCustomerCommand.cs | 40 ++++++ backend/core/src/Core.Application/Enums.cs | 3 +- .../Repositories/ICustomerRepository.cs | 2 + .../AzureB2CGraphService/B2CGraphService.cs | 67 ++++++++++ .../AzureB2CGraphService/B2CServiceOptions.cs | 20 +++ .../AzureB2CGraphService/IB2CGraphService.cs | 13 ++ .../Core.Infrastructure.csproj | 4 +- .../src/Core.Infrastructure/Nexus/Enums.cs | 3 +- .../Repositories/NexusCustomerRepository.cs | 114 +++++++++++++++++- .../Controllers/CustomersController.cs | 14 ++- .../Helpers/NexusSDKHelper.cs | 1 + .../NexusCustomerRepositoryTests.cs | 35 +++++- mobile/src/context/AppStateContext.tsx | 4 + .../__tests__/WelcomeStack.test.tsx | 4 + mobile/src/utils/functions.ts | 4 + 18 files changed, 343 insertions(+), 14 deletions(-) create mode 100644 backend/core/src/Core.Application/Commands/CustomerCommands/DeleteCustomerCommand.cs create mode 100644 backend/core/src/Core.Infrastructure/AzureB2CGraphService/B2CGraphService.cs create mode 100644 backend/core/src/Core.Infrastructure/AzureB2CGraphService/B2CServiceOptions.cs create mode 100644 backend/core/src/Core.Infrastructure/AzureB2CGraphService/IB2CGraphService.cs diff --git a/backend/core/src/Core.API/Core.API.csproj b/backend/core/src/Core.API/Core.API.csproj index 3948f181..6944383b 100644 --- a/backend/core/src/Core.API/Core.API.csproj +++ b/backend/core/src/Core.API/Core.API.csproj @@ -22,8 +22,8 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - - + + diff --git a/backend/core/src/Core.API/DependencyInjection/InfrastructureInjection.cs b/backend/core/src/Core.API/DependencyInjection/InfrastructureInjection.cs index 636cb3f6..ce129710 100644 --- a/backend/core/src/Core.API/DependencyInjection/InfrastructureInjection.cs +++ b/backend/core/src/Core.API/DependencyInjection/InfrastructureInjection.cs @@ -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; @@ -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; @@ -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) @@ -83,6 +86,23 @@ private static IServiceCollection AddIPLocator(this IServiceCollection services, return services; } + public static IServiceCollection AddB2CGraphService(this IServiceCollection services, IConfiguration configuration) + { + services.AddOptions() + .Bind(configuration.GetSection("B2CServiceOptions")) + .ValidateDataAnnotationsRecursively() + .ValidateOnStart(); + + services.AddSingleton(sp => sp.GetRequiredService>().Value); + + services.AddHttpClient(); + + services.AddScoped(); + services.AddScoped(); + + return services; + } + private static IServiceCollection AddSanctionlist(this IServiceCollection services, IConfiguration configuration) { services.AddOptions() diff --git a/backend/core/src/Core.API/appsettings.json b/backend/core/src/Core.API/appsettings.json index 35a653f0..1d5318bc 100644 --- a/backend/core/src/Core.API/appsettings.json +++ b/backend/core/src/Core.API/appsettings.json @@ -12,6 +12,11 @@ "Audience": "" } ], + "B2CServiceOptions": { + "ClientId": "", + "ClientSecret": "", + "Tenant": "" + }, "NexusOptions": { "ApiUrl": "", "PaymentMethodOptions": { diff --git a/backend/core/src/Core.Application/Commands/CustomerCommands/DeleteCustomerCommand.cs b/backend/core/src/Core.Application/Commands/CustomerCommands/DeleteCustomerCommand.cs new file mode 100644 index 00000000..c8cb5d83 --- /dev/null +++ b/backend/core/src/Core.Application/Commands/CustomerCommands/DeleteCustomerCommand.cs @@ -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 + { + 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); + } + } +} diff --git a/backend/core/src/Core.Application/Enums.cs b/backend/core/src/Core.Application/Enums.cs index 2dfeb26e..7cae80ef 100644 --- a/backend/core/src/Core.Application/Enums.cs +++ b/backend/core/src/Core.Application/Enums.cs @@ -8,6 +8,7 @@ public enum ApplicationErrorCode { InvalidStatusError, InvalidPropertyError, - ExistingPropertyError + ExistingPropertyError, + NotFoundError } } diff --git a/backend/core/src/Core.Domain/Repositories/ICustomerRepository.cs b/backend/core/src/Core.Domain/Repositories/ICustomerRepository.cs index 8dc01c7f..6322ae77 100644 --- a/backend/core/src/Core.Domain/Repositories/ICustomerRepository.cs +++ b/backend/core/src/Core.Domain/Repositories/ICustomerRepository.cs @@ -15,5 +15,7 @@ public interface ICustomerRepository public Task> 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); } } diff --git a/backend/core/src/Core.Infrastructure/AzureB2CGraphService/B2CGraphService.cs b/backend/core/src/Core.Infrastructure/AzureB2CGraphService/B2CGraphService.cs new file mode 100644 index 00000000..3ce53725 --- /dev/null +++ b/backend/core/src/Core.Infrastructure/AzureB2CGraphService/B2CGraphService.cs @@ -0,0 +1,67 @@ +// 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 Azure.Identity; +using Core.Domain.Exceptions; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Microsoft.Graph; + +namespace Core.Infrastructure.AzureB2CGraphService +{ + public class B2CGraphService : IB2CGraphService + { + private readonly GraphServiceClient _graphServiceClient; + private readonly ILogger _logger; + + public B2CGraphService( + GraphServiceClient graphServiceClient, + IOptions options, + ILogger logger) + { + _graphServiceClient = graphServiceClient; + _logger = logger; + + var clientId = options.Value.ClientId; + var clientSecret = options.Value.ClientSecret; + var tenant = options.Value.Tenant; + + var clientSecretCredential = new ClientSecretCredential(tenant, clientId, clientSecret); + + var scopes = new[] { "https://graph.microsoft.com/.default" }; + + _graphServiceClient = new GraphServiceClient(clientSecretCredential, scopes); + } + + public async Task DeleteUserAsync(string customerCode) + { + _logger.LogInformation("Attempting to delete user {CustomerId} from B2C.", customerCode); + + try + { + var user = await _graphServiceClient.Users[customerCode].GetAsync(); + + if (user != null) + { + await _graphServiceClient.Users[customerCode].DeleteAsync(); + _logger.LogInformation("User {CustomerId} deleted successfully.", customerCode); + } + else + { + _logger.LogInformation("User {CustomerId} not found.", customerCode); + } + } + catch (ServiceException ex) when (ex.IsMatch(GraphErrorCode.Request_ResourceNotFound.ToString())) + { + _logger.LogInformation("User {CustomerId} not found.", customerCode); + throw new CustomErrorsException("B2CGraphService", customerCode, "User not found."); + } + catch (Exception ex) + { + _logger.LogError(ex, "An unexpected error occurred while deleting user {CustomerId}.", customerCode); + throw new CustomErrorsException("B2CGraphService", customerCode, "An unexpected error occurred while deleting user."); + } + } + } +} diff --git a/backend/core/src/Core.Infrastructure/AzureB2CGraphService/B2CServiceOptions.cs b/backend/core/src/Core.Infrastructure/AzureB2CGraphService/B2CServiceOptions.cs new file mode 100644 index 00000000..266a61ea --- /dev/null +++ b/backend/core/src/Core.Infrastructure/AzureB2CGraphService/B2CServiceOptions.cs @@ -0,0 +1,20 @@ +// 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 System.ComponentModel.DataAnnotations; + +namespace Core.Infrastructure.AzureB2CGraphService +{ + public class B2CServiceOptions + { + [Required] + public required string ClientId { get; set; } + + [Required] + public required string ClientSecret { get; set; } + + [Required] + public required string Tenant { get; set; } + } +} diff --git a/backend/core/src/Core.Infrastructure/AzureB2CGraphService/IB2CGraphService.cs b/backend/core/src/Core.Infrastructure/AzureB2CGraphService/IB2CGraphService.cs new file mode 100644 index 00000000..f0ed92f7 --- /dev/null +++ b/backend/core/src/Core.Infrastructure/AzureB2CGraphService/IB2CGraphService.cs @@ -0,0 +1,13 @@ +// 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.Entities.CustomerAggregate; + +namespace Core.Infrastructure.AzureB2CGraphService +{ + public interface IB2CGraphService + { + public Task DeleteUserAsync(string customerCode); + } +} diff --git a/backend/core/src/Core.Infrastructure/Core.Infrastructure.csproj b/backend/core/src/Core.Infrastructure/Core.Infrastructure.csproj index 3ab2b2a3..ce1e7eb2 100644 --- a/backend/core/src/Core.Infrastructure/Core.Infrastructure.csproj +++ b/backend/core/src/Core.Infrastructure/Core.Infrastructure.csproj @@ -7,9 +7,11 @@ + + - + diff --git a/backend/core/src/Core.Infrastructure/Nexus/Enums.cs b/backend/core/src/Core.Infrastructure/Nexus/Enums.cs index 0787ebf8..7184a989 100644 --- a/backend/core/src/Core.Infrastructure/Nexus/Enums.cs +++ b/backend/core/src/Core.Infrastructure/Nexus/Enums.cs @@ -24,7 +24,8 @@ public enum NexusErrorCodes TrustlevelNotFoundError = 4, InvalidStatus = 4, InvalidProperty = 5, - TransactionNotFoundError = 6 + TransactionNotFoundError = 6, + NonZeroAccountBalance = 7, } public enum MailStatus diff --git a/backend/core/src/Core.Infrastructure/Nexus/Repositories/NexusCustomerRepository.cs b/backend/core/src/Core.Infrastructure/Nexus/Repositories/NexusCustomerRepository.cs index 4d39e6f8..4815fe33 100644 --- a/backend/core/src/Core.Infrastructure/Nexus/Repositories/NexusCustomerRepository.cs +++ b/backend/core/src/Core.Infrastructure/Nexus/Repositories/NexusCustomerRepository.cs @@ -5,9 +5,13 @@ using Core.Domain.Entities.CustomerAggregate; using Core.Domain.Exceptions; using Core.Domain.Repositories; +using Core.Infrastructure.AzureB2CGraphService; +using Core.Infrastructure.Nexus.SigningService; using Nexus.Sdk.Shared.Requests; using Nexus.Sdk.Token; +using Nexus.Sdk.Token.Requests; using Nexus.Sdk.Token.Responses; +using Account = Core.Domain.Entities.AccountAggregate.Account; namespace Core.Infrastructure.Nexus.Repositories { @@ -15,11 +19,19 @@ public class NexusCustomerRepository : ICustomerRepository { private readonly ITokenServer _tokenServer; private readonly TokenOptions _tokenSettings; - - public NexusCustomerRepository(ITokenServer tokenServer, TokenOptions tokenSettings) + private readonly ISigningService _signingService; + private readonly IB2CGraphService _b2cGraphService; + + public NexusCustomerRepository( + ITokenServer tokenServer, + TokenOptions tokenSettings, + ISigningService signingService, + IB2CGraphService b2CGraphService) { _tokenServer = tokenServer; _tokenSettings = tokenSettings; + _signingService = signingService; + _b2cGraphService = b2CGraphService; } public async Task CreateAsync(Customer customer, string? ip = null, CancellationToken cancellationToken = default) @@ -144,6 +156,104 @@ public async Task> GetLimitsAsync(string customerCode return limits; } + public async Task DeleteAsync(Customer customer, string? ip = null, CancellationToken cancellationToken = default) + { + var customerCodeExists = await _tokenServer.Customers.Exists(customer.CustomerCode); + + if (!customerCodeExists) + { + throw new CustomErrorsException(NexusErrorCodes.CustomerNotFoundError.ToString(), customer.CustomerCode, Constants.NexusErrorMessages.CustomerNotFound); + } + + // Check if the customer status is ACTIVE + if (customer.Status != CustomerStatus.ACTIVE.ToString()) + { + throw new CustomErrorsException(NexusErrorCodes.InvalidStatus.ToString(), customer.Status.ToString(), "Invalid customer status"); + } + + // Get Customer accounts + var accounts = await _tokenServer.Accounts.Get( + new Dictionary + { + { "CustomerCode", customer.CustomerCode }, + { "Status", "ACTIVE" } + }); + + // Check if the customer has any accounts and balance + if (accounts.Records.Any()) + { + foreach (var acc in accounts.Records) + { + Account account = new() + { + AccountCode = acc.AccountCode, + CustomerCode = acc.CustomerCode, + PublicKey = acc.PublicKey + }; + + // Get the account balance + var response = await _tokenServer.Accounts.GetBalances(account.AccountCode); + + var accountBalances = response.Balances; + + if (response.Balances.Any(balance => balance.Amount > 0)) + { + throw new CustomErrorsException(NexusErrorCodes.NonZeroAccountBalance.ToString(), account.AccountCode, "Customer cannot be deleted due to non-zero balance"); + } + + if (accountBalances.Any()) + { + // Get the token codes to be disabled + var tokensToBeDisabled = accountBalances.Select(b => b.TokenCode).ToArray(); + + await RemoveTrustlines(customer, account, tokensToBeDisabled); + } + } + } + + var deleteRequest = new DeleteCustomerRequest + { + CustomerCode = customer.CustomerCode + }; + + await _tokenServer.Customers.Delete(deleteRequest, ip); + + // delete user from azure b2c + await _b2cGraphService.DeleteUserAsync(customer.CustomerCode); + } + + private async Task RemoveTrustlines(Customer customer, Account account, string[]? tokensToBeDisabled = null) + { + var updateAccount = new UpdateTokenAccountRequest + { + Settings = new UpdateTokenAccountSettings + { + AllowedTokens = new AllowedTokens + { + DisableTokens = tokensToBeDisabled + } + } + }; + + var signableResponse = await _tokenServer.Accounts.Update(customer.CustomerCode, account.AccountCode, updateAccount); + + switch (_tokenSettings.Blockchain) + { + case Blockchain.STELLAR: + { + var submitRequest = await _signingService.SignStellarTransactionEnvelopeAsync(account.PublicKey, signableResponse); + await _tokenServer.Submit.OnStellarAsync(submitRequest); + break; + } + case Blockchain.ALGORAND: + { + var submitRequest = await _signingService.SignAlgorandTransactionAsync(account.PublicKey, signableResponse); + await _tokenServer.Submit.OnAlgorandAsync(submitRequest); + break; + } + } + } + public Task SaveChangesAsync(CancellationToken cancellationToken = default) { return Task.FromResult(true); diff --git a/backend/core/src/Core.Presentation/Controllers/CustomersController.cs b/backend/core/src/Core.Presentation/Controllers/CustomersController.cs index 1d551bc0..ce6c93c5 100644 --- a/backend/core/src/Core.Presentation/Controllers/CustomersController.cs +++ b/backend/core/src/Core.Presentation/Controllers/CustomersController.cs @@ -3,7 +3,6 @@ // of this distribution or at http://www.apache.org/licenses/LICENSE-2.0 using Asp.Versioning; -using Core.Application.Commands; using Core.Application.Commands.CustomerCommands; using Core.Application.Queries.CustomerQueries; using Core.Presentation.Models; @@ -88,6 +87,19 @@ public async Task GetCustomerTokenLimitsAsync() return Ok(response); } + [HttpDelete(Name = "DeleteCustomer")] + [ProducesResponseType(typeof(CustomResponse), 201)] + [ProducesResponseType(typeof(CustomErrorsResponse), 400)] + [ProducesResponseType(typeof(CustomErrorsResponse), 404)] + [ProducesResponseType(typeof(CustomErrorsResponse), 500)] + [RequiredScope("Customer.Delete")] + public async Task DeleteCustomerAsync() + { + var command = new DeleteCustomerCommand(GetUserId(), GetIP()); + await _sender.Send(command); + return CreatedAtRoute("DeleteCustomer", null, new EmptyCustomResponse()); + } + [HttpPost("devices", Name = "DeviceAuthentication")] [ProducesResponseType(typeof(CustomResponse), 201)] [ProducesResponseType(typeof(CustomErrorsResponse), 400)] diff --git a/backend/core/tests/Core.InfrastructureTests/Helpers/NexusSDKHelper.cs b/backend/core/tests/Core.InfrastructureTests/Helpers/NexusSDKHelper.cs index 91afb881..44f48dad 100644 --- a/backend/core/tests/Core.InfrastructureTests/Helpers/NexusSDKHelper.cs +++ b/backend/core/tests/Core.InfrastructureTests/Helpers/NexusSDKHelper.cs @@ -49,6 +49,7 @@ public static AccountResponse AccountResponse(string customerCode) public static SignableResponse SignableResponse() { var response = new BlockchainResponse( + "Code", "TestHash", "EncodedEnvelope", new RequiredSignaturesResponse[] diff --git a/backend/core/tests/Core.InfrastructureTests/Nexus/Repositories/NexusCustomerRepositoryTests.cs b/backend/core/tests/Core.InfrastructureTests/Nexus/Repositories/NexusCustomerRepositoryTests.cs index 18d20624..c44fdeb6 100644 --- a/backend/core/tests/Core.InfrastructureTests/Nexus/Repositories/NexusCustomerRepositoryTests.cs +++ b/backend/core/tests/Core.InfrastructureTests/Nexus/Repositories/NexusCustomerRepositoryTests.cs @@ -4,13 +4,18 @@ using Core.Domain.Entities.CustomerAggregate; using Core.Domain.Exceptions; +using Core.Infrastructure.AzureB2CGraphService; +using Core.Infrastructure.Nexus; using Core.Infrastructure.Nexus.Repositories; +using Core.Infrastructure.Nexus.SigningService; using Core.InfrastructureTests.Helpers; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Nexus.Sdk.Shared.Requests; using Nexus.Sdk.Shared.Responses; using Nexus.Sdk.Token; +using Nexus.Sdk.Token.Requests; +using Nexus.Sdk.Token.Responses; namespace Core.InfrastructureTests.Nexus.Repositories { @@ -26,7 +31,10 @@ public async Task CreateCustomer_Creates_Customer_TestAsync() server.Setup(s => s.Customers.Create(It.IsAny(), It.IsAny())) .Returns(Task.FromResult(NexusSDKHelper.PrivateCustomer("TestCustomer"))); - var repo = new NexusCustomerRepository(server.Object, DefaultOptions.TokenOptions); + var signingService = new Mock().Object; + var b2cGraphService = new Mock().Object; + + var repo = new NexusCustomerRepository(server.Object, DefaultOptions.TokenOptions, signingService, b2cGraphService); var customer = new Customer() { @@ -74,6 +82,9 @@ public async Task CreateCustomer_Creates_Business_TestAsync() server.Setup(s => s.Customers.Create(It.IsAny(), It.IsAny())) .Returns(Task.FromResult(NexusSDKHelper.PrivateCustomer("TestCustomer"))); + var signingService = new Mock().Object; + var b2cGraphService = new Mock().Object; + var customer = new Customer() { CustomerCode = "TestCustomer", @@ -90,7 +101,7 @@ public async Task CreateCustomer_Creates_Business_TestAsync() } }; - var repo = new NexusCustomerRepository(server.Object, DefaultOptions.TokenOptions); + var repo = new NexusCustomerRepository(server.Object, DefaultOptions.TokenOptions, signingService, b2cGraphService); await repo.CreateAsync(customer); var expected = new CreateCustomerRequest @@ -119,7 +130,10 @@ public async Task CreateCustomer_Creating_Throws_ExistsPropertyError_TestAsync() server.Setup(s => s.Customers.Exists("TestCustomer")) .Returns(Task.FromResult(true)); - var repo = new NexusCustomerRepository(server.Object, DefaultOptions.TokenOptions); + var signingService = new Mock().Object; + var b2cGraphService = new Mock().Object; + + var repo = new NexusCustomerRepository(server.Object, DefaultOptions.TokenOptions, signingService, b2cGraphService); var customer = new Customer() { @@ -157,7 +171,10 @@ public async Task CreateCustomer_Creating_Throws_EmailExistsError_TestAsync() filteringParameters: new Dictionary(), records: [NexusSDKHelper.PrivateCustomer("TestCustomer123")]))); - var repo = new NexusCustomerRepository(server.Object, DefaultOptions.TokenOptions); + var signingService = new Mock().Object; + var b2cGraphService = new Mock().Object; + + var repo = new NexusCustomerRepository(server.Object, DefaultOptions.TokenOptions, signingService, b2cGraphService); var customer = new Customer() { @@ -196,7 +213,10 @@ public async Task CreateCustomer_Creating_EmailExists_StatusDeleted_Success_Test filteringParameters: new Dictionary(), records: [NexusSDKHelper.DeletedPrivateCustomer("TestCustomer")]))); - var repo = new NexusCustomerRepository(server.Object, DefaultOptions.TokenOptions); + var signingService = new Mock().Object; + var b2cGraphService = new Mock().Object; + + var repo = new NexusCustomerRepository(server.Object, DefaultOptions.TokenOptions, signingService, b2cGraphService); var customer = new Customer() { @@ -227,7 +247,10 @@ public async static Task Get_Returns_Valid_Customer_TestAsync() server.Setup(s => s.Customers.Get("TestCustomer")) .Returns(Task.FromResult(NexusSDKHelper.PrivateCustomer("TestCustomer"))); - var repo = new NexusCustomerRepository(server.Object, DefaultOptions.TokenOptions); + var signingService = new Mock().Object; + var b2cGraphService = new Mock().Object; + + var repo = new NexusCustomerRepository(server.Object, DefaultOptions.TokenOptions, signingService, b2cGraphService); var customer = await repo.GetAsync("TestCustomer"); diff --git a/mobile/src/context/AppStateContext.tsx b/mobile/src/context/AppStateContext.tsx index c14ee74a..f6153d95 100644 --- a/mobile/src/context/AppStateContext.tsx +++ b/mobile/src/context/AppStateContext.tsx @@ -1,3 +1,7 @@ +// 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 + import { createContext, useContext, diff --git a/mobile/src/navigation/__tests__/WelcomeStack.test.tsx b/mobile/src/navigation/__tests__/WelcomeStack.test.tsx index 665bc8e1..82ed6116 100644 --- a/mobile/src/navigation/__tests__/WelcomeStack.test.tsx +++ b/mobile/src/navigation/__tests__/WelcomeStack.test.tsx @@ -1,3 +1,7 @@ +// 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 + import { waitFor } from "@testing-library/react-native"; import WelcomeStackNavigator from "../WelcomeStack"; import * as functions from "../../utils/functions"; diff --git a/mobile/src/utils/functions.ts b/mobile/src/utils/functions.ts index 9e0befc9..d474a462 100644 --- a/mobile/src/utils/functions.ts +++ b/mobile/src/utils/functions.ts @@ -1,3 +1,7 @@ +// 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 + import * as SecureStore from "expo-secure-store"; import * as ed from "@noble/ed25519"; import { sha512 } from "@noble/hashes/sha512";