From abf2758e80bebdeace1f48c231d3075b3845d7d5 Mon Sep 17 00:00:00 2001 From: Adam Storr Date: Wed, 2 Aug 2017 21:39:36 +0100 Subject: [PATCH 1/3] Tidy and refactor --- Fitbit.NetCore/Fitbit.NetCore.csproj | 5 + .../Helpers/ResponseFaker.cs | 2 +- Fitbit.Portable.Tests/InterceptorCounter.cs | 2 +- .../RemoveSubscriptionTests.cs | 4 +- Fitbit.Portable/Fitbit.Portable.csproj | 1 + Fitbit.Portable/FitbitClient.cs | 125 ++++++++---------- Fitbit.Portable/FitbitHttpMessageHandler.cs | 46 +++---- Fitbit.Portable/FitbitRequestException.cs | 3 +- .../Interceptors/FitbitHttpErrorHandler.cs | 27 ++-- .../JsonDotNetSerializerExtensions.cs | 36 ++--- Fitbit.Portable/OAuth2/DefaultTokenManager.cs | 2 +- Fitbit.Portable/OAuth2/OAuth2Helper.cs | 43 +++--- Fitbit.Portable/SubscriptionManager.cs | 8 +- 13 files changed, 127 insertions(+), 177 deletions(-) diff --git a/Fitbit.NetCore/Fitbit.NetCore.csproj b/Fitbit.NetCore/Fitbit.NetCore.csproj index 125143c4..503297fe 100644 --- a/Fitbit.NetCore/Fitbit.NetCore.csproj +++ b/Fitbit.NetCore/Fitbit.NetCore.csproj @@ -19,6 +19,11 @@ ..\NuGet + + true + + + %(RecursiveDir)%(Filename)%(Extension) diff --git a/Fitbit.Portable.Tests/Helpers/ResponseFaker.cs b/Fitbit.Portable.Tests/Helpers/ResponseFaker.cs index 6ca55ce4..4efbc639 100644 --- a/Fitbit.Portable.Tests/Helpers/ResponseFaker.cs +++ b/Fitbit.Portable.Tests/Helpers/ResponseFaker.cs @@ -17,7 +17,7 @@ public class ResponseFaker : IFitbitInterceptor public ResponseFaker(string content, HttpStatusCode code = HttpStatusCode.OK) { var c = new StringContent(content); - this.fakeResponse = new HttpResponseMessage(code) { Content = c }; + fakeResponse = new HttpResponseMessage(code) { Content = c }; } public ResponseFaker(HttpResponseMessage fakeResponse = null) diff --git a/Fitbit.Portable.Tests/InterceptorCounter.cs b/Fitbit.Portable.Tests/InterceptorCounter.cs index 78baf743..bb4e4b98 100644 --- a/Fitbit.Portable.Tests/InterceptorCounter.cs +++ b/Fitbit.Portable.Tests/InterceptorCounter.cs @@ -21,7 +21,7 @@ public Task InterceptRequest(HttpRequestMessage request, Ca public async Task InterceptResponse(Task response, CancellationToken cancellationToken, FitbitClient client) { ResponseCount++; - this.responseContent = await response.Result.Content.ReadAsStringAsync(); + responseContent = await response.Result.Content.ReadAsStringAsync(); return null; } } diff --git a/Fitbit.Portable.Tests/RemoveSubscriptionTests.cs b/Fitbit.Portable.Tests/RemoveSubscriptionTests.cs index 42ffd200..bcdff8dd 100644 --- a/Fitbit.Portable.Tests/RemoveSubscriptionTests.cs +++ b/Fitbit.Portable.Tests/RemoveSubscriptionTests.cs @@ -23,7 +23,7 @@ public void DeleteSubscription_Correctly() var subId = "320"; var expectedUrl = @"https://api.fitbit.com/1/user/-/apiSubscriptions/"+subId+".json"; - var sut = this.SetupFitbitClient(null, expectedUrl, HttpMethod.Delete); + var sut = SetupFitbitClient(null, expectedUrl, HttpMethod.Delete); //Any unexpected behavior will throw exception or fail on request checks (in handler) //Pass APICollectionType.user to delete subscriptions for all data sets @@ -39,7 +39,7 @@ public void DeleteSubscriptonFromSpecificCollection() var collection = APICollectionType.activities; var expectedUrl = @"https://api.fitbit.com/1/user/-/"+ collection +@"/apiSubscriptions/" + subId + ".json"; - var sut = this.SetupFitbitClient(null, expectedUrl, HttpMethod.Delete); + var sut = SetupFitbitClient(null, expectedUrl, HttpMethod.Delete); //Any unexpected behavior will throw exception or fail on request checks (in handler) sut.DeleteSubscriptionAsync(collection, subId).Wait(); diff --git a/Fitbit.Portable/Fitbit.Portable.csproj b/Fitbit.Portable/Fitbit.Portable.csproj index f650b2fe..15976938 100644 --- a/Fitbit.Portable/Fitbit.Portable.csproj +++ b/Fitbit.Portable/Fitbit.Portable.csproj @@ -27,6 +27,7 @@ TRACE;DEBUG;REQUIRES_JSONNET prompt 4 + true pdbonly diff --git a/Fitbit.Portable/FitbitClient.cs b/Fitbit.Portable/FitbitClient.cs index 2ca91a5f..c5029632 100644 --- a/Fitbit.Portable/FitbitClient.cs +++ b/Fitbit.Portable/FitbitClient.cs @@ -13,21 +13,20 @@ namespace Fitbit.Api.Portable { public class FitbitClient : IFitbitClient { - public FitbitAppCredentials AppCredentials { get; private set; } + public FitbitAppCredentials AppCredentials { get; } private OAuth2AccessToken _accesToken; public OAuth2AccessToken AccessToken { - get - { - return _accesToken; - } + get => _accesToken; set { _accesToken = value; //If we update the AccessToken after HttpClient has been created, then reconfigure authorization header if (HttpClient != null) + { ConfigureAuthorizationHeader(); + } } } @@ -37,10 +36,10 @@ public OAuth2AccessToken AccessToken public HttpClient HttpClient { get; private set; } public ITokenManager TokenManager { get; private set; } - public bool OAuth2TokenAutoRefresh { get; set; } - public List FitbitInterceptorPipeline { get; private set; } + public bool OAuth2TokenAutoRefresh { get; set; } + public List FitbitInterceptorPipeline { get; } = new List(); /// /// Simplest constructor for OAuth2- requires the minimum information required by FitBit.Net client to make succesful calls to Fitbit Api @@ -48,17 +47,16 @@ public OAuth2AccessToken AccessToken /// Obtain this information from your developer dashboard. App credentials are required to perform token refresh /// Authenticate with Fitbit API using OAuth2. Authenticator2 class is a helper for this process /// An interface that enables sniffing all outgoing and incoming http requests from FitbitClient + /// Enable auto refresh for the OAuth2 auhtorization token + /// ITokenManager implementation; if none provided an instance of DefaultTokenManager is used public FitbitClient(FitbitAppCredentials credentials, OAuth2AccessToken accessToken, IFitbitInterceptor interceptor = null, bool enableOAuth2TokenRefresh = true, ITokenManager tokenManager = null) { - this.AppCredentials = credentials; - this.AccessToken = accessToken; - - this.FitbitInterceptorPipeline = new List(); - - + AppCredentials = credentials; + AccessToken = accessToken; + if(interceptor != null) { - this.FitbitInterceptorPipeline.Add(interceptor); + FitbitInterceptorPipeline.Add(interceptor); } ConfigureTokenManager(tokenManager); @@ -71,10 +69,11 @@ public FitbitClient(FitbitAppCredentials credentials, OAuth2AccessToken accessTo private void ConfigureAutoRefresh(bool enableOAuth2TokenRefresh) { - this.OAuth2TokenAutoRefresh = enableOAuth2TokenRefresh; - if(OAuth2TokenAutoRefresh) - this.FitbitInterceptorPipeline.Add(new OAuth2AutoRefreshInterceptor()); - return; + OAuth2TokenAutoRefresh = enableOAuth2TokenRefresh; + if (OAuth2TokenAutoRefresh) + { + FitbitInterceptorPipeline.Add(new OAuth2AutoRefreshInterceptor()); + } } /// @@ -85,13 +84,15 @@ private void ConfigureAutoRefresh(bool enableOAuth2TokenRefresh) /// An interface that enables sniffing all outgoing and incoming http requests from FitbitClient public FitbitClient(FitbitAppCredentials credentials, OAuth2AccessToken accessToken, List interceptors, bool enableOAuth2TokenRefresh = true, ITokenManager tokenManager = null) { - this.AppCredentials = credentials; - this.AccessToken = accessToken; + AppCredentials = credentials; + AccessToken = accessToken; - this.FitbitInterceptorPipeline = new List(); + FitbitInterceptorPipeline = new List(); - if(interceptors != null && interceptors.Count > 0) - this.FitbitInterceptorPipeline.AddRange(interceptors); + if (interceptors != null && interceptors.Count > 0) + { + FitbitInterceptorPipeline.AddRange(interceptors); + } ConfigureTokenManager(tokenManager); @@ -100,7 +101,6 @@ public FitbitClient(FitbitAppCredentials credentials, OAuth2AccessToken accessTo CreateHttpClientForOAuth2(); } - public FitbitClient(FitbitAppCredentials credentials, OAuth2AccessToken accessToken, bool enableOAuth2TokenRefresh) : this(credentials, accessToken, null, enableOAuth2TokenRefresh) { @@ -126,12 +126,12 @@ public FitbitClient(FitbitAppCredentials credentials, OAuth2AccessToken accessTo /// /// A function or lambda expression who is in charge of creating th HttpClient. It takes as an argument a HttpMessageHandler which does wiring for IFitbitInterceptor. To use IFitbitInterceptor you must pass this HttpMessageHandler as anargument to the constuctor of HttpClient /// An interface that enables sniffing all outgoing and incoming http requests from FitbitClient - public FitbitClient(Func customFactory, IFitbitInterceptor interceptor = null, ITokenManager tokenManager = null) + internal FitbitClient(Func customFactory, IFitbitInterceptor interceptor = null, ITokenManager tokenManager = null) { - this.OAuth2TokenAutoRefresh = false; + OAuth2TokenAutoRefresh = false; ConfigureTokenManager(tokenManager); - this.HttpClient = customFactory(new FitbitHttpMessageHandler(this, interceptor)); + HttpClient = customFactory(new FitbitHttpMessageHandler(this, interceptor)); } private void ConfigureTokenManager(ITokenManager tokenManager) @@ -142,10 +142,7 @@ private void ConfigureTokenManager(ITokenManager tokenManager) private void CreateHttpClientForOAuth2() { var pipeline = this.CreatePipeline(FitbitInterceptorPipeline); - if (pipeline != null) - this.HttpClient = new HttpClient(pipeline); - else - this.HttpClient = new HttpClient(); + HttpClient = pipeline != null ? new HttpClient(pipeline) : new HttpClient(); ConfigureAuthorizationHeader(); } @@ -202,16 +199,14 @@ public async Task GetDayActivitySummaryAsync(DateTime activityD /// public async Task GetActivitiesStatsAsync(string encodedUserId = null) { - string apiCall = FitbitClientHelperExtensions.ToFullUrl("/1/user/{0}/activities.json", encodedUserId); + string apiCall = FitbitClientHelperExtensions.ToFullUrl("/1/user/{0}/activities.json", encodedUserId); HttpResponseMessage response = await HttpClient.GetAsync(apiCall); await HandleResponse(response); string responseBody = await response.Content.ReadAsStringAsync(); var serializer = new JsonDotNetSerializer(); return serializer.Deserialize(responseBody); } - - #region Sleep - + /// /// Requests the sleep data for the specified date for the logged in user /// NOTE: This is for the V1 of the sleep api which is now Deprecated @@ -270,7 +265,6 @@ public async Task GetSleepDateRangeAsync(DateTime startDate, return data; } - /// /// The Get Sleep Logs List endpoint returns a list of a user's sleep logs (including naps) /// before or after a given day with offset, limit, and sort order. @@ -329,7 +323,6 @@ public async Task GetSleepLogListAsync(DateTime dateToList, Sl return data; } - /// /// Creates a log entry for a sleep event and returns a response in the format requested /// @@ -352,9 +345,7 @@ public async Task PostLogSleepAsync(string startTime, int dur return serialzer.Deserialize(responeBody); } - - #endregion Sleep - + /// /// Requests the devices for the current logged in user /// @@ -424,14 +415,22 @@ public async Task GetHeartRateIntraday(DateTime date, H string resolutionText = null; //this little big of section is necessary because enums can't start with numbers - if (resolution == HeartRateResolution.oneSecond) - resolutionText = "1sec"; - else if (resolution == HeartRateResolution.oneMinute) - resolutionText = "1min"; - else - resolutionText = "15min"; + switch (resolution) + { + case HeartRateResolution.oneSecond: + resolutionText = "1sec"; + break; - string apiCall = String.Format("https://api.fitbit.com/1.1/user/-/activities/heart/date/{0}/{1}/{2}/time/00:00:00/23:59:59.json", date.ToString("yyyy-MM-dd"), date.ToString("yyyy-MM-dd"), resolutionText); + case HeartRateResolution.oneMinute: + resolutionText = "1min"; + break; + + default: + resolutionText = "15min"; + break; + } + + string apiCall = string.Format("https://api.fitbit.com/1.1/user/-/activities/heart/date/{0}/{1}/{2}/time/00:00:00/23:59:59.json", date.ToString("yyyy-MM-dd"), date.ToString("yyyy-MM-dd"), resolutionText); HttpResponseMessage response = await HttpClient.GetAsync(apiCall); await HandleResponse(response); @@ -501,8 +500,7 @@ public async Task GetHeartRateIntraday(DateTime date, H string responseBody = await response.Content.ReadAsStringAsync(); var serializer = new JsonDotNetSerializer {RootProperty = timeSeriesResourceType.ToTimeSeriesProperty()}; - return serializer.GetTimeSeriesDataList(responseBody); - + return serializer.GetTimeSeriesDataList(responseBody); } /// @@ -554,8 +552,8 @@ public async Task GetIntraDayTimeSeriesAsync(IntradayResourceType { string apiCall; - if (intraDayTimeSpan > new TimeSpan(0, 1, 0) && //the timespan is greater than a minute - dayAndStartTime.Day == dayAndStartTime.Add(intraDayTimeSpan).Day) //adding the timespan doesn't go in to the next day + if (intraDayTimeSpan > new TimeSpan(0, 1, 0) //the timespan is greater than a minute + && dayAndStartTime.Day == dayAndStartTime.Add(intraDayTimeSpan).Day) //adding the timespan doesn't go in to the next day { apiCall = string.Format("/1/user/-{0}/date/{1}/1d/time/{2}/{3}.json", timeSeriesResourceType.GetStringValue(), @@ -572,7 +570,7 @@ public async Task GetIntraDayTimeSeriesAsync(IntradayResourceType apiCall = FitbitClientHelperExtensions.ToFullUrl(apiCall); - HttpResponseMessage response = null; + HttpResponseMessage response; try { response = await HttpClient.GetAsync(apiCall); @@ -583,11 +581,9 @@ public async Task GetIntraDayTimeSeriesAsync(IntradayResourceType { return null; } - else - { - //otherwise, rethrow because we only want to alter behavior for the very specific case above - throw; - } + + //otherwise, rethrow because we only want to alter behavior for the very specific case above + throw; } await HandleResponse(response); string responseBody = await response.Content.ReadAsStringAsync(); @@ -599,16 +595,15 @@ public async Task GetIntraDayTimeSeriesAsync(IntradayResourceType var serializer = new JsonDotNetSerializer { RootProperty = timeSeriesResourceType.ToTimeSeriesProperty() }; - IntradayData data = null; + IntradayData data; try { data = serializer.GetIntradayTimeSeriesData(responseBody); } - catch(Exception ex) + catch(Exception exception) { - FitbitRequestException fEx = new FitbitRequestException(response, null, "Serialization Error in GetIntradayTimeSeriesData", ex); - throw fEx; + throw new FitbitRequestException(response, null, "Serialization Error in GetIntradayTimeSeriesData", exception); } return data; @@ -940,12 +935,7 @@ public async Task> GetSubscriptionsAsync() public async Task DeleteSubscriptionAsync(APICollectionType collection, string uniqueSubscriptionId, string subscriberId = null) { - var collectionString = string.Empty; - - if (collection == APICollectionType.user) - collectionString = string.Empty; - else - collectionString = collection.ToString() + @"/"; + var collectionString = collection == APICollectionType.user ? string.Empty : collection + @"/"; string url = "/1/user/-/{2}apiSubscriptions/{1}.json"; string apiCall = FitbitClientHelperExtensions.ToFullUrl(url, args: new object[] { uniqueSubscriptionId, collectionString }); @@ -1024,6 +1014,5 @@ private async Task HandleResponse(HttpResponseMessage response) throw new FitbitException($"An error has occured. Please see error list for details - {response.StatusCode}", errors); } } - } } \ No newline at end of file diff --git a/Fitbit.Portable/FitbitHttpMessageHandler.cs b/Fitbit.Portable/FitbitHttpMessageHandler.cs index 804b76af..58ec268d 100644 --- a/Fitbit.Portable/FitbitHttpMessageHandler.cs +++ b/Fitbit.Portable/FitbitHttpMessageHandler.cs @@ -1,28 +1,20 @@ namespace Fitbit.Api.Portable { - using Newtonsoft.Json; - using Newtonsoft.Json.Linq; - using System; - using System.Collections.Generic; using System.Diagnostics; - using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; internal class FitbitHttpMessageHandler : DelegatingHandler { - private IFitbitInterceptor interceptor; - Func, CancellationToken, Task> responseHandler; + private readonly IFitbitInterceptor _interceptor; - - public FitbitClient FitbitClient { get; private set; } + public FitbitClient FitbitClient { get; } public FitbitHttpMessageHandler(FitbitClient fitbitClient, IFitbitInterceptor interceptor) { - this.FitbitClient = fitbitClient; - this.interceptor = interceptor; - responseHandler = ResponseHandler; + FitbitClient = fitbitClient; + _interceptor = interceptor; //Define the inner must handler. Otherwise exception is thrown. InnerHandler = new HttpClientHandler(); } @@ -34,23 +26,20 @@ protected override Task SendAsync(HttpRequestMessage reques Task interceptorFakeResponse = null; Debug.WriteLine("Entering Http client's request message handler. Request details: {0}", request.ToString()); - if (interceptor != null) - interceptorFakeResponse = interceptor.InterceptRequest(request, cancellationToken, FitbitClient); + if (_interceptor != null) + { + interceptorFakeResponse = _interceptor.InterceptRequest(request, cancellationToken, FitbitClient); + } if (interceptorFakeResponse != null) //then highjack the request pipeline and return the HttpResponse returned by interceptor. Invoke Response handler at return. { //If we are faking the response, have the courtesy of setting the original HttpRequestMessage interceptorFakeResponse.Result.RequestMessage = request; - return interceptorFakeResponse.ContinueWith( - responseTask => ResponseHandler(responseTask, cancellationToken).Result - ); - } - else //Let the base object continue with the request pipeline. Invoke Response handler at return. - { - return base.SendAsync(request, cancellationToken).ContinueWith( - responseTask => ResponseHandler(responseTask, cancellationToken).Result - ); + return interceptorFakeResponse.ContinueWith(responseTask => ResponseHandler(responseTask, cancellationToken).Result, cancellationToken); } + + //Let the base object continue with the request pipeline. Invoke Response handler at return. + return base.SendAsync(request, cancellationToken).ContinueWith(responseTask => ResponseHandler(responseTask, cancellationToken).Result, cancellationToken); } //Handle the following method with EXTREME care as it will be invoked on ALL responses made by FitbitClient @@ -58,9 +47,9 @@ private async Task ResponseHandler(Task ResponseHandler(Task requestTask) { - string responseContent = null; - - if (requestTask.Result.Content != null) - responseContent = requestTask.Result.Content.ReadAsStringAsync().Result; + var responseContent = requestTask?.Result?.Content?.ReadAsStringAsync().Result; - Debug.WriteLine("Entering Http client's response message handler. Response details: \n {0}", requestTask.Result); + Debug.WriteLine("Entering Http client's response message handler. Response details: \n {0}", requestTask?.Result); Debug.WriteLine("Response Content: \n {0}", responseContent ?? "Response body was empty"); } } diff --git a/Fitbit.Portable/FitbitRequestException.cs b/Fitbit.Portable/FitbitRequestException.cs index 5e865199..65eb6821 100644 --- a/Fitbit.Portable/FitbitRequestException.cs +++ b/Fitbit.Portable/FitbitRequestException.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Net; using System.Net.Http; using Fitbit.Models; using System; @@ -13,7 +12,7 @@ public class FitbitRequestException : FitbitException public FitbitRequestException(HttpResponseMessage response, IEnumerable errors, string message = default(string), Exception innerEx = null) : base(message ?? $"Fitbit Request exception - Http Status Code: {response.StatusCode} - see errors for more details.", errors, innerEx) { - this.Response = response; + Response = response; } } } \ No newline at end of file diff --git a/Fitbit.Portable/Interceptors/FitbitHttpErrorHandler.cs b/Fitbit.Portable/Interceptors/FitbitHttpErrorHandler.cs index d236f88f..23914b7b 100644 --- a/Fitbit.Portable/Interceptors/FitbitHttpErrorHandler.cs +++ b/Fitbit.Portable/Interceptors/FitbitHttpErrorHandler.cs @@ -1,9 +1,6 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Net; using System.Net.Http; -using System.Text; using System.Threading; using System.Threading.Tasks; using Fitbit.Models; @@ -19,37 +16,33 @@ public Task InterceptRequest(HttpRequestMessage request, Ca public async Task InterceptResponse(Task response, CancellationToken cancellationToken, FitbitClient invokingClient) { - if ((await response).IsSuccessStatusCode) return null; - else + if ((await response).IsSuccessStatusCode) { - await GenerateFitbitRequestException(await response); return null; } - + await GenerateFitbitRequestException(await response); + return null; } - - + private async Task GenerateFitbitRequestException(HttpResponseMessage response) { - List errors = null; + List errors; try { // assumption is error response from fitbit in the 4xx range errors = new JsonDotNetSerializer().ParseErrors(await response.Content.ReadAsStringAsync()); } - catch(ArgumentNullException emptyBodyException) + catch(ArgumentNullException) { - errors = new List() { {new ApiError() {ErrorType = "Fitbit.Net client library error", Message = "Error parsing content body. The content was empty"} } }; + errors = new List { new ApiError {ErrorType = "Fitbit.Net client library error", Message = "Error parsing content body. The content was empty" }}; } - catch (Exception e) + catch (Exception) { - errors = new List() { { new ApiError() { ErrorType = "Fitbit.Net client library error", Message = "Unexpected error when deserializing the content of Fitbit's response." } } }; + errors = new List { new ApiError { ErrorType = "Fitbit.Net client library error", Message = "Unexpected error when deserializing the content of Fitbit's response." }}; } - var exception = new FitbitRequestException(response, errors); - - throw exception; + throw new FitbitRequestException(response, errors); } } } diff --git a/Fitbit.Portable/JsonDotNetSerializerExtensions.cs b/Fitbit.Portable/JsonDotNetSerializerExtensions.cs index 40ed1d2b..712aeed9 100644 --- a/Fitbit.Portable/JsonDotNetSerializerExtensions.cs +++ b/Fitbit.Portable/JsonDotNetSerializerExtensions.cs @@ -24,9 +24,7 @@ internal static List ParseErrors(this JsonDotNetSerializer serializer, serializer.RootProperty = "errors"; return serializer.Deserialize>(errorJson); } - - - + /// /// GetFat has to doe some custom manipulation with the returned representation /// @@ -83,21 +81,18 @@ internal static List GetFriends(this JsonDotNetSerializer serialize return friends.Children().Select(serializer.Deserialize).ToList(); } - - - - /// /// GetTimeSeriesDataList has to do some custom manipulation with the returned representation /// /// - /// + /// + /// /// internal static HeartActivitiesIntraday GetHeartRateIntraday(this JsonDotNetSerializer serializer, DateTime date, string heartRateIntradayJson) { if (string.IsNullOrWhiteSpace(heartRateIntradayJson)) { - throw new ArgumentNullException("heartRateIntradayJson", "heartRateIntradayJson can not be empty, null or whitespace."); + throw new ArgumentNullException(nameof(heartRateIntradayJson), "heartRateIntradayJson can not be empty, null or whitespace."); } var activitiesHeartIntraday = JToken.Parse(heartRateIntradayJson)["activities-heart-intraday"]; @@ -108,7 +103,7 @@ internal static HeartActivitiesIntraday GetHeartRateIntraday(this JsonDotNetSeri Dataset = (from item in dataset select new DatasetInterval { - Time = DateTime.Parse(date.ToString("yyyy-MM-dd") + " " + item["time"].ToString()), //here, maybe pass in the date so we have a full object of date and time + Time = DateTime.Parse(date.ToString("yyyy-MM-dd") + " " + item["time"]), //here, maybe pass in the date so we have a full object of date and time Value = int.Parse(item["value"].ToString()) }).ToList(), DatasetInterval = Convert.ToInt32(activitiesHeartIntraday["datasetInterval"]), @@ -118,13 +113,12 @@ internal static HeartActivitiesIntraday GetHeartRateIntraday(this JsonDotNetSeri return result; } - - + internal static HeartActivitiesTimeSeries GetHeartActivitiesTimeSeries(this JsonDotNetSerializer serializer, string heartActivitiesTimeSeries) { if (string.IsNullOrWhiteSpace(heartActivitiesTimeSeries)) { - throw new ArgumentNullException("heartActivitiesTimeSeries", "heartActivitiesTimeSeries can not be empty, null or whitespace."); + throw new ArgumentNullException(nameof(heartActivitiesTimeSeries), "heartActivitiesTimeSeries can not be empty, null or whitespace."); } var activitiesHeartIntraday = JToken.Parse(heartActivitiesTimeSeries)["activities-heart"]; @@ -143,10 +137,7 @@ internal static HeartActivitiesTimeSeries GetHeartActivitiesTimeSeries(this Json return result; } - - - - + /// /// GetTimeSeriesDataList has to do some custom manipulation with the returned representation /// @@ -200,10 +191,7 @@ internal static TimeSeriesDataListInt GetTimeSeriesDataListInt(this JsonDotNetSe return result; } - - - - + internal static IntradayData GetIntradayTimeSeriesData(this JsonDotNetSerializer serializer, string intradayDataJson) { if (string.IsNullOrWhiteSpace(intradayDataJson)) @@ -219,7 +207,7 @@ internal static IntradayData GetIntradayTimeSeriesData(this JsonDotNetSerializer { date = parsedJToken.SelectToken(serializer.RootProperty).First["dateTime"]; } - catch (NullReferenceException nullReferenceException) + catch (NullReferenceException) { //We'll nullref here if we're querying a future date - Fitbit omits dateTime in that case. //Return null since this error will, in all cases, coincide with an otherwise empty (all zeros) object @@ -234,8 +222,8 @@ internal static IntradayData GetIntradayTimeSeriesData(this JsonDotNetSerializer { Time = DateTime.Parse(date + " " + item["time"]), Value = item["value"].ToObject().ToString("R"), //converting to double is required to keep precision - METs = item["mets"] != null ? item["mets"].ToString() : null, - Level = item["level"] != null ? item["level"].ToString() : null + METs = item["mets"]?.ToString(), + Level = item["level"]?.ToString() }).ToList() }; diff --git a/Fitbit.Portable/OAuth2/DefaultTokenManager.cs b/Fitbit.Portable/OAuth2/DefaultTokenManager.cs index 20c23128..af55388d 100644 --- a/Fitbit.Portable/OAuth2/DefaultTokenManager.cs +++ b/Fitbit.Portable/OAuth2/DefaultTokenManager.cs @@ -37,4 +37,4 @@ public async Task RefreshTokenAsync(FitbitClient client) return OAuth2Helper.ParseAccessTokenResponse(responseString); } } -} +} \ No newline at end of file diff --git a/Fitbit.Portable/OAuth2/OAuth2Helper.cs b/Fitbit.Portable/OAuth2/OAuth2Helper.cs index 94d5d2ae..d1d853d9 100644 --- a/Fitbit.Portable/OAuth2/OAuth2Helper.cs +++ b/Fitbit.Portable/OAuth2/OAuth2Helper.cs @@ -10,21 +10,20 @@ namespace Fitbit.Api.Portable.OAuth2 public class OAuth2Helper { private const string FitbitWebAuthBaseUrl = "https://www.fitbit.com"; - private const string FitbitApiBaseUrl = "https://api.fitbit.com"; - + private const string FitbitOauthPostUrl = "https://api.fitbit.com/oauth2/token"; private const string OAuthBase = "/oauth2"; - private string ClientId; - private string ClientSecret; - - private string RedirectUri; + private readonly string _clientId; + private readonly string _clientSecret; + private readonly string _redirectUri; public OAuth2Helper(FitbitAppCredentials credentials, string redirectUri) { - this.ClientId = credentials.ClientId; - this.ClientSecret = credentials.ClientSecret; - this.RedirectUri = redirectUri; + _clientId = credentials.ClientId; + _clientSecret = credentials.ClientSecret; + _redirectUri = redirectUri; } + public string GenerateAuthUrl(string[] scopeTypes, string state = null) { var sb = new StringBuilder(); @@ -33,12 +32,14 @@ public string GenerateAuthUrl(string[] scopeTypes, string state = null) sb.Append(OAuthBase); sb.Append("/authorize?"); sb.Append("response_type=code"); - sb.Append(string.Format("&client_id={0}", this.ClientId)); - sb.Append(string.Format("&redirect_uri={0}", Uri.EscapeDataString(this.RedirectUri))); + sb.Append(string.Format("&client_id={0}", _clientId)); + sb.Append(string.Format("&redirect_uri={0}", Uri.EscapeDataString(_redirectUri))); sb.Append(string.Format("&scope={0}", String.Join(" ", scopeTypes))); if (!string.IsNullOrWhiteSpace(state)) + { sb.Append(string.Format("&state={0}", state)); + } return sb.ToString(); } @@ -47,33 +48,25 @@ public async Task ExchangeAuthCodeForAccessTokenAsync(string { HttpClient httpClient = new HttpClient(); - string postUrl = OAuth2Helper.FitbitOauthPostUrl; - var content = new FormUrlEncodedContent(new[] { new KeyValuePair("grant_type", "authorization_code"), - new KeyValuePair("client_id", ClientId), + new KeyValuePair("client_id", _clientId), //new KeyValuePair("client_secret", AppSecret), new KeyValuePair("code", code), - new KeyValuePair("redirect_uri", this.RedirectUri) + new KeyValuePair("redirect_uri", _redirectUri) }); - - string clientIdConcatSecret = OAuth2Helper.Base64Encode(ClientId + ":" + ClientSecret); + string clientIdConcatSecret = Base64Encode(_clientId + ":" + _clientSecret); httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", clientIdConcatSecret); - HttpResponseMessage response = await httpClient.PostAsync(postUrl, content); + HttpResponseMessage response = await httpClient.PostAsync(FitbitOauthPostUrl, content); string responseString = await response.Content.ReadAsStringAsync(); - OAuth2AccessToken accessToken = OAuth2Helper.ParseAccessTokenResponse(responseString); - - return accessToken; + return ParseAccessTokenResponse(responseString); } - public static readonly string FitbitOauthPostUrl = "https://api.fitbit.com/oauth2/token"; - - public static OAuth2AccessToken ParseAccessTokenResponse(string responseString) { // assumption is the errors json will return in usual format eg. errors array @@ -95,7 +88,7 @@ public static OAuth2AccessToken ParseAccessTokenResponse(string responseString) /// /// /// - public static string Base64Encode(string plainText) + internal static string Base64Encode(string plainText) { var plainTextBytes = Encoding.UTF8.GetBytes(plainText); return Convert.ToBase64String(plainTextBytes); diff --git a/Fitbit.Portable/SubscriptionManager.cs b/Fitbit.Portable/SubscriptionManager.cs index e93b88e4..36b9040d 100644 --- a/Fitbit.Portable/SubscriptionManager.cs +++ b/Fitbit.Portable/SubscriptionManager.cs @@ -1,16 +1,12 @@ using System.Diagnostics; using System.IO; -using System.Linq; -using System.Xml; using System.Xml.Serialization; using Fitbit.Api.Portable.Models; using Fitbit.Models; +using System.Collections.Generic; namespace Fitbit.Api.Portable -{ - using System; - using System.Collections.Generic; - +{ public class SubscriptionManager { public List ProcessUpdateReponseBody(string bodyContent) From 7b9cefb8d848dff55f359d9c82cd4222ac1700cb Mon Sep 17 00:00:00 2001 From: Adam Storr Date: Fri, 4 Aug 2017 09:28:11 +0100 Subject: [PATCH 2/3] Update nameof usages after code review --- .../JsonDotNetSerializerExtensions.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Fitbit.Portable/JsonDotNetSerializerExtensions.cs b/Fitbit.Portable/JsonDotNetSerializerExtensions.cs index 712aeed9..4dc975e1 100644 --- a/Fitbit.Portable/JsonDotNetSerializerExtensions.cs +++ b/Fitbit.Portable/JsonDotNetSerializerExtensions.cs @@ -18,7 +18,7 @@ internal static List ParseErrors(this JsonDotNetSerializer serializer, { if (string.IsNullOrWhiteSpace(errorJson)) { - throw new ArgumentNullException(nameof(errorJson), "errorJson can not be empty, null or whitespace"); + throw new ArgumentNullException(nameof(errorJson), $"{nameof(errorJson)} can not be empty, null or whitespace"); } serializer.RootProperty = "errors"; @@ -35,7 +35,7 @@ internal static Fat GetFat(this JsonDotNetSerializer serializer, string fatJson) { if (string.IsNullOrWhiteSpace(fatJson)) { - throw new ArgumentNullException(nameof(fatJson), "fatJson can not be empty, null or whitespace"); + throw new ArgumentNullException(nameof(fatJson), $"{nameof(fatJson)} can not be empty, null or whitespace"); } var fatlogs = JToken.Parse(fatJson)["fat"]; @@ -54,7 +54,7 @@ internal static Weight GetWeight(this JsonDotNetSerializer serializer, string we { if (string.IsNullOrWhiteSpace(weightJson)) { - throw new ArgumentNullException(nameof(weightJson), "weightJson can not be empty, null or whitespace"); + throw new ArgumentNullException(nameof(weightJson), $"{nameof(weightJson)} can not be empty, null or whitespace"); } var weightlogs = JToken.Parse(weightJson)["weight"]; @@ -73,7 +73,7 @@ internal static List GetFriends(this JsonDotNetSerializer serialize { if (string.IsNullOrWhiteSpace(friendsJson)) { - throw new ArgumentNullException(nameof(friendsJson), "friendsJson can not be empty, null or whitespace."); + throw new ArgumentNullException(nameof(friendsJson), $"{nameof(friendsJson)} can not be empty, null or whitespace."); } serializer.RootProperty = "user"; @@ -92,7 +92,7 @@ internal static HeartActivitiesIntraday GetHeartRateIntraday(this JsonDotNetSeri { if (string.IsNullOrWhiteSpace(heartRateIntradayJson)) { - throw new ArgumentNullException(nameof(heartRateIntradayJson), "heartRateIntradayJson can not be empty, null or whitespace."); + throw new ArgumentNullException(nameof(heartRateIntradayJson), $"{nameof(heartRateIntradayJson)} can not be empty, null or whitespace."); } var activitiesHeartIntraday = JToken.Parse(heartRateIntradayJson)["activities-heart-intraday"]; @@ -118,7 +118,7 @@ internal static HeartActivitiesTimeSeries GetHeartActivitiesTimeSeries(this Json { if (string.IsNullOrWhiteSpace(heartActivitiesTimeSeries)) { - throw new ArgumentNullException(nameof(heartActivitiesTimeSeries), "heartActivitiesTimeSeries can not be empty, null or whitespace."); + throw new ArgumentNullException(nameof(heartActivitiesTimeSeries), $"{nameof(heartActivitiesTimeSeries)} can not be empty, null or whitespace."); } var activitiesHeartIntraday = JToken.Parse(heartActivitiesTimeSeries)["activities-heart"]; @@ -148,7 +148,7 @@ internal static TimeSeriesDataList GetTimeSeriesDataList(this JsonDotNetSerializ { if (string.IsNullOrWhiteSpace(timeSeriesDataJson)) { - throw new ArgumentNullException(nameof(timeSeriesDataJson), "timeSeriesDataJson can not be empty, null or whitespace."); + throw new ArgumentNullException(nameof(timeSeriesDataJson), $"{nameof(timeSeriesDataJson)} can not be empty, null or whitespace."); } var dataPoints = JToken.Parse(timeSeriesDataJson)[serializer.RootProperty]; @@ -175,7 +175,7 @@ internal static TimeSeriesDataListInt GetTimeSeriesDataListInt(this JsonDotNetSe { if (string.IsNullOrWhiteSpace(timeSeriesDataJson)) { - throw new ArgumentNullException(nameof(timeSeriesDataJson), "timeSeriesDataJson can not be empty, null or whitespace."); + throw new ArgumentNullException(nameof(timeSeriesDataJson), $"{nameof(timeSeriesDataJson)} can not be empty, null or whitespace."); } var dataPoints = JToken.Parse(timeSeriesDataJson)[serializer.RootProperty]; @@ -196,7 +196,7 @@ internal static IntradayData GetIntradayTimeSeriesData(this JsonDotNetSerializer { if (string.IsNullOrWhiteSpace(intradayDataJson)) { - throw new ArgumentNullException(nameof(intradayDataJson), "intradayDataJson can not be empty, null or whitespace."); + throw new ArgumentNullException(nameof(intradayDataJson), $"{nameof(intradayDataJson)} can not be empty, null or whitespace."); } var parsedJToken = JToken.Parse(intradayDataJson); From 38d7eaebbe22afa71c4e0a62a8159cb65455b7ab Mon Sep 17 00:00:00 2001 From: Adam Storr Date: Sat, 5 Aug 2017 10:18:04 +0100 Subject: [PATCH 3/3] Remove expression body --- Fitbit.Portable/FitbitClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Fitbit.Portable/FitbitClient.cs b/Fitbit.Portable/FitbitClient.cs index c5029632..30153f0a 100644 --- a/Fitbit.Portable/FitbitClient.cs +++ b/Fitbit.Portable/FitbitClient.cs @@ -18,7 +18,7 @@ public class FitbitClient : IFitbitClient private OAuth2AccessToken _accesToken; public OAuth2AccessToken AccessToken { - get => _accesToken; + get { return _accesToken; } set { _accesToken = value;