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

feat: support ad-hoc calls #129

Merged
Merged
Show file tree
Hide file tree
Changes from 2 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
191 changes: 185 additions & 6 deletions MetasysServices.Tests/MetasysClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@
using System.Collections.Generic;
using System.Net.Http;
using System.Linq;
using Flurl.Http.Testing;
using NUnit.Framework;
using Newtonsoft.Json.Linq;
using JohnsonControls.Metasys.BasicServices;
using Nito.AsyncEx;
using System.Threading.Tasks;
using System.Globalization;
using JohnsonControls.Metasys.BasicServices.Enums;
using Flurl.Util;
using Flurl.Http;
using System.IO;
using System.Text;
using System.Net;
using System.Net.Http.Json;

namespace MetasysServices.Tests
{
Expand Down Expand Up @@ -1548,7 +1550,7 @@ public void TestGetNetworkDevicesWithClassification()
"\"items\": [", device, "],",
"\"self\": \"https://hostname/api/v2/networkDevices?page=1&pageSize=200&sort=name\"}"));

var devices = client.NetworkDevices.GetAsync(NetworkDeviceClassificationEnum.Controller).GetAwaiter().GetResult();
var devices = client.NetworkDevices.GetAsync(NetworkDeviceClassificationEnum.Controller).GetAwaiter().GetResult();

httpTest.ShouldHaveCalled($"https://hostname/api/v2/networkDevices")
.WithVerb(HttpMethod.Get)
Expand Down Expand Up @@ -2403,7 +2405,7 @@ public async Task TestSendAsyncCheckRequestHeadersAreUsed()

httpTest.ShouldHaveCalled("https://hostname/api/v5/networkDevices?sort=itemReference")
.WithVerb(HttpMethod.Get)
.WithHeader("request-header")
.WithHeader("request-header", "header_value")
.Times(1);
}

