diff --git a/TransactionProcessor.Mobile.BusinessLogic.Tests/ServicesTests/ConfigurationServiceTests.cs b/TransactionProcessor.Mobile.BusinessLogic.Tests/ServicesTests/ConfigurationServiceTests.cs index 45541579..7d645558 100644 --- a/TransactionProcessor.Mobile.BusinessLogic.Tests/ServicesTests/ConfigurationServiceTests.cs +++ b/TransactionProcessor.Mobile.BusinessLogic.Tests/ServicesTests/ConfigurationServiceTests.cs @@ -52,6 +52,7 @@ public async Task ConfigurationService_GetConfiguration_ResultSuccess_And_Config DeviceIdentifier = TestData.DeviceIdentifier, LogLevel = configLoggingLevel, Id = Guid.NewGuid().ToString(), + SentryDsn = "https://key@sentry.io/123", }; Logger.Initialise(new NullLogger()); @@ -68,6 +69,7 @@ public async Task ConfigurationService_GetConfiguration_ResultSuccess_And_Config configurationResult.Data.SecurityServiceUri.ShouldBe(expectedConfiguration.HostAddresses.Single(s => s.ServiceType == ServiceType.Security).Uri); configurationResult.Data.TransactionProcessorAclUri.ShouldBe(expectedConfiguration.HostAddresses.Single(s => s.ServiceType == ServiceType.TransactionProcessorAcl).Uri); configurationResult.Data.LogLevel.ShouldBe(expectedLogLevel); + configurationResult.Data.SentryDsn.ShouldBe(expectedConfiguration.SentryDsn); } [Fact] diff --git a/TransactionProcessor.Mobile.BusinessLogic.Tests/ViewModelTests/LoginPageViewModelTests.cs b/TransactionProcessor.Mobile.BusinessLogic.Tests/ViewModelTests/LoginPageViewModelTests.cs index 1106f036..49da0875 100644 --- a/TransactionProcessor.Mobile.BusinessLogic.Tests/ViewModelTests/LoginPageViewModelTests.cs +++ b/TransactionProcessor.Mobile.BusinessLogic.Tests/ViewModelTests/LoginPageViewModelTests.cs @@ -35,6 +35,9 @@ public class LoginPageViewModelTests private readonly Mock UpdateService; private readonly Mock BalanceRefresher; + + private readonly Mock SentryService; + public LoginPageViewModelTests() { this.Mediator = new Mock(); this.NavigationService = new Mock(); @@ -46,13 +49,14 @@ public LoginPageViewModelTests() { this.DialogService = new Mock(); this.UpdateService = new Mock(); this.BalanceRefresher = new Mock(); + this.SentryService = new Mock(); this.ViewModel = new LoginPageViewModel(this.Mediator.Object, this.NavigationService.Object, this.ApplicationCache.Object, this.DeviceService.Object, this.ApplicationInfoService.Object, this.DialogService.Object, this.NavigationParameterService.Object, this.UpdateService.Object, this.ApplicationUpdateLauncherService.Object, - this.BalanceRefresher.Object); + this.BalanceRefresher.Object, this.SentryService.Object); Logger.Initialise(new Logging.NullLogger()); } @@ -281,6 +285,21 @@ public void LoginPageViewModel_BackButtonCommand_Execute_IsExecuted() this.ViewModel.BackButtonCommand.Execute(null); this.NavigationService.Verify(n => n.QuitApplication(), Times.Once); } + + [Fact] + public async Task LoginPageViewModel_LoginCommand_Execute_SentryInitialisedWithDsnFromConfiguration() + { + String sentryDsn = "https://key@sentry.io/123"; + this.Mediator.Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(Result.Success(new Configuration { SentryDsn = sentryDsn })); + this.Mediator.Setup(m => m.Send(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success(TestData.AccessToken)); + this.Mediator.Setup(m => m.Send(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success(TestData.PerformLogonResponseModel)); + this.Mediator.Setup(m => m.Send(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success(TestData.ContractProductList)); + + await this.ViewModel.LogonCommand.ExecuteAsync(null); + + this.SentryService.Verify(s => s.InitializeSentry(sentryDsn), Times.Once); + } [Fact] public void LoginPageViewModel_PropertyTests_ValuesAreAsExpected(){ diff --git a/TransactionProcessor.Mobile.BusinessLogic/Models/TokenResponseModel.cs b/TransactionProcessor.Mobile.BusinessLogic/Models/TokenResponseModel.cs index 0f1b54f5..40a372b6 100644 --- a/TransactionProcessor.Mobile.BusinessLogic/Models/TokenResponseModel.cs +++ b/TransactionProcessor.Mobile.BusinessLogic/Models/TokenResponseModel.cs @@ -39,5 +39,6 @@ public class Configuration public Boolean ShowDebugMessages { get; set; } public Int32? LogMessageBatchSize { get; set; } + public String SentryDsn { get; set; } } } diff --git a/TransactionProcessor.Mobile.BusinessLogic/Services/ConfigurationService.cs b/TransactionProcessor.Mobile.BusinessLogic/Services/ConfigurationService.cs index d5acccfb..f3f83718 100644 --- a/TransactionProcessor.Mobile.BusinessLogic/Services/ConfigurationService.cs +++ b/TransactionProcessor.Mobile.BusinessLogic/Services/ConfigurationService.cs @@ -82,6 +82,7 @@ public async Task> GetConfiguration(String deviceIdentifie TransactionProcessorAclUri = apiResponse.HostAddresses.Single(h => h.ServiceType == ServiceType.TransactionProcessorAcl).Uri, LogMessageBatchSize = apiResponse.LogMessageBatchSize.GetValueOrDefault(), + SentryDsn = apiResponse.SentryDsn ?? String.Empty, }; Logger.LogDebug($"About to xlate log level"); diff --git a/TransactionProcessor.Mobile.BusinessLogic/Services/DataTransferObjects/ConfigurationResponse.cs b/TransactionProcessor.Mobile.BusinessLogic/Services/DataTransferObjects/ConfigurationResponse.cs index c71412e9..75ee571c 100644 --- a/TransactionProcessor.Mobile.BusinessLogic/Services/DataTransferObjects/ConfigurationResponse.cs +++ b/TransactionProcessor.Mobile.BusinessLogic/Services/DataTransferObjects/ConfigurationResponse.cs @@ -21,4 +21,6 @@ public class ConfigurationResponse public LoggingLevel LogLevel { get; set; } public Int32? LogMessageBatchSize { get; set; } + + public String SentryDsn { get; set; } } diff --git a/TransactionProcessor.Mobile.BusinessLogic/Services/TrainingModeServices/TrainingConfigurationService.cs b/TransactionProcessor.Mobile.BusinessLogic/Services/TrainingModeServices/TrainingConfigurationService.cs index 944fa1a3..2bec7e77 100644 --- a/TransactionProcessor.Mobile.BusinessLogic/Services/TrainingModeServices/TrainingConfigurationService.cs +++ b/TransactionProcessor.Mobile.BusinessLogic/Services/TrainingModeServices/TrainingConfigurationService.cs @@ -19,8 +19,9 @@ public async Task> GetConfiguration(String deviceIdentifie EstateReportingUri = "http://localhost:5006", LogLevel = LogLevel.Debug, SecurityServiceUri = "http://localhost:5001", - TransactionProcessorAclUri = "http://localhost:5003" - }); + TransactionProcessorAclUri = "http://localhost:5003", + SentryDsn = "https://22dde94969082eb69783ee0beb25f401@o4504618032693248.ingest.us.sentry.io/4511070608293888", + }); } public async Task PostDiagnosticLogs(String deviceIdentifier, diff --git a/TransactionProcessor.Mobile.BusinessLogic/UIServices/ISentryService.cs b/TransactionProcessor.Mobile.BusinessLogic/UIServices/ISentryService.cs new file mode 100644 index 00000000..3e7b48be --- /dev/null +++ b/TransactionProcessor.Mobile.BusinessLogic/UIServices/ISentryService.cs @@ -0,0 +1,7 @@ +namespace TransactionProcessor.Mobile.BusinessLogic.UIServices +{ + public interface ISentryService + { + void InitializeSentry(String dsn); + } +} diff --git a/TransactionProcessor.Mobile.BusinessLogic/ViewModels/LoginPageViewModel.cs b/TransactionProcessor.Mobile.BusinessLogic/ViewModels/LoginPageViewModel.cs index 52022e0a..0a422647 100644 --- a/TransactionProcessor.Mobile.BusinessLogic/ViewModels/LoginPageViewModel.cs +++ b/TransactionProcessor.Mobile.BusinessLogic/ViewModels/LoginPageViewModel.cs @@ -22,6 +22,7 @@ public partial class LoginPageViewModel : ExtendedBaseViewModel private readonly IApplicationUpdateLauncherService ApplicationUpdateLauncherService; private readonly IBalanceRefresher BalanceRefresher; private readonly IUpdateService UpdateService; + private readonly ISentryService SentryService; private String userName; @@ -39,13 +40,15 @@ public LoginPageViewModel(IMediator mediator, INavigationService navigationServi INavigationParameterService navigationParameterService, IUpdateService updateService, IApplicationUpdateLauncherService applicationUpdateLauncherService, - IBalanceRefresher balanceRefresher) : base(applicationCache,dialogService,navigationService, deviceService, navigationParameterService) + IBalanceRefresher balanceRefresher, + ISentryService sentryService) : base(applicationCache,dialogService,navigationService, deviceService, navigationParameterService) { this.ApplicationInfoService = applicationInfoService; this.ApplicationUpdateLauncherService = applicationUpdateLauncherService; this.BalanceRefresher = balanceRefresher; this.Mediator = mediator; this.UpdateService = updateService; + this.SentryService = sentryService; } #endregion @@ -96,6 +99,8 @@ private async Task> GetConfiguration() { if (configurationResult.IsSuccess) { // Cache the config object this.ApplicationCache.SetConfiguration(configurationResult.Data); + // Initialise Sentry with the DSN from the configuration service + this.SentryService.InitializeSentry(configurationResult.Data.SentryDsn); } return configurationResult; diff --git a/TransactionProcessor.Mobile/Extensions/MauiAppBuilderExtensions.cs b/TransactionProcessor.Mobile/Extensions/MauiAppBuilderExtensions.cs index 7237dbfb..8fd93c1d 100644 --- a/TransactionProcessor.Mobile/Extensions/MauiAppBuilderExtensions.cs +++ b/TransactionProcessor.Mobile/Extensions/MauiAppBuilderExtensions.cs @@ -178,6 +178,7 @@ public static MauiAppBuilder ConfigureUIServices(this MauiAppBuilder builder) { builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); + builder.Services.AddSingleton(); return builder; } diff --git a/TransactionProcessor.Mobile/TransactionProcessor.Mobile.csproj b/TransactionProcessor.Mobile/TransactionProcessor.Mobile.csproj index 14083e6d..8d2d85a3 100644 --- a/TransactionProcessor.Mobile/TransactionProcessor.Mobile.csproj +++ b/TransactionProcessor.Mobile/TransactionProcessor.Mobile.csproj @@ -160,6 +160,7 @@ + diff --git a/TransactionProcessor.Mobile/UIServices/SentryService.cs b/TransactionProcessor.Mobile/UIServices/SentryService.cs new file mode 100644 index 00000000..a279e71f --- /dev/null +++ b/TransactionProcessor.Mobile/UIServices/SentryService.cs @@ -0,0 +1,34 @@ +using Sentry; +using TransactionProcessor.Mobile.BusinessLogic.UIServices; + +namespace TransactionProcessor.Mobile.UIServices; + +public class SentryService : ISentryService +{ + private readonly IApplicationInfoService ApplicationInfoService; + private Boolean isInitialized; + + public SentryService(IApplicationInfoService applicationInfoService) { + this.ApplicationInfoService = applicationInfoService; + } + + public void InitializeSentry(String dsn) + { + if (this.isInitialized || String.IsNullOrWhiteSpace(dsn)) + { + return; + } + + SentrySdk.Init(o => + { + o.Dsn = dsn; +#if ANDROID || IOS || MACCATALYST + o.Native.AttachScreenshot = true; +#endif + o.SendDefaultPii = true; + o.Release = this.ApplicationInfoService.VersionString; + }); + + this.isInitialized = true; + } +}