// TODO [log]
        // TODO [ref] separate to ApiClientResponseResolver
        // TODO [ref] errors when another api endpoints are added
        private async Task <Either <AirlyClientError, T> > GetHttpResponseAsync <T>(string url) where T : class
        {
            try
            {
                var response = await _httpClient.GetAsync(url);

                if (response.Headers.TryGetValues("X-RateLimit-Limit-day", out var dayLimit) &&
                    response.Headers.TryGetValues("X-RateLimit-Remaining-day", out var dayLimitRemaining))
                {
                    // TODO [log]
                }

                switch ((int)response.StatusCode)
                {
                // TODO handle 301 and 404
                case 200:
                    string content = await response.Content.ReadAsStringAsync();

                    var result = JsonConvert.DeserializeObject <T>(content);
                    return(Either <AirlyClientError, T> .Right <AirlyClientError, T>(result));

                case 400:
                case 405:
                case 406:
                    return(Either <AirlyClientError, T> .Left <AirlyClientError, T>(AirlyClientError.BadRequest()));

                case 401:
                    return(Either <AirlyClientError, T> .Left <AirlyClientError, T>(AirlyClientError.AuthorizationFailed()));

                case 404:
                    return(Either <AirlyClientError, T> .Left <AirlyClientError, T>(AirlyClientError.NotFound()));

                case 429:
                    return(Either <AirlyClientError, T> .Left <AirlyClientError, T>(AirlyClientError.RequestLimitExceeded()));

                case 500:
                    return(Either <AirlyClientError, T> .Left <AirlyClientError, T>(AirlyClientError.InternalServerError()));

                default:
                    // TODO [airly error] put airly error message into error
                    return(Either <AirlyClientError, T> .Left <AirlyClientError, T>(AirlyClientError.Default()));
                }
            }
            catch (Exception ex)
            {
                // TODO [airly error] handle different airly errors, communication, online, json parsing etc
                return(Either <AirlyClientError, T> .Left <AirlyClientError, T>(AirlyClientError.Default()));
            }
        }
 public static InstallationError ErrorFromResponse(AirlyClientError errorResponse)
 => InstallationError.ClientError(errorResponse.Message);
 public static MeasurementError ErrorFromResponse(AirlyClientError errorResponse)
 => MeasurementError.ClientError(errorResponse.Message);