diff --git a/.gitignore b/.gitignore
index 55423de..0250353 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,8 @@
+# General macOS
+.DS_Store
+.AppleDouble
+.LSOverride
+
# Build results
bin/
obj/
diff --git a/Bynder/Sample/ApiSample.cs b/Bynder/Sample/ApiSample.cs
index 9b74a90..2e692f2 100644
--- a/Bynder/Sample/ApiSample.cs
+++ b/Bynder/Sample/ApiSample.cs
@@ -2,10 +2,7 @@
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
using System;
-using System.Configuration;
-using Bynder.Sdk;
using Bynder.Sdk.Service;
-using Bynder.Sample;
using Bynder.Sample.Utils;
using Bynder.Sdk.Query.Asset;
using Bynder.Sdk.Query.Collection;
@@ -24,6 +21,13 @@ public class ApiSample
///
/// arguments to main
public static void Main(string[] args)
+ {
+ //Choose your authentication method by commenting one of these lines.
+ PermanentToken();
+ Oauth();
+ }
+
+ private static void PermanentToken()
{
using (var client = ClientFactory.Create(Configuration.FromJson("Config.json")))
{
@@ -41,34 +45,33 @@ public static void Main(string[] args)
Console.WriteLine(collection.Name);
}
}
+ }
+ private static void Oauth()
+ {
using (var waitForToken = new WaitForToken())
+ using (var listener = new UrlHttpListener("http://localhost:8888/", waitForToken))
+ using (var client = ClientFactory.Create(Configuration.FromJson("Config.json")))
{
- using (var listener = new UrlHttpListener("http://localhost:8888/", waitForToken))
- {
- using (var client = ClientFactory.Create(Configuration.FromJson("Config.json")))
- {
- Browser.Launch(client.GetOAuthService().GetAuthorisationUrl("state example", "offline asset:read collection:read"));
- waitForToken.WaitHandle.WaitOne(50000);
+ Browser.Launch(client.GetOAuthService().GetAuthorisationUrl("state example", "offline asset:read collection:read"));
+ waitForToken.WaitHandle.WaitOne(50000);
- if (waitForToken.Success)
- {
- client.GetOAuthService().GetAccessTokenAsync(waitForToken.Token, "offline asset:read collection:read").Wait();
+ if (waitForToken.Success)
+ {
+ client.GetOAuthService().GetAccessTokenAsync(waitForToken.Token, "offline asset:read collection:read").Wait();
- var mediaList = client.GetAssetService().GetMediaListAsync(new MediaQuery()).Result;
+ var mediaList = client.GetAssetService().GetMediaListAsync(new MediaQuery()).Result;
- foreach (var media in mediaList)
- {
- Console.WriteLine(media.Name);
- }
+ foreach (var media in mediaList)
+ {
+ Console.WriteLine(media.Name);
+ }
- var collectionList = client.GetCollectionService().GetCollectionsAsync(new GetCollectionsQuery()).Result;
+ var collectionList = client.GetCollectionService().GetCollectionsAsync(new GetCollectionsQuery()).Result;
- foreach (var collection in collectionList)
- {
- Console.WriteLine(collection.Name);
- }
- }
+ foreach (var collection in collectionList)
+ {
+ Console.WriteLine(collection.Name);
}
}
}
diff --git a/Bynder/Sdk/Api/RequestSender/ApiRequestSender.cs b/Bynder/Sdk/Api/RequestSender/ApiRequestSender.cs
index 45a1d9f..e32903e 100644
--- a/Bynder/Sdk/Api/RequestSender/ApiRequestSender.cs
+++ b/Bynder/Sdk/Api/RequestSender/ApiRequestSender.cs
@@ -9,7 +9,6 @@
using System.Threading.Tasks;
using Bynder.Sdk.Model;
using Bynder.Sdk.Api.Requests;
-using Bynder.Sdk.Service;
using Newtonsoft.Json;
using Bynder.Sdk.Query.Decoder;
using Bynder.Sdk.Query;
@@ -28,6 +27,8 @@ internal class ApiRequestSender : IApiRequestSender
private SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
private IHttpRequestSender _httpSender;
+ public const string TokenPath = "/v6/authentication/oauth2/token";
+
///
/// Initializes a new instance of the class.
///
@@ -90,13 +91,21 @@ public async Task SendRequestAsync(Requests.Request request)
}
var httpRequest = CreateHttpRequest(request);
- var responseString = await _httpSender.SendHttpRequest(httpRequest).ConfigureAwait(false);
- if (!string.IsNullOrEmpty(responseString))
+ var response = await _httpSender.SendHttpRequest(httpRequest).ConfigureAwait(false);
+
+ var responseContent = response.Content;
+ if (response.Content == null)
+ {
+ return default(T);
+ }
+
+ var responseString = await responseContent.ReadAsStringAsync().ConfigureAwait(false);
+ if (string.IsNullOrEmpty(responseString))
{
- return JsonConvert.DeserializeObject(responseString);
+ return default(T);
}
- return default(T);
+ return JsonConvert.DeserializeObject(responseString);
}
private HttpRequestMessage CreateHttpRequest(Requests.Request request)
@@ -131,7 +140,7 @@ private async Task RefreshToken()
{
Authenticated = false,
Query = query,
- Path = $"/v6/authentication/oauth2/token",
+ Path = TokenPath,
HTTPMethod = HttpMethod.Post
};
diff --git a/Bynder/Sdk/Api/RequestSender/HttpRequestSender.cs b/Bynder/Sdk/Api/RequestSender/HttpRequestSender.cs
index efafd6c..3de9ecf 100644
--- a/Bynder/Sdk/Api/RequestSender/HttpRequestSender.cs
+++ b/Bynder/Sdk/Api/RequestSender/HttpRequestSender.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
using System.Net.Http;
+using System.Reflection;
using System.Threading.Tasks;
namespace Bynder.Sdk.Api.RequestSender
@@ -14,17 +15,25 @@ internal class HttpRequestSender : IHttpRequestSender
{
private readonly HttpClient _httpClient = new HttpClient();
+ ///
+ /// User-Agent header we add to each request.
+ ///
+ public string UserAgent
+ {
+ get { return $"bynder-c-sharp-sdk/{Assembly.GetExecutingAssembly().GetName().Version.ToString()}"; }
+ }
+
///
- /// Sends the HTTP request and returns the content as string.
+ /// Sends the HTTP request and returns its response.
///
/// The HTTP request response.
/// HTTP request.
- public async Task SendHttpRequest(HttpRequestMessage httpRequest)
+ public async Task SendHttpRequest(HttpRequestMessage httpRequest)
{
+ httpRequest.Headers.Add("User-Agent", UserAgent);
var response = await _httpClient.SendAsync(httpRequest).ConfigureAwait(false);
-
response.EnsureSuccessStatusCode();
- return await response.Content.ReadAsStringAsync().ConfigureAwait(false);
+ return response;
}
///
diff --git a/Bynder/Sdk/Api/RequestSender/IHttpRequestSender.cs b/Bynder/Sdk/Api/RequestSender/IHttpRequestSender.cs
index 7b52963..d46f2b4 100644
--- a/Bynder/Sdk/Api/RequestSender/IHttpRequestSender.cs
+++ b/Bynder/Sdk/Api/RequestSender/IHttpRequestSender.cs
@@ -14,10 +14,10 @@ namespace Bynder.Sdk.Api.RequestSender
internal interface IHttpRequestSender : IDisposable
{
///
- /// Sends the HTTP request and returns the content as string.
+ /// Sends the HTTP request and returns its response.
///
/// The HTTP request response.
/// HTTP request.
- Task SendHttpRequest(HttpRequestMessage httpRequest);
+ Task SendHttpRequest(HttpRequestMessage httpRequest);
}
}
diff --git a/Bynder/Sdk/Bynder.Sdk.csproj b/Bynder/Sdk/Bynder.Sdk.csproj
index c4688c3..ac17e6e 100644
--- a/Bynder/Sdk/Bynder.Sdk.csproj
+++ b/Bynder/Sdk/Bynder.Sdk.csproj
@@ -6,10 +6,29 @@
Bynder
Bynder.Sdk
Copyright © Bynder
+ true
+ 2.2.0
+ BynderDevops
+ The main goal of this SDK is to speed up the integration of Bynder customers who use C# making it easier to connect to the Bynder API (http://docs.bynder.apiary.io/) and executing requests on it.
+ https://bynder.com/static/3.0/img/favicon-black.ico
+ en
+ https://github.com/Bynder/bynder-c-sharp-sdk/blob/master/LICENSE
+ true
+ BynderDevops
+ https://github.com/Bynder/bynder-c-sharp-sdk
+ Adds the SDK's name and version to each request in the User-Agent header, to enable better insight in the Bynder API usage.
+ The main goal of this SDK is to speed up the integration of Bynder customers who use C# making it easier to connect to the Bynder API (http://docs.bynder.apiary.io/) and executing requests on it.
+ Bynder API C# SDK
+ Bynder.Sdk
+ Bynder.Sdk
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
diff --git a/Bynder/Test/Api/RequestSender/ApiRequestSenderTest.cs b/Bynder/Test/Api/RequestSender/ApiRequestSenderTest.cs
index 4a8bf95..8d98bdc 100644
--- a/Bynder/Test/Api/RequestSender/ApiRequestSenderTest.cs
+++ b/Bynder/Test/Api/RequestSender/ApiRequestSenderTest.cs
@@ -15,147 +15,127 @@ namespace Bynder.Test.Api.RequestSender
{
public class ApiRequestSenderTest
{
- [Fact]
- public async Task WhenRequestIsPostThenParametersAreAddedToContent()
+ private const string _accessToken = "some_access_token";
+ private const string _authHeader = "Bearer " + _accessToken;
+ private const string _path = "/fake/api";
+ private const string _queryValue = "some_query_value";
+ private const string _queryString = "Item1=" + _queryValue;
+
+ private Mock _httpSenderMock;
+ private StubQuery _query;
+
+ public ApiRequestSenderTest()
{
- var httpSenderMock = new Mock();
- var query = new StubQuery
+ _httpSenderMock = new Mock();
+ _httpSenderMock
+ .Setup(sender => sender.SendHttpRequest(It.IsAny()))
+ .Returns(Task.FromResult(new HttpResponseMessage(System.Net.HttpStatusCode.OK)));
+ _query = new StubQuery
{
- Item1 = "Value"
+ Item1 = _queryValue
};
- var accessToken = "access_token";
+ }
- using (ApiRequestSender apiRequestSender = new ApiRequestSender(
- new Configuration{
- BaseUrl = new Uri("https://example.bynder.com"),
- },
- GetCredentials(true, accessToken),
- httpSenderMock.Object
- ))
- {
- var apiRequest = new ApiRequest()
- {
- Path = "/fake/api",
- HTTPMethod = HttpMethod.Post,
- Query = query
- };
-
- await apiRequestSender.SendRequestAsync(apiRequest);
-
- httpSenderMock.Verify(sender => sender.SendHttpRequest(
- It.Is(
- req => req.RequestUri.PathAndQuery.Contains("/fake/api")
- && req.Method == HttpMethod.Post
- && req.Headers.Authorization.ToString() == $"Bearer {accessToken}"
- && req.Content.ReadAsStringAsync().Result.Contains("Item1=Value")
- )));
-
- httpSenderMock.Verify(sender => sender.SendHttpRequest(
- It.IsAny()
- ), Times.Once);
- }
+ [Fact]
+ public async Task WhenRequestIsPostThenParametersAreAddedToContent()
+ {
+ await SendRequest(hasValidCredentials: true, httpMethod: HttpMethod.Post);
+
+ _httpSenderMock.Verify(sender => sender.SendHttpRequest(
+ It.Is(req =>
+ req.RequestUri.PathAndQuery.Contains(_path)
+ && req.Method == HttpMethod.Post
+ && req.Headers.Authorization.ToString() == _authHeader
+ && req.Content.ReadAsStringAsync().Result.Contains(_queryString)
+ )
+ ));
+
+ _httpSenderMock.Verify(sender => sender.SendHttpRequest(
+ It.IsAny()
+ ), Times.Once);
}
[Fact]
public async Task WhenCredentialInvalidTwoRequestsSent()
{
- var httpSenderMock = new Mock();
-
- var query = new StubQuery
- {
- Item1 = "Value"
- };
- var accessToken = "access_token";
-
- using (ApiRequestSender apiRequestSender = new ApiRequestSender(
- new Configuration{
- BaseUrl = new Uri("https://example.bynder.com"),
- },
- GetCredentials(false, accessToken),
- httpSenderMock.Object
- ))
- {
- var apiRequest = new ApiRequest()
- {
- Path = "/fake/api",
- HTTPMethod = HttpMethod.Get,
- Query = query
- };
-
- await apiRequestSender.SendRequestAsync(apiRequest);
-
- httpSenderMock.Verify(sender => sender.SendHttpRequest(
- It.Is(
- req => req.RequestUri.PathAndQuery.Contains("/oauth2/token")
- && req.Method == HttpMethod.Post
- )));
-
- httpSenderMock.Verify(sender => sender.SendHttpRequest(
- It.Is(
- req => req.RequestUri.PathAndQuery.Contains("/fake/api")
- && req.Method == HttpMethod.Get
- && req.Headers.Authorization.ToString() == $"Bearer {accessToken}"
- && req.RequestUri.Query.Contains("Item1=Value")
- )));
-
- httpSenderMock.Verify(sender => sender.SendHttpRequest(
- It.IsAny()
- ), Times.Exactly(2));
- }
+ await SendRequest(hasValidCredentials: false, httpMethod: HttpMethod.Get);
+
+ _httpSenderMock.Verify(sender => sender.SendHttpRequest(
+ It.Is(
+ req => req.RequestUri.PathAndQuery == ApiRequestSender.TokenPath
+ && req.Method == HttpMethod.Post
+ )
+ ));
+
+ _httpSenderMock.Verify(sender => sender.SendHttpRequest(
+ It.Is(
+ req => req.RequestUri.PathAndQuery.Contains(_path)
+ && req.Method == HttpMethod.Get
+ && req.Headers.Authorization.ToString() == _authHeader
+ && req.RequestUri.Query.Contains(_queryString)
+ )
+ ));
+
+ _httpSenderMock.Verify(sender => sender.SendHttpRequest(
+ It.IsAny()
+ ), Times.Exactly(2));
}
[Fact]
public async Task WhenRequestIsGetThenParametersAreAddedToUrl()
{
- var httpSenderMock = new Mock();
- var query = new StubQuery
- {
- Item1 = "Value"
- };
- var accessToken = "access_token";
+ await SendRequest(hasValidCredentials: true, httpMethod: HttpMethod.Get);
+
+ _httpSenderMock.Verify(sender => sender.SendHttpRequest(
+ It.Is(
+ req => req.RequestUri.PathAndQuery.Contains(_path)
+ && req.Method == HttpMethod.Get
+ && req.Headers.Authorization.ToString() == _authHeader
+ && req.RequestUri.Query.Contains(_queryString)
+ )
+ ));
+
+ _httpSenderMock.Verify(sender => sender.SendHttpRequest(
+ It.IsAny()
+ ), Times.Once);
+ }
+
+ private async Task SendRequest(bool hasValidCredentials, HttpMethod httpMethod)
+ {
+ await CreateApiRequestSender(hasValidCredentials).SendRequestAsync(
+ new ApiRequest()
+ {
+ Path = _path,
+ HTTPMethod = httpMethod,
+ Query = _query
+ }
+ );
+ }
- using (ApiRequestSender apiRequestSender = new ApiRequestSender(
- new Configuration{
+ private IApiRequestSender CreateApiRequestSender(bool hasValidCredentials)
+ {
+ return new ApiRequestSender(
+ new Configuration
+ {
BaseUrl = new Uri("https://example.bynder.com"),
},
- GetCredentials(true, accessToken),
- httpSenderMock.Object
- ))
- {
- var apiRequest = new ApiRequest
- {
- Path = "/fake/api",
- HTTPMethod = HttpMethod.Get,
- Query = query
- };
-
- await apiRequestSender.SendRequestAsync(apiRequest);
-
- httpSenderMock.Verify(sender => sender.SendHttpRequest(
- It.Is(
- req => req.RequestUri.PathAndQuery.Contains("/fake/api")
- && req.Method == HttpMethod.Get
- && req.Headers.Authorization.ToString() == $"Bearer {accessToken}"
- && req.RequestUri.Query.Contains("Item1=Value")
- )));
-
- httpSenderMock.Verify(sender => sender.SendHttpRequest(
- It.IsAny()
- ), Times.Once);
- }
+ GetCredentials(hasValidCredentials),
+ _httpSenderMock.Object
+ );
}
- private ICredentials GetCredentials(bool valid = true, string accessToken = null)
+ private ICredentials GetCredentials(bool isValid)
{
var credentialsMock = new Mock();
+
credentialsMock
.Setup(mock => mock.AreValid())
- .Returns(valid);
+ .Returns(isValid);
credentialsMock
.SetupGet(mock => mock.AccessToken)
- .Returns(accessToken);
+ .Returns(_accessToken);
credentialsMock
.SetupGet(mock => mock.TokenType)
diff --git a/Bynder/Test/Api/RequestSender/HttpRequestSenderTest.cs b/Bynder/Test/Api/RequestSender/HttpRequestSenderTest.cs
index 82bf28d..4d6c679 100644
--- a/Bynder/Test/Api/RequestSender/HttpRequestSenderTest.cs
+++ b/Bynder/Test/Api/RequestSender/HttpRequestSenderTest.cs
@@ -1,8 +1,10 @@
// Copyright (c) Bynder. All rights reserved.
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
+using System.Linq;
using System.Net;
using System.Net.Http;
+using System.Threading.Tasks;
using Bynder.Sdk.Api.RequestSender;
using Bynder.Test.Utils;
using Xunit;
@@ -12,16 +14,31 @@ namespace Bynder.Test.Api.RequestSender
public class HttpRequestSenderTest
{
[Fact]
- public void WhenErrorReceivedAnExceptionIsThown()
+ public async Task WhenSuccessReceivedResponseIsReturned()
{
- using (var testHttpListener = new TestHttpListener(HttpStatusCode.Forbidden, null))
+ using (var httpListener = new TestHttpListener(HttpStatusCode.OK))
+ using (var httpRequestSender = new HttpRequestSender())
{
- using (HttpRequestSender apiRequestSender = new HttpRequestSender())
- {
- HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Get, testHttpListener.ListeningUrl);
+ var requestMessage = new HttpRequestMessage(HttpMethod.Get, httpListener.ListeningUrl);
+ var response = await httpRequestSender.SendHttpRequest(requestMessage);
- Assert.ThrowsAsync(async () => await apiRequestSender.SendHttpRequest(requestMessage));
- }
+ Assert.Equal(
+ httpRequestSender.UserAgent,
+ response.RequestMessage.Headers.GetValues("User-Agent").First()
+ );
+
+ }
+ }
+ [Fact]
+ public async Task WhenErrorReceivedAnExceptionIsThown()
+ {
+ using (var httpListener = new TestHttpListener(HttpStatusCode.Forbidden))
+ using (var httpRequestSender = new HttpRequestSender())
+ {
+ var requestMessage = new HttpRequestMessage(HttpMethod.Get, httpListener.ListeningUrl);
+ var doRequest = httpRequestSender.SendHttpRequest(requestMessage);
+
+ await Assert.ThrowsAsync(() => doRequest);
}
}
}
diff --git a/Bynder/Test/Utils/TestHttpListener.cs b/Bynder/Test/Utils/TestHttpListener.cs
index f1ba166..e389377 100644
--- a/Bynder/Test/Utils/TestHttpListener.cs
+++ b/Bynder/Test/Utils/TestHttpListener.cs
@@ -20,6 +20,11 @@ public sealed class TestHttpListener : IDisposable
///
private readonly HttpListenerFactory _httpListenerFactory;
+ ///
+ /// Path of the file which contents will be the response to requests.
+ ///
+ private readonly string _responseContent;
+
///
/// Path of the file which contents will be the response to requests.
///
@@ -37,11 +42,13 @@ public sealed class TestHttpListener : IDisposable
/// Creates an instance of the class.
///
/// HTTP status code that the class will return when having requests
+ /// String whose contents the class will return in the response
/// File whose contents the class will return in the response
- public TestHttpListener(HttpStatusCode statusCode, string responsePath)
+ public TestHttpListener(HttpStatusCode statusCode, string responseContent = null, string responsePath = null)
{
- _responsePath = responsePath;
_statusCode = statusCode;
+ _responseContent = responseContent;
+ _responsePath = responsePath;
_httpListenerFactory = new HttpListenerFactory();
@@ -97,7 +104,15 @@ private void SendResponse(HttpListenerResponse response)
{
response.StatusCode = (int)_statusCode;
- if (!string.IsNullOrEmpty(_responsePath))
+ if (!string.IsNullOrEmpty(_responseContent))
+ {
+ using (var sw = new StreamWriter(response.OutputStream))
+ {
+ sw.Write(_responseContent);
+ }
+ }
+
+ else if (!string.IsNullOrEmpty(_responsePath))
{
var dr = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
using (var fs = new FileStream(Path.Combine(dr, _responsePath), FileMode.Open, FileAccess.Read))