Add API client in preparation for new microservices.
This commit is contained in:
parent
d9dc33004f
commit
176c401cf1
4 changed files with 144 additions and 0 deletions
83
Website.Tests/Data/ApiClientTests.cs
Normal file
83
Website.Tests/Data/ApiClientTests.cs
Normal file
|
@ -0,0 +1,83 @@
|
|||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using RichardSzalay.MockHttp;
|
||||
using Website.Data;
|
||||
using Xunit;
|
||||
|
||||
namespace Website.Tests.Data {
|
||||
public class ApiClientTests {
|
||||
private class TestApiClient : ApiClient {
|
||||
public TestApiClient(HttpClient client) : base(client) {
|
||||
}
|
||||
|
||||
public async Task<T> Post<T>(string url, object value, object query = null) => await base.Post<T>(url, value, query);
|
||||
|
||||
public async Task<T> Get<T>(string url, object query = null) => await base.Get<T>(url, query);
|
||||
}
|
||||
|
||||
private class TestObject {
|
||||
public int TestProperty { get; set; }
|
||||
}
|
||||
|
||||
private static HttpClient MakeHttpClient(HttpMethod method, string url, string response) {
|
||||
var httpMessageHandler = new MockHttpMessageHandler();
|
||||
httpMessageHandler.When(method, url).Respond("application/json", response);
|
||||
|
||||
var httpClient = httpMessageHandler.ToHttpClient();
|
||||
httpClient.BaseAddress = new Uri("http://example.com");
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Get_WithUrl_ReturnsResult() {
|
||||
var httpClient = MakeHttpClient(HttpMethod.Get, "/test", @"{""TestProperty"":1}");
|
||||
|
||||
var expected = new TestObject {TestProperty = 1};
|
||||
|
||||
var client = new TestApiClient(httpClient);
|
||||
(await client.Get<TestObject>("/test")).Should().BeEquivalentTo(expected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Get_WithUrlAndQuery_ReturnsResult() {
|
||||
var httpClient = MakeHttpClient(HttpMethod.Get, "/test?query1=1&query2=2", @"{""TestProperty"":1}");
|
||||
|
||||
var expected = new TestObject {TestProperty = 1};
|
||||
|
||||
var client = new TestApiClient(httpClient);
|
||||
(await client.Get<TestObject>("/test", new {query1 = 1, query2 = 2})).Should().BeEquivalentTo(expected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Get_WithUrlThatReturns404_ThrowsException() {
|
||||
var httpClient = MakeHttpClient(HttpMethod.Get, "/test", "");
|
||||
|
||||
var client = new TestApiClient(httpClient);
|
||||
|
||||
client.Invoking(apiClient => apiClient.Get<TestObject>("/404")).Should().Throw<ApiCallException>()
|
||||
.WithMessage("Error calling API http://example.com/404: 404, No matching mock handler for \"GET http://example.com/404\"");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Post_WithUrlAndValue_ReturnsResult() {
|
||||
var httpClient = MakeHttpClient(HttpMethod.Post, "/test", @"{""TestProperty"":1}");
|
||||
|
||||
var expected = new TestObject {TestProperty = 1};
|
||||
|
||||
var client = new TestApiClient(httpClient);
|
||||
(await client.Post<TestObject>("/test", "value")).Should().BeEquivalentTo(expected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Post_WithUrlAndValueAndQuery_ReturnsResult() {
|
||||
var httpClient = MakeHttpClient(HttpMethod.Post, "/test?query1=1&query2=2", @"{""TestProperty"":1}");
|
||||
|
||||
var expected = new TestObject {TestProperty = 1};
|
||||
|
||||
var client = new TestApiClient(httpClient);
|
||||
(await client.Post<TestObject>("/test", "value", new {query1 = 1, query2 = 2})).Should().BeEquivalentTo(expected);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@
|
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
|
||||
<PackageReference Include="mysqlconnector" Version="0.61.0" />
|
||||
<PackageReference Include="nsubstitute" Version="4.2.1" />
|
||||
<PackageReference Include="RichardSzalay.MockHttp" Version="6.0.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
|
12
Website/Data/ApiCallException.cs
Normal file
12
Website/Data/ApiCallException.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace Website.Data {
|
||||
public class ApiCallException : Exception {
|
||||
private readonly HttpResponseMessage Response;
|
||||
|
||||
public ApiCallException(HttpResponseMessage response):base($"Error calling API {response.RequestMessage.RequestUri}: {(int)response.StatusCode}, {response.ReasonPhrase}") {
|
||||
Response = response;
|
||||
}
|
||||
}
|
||||
}
|
48
Website/Data/ApiClient.cs
Normal file
48
Website/Data/ApiClient.cs
Normal file
|
@ -0,0 +1,48 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Website.Data {
|
||||
public abstract class ApiClient {
|
||||
private readonly HttpClient _client;
|
||||
|
||||
protected ApiClient(HttpClient client) {
|
||||
_client = client;
|
||||
}
|
||||
|
||||
private IDictionary<string, string> ParseQueryParameters(object query) {
|
||||
var type = query.GetType();
|
||||
var props = type.GetProperties();
|
||||
return props.ToDictionary(info => info.Name, info => info.GetValue(query, null).ToString());
|
||||
}
|
||||
|
||||
private async Task<T> Send<T>(HttpMethod method, string url, object query, HttpContent content = null) {
|
||||
if (query != null)
|
||||
url = QueryHelpers.AddQueryString(url, ParseQueryParameters(query));
|
||||
|
||||
using var httpRequest = new HttpRequestMessage(method, url) { Content = content };
|
||||
var response = await _client.SendAsync(httpRequest);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
throw new ApiCallException(response);
|
||||
|
||||
var json = await response.Content.ReadAsStringAsync();
|
||||
return JsonConvert.DeserializeObject<T>(json);
|
||||
}
|
||||
|
||||
protected async Task<T> Post<T>(string url, object value, object query = null) {
|
||||
var json = JsonConvert.SerializeObject(value);
|
||||
using var requestBody = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
return await Send<T>(HttpMethod.Post, url, query, requestBody);
|
||||
}
|
||||
|
||||
protected async Task<T> Get<T>(string url, object query = null) {
|
||||
return await Send<T>(HttpMethod.Get, url, query);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue