Skip to content

Commit

Permalink
Versions 4.3.0 & 4.4.0 added Act method
Browse files Browse the repository at this point in the history
  • Loading branch information
Darran committed Jan 25, 2021
1 parent 5fbd2db commit 876af79
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@
namespace DalSoft.RestClient.Test.Unit
{
[TestFixture]
public class RestExtensionTests
public class RestClientExtensionTests
{
private const string Json = "{ 'name': 'Leanne Graham', 'username': 'Bret' }";
private RestClient _internalServerRestClient;
private RestClient _internalServerErrorRestClient;
private RestClient _restClient;


[SetUp]
public void RestExtensionTestsSetUp()
{
_internalServerRestClient = new RestClient("https://jsonplaceholder.typicode.com/",
_internalServerErrorRestClient = new RestClient("https://jsonplaceholder.typicode.com/",
new Config().UseUnitTestHandler(message =>
new HttpResponseMessage
{
Expand Down Expand Up @@ -57,7 +57,7 @@ public void Verify_FailingVerifications_ThrowsAggregateVerifiedFailedException()

var verifiedFailedException = Assert.ThrowsAsync<AggregateException>(async () =>
{
await _internalServerRestClient.Resource("users/1").Get()
await _internalServerErrorRestClient.Resource("users/1").Get()
.Verify<HttpResponseMessage>(verifyIsSuccessStatusCode) // Verify using HttpResponseMessage this should fail
.Verify<string>(s => s.Contains("Leanne Graham")) // Verify string response body
.Verify<User>(user => user.username == "Bret") // Verify model
Expand All @@ -75,7 +75,7 @@ await _internalServerRestClient.Resource("users/1").Get()
public async Task OnVerifyFailed_FailingVerificationsByDefault_ExceptionNotThrownAndOnlyFirstOnVerifyFailedCalled()
{
var invoked = 0;
await _internalServerRestClient.Resource("users/1").Get()
await _internalServerErrorRestClient.Resource("users/1").Get()
.Verify<HttpResponseMessage>(response => response.IsSuccessStatusCode) // Verify using HttpResponseMessage
.Verify<string>(s => s.Contains("Leanne GrahamXXX")) // Verify string response body
.Verify<User>(user => user.username == "Bret") // Verify model
Expand All @@ -97,7 +97,7 @@ public async Task OnVerifyFailed_FailingVerificationByDefault_ExceptionNotThrown
{
var invoked = 0;

var result = await _internalServerRestClient.Resource("users/1").Get()
var result = await _internalServerErrorRestClient.Resource("users/1").Get()
.Verify<HttpResponseMessage>(response => response.IsSuccessStatusCode)
.Verify<string>(s => s.Contains("Leanne GrahamXXX"))
.Verify<User>(user => user.username == "Bret")
Expand All @@ -115,7 +115,7 @@ public async Task OnVerifyFailed_FailingVerificationByDefault_ExceptionNotThrown
public async Task OnVerifyFailed_FailingVerificationAndThrowOnVerifyFailedTrue_NextOnVerifyFailedCalledAndExceptionsAreCorrectBetweenCallbacks()
{
var invoked = 0;
await _internalServerRestClient.Resource("users/1").Get()
await _internalServerErrorRestClient.Resource("users/1").Get()
.Verify<HttpResponseMessage>(response => response.IsSuccessStatusCode)
.Verify<string>(s => s.Contains("Leanne GrahamXXX"))
.Verify<User>(user => user.username == "Bret")
Expand Down Expand Up @@ -145,7 +145,7 @@ public void OnVerifyFailed_FailingVerificationAndThrowOnVerifyFailedTrueOnLastCa

var verifiedFailedException = Assert.ThrowsAsync<AggregateException>(async () =>
{
await _internalServerRestClient.Resource("users/1").Get()
await _internalServerErrorRestClient.Resource("users/1").Get()
.Verify<HttpResponseMessage>(response => response.IsSuccessStatusCode)
.Verify<string>(s => s.Contains("Leanne GrahamXXX"))
.Verify<User>(user => user.username == "Bret")
Expand Down Expand Up @@ -177,7 +177,7 @@ public void OnVerifyFailed_FailingVerificationAndThrowOnVerifyFailedTrueOnAllCal

var verifiedFailedException = Assert.ThrowsAsync<AggregateException>(async () =>
{
await _internalServerRestClient.Resource("users/1").Get()
await _internalServerErrorRestClient.Resource("users/1").Get()
.Verify<HttpResponseMessage>(response => response.IsSuccessStatusCode)
.Verify<string>(s => s.Contains("Leanne GrahamXXX"))
.Verify<User>(user => user.username == "Bret")
Expand Down Expand Up @@ -270,5 +270,45 @@ public void Map_MapFromObjectNullMap_ThrowsArgumentNullException()
await _restClient.Resource("users/1").Get().Map<User, UserCamelCase>(null);
});
}

[Test]
public async Task Act_ActOnDynamicObject_ActsAsExpected()
{
await _restClient.Resource("users/1").Get()
.Act(response =>
{
Assert.AreEqual("Leanne Graham", response.name);
Assert.AreEqual("Bret", response.username);
});
}

[Test]
public async Task Act_ActOnObject_ActsAsExpected()
{
await _restClient.Resource("users/1").Get()
.Act<User>(response =>
{
Assert.AreEqual("Leanne Graham", response.name);
Assert.AreEqual("Bret", response.username);
});
}

[Test]
public void Act_ActOnDynamicObjectNullAct_ThrowsArgumentNullException()
{
Assert.ThrowsAsync<ArgumentNullException>(async () =>
{
await _restClient.Resource("users/1").Get().Act(null);
});
}

[Test]
public void Map_ActOnObjectNullAct_ThrowsArgumentNullException()
{
Assert.ThrowsAsync<ArgumentNullException>(async () =>
{
await _restClient.Resource("users/1").Get().Act<User>(null);
});
}
}
}
24 changes: 17 additions & 7 deletions DalSoft.RestClient/DalSoft.RestClient.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,35 @@

<PropertyGroup>
<Description>
Dynamic Rest Client with fluent API. Supported Platforms: Windows, Linux, Mac, iOS, Android and UWP.
Chain methods to create the uri and verb for example new RestClient("https://jsonplaceholder.typicode.com").Posts(1).Get()
Explicit and implicit casting from dynamic for example Post post = RestClient("https://jsonplaceholder.typicode.com").Posts(1).Get()
DalSoft.RestClient - Powerful C# Rest Client

A beautiful, dynamic and fluent C# Rest Client for all platforms.
Create frictionless code whlist still using the HttpClient you already know.

Extend HttpClient using Pipeline features

Trusted by enterprises

Easy Unit Testing

Everything you need to know: https://restclient.dalsoft.io/
</Description>
<VersionPrefix>4.2.1</VersionPrefix>
<VersionPrefix>4.4.0</VersionPrefix>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<AssemblyName>DalSoft.RestClient</AssemblyName>
<PackageId>DalSoft.RestClient</PackageId>
<Title>DalSoft.RestClient - Powerful C# Rest Client</Title>
<Summary>C# Rest Client with a fluent API for all platforms</Summary>
<PackageTags>Rest Client;RestClient;REST;Client;JSON;Dynamic;WebApi;HttpClient;Core;iOS;Android;UWP</PackageTags>
<Owners>DalSoft</Owners>
<Authors>DalSoft, joakimjm</Authors>
<PackageReleaseNotes>
#99 Verify can deadlock in certain situations, because of the use of a local function.

#96 Verify should take an action as well as a Func
Added syntactic sugar for testing REST API's
</PackageReleaseNotes>
<PackageIconUrl>https://dalsoft.co.uk/images/icon.png</PackageIconUrl>
<PackageProjectUrl>https://restclient.dalsoft.io</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/DalSoft/DalSoft.RestClient/blob/master/LICENSE</PackageLicenseUrl>
<Repository>https://github.com/DalSoft/DalSoft.RestClient.git</Repository>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
Expand Down
100 changes: 70 additions & 30 deletions DalSoft.RestClient/RestClientExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -197,60 +197,58 @@ dynamic ContinuationFunction(Task<dynamic> task, object state)
);
}

public static Task<dynamic> Act<TResponse>(this Task<dynamic> request,
Action<TResponse, dynamic> onResponse,
public static Task<T> As<T>(this Task<dynamic> request,
CancellationToken cancellationToken = default,
TaskContinuationOptions continuationOptions = TaskContinuationOptions.None,
TaskScheduler scheduler = null) where TResponse : class
TaskScheduler scheduler = null) where T : class
{
var result = request.AsyncState ?? request.Result; // In the case of a faulted task and use the first to verify the result

dynamic ContinuationFunction(Task<dynamic> task, object state)
T ContinuationFunction(Task<dynamic> task)
{
if (task.IsFaulted || task.IsCanceled)
{
bool.TryParse(task.Exception?.InnerException?.Data[ThrowOnExceptionKey]?.ToString(), out var throwOnException);

if (throwOnException)
throw task.Exception.ToFlatAggregateException(throwOnException: true);
throw task.Exception.ToFlatAggregateException(throwOnException:true);

return null;
}

onResponse?.Invoke(task.Result as TResponse, result);

return state;
return (T)task.Result;
}

return request.ContinueWith
(
continuationFunction: (Func<Task<dynamic>, object, object>)((task, state) => ContinuationFunction(task, state)) ,
continuationFunction: task => ContinuationFunction(task),
cancellationToken: cancellationToken,
continuationOptions: continuationOptions,
scheduler: scheduler ?? TaskScheduler.Default,
state: result
scheduler: scheduler ?? TaskScheduler.Default
);
}

public static Task<T> As<T>(this Task<dynamic> request,
public static Task<TTo> Map<TFrom, TTo>(this Task<dynamic> request, Func<TFrom, TTo> map,
CancellationToken cancellationToken = default,
TaskContinuationOptions continuationOptions = TaskContinuationOptions.None,
TaskScheduler scheduler = null) where T : class
TaskScheduler scheduler = null) where TFrom : class where TTo : class
{

T ContinuationFunction(Task<dynamic> task)
TTo ContinuationFunction(Task<dynamic> task)
{
if (map == null) throw new ArgumentNullException(nameof(map));

if (task.IsFaulted || task.IsCanceled)
{
bool.TryParse(task.Exception?.InnerException?.Data[ThrowOnExceptionKey]?.ToString(), out var throwOnException);

if (throwOnException)
throw task.Exception.ToFlatAggregateException(throwOnException:true);
throw task.Exception.ToFlatAggregateException(throwOnException: true);

return null;
}

return (T)task.Result;
TFrom from = task.Result;

return map(from);
}

return request.ContinueWith
Expand All @@ -262,10 +260,10 @@ T ContinuationFunction(Task<dynamic> task)
);
}

public static Task<TTo> Map<TFrom, TTo>(this Task<dynamic> request, Func<TFrom, TTo> map,
public static Task<TTo> Map<TTo>(this Task<dynamic> request, Func<dynamic, TTo> map,
CancellationToken cancellationToken = default,
TaskContinuationOptions continuationOptions = TaskContinuationOptions.None,
TaskScheduler scheduler = null) where TFrom : class where TTo : class
TaskScheduler scheduler = null) where TTo : class
{
TTo ContinuationFunction(Task<dynamic> task)
{
Expand All @@ -281,9 +279,9 @@ TTo ContinuationFunction(Task<dynamic> task)
return null;
}

TFrom from = task.Result;
TTo to = map(task.Result);

return map(from);
return to;
}

return request.ContinueWith
Expand All @@ -295,14 +293,55 @@ TTo ContinuationFunction(Task<dynamic> task)
);
}

public static Task<TTo> Map<TTo>(this Task<dynamic> request, Func<dynamic, TTo> map,
public static Task<dynamic> Act<TResponse>(this Task<dynamic> request,
Action<TResponse> act,
CancellationToken cancellationToken = default,
TaskContinuationOptions continuationOptions = TaskContinuationOptions.None,
TaskScheduler scheduler = null) where TResponse : class
{
var result = request.AsyncState ?? request.Result; // In the case of a faulted task and use the first to verify the result

dynamic ContinuationFunction(Task<dynamic> task, object state)
{
if (act == null) throw new ArgumentNullException(nameof(act));

if (task.IsFaulted || task.IsCanceled)
{
bool.TryParse(task.Exception?.InnerException?.Data[ThrowOnExceptionKey]?.ToString(), out var throwOnException);

if (throwOnException)
throw task.Exception.ToFlatAggregateException(throwOnException: true);

return null;
}

TResponse response = task.Result;
act(response);

return state;
}

return request.ContinueWith
(
continuationFunction: (Func<Task<dynamic>, object, object>)((task, state) => ContinuationFunction(task, state)),
cancellationToken: cancellationToken,
continuationOptions: continuationOptions,
scheduler: scheduler ?? TaskScheduler.Default,
state: result
);
}

public static Task<dynamic> Act(this Task<dynamic> request,
Action<dynamic> act,
CancellationToken cancellationToken = default,
TaskContinuationOptions continuationOptions = TaskContinuationOptions.None,
TaskScheduler scheduler = null) where TTo : class
TaskScheduler scheduler = null)
{
TTo ContinuationFunction(Task<dynamic> task)
var result = request.AsyncState ?? request.Result; // In the case of a faulted task and use the first to verify the result

dynamic ContinuationFunction(Task<dynamic> task, object state)
{
if (map == null) throw new ArgumentNullException(nameof(map));
if (act == null) throw new ArgumentNullException(nameof(act));

if (task.IsFaulted || task.IsCanceled)
{
Expand All @@ -314,17 +353,18 @@ TTo ContinuationFunction(Task<dynamic> task)
return null;
}

TTo to = map(task.Result);
act(task.Result);

return to;
return state;
}

return request.ContinueWith
(
continuationFunction: task => ContinuationFunction(task),
continuationFunction: (Func<Task<dynamic>, object, object>)((task, state) => ContinuationFunction(task, state)),
cancellationToken: cancellationToken,
continuationOptions: continuationOptions,
scheduler: scheduler ?? TaskScheduler.Default
scheduler: scheduler ?? TaskScheduler.Default,
state: result
);
}

Expand Down

0 comments on commit 876af79

Please sign in to comment.