Skip to content

Commit

Permalink
Fixed merge conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
Nate Malubay committed Jan 24, 2022
2 parents 5d4f01c + 6b160d7 commit 3f87992
Show file tree
Hide file tree
Showing 17 changed files with 739 additions and 28 deletions.
3 changes: 3 additions & 0 deletions src/console/MeasurementCollectionToFhir/Processor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Microsoft.Health.Common.Telemetry;
using Microsoft.Health.Events.EventConsumers;
using Microsoft.Health.Events.Model;
using Microsoft.Health.Events.Telemetry;
using Microsoft.Health.Fhir.Ingest.Console.Template;
using Microsoft.Health.Fhir.Ingest.Host;
using Microsoft.Health.Fhir.Ingest.Service;
Expand Down Expand Up @@ -39,6 +40,8 @@ public Processor(
_measurementImportService = EnsureArg.IsNotNull(measurementImportService, nameof(measurementImportService));
_logger = EnsureArg.IsNotNull(logger, nameof(logger));
_retryPolicy = CreateRetryPolicy(logger);

EventMetrics.SetConnectorOperation(ConnectorOperation.FHIRConversion);
}

public async Task ConsumeAsync(IEnumerable<IEventMessage> events)
Expand Down
8 changes: 3 additions & 5 deletions src/console/Normalize/Processor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,14 @@

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Azure;
using Azure.Identity;
using EnsureThat;
using Microsoft.Azure.EventHubs;
using Microsoft.Azure.WebJobs;
using Microsoft.Health.Common.Telemetry;
using Microsoft.Health.Events.EventConsumers;
using Microsoft.Health.Events.Model;
using Microsoft.Health.Events.Telemetry;
using Microsoft.Health.Fhir.Ingest.Console.Template;
using Microsoft.Health.Fhir.Ingest.Data;
using Microsoft.Health.Fhir.Ingest.Service;
Expand Down Expand Up @@ -52,6 +48,8 @@ public Processor(
_retryPolicy = CreateRetryPolicy(logger);
_collectionTemplateFactory = EnsureArg.IsNotNull(collectionTemplateFactory, nameof(collectionTemplateFactory));
_exceptionTelemetryProcessor = new NormalizationExceptionTelemetryProcessor();

EventMetrics.SetConnectorOperation(ConnectorOperation.Normalization);
}

