using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Microsoft.Rest; using Microsoft.Rest.Serialization; using Newtonsoft.Json; namespace PowerBI { // Samples: // {"error":{"code":"InvalidRequest", "message":"datasetId is null or empty"}} // {"error":{"code":"GeneralException", "message":"Failed to process dataset PostDataset","target":"PostDataset","details":[{"message":"SQL operation failed: The Database operation reached a timeout of '00:01:30'"}]}} // {"error":{"code":"DM_GWPipeline_UnknownError","pbi.error":{"code":"DM_GWPipeline_UnknownError","parameters":{},"details":[]}}} // {"error":{"code":"FeatureNotAvailableError","pbi.error":{"code":"FeatureNotAvailableError","parameters":{},"details":[]}}} // {"error":{"code":"UnknownError", "pbi.error":{"code":"UnknownError","parameters":{},"details":[],"exceptionCulprit":1}}} public class PowerBIHttpOperationExceptionHandler : DelegatingHandler { const string XPowerBIErrorDetailsHeaderKey = "X-PowerBI-Error-Details"; const string RequestIdHeaderKey = "RequestId"; protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { var response = await base.SendAsync(request, cancellationToken); if (response.IsSuccessStatusCode) { return response; } var errorBody = ErrorInHeader(response); var content = response.Content?.ReadAsStringAsync().Result ?? string.Empty; errorBody = errorBody ?? content; var errorTypeSafe = IsPowerBIErrorJson(errorBody); if (errorTypeSafe == null) { return response; } var message = $"Power BI operation returned an error code '{errorTypeSafe.Code}'"; var exception = new PowerBIErrorException(message) { Body = errorBody, Code = errorTypeSafe.Code, ErrorMessage = errorTypeSafe.Message, Details = errorTypeSafe.Details?.FirstOrDefault()?.Message, Request = new HttpRequestMessageWrapper(request, request.Content?.ReadAsStringAsync().Result), //If the user was uploading a 10meg pbix, we have just read it twice Response = new HttpResponseMessageWrapper(response, content) }; if (response.Headers.Contains(RequestIdHeaderKey)) { exception.RequestId = response.Headers.GetValues(RequestIdHeaderKey).FirstOrDefault(); } var shouldTrace = ServiceClientTracing.IsEnabled; if (shouldTrace) { //var invocationId = (ServiceClientTracing.NextInvocationId - 1).ToString(); //need to use the same id from the call. //ServiceClientTracing.Error(invocationId, exception); } request.Dispose(); response.Dispose(); throw exception; } static string ErrorInHeader(HttpResponseMessage response) { IEnumerable errorHeaderValues = null; if (response.Headers?.TryGetValues(XPowerBIErrorDetailsHeaderKey, out errorHeaderValues) == true) { var xHeaderErrorDetails = errorHeaderValues?.SingleOrDefault(); if (xHeaderErrorDetails != null) { return xHeaderErrorDetails; } } return null; } static PowerBIError IsPowerBIErrorJson(string body) { if (string.IsNullOrEmpty(body)) { return null; } try { var json = SafeJsonConvert.DeserializeObject(body); return json?.Error; } catch (JsonException) { } return null; } public class PowerBIErrorBody { [JsonProperty(PropertyName = "error")] public PowerBIError Error { get; set; } } public class PowerBIError { [JsonProperty(PropertyName = "code")] public string Code { get; set; } [JsonProperty(PropertyName = "details")] public IEnumerable Details { get; set; } [JsonProperty(PropertyName = "message")] public string Message { get; set; } [JsonProperty(PropertyName = "target")] public string Target { get; set; } } public class PowerBIExceptionDetails { [JsonProperty(PropertyName = "message")] public string Message { get; set; } } } }