Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add extend access token request #3

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
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
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>

<IsPackable>false</IsPackable>
</PropertyGroup>
Expand Down
62 changes: 62 additions & 0 deletions FacebookCore.Tests/FacebookUserApiTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using FluentAssertions;
using Microsoft.Extensions.Configuration;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.IO;
using System.Threading.Tasks;

namespace FacebookCore.Tests
{
[TestClass]
public class FacebookUserApiTests
{
private readonly FacebookClient _client;
private readonly string _accessToken;

public FacebookUserApiTests()
{
string basePath = Directory.GetCurrentDirectory();

var builder = new ConfigurationBuilder()
.SetBasePath(basePath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: false);

IConfigurationRoot configurationRoot = builder.Build();

var clientId = configurationRoot["client_id"];
var clientSecret = configurationRoot["client_secret"];

_accessToken = configurationRoot["access_token"];
_client = new FacebookClient(clientId, clientSecret);
}

[TestMethod]
public async Task ShouldExtendUserToken()
{
var userApi = _client.GetUserApi(_accessToken);

var extendResult = await userApi.RequestExtendAccessToken();

extendResult["access_token"].ToString().Should().NotBeNullOrWhiteSpace();
}

[TestMethod]
public async Task ShouldGetUserInfo()
{
var userApi = _client.GetUserApi(_accessToken);

var extendResult = await userApi.RequestInformationAsync();

extendResult["id"].ToString().Should().NotBeNullOrWhiteSpace();
}

[TestMethod]
public async Task ShouldGetUserMetaInfo()
{
var userApi = _client.GetUserApi(_accessToken);

var metadata = await userApi.RequestMetaDataAsync();

metadata["metadata"].ToString().Should().NotBeNullOrWhiteSpace();
}
}
}
5 changes: 5 additions & 0 deletions FacebookCore.Tests/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"client_id": "<your-app-id-here>",
"client_secret": "<your-secret-here>",
"access_token": "<your-access-token-here>"
}
6 changes: 3 additions & 3 deletions FacebookCore.sln
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26228.9
# Visual Studio Version 16
VisualStudioVersion = 16.0.30104.148
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FacebookCore", "FacebookCore\FacebookCore.csproj", "{5AECB266-5389-4CE0-AF75-83F08FB469B6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FacebookCore.Tests", "FacebookCore.Tests\FacebookCore.Tests.csproj", "{BA8B6303-F772-4B12-971B-D694E4E6ED86}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FacebookCore.IntegrationTests", "FacebookCore.Tests\FacebookCore.IntegrationTests.csproj", "{BA8B6303-F772-4B12-971B-D694E4E6ED86}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down
5 changes: 3 additions & 2 deletions FacebookCore/APIs/FacebookAppApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public class FacebookAppApi : ApiBaseClass
/// <param name="client">Facebook client instance</param>
public FacebookAppApi(FacebookClient client) : base(client)
{
_appId = client.ClientId;
}

/// <summary>
Expand Down Expand Up @@ -51,7 +52,7 @@ public async Task<string> GetAccessTokenAsync()
_appAccessToken = json["access_token"].ToString();
return _appAccessToken;
}
catch (Exception ex)
catch
{
return null;
}
Expand Down Expand Up @@ -89,7 +90,7 @@ public string GetAppId(string accessToken)
string appId = accessToken.Split(new char[] { '|' })[0];
return appId;
}
catch (Exception e)
catch
{
return null;
}
Expand Down
17 changes: 16 additions & 1 deletion FacebookCore/APIs/FacebookUserApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,23 @@ public async Task<JObject> RequestInformationAsync(string[] fields = null)
/// <returns>JObject with user information</returns>
public async Task<JObject> RequestMetaDataAsync()
{
var response = await FacebookClient.GetAsync($"/{FacebookClient.GraphApiVersion}/me?metadata=1", _authToken);
var response = await FacebookClient.GetAsync($"/me?metadata=1", _authToken);
return response;
}