public async Task ConsumeAsync(IEnumerable<IEventMessage> events)
Expand Down
2 changes: 0 additions & 2 deletions src/console/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,13 @@ public virtual List<IEventConsumer> ResolveEventConsumers(IServiceProvider servi
var collectionContentFactory = serviceProvider.GetRequiredService<CollectionTemplateFactory<IContentTemplate, IContentTemplate>>();
var deviceDataNormalization = new Normalize.Processor(template, templateManager, collector, logger, collectionContentFactory);
eventConsumers.Add(deviceDataNormalization);
EventMetrics.SetConnectorOperation(ConnectorOperation.Normalization);
}
else if (applicationType == _measurementToFhirAppType)
{
template = Configuration.GetSection("Template:FhirMapping").Value;
var importService = serviceProvider.GetRequiredService<MeasurementFhirImportService>();
var measurementCollectionToFhir = new MeasurementCollectionToFhir.Processor(template, templateManager, importService, logger);
eventConsumers.Add(measurementCollectionToFhir);
EventMetrics.SetConnectorOperation(ConnectorOperation.FHIRConversion);
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public static class ErrorType
/// <summary>
/// A metric type for errors that occur when interacting with the FHIR server.
/// </summary>
public static string FHIRServerError => nameof(FHIRServerError);
public static string FHIRServiceError => nameof(FHIRServiceError);

/// <summary>
/// A metric type for errors of unknown type (e.g. unhandled exceptions)
Expand Down
27 changes: 15 additions & 12 deletions src/lib/Microsoft.Health.Extensions.Fhir.R4/FhirClientFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using Microsoft.Health.Common.Auth;
using Microsoft.Health.Common.Telemetry;
using Microsoft.Health.Extensions.Fhir.Config;
using Microsoft.Health.Extensions.Fhir.Telemetry.Exceptions;
using Microsoft.Health.Extensions.Fhir.Telemetry.Metrics;
using Microsoft.Health.Extensions.Host.Auth;
using Microsoft.Health.Logging.Telemetry;
Expand Down Expand Up @@ -64,18 +65,27 @@ public FhirClient Create()

private static FhirClient CreateClient(TokenCredential tokenCredential, ITelemetryLogger logger)
{
EnsureArg.IsNotNull(tokenCredential, nameof(tokenCredential));

var url = Environment.GetEnvironmentVariable("FhirService:Url");
EnsureArg.IsNotNullOrEmpty(url, nameof(url));
var uri = new Uri(url);

EnsureArg.IsNotNull(tokenCredential, nameof(tokenCredential));

var fhirClientSettings = new FhirClientSettings
{
PreferredFormat = ResourceFormat.Json,
};

var client = new FhirClient(url, fhirClientSettings, new BearerTokenAuthorizationMessageHandler(uri, tokenCredential, logger));
FhirClient client = null;
try
{
client = new FhirClient(url, fhirClientSettings, new BearerTokenAuthorizationMessageHandler(uri, tokenCredential, logger));
FhirServiceValidator.ValidateFhirService(client, logger);
}
catch (Exception ex)
{
FhirServiceExceptionProcessor.ProcessException(ex, logger);
}

return client;
}
Expand Down Expand Up @@ -118,15 +128,8 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
if (Logger != null && !response.IsSuccessStatusCode)
{
var statusDescription = response.ReasonPhrase.Replace(" ", string.Empty);

if (response.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
{
Logger.LogMetric(FhirClientMetrics.HandledException($"FhirServerError{statusDescription}", ErrorSeverity.Informational, ConnectorOperation.FHIRConversion), 1);
}
else
{
Logger.LogMetric(FhirClientMetrics.HandledException($"FhirServerError{statusDescription}", ErrorSeverity.Critical, ConnectorOperation.FHIRConversion), 1);
}
var severity = response.StatusCode == System.Net.HttpStatusCode.TooManyRequests ? ErrorSeverity.Informational : ErrorSeverity.Critical;
Logger.LogMetric(FhirClientMetrics.HandledException($"{ErrorType.FHIRServiceError}{statusDescription}", severity), 1);
}

return response;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// -------------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// -------------------------------------------------------------------------------------------------

using System;
using EnsureThat;
using Hl7.Fhir.Rest;
using Microsoft.Health.Extensions.Fhir.Telemetry.Exceptions;
using Microsoft.Health.Logging.Telemetry;

namespace Microsoft.Health.Extensions.Fhir
{
public static class FhirServiceValidator
{
public static bool ValidateFhirService(FhirClient client, ITelemetryLogger logger)
{
EnsureArg.IsNotNull(client, nameof(client));

try
{
client.CapabilityStatement(SummaryType.True);
return true;
}
catch (Exception exception)
{
FhirServiceExceptionProcessor.ProcessException(exception, logger);
return false;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// -------------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// -------------------------------------------------------------------------------------------------

namespace Microsoft.Health.Extensions.Fhir.Telemetry.Exceptions
{
public enum FhirServiceErrorCode
{
/// <summary>
/// Error code that categorizes invalid configurations (e.g. invalid FHIR service URL)
/// </summary>
ConfigurationError,

/// <summary>
/// Error code that categorizes authorization errors (e.g. missing role with permission to write FHIR data)
/// </summary>
AuthorizationError,

/// <summary>
/// Error code that categorizes invalid arguments (i.e. exceptions encountered of type ArgumentException), which may occur when FhirClient's endpoint is validated
/// </summary>
ArgumentError,

/// <summary>
/// Error code that categorizes HTTP request exceptions (i.e. exceptions encountered of type HttpRequestException)
/// </summary>
HttpRequestError,

/// <summary>
/// Error code that categorizes MSAL.NET exceptions (i.e. exceptions encountered of type MsalServiceException)
/// </summary>
MsalServiceError,

/// <summary>
/// Error code that categorizes all other generic exceptions
/// </summary>
GeneralError,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// -------------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// -------------------------------------------------------------------------------------------------

using System;
using System.Net;
using System.Net.Http;
using EnsureThat;
using Hl7.Fhir.Rest;
using Microsoft.Health.Common.Telemetry;
using Microsoft.Health.Extensions.Fhir.Resources;
using Microsoft.Health.Extensions.Fhir.Telemetry.Metrics;
using Microsoft.Health.Logging.Telemetry;
using Microsoft.Identity.Client;

namespace Microsoft.Health.Extensions.Fhir.Telemetry.Exceptions
{
public static class FhirServiceExceptionProcessor
{
private static readonly IExceptionTelemetryProcessor _exceptionTelemetryProcessor = new ExceptionTelemetryProcessor();

public static void ProcessException(Exception exception, ITelemetryLogger logger)
{
EnsureArg.IsNotNull(logger, nameof(logger));

var (customException, errorName) = CustomizeException(exception);

logger.LogError(customException);

string exceptionName = customException.Equals(exception) ? $"{ErrorType.FHIRServiceError}{errorName}" : customException.GetType().Name;
_exceptionTelemetryProcessor.LogExceptionMetric(customException, logger, FhirClientMetrics.HandledException(exceptionName, ErrorSeverity.Critical));
}

public static (Exception customException, string errorName) CustomizeException(Exception exception)
{
EnsureArg.IsNotNull(exception, nameof(exception));

string message;
string errorName;

switch (exception)
{
case FhirOperationException _:
var status = ((FhirOperationException)exception).Status;
switch (status)
{
case HttpStatusCode.Forbidden:
message = FhirResources.FhirServiceAccessForbidden;
string helpLink = "https://docs.microsoft.com/azure/healthcare-apis/iot/deploy-iot-connector-in-azure#accessing-the-iot-connector-from-the-fhir-service";
errorName = nameof(FhirServiceErrorCode.AuthorizationError);
return (new UnauthorizedAccessFhirServiceException(message, exception, helpLink, errorName), errorName);
case HttpStatusCode.NotFound:
message = FhirResources.FhirServiceNotFound;
errorName = nameof(FhirServiceErrorCode.ConfigurationError);
return (new InvalidFhirServiceException(message, exception, errorName), errorName);
default:
return (exception, status.ToString());
}

case ArgumentException _:
var paramName = ((ArgumentException)exception).ParamName;
if (paramName.Contains("endpoint", StringComparison.OrdinalIgnoreCase))
{
message = FhirResources.FhirServiceEndpointInvalid;
errorName = nameof(FhirServiceErrorCode.ConfigurationError);
return (new InvalidFhirServiceException(message, exception, errorName), errorName);
}

return (exception, $"{FhirServiceErrorCode.ArgumentError}{paramName}");

case UriFormatException _:
message = FhirResources.FhirServiceUriFormatInvalid;
errorName = nameof(FhirServiceErrorCode.ConfigurationError);
return (new InvalidFhirServiceException(message, exception, errorName), errorName);

case HttpRequestException _:
// TODO: In .NET 5 and later, check HttpRequestException's StatusCode property instead of the Message property
if (exception.Message.Contains(FhirResources.HttpRequestErrorNotKnown, StringComparison.CurrentCultureIgnoreCase))
{
message = FhirResources.FhirServiceHttpRequestError;
errorName = nameof(FhirServiceErrorCode.ConfigurationError);
return (new InvalidFhirServiceException(message, exception, errorName), errorName);
}

return (exception, nameof(FhirServiceErrorCode.HttpRequestError));

case MsalServiceException _:
var errorCode = ((MsalServiceException)exception).ErrorCode;
if (string.Equals(errorCode, "invalid_resource", StringComparison.OrdinalIgnoreCase)
|| string.Equals(errorCode, "invalid_scope", StringComparison.OrdinalIgnoreCase))
{
message = FhirResources.FhirServiceMsalServiceError;
errorName = nameof(FhirServiceErrorCode.ConfigurationError);
return (new InvalidFhirServiceException(message, exception, errorName), errorName);
}

return (exception, $"{FhirServiceErrorCode.MsalServiceError}{errorCode}");

default:
return (exception, nameof(FhirServiceErrorCode.GeneralError));
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// -------------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// -------------------------------------------------------------------------------------------------

using System;
using Microsoft.Health.Common.Telemetry;
using Microsoft.Health.Common.Telemetry.Exceptions;

namespace Microsoft.Health.Extensions.Fhir.Telemetry.Exceptions
{
public sealed class InvalidFhirServiceException : IomtTelemetryFormattableException
{
private static readonly string _errorType = ErrorType.FHIRServiceError;

public InvalidFhirServiceException()
{
}

public InvalidFhirServiceException(string message)
: base(message)
{
}

public InvalidFhirServiceException(string message, Exception innerException)
: base(message, innerException)
{
}

public InvalidFhirServiceException(
string message,
Exception innerException,
string errorName)
: base(
message,
innerException,
name: $"{_errorType}{errorName}",
operation: ConnectorOperation.FHIRConversion)
{
}

public override string ErrType => _errorType;

public override string ErrSeverity => ErrorSeverity.Critical;

public override string ErrSource => nameof(ErrorSource.User);
}
}
Loading

0 comments on commit 3f87992

Please sign in to comment.