Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Fitbit.NetCore/Fitbit.NetCore.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
<PackageOutputPath>..\NuGet</PackageOutputPath>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|netstandard1.3|AnyCPU'">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<WarningsAsErrors />
</PropertyGroup>

<ItemGroup>
<Compile Include="..\Fitbit.Portable\**\*.cs" Exclude="..\Fitbit.Portable\**\AssemblyInfo.cs">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
Expand Down
2 changes: 1 addition & 1 deletion Fitbit.Portable.Tests/Helpers/ResponseFaker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion Fitbit.Portable.Tests/InterceptorCounter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public Task<HttpResponseMessage> InterceptRequest(HttpRequestMessage request, Ca
public async Task<HttpResponseMessage> InterceptResponse(Task<HttpResponseMessage> response, CancellationToken cancellationToken, FitbitClient client)
{
ResponseCount++;
this.responseContent = await response.Result.Content.ReadAsStringAsync();
responseContent = await response.Result.Content.ReadAsStringAsync();
return null;
}
}
Expand Down
4 changes: 2 additions & 2 deletions Fitbit.Portable.Tests/RemoveSubscriptionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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();
Expand Down
1 change: 1 addition & 0 deletions Fitbit.Portable/Fitbit.Portable.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
<DefineConstants>TRACE;DEBUG;REQUIRES_JSONNET</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
Expand Down
125 changes: 57 additions & 68 deletions Fitbit.Portable/FitbitClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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 { return _accesToken; }
set
{
_accesToken = value;
//If we update the AccessToken after HttpClient has been created, then reconfigure authorization header
if (HttpClient != null)
{
ConfigureAuthorizationHeader();
}
}
}

Expand All @@ -37,28 +36,27 @@ public OAuth2AccessToken AccessToken
public HttpClient HttpClient { get; private set; }

public ITokenManager TokenManager { get; private set; }
public bool OAuth2TokenAutoRefresh { get; set; }
public List<IFitbitInterceptor> FitbitInterceptorPipeline { get; private set; }

public bool OAuth2TokenAutoRefresh { get; set; }

public List<IFitbitInterceptor> FitbitInterceptorPipeline { get; } = new List<IFitbitInterceptor>();

/// <summary>
/// Simplest constructor for OAuth2- requires the minimum information required by FitBit.Net client to make succesful calls to Fitbit Api
/// </summary>
/// <param name="credentials">Obtain this information from your developer dashboard. App credentials are required to perform token refresh</param>
/// <param name="accessToken">Authenticate with Fitbit API using OAuth2. Authenticator2 class is a helper for this process</param>
/// <param name="interceptor">An interface that enables sniffing all outgoing and incoming http requests from FitbitClient</param>
/// <param name="enableOAuth2TokenRefresh">Enable auto refresh for the OAuth2 auhtorization token</param>
/// <param name="tokenManager">ITokenManager implementation; if none provided an instance of DefaultTokenManager is used</param>
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<IFitbitInterceptor>();


AppCredentials = credentials;
AccessToken = accessToken;

if(interceptor != null)
{
this.FitbitInterceptorPipeline.Add(interceptor);
FitbitInterceptorPipeline.Add(interceptor);
}

ConfigureTokenManager(tokenManager);
Expand All @@ -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());
}
}

/// <summary>
Expand All @@ -85,13 +84,15 @@ private void ConfigureAutoRefresh(bool enableOAuth2TokenRefresh)
/// <param name="interceptor">An interface that enables sniffing all outgoing and incoming http requests from FitbitClient</param>
public FitbitClient(FitbitAppCredentials credentials, OAuth2AccessToken accessToken, List <IFitbitInterceptor> interceptors, bool enableOAuth2TokenRefresh = true, ITokenManager tokenManager = null)
{
this.AppCredentials = credentials;
this.AccessToken = accessToken;
AppCredentials = credentials;
AccessToken = accessToken;

this.FitbitInterceptorPipeline = new List<IFitbitInterceptor>();
FitbitInterceptorPipeline = new List<IFitbitInterceptor>();

if(interceptors != null && interceptors.Count > 0)
this.FitbitInterceptorPipeline.AddRange(interceptors);
if (interceptors != null && interceptors.Count > 0)
{
FitbitInterceptorPipeline.AddRange(interceptors);
}

ConfigureTokenManager(tokenManager);

Expand All @@ -100,7 +101,6 @@ public FitbitClient(FitbitAppCredentials credentials, OAuth2AccessToken accessTo
CreateHttpClientForOAuth2();
}


public FitbitClient(FitbitAppCredentials credentials, OAuth2AccessToken accessToken, bool enableOAuth2TokenRefresh) : this(credentials, accessToken, null, enableOAuth2TokenRefresh)
{

Expand All @@ -126,12 +126,12 @@ public FitbitClient(FitbitAppCredentials credentials, OAuth2AccessToken accessTo
/// </summary>
/// <param name="customFactory">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</param>
/// <param name="interceptor">An interface that enables sniffing all outgoing and incoming http requests from FitbitClient</param>
public FitbitClient(Func<HttpMessageHandler, HttpClient> customFactory, IFitbitInterceptor interceptor = null, ITokenManager tokenManager = null)
internal FitbitClient(Func<HttpMessageHandler, HttpClient> 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)
Expand All @@ -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();
}
Expand Down Expand Up @@ -202,16 +199,14 @@ public async Task<ActivitySummary> GetDayActivitySummaryAsync(DateTime activityD
/// <returns></returns>
public async Task<ActivitiesStats> 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<ActivitiesStats>(responseBody);
}

#region Sleep


/// <summary>
/// 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
Expand Down Expand Up @@ -270,7 +265,6 @@ public async Task<SleepDateRangeBase> GetSleepDateRangeAsync(DateTime startDate,
return data;
}


/// <summary>
/// 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.
Expand Down Expand Up @@ -329,7 +323,6 @@ public async Task<SleepLogListBase> GetSleepLogListAsync(DateTime dateToList, Sl
return data;
}


/// <summary>
/// Creates a log entry for a sleep event and returns a response in the format requested
/// </summary>
Expand All @@ -352,9 +345,7 @@ public async Task<SleepLogDateRange> PostLogSleepAsync(string startTime, int dur

return serialzer.Deserialize<SleepLogDateRange>(responeBody);
}

#endregion Sleep


/// <summary>
/// Requests the devices for the current logged in user
/// </summary>
Expand Down Expand Up @@ -424,14 +415,22 @@ public async Task<HeartActivitiesIntraday> 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);
Expand Down Expand Up @@ -501,8 +500,7 @@ public async Task<HeartActivitiesIntraday> 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);
}

/// <summary>
Expand Down Expand Up @@ -554,8 +552,8 @@ public async Task<IntradayData> 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(),
Expand All @@ -572,7 +570,7 @@ public async Task<IntradayData> GetIntraDayTimeSeriesAsync(IntradayResourceType

apiCall = FitbitClientHelperExtensions.ToFullUrl(apiCall);

HttpResponseMessage response = null;
HttpResponseMessage response;
try
{
response = await HttpClient.GetAsync(apiCall);
Expand All @@ -583,11 +581,9 @@ public async Task<IntradayData> 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();
Expand All @@ -599,16 +595,15 @@ public async Task<IntradayData> 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;
Expand Down Expand Up @@ -940,12 +935,7 @@ public async Task<List<ApiSubscription>> 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 });
Expand Down Expand Up @@ -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);
}
}

}
}
Loading