Expand All @@ -2412,12 +2414,189 @@ public void TestSendAsyncWithInvalidAbsoluteUrlThrowsException()
{
var requestUrl = "https://different-hostname/api/v5/networkDevices";
var httpRequest = new HttpRequestMessage(HttpMethod.Get, requestUrl);
var e = Assert.Throws<MetasysHttpException>(() =>
var e = Assert.Throws<UriFormatException>(() =>
client.SendAsync(httpRequest).GetAwaiter().GetResult());

PrintMessage($"TestSendAsyncWithInvalidAbsoluteUrlThrowsException: {e.Message}", true);
}

[TestCase("v5/networkDevices", "v5/networkDevices")]
[TestCase("/v5/networkDevices", "v5/networkDevices")]
public async Task TestSendAsyncWithCustomPort(string requestUrl, string expectedRelativeUrl)
{
var originalHostname = client.Hostname;
var expectedUrl = $"https://{originalHostname}:8080/api/{expectedRelativeUrl}";

client.Hostname = $"{originalHostname}:8080";

var httpRequest = new HttpRequestMessage(HttpMethod.Get, requestUrl);
await client.SendAsync(httpRequest);

Assert.AreEqual(expectedUrl, httpTest.CallLog.Last().Request.RequestUri.ToString());
hicksjacobp marked this conversation as resolved.
Show resolved Hide resolved

httpTest.ShouldHaveCalled(expectedUrl)
.WithVerb(HttpMethod.Get)
.Times(1);

client.Hostname = originalHostname;
}

[Test]
public async Task TestSendAsyncCanGetJson()
{
httpTest.RespondWithJson(new TestData { id = 1, name = "Metasys" });

var httpRequest = new HttpRequestMessage(HttpMethod.Get, "https://hostname/api/v5/getJson");
var data = await client.SendAsync(httpRequest).ReceiveJson<TestData>();

Assert.AreEqual(1, data.id);
Assert.AreEqual("Metasys", data.name);
}

[Test]
public async Task TestSendAsyncCanGetJsonDynamic()
{
httpTest.RespondWithJson(new { id = 1, name = "Metasys" });

var httpRequest = new HttpRequestMessage(HttpMethod.Get, "https://hostname/api/v5/getJsonDynamic");
var data = await client.SendAsync(httpRequest).ReceiveJson();

Assert.AreEqual(1, data.id);
Assert.AreEqual("Metasys", data.name);
}

[Test]
public async Task TestSendAsyncCanGetJsonDynamicList()
{
httpTest.RespondWithJson(
new[] {
new { id = 1, name = "Metasys" },
new { id = 2, name = "Client" }
}
);

var httpRequest = new HttpRequestMessage(HttpMethod.Get, "https://hostname/api/v5/getJsonDynamicList");
var data = await client.SendAsync(httpRequest).ReceiveJsonList();

Assert.AreEqual(1, data[0].id);
Assert.AreEqual("Metasys", data[0].name);
Assert.AreEqual(2, data[1].id);
Assert.AreEqual("Client", data[1].name);
}

[Test]
public async Task TestSendAsyncCanGetString()
{
httpTest.RespondWith("Metasys Client");

var httpRequest = new HttpRequestMessage(HttpMethod.Get, "https://hostname/api/v5/getString");
var data = await client.SendAsync(httpRequest).ReceiveString();

Assert.AreEqual("Metasys Client", data);
}

[Test]
public async Task TestSendAsyncCanGetStream()
{
httpTest.RespondWith("Metasys Client");

var httpRequest = new HttpRequestMessage(HttpMethod.Get, "https://hostname/api/v5/getStream");
var data = await client.SendAsync(httpRequest).ReceiveStream();

Assert.AreEqual(new MemoryStream(Encoding.UTF8.GetBytes("Metasys Client")), data);
}

[Test]
public async Task TestSendAsyncCanGetBytes()
{
httpTest.RespondWith("Metasys Client");

var httpRequest = new HttpRequestMessage(HttpMethod.Get, "https://hostname/api/v5/getBytes");
var data = await client.SendAsync(httpRequest).ReceiveBytes();

Assert.AreEqual(Encoding.UTF8.GetBytes("Metasys Client"), data);
}

[Test]
public async Task TestSendAsyncCanMakePutRequest()
{
var httpRequest = new HttpRequestMessage(HttpMethod.Put, "https://hostname/api/v5/put")
{
Content = JsonContent.Create(new { id = 1, name = "Metasys" })
};

var response = await client.SendAsync(httpRequest);

Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
hicksjacobp marked this conversation as resolved.
Show resolved Hide resolved
}

[Test]
public async Task TestSendAsyncCanMakePostRequest()
{
var httpRequest = new HttpRequestMessage(HttpMethod.Post, "https://hostname/api/v5/post")
{
Content = JsonContent.Create(new { id = 1, name = "Metasys" })
};

var response = await client.SendAsync(httpRequest);

Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
hicksjacobp marked this conversation as resolved.
Show resolved Hide resolved
}

[Test]
public async Task TestSendAsyncCanMakePatchRequest()
{
var httpRequest = new HttpRequestMessage(HttpMethod.Patch, "https://hostname/api/v5/patch")
{
Content = JsonContent.Create(new { name = "Metasys Client" })
};

var response = await client.SendAsync(httpRequest);

Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
hicksjacobp marked this conversation as resolved.
Show resolved Hide resolved
}

[Test]
public async Task TestSendAsyncCanMakeDeleteRequest()
{
var httpRequest = new HttpRequestMessage(HttpMethod.Delete, "https://hostname/api/v5/delete");
var response = await client.SendAsync(httpRequest);

Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}

[Test]
public async Task TestSendAsyncCanMakeHeadRequest()
{
var httpRequest = new HttpRequestMessage(HttpMethod.Head, "https://hostname/api/v5/head");
var response = await client.SendAsync(httpRequest);

Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}

[Test]
public async Task TestSendAsyncCanMakeTraceRequest()
{
var httpRequest = new HttpRequestMessage(HttpMethod.Trace, "https://hostname/api/v5/trace");
var response = await client.SendAsync(httpRequest);

Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}

[Test]
public async Task TestSendAsyncCanMakeOptionsRequest()
{
var httpRequest = new HttpRequestMessage(HttpMethod.Options, "https://hostname/api/v5/options");
var response = await client.SendAsync(httpRequest);

Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}

private class TestData
{
public int id { get; set; }
public string name { get; set; }
}
#endregion
}
}
4 changes: 4 additions & 0 deletions MetasysServices/Interfaces/IMetasysClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,10 @@ public interface IMetasysClient :IBasicService

/// <summary>
/// Send an HTTP request as an asynchronous operation.
///
/// <para>
/// This method currently only supports 1 value per header rather than multiple. In a future revision, this is planned to be addressed.
/// </para>
/// </summary>
/// <param name="request">The HTTP request message to send.</param>
/// <param name="completionOption"> When the operation should complete (as soon as a response is available or after reading the whole response content).</param>
Expand Down
8 changes: 6 additions & 2 deletions MetasysServices/MetasysClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1219,7 +1219,11 @@ public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage requestMessa
{
var flurlRequest = Client.Request();
flurlRequest.Url = GetUrlFromHttpRequest(requestMessage);
flurlRequest.WithHeaders(requestMessage.Headers);

// Flurl.Http 2.4.2 can only work with 1 value per header
// Once upgraded to Flurl 3.0.1, then multiple values can be supported
var headers = requestMessage.Headers.ToDictionary((kvp) => kvp.Key, (kvp) => kvp.Value.First());
flurlRequest.WithHeaders(headers);

response = await flurlRequest.SendAsync(requestMessage.Method, requestMessage.Content, cancellationToken, completionOption).ConfigureAwait(false);
}
Expand All @@ -1238,7 +1242,7 @@ private Url GetUrlFromHttpRequest(HttpRequestMessage requestMessage)
{
if (Uri.Compare(baseUri, requestMessage.RequestUri, UriComponents.SchemeAndServer, UriFormat.SafeUnescaped, StringComparison.OrdinalIgnoreCase) != 0)
{
throw new MetasysHttpException("HTTP request can not be made.", "You are trying to connect to a different host.");
throw new UriFormatException("HTTP request can not be made. Scheme or Host is invalid.");
}
return new Url(requestUri);
}
Expand Down