diff --git a/.editorconfig b/.editorconfig index 73472fa15f..6ee110ee1e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -20,6 +20,15 @@ indent_size = 2 # All Rules are enabled with default severity. # Rules with IsEnabledByDefault = false are force-enabled with default severity. +# CA1515 Suppress "Because an application's API isn't typically referenced from outside the assembly, types can be made internal" +dotnet_diagnostic.ca1515.severity = none + +# IDE0290 Suppress "Use primary constructor" +dotnet_diagnostic.IDE0290.severity = none + +# IDE0160 Suppress "Convert to block scoped namespace" +dotnet_diagnostic.IDE0160.severity = none + # S101: Types should be named in PascalCase dotnet_diagnostic.s101.severity = none diff --git a/Apps/AccountDataAccess/src/Audit/AuditRepository.cs b/Apps/AccountDataAccess/src/Audit/AuditRepository.cs index ceabdd7f2b..3c1ef3b508 100644 --- a/Apps/AccountDataAccess/src/Audit/AuditRepository.cs +++ b/Apps/AccountDataAccess/src/Audit/AuditRepository.cs @@ -25,19 +25,9 @@ namespace HealthGateway.AccountDataAccess.Audit /// /// Handle audit data. /// - public class AuditRepository : IAuditRepository + /// The injected agent audit delegate. + public class AuditRepository(IAgentAuditDelegate agentAuditDelegate) : IAuditRepository { - private readonly IAgentAuditDelegate agentAuditDelegate; - - /// - /// Initializes a new instance of the class. - /// - /// The injected agent audit delegate. - public AuditRepository(IAgentAuditDelegate agentAuditDelegate) - { - this.agentAuditDelegate = agentAuditDelegate; - } - private static ActivitySource ActivitySource { get; } = new(typeof(AuditRepository).FullName); /// @@ -50,7 +40,7 @@ public async Task> HandleAsync(AgentAuditQuery query, Ca activity?.AddBaggage("AuditGroup", query.Group.ToString()); } - return await this.agentAuditDelegate.GetAgentAuditsAsync(query.Hdid, query.Group, ct); + return await agentAuditDelegate.GetAgentAuditsAsync(query.Hdid, query.Group, ct); } } } diff --git a/Apps/AccountDataAccess/src/Patient/Address.cs b/Apps/AccountDataAccess/src/Patient/Address.cs index 5081950b25..91c317e164 100644 --- a/Apps/AccountDataAccess/src/Patient/Address.cs +++ b/Apps/AccountDataAccess/src/Patient/Address.cs @@ -16,7 +16,6 @@ namespace HealthGateway.AccountDataAccess.Patient { using System.Collections.Generic; - using System.Linq; /// /// Represents an address. @@ -26,7 +25,7 @@ public class Address /// /// Gets or sets the street lines. /// - public IEnumerable StreetLines { get; set; } = Enumerable.Empty(); + public IEnumerable StreetLines { get; set; } = []; /// /// Gets or sets the city name. diff --git a/Apps/AccountDataAccess/src/Patient/ClientRegistriesDelegate.cs b/Apps/AccountDataAccess/src/Patient/ClientRegistriesDelegate.cs index ad75bf4d28..82a2b43383 100644 --- a/Apps/AccountDataAccess/src/Patient/ClientRegistriesDelegate.cs +++ b/Apps/AccountDataAccess/src/Patient/ClientRegistriesDelegate.cs @@ -32,7 +32,11 @@ namespace HealthGateway.AccountDataAccess.Patient /// /// The Client Registries delegate. /// - internal class ClientRegistriesDelegate : IClientRegistriesDelegate + /// The injected logger provider. + /// The injected client registries soap client. + internal class ClientRegistriesDelegate( + ILogger logger, + QUPA_AR101102_PortType clientRegistriesClient) : IClientRegistriesDelegate { private const string Instance = "INSTANCE"; private static readonly List WarningResponseCodes = @@ -41,23 +45,6 @@ internal class ClientRegistriesDelegate : IClientRegistriesDelegate "BCHCIM.GD.1.0022", "BCHCIM.GD.0.0023", "BCHCIM.GD.1.0023", "BCHCIM.GD.0.0578", "BCHCIM.GD.1.0578", ]; - private readonly QUPA_AR101102_PortType clientRegistriesClient; - private readonly ILogger logger; - - /// - /// Initializes a new instance of the class. - /// Constructor that requires an IEndpointBehavior for dependency injection. - /// - /// The injected logger provider. - /// The injected client registries soap client. - public ClientRegistriesDelegate( - ILogger logger, - QUPA_AR101102_PortType clientRegistriesClient) - { - this.logger = logger; - this.clientRegistriesClient = clientRegistriesClient; - } - private static ActivitySource ActivitySource { get; } = new(typeof(ClientRegistriesDelegate).FullName); /// @@ -66,13 +53,13 @@ public async Task GetDemographicsAsync(OidType type, string identi using Activity? activity = ActivitySource.StartActivity(); activity?.AddBaggage("PatientIdentifier", identifier); - this.logger.LogDebug("Retrieving patient from the Client Registry"); + logger.LogDebug("Retrieving patient from the Client Registry"); // Create request object HCIM_IN_GetDemographicsRequest request = CreateRequest(type, identifier); // Perform the request - HCIM_IN_GetDemographicsResponse1 reply = await this.clientRegistriesClient.HCIM_IN_GetDemographicsAsync(request); + HCIM_IN_GetDemographicsResponse1 reply = await clientRegistriesClient.HCIM_IN_GetDemographicsAsync(request); return this.ParseResponse(reply); } @@ -162,7 +149,32 @@ private static HCIM_IN_GetDemographicsRequest CreateRequest(OidType oidType, str return new HCIM_IN_GetDemographicsRequest(request); } - private static Address? MapAddress(AD? address) + private static Name ExtractName(PN nameSection) + { + // Extract the subject names + List givenNameList = []; + List lastNameList = []; + foreach (ENXP name in nameSection.Items) + { + if (name.GetType() == typeof(engiven) && (name.qualifier == null || !name.qualifier.Contains(cs_EntityNamePartQualifier.CL))) + { + givenNameList.Add(name.Text[0]); + } + else if (name.GetType() == typeof(enfamily) && (name.qualifier == null || !name.qualifier.Contains(cs_EntityNamePartQualifier.CL))) + { + lastNameList.Add(name.Text[0]); + } + } + + const string delimiter = " "; + return new Name + { + GivenName = givenNameList.Aggregate((i, j) => i + delimiter + j), + Surname = lastNameList.Aggregate((i, j) => i + delimiter + j), + }; + } + + private Address? MapAddress(AD? address) { if (address?.Items == null) { @@ -189,37 +201,15 @@ private static HCIM_IN_GetDemographicsRequest CreateRequest(OidType oidType, str case ADCountry country: retAddress.Country = country.Text[0] ?? string.Empty; break; + default: + logger.LogDebug("Unmatched item of type {Type}", item.GetType()); + break; } } return retAddress; } - private static Name ExtractName(PN nameSection) - { - // Extract the subject names - List givenNameList = []; - List lastNameList = []; - foreach (ENXP name in nameSection.Items) - { - if (name.GetType() == typeof(engiven) && (name.qualifier == null || !name.qualifier.Contains(cs_EntityNamePartQualifier.CL))) - { - givenNameList.Add(name.Text[0]); - } - else if (name.GetType() == typeof(enfamily) && (name.qualifier == null || !name.qualifier.Contains(cs_EntityNamePartQualifier.CL))) - { - lastNameList.Add(name.Text[0]); - } - } - - const string delimiter = " "; - return new Name - { - GivenName = givenNameList.Aggregate((i, j) => i + delimiter + j), - Surname = lastNameList.Aggregate((i, j) => i + delimiter + j), - }; - } - private void CheckResponseCode(string responseCode) { if (responseCode.Contains("BCHCIM.GD.2.0018", StringComparison.InvariantCulture)) @@ -229,7 +219,7 @@ private void CheckResponseCode(string responseCode) if (responseCode.Contains("BCHCIM.GD.2.0006", StringComparison.InvariantCulture)) { - this.logger.LogWarning(ErrorMessages.PhnInvalid); + logger.LogWarning(ErrorMessages.PhnInvalid); throw new ValidationException(ErrorMessages.PhnInvalid, [new("PHN", ErrorMessages.PhnInvalid)]); // should throw InvalidDataException instead } @@ -241,7 +231,7 @@ private void CheckResponseCode(string responseCode) // Verify that the reply contains a result if (!responseCode.Contains("BCHCIM.GD.0.0013", StringComparison.InvariantCulture)) { - this.logger.LogWarning(ErrorMessages.ClientRegistryDoesNotReturnPerson); + logger.LogWarning(ErrorMessages.ClientRegistryDoesNotReturnPerson); throw new NotFoundException(ErrorMessages.ClientRegistryDoesNotReturnPerson); } } @@ -261,7 +251,7 @@ private PatientModel ParseResponse(HCIM_IN_GetDemographicsResponse1 reply) string? dobStr = retrievedPerson.identifiedPerson.birthTime.value; if (!DateTime.TryParseExact(dobStr, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime dob)) { - this.logger.LogWarning("Unable to parse patient's date of birth"); + logger.LogWarning("Unable to parse patient's date of birth"); throw new NotFoundException(ErrorMessages.InvalidServicesCard); } @@ -288,8 +278,8 @@ private PatientModel ParseResponse(HCIM_IN_GetDemographicsResponse1 reply) AD[] addresses = retrievedPerson.addr; if (addresses != null) { - patientModel.PhysicalAddress = MapAddress(addresses.FirstOrDefault(a => a.use.Any(u => u == cs_PostalAddressUse.PHYS))); - patientModel.PostalAddress = MapAddress(addresses.FirstOrDefault(a => a.use.Any(u => u == cs_PostalAddressUse.PST))); + patientModel.PhysicalAddress = this.MapAddress(addresses.FirstOrDefault(a => a.use.Any(u => u == cs_PostalAddressUse.PHYS))); + patientModel.PostalAddress = this.MapAddress(addresses.FirstOrDefault(a => a.use.Any(u => u == cs_PostalAddressUse.PST))); } if (WarningResponseCodes.Any(code => responseCode.Contains(code, StringComparison.InvariantCulture))) @@ -309,11 +299,11 @@ private void PopulateNames(HCIM_IN_GetDemographicsResponseIdentifiedPerson retri if (documentedName == null && legalName == null) { - this.logger.LogWarning("The Client Registry returned a person without a Documented Name or a Legal Name"); + logger.LogWarning("The Client Registry returned a person without a Documented Name or a Legal Name"); } else if (documentedName == null) { - this.logger.LogWarning("The Client Registry returned a person without a Documented Name"); + logger.LogWarning("The Client Registry returned a person without a Documented Name"); } if (documentedName != null) @@ -333,7 +323,7 @@ private void PopulateIdentifiers(HCIM_IN_GetDemographicsResponseIdentifiedPerson II? identifiedPersonId = retrievedPerson.identifiedPerson?.id?.FirstOrDefault(x => x.root == OidType.Phn.ToString()); if (identifiedPersonId == null) { - this.logger.LogWarning("The Client Registry returned a person without a PHN"); + logger.LogWarning("The Client Registry returned a person without a PHN"); } else { @@ -343,7 +333,7 @@ private void PopulateIdentifiers(HCIM_IN_GetDemographicsResponseIdentifiedPerson II? subjectId = retrievedPerson.id?.FirstOrDefault(x => x.displayable && x.root == OidType.Hdid.ToString()); if (subjectId == null) { - this.logger.LogWarning("The Client Registry returned a person without an HDID"); + logger.LogWarning("The Client Registry returned a person without an HDID"); } else { diff --git a/Apps/AccountDataAccess/src/Patient/PatientMappings.cs b/Apps/AccountDataAccess/src/Patient/PatientMappings.cs index d893e8423d..aa8a13fcf1 100644 --- a/Apps/AccountDataAccess/src/Patient/PatientMappings.cs +++ b/Apps/AccountDataAccess/src/Patient/PatientMappings.cs @@ -38,7 +38,7 @@ public PatientMappings() private static Name ExtractPreferredName(PatientIdentity patientIdentity) { string preferredName = StringManipulator.JoinWithoutBlanks( - new[] { patientIdentity.PreferredFirstName, patientIdentity.PreferredSecondName, patientIdentity.PreferredThirdName }); + [patientIdentity.PreferredFirstName, patientIdentity.PreferredSecondName, patientIdentity.PreferredThirdName]); return new() { @@ -50,7 +50,7 @@ private static Name ExtractPreferredName(PatientIdentity patientIdentity) private static Name ExtractLegalName(PatientIdentity patientIdentity) { string legalName = StringManipulator.JoinWithoutBlanks( - new[] { patientIdentity.LegalFirstName, patientIdentity.LegalSecondName, patientIdentity.LegalThirdName }); + [patientIdentity.LegalFirstName, patientIdentity.LegalSecondName, patientIdentity.LegalThirdName]); return new() { @@ -64,7 +64,7 @@ private static Name ExtractLegalName(PatientIdentity patientIdentity) if (ShouldReturnHomeAddress(patientIdentity)) { IEnumerable streetLines = StringManipulator.ExcludeBlanks( - new[] { patientIdentity.HomeAddressStreetOne, patientIdentity.HomeAddressStreetTwo, patientIdentity.HomeAddressStreetThree }); + [patientIdentity.HomeAddressStreetOne, patientIdentity.HomeAddressStreetTwo, patientIdentity.HomeAddressStreetThree]); return new() { @@ -84,7 +84,7 @@ private static Name ExtractLegalName(PatientIdentity patientIdentity) if (ShouldReturnPostalAddress(patientIdentity)) { IEnumerable streetLines = StringManipulator.ExcludeBlanks( - new[] { patientIdentity.MailAddressStreetOne, patientIdentity.MailAddressStreetTwo, patientIdentity.MailAddressStreetThree }); + [patientIdentity.MailAddressStreetOne, patientIdentity.MailAddressStreetTwo, patientIdentity.MailAddressStreetThree]); return new() { diff --git a/Apps/AccountDataAccess/src/Patient/PatientRepository.cs b/Apps/AccountDataAccess/src/Patient/PatientRepository.cs index 5e8942f8c2..8e1b941222 100644 --- a/Apps/AccountDataAccess/src/Patient/PatientRepository.cs +++ b/Apps/AccountDataAccess/src/Patient/PatientRepository.cs @@ -189,6 +189,11 @@ private static string GetStrategy(string? hdid, PatientDetailSource source) private async Task HandlePatientDetailsQueryAsync(PatientDetailsQuery query, CancellationToken ct) { + if (ct.IsCancellationRequested) + { + throw new InvalidOperationException("Cancellation was requested"); + } + using Activity? activity = ActivitySource.StartActivity(); if (!string.IsNullOrEmpty(query.Hdid)) @@ -206,17 +211,9 @@ private async Task HandlePatientDetailsQueryAsync(PatientDet this.logger.LogDebug("Retrieving patient details"); - if (ct.IsCancellationRequested) - { - throw new InvalidOperationException("Cancellation was requested"); - } - - if (query.Hdid == null && query.Phn == null) - { - throw new InvalidOperationException("Must specify either Hdid or Phn to query patient details"); - } - - return await this.GetPatientAsync(query, ct: ct); + return (query.Hdid ?? query.Phn) == null + ? throw new InvalidOperationException("Must specify either Hdid or Phn to query patient details") + : await this.GetPatientAsync(query, ct: ct); } private async Task GetPatientAsync(PatientDetailsQuery query, bool disabledValidation = false, CancellationToken ct = default) diff --git a/Apps/AccountDataAccess/src/Patient/Strategy/HdidAllStrategy.cs b/Apps/AccountDataAccess/src/Patient/Strategy/HdidAllStrategy.cs index 7a2e8ab1a0..ed7f313692 100644 --- a/Apps/AccountDataAccess/src/Patient/Strategy/HdidAllStrategy.cs +++ b/Apps/AccountDataAccess/src/Patient/Strategy/HdidAllStrategy.cs @@ -31,35 +31,20 @@ namespace HealthGateway.AccountDataAccess.Patient.Strategy /// /// Strategy implementation for all hdid patient data sources with or without cache. /// - internal class HdidAllStrategy : PatientQueryStrategy + /// The injected configuration. + /// The injected cache provider. + /// The injected logger. + /// The injected client registries delegate. + /// The injected patient identity api. + /// The injected mapper. + internal class HdidAllStrategy( + IConfiguration configuration, + ICacheProvider cacheProvider, + IClientRegistriesDelegate clientRegistriesDelegate, + IPatientIdentityApi patientIdentityApi, + ILogger logger, + IMapper mapper) : PatientQueryStrategy(configuration, cacheProvider, logger) { - private readonly IClientRegistriesDelegate clientRegistriesDelegate; - private readonly IPatientIdentityApi patientIdentityApi; - private readonly IMapper mapper; - - /// - /// Initializes a new instance of the class. - /// - /// The injected configuration. - /// The injected cache provider. - /// The injected logger. - /// The injected client registries delegate. - /// The injected patient identity api. - /// The injected mapper. - public HdidAllStrategy( - IConfiguration configuration, - ICacheProvider cacheProvider, - IClientRegistriesDelegate clientRegistriesDelegate, - IPatientIdentityApi patientIdentityApi, - ILogger logger, - IMapper mapper) - : base(configuration, cacheProvider, logger) - { - this.clientRegistriesDelegate = clientRegistriesDelegate; - this.patientIdentityApi = patientIdentityApi; - this.mapper = mapper; - } - /// public override async Task GetPatientAsync(PatientRequest request, CancellationToken ct = default) { @@ -67,7 +52,7 @@ public override async Task GetPatientAsync(PatientRequest request, try { - patient ??= await this.clientRegistriesDelegate.GetDemographicsAsync(OidType.Hdid, request.Identifier, request.DisabledValidation, ct); + patient ??= await clientRegistriesDelegate.GetDemographicsAsync(OidType.Hdid, request.Identifier, request.DisabledValidation, ct); } catch (CommunicationException ce) { @@ -76,8 +61,8 @@ public override async Task GetPatientAsync(PatientRequest request, try { this.GetLogger().LogDebug("Retrieving patient from PHSA"); - PatientIdentity result = await this.patientIdentityApi.GetPatientIdentityAsync(request.Identifier, ct); - patient = this.mapper.Map(result); + PatientIdentity result = await patientIdentityApi.GetPatientIdentityAsync(request.Identifier, ct); + patient = mapper.Map(result); } catch (ApiException e) when (e.StatusCode == HttpStatusCode.NotFound) { diff --git a/Apps/AccountDataAccess/src/Patient/Strategy/HdidEmpiStrategy.cs b/Apps/AccountDataAccess/src/Patient/Strategy/HdidEmpiStrategy.cs index 990f39d695..cb87a117e9 100644 --- a/Apps/AccountDataAccess/src/Patient/Strategy/HdidEmpiStrategy.cs +++ b/Apps/AccountDataAccess/src/Patient/Strategy/HdidEmpiStrategy.cs @@ -25,32 +25,21 @@ namespace HealthGateway.AccountDataAccess.Patient.Strategy /// /// Strategy implementation for EMPI hdid patient data source with or without cache. /// - internal class HdidEmpiStrategy : PatientQueryStrategy + /// The injected configuration. + /// The injected cache provider. + /// The injected client registries delegate. + /// The injected logger. + internal class HdidEmpiStrategy( + IConfiguration configuration, + ICacheProvider cacheProvider, + IClientRegistriesDelegate clientRegistriesDelegate, + ILogger logger) : PatientQueryStrategy(configuration, cacheProvider, logger) { - private readonly IClientRegistriesDelegate clientRegistriesDelegate; - - /// - /// Initializes a new instance of the class. - /// - /// The injected configuration. - /// The injected cache provider. - /// The injected client registries delegate. - /// The injected logger. - public HdidEmpiStrategy( - IConfiguration configuration, - ICacheProvider cacheProvider, - IClientRegistriesDelegate clientRegistriesDelegate, - ILogger logger) - : base(configuration, cacheProvider, logger) - { - this.clientRegistriesDelegate = clientRegistriesDelegate; - } - /// public override async Task GetPatientAsync(PatientRequest request, CancellationToken ct = default) { PatientModel patient = (request.UseCache ? await this.GetFromCacheAsync(request.Identifier, PatientIdentifierType.Hdid, ct) : null) ?? - await this.clientRegistriesDelegate.GetDemographicsAsync(OidType.Hdid, request.Identifier, request.DisabledValidation, ct); + await clientRegistriesDelegate.GetDemographicsAsync(OidType.Hdid, request.Identifier, request.DisabledValidation, ct); await this.CachePatientAsync(patient, request.DisabledValidation, ct); return patient; diff --git a/Apps/AccountDataAccess/src/Patient/Strategy/HdidPhsaStrategy.cs b/Apps/AccountDataAccess/src/Patient/Strategy/HdidPhsaStrategy.cs index beab4373bf..813b284165 100644 --- a/Apps/AccountDataAccess/src/Patient/Strategy/HdidPhsaStrategy.cs +++ b/Apps/AccountDataAccess/src/Patient/Strategy/HdidPhsaStrategy.cs @@ -30,31 +30,18 @@ namespace HealthGateway.AccountDataAccess.Patient.Strategy /// /// Strategy implementation for PHSA phn patient data source with or without cache. /// - internal class HdidPhsaStrategy : PatientQueryStrategy + /// The injected configuration. + /// The injected cache provider. + /// The injected patient identity api. + /// The injected logger. + /// The injected mapper. + internal class HdidPhsaStrategy( + IConfiguration configuration, + ICacheProvider cacheProvider, + IPatientIdentityApi patientIdentityApi, + ILogger logger, + IMapper mapper) : PatientQueryStrategy(configuration, cacheProvider, logger) { - private readonly IPatientIdentityApi patientIdentityApi; - private readonly IMapper mapper; - - /// - /// Initializes a new instance of the class. - /// - /// The injected configuration. - /// The injected cache provider. - /// The injected patient identity api. - /// The injected logger. - /// The injected mapper. - public HdidPhsaStrategy( - IConfiguration configuration, - ICacheProvider cacheProvider, - IPatientIdentityApi patientIdentityApi, - ILogger logger, - IMapper mapper) - : base(configuration, cacheProvider, logger) - { - this.patientIdentityApi = patientIdentityApi; - this.mapper = mapper; - } - /// public override async Task GetPatientAsync(PatientRequest request, CancellationToken ct = default) { @@ -65,8 +52,8 @@ public override async Task GetPatientAsync(PatientRequest request, try { this.GetLogger().LogDebug("Retrieving patient from PHSA"); - PatientIdentity result = await this.patientIdentityApi.GetPatientIdentityAsync(request.Identifier, ct); - patient = this.mapper.Map(result); + PatientIdentity result = await patientIdentityApi.GetPatientIdentityAsync(request.Identifier, ct); + patient = mapper.Map(result); } catch (ApiException e) when (e.StatusCode == HttpStatusCode.NotFound) { diff --git a/Apps/AccountDataAccess/src/Patient/Strategy/PatientQueryContext.cs b/Apps/AccountDataAccess/src/Patient/Strategy/PatientQueryContext.cs index ce2f5f32ab..a8f1b724f0 100644 --- a/Apps/AccountDataAccess/src/Patient/Strategy/PatientQueryContext.cs +++ b/Apps/AccountDataAccess/src/Patient/Strategy/PatientQueryContext.cs @@ -21,22 +21,9 @@ namespace HealthGateway.AccountDataAccess.Patient.Strategy /// /// The Context defines the interface of interest to clients. /// - internal class PatientQueryContext + /// The implemented request to use. + internal class PatientQueryContext(PatientQueryStrategy patientQueryStrategy) { - // The Context maintains a reference to one of the Strategy objects. The - // Context does not know the concrete class of a request. It should - // work with all strategies via the Strategy interface. - private readonly PatientQueryStrategy patientQueryStrategy; - - /// - /// Initializes a new instance of the class. - /// - /// The implemented request to use. - public PatientQueryContext(PatientQueryStrategy patientQueryStrategy) - { - this.patientQueryStrategy = patientQueryStrategy; - } - /// /// Returns patient from the specified source in the patient request. /// @@ -45,7 +32,7 @@ public PatientQueryContext(PatientQueryStrategy patientQueryStrategy) /// The patient model. public async Task GetPatientAsync(PatientRequest patientRequest, CancellationToken ct = default) { - return await this.patientQueryStrategy.GetPatientAsync(patientRequest, ct); + return await patientQueryStrategy.GetPatientAsync(patientRequest, ct); } } } diff --git a/Apps/AccountDataAccess/src/Patient/Strategy/PatientQueryFactory.cs b/Apps/AccountDataAccess/src/Patient/Strategy/PatientQueryFactory.cs index b76bdcf07d..694fb02de3 100644 --- a/Apps/AccountDataAccess/src/Patient/Strategy/PatientQueryFactory.cs +++ b/Apps/AccountDataAccess/src/Patient/Strategy/PatientQueryFactory.cs @@ -21,19 +21,9 @@ namespace HealthGateway.AccountDataAccess.Patient.Strategy /// /// Factory for instances. /// - internal class PatientQueryFactory + /// The injected service provider. + internal class PatientQueryFactory(IServiceProvider serviceProvider) { - private readonly IServiceProvider serviceProvider; - - /// - /// Initializes a new instance of the class. - /// - /// The injected service provider. - public PatientQueryFactory(IServiceProvider serviceProvider) - { - this.serviceProvider = serviceProvider; - } - /// /// Returns an instance of the requested class. /// @@ -42,7 +32,7 @@ public PatientQueryFactory(IServiceProvider serviceProvider) public PatientQueryStrategy GetPatientQueryStrategy(string strategy) { Type type = Type.GetType(strategy) ?? throw new ArgumentException($"Invalid strategy type {strategy}"); - return (PatientQueryStrategy)this.serviceProvider.GetRequiredService(type); + return (PatientQueryStrategy)serviceProvider.GetRequiredService(type); } } } diff --git a/Apps/AccountDataAccess/src/Patient/Strategy/PatientQueryStrategy.cs b/Apps/AccountDataAccess/src/Patient/Strategy/PatientQueryStrategy.cs index 51e2c19338..f99a825633 100644 --- a/Apps/AccountDataAccess/src/Patient/Strategy/PatientQueryStrategy.cs +++ b/Apps/AccountDataAccess/src/Patient/Strategy/PatientQueryStrategy.cs @@ -33,29 +33,16 @@ namespace HealthGateway.AccountDataAccess.Patient.Strategy /// The Context uses this interface to call the algorithm defined by Concrete /// Strategies. /// - internal abstract class PatientQueryStrategy + /// The Configuration to use. + /// The injected cache provider. + /// The injected logger. + internal abstract class PatientQueryStrategy( + IConfiguration configuration, + ICacheProvider cacheProvider, + ILogger logger) { private const string PatientCacheDomain = "PatientV2"; - - private readonly ILogger logger; - private readonly ICacheProvider cacheProvider; - private readonly int cacheTtl; - - /// - /// Initializes a new instance of the class. - /// - /// The Configuration to use. - /// The injected cache provider. - /// The injected logger. - protected PatientQueryStrategy( - IConfiguration configuration, - ICacheProvider cacheProvider, - ILogger logger) - { - this.cacheProvider = cacheProvider; - this.logger = logger; - this.cacheTtl = configuration.GetSection("PatientService").GetValue("CacheTTL", 0); - } + private readonly int cacheTtl = configuration.GetSection("PatientService").GetValue("CacheTTL", 0); private static ActivitySource ActivitySource { get; } = new(typeof(PatientQueryStrategy).FullName); @@ -75,7 +62,7 @@ protected PatientQueryStrategy( /// A class. protected ILogger GetLogger() { - return this.logger; + return logger; } /// @@ -94,16 +81,16 @@ protected ILogger GetLogger() using Activity? activity = ActivitySource.StartActivity(); activity?.AddBaggage("CacheIdentifier", identifier); - this.logger.LogDebug("Accessing patient cache via {CacheIdentifierType}", identifierType); + logger.LogDebug("Accessing patient cache via {CacheIdentifierType}", identifierType); PatientModel? retPatient = identifierType switch { - PatientIdentifierType.Hdid => await this.cacheProvider.GetItemAsync($"{PatientCacheDomain}:HDID:{identifier}", ct), - PatientIdentifierType.Phn => await this.cacheProvider.GetItemAsync($"{PatientCacheDomain}:PHN:{identifier}", ct), + PatientIdentifierType.Hdid => await cacheProvider.GetItemAsync($"{PatientCacheDomain}:HDID:{identifier}", ct), + PatientIdentifierType.Phn => await cacheProvider.GetItemAsync($"{PatientCacheDomain}:PHN:{identifier}", ct), _ => null, }; - this.logger.LogDebug("Patient cache access outcome: {CacheResult}", retPatient == null ? "miss" : "hit"); + logger.LogDebug("Patient cache access outcome: {CacheResult}", retPatient == null ? "miss" : "hit"); return retPatient; } @@ -138,15 +125,15 @@ private async Task CachePatientAsync(PatientModel patientModel, CancellationToke if (!string.IsNullOrEmpty(patientModel.Hdid)) { activity?.AddBaggage("CacheIdentifier", patientModel.Hdid); - this.logger.LogDebug("Storing patient in cache via {CacheIdentifierType}", PatientIdentifierType.Hdid); - await this.cacheProvider.AddItemAsync($"{PatientCacheDomain}:HDID:{patientModel.Hdid}", patientModel, expiry, ct); + logger.LogDebug("Storing patient in cache via {CacheIdentifierType}", PatientIdentifierType.Hdid); + await cacheProvider.AddItemAsync($"{PatientCacheDomain}:HDID:{patientModel.Hdid}", patientModel, expiry, ct); } if (!string.IsNullOrEmpty(patientModel.Phn)) { activity?.AddBaggage("CacheIdentifier", patientModel.Phn); - this.logger.LogDebug("Storing patient in cache via {CacheIdentifierType}", PatientIdentifierType.Phn); - await this.cacheProvider.AddItemAsync($"{PatientCacheDomain}:PHN:{patientModel.Phn}", patientModel, expiry, ct); + logger.LogDebug("Storing patient in cache via {CacheIdentifierType}", PatientIdentifierType.Phn); + await cacheProvider.AddItemAsync($"{PatientCacheDomain}:PHN:{patientModel.Phn}", patientModel, expiry, ct); } } } diff --git a/Apps/AccountDataAccess/src/Patient/Strategy/PhnEmpiStrategy.cs b/Apps/AccountDataAccess/src/Patient/Strategy/PhnEmpiStrategy.cs index 55e6f3432f..65cf43cbbb 100644 --- a/Apps/AccountDataAccess/src/Patient/Strategy/PhnEmpiStrategy.cs +++ b/Apps/AccountDataAccess/src/Patient/Strategy/PhnEmpiStrategy.cs @@ -27,34 +27,23 @@ namespace HealthGateway.AccountDataAccess.Patient.Strategy /// /// Strategy implementation for EMPI phn patient data source with or without cache. /// - internal class PhnEmpiStrategy : PatientQueryStrategy + /// The injected configuration. + /// The injected cache provider. + /// The injected client registries delegate. + /// The injected logger. + internal class PhnEmpiStrategy( + IConfiguration configuration, + ICacheProvider cacheProvider, + IClientRegistriesDelegate clientRegistriesDelegate, + ILogger logger) : PatientQueryStrategy(configuration, cacheProvider, logger) { - private readonly IClientRegistriesDelegate clientRegistriesDelegate; - - /// - /// Initializes a new instance of the class. - /// - /// The injected configuration. - /// The injected cache provider. - /// The injected client registries delegate. - /// The injected logger. - public PhnEmpiStrategy( - IConfiguration configuration, - ICacheProvider cacheProvider, - IClientRegistriesDelegate clientRegistriesDelegate, - ILogger logger) - : base(configuration, cacheProvider, logger) - { - this.clientRegistriesDelegate = clientRegistriesDelegate; - } - /// public override async Task GetPatientAsync(PatientRequest request, CancellationToken ct = default) { await new PhnValidator(ErrorMessages.PhnInvalid).ValidateAndThrowAsync(request.Identifier, ct); PatientModel patient = (request.UseCache ? await this.GetFromCacheAsync(request.Identifier, PatientIdentifierType.Phn, ct) : null) ?? - await this.clientRegistriesDelegate.GetDemographicsAsync(OidType.Phn, request.Identifier, request.DisabledValidation, ct); + await clientRegistriesDelegate.GetDemographicsAsync(OidType.Phn, request.Identifier, request.DisabledValidation, ct); await this.CachePatientAsync(patient, request.DisabledValidation, ct); return patient; diff --git a/Apps/AccountDataAccess/test/unit/AccountDataAccessTests.csproj b/Apps/AccountDataAccess/test/unit/AccountDataAccessTests.csproj index 714bf0404b..463aa7be64 100644 --- a/Apps/AccountDataAccess/test/unit/AccountDataAccessTests.csproj +++ b/Apps/AccountDataAccess/test/unit/AccountDataAccessTests.csproj @@ -12,20 +12,20 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/Apps/AccountDataAccess/test/unit/ClientRegistriesDelegateTests.cs b/Apps/AccountDataAccess/test/unit/ClientRegistriesDelegateTests.cs index c0a292905f..6182d7a6d3 100644 --- a/Apps/AccountDataAccess/test/unit/ClientRegistriesDelegateTests.cs +++ b/Apps/AccountDataAccess/test/unit/ClientRegistriesDelegateTests.cs @@ -478,21 +478,18 @@ private static EnName GenerateEnName(string name, cs_EntityNamePartQualifier? qu private static II[] GenerateId(bool shouldReturnEmpty = false, string? extension = null, string? oidType = null, bool displayable = true) { - if (shouldReturnEmpty) - { - return []; - } - - return - [ - new II - { - root = oidType ?? HdidOidType, - extension = oidType == null || (oidType == HdidOidType && extension == null) ? Hdid - : oidType == PhnOidType && extension == null ? Phn : extension, - displayable = displayable, - }, - ]; + return shouldReturnEmpty + ? [] + : + [ + new II + { + root = oidType ?? HdidOidType, + extension = oidType == null || (oidType == HdidOidType && extension == null) ? Hdid + : oidType == PhnOidType && extension == null ? Phn : extension, + displayable = displayable, + }, + ]; } private static HCIM_IN_GetDemographicsResponsePerson GenerateIdentifiedPerson( @@ -507,7 +504,9 @@ private static HCIM_IN_GetDemographicsResponsePerson GenerateIdentifiedPerson( return new HCIM_IN_GetDemographicsResponsePerson { deceasedInd = new BL - { value = deceasedInd }, + { + value = deceasedInd, + }, id = GenerateId(shouldReturnEmpty, extension, oidType), name = names.ToArray(), birthTime = new TS diff --git a/Apps/AccountDataAccess/test/unit/PatientMappingsTests.cs b/Apps/AccountDataAccess/test/unit/PatientMappingsTests.cs index fb2e0afd64..72b4bd3aab 100644 --- a/Apps/AccountDataAccess/test/unit/PatientMappingsTests.cs +++ b/Apps/AccountDataAccess/test/unit/PatientMappingsTests.cs @@ -86,9 +86,15 @@ public void ShouldGetPatientModel() ResponseCode = string.Empty, IsDeceased = true, CommonName = new Name - { GivenName = expectedCommonGivenName, Surname = PhsaPreferredLastName }, + { + GivenName = expectedCommonGivenName, + Surname = PhsaPreferredLastName, + }, LegalName = new Name - { GivenName = expectedLegalGivenName, Surname = PhsaLegalLastName }, + { + GivenName = expectedLegalGivenName, + Surname = PhsaLegalLastName, + }, PhysicalAddress = new Address { StreetLines = @@ -173,9 +179,15 @@ public void ShouldGetPatientModelGivenNoNamesAndAddresses() ResponseCode = string.Empty, IsDeceased = true, CommonName = new Name - { GivenName = expectedCommonGivenName, Surname = expectedCommonSurname }, + { + GivenName = expectedCommonGivenName, + Surname = expectedCommonSurname, + }, LegalName = new Name - { GivenName = expectedLegalGivenName, Surname = expectedLegalSurname }, + { + GivenName = expectedLegalGivenName, + Surname = expectedLegalSurname, + }, PhysicalAddress = null, PostalAddress = null, }; diff --git a/Apps/AccountDataAccess/test/unit/PatientRepositoryTests.cs b/Apps/AccountDataAccess/test/unit/PatientRepositoryTests.cs index e4048e4c43..f2df441bdb 100644 --- a/Apps/AccountDataAccess/test/unit/PatientRepositoryTests.cs +++ b/Apps/AccountDataAccess/test/unit/PatientRepositoryTests.cs @@ -303,7 +303,7 @@ private static IConfigurationRoot GetIConfigurationRoot(bool blockedDataSourcesE }; return new ConfigurationBuilder() - .AddInMemoryCollection(myConfiguration.ToList()) + .AddInMemoryCollection(myConfiguration) .Build(); } @@ -330,10 +330,12 @@ private static BlockAccessMock SetupBlockAccessMock(BlockedAccess blockedAccess, if (changeFeedEnabled) { - IEnumerable events = new MessageEnvelope[] - { - new(new DataSourcesBlockedEvent(blockedAccess.Hdid, GetDataSourceValues(blockedAccess.DataSources)), blockedAccess.Hdid), - }; + IEnumerable events = + [ + new( + new DataSourcesBlockedEvent(blockedAccess.Hdid, GetDataSourceValues(blockedAccess.DataSources)), + blockedAccess.Hdid), + ]; messageSender.Setup(ms => ms.SendAsync(events, It.IsAny())); } diff --git a/Apps/AccountDataAccess/test/unit/Strategy/CacheProviderTests.cs b/Apps/AccountDataAccess/test/unit/Strategy/CacheProviderTests.cs index a2cd5ac834..ed9f872af4 100644 --- a/Apps/AccountDataAccess/test/unit/Strategy/CacheProviderTests.cs +++ b/Apps/AccountDataAccess/test/unit/Strategy/CacheProviderTests.cs @@ -87,7 +87,7 @@ private static IConfigurationRoot GetConfiguration(bool useCache) }; return new ConfigurationBuilder() - .AddInMemoryCollection(myConfiguration.ToList()) + .AddInMemoryCollection(myConfiguration) .Build(); } @@ -105,7 +105,7 @@ private static CachePatientMock SetupCachePatientMock(bool useCache, PatientMode s => s.GetDemographicsAsync( It.Is(x => x == OidType.Hdid), It.Is(x => x == Hdid), - It.Is(x => x == false), + It.Is(x => !x), It.IsAny())) .ReturnsAsync(patient); diff --git a/Apps/AccountDataAccess/test/unit/Strategy/HdidAllStrategyTests.cs b/Apps/AccountDataAccess/test/unit/Strategy/HdidAllStrategyTests.cs index 4319916c82..8a7e4c9480 100644 --- a/Apps/AccountDataAccess/test/unit/Strategy/HdidAllStrategyTests.cs +++ b/Apps/AccountDataAccess/test/unit/Strategy/HdidAllStrategyTests.cs @@ -98,9 +98,15 @@ public async Task ShouldGetPatientFromPhsa() ResponseCode = string.Empty, IsDeceased = false, CommonName = new Name - { GivenName = string.Empty, Surname = string.Empty }, + { + GivenName = string.Empty, + Surname = string.Empty, + }, LegalName = new Name - { GivenName = string.Empty, Surname = string.Empty }, + { + GivenName = string.Empty, + Surname = string.Empty, + }, }; PatientIdentity patientIdentity = new() @@ -146,7 +152,7 @@ private static IConfigurationRoot GetConfiguration() }; return new ConfigurationBuilder() - .AddInMemoryCollection(myConfiguration.ToList()) + .AddInMemoryCollection(myConfiguration) .Build(); } @@ -169,7 +175,7 @@ private static PatientMock SetupGetPatientMock(bool useCache, PatientModel patie s => s.GetDemographicsAsync( It.Is(x => x == OidType.Hdid), It.Is(x => x == Hdid), - It.Is(x => x == false), + It.Is(x => !x), It.IsAny())) .ReturnsAsync(patient); } @@ -192,7 +198,7 @@ private static HdidAllStrategy SetupHdidAllStrategyForGetPatientFromPhsa(Patient s => s.GetDemographicsAsync( It.Is(x => x == OidType.Hdid), It.Is(x => x == PhsaHdid), - It.Is(x => x == false), + It.Is(x => !x), It.IsAny())) .Throws(new CommunicationException("Unit test PHSA get patient identity.")); @@ -219,7 +225,7 @@ private static HdidAllStrategy SetupHdidAllStrategyForGetPatientHandlesPatientId s => s.GetDemographicsAsync( It.Is(x => x == OidType.Hdid), It.Is(x => x == PhsaHdidNotFound), - It.Is(x => x == false), + It.Is(x => !x), It.IsAny())) .Throws(new CommunicationException("Unit test PHSA get patient identity. NotFound")); diff --git a/Apps/AccountDataAccess/test/unit/Strategy/HdidEmpiStrategyTests.cs b/Apps/AccountDataAccess/test/unit/Strategy/HdidEmpiStrategyTests.cs index b41c4978a6..bb7da4f68c 100644 --- a/Apps/AccountDataAccess/test/unit/Strategy/HdidEmpiStrategyTests.cs +++ b/Apps/AccountDataAccess/test/unit/Strategy/HdidEmpiStrategyTests.cs @@ -77,7 +77,7 @@ private static IConfigurationRoot GetConfiguration() }; return new ConfigurationBuilder() - .AddInMemoryCollection(myConfiguration.ToList()) + .AddInMemoryCollection(myConfiguration) .Build(); } @@ -100,7 +100,7 @@ private static PatientMock SetupGetPatientMock(bool useCache, PatientModel patie s => s.GetDemographicsAsync( It.Is(x => x == OidType.Hdid), It.Is(x => x == Hdid), - It.Is(x => x == false), + It.Is(x => !x), It.IsAny())) .ReturnsAsync(patient); } diff --git a/Apps/AccountDataAccess/test/unit/Strategy/HdidPhsaStrategyTests.cs b/Apps/AccountDataAccess/test/unit/Strategy/HdidPhsaStrategyTests.cs index d8679e5ffb..bb25a3ebc1 100644 --- a/Apps/AccountDataAccess/test/unit/Strategy/HdidPhsaStrategyTests.cs +++ b/Apps/AccountDataAccess/test/unit/Strategy/HdidPhsaStrategyTests.cs @@ -63,9 +63,15 @@ public async Task ShouldGetPatient(bool useCache) ResponseCode = string.Empty, IsDeceased = false, CommonName = new Name - { GivenName = string.Empty, Surname = string.Empty }, + { + GivenName = string.Empty, + Surname = string.Empty, + }, LegalName = new Name - { GivenName = string.Empty, Surname = string.Empty }, + { + GivenName = string.Empty, + Surname = string.Empty, + }, }; (HdidPhsaStrategy strategy, Mock cacheProvider) = SetupPatientMockForGetPatient(useCache, expected); @@ -110,7 +116,7 @@ private static IConfigurationRoot GetConfiguration() }; return new ConfigurationBuilder() - .AddInMemoryCollection(myConfiguration.ToList()) + .AddInMemoryCollection(myConfiguration) .Build(); } diff --git a/Apps/AccountDataAccess/test/unit/Strategy/PhnEmpiStrategyTests.cs b/Apps/AccountDataAccess/test/unit/Strategy/PhnEmpiStrategyTests.cs index 5b0b5883c7..a175c0a8c5 100644 --- a/Apps/AccountDataAccess/test/unit/Strategy/PhnEmpiStrategyTests.cs +++ b/Apps/AccountDataAccess/test/unit/Strategy/PhnEmpiStrategyTests.cs @@ -104,7 +104,7 @@ private static IConfigurationRoot GetConfiguration() }; return new ConfigurationBuilder() - .AddInMemoryCollection(myConfiguration.ToList()) + .AddInMemoryCollection(myConfiguration) .Build(); } @@ -127,7 +127,7 @@ private static PatientMock SetupGetPatientMock(bool useCache, PatientModel patie s => s.GetDemographicsAsync( It.Is(x => x == OidType.Phn), It.Is(x => x == Phn), - It.Is(x => x == false), + It.Is(x => !x), It.IsAny())) .ReturnsAsync(patient); } diff --git a/Apps/Admin/Client/Admin.Client.csproj b/Apps/Admin/Client/Admin.Client.csproj index c354e5a27f..7adb9d4f67 100644 --- a/Apps/Admin/Client/Admin.Client.csproj +++ b/Apps/Admin/Client/Admin.Client.csproj @@ -13,20 +13,20 @@ - - - - + + + + - - - - - - - + + + + + + + diff --git a/Apps/Admin/Client/Components/Communications/BroadcastDialog.razor.cs b/Apps/Admin/Client/Components/Communications/BroadcastDialog.razor.cs index c622c4b74c..2cc6b5e7fb 100644 --- a/Apps/Admin/Client/Components/Communications/BroadcastDialog.razor.cs +++ b/Apps/Admin/Client/Components/Communications/BroadcastDialog.razor.cs @@ -17,6 +17,7 @@ namespace HealthGateway.Admin.Client.Components.Communications; using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using Fluxor; using Fluxor.Blazor.Web.Components; @@ -47,21 +48,18 @@ public partial class BroadcastDialog : FluxorComponent BroadcastActionType.ExternalLink, ]; + [SuppressMessage("Style", "IDE0072:Populate switch", Justification = "Team decision")] private Func ValidateActionUrl => urlString => { - switch (this.Broadcast.ActionType) + return this.Broadcast.ActionType switch { - case BroadcastActionType.InternalLink: - case BroadcastActionType.ExternalLink: - return urlString.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase) - && Uri.TryCreate(this.ActionUrlString, UriKind.Absolute, out Uri? _) - ? null - : "URL is invalid"; - case BroadcastActionType.None: - default: - return urlString.Length == 0 ? null : "Selected Action Type does not support Action URL"; - } + BroadcastActionType.InternalLink or BroadcastActionType.ExternalLink => urlString.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase) + && Uri.TryCreate(this.ActionUrlString, UriKind.Absolute, out Uri? _) + ? null + : "URL is invalid", + _ => urlString.Length == 0 ? null : "Selected Action Type does not support Action URL", + }; }; [CascadingParameter] @@ -149,14 +147,10 @@ private void RetrieveFormValues() ? null : (this.ExpiryDate.Value + this.ExpiryTime.Value).ToUniversalTime(); - if (this.ActionUrlString.Length > 0 && Uri.TryCreate(this.ActionUrlString, UriKind.Absolute, out Uri? result)) - { - this.Broadcast.ActionUrl = result; - } - else - { - this.Broadcast.ActionUrl = null; - } + this.Broadcast.ActionUrl = this.ActionUrlString.Length > 0 && + Uri.TryCreate(this.ActionUrlString, UriKind.Absolute, out Uri? result) + ? result + : null; } private void HandleClickCancel() diff --git a/Apps/Admin/Client/Components/Delegation/DelegateTable.razor.cs b/Apps/Admin/Client/Components/Delegation/DelegateTable.razor.cs index 9ffb916f0b..e0470b3c9e 100644 --- a/Apps/Admin/Client/Components/Delegation/DelegateTable.razor.cs +++ b/Apps/Admin/Client/Components/Delegation/DelegateTable.razor.cs @@ -17,6 +17,7 @@ namespace HealthGateway.Admin.Client.Components.Delegation { using System; using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; using System.Linq; using Fluxor; using Fluxor.Blazor.Web.Components; @@ -49,6 +50,7 @@ public partial class DelegateTable : FluxorComponent private IEnumerable Rows => this.Data.Select(d => new DelegateRow(d)); + [SuppressMessage("Style", "IDE0072:Populate switch", Justification = "Team decision")] private static Color GetStatusColor(DelegationStatus status) { return status switch @@ -73,11 +75,9 @@ public DelegateRow(ExtendedDelegateInfo model) this.DateOfBirth = model.Birthdate; this.PersonalHealthNumber = model.PersonalHealthNumber; this.Address = AddressUtility.GetAddressAsSingleLine(model.PhysicalAddress ?? model.PostalAddress); - this.DelegationStatus = model.DelegationStatus switch - { - DelegationStatus.Unknown => DelegationStatus.Allowed, - _ => model.DelegationStatus, - }; + this.DelegationStatus = model.DelegationStatus == DelegationStatus.Unknown + ? DelegationStatus.Allowed + : model.DelegationStatus; this.ToBeRemoved = model.StagedDelegationStatus == DelegationStatus.Disallowed; } diff --git a/Apps/Admin/Client/Components/Support/AddressConfirmationDialog.razor.cs b/Apps/Admin/Client/Components/Support/AddressConfirmationDialog.razor.cs index c52b07f695..eb1cd0309a 100644 --- a/Apps/Admin/Client/Components/Support/AddressConfirmationDialog.razor.cs +++ b/Apps/Admin/Client/Components/Support/AddressConfirmationDialog.razor.cs @@ -109,6 +109,7 @@ public partial class AddressConfirmationDialog : F private IMask? PostalCodeMask { get; set; } + [SuppressMessage("Style", "IDE0072:Populate switch", Justification = "Team decision")] private string Country { get => this.country; @@ -188,6 +189,16 @@ private static string GetCountryNameFromInput(string input) return string.Empty; } + private static string? ValidateCanadianPostalCode(string postalCode) + { + return !AddressUtility.PostalCodeRegex().IsMatch(postalCode) ? "Incomplete postal code" : null; + } + + private static string? ValidateUsPostalCode(string postalCode) + { + return !AddressUtility.ZipCodeRegex().IsMatch(postalCode) ? "Incomplete zip code" : null; + } + private void PopulateInputs(Address address) { this.AddressLines = string.Join(Environment.NewLine, address.StreetLines); @@ -211,14 +222,16 @@ private void PopulateInputs(Address address) private string GetCountryToOutput() { - if (this.SelectedCountryCode == CountryCode.CA && this.OutputCanadaAsEmptyString) - { - return string.Empty; - } + string? canadaOutput = this.SelectedCountryCode == CountryCode.CA && this.OutputCanadaAsEmptyString + ? string.Empty + : null; - return this.OutputCountryCodeFormat ? this.SelectedCountryCode.ToString() : AddressUtility.GetCountryName(this.SelectedCountryCode); + return canadaOutput ?? (this.OutputCountryCodeFormat + ? this.SelectedCountryCode.ToString() + : AddressUtility.GetCountryName(this.SelectedCountryCode)); } + [SuppressMessage("Style", "IDE0072:Populate switch", Justification = "Team decision")] private Address GetAddressModel() { return new() @@ -245,22 +258,17 @@ private void HandleCountryChanged(string value) this.OtherState = string.Empty; } + [SuppressMessage("Style", "IDE0072:Populate switch", Justification = "Team decision")] private string? ValidatePostalCode(string postalCode) { - if (string.IsNullOrWhiteSpace(postalCode)) - { - return "Required"; - } - - switch (this.SelectedCountryCode) - { - case CountryCode.CA: - return !AddressUtility.PostalCodeRegex().IsMatch(postalCode) ? "Incomplete postal code" : null; - case CountryCode.US: - return !AddressUtility.ZipCodeRegex().IsMatch(postalCode) ? "Incomplete zip code" : null; - default: - return null; - } + return string.IsNullOrWhiteSpace(postalCode) + ? "Required" + : this.SelectedCountryCode switch + { + CountryCode.CA => ValidateCanadianPostalCode(postalCode), + CountryCode.US => ValidateUsPostalCode(postalCode), + _ => null, + }; } private void HandleActionSuccess(TSuccessAction successAction) diff --git a/Apps/Admin/Client/Pages/AgentAccessPage.razor.cs b/Apps/Admin/Client/Pages/AgentAccessPage.razor.cs index 43f4c2ff30..83bab27814 100644 --- a/Apps/Admin/Client/Pages/AgentAccessPage.razor.cs +++ b/Apps/Admin/Client/Pages/AgentAccessPage.razor.cs @@ -40,17 +40,13 @@ public partial class AgentAccessPage : FluxorComponent private static Func ValidateQueryParameter => parameter => { - if (string.IsNullOrWhiteSpace(parameter)) - { - return "Search parameter is required"; - } - - if (StringManipulator.StripWhitespace(parameter).Length < 3) - { - return "Query must contain at least 3 characters"; - } + string? lengthValidationResult = StringManipulator.StripWhitespace(parameter).Length < 3 + ? "Query must contain at least 3 characters" + : null; - return null; + return string.IsNullOrWhiteSpace(parameter) + ? "Search parameter is required" + : lengthValidationResult; }; [Inject] diff --git a/Apps/Admin/Client/Pages/BetaAccessPage.razor.cs b/Apps/Admin/Client/Pages/BetaAccessPage.razor.cs index 740b70a41c..d0078745d3 100644 --- a/Apps/Admin/Client/Pages/BetaAccessPage.razor.cs +++ b/Apps/Admin/Client/Pages/BetaAccessPage.razor.cs @@ -36,17 +36,13 @@ public partial class BetaAccessPage : FluxorComponent { private static Func ValidateQueryParameter => parameter => { - if (string.IsNullOrWhiteSpace(parameter)) - { - return "Email is required"; - } - - if (!NaiveEmailValidator.IsValid(StringManipulator.StripWhitespace(parameter))) - { - return "Invalid email format"; - } + string? emailValidationResult = !NaiveEmailValidator.IsValid(StringManipulator.StripWhitespace(parameter)) + ? "Invalid email format" + : null; - return null; + return string.IsNullOrWhiteSpace(parameter) + ? "Email is required" + : emailValidationResult; }; [Inject] diff --git a/Apps/Admin/Client/Pages/DashboardPage.razor.cs b/Apps/Admin/Client/Pages/DashboardPage.razor.cs index ca22ead45c..e36cedc22e 100644 --- a/Apps/Admin/Client/Pages/DashboardPage.razor.cs +++ b/Apps/Admin/Client/Pages/DashboardPage.razor.cs @@ -134,24 +134,19 @@ private int UniqueDays } } - private IEnumerable TableData - { - get - { - return this.DailyUsageCounts.UserRegistrations.Select(x => new DailyDataRow { Date = x.Key, UserRegistrations = x.Value }) - .Concat(this.DailyUsageCounts.UserLogins.Select(x => new DailyDataRow { Date = x.Key, UserLogins = x.Value })) - .Concat(this.DailyUsageCounts.DependentRegistrations.Select(x => new DailyDataRow { Date = x.Key, DependentRegistrations = x.Value })) - .GroupBy(x => x.Date) - .Select( - g => new DailyDataRow - { - Date = g.Key, - UserRegistrations = g.Sum(x => x.UserRegistrations), - UserLogins = g.Sum(x => x.UserLogins), - DependentRegistrations = g.Sum(x => x.DependentRegistrations), - }); - } - } + private IEnumerable TableData => + this.DailyUsageCounts.UserRegistrations.Select(x => new DailyDataRow { Date = x.Key, UserRegistrations = x.Value }) + .Concat(this.DailyUsageCounts.UserLogins.Select(x => new DailyDataRow { Date = x.Key, UserLogins = x.Value })) + .Concat(this.DailyUsageCounts.DependentRegistrations.Select(x => new DailyDataRow { Date = x.Key, DependentRegistrations = x.Value })) + .GroupBy(x => x.Date) + .Select( + g => new DailyDataRow + { + Date = g.Key, + UserRegistrations = g.Sum(x => x.UserRegistrations), + UserLogins = g.Sum(x => x.UserLogins), + DependentRegistrations = g.Sum(x => x.DependentRegistrations), + }); /// protected override void OnInitialized() diff --git a/Apps/Admin/Client/Pages/DelegationPage.razor.cs b/Apps/Admin/Client/Pages/DelegationPage.razor.cs index f9121eee37..c0218c981d 100644 --- a/Apps/Admin/Client/Pages/DelegationPage.razor.cs +++ b/Apps/Admin/Client/Pages/DelegationPage.razor.cs @@ -43,17 +43,13 @@ public partial class DelegationPage : FluxorComponent private static Func ValidateQueryParameter => parameter => { - if (string.IsNullOrWhiteSpace(parameter)) - { - return "PHN is required"; - } - - if (!PhnValidator.Validate(StringManipulator.StripWhitespace(parameter)).IsValid) - { - return "Invalid PHN"; - } + string? phnValidationResult = !PhnValidator.Validate(StringManipulator.StripWhitespace(parameter)).IsValid + ? "Invalid PHN" + : null; - return null; + return string.IsNullOrWhiteSpace(parameter) + ? "PHN is required" + : phnValidationResult; }; [Inject] diff --git a/Apps/Admin/Client/Pages/LoginPage.razor.cs b/Apps/Admin/Client/Pages/LoginPage.razor.cs index d0559d13c0..50cbe960b1 100644 --- a/Apps/Admin/Client/Pages/LoginPage.razor.cs +++ b/Apps/Admin/Client/Pages/LoginPage.razor.cs @@ -16,6 +16,7 @@ namespace HealthGateway.Admin.Client.Pages; +using System; using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Components; @@ -52,7 +53,29 @@ protected override async Task OnInitializedAsync() AuthenticationState authState = await this.AuthenticationStateProvider.GetAuthenticationStateAsync(); if (authState.User.Identity is { IsAuthenticated: true }) { - this.NavigationManager.NavigateTo(this.ReturnPath ?? "/", replace: true); + // Validate ReturnPath to ensure it's a safe URL + string safeReturnPath = ValidateReturnPath(this.ReturnPath); + + // Perform the navigation + this.NavigationManager.NavigateTo(safeReturnPath, true); } } + + private static string ValidateReturnPath(string? returnPath) + { + // Ensure the return path is not null or whitespace + if (!string.IsNullOrWhiteSpace(returnPath) && + Uri.TryCreate(returnPath, UriKind.Relative, out Uri? validatedUri)) + { + // Normalize the path and ensure it starts with "/" + string normalizedPath = validatedUri.ToString(); + if (normalizedPath.StartsWith('/')) + { + return normalizedPath; + } + } + + // Fallback to the default safe path + return "/"; + } } diff --git a/Apps/Admin/Client/Pages/SupportPage.razor.cs b/Apps/Admin/Client/Pages/SupportPage.razor.cs index 16f41a755c..591c9a52be 100644 --- a/Apps/Admin/Client/Pages/SupportPage.razor.cs +++ b/Apps/Admin/Client/Pages/SupportPage.razor.cs @@ -18,6 +18,7 @@ namespace HealthGateway.Admin.Client.Pages using System; using System.Collections.Generic; using System.Collections.Immutable; + using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading.Tasks; using Fluxor; @@ -109,24 +110,8 @@ private PatientQueryType SelectedQueryType private Func ValidateQueryParameter => parameter => { - if (string.IsNullOrWhiteSpace(parameter)) - { - return "Search parameter is required"; - } - - if (this.PhnOrDependentSelected && !PhnValidator.Validate(StringManipulator.StripWhitespace(parameter)).IsValid) - { - return "Invalid PHN"; - } - - return this.SelectedQueryType switch - { - PatientQueryType.Email or PatientQueryType.Sms when StringManipulator.StripWhitespace(parameter).Length < 5 - => "Email/SMS must be minimum 5 characters", - PatientQueryType.Sms when !StringManipulator.IsPositiveNumeric(parameter) - => "SMS must contain digits only", - _ => null, - }; + string? result = ValidateParameterInput(parameter) ?? ValidatePhn(parameter, this.PhnOrDependentSelected); + return result ?? ValidateQueryType(parameter, this.SelectedQueryType); }; private AuthenticationState? AuthenticationState { get; set; } @@ -167,6 +152,31 @@ protected override async ValueTask DisposeAsyncCore(bool disposing) await base.DisposeAsyncCore(disposing); } + private static string? ValidateParameterInput(string parameter) + { + return string.IsNullOrWhiteSpace(parameter) ? "Search parameter is required" : null; + } + + private static string? ValidatePhn(string parameter, bool phnOrDependentSelected) + { + return phnOrDependentSelected && !PhnValidator.Validate(StringManipulator.StripWhitespace(parameter)).IsValid + ? "Invalid PHN" + : null; + } + + [SuppressMessage("Style", "IDE0072:Populate switch", Justification = "Team decision")] + private static string? ValidateQueryType(string parameter, PatientQueryType selectedQueryType) + { + return selectedQueryType switch + { + PatientQueryType.Email or PatientQueryType.Sms when StringManipulator.StripWhitespace(parameter).Length < 5 + => "Email/SMS must be minimum 5 characters", + PatientQueryType.Sms when !StringManipulator.IsPositiveNumeric(parameter) + => "SMS must contain digits only", + _ => null, + }; + } + private void Clear() { this.Dispatcher.Dispatch(new PatientSupportActions.ResetStateAction()); diff --git a/Apps/Admin/Client/Pages/UserInfoPage.razor.cs b/Apps/Admin/Client/Pages/UserInfoPage.razor.cs index f4addec4e8..afc89391e6 100644 --- a/Apps/Admin/Client/Pages/UserInfoPage.razor.cs +++ b/Apps/Admin/Client/Pages/UserInfoPage.razor.cs @@ -53,6 +53,7 @@ public partial class UserInfoPage : FluxorComponent /// protected override async Task OnInitializedAsync() { + await base.OnInitializedAsync(); await this.GetClaimsPrincipalDataAsync(); await this.CreateCookieAsync("HGAdmin", "dark mode", 365); } diff --git a/Apps/Admin/Client/Services/DateConversionService.cs b/Apps/Admin/Client/Services/DateConversionService.cs index e4079a4986..8ac1bec372 100644 --- a/Apps/Admin/Client/Services/DateConversionService.cs +++ b/Apps/Admin/Client/Services/DateConversionService.cs @@ -32,17 +32,8 @@ public DateTime ConvertFromUtc(DateTime utcDateTime) /// public DateTime? ConvertFromUtc(DateTime? utcDateTime, bool returnNowIfNull = false) { - if (utcDateTime != null) - { - return this.ConvertFromUtc(utcDateTime.Value); - } - - if (returnNowIfNull) - { - return this.ConvertFromUtc(DateTime.UtcNow); - } - - return null; + DateTime? dateTimeToConvert = GetDateTimeToConvert(utcDateTime, returnNowIfNull); + return dateTimeToConvert.HasValue ? this.ConvertFromUtc(dateTimeToConvert.Value) : null; } /// @@ -50,5 +41,10 @@ public string ConvertToShortFormatFromUtc(DateTime? utcDateTime, string fallback { return utcDateTime == null ? fallbackString : DateFormatter.ToShortDateAndTime(this.ConvertFromUtc(utcDateTime.Value)); } + + private static DateTime? GetDateTimeToConvert(DateTime? utcDateTime, bool returnNowIfNull) + { + return utcDateTime ?? (returnNowIfNull ? DateTime.UtcNow : null); + } } } diff --git a/Apps/Admin/Client/Utils/BreakpointUtility.cs b/Apps/Admin/Client/Utils/BreakpointUtility.cs index a5627a2728..b09cceea01 100644 --- a/Apps/Admin/Client/Utils/BreakpointUtility.cs +++ b/Apps/Admin/Client/Utils/BreakpointUtility.cs @@ -15,6 +15,7 @@ // ------------------------------------------------------------------------- namespace HealthGateway.Admin.Client.Utils { + using System.Diagnostics.CodeAnalysis; using MudBlazor; /// @@ -59,6 +60,7 @@ public static class BreakpointUtility /// A string containing the override code for the breakpoint or null if the breakpoint has no associated override /// code. /// + [SuppressMessage("Style", "IDE0072:Populate switch", Justification = "Team decision")] public static string? GetOverrideCode(this Breakpoint breakpoint) { return breakpoint switch diff --git a/Apps/Admin/Client/Utils/FormattingUtility.cs b/Apps/Admin/Client/Utils/FormattingUtility.cs index a9a322a351..0a03ef852d 100644 --- a/Apps/Admin/Client/Utils/FormattingUtility.cs +++ b/Apps/Admin/Client/Utils/FormattingUtility.cs @@ -15,6 +15,7 @@ // ------------------------------------------------------------------------- namespace HealthGateway.Admin.Client.Utils { + using System.Diagnostics.CodeAnalysis; using HealthGateway.Admin.Common.Constants; using HealthGateway.Common.Data.Constants; using HealthGateway.Common.Data.Models; @@ -55,6 +56,7 @@ public static string FormatBroadcastEnabled(bool enabled) /// /// The communication status to format. /// A string formatted for display. + [SuppressMessage("Style", "IDE0072:Populate switch", Justification = "Team decision")] public static string FormatCommunicationStatus(CommunicationStatus status) { return status switch @@ -69,6 +71,7 @@ public static string FormatCommunicationStatus(CommunicationStatus status) /// /// The communication type to format. /// A string formatted for display. + [SuppressMessage("Style", "IDE0072:Populate switch", Justification = "Team decision")] public static string FormatCommunicationType(CommunicationType? communicationType) { return communicationType switch @@ -85,6 +88,7 @@ public static string FormatCommunicationType(CommunicationType? communicationTyp /// /// The data source to format. /// A string formatted for display. + [SuppressMessage("Style", "IDE0072:Populate switch", Justification = "Team decision")] public static string FormatDataSource(DataSource dataSource) { return dataSource switch @@ -110,6 +114,7 @@ public static string FormatDataSource(DataSource dataSource) /// /// The identity provider to format. /// A string formatted for display. + [SuppressMessage("Style", "IDE0072:Populate switch", Justification = "Team decision")] public static string FormatKeycloakIdentityProvider(KeycloakIdentityProvider identityProvider) { return identityProvider switch @@ -125,6 +130,7 @@ public static string FormatKeycloakIdentityProvider(KeycloakIdentityProvider ide /// /// The patient status to format. /// A string formatted for display. + [SuppressMessage("Style", "IDE0072:Populate switch", Justification = "Team decision")] public static string FormatPatientStatus(PatientStatus status) { return status switch @@ -141,6 +147,7 @@ public static string FormatPatientStatus(PatientStatus status) /// /// The query type to format. /// A string formatted for display. + [SuppressMessage("Style", "IDE0072:Populate switch", Justification = "Team decision")] public static string FormatPatientQueryType(PatientQueryType queryType) { return queryType switch diff --git a/Apps/Admin/Client/Utils/StoreUtility.cs b/Apps/Admin/Client/Utils/StoreUtility.cs index f5f37c95f1..79fa13c6fb 100644 --- a/Apps/Admin/Client/Utils/StoreUtility.cs +++ b/Apps/Admin/Client/Utils/StoreUtility.cs @@ -48,32 +48,7 @@ public static RequestError FormatRequestError(RequestResultError? resultError) /// The generated . public static RequestError FormatRequestError(Exception? exception, RequestResultError? resultError = null) { - if (resultError is not null) - { - return new() - { - Message = resultError.ResultMessage, - Details = new() - { - { "errorCode", resultError.ErrorCode }, - { "traceId", resultError.TraceId }, - { "actionCode", resultError.ActionCodeValue ?? string.Empty }, - }, - }; - } - - if (exception is not null) - { - return new() - { - Message = exception.Message, - }; - } - - return new() - { - Message = "Unknown error", - }; + return resultError is not null ? CreateRequestError(resultError) : CreateRequestError(exception); } /// @@ -100,5 +75,27 @@ public static async Task LoadPatientSupportAction( await SessionUtility.SetSessionStorageItem(jsRuntime, SessionUtility.SupportQueryType, patientQueryType.ToString()); await SessionUtility.SetSessionStorageItem(jsRuntime, SessionUtility.SupportQueryString, patientQueryString); } + + private static RequestError CreateRequestError(RequestResultError resultError) + { + return new() + { + Message = resultError.ResultMessage, + Details = new() + { + { "errorCode", resultError.ErrorCode }, + { "traceId", resultError.TraceId }, + { "actionCode", resultError.ActionCodeValue ?? string.Empty }, + }, + }; + } + + private static RequestError CreateRequestError(Exception? exception) + { + return new() + { + Message = exception?.Message ?? "Unknown error", + }; + } } } diff --git a/Apps/Admin/Server/Admin.Server.csproj b/Apps/Admin/Server/Admin.Server.csproj index 62a2c32056..57733962bd 100644 --- a/Apps/Admin/Server/Admin.Server.csproj +++ b/Apps/Admin/Server/Admin.Server.csproj @@ -9,7 +9,7 @@ - + diff --git a/Apps/Admin/Server/Controllers/SupportController.cs b/Apps/Admin/Server/Controllers/SupportController.cs index 72ddff20e3..6507f105c8 100644 --- a/Apps/Admin/Server/Controllers/SupportController.cs +++ b/Apps/Admin/Server/Controllers/SupportController.cs @@ -16,6 +16,7 @@ namespace HealthGateway.Admin.Server.Controllers { using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; using System.Security.Claims; using System.Threading; using System.Threading.Tasks; @@ -41,6 +42,7 @@ namespace HealthGateway.Admin.Server.Controllers [Route("v{version:apiVersion}/api/[controller]")] [Produces("application/json")] [Authorize(Roles = "AdminUser,AdminReviewer,SupportUser")] + [SuppressMessage("Major Code Smell", "S6960:This controller has multiple responsibilities and could be split into 2 smaller controllers", Justification = "Team decision")] public class SupportController(ICovidSupportService covidSupportService, ISupportService supportService) : ControllerBase { /// diff --git a/Apps/Admin/Server/Delegates/RestImmunizationAdminDelegate.cs b/Apps/Admin/Server/Delegates/RestImmunizationAdminDelegate.cs index a730eed8bf..8f8571b470 100644 --- a/Apps/Admin/Server/Delegates/RestImmunizationAdminDelegate.cs +++ b/Apps/Admin/Server/Delegates/RestImmunizationAdminDelegate.cs @@ -17,6 +17,7 @@ namespace HealthGateway.Admin.Server.Delegates { using System; using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -47,6 +48,7 @@ public class RestImmunizationAdminDelegate( private static ActivitySource ActivitySource { get; } = new(typeof(RestImmunizationAdminDelegate).FullName); /// + [SuppressMessage("Style", "IDE0046:Simplify 'if' statement", Justification = "Team decision")] public async Task GetVaccineDetailsWithRetriesAsync(string phn, string accessToken, bool refresh = false, CancellationToken ct = default) { using Activity? activity = ActivitySource.StartActivity(); diff --git a/Apps/Admin/Server/Delegates/RestVaccineStatusDelegate.cs b/Apps/Admin/Server/Delegates/RestVaccineStatusDelegate.cs index fe9bce50c4..1fc9d4699f 100644 --- a/Apps/Admin/Server/Delegates/RestVaccineStatusDelegate.cs +++ b/Apps/Admin/Server/Delegates/RestVaccineStatusDelegate.cs @@ -17,6 +17,7 @@ namespace HealthGateway.Admin.Server.Delegates { using System; using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; using HealthGateway.Admin.Server.Api; @@ -42,6 +43,7 @@ public class RestVaccineStatusDelegate( private static ActivitySource ActivitySource { get; } = new(typeof(RestVaccineStatusDelegate).FullName); /// + [SuppressMessage("Style", "IDE0046:Simplify 'if' statement", Justification = "Team decision")] public async Task> GetVaccineStatusWithRetriesAsync(string phn, DateTime dateOfBirth, string accessToken, CancellationToken ct = default) { using Activity? activity = ActivitySource.StartActivity(); diff --git a/Apps/Admin/Server/MapProfiles/BetaFeatureProfile.cs b/Apps/Admin/Server/MapProfiles/BetaFeatureProfile.cs index 7846c95273..495a23902a 100644 --- a/Apps/Admin/Server/MapProfiles/BetaFeatureProfile.cs +++ b/Apps/Admin/Server/MapProfiles/BetaFeatureProfile.cs @@ -16,12 +16,14 @@ namespace HealthGateway.Admin.Server.MapProfiles { using System; + using System.Diagnostics.CodeAnalysis; using AutoMapper; using HealthGateway.Admin.Common.Constants; /// /// An AutoMapper profile class which defines mapping between DB and UI Models. /// + [SuppressMessage("Style", "IDE0072:Populate switch", Justification = "Team decision")] public class BetaFeatureProfile : Profile { /// diff --git a/Apps/Admin/Server/Services/AdminReportService.cs b/Apps/Admin/Server/Services/AdminReportService.cs index bfb65f6fb2..5557b89437 100644 --- a/Apps/Admin/Server/Services/AdminReportService.cs +++ b/Apps/Admin/Server/Services/AdminReportService.cs @@ -67,7 +67,7 @@ public async Task> GetBlockedAccessReportAsync( IList records = await blockedAccessDelegate.GetAllAsync(ct); return records .Where(r => r.DataSources.Count > 0) - .Select(r => new BlockedAccessRecord(r.Hdid, r.DataSources.ToList())); + .Select(r => new BlockedAccessRecord(r.Hdid, [.. r.DataSources])); } } } diff --git a/Apps/Admin/Server/Services/CovidSupportService.cs b/Apps/Admin/Server/Services/CovidSupportService.cs index af1d612b26..8805488698 100644 --- a/Apps/Admin/Server/Services/CovidSupportService.cs +++ b/Apps/Admin/Server/Services/CovidSupportService.cs @@ -16,6 +16,7 @@ namespace HealthGateway.Admin.Server.Services { using System; + using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; using HealthGateway.AccountDataAccess.Patient; @@ -89,15 +90,10 @@ private async Task GetPatientAsync(string phn, CancellationToken c private async Task GetVaccineStatusResultAsync(string phn, DateTime birthdate, string accessToken, CancellationToken ct) { PhsaResult phsaResult = await vaccineStatusDelegate.GetVaccineStatusWithRetriesAsync(phn, birthdate, accessToken, ct); - - if (phsaResult.Result == null) - { - throw new NotFoundException(ErrorMessages.CannotGetVaccineStatus); - } - - return phsaResult.Result; + return phsaResult.Result ?? throw new NotFoundException(ErrorMessages.CannotGetVaccineStatus); } + [SuppressMessage("Style", "IDE0046:Simplify 'if' statement", Justification = "Team decision")] private VaccinationStatus GetVaccinationStatus(VaccineStatusResult result) { logger.LogDebug("Vaccination Status Indicator: {VaccinationStatusIndicator}", result.StatusIndicator); @@ -118,6 +114,7 @@ VaccineState.NotFound or VaccineState.DataMismatch or VaccineState.Threshold or }; } + [SuppressMessage("Style", "IDE0046:Simplify 'if' statement", Justification = "Team decision")] private async Task GetVaccineProofAsync(VaccinationStatus vaccinationStatus, string qrCode, CancellationToken ct) { VaccineProofRequest request = new() @@ -127,6 +124,7 @@ private async Task GetVaccineProofAsync(VaccinationStatus }; RequestResult vaccineProof = await vaccineProofDelegate.GenerateAsync(this.vaccineCardConfig.PrintTemplate, request, ct); + if (vaccineProof.ResultStatus != ResultType.Success || vaccineProof.ResourcePayload == null) { throw new NotFoundException(vaccineProof.ResultError?.ResultMessage ?? ErrorMessages.CannotGetVaccineProof); diff --git a/Apps/Admin/Server/Services/DelegationService.cs b/Apps/Admin/Server/Services/DelegationService.cs index 6fecc38713..510c480b7b 100644 --- a/Apps/Admin/Server/Services/DelegationService.cs +++ b/Apps/Admin/Server/Services/DelegationService.cs @@ -17,6 +17,7 @@ namespace HealthGateway.Admin.Server.Services { using System; using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -211,13 +212,7 @@ public async Task ProtectDependentAsync(string dependentHdid, IEnum public async Task UnprotectDependentAsync(string dependentHdid, string reason, CancellationToken ct = default) { string authenticatedUserId = authenticationDelegate.FetchAuthenticatedUserId() ?? UserId.DefaultUser; - Dependent? dependent = await delegationDelegate.GetDependentAsync(dependentHdid, true, ct); - - if (dependent == null) - { - throw new NotFoundException($"Dependent not found for hdid: {dependentHdid}"); - } - + Dependent dependent = await delegationDelegate.GetDependentAsync(dependentHdid, true, ct) ?? throw new NotFoundException($"Dependent not found for hdid: {dependentHdid}"); dependent.Protected = false; dependent.UpdatedBy = authenticatedUserId; dependent.AllowedDelegations = []; @@ -253,6 +248,7 @@ public async Task UnprotectDependentAsync(string dependentHdid, str return mappingService.MapToAgentAction(agentAudit); } + [SuppressMessage("Style", "IDE0010:Populate switch", Justification = "Team decision")] private static void ValidatePatientResult(RequestResult patientResult) { switch (patientResult) diff --git a/Apps/Admin/Server/Services/InactiveUserService.cs b/Apps/Admin/Server/Services/InactiveUserService.cs index 7b8d872fe2..3e4e5e6cc6 100644 --- a/Apps/Admin/Server/Services/InactiveUserService.cs +++ b/Apps/Admin/Server/Services/InactiveUserService.cs @@ -158,19 +158,17 @@ private void PopulateUserDetails(List inactiveUsers, List< private void AddInactiveUser( List inactiveUsers, IEnumerable activeUserProfiles, - ICollection identityAccessUsers, + List identityAccessUsers, IdentityAccessRole role) { this.logger.LogDebug("Keycloak {Role} count: {Count}...", role, identityAccessUsers.Count); // Filter identities from keycloak where inactive (database) user's username does not match keycloak user's username // and active (database) user's username does not match keycloak users username - IEnumerable adminUserProfiles = identityAccessUsers - .Where(x1 => inactiveUsers.TrueForAll(x2 => x1.Username != x2.Username) && activeUserProfiles.All(x2 => x1.Username != x2.Username)) - .Select(user => this.mappingService.MapToAdminUserProfileView(user)); - - foreach (AdminUserProfileView adminUserProfile in adminUserProfiles) + foreach (UserRepresentation user in identityAccessUsers + .Where(x1 => inactiveUsers.TrueForAll(x2 => x1.Username != x2.Username) && activeUserProfiles.All(x2 => x1.Username != x2.Username))) { + AdminUserProfileView adminUserProfile = this.mappingService.MapToAdminUserProfileView(user); adminUserProfile.RealmRoles = role.ToString(); inactiveUsers.Add(adminUserProfile); } diff --git a/Apps/Admin/Server/Services/SupportService.cs b/Apps/Admin/Server/Services/SupportService.cs index 5116da1ef7..a3532eb19b 100644 --- a/Apps/Admin/Server/Services/SupportService.cs +++ b/Apps/Admin/Server/Services/SupportService.cs @@ -17,6 +17,7 @@ namespace HealthGateway.Admin.Server.Services { using System; using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Threading; @@ -72,16 +73,11 @@ public async Task GetPatientSupportDetailsAsync( PatientSupportDetailsQuery query, CancellationToken ct = default) { - PatientModel patient; - - if (query.QueryType == ClientRegistryType.Hdid) - { - patient = await this.GetPatientAsync(new(Hdid: query.QueryParameter, Source: PatientDetailSource.All, UseCache: false), ct); - } - else - { - patient = await this.GetPatientAsync(new(query.QueryParameter, Source: PatientDetailSource.Empi, UseCache: false), ct); - } + PatientModel patient = await this.GetPatientAsync( + query.QueryType == ClientRegistryType.Hdid + ? new(Hdid: query.QueryParameter, Source: PatientDetailSource.All, UseCache: false) + : new(query.QueryParameter, Source: PatientDetailSource.Empi, UseCache: false), + ct); Task? getVaccineDetails = query.IncludeCovidDetails ? this.GetVaccineDetailsAsync(patient, query.RefreshVaccineDetails, ct) : null; @@ -109,6 +105,7 @@ public async Task GetPatientSupportDetailsAsync( } /// + [SuppressMessage("Style", "IDE0072:Populate switch", Justification = "Team decision")] public async Task> GetPatientsAsync(PatientQueryType queryType, string queryString, CancellationToken ct = default) { if (queryType is PatientQueryType.Hdid or PatientQueryType.Phn) @@ -206,6 +203,7 @@ private async Task> GetAllDependentInfo return dependentInfo; } + [SuppressMessage("Style", "IDE0046:'if' statement can be simplified", Justification = "Team decision")] private async Task GetVaccineDetailsAsync(PatientModel patient, bool refresh, CancellationToken ct) { if (string.IsNullOrEmpty(patient.Phn) || patient.Birthdate == DateTime.MinValue) @@ -233,12 +231,9 @@ private async Task GetAccessTokenAsync(CancellationToken ct) ? null : await userProfileDelegate.GetUserProfileAsync(hdid, ct: ct); - if (patient == null && profile == null) - { - return null; - } - - return mappingService.MapToPatientSupportResult(patient, profile); + return patient == null && profile == null + ? null + : mappingService.MapToPatientSupportResult(patient, profile); } private async Task GetPatientSupportResultAsync(UserProfile profile, CancellationToken ct) diff --git a/Apps/Admin/Server/Services/UserFeedbackService.cs b/Apps/Admin/Server/Services/UserFeedbackService.cs index 0dd46e44f6..715bc5b134 100644 --- a/Apps/Admin/Server/Services/UserFeedbackService.cs +++ b/Apps/Admin/Server/Services/UserFeedbackService.cs @@ -103,7 +103,7 @@ public async Task>> GetAllTagsAsync(Cancellati { IEnumerable adminTags = await adminTagDelegate.GetAllAsync(ct); - IList adminTagViews = adminTags.Select(mappingService.MapToAdminTagView).ToList(); + List adminTagViews = adminTags.Select(mappingService.MapToAdminTagView).ToList(); return new RequestResult> { diff --git a/Apps/Admin/Tests/Admin.Tests.csproj b/Apps/Admin/Tests/Admin.Tests.csproj index 55b4dde9fc..a62430c7ad 100644 --- a/Apps/Admin/Tests/Admin.Tests.csproj +++ b/Apps/Admin/Tests/Admin.Tests.csproj @@ -10,13 +10,13 @@ - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/Apps/Admin/Tests/Delegates/RestImmunizationAdminDelegateTests.cs b/Apps/Admin/Tests/Delegates/RestImmunizationAdminDelegateTests.cs index f038f49062..72f1f7b2ba 100644 --- a/Apps/Admin/Tests/Delegates/RestImmunizationAdminDelegateTests.cs +++ b/Apps/Admin/Tests/Delegates/RestImmunizationAdminDelegateTests.cs @@ -17,7 +17,6 @@ namespace HealthGateway.Admin.Tests.Delegates { using System; using System.Collections.Generic; - using System.Linq; using System.Threading; using System.Threading.Tasks; using HealthGateway.Admin.Common.Models.CovidSupport; @@ -190,7 +189,7 @@ private static IConfigurationRoot GetIConfigurationRoot() }; return new ConfigurationBuilder() - .AddInMemoryCollection(myConfiguration.ToList()) + .AddInMemoryCollection(myConfiguration) .Build(); } } diff --git a/Apps/Admin/Tests/Delegates/RestVaccineStatusDelegateTests.cs b/Apps/Admin/Tests/Delegates/RestVaccineStatusDelegateTests.cs index 44b99821a8..3650b33e1d 100644 --- a/Apps/Admin/Tests/Delegates/RestVaccineStatusDelegateTests.cs +++ b/Apps/Admin/Tests/Delegates/RestVaccineStatusDelegateTests.cs @@ -18,7 +18,6 @@ namespace HealthGateway.Admin.Tests.Delegates using System; using System.Collections.Generic; using System.Globalization; - using System.Linq; using System.Threading; using System.Threading.Tasks; using HealthGateway.Admin.Server.Api; @@ -151,7 +150,7 @@ private static IConfigurationRoot GetIConfigurationRoot() }; return new ConfigurationBuilder() - .AddInMemoryCollection(myConfiguration.ToList()) + .AddInMemoryCollection(myConfiguration) .Build(); } } diff --git a/Apps/Admin/Tests/Functional/cypress/integration/ui/analytics.cy.js b/Apps/Admin/Tests/Functional/cypress/integration/e2e/analytics-read.cy.js similarity index 100% rename from Apps/Admin/Tests/Functional/cypress/integration/ui/analytics.cy.js rename to Apps/Admin/Tests/Functional/cypress/integration/e2e/analytics-read.cy.js diff --git a/Apps/Admin/Tests/Functional/cypress/integration/e2e/beta-access-read.cy.js b/Apps/Admin/Tests/Functional/cypress/integration/e2e/beta-access-read.cy.js index 814e218fe0..5464447dbe 100644 --- a/Apps/Admin/Tests/Functional/cypress/integration/e2e/beta-access-read.cy.js +++ b/Apps/Admin/Tests/Functional/cypress/integration/e2e/beta-access-read.cy.js @@ -9,18 +9,10 @@ function setupGetUserAccessAlias() { cy.intercept("GET", "**/BetaFeature/UserAccess*").as("getUserAccess"); } -function setupPutUserAccessAlias() { - cy.intercept("PUT", "**/UserAccess").as("putUserAccess"); -} - function waitForGetUserAccess() { cy.wait("@getUserAccess", { timeout: defaultTimeout }); } -function waitForPutUserAccess() { - cy.wait("@putUserAccess", { timeout: defaultTimeout }); -} - describe("Beta feature access", () => { beforeEach(() => { cy.login( diff --git a/Apps/Admin/Tests/Functional/cypress/integration/e2e/delegation-read.cy.js b/Apps/Admin/Tests/Functional/cypress/integration/e2e/delegation-read.cy.js index d173d31c0a..9b5d7ecf7b 100644 --- a/Apps/Admin/Tests/Functional/cypress/integration/e2e/delegation-read.cy.js +++ b/Apps/Admin/Tests/Functional/cypress/integration/e2e/delegation-read.cy.js @@ -5,7 +5,7 @@ const dependentExceedingAgeCutoff = { phn: "9735353315" }; const defaultTimeout = 60000; function performSearch(phn) { - cy.intercept("GET", "**/Delegation/").as("getDelegation"); + cy.intercept("GET", "**/Delegation*").as("getDelegation"); cy.get("[data-testid=query-input]").clear().type(phn); cy.get("[data-testid=search-button]").click(); cy.wait("@getDelegation", { timeout: defaultTimeout }); @@ -31,10 +31,17 @@ describe("Delegation Search", () => { getTableRows("[data-testid=dependent-table]") .should("have.length", 1) .within((_$rows) => { - cy.get("[data-testid=dependent-name]").should("not.be.empty"); - cy.get("[data-testid=dependent-dob]").should("not.be.empty"); + cy.get("[data-testid=dependent-name]").should( + "not.have.text", + "" + ); + cy.get("[data-testid=dependent-dob]").should( + "not.have.text", + "" + ); cy.get("[data-testid=dependent-address]").should( - "not.be.empty" + "not.have.text", + "" ); cy.get("[data-testid=dependent-protected-switch]").should( "not.be.checked" @@ -50,10 +57,17 @@ describe("Delegation Search", () => { getTableRows("[data-testid=dependent-table]") .should("have.length", 1) .within((_$rows) => { - cy.get("[data-testid=dependent-name]").should("not.be.empty"); - cy.get("[data-testid=dependent-dob]").should("not.be.empty"); + cy.get("[data-testid=dependent-name]").should( + "not.have.text", + "" + ); + cy.get("[data-testid=dependent-dob]").should( + "not.have.text", + "" + ); cy.get("[data-testid=dependent-address]").should( - "not.be.empty" + "not.have.text", + "" ); cy.get("[data-testid=dependent-protected-switch]").should( "not.be.checked" @@ -67,16 +81,19 @@ describe("Delegation Search", () => { .eq(0) .within((_$row) => { cy.get("[data-testid=delegate-name]").should( - "not.be.empty" + "not.have.text", + "" ); cy.get("[data-testid=delegate-phn]").contains( dependentWithGuardian.guardianPhn ); cy.get("[data-testid=delegate-dob]").should( - "not.be.empty" + "not.have.text", + "" ); cy.get("[data-testid=delegate-address]").should( - "not.be.empty" + "not.have.text", + "" ); cy.get("[data-testid=delegate-status]").contains( "Added" diff --git a/Apps/Admin/Tests/Functional/cypress/integration/e2e/delegation-write.cy.js b/Apps/Admin/Tests/Functional/cypress/integration/e2e/delegation-write.cy.js index 75d690f547..58acd828a0 100644 --- a/Apps/Admin/Tests/Functional/cypress/integration/e2e/delegation-write.cy.js +++ b/Apps/Admin/Tests/Functional/cypress/integration/e2e/delegation-write.cy.js @@ -62,7 +62,10 @@ describe("Delegation Protect", () => { cy.get("[data-testid=save-button]").click(); // Protect reason input is empty - cy.get("[data-testid=audit-reason-input]").should("be.empty"); + cy.get("[data-testid=audit-reason-input]") + .should("exist") + .should("be.visible") + .should("have.value", ""); // Cancel confirmation dialog cy.get("[data-testid=audit-cancel-button]").click(); diff --git a/Apps/Admin/Tests/Functional/cypress/integration/e2e/patient-details-write.cy.js b/Apps/Admin/Tests/Functional/cypress/integration/e2e/patient-details-write.cy.js index d881be8356..06b582b887 100644 --- a/Apps/Admin/Tests/Functional/cypress/integration/e2e/patient-details-write.cy.js +++ b/Apps/Admin/Tests/Functional/cypress/integration/e2e/patient-details-write.cy.js @@ -204,7 +204,8 @@ describe("Patient details page as admin user", () => { cy.get("[data-testid=dependent-dob]").contains("2005-01-01"); cy.get("[data-testid=dependent-phn]").contains("9874307215"); cy.get("[data-testid=dependent-address]").should( - "not.be.empty" + "not.have.text", + "" ); cy.get("[data-testid=dependent-expiry-date]") .invoke("text") diff --git a/Apps/Admin/Tests/Functional/cypress/integration/ui/support.cy.js b/Apps/Admin/Tests/Functional/cypress/integration/ui/support.cy.js index cd79fe984c..efd73a61f4 100644 --- a/Apps/Admin/Tests/Functional/cypress/integration/ui/support.cy.js +++ b/Apps/Admin/Tests/Functional/cypress/integration/ui/support.cy.js @@ -171,7 +171,10 @@ describe("Support", () => { verifySupportTableResults(hdid, phn, 2); cy.get("[data-testid=clear-btn]").click(); cy.get("[data-testid=query-type-select]").should("have.value", "Sms"); - cy.get("[data-testid=query-input]").should("be.empty"); + cy.get("[data-testid=query-input]") + .should("exist") + .should("be.visible") + .should("have.value", ""); cy.get("[data-testid=user-table]").should("not.exist"); }); }); diff --git a/Apps/Admin/Tests/Functional/cypress/support/commands.js b/Apps/Admin/Tests/Functional/cypress/support/commands.js index 824ff23424..263848a228 100644 --- a/Apps/Admin/Tests/Functional/cypress/support/commands.js +++ b/Apps/Admin/Tests/Functional/cypress/support/commands.js @@ -112,6 +112,8 @@ Cypress.Commands.add("login", (username, password, path) => { cy.log(`Visiting ${path}`); setupStandardAliases(path); + cy.disableServiceWorker(); + cy.visit(path, { timeout: 60000 }); waitForInitialDataLoad(path); @@ -145,3 +147,16 @@ Cypress.Commands.add("validateTableLoad", (tableSelector) => { .find(".mud-table-loading-progress") .should("not.exist"); }); + +Cypress.Commands.add("disableServiceWorker", () => { + cy.log("Unregistering ServiceWorker."); + cy.window().then((win) => { + if ("serviceWorker" in navigator) { + navigator.serviceWorker.getRegistrations().then((registrations) => { + registrations.forEach((registration) => { + registration.unregister(); + }); + }); + } + }); +}); diff --git a/Apps/Admin/Tests/Services/AdminReportServiceTests.cs b/Apps/Admin/Tests/Services/AdminReportServiceTests.cs index e936245072..4a3b633196 100644 --- a/Apps/Admin/Tests/Services/AdminReportServiceTests.cs +++ b/Apps/Admin/Tests/Services/AdminReportServiceTests.cs @@ -132,9 +132,9 @@ private static IAdminReportService GetAdminReportService( Mock? blockedAccessDelegateMock = null, Mock? patientRepositoryMock = null) { - delegationDelegateMock = delegationDelegateMock ?? new(); - blockedAccessDelegateMock = blockedAccessDelegateMock ?? new(); - patientRepositoryMock = patientRepositoryMock ?? new(); + delegationDelegateMock ??= new(); + blockedAccessDelegateMock ??= new(); + patientRepositoryMock ??= new(); return new AdminReportService( delegationDelegateMock.Object, diff --git a/Apps/Admin/Tests/Services/AgentAccessServiceTests.cs b/Apps/Admin/Tests/Services/AgentAccessServiceTests.cs index a2932a574e..fb26107b29 100644 --- a/Apps/Admin/Tests/Services/AgentAccessServiceTests.cs +++ b/Apps/Admin/Tests/Services/AgentAccessServiceTests.cs @@ -236,8 +236,8 @@ private static IAgentAccessService GetAgentAccessService( Mock? authenticationDelegateMock = null, Mock? keycloakAdminApiMock = null) { - authenticationDelegateMock = authenticationDelegateMock ?? new(); - keycloakAdminApiMock = keycloakAdminApiMock ?? new(); + authenticationDelegateMock ??= new(); + keycloakAdminApiMock ??= new(); return new AgentAccessService( authenticationDelegateMock.Object, diff --git a/Apps/Admin/Tests/Services/BetaFeatureServiceTests.cs b/Apps/Admin/Tests/Services/BetaFeatureServiceTests.cs index 0a046aec2f..2bb53fa52f 100644 --- a/Apps/Admin/Tests/Services/BetaFeatureServiceTests.cs +++ b/Apps/Admin/Tests/Services/BetaFeatureServiceTests.cs @@ -285,7 +285,7 @@ private static IConfigurationRoot GetIConfigurationRoot() }; return new ConfigurationBuilder() - .AddInMemoryCollection(myConfiguration.ToList()) + .AddInMemoryCollection(myConfiguration) .Build(); } @@ -358,8 +358,8 @@ private static IBetaFeatureService GetBetaFeatureService( Mock? profileDelegateMock = null, Mock? betaFeatureAccessDelegateMock = null) { - profileDelegateMock = profileDelegateMock ?? new(); - betaFeatureAccessDelegateMock = betaFeatureAccessDelegateMock ?? new(); + profileDelegateMock ??= new(); + betaFeatureAccessDelegateMock ??= new(); return new BetaFeatureService( profileDelegateMock.Object, diff --git a/Apps/Admin/Tests/Services/CommunicationServiceTests.cs b/Apps/Admin/Tests/Services/CommunicationServiceTests.cs index e2abb9d618..673280fe77 100644 --- a/Apps/Admin/Tests/Services/CommunicationServiceTests.cs +++ b/Apps/Admin/Tests/Services/CommunicationServiceTests.cs @@ -17,7 +17,6 @@ namespace HealthGateway.Admin.Tests.Services { using System; using System.Collections.Generic; - using System.Linq; using System.Threading; using System.Threading.Tasks; using DeepEqual.Syntax; @@ -306,7 +305,7 @@ private static IConfigurationRoot GetIConfigurationRoot() }; return new ConfigurationBuilder() - .AddInMemoryCollection(myConfiguration.ToList()) + .AddInMemoryCollection(myConfiguration) .Build(); } } diff --git a/Apps/Admin/Tests/Services/ConfigurationServiceTests.cs b/Apps/Admin/Tests/Services/ConfigurationServiceTests.cs index 8908934415..cfbe7410ae 100644 --- a/Apps/Admin/Tests/Services/ConfigurationServiceTests.cs +++ b/Apps/Admin/Tests/Services/ConfigurationServiceTests.cs @@ -16,7 +16,6 @@ namespace HealthGateway.Admin.Tests.Services { using System.Collections.Generic; - using System.Linq; using HealthGateway.Admin.Common.Models; using HealthGateway.Admin.Server.Services; using Microsoft.Extensions.Configuration; @@ -61,7 +60,7 @@ private static IConfigurationRoot GetIConfigurationRoot() }; return new ConfigurationBuilder() - .AddInMemoryCollection(myConfiguration.ToList()) + .AddInMemoryCollection(myConfiguration) .Build(); } } diff --git a/Apps/Admin/Tests/Services/CovidSupportServiceTests.cs b/Apps/Admin/Tests/Services/CovidSupportServiceTests.cs index 4a7a48ba36..8291367e69 100644 --- a/Apps/Admin/Tests/Services/CovidSupportServiceTests.cs +++ b/Apps/Admin/Tests/Services/CovidSupportServiceTests.cs @@ -18,7 +18,6 @@ namespace HealthGateway.Admin.Tests.Services using System; using System.Collections.Generic; using System.Globalization; - using System.Linq; using System.Threading; using System.Threading.Tasks; using HealthGateway.AccountDataAccess.Patient; @@ -553,7 +552,7 @@ private static MailDocumentRequest GenerateMailDocumentRequest() PersonalHealthNumber = Phn, MailAddress = new() { - StreetLines = new[] { "9105 ROTTERDAM PLACE" }, + StreetLines = ["9105 ROTTERDAM PLACE"], City = "VANCOUVER", Country = string.Empty, PostalCode = "V3X 4J5", @@ -659,7 +658,7 @@ private static IConfigurationRoot GetIConfigurationRoot() }; return new ConfigurationBuilder() - .AddInMemoryCollection(myConfiguration.ToList()) + .AddInMemoryCollection(myConfiguration) .Build(); } } diff --git a/Apps/Admin/Tests/Services/CsvExportServiceTests.cs b/Apps/Admin/Tests/Services/CsvExportServiceTests.cs index 5024f0898f..bce1209951 100644 --- a/Apps/Admin/Tests/Services/CsvExportServiceTests.cs +++ b/Apps/Admin/Tests/Services/CsvExportServiceTests.cs @@ -19,7 +19,6 @@ namespace HealthGateway.Admin.Tests.Services using System; using System.Collections.Generic; using System.IO; - using System.Linq; using System.Threading; using System.Threading.Tasks; using DeepEqual.Syntax; @@ -354,7 +353,7 @@ private static IConfigurationRoot GetIConfigurationRoot() }; return new ConfigurationBuilder() - .AddInMemoryCollection(myConfiguration.ToList()) + .AddInMemoryCollection(myConfiguration) .Build(); } @@ -366,12 +365,12 @@ private static ICsvExportService GetCsvExportService( Mock? inactiveUserServiceMock = null, Mock? feedbackDelegateMock = null) { - profileDelegateMock = profileDelegateMock ?? new(); - commentDelegateMock = commentDelegateMock ?? new(); - noteDelegateMock = noteDelegateMock ?? new(); - ratingDelegateMock = ratingDelegateMock ?? new(); - inactiveUserServiceMock = inactiveUserServiceMock ?? new(); - feedbackDelegateMock = feedbackDelegateMock ?? new(); + profileDelegateMock ??= new(); + commentDelegateMock ??= new(); + noteDelegateMock ??= new(); + ratingDelegateMock ??= new(); + inactiveUserServiceMock ??= new(); + feedbackDelegateMock ??= new(); return new CsvExportService( Configuration, diff --git a/Apps/Admin/Tests/Services/DashboardServiceTests.cs b/Apps/Admin/Tests/Services/DashboardServiceTests.cs index 3e437db5ac..8e20a3ff6c 100644 --- a/Apps/Admin/Tests/Services/DashboardServiceTests.cs +++ b/Apps/Admin/Tests/Services/DashboardServiceTests.cs @@ -17,7 +17,6 @@ namespace HealthGateway.Admin.Tests.Services { using System; using System.Collections.Generic; - using System.Linq; using System.Threading; using System.Threading.Tasks; using DeepEqual.Syntax; @@ -55,7 +54,7 @@ public async Task ShouldGetAllTimeCounts() Dependents = dependentCount, }; - IDashboardService service = SetupGetAllTimeCountsMock(userProfileCount, closedUserProfileCount, dependentCount); + IDashboardService service = SetupDashboardServiceForAllTimeCounts(userProfileCount, closedUserProfileCount, dependentCount); // Act AllTimeCounts actual = await service.GetAllTimeCountsAsync(); @@ -104,7 +103,7 @@ public async Task ShouldGetDailyUsageCounts() DependentRegistrations = new SortedDictionary(dependentRegistrationCounts), }; - IDashboardService service = SetupDailyUsageCountsMock(userRegistrationCounts, userLoginCounts, dependentRegistrationCounts); + IDashboardService service = SetupDashboardServiceForDailyUsageCounts(userRegistrationCounts, userLoginCounts, dependentRegistrationCounts); // Act DailyUsageCounts actual = await service.GetDailyUsageCountsAsync(startDate, endDate); @@ -128,7 +127,7 @@ public async Task ShouldGetRecurringUserCount() const int userCount = 10; const int expected = 10; - IDashboardService service = SetupGetRecurringUserCountMock(dayCount, userCount); + IDashboardService service = SetupDashboardServiceForRecurringUserCount(dayCount, userCount); // Act int actual = await service.GetRecurringUserCountAsync(dayCount, startDate, endDate); @@ -156,7 +155,7 @@ public async Task ShouldGetAppLoginCounts() AppLoginCounts expected = new(webCount, mobileCount + androidCount + iosCount, androidCount, iosCount, salesforceCount); - IDashboardService service = SetupGetAppLoginCountsMock(webCount, mobileCount, androidCount, iosCount, salesforceCount); + IDashboardService service = SetupDashboardServiceForAppLoginCounts(webCount, mobileCount, androidCount, iosCount, salesforceCount); // Act AppLoginCounts actual = await service.GetAppLoginCountsAsync(startDate, endDate); @@ -185,7 +184,7 @@ public async Task ShouldGetRatingsSummary() { "5", fiveStarCount }, }; - IDashboardService service = SetupGetRatingsSummaryMock(threeStarCount, fiveStarCount); + IDashboardService service = SetupDashboardServiceForRatingsSummary(threeStarCount, fiveStarCount); // Act IDictionary actual = await service.GetRatingsSummaryAsync(startDate, endDate); @@ -206,8 +205,9 @@ public async Task ShouldGetAgeCounts() DateOnly startDatePlus5Years = startDate.AddYears(5); DateOnly endDate = new(2024, 1, 15); - const int startDateAge = 14; - const int startDatePlus5YearsAge = 9; + int currentYear = DateTime.UtcNow.Year; + int startDateAge = currentYear - startDate.Year; + int startDatePlus5YearsAge = currentYear - startDatePlus5Years.Year; const int startDateAgeCount = 5; const int startDatePlus5YearsAgeCount = 1; @@ -217,7 +217,7 @@ public async Task ShouldGetAgeCounts() { startDatePlus5YearsAge, startDatePlus5YearsAgeCount }, }; - IDashboardService service = SetupGetAgeCountsMock(startDate, startDatePlus5Years, startDateAgeCount, startDatePlus5YearsAgeCount); + IDashboardService service = SetupDashboardServiceForAgeCounts(startDate, startDatePlus5Years, startDateAgeCount, startDatePlus5YearsAgeCount); // Act IDictionary actual = await service.GetAgeCountsAsync(startDate, endDate); @@ -235,7 +235,7 @@ private static IConfigurationRoot GetIConfigurationRoot() }; return new ConfigurationBuilder() - .AddInMemoryCollection(myConfiguration.ToList()) + .AddInMemoryCollection(myConfiguration) .Build(); } @@ -255,7 +255,7 @@ private static IDashboardService GetDashboardService( ratingDelegateMock.Object); } - private static IDashboardService SetupGetAllTimeCountsMock(int userProfileCount, int closedUserProfileCount, int dependentCount) + private static IDashboardService SetupDashboardServiceForAllTimeCounts(int userProfileCount, int closedUserProfileCount, int dependentCount) { Mock userProfileDelegateMock = new(); userProfileDelegateMock.Setup(s => s.GetUserProfileCountAsync(It.IsAny())).ReturnsAsync(userProfileCount); @@ -267,7 +267,7 @@ private static IDashboardService SetupGetAllTimeCountsMock(int userProfileCount, return GetDashboardService(dependentDelegateMock, userProfileDelegateMock); } - private static IDashboardService SetupDailyUsageCountsMock( + private static IDashboardService SetupDashboardServiceForDailyUsageCounts( IDictionary userRegistrationCounts, IDictionary userLoginCounts, IDictionary dependentRegistrationCounts) @@ -284,7 +284,7 @@ private static IDashboardService SetupDailyUsageCountsMock( return GetDashboardService(dependentDelegateMock, userProfileDelegateMock); } - private static IDashboardService SetupGetRecurringUserCountMock(int dayCount, int userCount) + private static IDashboardService SetupDashboardServiceForRecurringUserCount(int dayCount, int userCount) { Mock userProfileDelegateMock = new(); userProfileDelegateMock.Setup(s => s.GetRecurringUserCountAsync(dayCount, It.IsAny(), It.IsAny(), It.IsAny())) @@ -293,7 +293,7 @@ private static IDashboardService SetupGetRecurringUserCountMock(int dayCount, in return GetDashboardService(userProfileDelegateMock: userProfileDelegateMock); } - private static IDashboardService SetupGetAppLoginCountsMock(int webCount, int mobileCount, int androidCount, int iosCount, int salesforceCount) + private static IDashboardService SetupDashboardServiceForAppLoginCounts(int webCount, int mobileCount, int androidCount, int iosCount, int salesforceCount) { IDictionary lastLoginClientCounts = new Dictionary { @@ -311,7 +311,7 @@ private static IDashboardService SetupGetAppLoginCountsMock(int webCount, int mo return GetDashboardService(userProfileDelegateMock: userProfileDelegateMock); } - private static IDashboardService SetupGetRatingsSummaryMock(int threeStarCount, int fiveStarCount) + private static IDashboardService SetupDashboardServiceForRatingsSummary(int threeStarCount, int fiveStarCount) { IDictionary summary = new Dictionary { @@ -326,7 +326,7 @@ private static IDashboardService SetupGetRatingsSummaryMock(int threeStarCount, return GetDashboardService(ratingDelegateMock: ratingsDelegateMock); } - private static IDashboardService SetupGetAgeCountsMock(DateOnly startDate, DateOnly startDatePlus5Years, int startDateAgeCount, int startDatePlus5YearsAgeCount) + private static IDashboardService SetupDashboardServiceForAgeCounts(DateOnly startDate, DateOnly startDatePlus5Years, int startDateAgeCount, int startDatePlus5YearsAgeCount) { IDictionary yearOfBirthCounts = new Dictionary { diff --git a/Apps/Admin/Tests/Services/DelegationServiceTests.cs b/Apps/Admin/Tests/Services/DelegationServiceTests.cs index d8e56c59ae..9405e82803 100644 --- a/Apps/Admin/Tests/Services/DelegationServiceTests.cs +++ b/Apps/Admin/Tests/Services/DelegationServiceTests.cs @@ -575,7 +575,7 @@ private static IConfigurationRoot GetIConfigurationRoot(bool isChangeFeedEnabled }; return new ConfigurationBuilder() - .AddInMemoryCollection(myConfiguration.ToList()) + .AddInMemoryCollection(myConfiguration) .Build(); } @@ -844,7 +844,7 @@ private static DelegationService GetDelegationService( delegationDelegate.Setup(p => p.GetDependentAsync(resourceOwnerHdid, true, CancellationToken.None)).ReturnsAsync(dependent); - messageSender = messageSender ?? new(); + messageSender ??= new(); return new( GetIConfigurationRoot(isChangeFeedEnabled), diff --git a/Apps/Admin/Tests/Services/InactiveUserServiceTests.cs b/Apps/Admin/Tests/Services/InactiveUserServiceTests.cs index 1d01043b7d..c350f85a2d 100644 --- a/Apps/Admin/Tests/Services/InactiveUserServiceTests.cs +++ b/Apps/Admin/Tests/Services/InactiveUserServiceTests.cs @@ -17,7 +17,6 @@ namespace HealthGateway.Admin.Tests.Services { using System; using System.Collections.Generic; - using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; @@ -234,7 +233,7 @@ private static IConfigurationRoot GetIConfigurationRoot() }; return new ConfigurationBuilder() - .AddInMemoryCollection(myConfiguration.ToList()) + .AddInMemoryCollection(myConfiguration) .Build(); } diff --git a/Apps/Admin/Tests/Services/SupportServiceTests.cs b/Apps/Admin/Tests/Services/SupportServiceTests.cs index db34f02820..81bfd60a1b 100644 --- a/Apps/Admin/Tests/Services/SupportServiceTests.cs +++ b/Apps/Admin/Tests/Services/SupportServiceTests.cs @@ -148,11 +148,11 @@ public async Task ShouldGetPatientSupportDetails( IList messagingVerifications = GenerateMessagingVerifications(SmsNumber, Email); VaccineDetails vaccineDetails = GenerateVaccineDetails(GenerateVaccineDose()); AgentAuditQuery auditQuery = new(Hdid); - IEnumerable agentAudits = new[] { GenerateAgentAudit() }; - IEnumerable blockedDataSources = new[] - { + IEnumerable agentAudits = [GenerateAgentAudit()]; + IEnumerable blockedDataSources = + [ DataSource.Immunization, - }; + ]; ResourceDelegateQuery resourceDelegateQuery = new() { ByDelegateHdid = patientQuery.Hdid, IncludeDependent = true }; ResourceDelegateQueryResult resourceDelegateQueryResult = GenerateResourceDelegatesForDelegate(patientQuery.Hdid, [Hdid2]); PatientDetailsQuery dependentPatientQuery = diff --git a/Apps/Admin/Tests/Services/UserFeedbackServiceTests.cs b/Apps/Admin/Tests/Services/UserFeedbackServiceTests.cs index 2bab6a132c..ac5b54604b 100644 --- a/Apps/Admin/Tests/Services/UserFeedbackServiceTests.cs +++ b/Apps/Admin/Tests/Services/UserFeedbackServiceTests.cs @@ -429,7 +429,7 @@ private static IConfigurationRoot GetIConfigurationRoot() }; return new ConfigurationBuilder() - .AddInMemoryCollection(myConfiguration.ToList()) + .AddInMemoryCollection(myConfiguration) .Build(); } @@ -438,9 +438,9 @@ private static IUserFeedbackService GetUserFeedbackService( Mock? adminTagDelegateMock = null, Mock? userProfileDelegateMock = null) { - feedbackDelegateMock = feedbackDelegateMock ?? new(); - adminTagDelegateMock = adminTagDelegateMock ?? new(); - userProfileDelegateMock = userProfileDelegateMock ?? new(); + feedbackDelegateMock ??= new(); + adminTagDelegateMock ??= new(); + userProfileDelegateMock ??= new(); return new UserFeedbackService( new Mock>().Object, diff --git a/Apps/ClinicalDocument/src/Services/ClinicalDocumentService.cs b/Apps/ClinicalDocument/src/Services/ClinicalDocumentService.cs index 011498d0d7..accbae24a5 100644 --- a/Apps/ClinicalDocument/src/Services/ClinicalDocumentService.cs +++ b/Apps/ClinicalDocument/src/Services/ClinicalDocumentService.cs @@ -94,7 +94,7 @@ public async Task>> GetRecords this.logger.LogDebug("Retrieving clinical documents"); PhsaHealthDataResponse apiResponse = await this.clinicalDocumentsApi.GetClinicalDocumentRecordsAsync(pid, ct); - IList clinicalDocuments = apiResponse.Data.Select(this.mappingService.MapToClinicalDocumentRecord).ToList(); + List clinicalDocuments = apiResponse.Data.Select(this.mappingService.MapToClinicalDocumentRecord).ToList(); requestResult.ResultStatus = ResultType.Success; requestResult.ResourcePayload = clinicalDocuments; requestResult.TotalResultCount = clinicalDocuments.Count; diff --git a/Apps/ClinicalDocument/test/unit/ClinicalDocumentTests.csproj b/Apps/ClinicalDocument/test/unit/ClinicalDocumentTests.csproj index 26bf43a561..99a704b768 100644 --- a/Apps/ClinicalDocument/test/unit/ClinicalDocumentTests.csproj +++ b/Apps/ClinicalDocument/test/unit/ClinicalDocumentTests.csproj @@ -10,19 +10,19 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/Apps/Common/src/AccessManagement/Authentication/AuthenticationDelegate.cs b/Apps/Common/src/AccessManagement/Authentication/AuthenticationDelegate.cs index 108201b41f..04f49fda08 100644 --- a/Apps/Common/src/AccessManagement/Authentication/AuthenticationDelegate.cs +++ b/Apps/Common/src/AccessManagement/Authentication/AuthenticationDelegate.cs @@ -217,13 +217,13 @@ private async Task GetSystemTokenAsync(ClientCredentialsRequest reques private async Task ClientCredentialsGrantAsync(ClientCredentialsRequest request, CancellationToken ct) { ClientCredentialsRequestParameters parameters = request.Parameters; - IEnumerable> oauthParams = new[] - { - new KeyValuePair("client_id", parameters.ClientId), - new KeyValuePair("client_secret", parameters.ClientSecret), - new KeyValuePair("audience", parameters.Audience), - new KeyValuePair("grant_type", "client_credentials"), - }; + IEnumerable> oauthParams = + [ + new("client_id", parameters.ClientId), + new("client_secret", parameters.ClientSecret), + new("audience", parameters.Audience), + new("grant_type", "client_credentials"), + ]; using FormUrlEncodedContent content = new(oauthParams); return await this.AuthenticateAsync(request.TokenUri, content, ct); } diff --git a/Apps/Common/src/AspNetConfiguration/Modules/Audit.cs b/Apps/Common/src/AspNetConfiguration/Modules/Audit.cs index a8866df15d..46329d6ab2 100644 --- a/Apps/Common/src/AspNetConfiguration/Modules/Audit.cs +++ b/Apps/Common/src/AspNetConfiguration/Modules/Audit.cs @@ -37,7 +37,7 @@ public static class Audit /// The configuration to use. public static void ConfigureAuditServices(IServiceCollection services, ILogger logger, IConfiguration configuration) { - services.AddMvc(options => options.Filters.Add(typeof(AuditFilter))); + services.AddMvc(options => options.Filters.Add()); bool redisAuditing = configuration.GetValue("RedisAuditing", false); if (redisAuditing) diff --git a/Apps/Common/src/AspNetConfiguration/Modules/Auth.cs b/Apps/Common/src/AspNetConfiguration/Modules/Auth.cs index de41da3219..0f03f608d7 100644 --- a/Apps/Common/src/AspNetConfiguration/Modules/Auth.cs +++ b/Apps/Common/src/AspNetConfiguration/Modules/Auth.cs @@ -58,6 +58,7 @@ public static class Auth /// The services collection provider. /// The logger to use. /// The configuration to use for values. + [SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Parameter kept for future extensibility")] public static void ConfigureAuthorizationServices(IServiceCollection services, ILogger logger, IConfiguration configuration) { logger.LogDebug("ConfigureAuthorizationServices..."); diff --git a/Apps/Common/src/AspNetConfiguration/Modules/Db.cs b/Apps/Common/src/AspNetConfiguration/Modules/Db.cs index 51ca43a4d6..37fbdd5ee7 100644 --- a/Apps/Common/src/AspNetConfiguration/Modules/Db.cs +++ b/Apps/Common/src/AspNetConfiguration/Modules/Db.cs @@ -43,17 +43,18 @@ public static void ConfigureDatabaseServices(IServiceCollection services, ILogge logger.LogDebug("Sensitive Data Logging is enabled: {IsSensitiveDataLoggingEnabled}", isSensitiveDataLoggingEnabled); services.AddEntityFrameworkNpgsql(); - services.AddSingleton(_ => - { - NpgsqlDataSourceBuilder dataSourceBuilder = new(configuration.GetConnectionString("GatewayConnection")); - dataSourceBuilder.EnableDynamicJson(); - return dataSourceBuilder.Build(); - }); + services.AddSingleton( + _ => + { + NpgsqlDataSourceBuilder dataSourceBuilder = new(configuration.GetConnectionString("GatewayConnection")); + dataSourceBuilder.EnableDynamicJson(); + return dataSourceBuilder.Build(); + }); services.AddDbContextPool( (sp, options) => { - var ds = sp.GetRequiredService(); + NpgsqlDataSource ds = sp.GetRequiredService(); options.UseInternalServiceProvider(sp); options.UseNpgsql( ds, diff --git a/Apps/Common/src/AspNetConfiguration/Modules/HttpWeb.cs b/Apps/Common/src/AspNetConfiguration/Modules/HttpWeb.cs index 6b02bdf039..dcd4569cba 100644 --- a/Apps/Common/src/AspNetConfiguration/Modules/HttpWeb.cs +++ b/Apps/Common/src/AspNetConfiguration/Modules/HttpWeb.cs @@ -291,11 +291,7 @@ private static StaticFileOptions GetBlazorStaticFileOptions(IWebHostEnvironment if (context.File.Name == "blazor.boot.json") { - if (context.Context.Response.Headers.ContainsKey("blazor-environment")) - { - context.Context.Response.Headers.Remove("blazor-environment"); - } - + context.Context.Response.Headers.Remove("blazor-environment"); context.Context.Response.Headers.Append("blazor-environment", environment.EnvironmentName); } }, diff --git a/Apps/Common/src/AspNetConfiguration/Modules/Observability.cs b/Apps/Common/src/AspNetConfiguration/Modules/Observability.cs index eaf425ea26..99add031ac 100644 --- a/Apps/Common/src/AspNetConfiguration/Modules/Observability.cs +++ b/Apps/Common/src/AspNetConfiguration/Modules/Observability.cs @@ -84,7 +84,9 @@ public static IApplicationBuilder UseDefaultHttpRequestLogging(this IApplication opts => { opts.IncludeQueryInRequestPath = true; - opts.GetLevel = (httpCtx, _, exception) => ExcludePaths(httpCtx, exception, excludePaths ?? []); + + // ReSharper disable once RedundantDelegateCreation + opts.GetLevel = new Func((httpCtx, _, exception) => ExcludePaths(httpCtx, exception, excludePaths ?? [])); opts.EnrichDiagnosticContext = (diagCtx, httpCtx) => { diagCtx.Set("Host", httpCtx.Request.Host.Value); @@ -104,7 +106,7 @@ public static IApplicationBuilder UseDefaultHttpRequestLogging(this IApplication /// OpenTelemetry configuration values. /// The DI container. #pragma warning disable CA1506 //Avoid excessive class coupling - public static IServiceCollection AddOpenTelemetryDefaults(this IServiceCollection services, OpenTelemetryConfig otlpConfig) + public static IServiceCollection AddOpenTelemetryDefaults(this IServiceCollection services, OpenTelemetryConfig otlpConfig) // NOSONAR { if (string.IsNullOrEmpty(otlpConfig.ServiceName)) { @@ -127,14 +129,14 @@ public static IServiceCollection AddOpenTelemetryDefaults(this IServiceCollectio .AddAspNetCoreInstrumentation( options => { - options.Filter = httpContext => !Array.Exists( + // ReSharper disable once RedundantLambdaParameterType + options.Filter = (HttpContext httpContext) => !Array.Exists( otlpConfig.IgnorePathPrefixes, s => httpContext.Request.Path.ToString().StartsWith(s, StringComparison.OrdinalIgnoreCase)); }) .AddRedisInstrumentation() .AddEntityFrameworkCoreInstrumentation() - .AddNpgsql() - ; + .AddNpgsql(); foreach (string source in otlpConfig.Sources) { @@ -167,8 +169,7 @@ public static IServiceCollection AddOpenTelemetryDefaults(this IServiceCollectio builder .AddHttpClientInstrumentation() .AddAspNetCoreInstrumentation() - .AddRuntimeInstrumentation() - ; + .AddRuntimeInstrumentation(); if (otlpConfig.MetricsConsoleExporterEnabled) { @@ -270,9 +271,8 @@ private static LogEventLevel ExcludePaths(HttpContext ctx, Exception? ex, string return LogEventLevel.Error; } - return Array.Exists(excludedPaths, path => IsWildcardMatch(ctx.Request.Path, path)) - ? LogEventLevel.Verbose - : LogEventLevel.Information; + bool isWildcardMatch = Array.Exists(excludedPaths, path => IsWildcardMatch(ctx.Request.Path, path)); + return isWildcardMatch ? LogEventLevel.Verbose : LogEventLevel.Information; } private static bool IsWildcardMatch(PathString requestPath, string path) @@ -282,17 +282,18 @@ private static bool IsWildcardMatch(PathString requestPath, string path) return false; } - if (path.EndsWith('*')) - { - return requestPath.Value!.StartsWith(path.Replace("*", string.Empty, StringComparison.InvariantCultureIgnoreCase), StringComparison.InvariantCultureIgnoreCase); - } + string requestPathValue = requestPath.Value!; - if (path.StartsWith('*')) + return path switch { - return requestPath.Value!.EndsWith(path.Replace("*", string.Empty, StringComparison.InvariantCultureIgnoreCase), StringComparison.InvariantCultureIgnoreCase); - } - - return requestPath.Equals(path, StringComparison.InvariantCultureIgnoreCase); + _ when path.EndsWith('*') => requestPathValue.StartsWith( + path.TrimEnd('*'), + StringComparison.InvariantCultureIgnoreCase), + _ when path.StartsWith('*') => requestPathValue.EndsWith( + path.TrimStart('*'), + StringComparison.InvariantCultureIgnoreCase), + _ => requestPath.Equals(path, StringComparison.InvariantCultureIgnoreCase), + }; } } } diff --git a/Apps/Common/src/AspNetConfiguration/Modules/Patient.cs b/Apps/Common/src/AspNetConfiguration/Modules/Patient.cs index 111bd8a118..a7789b3177 100644 --- a/Apps/Common/src/AspNetConfiguration/Modules/Patient.cs +++ b/Apps/Common/src/AspNetConfiguration/Modules/Patient.cs @@ -59,7 +59,8 @@ public static void ConfigurePatientAccess(IServiceCollection services, ILogger l // Note: As per reading we do not have to dispose of the certificate. string? clientCertificatePath = clientConfiguration.GetSection("ClientCertificate").GetValue("Path"); string? certificatePassword = clientConfiguration.GetSection("ClientCertificate").GetValue("Password"); - X509Certificate2 clientRegistriesCertificate = new(File.ReadAllBytes(clientCertificatePath), certificatePassword); + byte[] certificateData = File.ReadAllBytes(clientCertificatePath); + X509Certificate2 clientRegistriesCertificate = X509CertificateLoader.LoadPkcs12(certificateData, certificatePassword); client.ClientCredentials.ClientCertificate.Certificate = clientRegistriesCertificate; client.Endpoint.EndpointBehaviors.Add(s.GetService()); client.ClientCredentials.ServiceCertificate.SslCertificateAuthentication = diff --git a/Apps/Common/src/AspNetConfiguration/ProgramConfiguration.cs b/Apps/Common/src/AspNetConfiguration/ProgramConfiguration.cs index 4fe6d8b911..87b03daef3 100644 --- a/Apps/Common/src/AspNetConfiguration/ProgramConfiguration.cs +++ b/Apps/Common/src/AspNetConfiguration/ProgramConfiguration.cs @@ -84,7 +84,7 @@ public static ILogger GetInitialLogger(IConfiguration configuration) .Enrich.FromLogContext() .CreateBootstrapLogger(); - using var factory = new SerilogLoggerFactory(Log.Logger); + using SerilogLoggerFactory factory = new(Log.Logger); return factory.CreateLogger("Startup"); } } diff --git a/Apps/Common/src/Common.csproj b/Apps/Common/src/Common.csproj index 026a7101d4..7aba695750 100644 --- a/Apps/Common/src/Common.csproj +++ b/Apps/Common/src/Common.csproj @@ -6,22 +6,22 @@ - + - + - + - - - + + + - + - + @@ -33,25 +33,25 @@ - + - - - - - - + + + + + + - - + + - - - - - - - + + + + + + + diff --git a/Apps/Common/src/Constants/OidType.cs b/Apps/Common/src/Constants/OidType.cs index 339909a40d..94d3063148 100644 --- a/Apps/Common/src/Constants/OidType.cs +++ b/Apps/Common/src/Constants/OidType.cs @@ -50,23 +50,19 @@ private OidType(string value) /// The first type to compare, or null. /// The second type to compare, or null. [ExcludeFromCodeCoverage] - public static bool operator ==(OidType lhs, OidType rhs) + public static bool operator ==(OidType? lhs, OidType? rhs) { - // Check for null on left side. - if (ReferenceEquals(lhs, null)) + // Use ReferenceEquals to check for null equality. + if (ReferenceEquals(lhs, rhs)) { - if (ReferenceEquals(rhs, null)) - { - // null == null = true. - return true; - } - - // Only the left side is null. - return false; + return true; } - // Equals handles case of null on right side. - return lhs.Equals(rhs); + // If lhs is null, return false (rhs is not null at this point). + return lhs is not null && + + // Delegate to Equals for further comparison. + lhs.Equals(rhs); } /// @@ -109,13 +105,9 @@ public override string ToString() [ExcludeFromCodeCoverage] public override bool Equals(object? obj) { - // Check for null and compare run-time types. - if (obj == null || !this.GetType().Equals(obj.GetType())) - { - return false; - } - - return this.Equals((OidType)obj); + // Use 'is' to check for null and type compatibility, then delegate to the strongly-typed Equals method. + return obj is OidType other && + this.Equals(other); } /// diff --git a/Apps/Common/src/Delegates/ClientRegistriesDelegate.cs b/Apps/Common/src/Delegates/ClientRegistriesDelegate.cs index b962bc3515..3af0d154b9 100644 --- a/Apps/Common/src/Delegates/ClientRegistriesDelegate.cs +++ b/Apps/Common/src/Delegates/ClientRegistriesDelegate.cs @@ -18,6 +18,7 @@ namespace HealthGateway.Common.Delegates using System; using System.Collections.Generic; using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.ServiceModel; @@ -129,51 +130,91 @@ public async Task> GetDemographicsByPhnAsync(string private static HCIM_IN_GetDemographicsRequest CreateRequest(OidType oidType, string identifierValue, string clientIp) { - HCIM_IN_GetDemographics request = new(); - request.id = new II { root = "2.16.840.1.113883.3.51.1.1.1", extension = DateTimeOffset.Now.ToUnixTimeMilliseconds().ToString(CultureInfo.InvariantCulture) }; - request.creationTime = new TS { value = DateTime.Now.ToString("yyyyMMddHHmmss", CultureInfo.InvariantCulture) }; - request.versionCode = new CS { code = "V3PR1" }; - request.interactionId = new II { root = "2.16.840.1.113883.3.51.1.1.2", extension = "HCIM_IN_GetDemographics" }; - request.processingCode = new CS { code = "P" }; - request.processingModeCode = new CS { code = "T" }; - request.acceptAckCode = new CS { code = "NE" }; - - request.receiver = new MCCI_MT000100Receiver { typeCode = "RCV" }; - request.receiver.device = new MCCI_MT000100Device { determinerCode = Instance, classCode = "DEV" }; - request.receiver.device.id = new II { root = "2.16.840.1.113883.3.51.1.1.4", extension = clientIp }; - request.receiver.device.asAgent = new MCCI_MT000100Agent { classCode = "AGNT" }; - request.receiver.device.asAgent.representedOrganization = new MCCI_MT000100Organization { determinerCode = Instance, classCode = "ORG" }; - request.receiver.device.asAgent.representedOrganization = new MCCI_MT000100Organization { determinerCode = Instance, classCode = "ORG" }; - request.receiver.device.asAgent.representedOrganization.id = new II { root = "2.16.840.1.113883.3.51.1.1.3", extension = "HCIM" }; - - request.sender = new MCCI_MT000100Sender { typeCode = "SND" }; - request.sender.device = new MCCI_MT000100Device { determinerCode = Instance, classCode = "DEV" }; - request.sender.device.id = new II { root = "2.16.840.1.113883.3.51.1.1.5", extension = "MOH_CRS" }; - request.sender.device.asAgent = new MCCI_MT000100Agent { classCode = "AGNT" }; - request.sender.device.asAgent.representedOrganization = new MCCI_MT000100Organization { determinerCode = Instance, classCode = "ORG" }; - request.sender.device.asAgent.representedOrganization = new MCCI_MT000100Organization { determinerCode = Instance, classCode = "ORG" }; - request.sender.device.asAgent.representedOrganization.id = new II { root = "2.16.840.1.113883.3.51.1.1.3", extension = "HGWAY" }; - - request.controlActProcess = new HCIM_IN_GetDemographicsQUQI_MT020001ControlActProcess { classCode = "ACCM", moodCode = "EVN" }; - request.controlActProcess.effectiveTime = new IVL_TS { value = DateTime.Now.ToString("yyyyMMddHHmmss", CultureInfo.InvariantCulture) }; - request.controlActProcess.dataEnterer = new QUQI_MT020001DataEnterer { typeCode = "CST", time = null, typeId = null }; - request.controlActProcess.dataEnterer.assignedPerson = new COCT_MT090100AssignedPerson { classCode = "ENT" }; - - request.controlActProcess.dataEnterer.assignedPerson.id = new II { root = "2.16.840.1.113883.3.51.1.1.7", extension = "HLTHGTWAY" }; - - request.controlActProcess.queryByParameter = new HCIM_IN_GetDemographicsQUQI_MT020001QueryByParameter(); - request.controlActProcess.queryByParameter.queryByParameterPayload = new HCIM_IN_GetDemographicsQueryByParameterPayload(); - request.controlActProcess.queryByParameter.queryByParameterPayload.personid = new HCIM_IN_GetDemographicsPersonid(); - request.controlActProcess.queryByParameter.queryByParameterPayload.personid.value = new II + HCIM_IN_GetDemographics request = new() { - root = oidType.ToString(), - extension = identifierValue, - assigningAuthorityName = "LCTZ_IAS", + id = new II { root = "2.16.840.1.113883.3.51.1.1.1", extension = DateTimeOffset.Now.ToUnixTimeMilliseconds().ToString(CultureInfo.InvariantCulture) }, + creationTime = new TS { value = DateTime.Now.ToString("yyyyMMddHHmmss", CultureInfo.InvariantCulture) }, + versionCode = new CS { code = "V3PR1" }, + interactionId = new II { root = "2.16.840.1.113883.3.51.1.1.2", extension = "HCIM_IN_GetDemographics" }, + processingCode = new CS { code = "P" }, + processingModeCode = new CS { code = "T" }, + acceptAckCode = new CS { code = "NE" }, + receiver = new MCCI_MT000100Receiver + { + typeCode = "RCV", + device = new MCCI_MT000100Device + { + determinerCode = Instance, classCode = "DEV", + id = new II { root = "2.16.840.1.113883.3.51.1.1.4", extension = clientIp }, + asAgent = new MCCI_MT000100Agent + { + classCode = "AGNT", + representedOrganization = new MCCI_MT000100Organization { determinerCode = Instance, classCode = "ORG" }, + }, + }, + }, + }; + + request.receiver.device.asAgent.representedOrganization = new MCCI_MT000100Organization + { + determinerCode = Instance, classCode = "ORG", + id = new II { root = "2.16.840.1.113883.3.51.1.1.3", extension = "HCIM" }, + }; + + request.sender = new MCCI_MT000100Sender + { + typeCode = "SND", + device = new MCCI_MT000100Device + { + determinerCode = Instance, classCode = "DEV", + id = new II { root = "2.16.840.1.113883.3.51.1.1.5", extension = "MOH_CRS" }, + asAgent = new MCCI_MT000100Agent + { + classCode = "AGNT", + representedOrganization = new MCCI_MT000100Organization { determinerCode = Instance, classCode = "ORG" }, + }, + }, + }; + request.sender.device.asAgent.representedOrganization = new MCCI_MT000100Organization + { + determinerCode = Instance, classCode = "ORG", + id = new II { root = "2.16.840.1.113883.3.51.1.1.3", extension = "HGWAY" }, + }; + + request.controlActProcess = new HCIM_IN_GetDemographicsQUQI_MT020001ControlActProcess + { + classCode = "ACCM", moodCode = "EVN", + effectiveTime = new IVL_TS { value = DateTime.Now.ToString("yyyyMMddHHmmss", CultureInfo.InvariantCulture) }, + dataEnterer = new QUQI_MT020001DataEnterer + { + typeCode = "CST", time = null, typeId = null, + assignedPerson = new COCT_MT090100AssignedPerson + { + classCode = "ENT", + id = new II { root = "2.16.840.1.113883.3.51.1.1.7", extension = "HLTHGTWAY" }, + }, + }, + queryByParameter = new HCIM_IN_GetDemographicsQUQI_MT020001QueryByParameter + { + queryByParameterPayload = new HCIM_IN_GetDemographicsQueryByParameterPayload + { + personid = new HCIM_IN_GetDemographicsPersonid + { + value = new II + { + root = oidType.ToString(), + extension = identifierValue, + assigningAuthorityName = "LCTZ_IAS", + }, + }, + }, + }, }; return new HCIM_IN_GetDemographicsRequest(request); } + [SuppressMessage("Style", "IDE0010:Populate switch", Justification = "Team decision")] private static Address? MapAddress(AD? address) { if (address?.Items == null) @@ -399,7 +440,7 @@ private bool PopulateNames(HCIM_IN_GetDemographicsResponseIdentifiedPerson retri private bool PopulateIdentifiers(HCIM_IN_GetDemographicsResponseIdentifiedPerson retrievedPerson, PatientModel patient) { - II? identifiedPersonId = Array.Find(retrievedPerson.identifiedPerson.id ?? Array.Empty(), x => x.root == OidType.Phn.ToString()); + II? identifiedPersonId = Array.Find(retrievedPerson.identifiedPerson.id ?? [], x => x.root == OidType.Phn.ToString()); if (identifiedPersonId == null) { this.logger.LogWarning("The Client Registry returned a person without a PHN"); @@ -409,7 +450,7 @@ private bool PopulateIdentifiers(HCIM_IN_GetDemographicsResponseIdentifiedPerson patient.PersonalHealthNumber = identifiedPersonId.extension; } - II? subjectId = Array.Find(retrievedPerson.id ?? Array.Empty(), x => x.displayable && x.root == OidType.Hdid.ToString()); + II? subjectId = Array.Find(retrievedPerson.id ?? [], x => x.displayable && x.root == OidType.Hdid.ToString()); if (subjectId == null) { this.logger.LogWarning("The Client Registry returned a person without an HDID"); diff --git a/Apps/Common/src/Delegates/HmacHashDelegate.cs b/Apps/Common/src/Delegates/HmacHashDelegate.cs index 3c6f809f4d..e07ab8402d 100644 --- a/Apps/Common/src/Delegates/HmacHashDelegate.cs +++ b/Apps/Common/src/Delegates/HmacHashDelegate.cs @@ -16,6 +16,7 @@ namespace HealthGateway.Common.Delegates { using System; + using System.Diagnostics.CodeAnalysis; using System.Security.Cryptography; using HealthGateway.Common.Constants; using HealthGateway.Common.Models; @@ -53,6 +54,8 @@ public HmacHashDelegate(IConfiguration configuration) /// The pseudorandom function to use for the hash. /// The number of iterations to process over the hash. /// The hash object. + [SuppressMessage("Style", "IDE0010:Populate switch", Justification = "Team decision")] + [SuppressMessage("Style", "IDE0072:Populate switch", Justification = "Team decision")] public static HmacHash HmacHash( string? key, byte[] salt, @@ -69,19 +72,12 @@ public static HmacHash HmacHash( { // The key is not null, so we can generate a hash // Calculate the length in bytes of the hash given the function size - int hashLength; - switch (prf) + int hashLength = prf switch { - case KeyDerivationPrf.HMACSHA1: - hashLength = 20; - break; - case KeyDerivationPrf.HMACSHA256: - hashLength = 32; - break; - default: - hashLength = 64; - break; - } + KeyDerivationPrf.HMACSHA1 => 20, + KeyDerivationPrf.HMACSHA256 => 32, + _ => 64, + }; retHash.Salt = Convert.ToBase64String(salt); retHash.Hash = Convert.ToBase64String(KeyDerivation.Pbkdf2(key, salt, prf, iterations, hashLength)); diff --git a/Apps/Common/src/Delegates/VaccineProofDelegate.cs b/Apps/Common/src/Delegates/VaccineProofDelegate.cs index 3bf1227b99..c713790b6a 100644 --- a/Apps/Common/src/Delegates/VaccineProofDelegate.cs +++ b/Apps/Common/src/Delegates/VaccineProofDelegate.cs @@ -64,6 +64,7 @@ public VaccineProofDelegate( } /// + [SuppressMessage("Style", "IDE0072:Populate swtich", Justification = "Team decision")] public async Task> MailAsync(VaccineProofTemplate vaccineProofTemplate, VaccineProofRequest request, Address address, CancellationToken ct = default) { RequestResult retVal = new() @@ -134,6 +135,7 @@ public async Task> MailAsync(VaccineProofTem } /// + [SuppressMessage("Style", "IDE0072:Populate switch", Justification = "Team decision")] public async Task> GenerateAsync(VaccineProofTemplate vaccineProofTemplate, VaccineProofRequest request, CancellationToken ct = default) { RequestResult retVal = new() @@ -192,6 +194,7 @@ public async Task> GenerateAsync(VaccineProo /// [SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Prevent exception propagation")] + [SuppressMessage("Style", "IDE0010:Populate switch", Justification = "Team decision")] public async Task> GetAssetAsync(Uri assetUri, CancellationToken ct = default) { RequestResult retVal = new() @@ -258,6 +261,7 @@ public async Task> GetAssetAsync(Uri assetUri, Cancel } // ReSharper disable once CognitiveComplexity + [SuppressMessage("Style", "IDE0010:Populate switch", Justification = "Team decision")] private async Task> PostAsync(string endpointString, StringContent httpContent, CancellationToken ct) where T : class { diff --git a/Apps/Common/src/ErrorHandling/ExceptionUtilities.cs b/Apps/Common/src/ErrorHandling/ExceptionUtilities.cs index e199e8fcf6..e64f4b2c04 100644 --- a/Apps/Common/src/ErrorHandling/ExceptionUtilities.cs +++ b/Apps/Common/src/ErrorHandling/ExceptionUtilities.cs @@ -110,7 +110,8 @@ private static string FormatTitle(string title) return new { e.Message, e.StackTrace }; } - return new { e.Message, e.StackTrace, InnerException = FormatExceptionDetails(e.InnerException, maxDepth - 1) }; + object? innerExceptionDetails = FormatExceptionDetails(e.InnerException, maxDepth - 1); + return new { e.Message, e.StackTrace, InnerException = innerExceptionDetails }; } } } diff --git a/Apps/Common/src/Services/CommunicationService.cs b/Apps/Common/src/Services/CommunicationService.cs index 3144475a66..e29dbb7dee 100644 --- a/Apps/Common/src/Services/CommunicationService.cs +++ b/Apps/Common/src/Services/CommunicationService.cs @@ -141,6 +141,7 @@ public void ClearCache() /// /// The CommunicationType to retrieve the key for. /// The key for the cache associated with the given CommunicationType. + [SuppressMessage("Style", "IDE0072:Populate switch", Justification = "Team decision")] internal static string GetCacheKey(CommunicationType communicationType) { return communicationType switch diff --git a/Apps/Common/src/Services/NotificationSettingsService.cs b/Apps/Common/src/Services/NotificationSettingsService.cs index c1f840513d..ae2446ec45 100644 --- a/Apps/Common/src/Services/NotificationSettingsService.cs +++ b/Apps/Common/src/Services/NotificationSettingsService.cs @@ -17,6 +17,7 @@ namespace HealthGateway.Common.Services { using System; using System.Collections.Generic; + using System.Linq; using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -55,7 +56,7 @@ public NotificationSettingsService( /// public async Task QueueNotificationSettingsAsync(NotificationSettingsRequest notificationSettings, CancellationToken ct = default) { - if (notificationSettings.SmsEnabled && !notificationSettings.SmsVerified && string.IsNullOrEmpty(notificationSettings.SmsVerificationCode)) + if (notificationSettings is { SmsEnabled: true, SmsVerified: false } && string.IsNullOrEmpty(notificationSettings.SmsVerificationCode)) { throw new InvalidOperationException(); } @@ -66,26 +67,37 @@ public async Task QueueNotificationSettingsAsync(NotificationSettingsRequest not // Update the notification settings for any dependents IEnumerable resourceDelegates = await this.resourceDelegateDelegate.GetAsync(notificationSettings.SubjectHdid, 0, 500, ct); - foreach (ResourceDelegate resourceDelegate in resourceDelegates) - { - NotificationSettingsRequest dependentNotificationSettings = new() - { - SubjectHdid = resourceDelegate.ResourceOwnerHdid, - EmailAddress = notificationSettings.EmailAddress, - EmailEnabled = notificationSettings.EmailEnabled, - EmailScope = notificationSettings.EmailScope, - }; - // Only populate SMS number if it has been verified - if (notificationSettings.SmsVerified) - { - dependentNotificationSettings.SmsNumber = notificationSettings.SmsNumber; - dependentNotificationSettings.SmsEnabled = notificationSettings.SmsEnabled; - dependentNotificationSettings.SmsScope = notificationSettings.SmsScope; - dependentNotificationSettings.SmsVerified = notificationSettings.SmsVerified; - } + // Transform resourceDelegates to NotificationSettingsRequest using LINQ + List dependentNotificationSettingsList = resourceDelegates + .Select( + resourceDelegate => + { + NotificationSettingsRequest dependentNotificationSettings = new() + { + SubjectHdid = resourceDelegate.ResourceOwnerHdid, + EmailAddress = notificationSettings.EmailAddress, + EmailEnabled = notificationSettings.EmailEnabled, + EmailScope = notificationSettings.EmailScope, + }; + + // Only populate SMS number if it has been verified + if (notificationSettings.SmsVerified) + { + dependentNotificationSettings.SmsNumber = notificationSettings.SmsNumber; + dependentNotificationSettings.SmsEnabled = notificationSettings.SmsEnabled; + dependentNotificationSettings.SmsScope = notificationSettings.SmsScope; + dependentNotificationSettings.SmsVerified = notificationSettings.SmsVerified; + } - this.logger.LogDebug("Queueing notification settings push for dependent {Hdid}", resourceDelegate.ResourceOwnerHdid); + return dependentNotificationSettings; + }) + .ToList(); + + // Process the transformed list + foreach (NotificationSettingsRequest dependentNotificationSettings in dependentNotificationSettingsList) + { + this.logger.LogDebug("Queueing notification settings push for dependent {Hdid}", dependentNotificationSettings.SubjectHdid); string delegateJson = JsonSerializer.Serialize(dependentNotificationSettings); this.jobClient.Enqueue(j => j.PushNotificationSettingsAsync(delegateJson, ct)); } diff --git a/Apps/Common/src/Swagger/ConfigureSwaggerGenOptions.cs b/Apps/Common/src/Swagger/ConfigureSwaggerGenOptions.cs index f1ab2a5a33..97b26a2776 100644 --- a/Apps/Common/src/Swagger/ConfigureSwaggerGenOptions.cs +++ b/Apps/Common/src/Swagger/ConfigureSwaggerGenOptions.cs @@ -43,8 +43,10 @@ public ConfigureSwaggerGenOptions( IApiVersionDescriptionProvider versionDescriptionProvider, IOptions swaggerSettings) { - Debug.Assert(versionDescriptionProvider != null, $"{nameof(versionDescriptionProvider)} != null"); - Debug.Assert(swaggerSettings != null, $"{nameof(swaggerSettings)} != null"); +#pragma warning disable S3236 + Debug.Assert(versionDescriptionProvider != null, "The versionDescriptionProvider parameter cannot be null."); + Debug.Assert(swaggerSettings != null, "The swaggerSettings parameter cannot be null."); +#pragma warning restore S3236 this.provider = versionDescriptionProvider; this.settings = swaggerSettings.Value; diff --git a/Apps/Common/src/Swagger/ConfigureSwaggerUiOptions.cs b/Apps/Common/src/Swagger/ConfigureSwaggerUiOptions.cs index b7bd73f9c8..fb2f4aa97a 100644 --- a/Apps/Common/src/Swagger/ConfigureSwaggerUiOptions.cs +++ b/Apps/Common/src/Swagger/ConfigureSwaggerUiOptions.cs @@ -37,9 +37,10 @@ public sealed class ConfigureSwaggerUiOptions : IConfigureOptionssettings. public ConfigureSwaggerUiOptions(IApiVersionDescriptionProvider versionDescriptionProvider, IOptions settings) { - Debug.Assert(versionDescriptionProvider != null, $"{nameof(versionDescriptionProvider)} != null"); - Debug.Assert(settings != null, $"{nameof(versionDescriptionProvider)} != null"); - +#pragma warning disable S3236 + Debug.Assert(versionDescriptionProvider != null, "The versionDescriptionProvider parameter cannot be null."); + Debug.Assert(settings != null, "The settings parameter cannot be null."); +#pragma warning restore S3236 this.provider = versionDescriptionProvider; this.settings = settings.Value; } diff --git a/Apps/Common/src/Swagger/SwaggerDefaultValues.cs b/Apps/Common/src/Swagger/SwaggerDefaultValues.cs index dd10625056..a4e7d54e26 100644 --- a/Apps/Common/src/Swagger/SwaggerDefaultValues.cs +++ b/Apps/Common/src/Swagger/SwaggerDefaultValues.cs @@ -46,10 +46,7 @@ public void Apply(OpenApiOperation operation, OperationFilterContext context) ApiParameterDescription description = context.ApiDescription.ParameterDescriptions.First(p => p.Name == parameter.Name); ApiParameterRouteInfo? routeInfo = description.RouteInfo; - if (parameter.Description == null) - { - parameter.Description = description.ModelMetadata?.Description; - } + parameter.Description ??= description.ModelMetadata?.Description; if (routeInfo == null) { diff --git a/Apps/Common/src/Utils/Odr/OdrAuthorizationHandler.cs b/Apps/Common/src/Utils/Odr/OdrAuthorizationHandler.cs index b65aee78fb..a9abf7377d 100644 --- a/Apps/Common/src/Utils/Odr/OdrAuthorizationHandler.cs +++ b/Apps/Common/src/Utils/Odr/OdrAuthorizationHandler.cs @@ -52,7 +52,8 @@ public OdrAuthorizationHandler(IConfiguration configuration) if (odrConfig.ClientCertificate?.Enabled == true) { byte[] certificateData = File.ReadAllBytes(odrConfig.ClientCertificate?.Path); - this.ClientCertificates.Add(new X509Certificate2(certificateData, odrConfig.ClientCertificate?.Password)); + X509Certificate2 clientRegistriesCertificate = X509CertificateLoader.LoadPkcs12(certificateData, odrConfig.ClientCertificate?.Password); + this.ClientCertificates.Add(clientRegistriesCertificate); } } diff --git a/Apps/Common/src/Utils/PolymorphicJsonConverter.cs b/Apps/Common/src/Utils/PolymorphicJsonConverter.cs index a6ca226c40..0d338444c7 100644 --- a/Apps/Common/src/Utils/PolymorphicJsonConverter.cs +++ b/Apps/Common/src/Utils/PolymorphicJsonConverter.cs @@ -36,12 +36,8 @@ public class PolymorphicJsonConverter : JsonConverter /// public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - Type? actualType = this.FindType(typeToConvert, ref reader); - - if (actualType == null) - { - throw new JsonException($"serialized json doesn't have a {this.Discriminator} property"); - } + Type actualType = this.FindType(typeToConvert, ref reader) + ?? throw new JsonException($"serialized json doesn't have a {this.Discriminator} property"); return (T)(JsonSerializer.Deserialize(ref reader, actualType, options) ?? throw new JsonException($"failed to deserialize type {actualType.Name}")); diff --git a/Apps/Common/src/Utils/SerializationHelper.cs b/Apps/Common/src/Utils/SerializationHelper.cs index 1adca37af7..eb9b374480 100644 --- a/Apps/Common/src/Utils/SerializationHelper.cs +++ b/Apps/Common/src/Utils/SerializationHelper.cs @@ -64,12 +64,14 @@ public static byte[] Serialize(this T? obj, bool serializeUsingActualType = t { if (obj == null) { - return Array.Empty(); + return []; } - return serializeUsingActualType + byte[] serializedData = serializeUsingActualType ? SerializeUsingConcreteType(obj, options) : SerializeUsingGenericType(obj, options); + + return serializedData; } private static byte[] SerializeUsingConcreteType(T obj, JsonSerializerOptions? options = null) diff --git a/Apps/Common/test/unit/AccessManagement/Authentication/AuthenticationDelegateTests.cs b/Apps/Common/test/unit/AccessManagement/Authentication/AuthenticationDelegateTests.cs index fe6800b0a9..e8610cb6af 100644 --- a/Apps/Common/test/unit/AccessManagement/Authentication/AuthenticationDelegateTests.cs +++ b/Apps/Common/test/unit/AccessManagement/Authentication/AuthenticationDelegateTests.cs @@ -223,10 +223,9 @@ public void ShouldBeAbleToDetermineLoginClientType(string clientAzp, UserLoginCl { User = new ClaimsPrincipal( new ClaimsIdentity( - new Claim[] - { + [ new("azp", clientAzp), - }, + ], "token")), }; Mock mockHttpContextAccessor = new(); diff --git a/Apps/Common/test/unit/AccessManagement/Authorization/PersonalAccessHandlerTests.cs b/Apps/Common/test/unit/AccessManagement/Authorization/PersonalAccessHandlerTests.cs index 13d4053011..1eccd967d1 100644 --- a/Apps/Common/test/unit/AccessManagement/Authorization/PersonalAccessHandlerTests.cs +++ b/Apps/Common/test/unit/AccessManagement/Authorization/PersonalAccessHandlerTests.cs @@ -50,9 +50,9 @@ public void UnknownRequirement() List claims = [ - new Claim(ClaimTypes.Name, username), - new Claim(ClaimTypes.NameIdentifier, userId), - new Claim(GatewayClaims.Hdid, hdid), + new(ClaimTypes.Name, username), + new(ClaimTypes.NameIdentifier, userId), + new(GatewayClaims.Hdid, hdid), ]; ClaimsIdentity identity = new(claims, "TestAuth"); ClaimsPrincipal claimsPrincipal = new(identity); @@ -80,7 +80,7 @@ public void UnknownRequirement() ILogger logger = loggerFactory.CreateLogger(); PersonalAccessHandler authHandler = new(logger, httpContextAccessorMock.Object); - NameAuthorizationRequirement[] requirements = { new(username) }; + NameAuthorizationRequirement[] requirements = [new(username)]; AuthorizationHandlerContext context = new(requirements, claimsPrincipal, null); @@ -104,9 +104,9 @@ public void ReadWriteNoResource() List claims = [ - new Claim(ClaimTypes.Name, username), - new Claim(ClaimTypes.NameIdentifier, userId), - new Claim(GatewayClaims.Hdid, hdid), + new(ClaimTypes.Name, username), + new(ClaimTypes.NameIdentifier, userId), + new(GatewayClaims.Hdid, hdid), ]; ClaimsIdentity identity = new(claims, "TestAuth"); ClaimsPrincipal claimsPrincipal = new(identity); @@ -131,7 +131,7 @@ public void ReadWriteNoResource() ILogger logger = loggerFactory.CreateLogger(); PersonalAccessHandler authHandler = new(logger, httpContextAccessorMock.Object); - PersonalFhirRequirement[] requirements = { new(FhirResource.Patient, FhirAccessType.Read) }; + PersonalFhirRequirement[] requirements = [new(FhirResource.Patient, FhirAccessType.Read)]; AuthorizationHandlerContext context = new(requirements, claimsPrincipal, null); @@ -156,9 +156,9 @@ public void ShouldAuthPatientReadAsOwner() List claims = [ - new Claim(ClaimTypes.Name, username), - new Claim(ClaimTypes.NameIdentifier, userId), - new Claim(GatewayClaims.Hdid, hdid), + new(ClaimTypes.Name, username), + new(ClaimTypes.NameIdentifier, userId), + new(GatewayClaims.Hdid, hdid), ]; ClaimsIdentity identity = new(claims, "TestAuth"); ClaimsPrincipal claimsPrincipal = new(identity); @@ -186,7 +186,7 @@ public void ShouldAuthPatientReadAsOwner() ILogger logger = loggerFactory.CreateLogger(); PersonalAccessHandler authHandler = new(logger, httpContextAccessorMock.Object); - PersonalFhirRequirement[] requirements = { new(FhirResource.Patient, FhirAccessType.Read) }; + PersonalFhirRequirement[] requirements = [new(FhirResource.Patient, FhirAccessType.Read)]; AuthorizationHandlerContext context = new(requirements, claimsPrincipal, null); @@ -211,9 +211,9 @@ public void ShouldAuthPatientReadAsOwnerUsingParameter() List claims = [ - new Claim(ClaimTypes.Name, username), - new Claim(ClaimTypes.NameIdentifier, userId), - new Claim(GatewayClaims.Hdid, hdid), + new(ClaimTypes.Name, username), + new(ClaimTypes.NameIdentifier, userId), + new(GatewayClaims.Hdid, hdid), ]; ClaimsIdentity identity = new(claims, "TestAuth"); ClaimsPrincipal claimsPrincipal = new(identity); @@ -242,7 +242,7 @@ public void ShouldAuthPatientReadAsOwnerUsingParameter() ILogger logger = loggerFactory.CreateLogger(); PersonalAccessHandler authHandler = new(logger, httpContextAccessorMock.Object); - PersonalFhirRequirement[] requirements = { new(FhirResource.Patient, FhirAccessType.Read, FhirSubjectLookupMethod.Parameter) }; + PersonalFhirRequirement[] requirements = [new(FhirResource.Patient, FhirAccessType.Read, FhirSubjectLookupMethod.Parameter)]; AuthorizationHandlerContext context = new(requirements, claimsPrincipal, null); @@ -267,8 +267,8 @@ public void ShouldNotAuthPatientReadWithoutHdidClaim() List claims = [ - new Claim(ClaimTypes.Name, username), - new Claim(ClaimTypes.NameIdentifier, userId), + new(ClaimTypes.Name, username), + new(ClaimTypes.NameIdentifier, userId), ]; ClaimsIdentity identity = new(claims, "TestAuth"); ClaimsPrincipal claimsPrincipal = new(identity); @@ -321,9 +321,9 @@ public void ShouldNotAuthPatientReadAsOwner() List claims = [ - new Claim(ClaimTypes.Name, username), - new Claim(ClaimTypes.NameIdentifier, userId), - new Claim(GatewayClaims.Hdid, hdid), + new(ClaimTypes.Name, username), + new(ClaimTypes.NameIdentifier, userId), + new(GatewayClaims.Hdid, hdid), ]; ClaimsIdentity identity = new(claims, "TestAuth"); ClaimsPrincipal claimsPrincipal = new(identity); @@ -351,7 +351,7 @@ public void ShouldNotAuthPatientReadAsOwner() ILogger logger = loggerFactory.CreateLogger(); PersonalAccessHandler authHandler = new(logger, httpContextAccessorMock.Object); - PersonalFhirRequirement[] requirements = { new(FhirResource.Patient, FhirAccessType.Read) }; + PersonalFhirRequirement[] requirements = [new(FhirResource.Patient, FhirAccessType.Read)]; AuthorizationHandlerContext context = new(requirements, claimsPrincipal, null); @@ -376,10 +376,10 @@ public void ShouldNotAuthPatientReadAsOtherPatient() string scopes = "user/Patient.read"; List claims = [ - new Claim(ClaimTypes.Name, username), - new Claim(ClaimTypes.NameIdentifier, userId), - new Claim(GatewayClaims.Hdid, hdid), - new Claim(GatewayClaims.Scope, scopes), + new(ClaimTypes.Name, username), + new(ClaimTypes.NameIdentifier, userId), + new(GatewayClaims.Hdid, hdid), + new(GatewayClaims.Scope, scopes), ]; ClaimsIdentity identity = new(claims, "TestAuth"); ClaimsPrincipal claimsPrincipal = new(identity); @@ -407,7 +407,7 @@ public void ShouldNotAuthPatientReadAsOtherPatient() ILogger logger = loggerFactory.CreateLogger(); PersonalAccessHandler authHandler = new(logger, httpContextAccessorMock.Object); - PersonalFhirRequirement[] requirements = { new(FhirResource.Patient, FhirAccessType.Read) }; + PersonalFhirRequirement[] requirements = [new(FhirResource.Patient, FhirAccessType.Read)]; AuthorizationHandlerContext context = new(requirements, claimsPrincipal, null); @@ -432,9 +432,9 @@ public void ShouldAuthPatientWriteAsOwner() List claims = [ - new Claim(ClaimTypes.Name, username), - new Claim(ClaimTypes.NameIdentifier, userId), - new Claim(GatewayClaims.Hdid, hdid), + new(ClaimTypes.Name, username), + new(ClaimTypes.NameIdentifier, userId), + new(GatewayClaims.Hdid, hdid), ]; ClaimsIdentity identity = new(claims, "TestAuth"); ClaimsPrincipal claimsPrincipal = new(identity); @@ -462,7 +462,7 @@ public void ShouldAuthPatientWriteAsOwner() ILogger logger = loggerFactory.CreateLogger(); PersonalAccessHandler authHandler = new(logger, httpContextAccessorMock.Object); - PersonalFhirRequirement[] requirements = { new(FhirResource.Patient, FhirAccessType.Write) }; + PersonalFhirRequirement[] requirements = [new(FhirResource.Patient, FhirAccessType.Write)]; AuthorizationHandlerContext context = new(requirements, claimsPrincipal, null); @@ -487,10 +487,10 @@ public void ShouldNotAuthPatientWriteAsOtherPatient() string scopes = "user/Patient.write"; List claims = [ - new Claim(ClaimTypes.Name, username), - new Claim(ClaimTypes.NameIdentifier, userId), - new Claim(GatewayClaims.Hdid, hdid), - new Claim(GatewayClaims.Scope, scopes), + new(ClaimTypes.Name, username), + new(ClaimTypes.NameIdentifier, userId), + new(GatewayClaims.Hdid, hdid), + new(GatewayClaims.Scope, scopes), ]; ClaimsIdentity identity = new(claims, "TestAuth"); ClaimsPrincipal claimsPrincipal = new(identity); @@ -518,7 +518,7 @@ public void ShouldNotAuthPatientWriteAsOtherPatient() ILogger logger = loggerFactory.CreateLogger(); PersonalAccessHandler authHandler = new(logger, httpContextAccessorMock.Object); - PersonalFhirRequirement[] requirements = { new(FhirResource.Patient, FhirAccessType.Write) }; + PersonalFhirRequirement[] requirements = [new(FhirResource.Patient, FhirAccessType.Write)]; AuthorizationHandlerContext context = new(requirements, claimsPrincipal, null); @@ -543,9 +543,9 @@ public void ShouldNotAuthPatientWriteAsOwner() List claims = [ - new Claim(ClaimTypes.Name, username), - new Claim(ClaimTypes.NameIdentifier, userId), - new Claim(GatewayClaims.Hdid, hdid), + new(ClaimTypes.Name, username), + new(ClaimTypes.NameIdentifier, userId), + new(GatewayClaims.Hdid, hdid), ]; ClaimsIdentity identity = new(claims, "TestAuth"); ClaimsPrincipal claimsPrincipal = new(identity); @@ -573,7 +573,7 @@ public void ShouldNotAuthPatientWriteAsOwner() ILogger logger = loggerFactory.CreateLogger(); PersonalAccessHandler authHandler = new(logger, httpContextAccessorMock.Object); - PersonalFhirRequirement[] requirements = { new(FhirResource.Patient, FhirAccessType.Write) }; + PersonalFhirRequirement[] requirements = [new(FhirResource.Patient, FhirAccessType.Write)]; AuthorizationHandlerContext context = new(requirements, claimsPrincipal, null); diff --git a/Apps/Common/test/unit/AccessManagement/Authorization/SystemDelegatedAccessHandlerTests.cs b/Apps/Common/test/unit/AccessManagement/Authorization/SystemDelegatedAccessHandlerTests.cs index 802a491627..2fb346a008 100644 --- a/Apps/Common/test/unit/AccessManagement/Authorization/SystemDelegatedAccessHandlerTests.cs +++ b/Apps/Common/test/unit/AccessManagement/Authorization/SystemDelegatedAccessHandlerTests.cs @@ -49,9 +49,9 @@ public void UnknownRequirement() List claims = [ - new Claim(ClaimTypes.Name, username), - new Claim(ClaimTypes.NameIdentifier, userId), - new Claim(GatewayClaims.Hdid, hdid), + new(ClaimTypes.Name, username), + new(ClaimTypes.NameIdentifier, userId), + new(GatewayClaims.Hdid, hdid), ]; ClaimsIdentity identity = new(claims, "TestAuth"); ClaimsPrincipal claimsPrincipal = new(identity); @@ -79,7 +79,7 @@ public void UnknownRequirement() ILogger logger = loggerFactory.CreateLogger(); SystemDelegatedAccessHandler authHandler = new(logger, httpContextAccessorMock.Object); - NameAuthorizationRequirement[] requirements = { new(username) }; + NameAuthorizationRequirement[] requirements = [new(username)]; AuthorizationHandlerContext context = new(requirements, claimsPrincipal, null); @@ -104,9 +104,9 @@ public void ShouldNotAuthPatientReadAsSystemDelegationDisabled() string scopes = "system/Patient.read"; List claims = [ - new Claim(ClaimTypes.Name, username), - new Claim(ClaimTypes.NameIdentifier, userId), - new Claim(GatewayClaims.Scope, scopes), + new(ClaimTypes.Name, username), + new(ClaimTypes.NameIdentifier, userId), + new(GatewayClaims.Scope, scopes), ]; ClaimsIdentity identity = new(claims, "TestAuth"); ClaimsPrincipal claimsPrincipal = new(identity); @@ -134,7 +134,7 @@ public void ShouldNotAuthPatientReadAsSystemDelegationDisabled() ILogger logger = loggerFactory.CreateLogger(); SystemDelegatedAccessHandler authHandler = new(logger, httpContextAccessorMock.Object); - PersonalFhirRequirement[] requirements = { new(FhirResource.Patient, FhirAccessType.Read, supportsSystemDelegation: false) }; + PersonalFhirRequirement[] requirements = [new(FhirResource.Patient, FhirAccessType.Read, supportsSystemDelegation: false)]; AuthorizationHandlerContext context = new(requirements, claimsPrincipal, null); @@ -159,9 +159,9 @@ public void ShouldNotAuthPatientReadAsSystemEmptyScope() string scopes = string.Empty; List claims = [ - new Claim(ClaimTypes.Name, username), - new Claim(ClaimTypes.NameIdentifier, userId), - new Claim(GatewayClaims.Scope, scopes), + new(ClaimTypes.Name, username), + new(ClaimTypes.NameIdentifier, userId), + new(GatewayClaims.Scope, scopes), ]; ClaimsIdentity identity = new(claims, "TestAuth"); ClaimsPrincipal claimsPrincipal = new(identity); @@ -189,7 +189,7 @@ public void ShouldNotAuthPatientReadAsSystemEmptyScope() ILogger logger = loggerFactory.CreateLogger(); SystemDelegatedAccessHandler authHandler = new(logger, httpContextAccessorMock.Object); - PersonalFhirRequirement[] requirements = { new(FhirResource.Patient, FhirAccessType.Read) }; + PersonalFhirRequirement[] requirements = [new(FhirResource.Patient, FhirAccessType.Read)]; AuthorizationHandlerContext context = new(requirements, claimsPrincipal, null); @@ -213,8 +213,8 @@ public void ShouldNotAuthPatientReadAsSystemMissingScope() string username = "User Name"; List claims = [ - new Claim(ClaimTypes.Name, username), - new Claim(ClaimTypes.NameIdentifier, userId), + new(ClaimTypes.Name, username), + new(ClaimTypes.NameIdentifier, userId), ]; ClaimsIdentity identity = new(claims, "TestAuth"); ClaimsPrincipal claimsPrincipal = new(identity); @@ -242,7 +242,7 @@ public void ShouldNotAuthPatientReadAsSystemMissingScope() ILogger logger = loggerFactory.CreateLogger(); SystemDelegatedAccessHandler authHandler = new(logger, httpContextAccessorMock.Object); - PersonalFhirRequirement[] requirements = { new(FhirResource.Patient, FhirAccessType.Read) }; + PersonalFhirRequirement[] requirements = [new(FhirResource.Patient, FhirAccessType.Read)]; AuthorizationHandlerContext context = new(requirements, claimsPrincipal, null); @@ -267,9 +267,9 @@ public void ShouldAuthPatientReadAsSystem() string scopes = "system/Patient.read"; List claims = [ - new Claim(ClaimTypes.Name, username), - new Claim(ClaimTypes.NameIdentifier, userId), - new Claim(GatewayClaims.Scope, scopes), + new(ClaimTypes.Name, username), + new(ClaimTypes.NameIdentifier, userId), + new(GatewayClaims.Scope, scopes), ]; ClaimsIdentity identity = new(claims, "TestAuth"); ClaimsPrincipal claimsPrincipal = new(identity); @@ -297,7 +297,7 @@ public void ShouldAuthPatientReadAsSystem() ILogger logger = loggerFactory.CreateLogger(); SystemDelegatedAccessHandler authHandler = new(logger, httpContextAccessorMock.Object); - PersonalFhirRequirement[] requirements = { new(FhirResource.Patient, FhirAccessType.Read) }; + PersonalFhirRequirement[] requirements = [new(FhirResource.Patient, FhirAccessType.Read)]; AuthorizationHandlerContext context = new(requirements, claimsPrincipal, null); @@ -322,9 +322,9 @@ public void ShouldAuthPatientWriteAsSystem() string scopes = "system/Patient.write"; List claims = [ - new Claim(ClaimTypes.Name, username), - new Claim(ClaimTypes.NameIdentifier, userId), - new Claim(GatewayClaims.Scope, scopes), + new(ClaimTypes.Name, username), + new(ClaimTypes.NameIdentifier, userId), + new(GatewayClaims.Scope, scopes), ]; ClaimsIdentity identity = new(claims, "TestAuth"); ClaimsPrincipal claimsPrincipal = new(identity); @@ -352,7 +352,7 @@ public void ShouldAuthPatientWriteAsSystem() ILogger logger = loggerFactory.CreateLogger(); SystemDelegatedAccessHandler authHandler = new(logger, httpContextAccessorMock.Object); - PersonalFhirRequirement[] requirements = { new(FhirResource.Patient, FhirAccessType.Write) }; + PersonalFhirRequirement[] requirements = [new(FhirResource.Patient, FhirAccessType.Write)]; AuthorizationHandlerContext context = new(requirements, claimsPrincipal, null); diff --git a/Apps/Common/test/unit/AccessManagement/Authorization/UserDelegatedAccessHandlerTests.cs b/Apps/Common/test/unit/AccessManagement/Authorization/UserDelegatedAccessHandlerTests.cs index 8ab923c65e..b1a9ed81d0 100644 --- a/Apps/Common/test/unit/AccessManagement/Authorization/UserDelegatedAccessHandlerTests.cs +++ b/Apps/Common/test/unit/AccessManagement/Authorization/UserDelegatedAccessHandlerTests.cs @@ -18,7 +18,6 @@ namespace HealthGateway.CommonTests.AccessManagement.Authorization using System; using System.Collections.Generic; using System.Globalization; - using System.Linq; using System.Security.Claims; using System.Threading; using System.Threading.Tasks; @@ -73,7 +72,7 @@ public async Task UnknownRequirement() httpContextAccessorMock.Object, new Mock().Object, new Mock().Object); - NameAuthorizationRequirement[] requirements = { new(this.username) }; + NameAuthorizationRequirement[] requirements = [new(this.username)]; AuthorizationHandlerContext context = new(requirements, claimsPrincipal, null); @@ -118,7 +117,7 @@ public async Task ReadWriteNoResource() httpContextAccessorMock.Object, new Mock().Object, new Mock().Object); - PersonalFhirRequirement[] requirements = { new(FhirResource.Patient, FhirAccessType.Read) }; + PersonalFhirRequirement[] requirements = [new(FhirResource.Patient, FhirAccessType.Read)]; AuthorizationHandlerContext context = new(requirements, claimsPrincipal, null); @@ -148,7 +147,7 @@ public async Task ShouldNotAuthPatientReadAsUserDelegationDisabled() httpContextAccessorMock.Object, new Mock().Object, new Mock().Object); - PersonalFhirRequirement[] requirements = { new(FhirResource.Patient, FhirAccessType.Read, supportsUserDelegation: false) }; + PersonalFhirRequirement[] requirements = [new(FhirResource.Patient, FhirAccessType.Read, supportsUserDelegation: false)]; AuthorizationHandlerContext context = new(requirements, claimsPrincipal, null); @@ -177,7 +176,7 @@ public async Task ShouldNotAuthPatientReadAsOwner() httpContextAccessorMock.Object, new Mock().Object, new Mock().Object); - PersonalFhirRequirement[] requirements = { new(FhirResource.Patient, FhirAccessType.Read) }; + PersonalFhirRequirement[] requirements = [new(FhirResource.Patient, FhirAccessType.Read)]; AuthorizationHandlerContext context = new(requirements, claimsPrincipal, null); @@ -227,7 +226,7 @@ public async Task ShouldAuthResourceDelegate(bool configContainsMaxAge) httpContextAccessorMock.Object, mockPatientService.Object, mockDependentDelegate.Object); - PersonalFhirRequirement[] requirements = { new(FhirResource.Observation, FhirAccessType.Read, supportsUserDelegation: true) }; + PersonalFhirRequirement[] requirements = [new(FhirResource.Observation, FhirAccessType.Read, supportsUserDelegation: true)]; AuthorizationHandlerContext context = new(requirements, claimsPrincipal, null); @@ -322,7 +321,7 @@ private static IConfigurationRoot GetConfiguration(bool includeMaxDependentAge = } return new ConfigurationBuilder() - .AddInMemoryCollection(configDictionary.ToList()) + .AddInMemoryCollection(configDictionary) .Build(); } @@ -330,9 +329,9 @@ private ClaimsPrincipal GetClaimsPrincipal() { List claims = [ - new Claim(ClaimTypes.Name, this.username), - new Claim(ClaimTypes.NameIdentifier, this.userId), - new Claim(GatewayClaims.Hdid, this.hdid), + new(ClaimTypes.Name, this.username), + new(ClaimTypes.NameIdentifier, this.userId), + new(GatewayClaims.Hdid, this.hdid), ]; ClaimsIdentity identity = new(claims, "TestAuth"); return new ClaimsPrincipal(identity); diff --git a/Apps/Common/test/unit/Auditing/DbAuditLoggerTests.cs b/Apps/Common/test/unit/Auditing/DbAuditLoggerTests.cs index 730d1e6dce..18e132812e 100644 --- a/Apps/Common/test/unit/Auditing/DbAuditLoggerTests.cs +++ b/Apps/Common/test/unit/Auditing/DbAuditLoggerTests.cs @@ -58,7 +58,7 @@ public void ShouldPopulateWithHttpContext(bool useRouteValues, bool useQueryPara // Arrange DefaultHttpContext ctx = new() { - Connection = { RemoteIpAddress = new IPAddress(new byte[] { 127, 0, 0, 1 }) }, + Connection = { RemoteIpAddress = new IPAddress([127, 0, 0, 1]) }, User = new(new ClaimsIdentity([new Claim("hdid", Hdid), new Claim("preferred_username", Idir)])), }; @@ -111,7 +111,7 @@ public void ShouldPopulateWithHttpContextHandleStatus401() { DefaultHttpContext ctx = new() { - Connection = { RemoteIpAddress = new IPAddress(new byte[] { 127, 0, 0, 1 }) }, + Connection = { RemoteIpAddress = new IPAddress([127, 0, 0, 1]) }, Response = { StatusCode = 401 }, }; AuditEvent expected = new() @@ -142,7 +142,7 @@ public void ShouldPopulateWithHttpContextHandleOtherStatus400() { DefaultHttpContext ctx = new() { - Connection = { RemoteIpAddress = new IPAddress(new byte[] { 127, 0, 0, 1 }) }, + Connection = { RemoteIpAddress = new IPAddress([127, 0, 0, 1]) }, Response = { StatusCode = 405 }, }; AuditEvent expected = new() @@ -173,7 +173,7 @@ public void ShouldPopulateWithHttpContextHandle500() { DefaultHttpContext ctx = new() { - Connection = { RemoteIpAddress = new IPAddress(new byte[] { 127, 0, 0, 1 }) }, + Connection = { RemoteIpAddress = new IPAddress([127, 0, 0, 1]) }, Response = { StatusCode = 500 }, }; AuditEvent expected = new() @@ -205,7 +205,7 @@ public async Task ShouldWriteAuditEvent() { DefaultHttpContext ctx = new() { - Connection = { RemoteIpAddress = new IPAddress(new byte[] { 127, 0, 0, 1 }) }, + Connection = { RemoteIpAddress = new IPAddress([127, 0, 0, 1]) }, }; AuditEvent expected = new() { @@ -235,7 +235,7 @@ public async Task ShouldWriteAuditEventHandleException() { DefaultHttpContext ctx = new() { - Connection = { RemoteIpAddress = new IPAddress(new byte[] { 127, 0, 0, 1 }) }, + Connection = { RemoteIpAddress = new IPAddress([127, 0, 0, 1]) }, }; AuditEvent expected = new() { diff --git a/Apps/Common/test/unit/CacheProviders/DistributedCacheProviderTests.cs b/Apps/Common/test/unit/CacheProviders/DistributedCacheProviderTests.cs index 32df332d29..5a12130338 100644 --- a/Apps/Common/test/unit/CacheProviders/DistributedCacheProviderTests.cs +++ b/Apps/Common/test/unit/CacheProviders/DistributedCacheProviderTests.cs @@ -284,7 +284,7 @@ private static object GetDefaultItemValue(CacheType cacheType) return number; case CacheType.ObjectType: - IEnumerable dataSources = new[] { DataSource.Immunization }; + IEnumerable dataSources = [DataSource.Immunization]; return dataSources; case CacheType.StringType: diff --git a/Apps/Common/test/unit/CommonTests.csproj b/Apps/Common/test/unit/CommonTests.csproj index c2d03018a3..b35a5e0795 100644 --- a/Apps/Common/test/unit/CommonTests.csproj +++ b/Apps/Common/test/unit/CommonTests.csproj @@ -10,22 +10,22 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all - + - - - - + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/Apps/Common/test/unit/Delegates/AesCryptoDelegateTests.cs b/Apps/Common/test/unit/Delegates/AesCryptoDelegateTests.cs index 4e6fb236a8..2f2d83cd51 100644 --- a/Apps/Common/test/unit/Delegates/AesCryptoDelegateTests.cs +++ b/Apps/Common/test/unit/Delegates/AesCryptoDelegateTests.cs @@ -51,7 +51,7 @@ public void VerifyConfigurationBinding() }; IConfigurationRoot configuration = new ConfigurationBuilder() - .AddInMemoryCollection(myConfiguration.ToList()) + .AddInMemoryCollection(myConfiguration) .Build(); AesCryptoDelegate aesDelegate = new(configuration); @@ -92,7 +92,7 @@ public void VerifyKeyGeneration() }; IConfigurationRoot configuration = new ConfigurationBuilder() - .AddInMemoryCollection(myConfiguration.ToList()) + .AddInMemoryCollection(myConfiguration) .Build(); AesCryptoDelegate aesDelegate = new(configuration); diff --git a/Apps/Common/test/unit/Delegates/CDogsDelegateTests.cs b/Apps/Common/test/unit/Delegates/CDogsDelegateTests.cs index 6067fc61e7..9fbd91fdff 100644 --- a/Apps/Common/test/unit/Delegates/CDogsDelegateTests.cs +++ b/Apps/Common/test/unit/Delegates/CDogsDelegateTests.cs @@ -97,7 +97,7 @@ public async Task ShouldReturnErrorForGenerateReportAsyncHttpError() using HttpResponseMessage httpResponseMessage = new(); httpResponseMessage.StatusCode = statusCode; - httpResponseMessage.Content = new ByteArrayContent(Array.Empty()); + httpResponseMessage.Content = new ByteArrayContent([]); Mock> mockLogger = new(); Mock mockCdogsApi = new(); diff --git a/Apps/Common/test/unit/Delegates/ClientRegistriesDelegateTests.cs b/Apps/Common/test/unit/Delegates/ClientRegistriesDelegateTests.cs index fab1ec577a..8d2c91a1c7 100644 --- a/Apps/Common/test/unit/Delegates/ClientRegistriesDelegateTests.cs +++ b/Apps/Common/test/unit/Delegates/ClientRegistriesDelegateTests.cs @@ -773,21 +773,18 @@ private static HCIM_IN_GetDemographicsResponse1 GenerateDemographicResponse( private static II[] GenerateId(bool shouldReturnEmpty = false, string? extension = null, string? oidType = null) { - if (shouldReturnEmpty) - { - return []; - } - - return - [ - new II - { - root = oidType ?? HdidOidType, - extension = oidType == null || (oidType == HdidOidType && extension == null) ? Hdid - : oidType == PhnOidType && extension == null ? Phn : extension, - displayable = true, - }, - ]; + return shouldReturnEmpty + ? [] + : + [ + new II + { + root = oidType ?? HdidOidType, + extension = oidType == null || (oidType == HdidOidType && extension == null) ? Hdid + : oidType == PhnOidType && extension == null ? Phn : extension, + displayable = true, + }, + ]; } private static IEnumerable GenerateItems(IEnumerable givenNames, IEnumerable familyNames) @@ -848,7 +845,9 @@ private static HCIM_IN_GetDemographicsResponsePerson GenerateIdentifiedPerson( return new HCIM_IN_GetDemographicsResponsePerson { deceasedInd = new BL - { value = deceasedInd }, + { + value = deceasedInd, + }, id = GenerateId(shouldReturnEmpty, extension, oidType), name = names.ToArray(), birthTime = new TS diff --git a/Apps/Common/test/unit/Delegates/HmacHashDelegateTests.cs b/Apps/Common/test/unit/Delegates/HmacHashDelegateTests.cs index 2f6271cf61..a5323e698f 100644 --- a/Apps/Common/test/unit/Delegates/HmacHashDelegateTests.cs +++ b/Apps/Common/test/unit/Delegates/HmacHashDelegateTests.cs @@ -54,7 +54,7 @@ public void VerifyConfigurationBinding() }; IConfigurationRoot configuration = new ConfigurationBuilder() - .AddInMemoryCollection(myConfiguration.ToList()) + .AddInMemoryCollection(myConfiguration) .Build(); HmacHashDelegate hashDelegate = new(configuration); diff --git a/Apps/Common/test/unit/Delegates/RestTokenSwapDelegateTests.cs b/Apps/Common/test/unit/Delegates/RestTokenSwapDelegateTests.cs index e3c0530013..d41fa1c2a0 100644 --- a/Apps/Common/test/unit/Delegates/RestTokenSwapDelegateTests.cs +++ b/Apps/Common/test/unit/Delegates/RestTokenSwapDelegateTests.cs @@ -16,7 +16,6 @@ namespace HealthGateway.CommonTests.Delegates { using System.Collections.Generic; - using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -103,7 +102,7 @@ private static IConfigurationRoot GetIConfigurationRoot() }; return new ConfigurationBuilder() - .AddInMemoryCollection(configuration.ToList()) + .AddInMemoryCollection(configuration) .Build(); } diff --git a/Apps/Common/test/unit/Delegates/VaccineProofDelegateTests.cs b/Apps/Common/test/unit/Delegates/VaccineProofDelegateTests.cs index c96c799197..f51eb30ebf 100644 --- a/Apps/Common/test/unit/Delegates/VaccineProofDelegateTests.cs +++ b/Apps/Common/test/unit/Delegates/VaccineProofDelegateTests.cs @@ -406,7 +406,7 @@ public async Task ValidateGetAssetAsyncSuccess() TotalResultCount = 1, }; - byte[] fileContents = { 1 }; + byte[] fileContents = [1]; using MemoryStream memoryStream = new(fileContents); using HttpResponseMessage httpResponseMessage = new(); httpResponseMessage.StatusCode = HttpStatusCode.OK; @@ -461,7 +461,7 @@ public async Task ValidateGetAssetAsyncNotFound() public async Task ValidateGetAssetAsyncEmptyPayload() { Uri jobUri = new($"https://localhost/{JobId}"); - byte[] fileContents = Array.Empty(); + byte[] fileContents = []; using MemoryStream memoryStream = new(fileContents); using HttpResponseMessage httpResponseMessage = new(); httpResponseMessage.StatusCode = HttpStatusCode.OK; diff --git a/Apps/Common/test/unit/Messaging/AzureServiceBusTests.cs b/Apps/Common/test/unit/Messaging/AzureServiceBusTests.cs index 33802e12a7..7849676afa 100644 --- a/Apps/Common/test/unit/Messaging/AzureServiceBusTests.cs +++ b/Apps/Common/test/unit/Messaging/AzureServiceBusTests.cs @@ -67,9 +67,9 @@ public async Task ValidateSendAsync(int messageCount, int batchSize, int expecte // Arrange IEnumerable allMessages = [ - new MessageEnvelope(AccountCreatedEvent, AccountCreatedEvent.Hdid) { CreatedOn = DateTime }, - new MessageEnvelope(AccountClosedEvent, AccountClosedEvent.Hdid) { CreatedOn = DateTime }, - new MessageEnvelope(DependentAddedEvent, DependentAddedEvent.DelegateHdid) { CreatedOn = DateTime }, + new(AccountCreatedEvent, AccountCreatedEvent.Hdid) { CreatedOn = DateTime }, + new(AccountClosedEvent, AccountClosedEvent.Hdid) { CreatedOn = DateTime }, + new(DependentAddedEvent, DependentAddedEvent.DelegateHdid) { CreatedOn = DateTime }, ]; IList messages = allMessages.Take(messageCount).ToList(); IList expectedMessages = messages.Select(m => new ServiceBusMessage { SessionId = m.SessionId, Body = new(m.Content.Serialize(false)) }).ToList(); @@ -236,14 +236,16 @@ private static SendAsyncSetup GetSendAsyncSetup(int messageCount, int batchSize) attemptedAddMessageCollections.Add(attemptedAddMessageCollection); int callCount = 0; - Func tryAddFunc = message => + + setupSequentialResult = setupSequentialResult.ReturnsAsync(ServiceBusModelFactory.ServiceBusMessageBatch(0, [], null, TryAddFunc)); + continue; + + bool TryAddFunc(ServiceBusMessage message) { bool added = callCount++ < batchSize; attemptedAddMessageCollection.Add((message, added)); return added; - }; - - setupSequentialResult = setupSequentialResult.ReturnsAsync(ServiceBusModelFactory.ServiceBusMessageBatch(0, [], null, tryAddFunc)); + } } setupSequentialResult.ThrowsAsync(new InvalidOperationException("More batches were created than expected.")); diff --git a/Apps/Common/test/unit/Services/AccessTokenServiceTests.cs b/Apps/Common/test/unit/Services/AccessTokenServiceTests.cs index fcd4d5a506..4ec3be3f3b 100644 --- a/Apps/Common/test/unit/Services/AccessTokenServiceTests.cs +++ b/Apps/Common/test/unit/Services/AccessTokenServiceTests.cs @@ -16,7 +16,6 @@ namespace HealthGateway.CommonTests.Services { using System.Collections.Generic; - using System.Linq; using System.Threading; using System.Threading.Tasks; using HealthGateway.Common.AccessManagement.Authentication; @@ -144,7 +143,7 @@ private static IConfigurationRoot GetIConfigurationRoot(bool cacheEnabled) } return new ConfigurationBuilder() - .AddInMemoryCollection(configuration.ToList()) + .AddInMemoryCollection(configuration) .Build(); } diff --git a/Apps/Common/test/unit/Services/CommunicationServiceTests.cs b/Apps/Common/test/unit/Services/CommunicationServiceTests.cs index 1bbdc387c6..439862a579 100644 --- a/Apps/Common/test/unit/Services/CommunicationServiceTests.cs +++ b/Apps/Common/test/unit/Services/CommunicationServiceTests.cs @@ -77,7 +77,11 @@ public async Task ShouldGetActiveCommunicationFromCache(Scenario scenario, Commu EffectiveDateTime = scenario switch { Scenario.Future => DateTime.UtcNow.AddDays(3), - _ => DateTime.UtcNow, + Scenario.Active => DateTime.UtcNow, + Scenario.Expired => DateTime.UtcNow, + Scenario.Deleted => DateTime.UtcNow, + Scenario.DifferentCache => DateTime.UtcNow, + _ => throw new ArgumentOutOfRangeException(nameof(scenario), scenario, "Unhandled scenario value"), }, }; @@ -100,9 +104,15 @@ public async Task ShouldGetActiveCommunicationFromCache(Scenario scenario, Commu Assert.Equal(ResultType.Success, actualResult.ResultStatus); break; - default: + case Scenario.Active: + case Scenario.Expired: + case Scenario.Deleted: + case Scenario.DifferentCache: Assert.Equal(commResult.ResourcePayload!.Id, actualResult.ResourcePayload!.Id); break; + + default: + throw new ArgumentOutOfRangeException(nameof(scenario), $"Unexpected scenario: {scenario}"); } } @@ -221,6 +231,9 @@ public void ShouldProcessChangeEvent(string action, Scenario scenario, bool cach communication.ExpiryDateTime = DateTime.Now.AddDays(5); break; + case Scenario.Active: + case Scenario.Deleted: + case Scenario.DifferentCache: default: communication.EffectiveDateTime = DateTime.Now; communication.ExpiryDateTime = DateTime.Now.AddDays(1); @@ -306,6 +319,9 @@ public void ShouldProcessChangeEvent(string action, Scenario scenario, bool cach Assert.Equal(ResultType.Success, cacheResult!.ResultStatus); Assert.Equal(1, cacheResult.TotalResultCount); break; + + default: + throw new InvalidOperationException($"Unhandled scenario: {scenario}"); } } diff --git a/Apps/Common/test/unit/Services/PatientServiceTests.cs b/Apps/Common/test/unit/Services/PatientServiceTests.cs index cb73245055..0d6849c79b 100644 --- a/Apps/Common/test/unit/Services/PatientServiceTests.cs +++ b/Apps/Common/test/unit/Services/PatientServiceTests.cs @@ -227,7 +227,7 @@ public async Task ShouldThrowIfInvalidIdentifierType() } /// - /// GetPatient - Returns action required when identifer is null. + /// GetPatient - Returns action required when identifier is null. /// /// The identifier type used to query with. /// @@ -297,7 +297,7 @@ private static async Task GetPatientAsync(PatientIdentifierType identifierType, Mock patientDelegateMock = new(); IConfigurationRoot config = new ConfigurationBuilder() - .AddInMemoryCollection(configDictionary.ToList()) + .AddInMemoryCollection(configDictionary) .Build(); patientDelegateMock.Setup(p => p.GetDemographicsByHdidAsync(It.IsAny(), false, It.IsAny())).ReturnsAsync(requestResult); diff --git a/Apps/Common/test/unit/Services/PersonalAccountServiceTests.cs b/Apps/Common/test/unit/Services/PersonalAccountServiceTests.cs index f95b473c04..5497267d55 100644 --- a/Apps/Common/test/unit/Services/PersonalAccountServiceTests.cs +++ b/Apps/Common/test/unit/Services/PersonalAccountServiceTests.cs @@ -17,7 +17,6 @@ namespace HealthGateway.CommonTests.Services { using System; using System.Collections.Generic; - using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -101,7 +100,7 @@ private static IConfigurationRoot GetIConfigurationRoot(string cacheToLive = "90 }; return new ConfigurationBuilder() - .AddInMemoryCollection(configuration.ToList()) + .AddInMemoryCollection(configuration) .Build(); } diff --git a/Apps/Common/test/unit/Utils/DateOnlyJsonConverterTests.cs b/Apps/Common/test/unit/Utils/DateOnlyJsonConverterTests.cs index 957377e2e9..5bc082ce66 100644 --- a/Apps/Common/test/unit/Utils/DateOnlyJsonConverterTests.cs +++ b/Apps/Common/test/unit/Utils/DateOnlyJsonConverterTests.cs @@ -55,7 +55,7 @@ public void ShouldDeserialize() Converters = { new DateOnlyJsonConverter() }, }; - object? actualResult = JsonSerializer.Deserialize($@"""{dateStr}""", typeof(DateOnly), options); + object? actualResult = JsonSerializer.Deserialize($@"""{dateStr}""", options); Assert.IsType(actualResult); Assert.True(date.Equals(actualResult)); diff --git a/Apps/Common/test/unit/Utils/HttpContextHelperTests.cs b/Apps/Common/test/unit/Utils/HttpContextHelperTests.cs index dd84921f74..4d52820793 100644 --- a/Apps/Common/test/unit/Utils/HttpContextHelperTests.cs +++ b/Apps/Common/test/unit/Utils/HttpContextHelperTests.cs @@ -1,18 +1,18 @@ -//------------------------------------------------------------------------- -// Copyright © 2019 Province of British Columbia +// ------------------------------------------------------------------------- +// Copyright © 2019 Province of British Columbia // -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -//------------------------------------------------------------------------- +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ------------------------------------------------------------------------- namespace HealthGateway.CommonTests.Utils { using System; @@ -61,7 +61,7 @@ public void ShouldGetResourceHdid(FhirSubjectLookupMethod lookupMethod) Assert.Equal(expected, actual); } - [SuppressMessage("ReSharper", "SwitchStatementHandlesSomeKnownEnumValuesWithDefault", Justification = "Unknown enum value expected and is handled in mock setup.")] + [SuppressMessage("Style", "IDE0010:Populate switch", Justification = "Team decision")] private static Mock SetupHttpContextForGetResourceHdid(FhirSubjectLookupMethod lookupMethod) { Mock httpRequestMock = new(); diff --git a/Apps/Common/test/unit/Utils/SerializationHelperTests.cs b/Apps/Common/test/unit/Utils/SerializationHelperTests.cs index 201ff9622a..cd0947e896 100644 --- a/Apps/Common/test/unit/Utils/SerializationHelperTests.cs +++ b/Apps/Common/test/unit/Utils/SerializationHelperTests.cs @@ -37,11 +37,11 @@ public class SerializationHelperTests [InlineData(typeof(string[]))] public void ShouldReturnDefaultOfGenericIfNoData(Type type) { - MethodInfo? deserializerRef = typeof(SerializationHelper).GetMethod(nameof(SerializationHelper.Deserialize), 2, new[] { typeof(byte[]), typeof(JsonSerializerOptions) }) + MethodInfo? deserializerRef = typeof(SerializationHelper).GetMethod(nameof(SerializationHelper.Deserialize), 2, [typeof(byte[]), typeof(JsonSerializerOptions)]) ?.MakeGenericMethod(type); if (deserializerRef != null) { - byte[] data = Array.Empty(); + byte[] data = []; Assert.Equal(Activator.CreateInstance(type), deserializerRef.Invoke(data, null)); byte[]? dataNull = null; Assert.Equal(Activator.CreateInstance(type), deserializerRef.Invoke(dataNull, null)); @@ -49,13 +49,13 @@ public void ShouldReturnDefaultOfGenericIfNoData(Type type) } /// - /// Non generic deserialize should return default of Object? when data is or empty which is null. + /// Non-generic deserialize should return default of Object? when data is or empty which is null. /// [Fact] public void ShouldReturnNullIfNoData() { Type anyType = typeof(string); - byte[] data = Array.Empty(); + byte[] data = []; byte[]? dataNull = null; Assert.Null(data.Deserialize(anyType)); Assert.Null(dataNull.Deserialize(anyType)); @@ -89,7 +89,7 @@ public void ShouldReturnEmptyByteArrayWhenNull() [Fact] public void ShouldSerializeWhetherConcreteOrGeneric() { - string anyObj = "SomeValue"; + const string anyObj = "SomeValue"; // serialize and deserialize using concrete byte[] serialized = anyObj.Serialize(); @@ -98,7 +98,7 @@ public void ShouldSerializeWhetherConcreteOrGeneric() // serialize and deserialize using generic byte[] serialized2 = anyObj.Serialize(false); - string? deserialized2 = serialized2.Deserialize(typeof(string)) as string; + string? deserialized2 = serialized2.Deserialize(); Assert.Equal(anyObj, deserialized2); // serialize results should be equivalent. diff --git a/Apps/Common/test/unit/Utils/TimeOnlyJsonConverterTests.cs b/Apps/Common/test/unit/Utils/TimeOnlyJsonConverterTests.cs index 97b80d47d3..af192f2591 100644 --- a/Apps/Common/test/unit/Utils/TimeOnlyJsonConverterTests.cs +++ b/Apps/Common/test/unit/Utils/TimeOnlyJsonConverterTests.cs @@ -57,7 +57,7 @@ public void ShouldDeserialize() Converters = { new TimeOnlyJsonConverter() }, }; - object? actualResult = JsonSerializer.Deserialize($@"""{timeStr}""", typeof(TimeOnly), options); + object? actualResult = JsonSerializer.Deserialize($@"""{timeStr}""", options); Assert.IsType(actualResult); Assert.True(time.Equals(actualResult)); diff --git a/Apps/Common/test/unit/Validations/VaccineStatusQueryValidatorTests.cs b/Apps/Common/test/unit/Validations/VaccineStatusQueryValidatorTests.cs index aa55709463..e663bb6567 100644 --- a/Apps/Common/test/unit/Validations/VaccineStatusQueryValidatorTests.cs +++ b/Apps/Common/test/unit/Validations/VaccineStatusQueryValidatorTests.cs @@ -17,6 +17,7 @@ namespace HealthGateway.CommonTests.Validations { using System; + using FluentValidation.Results; using HealthGateway.Common.Models.PHSA; using HealthGateway.Common.Validations; using Xunit; @@ -38,13 +39,14 @@ public class VaccineStatusQueryValidatorTests [InlineData("2000-01-01", "2021-01-01", "", "PHN is empty")] public void ShouldNotBeValid(DateTime dob, DateTime dov, string phn, string reason) { - var validator = new VaccineStatusQueryValidator(); - var validationResult = validator.Validate(new VaccineStatusQuery - { - DateOfBirth = dob, - DateOfVaccine = dov, - PersonalHealthNumber = phn, - }); + VaccineStatusQueryValidator validator = new(); + ValidationResult? validationResult = validator.Validate( + new VaccineStatusQuery + { + DateOfBirth = dob, + DateOfVaccine = dov, + PersonalHealthNumber = phn, + }); Assert.False(validationResult.IsValid, reason); } @@ -59,13 +61,14 @@ public void ShouldNotBeValid(DateTime dob, DateTime dov, string phn, string reas [InlineData("2000-01-01", "2021-01-01", "9735353315")] public void ShouldBeValid(DateTime dob, DateTime dov, string phn) { - var validator = new VaccineStatusQueryValidator(); - var validationResult = validator.Validate(new VaccineStatusQuery - { - DateOfBirth = dob, - DateOfVaccine = dov, - PersonalHealthNumber = phn, - }); + VaccineStatusQueryValidator validator = new(); + ValidationResult? validationResult = validator.Validate( + new VaccineStatusQuery + { + DateOfBirth = dob, + DateOfVaccine = dov, + PersonalHealthNumber = phn, + }); Assert.True(validationResult.IsValid); } diff --git a/Apps/CommonData/src/Common.Data.csproj b/Apps/CommonData/src/Common.Data.csproj index 182ebe94f3..1f22b400a2 100644 --- a/Apps/CommonData/src/Common.Data.csproj +++ b/Apps/CommonData/src/Common.Data.csproj @@ -11,7 +11,7 @@ - + diff --git a/Apps/CommonData/src/Models/Address.cs b/Apps/CommonData/src/Models/Address.cs index 009006e60a..649093c70a 100644 --- a/Apps/CommonData/src/Models/Address.cs +++ b/Apps/CommonData/src/Models/Address.cs @@ -16,7 +16,6 @@ namespace HealthGateway.Common.Data.Models { using System.Collections.Generic; - using System.Linq; /// /// Represents an address. @@ -26,7 +25,7 @@ public class Address /// /// Gets or sets the street lines. /// - public IEnumerable StreetLines { get; set; } = Enumerable.Empty(); + public IEnumerable StreetLines { get; set; } = []; /// /// Gets or sets the city name. diff --git a/Apps/CommonData/src/Utils/AddressUtility.cs b/Apps/CommonData/src/Utils/AddressUtility.cs index 9da47cc400..d478cdf054 100644 --- a/Apps/CommonData/src/Utils/AddressUtility.cs +++ b/Apps/CommonData/src/Utils/AddressUtility.cs @@ -240,7 +240,7 @@ public static string GetAddressAsSingleLine(Address? address, bool includeCountr } IEnumerable addressElements = address.StreetLines - .Concat(new[] { address.City, address.State, address.PostalCode }); + .Concat([address.City, address.State, address.PostalCode]); if (includeCountry) { diff --git a/Apps/CommonData/src/Utils/EnumUtility.cs b/Apps/CommonData/src/Utils/EnumUtility.cs index cf2899f55a..f451fe6b3d 100644 --- a/Apps/CommonData/src/Utils/EnumUtility.cs +++ b/Apps/CommonData/src/Utils/EnumUtility.cs @@ -45,7 +45,7 @@ public static string ToEnumString(T instance, bool useAttribute = false) if (attr != null) { // if there's no EnumMember attr, use the default value - enumString = attr.Value !; + enumString = attr.Value!; } } } diff --git a/Apps/CommonData/src/Validations/PhnValidator.cs b/Apps/CommonData/src/Validations/PhnValidator.cs index 6e3edd691e..ee1b4787c2 100644 --- a/Apps/CommonData/src/Validations/PhnValidator.cs +++ b/Apps/CommonData/src/Validations/PhnValidator.cs @@ -26,7 +26,7 @@ namespace HealthGateway.Common.Data.Validations /// public class PhnValidator : AbstractValidator { - private static readonly int[] PhnSigDigits = { 2, 4, 8, 5, 10, 9, 7, 3 }; + private static readonly int[] PhnSigDigits = [2, 4, 8, 5, 10, 9, 7, 3]; /// /// Initializes a new instance of the class. @@ -49,7 +49,7 @@ public static bool IsValid(string? phn) private static bool IsValidInternal(string? phn) { - if (string.IsNullOrEmpty(phn) || phn.Length != 10 || !phn.All(c => char.IsDigit(c)) || phn[0] != '9') + if (string.IsNullOrEmpty(phn) || phn.Length != 10 || !phn.All(char.IsDigit) || phn[0] != '9') { return false; } diff --git a/Apps/CommonData/test/unit/CommonDataTests.csproj b/Apps/CommonData/test/unit/CommonDataTests.csproj index 9db6427111..821f4dbe33 100644 --- a/Apps/CommonData/test/unit/CommonDataTests.csproj +++ b/Apps/CommonData/test/unit/CommonDataTests.csproj @@ -10,20 +10,20 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all - + - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/Apps/CommonData/test/unit/Extensions/ClaimsPrincipalExtensionsTests.cs b/Apps/CommonData/test/unit/Extensions/ClaimsPrincipalExtensionsTests.cs index 288104b31f..adf72bd21d 100644 --- a/Apps/CommonData/test/unit/Extensions/ClaimsPrincipalExtensionsTests.cs +++ b/Apps/CommonData/test/unit/Extensions/ClaimsPrincipalExtensionsTests.cs @@ -43,7 +43,7 @@ public class ClaimsPrincipalExtensionsTests public void ValidateToEnumString(string[] userRoles, string[] targetRoles, bool expected) { IEnumerable claims = userRoles.Select(r => new Claim(ClaimsIdentity.DefaultRoleClaimType, r)); - ClaimsPrincipal user = new(new[] { new ClaimsIdentity(claims) }); + ClaimsPrincipal user = new([new ClaimsIdentity(claims)]); bool actual = user.IsInAnyRole(targetRoles); diff --git a/Apps/CommonData/test/unit/Utils/DateFormatterTests.cs b/Apps/CommonData/test/unit/Utils/DateFormatterTests.cs index 532ca38050..c2fea27436 100644 --- a/Apps/CommonData/test/unit/Utils/DateFormatterTests.cs +++ b/Apps/CommonData/test/unit/Utils/DateFormatterTests.cs @@ -17,7 +17,6 @@ namespace HealthGateway.Common.Data.Tests.Utils { using System; using System.Collections.Generic; - using System.Linq; using HealthGateway.Common.Data.Utils; using Microsoft.Extensions.Configuration; using Xunit; @@ -211,7 +210,7 @@ private static IConfigurationRoot GetIConfigurationRoot(string unixLocalTimeZone }; return new ConfigurationBuilder() - .AddInMemoryCollection(myConfiguration.ToList()) + .AddInMemoryCollection(myConfiguration) .Build(); } } diff --git a/Apps/CommonUi/test/unit/CommonUiTests.csproj b/Apps/CommonUi/test/unit/CommonUiTests.csproj index bbff6364a5..75d956600e 100644 --- a/Apps/CommonUi/test/unit/CommonUiTests.csproj +++ b/Apps/CommonUi/test/unit/CommonUiTests.csproj @@ -10,19 +10,19 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/Apps/CommonUi/test/unit/Utils/IJSRuntimeExtensionMethodsTests.cs b/Apps/CommonUi/test/unit/Utils/IJSRuntimeExtensionMethodsTests.cs index 769fbd8878..d99ef783ab 100644 --- a/Apps/CommonUi/test/unit/Utils/IJSRuntimeExtensionMethodsTests.cs +++ b/Apps/CommonUi/test/unit/Utils/IJSRuntimeExtensionMethodsTests.cs @@ -42,9 +42,10 @@ public async Task ShouldInitializeInactivityTimer() jsRuntime.Verify( a => a.InvokeAsync( - It.Is(s => - s.Equals("initializeInactivityTimer", StringComparison.OrdinalIgnoreCase)), - It.IsAny()), + It.Is( + s => + s.Equals("initializeInactivityTimer", StringComparison.OrdinalIgnoreCase)), + It.IsAny()), Times.Once); } } diff --git a/Apps/DBMaintainer/Apps/BCPProvDrugDBApp.cs b/Apps/DBMaintainer/Apps/BCPProvDrugDBApp.cs index 292d3e879c..1ecbc7ff0e 100644 --- a/Apps/DBMaintainer/Apps/BCPProvDrugDBApp.cs +++ b/Apps/DBMaintainer/Apps/BCPProvDrugDBApp.cs @@ -120,10 +120,12 @@ protected override async Task ProcessDownloadAsync(string sourceFolder, FileDown /// Search for all download files matching this one. protected override void RemoveOldFiles(FileDownload downloadedFile) { - List oldIds = this.DrugDbContext.FileDownload - .Where(p => p.ProgramCode == downloadedFile.ProgramCode) - .Select(f => new FileDownload { Id = f.Id, ProgramCode = f.ProgramCode, Version = f.Version }) - .ToList(); + List oldIds = + [ + .. this.DrugDbContext.FileDownload + .Where(p => p.ProgramCode == downloadedFile.ProgramCode) + .Select(f => new FileDownload { Id = f.Id, ProgramCode = f.ProgramCode, Version = f.Version }), + ]; oldIds.ForEach(s => this.Logger.LogInformation("Deleting old file downloads with id: {Id} and program code: {ProgramCode}", s.Id, s.ProgramCode)); this.DrugDbContext.RemoveRange(oldIds); } @@ -131,12 +133,7 @@ protected override void RemoveOldFiles(FileDownload downloadedFile) private static string GetDownloadedFile(string sourceFolder, string filenamePattern) { string[] files = Directory.GetFiles(sourceFolder, filenamePattern); - if (files.Length > 1) - { - throw new FormatException($"The zip file contained {files.Length} CSV files, very confused."); - } - - return files[0]; + return files.Length > 1 ? throw new FormatException($"The zip file contained {files.Length} CSV files, very confused.") : files[0]; } private static void ModifyPharmaCareDrug(PharmacyAssessment pharmacyAssessment, IList pharmaCareDrugs) diff --git a/Apps/DBMaintainer/Apps/BaseDrugApp.cs b/Apps/DBMaintainer/Apps/BaseDrugApp.cs index 51f64be0b6..d819a829b6 100644 --- a/Apps/DBMaintainer/Apps/BaseDrugApp.cs +++ b/Apps/DBMaintainer/Apps/BaseDrugApp.cs @@ -140,10 +140,12 @@ protected async Task AddFileToDbAsync(FileDownload downloadedFile, CancellationT /// Search for all download files not matching this one. protected virtual void RemoveOldFiles(FileDownload downloadedFile) { - List oldIds = this.DrugDbContext.FileDownload - .Where(p => p.ProgramCode == downloadedFile.ProgramCode && p.Hash != downloadedFile.Hash) - .Select(f => new FileDownload { Id = f.Id, Version = f.Version }) - .ToList(); + List oldIds = + [ + .. this.DrugDbContext.FileDownload + .Where(p => p.ProgramCode == downloadedFile.ProgramCode && p.Hash != downloadedFile.Hash) + .Select(f => new FileDownload { Id = f.Id, Version = f.Version }), + ]; oldIds.ForEach(s => this.Logger.LogInformation("Deleting old Download file with hash: {Hash}", s.Hash)); this.DrugDbContext.RemoveRange(oldIds); } diff --git a/Apps/DBMaintainer/DBMaintainer.csproj b/Apps/DBMaintainer/DBMaintainer.csproj index 6bf1c39f25..c3e15316f1 100644 --- a/Apps/DBMaintainer/DBMaintainer.csproj +++ b/Apps/DBMaintainer/DBMaintainer.csproj @@ -16,16 +16,16 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - - - - + + + + + + diff --git a/Apps/DBMaintainer/Mappers/Converters/BooleanConverter.cs b/Apps/DBMaintainer/Mappers/Converters/BooleanConverter.cs index 68d11b8867..dadb9d9fb1 100644 --- a/Apps/DBMaintainer/Mappers/Converters/BooleanConverter.cs +++ b/Apps/DBMaintainer/Mappers/Converters/BooleanConverter.cs @@ -27,12 +27,7 @@ public class BooleanConverter : DefaultTypeConverter /// public override object? ConvertFromString(string? text, IReaderRow row, MemberMapData memberMapData) { - if (TryConvertToBool(text, out bool result)) - { - return result; - } - - return null; + return TryConvertToBool(text, out bool result) ? result : null; } private static bool TryConvertToBool(string text, out bool result) diff --git a/Apps/DBMaintainer/Mappers/PharmacyAssessmentMapper.cs b/Apps/DBMaintainer/Mappers/PharmacyAssessmentMapper.cs index e362aa10ef..70d086164f 100644 --- a/Apps/DBMaintainer/Mappers/PharmacyAssessmentMapper.cs +++ b/Apps/DBMaintainer/Mappers/PharmacyAssessmentMapper.cs @@ -30,7 +30,9 @@ public sealed class PharmacyAssessmentMapper : ClassMap /// Performs the mapping of read Pharmacy Assessment file to the db model. /// /// The fileDownload to map. - [SuppressMessage("ReSharper", "UnusedParameter.Local", Justification = "Required by ClassMap")] + [SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Required by ClassMap")] + + // ReSharper disable once UnusedParameter.Local public PharmacyAssessmentMapper(FileDownload fileDownload) { this.Map(m => m.Pin).Index(0); diff --git a/Apps/DBMaintainer/Parsers/FederalDrugProductParser.cs b/Apps/DBMaintainer/Parsers/FederalDrugProductParser.cs index 00ff2ecf19..afe2a79fbb 100644 --- a/Apps/DBMaintainer/Parsers/FederalDrugProductParser.cs +++ b/Apps/DBMaintainer/Parsers/FederalDrugProductParser.cs @@ -241,12 +241,7 @@ public IList ParseVeterinarySpeciesFile(string filePath, IEnu private static string GetFileMatching(string sourceFolder, string fileMatch) { string[] files = Directory.GetFiles(sourceFolder, fileMatch); - if (files.Length > 1 || files.Length == 0) - { - throw new FormatException($"The zip file contained {files.Length} CSV files, very confused."); - } - - return files[0]; + return files.Length is > 1 or 0 ? throw new FormatException($"The zip file contained {files.Length} CSV files, very confused.") : files[0]; } } } diff --git a/Apps/DBMaintainer/Program.cs b/Apps/DBMaintainer/Program.cs index 76c2b3fda5..2a063f3b5f 100644 --- a/Apps/DBMaintainer/Program.cs +++ b/Apps/DBMaintainer/Program.cs @@ -74,7 +74,7 @@ public static async Task Main(string[] args) /// /// The IHostBuilder. /// The set of command line arguments. - [SuppressMessage("Usage", "CA1801:Review unused parameters", Justification = "Required for migrations")] + [SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Required for migrations.")] public static IHostBuilder CreateWebHostBuilder(string[] args) { string environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Development"; diff --git a/Apps/Database/src/Database.csproj b/Apps/Database/src/Database.csproj index acf7869f2e..6d286a454a 100644 --- a/Apps/Database/src/Database.csproj +++ b/Apps/Database/src/Database.csproj @@ -5,8 +5,8 @@ - - + + diff --git a/Apps/Database/src/Delegates/DbBetaFeatureAccessDelegate.cs b/Apps/Database/src/Delegates/DbBetaFeatureAccessDelegate.cs index 27b33d87c2..f6816df3b5 100644 --- a/Apps/Database/src/Delegates/DbBetaFeatureAccessDelegate.cs +++ b/Apps/Database/src/Delegates/DbBetaFeatureAccessDelegate.cs @@ -82,7 +82,7 @@ public async Task>> GetAllA int totalCount = await emailQuery.CountAsync(ct); - IList emailAddresses = await emailQuery + List emailAddresses = await emailQuery .OrderBy(p => p) .Skip(pageIndex * pageSize) .Take(pageSize) diff --git a/Apps/Database/src/Delegates/DbResourceDelegateDelegate.cs b/Apps/Database/src/Delegates/DbResourceDelegateDelegate.cs index 77a5b279ef..fc20d14948 100644 --- a/Apps/Database/src/Delegates/DbResourceDelegateDelegate.cs +++ b/Apps/Database/src/Delegates/DbResourceDelegateDelegate.cs @@ -214,20 +214,23 @@ public async Task SearchAsync(ResourceDelegateQuery dbQuery = dbQuery.Take(query.TakeAmount.Value); } - IList items; - if (query.IncludeDependent) - { - items = await dbQuery.GroupJoin( + IList items = query.IncludeDependent + ? await dbQuery.GroupJoin( dbContext.Dependent, rd => rd.ResourceOwnerHdid, d => d.HdId, - (rd, d) => new ResourceDelegateQueryResultItem { ResourceDelegate = rd, Dependent = d.FirstOrDefault() }) + (rd, d) => new ResourceDelegateQueryResultItem + { + ResourceDelegate = rd, + Dependent = d.FirstOrDefault(), + }) + .ToListAsync(ct) + : await dbQuery.Select( + rd => new ResourceDelegateQueryResultItem + { + ResourceDelegate = rd, + }) .ToListAsync(ct); - } - else - { - items = await dbQuery.Select(rd => new ResourceDelegateQueryResultItem { ResourceDelegate = rd }).ToListAsync(ct); - } return new() { Items = items }; } diff --git a/Apps/Database/src/Wrapper/DBDelegateHelper.cs b/Apps/Database/src/Wrapper/DBDelegateHelper.cs index b12a31d241..5e8ed0e51e 100644 --- a/Apps/Database/src/Wrapper/DBDelegateHelper.cs +++ b/Apps/Database/src/Wrapper/DBDelegateHelper.cs @@ -19,7 +19,6 @@ namespace HealthGateway.Database.Wrapper using System.Linq; using System.Threading; using System.Threading.Tasks; - using HealthGateway.Database.Constants; using Microsoft.EntityFrameworkCore; /// @@ -27,26 +26,6 @@ namespace HealthGateway.Database.Wrapper /// public static class DbDelegateHelper { - /// - /// Gets a list of DBModel records for a specific page. - /// - /// A query that needs to be paged. - /// The starting offset for the query. - /// The maximum amount of rows to return. - /// A DBModel type. - /// A list of DBModel records wrapped in a DBResult. - public static DbResult> GetPagedDbResult(IQueryable query, int page, int pageSize) - where T : class - { - int offset = page * pageSize; - DbResult> result = new() - { - Payload = query.Skip(offset).Take(pageSize).ToList(), - }; - result.Status = DbStatusCode.Read; - return result; - } - /// /// Gets a list of DBModel records for a specific page. /// diff --git a/Apps/Database/test/unit/DatabaseTests.csproj b/Apps/Database/test/unit/DatabaseTests.csproj index b099c46844..06a4d6a395 100644 --- a/Apps/Database/test/unit/DatabaseTests.csproj +++ b/Apps/Database/test/unit/DatabaseTests.csproj @@ -20,22 +20,22 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all - - + + - - + + - + runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/Apps/Directory.Build.props b/Apps/Directory.Build.props index 633ba34147..080b8df71b 100644 --- a/Apps/Directory.Build.props +++ b/Apps/Directory.Build.props @@ -10,7 +10,7 @@ true - + - **/node_modules/**/*,**/wwwroot/**/*,**/ClientApp/public/**/*,**/Startup.cs + $(Build.SourcesDirectory)/Apps + **/node_modules/**/*,**/wwwroot/**/*,**/ClientApp/public/**/*,**/Startup.cs,**/Testing/**,**/Tools/** **/test/**/*Tests.cs,**/Tests/**/*Tests.cs **/*Controller.cs,**/Models/**/*,**/Admin/Client/**/*,**/Database/**/*,**/DBMaintainer/**/*,**/JobScheduler/**/*,**/ClientApp/**/* https://sonarcloud.io diff --git a/Testing/functional/tests/cypress/integration/e2e/dependent/dashboard.js b/Testing/functional/tests/cypress/integration/e2e/dependent/dashboard.js index 4a78d14ef1..6202771a3f 100644 --- a/Testing/functional/tests/cypress/integration/e2e/dependent/dashboard.js +++ b/Testing/functional/tests/cypress/integration/e2e/dependent/dashboard.js @@ -1,4 +1,6 @@ -const { AuthMethod } = require("../../../support/constants"); +import { AuthMethod } from "../../../support/constants"; + +const defaultTimeout = 120000; const validDependent = { hdid: "162346565465464564565463257", @@ -104,14 +106,17 @@ describe("dependents - dashboard", () => { .should("be.visible", "be.enabled") .click(); cy.get(recommendationsDownloadPdfButtonSelector).first().click(); - cy.get(confirmationModalButton).click(); - cy.verifyDownload( - "HealthGatewayDependentImmunizationRecommendationReport.pdf", - { - timeout: 60000, - interval: 5000, - } - ); + cy.intercept("POST", "**/Report").as("postReport"); + cy.get(confirmationModalButton).click(); + cy.wait("@postReport", { timeout: defaultTimeout }).then(() => { + cy.verifyDownload( + "HealthGatewayDependentImmunizationRecommendationReport.pdf", + { + timeout: 60000, + interval: 5000, + } + ); + }); }); }); diff --git a/Testing/functional/tests/cypress/integration/e2e/report/report.js b/Testing/functional/tests/cypress/integration/e2e/report/report.js index 29af2e02dc..8cc183602e 100644 --- a/Testing/functional/tests/cypress/integration/e2e/report/report.js +++ b/Testing/functional/tests/cypress/integration/e2e/report/report.js @@ -106,7 +106,9 @@ describe("Reports", () => { cy.get("[data-testid=export-record-btn]").click(); - cy.get("[data-testid=export-record-menu] .v-list-item").first().click(); + cy.get("[data-testid=export-record-menu] .v-list-item") + .first() + .click({ force: true }); cy.get("[data-testid=generic-message-modal]").should("be.visible"); diff --git a/Testing/functional/tests/cypress/integration/e2e/user/dependents.js b/Testing/functional/tests/cypress/integration/e2e/user/dependents.js index e89fead207..a287aea960 100644 --- a/Testing/functional/tests/cypress/integration/e2e/user/dependents.js +++ b/Testing/functional/tests/cypress/integration/e2e/user/dependents.js @@ -1,6 +1,6 @@ import { AuthMethod } from "../../../support/constants"; -const defaultTimeout = 60000; +const defaultTimeout = 120000; function triggerEmptyValidation(vuetifySelector) { cy.get(vuetifySelector + " input") @@ -359,63 +359,83 @@ describe("dependents", () => { cy.get( `[data-testid=download-immunization-history-report-pdf-btn-${validDependentHdid}]` ) - .should("be.visible", "be.enabled") - .click(); - - // Confirmation modal - cy.get("[data-testid=generic-message-modal]").should("be.visible"); - cy.get("[data-testid=generic-message-submit-btn]").click(); - - cy.verifyDownload("HealthGatewayDependentImmunizationReport.pdf", { - timeout: 60000, - interval: 5000, - }); - - // Click download dropdown under History tab - cy.get( - `[data-testid=download-immunization-history-report-btn-${validDependentHdid}]` - ) - .should("be.visible", "be.enabled") - .click(); - - // Click CSV - cy.get( - `[data-testid=download-immunization-history-report-csv-btn-${validDependentHdid}]` - ) - .should("be.visible", "be.enabled") - .click(); - - // Confirmation modal - cy.get("[data-testid=generic-message-modal]").should("be.visible"); - cy.get("[data-testid=generic-message-submit-btn]").click(); - - cy.verifyDownload("HealthGatewayDependentImmunizationReport.csv", { - timeout: 60000, - interval: 5000, - }); - - // Click download dropdown under History tab - cy.get( - `[data-testid=download-immunization-history-report-btn-${validDependentHdid}]` - ) - .should("be.visible", "be.enabled") - .click(); - - // Click XLSX - cy.get( - `[data-testid=download-immunization-history-report-xlsx-btn-${validDependentHdid}]` - ) - .should("be.visible", "be.enabled") + .should("be.visible") .click(); - // Confirmation modal + // Confirmation modal for PDF + cy.intercept("POST", "**/Report").as("postReport"); cy.get("[data-testid=generic-message-modal]").should("be.visible"); cy.get("[data-testid=generic-message-submit-btn]").click(); - - cy.verifyDownload("HealthGatewayDependentImmunizationReport.xlsx", { - timeout: 60000, - interval: 5000, - }); + cy.wait("@postReport", { timeout: defaultTimeout }) + .then(() => { + cy.verifyDownload( + "HealthGatewayDependentImmunizationReport.pdf", + { + timeout: 60000, + interval: 5000, + } + ); + }) + .then(() => { + // Click download dropdown under History tab for CSV + cy.get( + `[data-testid=download-immunization-history-report-btn-${validDependentHdid}]` + ) + .should("be.visible", "be.enabled") + .click(); + + // Click CSV + cy.get( + `[data-testid=download-immunization-history-report-csv-btn-${validDependentHdid}]` + ) + .should("be.visible") + .click(); + + // Confirmation modal for CSV + cy.get("[data-testid=generic-message-modal]").should( + "be.visible" + ); + cy.get("[data-testid=generic-message-submit-btn]").click(); + cy.wait("@postReport", { timeout: defaultTimeout }).then(() => { + cy.verifyDownload( + "HealthGatewayDependentImmunizationReport.csv", + { + timeout: 60000, + interval: 5000, + } + ); + }); + }) + .then(() => { + // Click download dropdown under History tab for XLSX + cy.get( + `[data-testid=download-immunization-history-report-btn-${validDependentHdid}]` + ) + .should("be.visible", "be.enabled") + .click(); + + // Click XLSX + cy.get( + `[data-testid=download-immunization-history-report-xlsx-btn-${validDependentHdid}]` + ) + .should("be.visible") + .click(); + + // Confirmation modal for XLSX + cy.get("[data-testid=generic-message-modal]").should( + "be.visible" + ); + cy.get("[data-testid=generic-message-submit-btn]").click(); + cy.wait("@postReport", { timeout: defaultTimeout }).then(() => { + cy.verifyDownload( + "HealthGatewayDependentImmunizationReport.xlsx", + { + timeout: 60000, + interval: 5000, + } + ); + }); + }); }); it("Validate Immunization Forecast - Verify result and download", () => { @@ -454,64 +474,84 @@ describe("dependents", () => { // Click PDF cy.get( `[data-testid=download-immunization-forecast-report-pdf-btn-${validDependentHdid}]` - ) - .should("be.visible", "be.enabled") - .click({ force: true }); - - // Confirmation modal - cy.get("[data-testid=generic-message-modal]").should("be.visible"); - cy.get("[data-testid=generic-message-submit-btn]").click(); - - cy.verifyDownload("HealthGatewayDependentImmunizationReport.pdf", { - timeout: 60000, - interval: 5000, - }); - - // Click download dropdown under Forecasts tab - cy.get( - `[data-testid=download-immunization-forecast-report-btn-${validDependentHdid}` - ) - .should("be.visible", "be.enabled") - .click(); - - // Click CSV - cy.get( - `[data-testid=download-immunization-forecast-report-csv-btn-${validDependentHdid}]` ) .should("be.visible") - .click({ force: true }); - - // Confirmation modal - cy.get("[data-testid=generic-message-modal]").should("be.visible"); - cy.get("[data-testid=generic-message-submit-btn]").click(); - - cy.verifyDownload("HealthGatewayDependentImmunizationReport.csv", { - timeout: 60000, - interval: 5000, - }); - - // Click download dropdown under Forecasts tab - cy.get( - `[data-testid=download-immunization-forecast-report-btn-${validDependentHdid}]` - ) - .should("be.visible", "be.enabled") - .click({ force: true }); - - // Click XLSX - cy.get( - `[data-testid=download-immunization-forecast-report-xlsx-btn-${validDependentHdid}]` - ) - .should("be.visible") - .click({ force: true }); + .click(); - // Confirmation modal + // Confirmation modal for PDF + cy.intercept("POST", "**/Report").as("postReport"); cy.get("[data-testid=generic-message-modal]").should("be.visible"); cy.get("[data-testid=generic-message-submit-btn]").click(); - - cy.verifyDownload("HealthGatewayDependentImmunizationReport.xlsx", { - timeout: 60000, - interval: 5000, - }); + cy.wait("@postReport", { timeout: defaultTimeout }) + .then(() => { + cy.verifyDownload( + "HealthGatewayDependentImmunizationReport.pdf", + { + timeout: 60000, + interval: 5000, + } + ); + }) + .then(() => { + // Click download dropdown under Forecasts tab for CSV + cy.get( + `[data-testid=download-immunization-forecast-report-btn-${validDependentHdid}]` + ) + .should("be.visible", "be.enabled") + .click(); + + // Click CSV + cy.get( + `[data-testid=download-immunization-forecast-report-csv-btn-${validDependentHdid}]` + ) + .should("be.visible") + .click(); + + // Confirmation modal for CSV + cy.get("[data-testid=generic-message-modal]").should( + "be.visible" + ); + cy.get("[data-testid=generic-message-submit-btn]").click(); + cy.wait("@postReport", { timeout: defaultTimeout }).then(() => { + cy.verifyDownload( + "HealthGatewayDependentImmunizationReport.csv", + { + timeout: 60000, + interval: 5000, + } + ); + }); + }) + .then(() => { + // Click download dropdown under Forecasts tab for XLSX + cy.get( + `[data-testid=download-immunization-forecast-report-btn-${validDependentHdid}]` + ) + .should("be.visible", "be.enabled") + .click(); + + // Click XLSX + cy.get( + `[data-testid=download-immunization-forecast-report-xlsx-btn-${validDependentHdid}]` + ) + .should("be.visible") + .click(); + + // Confirmation modal for XLSX + cy.get("[data-testid=generic-message-modal]").should( + "be.visible" + ); + cy.get("[data-testid=generic-message-submit-btn]").click(); + cy.wait("@postReport", { timeout: defaultTimeout }).then(() => { + cy.verifyDownload( + "HealthGatewayDependentImmunizationReport.xlsx", + { + timeout: 60000, + interval: 5000, + } + ); + }); + }); }); // test should be skipped until PHSA fixes test data for this dependent diff --git a/Testing/integration/tests/IntegrationTests.csproj b/Testing/integration/tests/IntegrationTests.csproj index 0c015d2b34..9a4e9325d6 100644 --- a/Testing/integration/tests/IntegrationTests.csproj +++ b/Testing/integration/tests/IntegrationTests.csproj @@ -23,20 +23,20 @@ - - - - + + + + - - - + + + - + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/Tools/Helm/Charts/healthgateway/templates/dc.tpl b/Tools/Helm/Charts/healthgateway/templates/deployment.tpl similarity index 85% rename from Tools/Helm/Charts/healthgateway/templates/dc.tpl rename to Tools/Helm/Charts/healthgateway/templates/deployment.tpl index b2ec4793ee..aad319b7a6 100644 --- a/Tools/Helm/Charts/healthgateway/templates/dc.tpl +++ b/Tools/Helm/Charts/healthgateway/templates/deployment.tpl @@ -1,5 +1,5 @@ -# deployment config template -{{- define "dc.tpl" }} +# deployment template +{{- define "deployment.tpl" }} {{- $top := index . 0 -}} {{- $context := index . 1 -}} {{- $name := printf "%s-%s" $top.Release.Name $context.name -}} @@ -9,32 +9,29 @@ {{- $protocol := ($context.protocol | default "tcp") -}} {{- $image := print $top.Values.defaultImageRepository "/" $top.Values.toolsNamespace "/" ($context.image.imageStreamName | default $context.name) -}} {{- $tag := $context.image.tag | default $top.Values.defaultImageTag | default "latest" -}} -{{- $replicas := (kindIs "float64" $context.replicas) | ternary $context.replicas (default ($top.Values.scaling).dcReplicas | required "dcReplicas required") -}} +{{- $replicas := (kindIs "float64" $context.replicas) | ternary $context.replicas (default ($top.Values.scaling).deploymentReplicas | required "deploymentReplicas required") -}} {{- $role := $context.role -}} -apiVersion: apps.openshift.io/v1 -kind: DeploymentConfig +apiVersion: apps/v1 +kind: Deployment metadata: - name: {{ $name }}-dc + name: {{ $name }}-deployment namespace: {{ $namespace }} - labels: {{ $labels | nindent 4 }} + labels: + {{- $labels | trim | nindent 4 }} role: {{ $role }} + annotations: + image.openshift.io/triggers: '[{"from":{"kind":"ImageStreamTag","name":"{{ $context.name }}:{{ $tag }}","namespace":"{{ $top.Values.toolsNamespace }}"},"fieldPath":"spec.template.spec.containers[?(@.name==\"{{ $name }}\")].image","pause":"false"}]' spec: replicas: {{ $replicas }} revisionHistoryLimit: 10 strategy: - type: Rolling - rollingParams: + type: RollingUpdate + rollingUpdate: maxUnavailable: 50% maxSurge: 50% - resources: - limits: - cpu: 15m - memory: 64Mi - requests: - cpu: 5m - memory: 32Mi selector: - name: {{ $name }} + matchLabels: + name: {{ $name }} template: metadata: name: {{ $name }} @@ -52,6 +49,7 @@ spec: {{ print "checksum/" $value.name "-secrets"}}: {{ $top.Files.Get $value.file | toYaml | sha256sum }} {{- end }} spec: + restartPolicy: Always containers: - name: {{ $name }} image: {{ $image }}:{{ $tag }} @@ -113,9 +111,6 @@ spec: timeoutSeconds: {{ $context.startupProbeTimeout | default 5 }} periodSeconds: {{ $context.startupProbePeriod | default 10 }} failureThreshold: {{ $context.startupProbeFailureThreshold | default 5 }} - dnsPolicy: ClusterFirst - restartPolicy: Always - terminationGracePeriodSeconds: 30 volumes: - name: dp persistentVolumeClaim: @@ -128,15 +123,4 @@ spec: configMap: name: {{ printf "%s-files" $top.Release.Name }} {{- end }} - triggers: - - type: ConfigChange - - type: ImageChange - imageChangeParams: - automatic: true - containerNames: - - {{ $name }} - from: - kind: ImageStreamTag - name: {{ $context.image.imageStreamName | default $context.name }}:{{ $tag }} - namespace: {{ $top.Values.toolsNamespace }} -{{- end }} +{{- end }} \ No newline at end of file diff --git a/Tools/Helm/Charts/healthgateway/templates/hpa.tpl b/Tools/Helm/Charts/healthgateway/templates/hpa.tpl index 2aec466f8f..51b4ee96db 100644 --- a/Tools/Helm/Charts/healthgateway/templates/hpa.tpl +++ b/Tools/Helm/Charts/healthgateway/templates/hpa.tpl @@ -3,7 +3,7 @@ {{- $top := index . 0 -}} {{- $context := index . 1 -}} {{- if or (or (not (hasKey $context "scaling")) (not (hasKey $context.scaling "enabled"))) ($context.scaling).enabled -}} - {{- $name := printf "%s-%s" $top.Release.Name $context.name -}} + {{- $name := printf "%s-%s-deployment" $top.Release.Name $context.name -}} {{- $namespace := $top.Values.namespace | default $top.Release.Namespace -}} {{- $labels := include "standard.labels" $top -}} {{- $minReplicas := ($context.scaling).hpaMinReplicas | default $top.Values.scaling.hpaMinReplicas | required "hpaMinReplicas required" -}} @@ -19,9 +19,9 @@ metadata: labels: {{ $labels | nindent 4 }} spec: scaleTargetRef: - kind: DeploymentConfig - name: {{ $name }}-dc - apiVersion: apps.openshift.io/v1 + kind: Deployment + name: {{ $name }}-deployment + apiVersion: apps/v1 minReplicas: {{ $minReplicas }} maxReplicas: {{ $maxReplicas }} metrics: @@ -39,4 +39,4 @@ spec: averageUtilization: {{ $memoryUtilization }} {{- end -}} {{- end -}} -{{- end -}} +{{- end -}} \ No newline at end of file diff --git a/Tools/Helm/Charts/healthgateway/templates/pdb.tpl b/Tools/Helm/Charts/healthgateway/templates/pdb.tpl index a0116e13be..d082fe2709 100644 --- a/Tools/Helm/Charts/healthgateway/templates/pdb.tpl +++ b/Tools/Helm/Charts/healthgateway/templates/pdb.tpl @@ -5,7 +5,7 @@ {{- $namespace := $top.Values.namespace | default $top.Release.Namespace -}} {{- $labels := include "standard.labels" $top -}} {{- $minAvailable := ($context.scaling).pdbMinAvailable | default ($top.Values.scaling).pdbMinAvailable | required "pdbMinAvailable required" -}} -{{- $replicas := (kindIs "float64" $context.replicas) | ternary $context.replicas (default ($top.Values.scaling).dcReplicas | required "dcReplicas required") -}} +{{- $replicas := (kindIs "float64" $context.replicas) | ternary $context.replicas (default ($top.Values.scaling).deploymentReplicas | required "deploymentReplicas required") -}} {{- if and (ne 0.0 $minAvailable) (gt $replicas 1.0) -}} kind: PodDisruptionBudget apiVersion: policy/v1 @@ -17,6 +17,6 @@ spec: minAvailable: {{ $minAvailable }} selector: matchLabels: - deploymentconfig: {{ $name }}-dc + name: {{ $name }} {{- end -}} {{- end -}} diff --git a/Tools/Helm/Charts/healthgateway/templates/services.yaml b/Tools/Helm/Charts/healthgateway/templates/services.yaml index b8b1438838..b5a79ec1c7 100644 --- a/Tools/Helm/Charts/healthgateway/templates/services.yaml +++ b/Tools/Helm/Charts/healthgateway/templates/services.yaml @@ -1,6 +1,6 @@ # creates each defined component using the templates {{- range $key, $value := .Values.components -}} - {{ include "dc.tpl" (list $ $value) | nindent 0 }} + {{ include "deployment.tpl" (list $ $value) | nindent 0 }} {{ "---" | nindent 0 }} {{ include "hpa.tpl" (list $ $value) | nindent 0 }} {{ "---" | nindent 0 }} diff --git a/Tools/KeyCloak/Terraform/authflow_Iapyx.tf b/Tools/KeyCloak/Terraform/authflow_Iapyx.tf new file mode 100644 index 0000000000..3a343c9928 --- /dev/null +++ b/Tools/KeyCloak/Terraform/authflow_Iapyx.tf @@ -0,0 +1,58 @@ +resource "keycloak_authentication_flow" "iapyx_login" { + count = local.devtest ? 1 : 0 + realm_id = data.keycloak_realm.hg_realm.id + alias = "iapyx Login" +} + +resource "keycloak_authentication_execution" "iapyx_cookie_execution" { + count = local.devtest ? 1 : 0 + realm_id = data.keycloak_realm.hg_realm.id + parent_flow_alias = local.devtest ? keycloak_authentication_flow.iapyx_login[0].alias : null + authenticator = "auth-cookie" + requirement = "ALTERNATIVE" +} + +resource "keycloak_authentication_execution" "iapyx_idp_redirector_execution" { + count = local.devtest ? 1 : 0 + realm_id = data.keycloak_realm.hg_realm.id + parent_flow_alias = local.devtest ? keycloak_authentication_flow.iapyx_login[0].alias : null + authenticator = "identity-provider-redirector" + requirement = "ALTERNATIVE" + + depends_on = [ + keycloak_authentication_execution.iapyx_cookie_execution + ] +} + +resource "keycloak_authentication_execution_config" "iapyx_idp_redirector_execution_config" { + count = local.devtest ? 1 : 0 + realm_id = data.keycloak_realm.hg_realm.id + execution_id = local.devtest ? keycloak_authentication_execution.iapyx_idp_redirector_execution[0].id : null + alias = "iapyx-idp-redirector-config" + config = { + defaultProvider = "bcsc" + } +} +resource "keycloak_authentication_execution" "iapyx_execution1" { + count = local.devtest ? 1 : 0 + realm_id = data.keycloak_realm.hg_realm.id + parent_flow_alias = local.devtest ? keycloak_authentication_flow.iapyx_login[0].alias : null + authenticator = "idp-create-user-if-unique" + requirement = "ALTERNATIVE" + + depends_on = [ + keycloak_authentication_execution.iapyx_idp_redirector_execution + ] +} + +resource "keycloak_authentication_execution" "iapyx_execution2" { + count = local.devtest ? 1 : 0 + realm_id = data.keycloak_realm.hg_realm.id + parent_flow_alias = keycloak_authentication_flow.iapyx_login[0].alias + authenticator = "idp-auto-link" + requirement = "ALTERNATIVE" + + depends_on = [ + keycloak_authentication_execution.iapyx_execution1 + ] +} diff --git a/Tools/KeyCloak/Terraform/client_Iapyx.tf b/Tools/KeyCloak/Terraform/client_Iapyx.tf new file mode 100644 index 0000000000..ae65bf47a6 --- /dev/null +++ b/Tools/KeyCloak/Terraform/client_Iapyx.tf @@ -0,0 +1,76 @@ +resource "keycloak_openid_client" "iapyx_client" { + count = local.devtest ? 1 : 0 + realm_id = data.keycloak_realm.hg_realm.id + client_id = var.client_iapyx.id + name = "Regional Demo Auth App - ${var.environment.name}" + description = "Regional Demo Auth App" + enabled = true + access_type = "PUBLIC" + login_theme = "bcgov-no-brand" + standard_flow_enabled = true + pkce_code_challenge_method = "S256" + direct_access_grants_enabled = false + valid_redirect_uris = var.client_iapyx.valid_redirects + web_origins = var.client_iapyx.web_origins + full_scope_allowed = false + access_token_lifespan = var.client_iapyx.token_lifespan + authentication_flow_binding_overrides { + browser_id = keycloak_authentication_flow.iapyx_login[0].id + } +} + +resource "keycloak_openid_client_default_scopes" "iapyx_client_default_scopes" { + count = local.devtest ? 1 : 0 + realm_id = data.keycloak_realm.hg_realm.id + client_id = local.devtest ? keycloak_openid_client.iapyx_client[0].id : null + + default_scopes = [ + "profile", + "web-origins", + "email" + ] +} +resource "keycloak_openid_client_optional_scopes" "iapyx_client_optional_scopes" { + count = local.devtest ? 1 : 0 + realm_id = data.keycloak_realm.hg_realm.id + client_id = local.devtest ? keycloak_openid_client.iapyx_client[0].id : null + + optional_scopes = [ + "address", + "phone", + "microprofile-jwt", + ] +} + +resource "keycloak_generic_role_mapper" "iapyx_uma" { + count = local.devtest ? 1 : 0 + realm_id = data.keycloak_realm.hg_realm.id + client_id = local.devtest ? keycloak_openid_client.iapyx_client[0].id : null + role_id = data.keycloak_role.Uma_authorization.id +} + +resource "keycloak_openid_user_attribute_protocol_mapper" "iapyx_hdid" { + count = local.devtest ? 1 : 0 + realm_id = data.keycloak_realm.hg_realm.id + client_id = local.devtest ? keycloak_openid_client.iapyx_client[0].id : null + name = "hdid" + user_attribute = "hdid" + claim_name = "hdid" + claim_value_type = "String" + add_to_id_token = true + add_to_access_token = true + add_to_userinfo = true +} + +resource "keycloak_openid_user_attribute_protocol_mapper" "iapyx_auth_method" { + count = local.devtest ? 1 : 0 + realm_id = data.keycloak_realm.hg_realm.id + client_id = local.devtest ? keycloak_openid_client.iapyx_client[0].id : null + name = "AuthMethod" + user_attribute = "idp" + claim_name = "idp" + claim_value_type = "String" + add_to_id_token = true + add_to_access_token = true + add_to_userinfo = true +} diff --git a/Tools/KeyCloak/Terraform/variables.tf b/Tools/KeyCloak/Terraform/variables.tf index 6aebfb293d..524f607f32 100644 --- a/Tools/KeyCloak/Terraform/variables.tf +++ b/Tools/KeyCloak/Terraform/variables.tf @@ -162,6 +162,16 @@ variable "client_icarus" { description = "Health Gateway Salesforce client configuration" } +variable "client_iapyx" { + type = object({ + id = optional(string, "iapyx") + valid_redirects = list(string) + web_origins = list(string) + token_lifespan = number + }) + description = "Regional Portal Demo Auth App" +} + variable "client_hg_phsa" { type = object({ id = optional(string, "hg-phsa") @@ -221,4 +231,5 @@ variable "client_hg_seq" { locals { development = var.environment.name == "Development" + devtest = var.environment.name == "Development" || var.environment.name == "Test" } \ No newline at end of file diff --git a/Tools/Pipelines/pipelines/FunctionalTests.yaml b/Tools/Pipelines/pipelines/FunctionalTests.yaml index e1ca98c9e4..936194297e 100644 --- a/Tools/Pipelines/pipelines/FunctionalTests.yaml +++ b/Tools/Pipelines/pipelines/FunctionalTests.yaml @@ -95,7 +95,7 @@ jobs: - "Admin_Seed" - "Admin_Authentication_Tests" strategy: - parallel: 3 + parallel: 9 timeoutInMinutes: 15 steps: - task: Bash@3 @@ -120,7 +120,7 @@ jobs: - "Admin_Seed" - "Admin_Read_Tests" strategy: - parallel: 3 + parallel: 5 timeoutInMinutes: 15 steps: - task: Bash@3