/// <summary>
/// Gets a user access token that lasts around 60 days.
/// </summary>
/// <source>https://developers.facebook.com/docs/facebook-login/access-tokens/refreshing/#get-a-long-lived-page-access-token</source>
/// <returns>JObject with extended access token</returns>
public async Task<JObject> RequestExtendAccessToken()
{
return await FacebookClient.GetAsync(
$"/oauth/access_token" +
$"?grant_type=fb_exchange_token" +
$"&client_id={FacebookClient.ClientId}" +
$"&client_secret={FacebookClient.ClientSecret}" +
$"&fb_exchange_token={_authToken}");
}
}
}
6 changes: 1 addition & 5 deletions FacebookCore/Collections/AppTestUsersCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,7 @@ namespace FacebookCore.Collections
{
public class AppTestUsersCollection : FacebookCollection<TestUser>
{
private FacebookClient _fbClient;
private string _appAccessToken;
private string _appId;

public FacebookCursor Cursor { get; internal set; }
public new FacebookCursor Cursor { get; internal set; }

public AppTestUsersCollection(FacebookClient client, string query, string token, FacebookCursor cursor = null) : base(client, query, token, TestUserMapper, cursor)
{
Expand Down
26 changes: 26 additions & 0 deletions FacebookCore/FacebookApiException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Newtonsoft.Json.Linq;
using System;
using System.Runtime.Serialization;

namespace FacebookCore
{
[Serializable]
internal class FacebookApiException : Exception
{
public FacebookApiException()
{
}

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

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

protected FacebookApiException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
}
71 changes: 48 additions & 23 deletions FacebookCore/FacebookClient.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Threading.Tasks;
Expand All @@ -17,7 +18,7 @@ namespace FacebookCore
public class FacebookClient
{
private FacebookAppApi _app;

internal string ClientId { get; private set; }

internal string ClientSecret { get; private set; }
Expand All @@ -30,14 +31,20 @@ public class FacebookClient
/// Application API
/// </summary>
public FacebookAppApi App => _app ?? (_app = new FacebookAppApi(this));

public FacebookClient(string clientId, string clientSecret)
{
ClientId = clientId;
ClientSecret = clientSecret;
RestClient = new RestClient("https://graph.facebook.com/");
}

public FacebookClient(FacebookConfig facebookConfig)
: this(facebookConfig.ClientId, facebookConfig.ClientSecret)
{
GraphApiVersion = facebookConfig.GraphApiVersion;
}

/// <summary>
/// Create a new instance of the Users API
/// </summary>
Expand Down Expand Up @@ -67,55 +74,73 @@ public FacebookPlacesApi GetPlacesApi(string authToken)
/// <param name="cursorDirection">What set of results should be taken (after or before the current cursor)</param>
/// <returns></returns>
public async Task<JObject> GetAsync(string path, string accessToken = null, FacebookCursor cursor = null, Direction cursorDirection = Direction.None)
{
path = StandardizePath(path);
path = AddAccessTokenToPathIfNeeded(path, accessToken);
path = AddCursorToPathIfNeeded(path, cursor, cursorDirection);

return SerializeResponse(await RestClient.GetAsync($"/{GraphApiVersion}{path}", false));
}

private string StandardizePath(string path)
{
if (!path.StartsWith("/"))
{
path = "/" + path;
}

if (accessToken == null)
{
accessToken = string.Empty;
}
else
return path;
}

private string AddAccessTokenToPathIfNeeded(string path, string accessToken)
{
var accessTokenPart = string.Empty;
if (accessToken != null)
{
accessToken = (path.Contains("?") ? "&" : "?") + "access_token=" + accessToken;
accessTokenPart = (path.Contains("?") ? "&" : "?") + "access_token=" + accessToken;
}

string cursorStr = string.Empty;
return path + accessTokenPart;
}

private string AddCursorToPathIfNeeded(string path, FacebookCursor cursor, Direction cursorDirection)
{
string cursorPart = string.Empty;

if (cursor != null && cursorDirection != Direction.None)
{
if ((cursorDirection == Direction.After || cursorDirection == Direction.Next) && !string.IsNullOrWhiteSpace(cursor.After))
if (!string.IsNullOrWhiteSpace(cursor.After) &&
(cursorDirection == Direction.After || cursorDirection == Direction.Next))
{
cursorStr = (path.Contains("?") ? "&" : "?") + "after=" + cursor.After;
cursorPart = (path.Contains("?") ? "&" : "?") + "after=" + cursor.After;
}
else if (!string.IsNullOrWhiteSpace(cursor.Before))
{
cursorStr = (path.Contains("?") ? "&" : "?") + "before=" + cursor.Before;
cursorPart = (path.Contains("?") ? "&" : "?") + "before=" + cursor.Before;
}
}

var response = await RestClient.GetAsync($"/{GraphApiVersion}{path}{accessToken}{cursorStr}", false);
var serializedResponse = SerializeResponse(response);
return serializedResponse;
return path + cursorPart;
}

internal JObject SerializeResponse(IRestResponse<string> response)
{
try
if (response.StatusCode == HttpStatusCode.OK)
{
if (response.StatusCode == HttpStatusCode.OK)
try
{
var jsreader = new JsonTextReader(new StringReader(response.RawData.ToString()));
var json = (JObject)new JsonSerializer().Deserialize(jsreader);
return json;
return (JObject)new JsonSerializer().Deserialize(jsreader);
}
catch
{
return null;
}
return null;
}
catch (Exception e)
else
{
return null;
throw new FacebookApiException(response.RawData.ToString(), response.Exception);
}
}
}
}
}
9 changes: 9 additions & 0 deletions FacebookCore/FacebookConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace FacebookCore
{
public interface FacebookConfig
{
string ClientId { get; set; }
string ClientSecret { get; set; }
string GraphApiVersion { get; set; }
}
}