Skip to content

Commit

Permalink
Exception refactor and bump to v1.5
Browse files Browse the repository at this point in the history
  • Loading branch information
qJake committed Jan 31, 2021
1 parent 44d49b8 commit d7271cb
Show file tree
Hide file tree
Showing 10 changed files with 176 additions and 25 deletions.
4 changes: 4 additions & 0 deletions HADotNet.Core.Tests/HADotNet.Core.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
<IsPackable>false</IsPackable>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>TRACE</DefineConstants>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="nunit" Version="3.13.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
Expand Down
28 changes: 24 additions & 4 deletions HADotNet.Core.Tests/InfoTests.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
using System;
using System.Threading.Tasks;
using HADotNet.Core.Clients;
using HADotNet.Core.Domain;
using NUnit.Framework;

namespace HADotNet.Core.Tests
{
/// <summary>
/// Tests for the Supervisor Info endpoints.
/// </summary>
/// <remarks>
/// NOTE: If you want to test against a non-Supervisor instance, you'll need to set
/// the compilation symbol TEST_ENV_HA_CORE which will test for the correct exception.
/// </remarks>
public class InfoTests
{
private Uri Instance { get; set; }
Expand All @@ -22,37 +30,49 @@ public void Setup()
[Test]
public async Task ShouldRetrieveSupervisorInfo()
{
var client = ClientFactory.GetClient<InfoClient>();

var client = ClientFactory.GetClient<InfoClient>();

#if TEST_ENV_HA_CORE
Assert.ThrowsAsync<SupervisorNotFoundException>(async () => await client.GetSupervisorInfo());
#else
var info = await client.GetSupervisorInfo();

Assert.AreEqual("ok", info.Result);
Assert.IsNotNull(info.Data);
Assert.IsNotNull(info.Data.Version);
#endif
}

[Test]
public async Task ShouldRetrieveHostInfo()
{
var client = ClientFactory.GetClient<InfoClient>();

var client = ClientFactory.GetClient<InfoClient>();

#if TEST_ENV_HA_CORE
Assert.ThrowsAsync<SupervisorNotFoundException>(async () => await client.GetHostInfo());
#else
var info = await client.GetHostInfo();

Assert.AreEqual("ok", info.Result);
Assert.IsNotNull(info.Data);
Assert.IsNotNull(info.Data.OperatingSystem);
#endif
}

[Test]
public async Task ShouldRetrieveCoreInfo()
{
var client = ClientFactory.GetClient<InfoClient>();

#if TEST_ENV_HA_CORE
Assert.ThrowsAsync<SupervisorNotFoundException>(async () => await client.GetCoreInfo());
#else
var info = await client.GetCoreInfo();

Assert.AreEqual("ok", info.Result);
Assert.IsNotNull(info.Data);
Assert.IsNotNull(info.Data.Version);
#endif
}
}
}
9 changes: 5 additions & 4 deletions HADotNet.Core/BaseClient.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Net;
using System.Threading.Tasks;
using HADotNet.Core.Domain;
using Newtonsoft.Json;
using RestSharp;

Expand Down Expand Up @@ -55,7 +56,7 @@ protected async Task<T> Get<T>(string path) where T : class
return JsonConvert.DeserializeObject<T>(resp.Content);
}

throw new Exception($"Unexpected response code {(int)resp.StatusCode} from Home Assistant API endpoint {path}.");
throw new HttpResponseException((int)resp.StatusCode, resp.ResponseStatus.ToString(), resp.ResponseUri.PathAndQuery, $"Unexpected GET response code {(int)resp.StatusCode} from Home Assistant API endpoint {path}.");
}

/// <summary>
Expand Down Expand Up @@ -98,7 +99,7 @@ protected async Task<T> Post<T>(string path, object body, bool isRawBody = false
return JsonConvert.DeserializeObject<T>(resp.Content);
}

throw new Exception($"Unexpected response code {(int)resp.StatusCode} from Home Assistant API endpoint {path}.");
throw new HttpResponseException((int)resp.StatusCode, resp.ResponseStatus.ToString(), resp.ResponseUri.PathAndQuery, $"Unexpected POST response code {(int)resp.StatusCode} from Home Assistant API endpoint {path}.");
}

/// <summary>
Expand Down Expand Up @@ -128,7 +129,7 @@ protected async Task<T> Delete<T>(string path) where T : class
return JsonConvert.DeserializeObject<T>(resp.Content);
}

throw new Exception($"Unexpected response code {(int)resp.StatusCode} from Home Assistant API endpoint {path}.");
throw new HttpResponseException((int)resp.StatusCode, resp.ResponseStatus.ToString(), resp.ResponseUri.PathAndQuery, $"Unexpected DELETE response code {(int)resp.StatusCode} from Home Assistant API endpoint {path}.");
}

/// <summary>
Expand All @@ -147,7 +148,7 @@ protected async Task Delete(string path)

if (!(resp.StatusCode == HttpStatusCode.OK || resp.StatusCode == HttpStatusCode.NoContent))
{
throw new Exception($"Unexpected response code {(int)resp.StatusCode} from Home Assistant API endpoint {path}.");
throw new HttpResponseException((int)resp.StatusCode, resp.ResponseStatus.ToString(), resp.ResponseUri.PathAndQuery, $"Unexpected DELETE response code {(int)resp.StatusCode} from Home Assistant API endpoint {path}.");
}
}
}
Expand Down
37 changes: 34 additions & 3 deletions HADotNet.Core/Clients/InfoClient.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Threading.Tasks;
using HADotNet.Core.Domain;
using HADotNet.Core.Models;

namespace HADotNet.Core.Clients
Expand All @@ -20,18 +21,48 @@ public InfoClient(Uri instance, string apiKey) : base(instance, apiKey) { }
/// Retrieves Supervisor information.
/// </summary>
/// <returns>A <see cref="SupervisorInfoObject"/> representing Supervisor informatio.</returns>
public async Task<ResponseObject<SupervisorInfoObject>> GetSupervisorInfo() => await Get<ResponseObject<SupervisorInfoObject>>("/api/hassio/supervisor/info");
public async Task<ResponseObject<SupervisorInfoObject>> GetSupervisorInfo()
{
try
{
return await Get<ResponseObject<SupervisorInfoObject>>("/api/hassio/supervisor/info");
}
catch (HttpResponseException hrex) when (hrex.StatusCode == 404)
{
throw new SupervisorNotFoundException("This does not appear to be a Home Assistant Supervisor instance. See inner exception for more details.", hrex);
}
}

/// <summary>
/// Retrieves Host information.
/// </summary>
/// <returns>A <see cref="HostInfoObject"/> representing Host informatio.</returns>
public async Task<ResponseObject<HostInfoObject>> GetHostInfo() => await Get<ResponseObject<HostInfoObject>>("/api/hassio/host/info");
public async Task<ResponseObject<HostInfoObject>> GetHostInfo()
{
try
{
return await Get<ResponseObject<HostInfoObject>>("/api/hassio/host/info");
}
catch (HttpResponseException hrex) when (hrex.StatusCode == 404)
{
throw new SupervisorNotFoundException("This does not appear to be a Home Assistant Supervisor instance. See inner exception for more details.", hrex);
}
}

/// <summary>
/// Retrieves Core information.
/// </summary>
/// <returns>A <see cref="CoreInfoObject"/> representing Host informatio.</returns>
public async Task<ResponseObject<CoreInfoObject>> GetCoreInfo() => await Get<ResponseObject<CoreInfoObject>>("/api/hassio/core/info");
public async Task<ResponseObject<CoreInfoObject>> GetCoreInfo()
{
try
{
return await Get<ResponseObject<CoreInfoObject>>("/api/hassio/core/info");
}
catch (HttpResponseException hrex) when (hrex.StatusCode == 404)
{
throw new SupervisorNotFoundException("This does not appear to be a Home Assistant Supervisor instance. See inner exception for more details.", hrex);
}
}
}
}
63 changes: 63 additions & 0 deletions HADotNet.Core/Domain/HttpResponseException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using System;

namespace HADotNet.Core.Domain
{
/// <summary>
/// Represents a failed HTTP call to a Home Assistant endpoint.
/// </summary>
public class HttpResponseException : Exception
{
/// <summary>
/// Gets the status code for the HTTP response.
/// </summary>
public int StatusCode { get; }

/// <summary>
/// Gets the network description, if the error was at the network level.
/// </summary>
public string NetworkDescription { get; }

/// <summary>
/// Gets the original request path.
/// </summary>
public string RequestPath { get; }

/// <summary>
/// Gets the error response body.
/// </summary>
public string ResponseBody { get; }

/// <summary>
/// Initializes a new HttpResponseException.
/// </summary>
public HttpResponseException(int statusCode, string networkDescription, string requestPath, string responseBody)
{
StatusCode = statusCode;
NetworkDescription = networkDescription;
RequestPath = requestPath;
ResponseBody = responseBody;
}

/// <summary>
/// Initializes a new HttpResponseException.
/// </summary>
public HttpResponseException(int statusCode, string networkDescription, string requestPath, string responseBody, string message) : base(message)
{
StatusCode = statusCode;
NetworkDescription = networkDescription;
RequestPath = requestPath;
ResponseBody = responseBody;
}

/// <summary>
/// Initializes a new HttpResponseException.
/// </summary>
public HttpResponseException(int statusCode, string networkDescription, string requestPath, string responseBody, string message, Exception innerException) : base(message, innerException)
{
StatusCode = statusCode;
NetworkDescription = networkDescription;
RequestPath = requestPath;
ResponseBody = responseBody;
}
}
}
31 changes: 31 additions & 0 deletions HADotNet.Core/Domain/SupervisorNotFoundException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;

namespace HADotNet.Core.Domain
{
/// <summary>
/// The exception that occurs when a Supervisor-only API call is made to a non-Supervisor environment.
/// </summary>
public class SupervisorNotFoundException : Exception
{
/// <summary>
/// Initializes a new instance of the SupervisorNotFoundException.
/// </summary>
public SupervisorNotFoundException()
{
}

/// <summary>
/// Initializes a new instance of the SupervisorNotFoundException.
/// </summary>
public SupervisorNotFoundException(string message) : base(message)
{
}

/// <summary>
/// Initializes a new instance of the SupervisorNotFoundException.
/// </summary>
public SupervisorNotFoundException(string message, Exception innerException) : base(message, innerException)
{
}
}
}
6 changes: 3 additions & 3 deletions HADotNet.Core/HADotNet.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@
<PackageTags>home-assistant external api library</PackageTags>
<Title>HADotNet Core Library</Title>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>1.4.1</Version>
<Version>1.5.0</Version>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/qJake/HADotNet</PackageProjectUrl>
<RepositoryUrl>https://github.com/qJake/HADotNet.git</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageReleaseNotes></PackageReleaseNotes>
<LangVersion>7.3</LangVersion>
<AssemblyVersion>1.4.1.0</AssemblyVersion>
<FileVersion>1.4.1.0</FileVersion>
<AssemblyVersion>1.5.0.0</AssemblyVersion>
<FileVersion>1.5.0.0</FileVersion>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
Expand Down
6 changes: 3 additions & 3 deletions HADotNet.Core/Models/AddonObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ public class AddonObject
public string Description { get; set; }

/// <summary>
/// <code>True</code> if icon is available, otherwise <code>False</code>.
/// <c>True</c> if icon is available, otherwise <c>False</c>.
/// </summary>
[JsonProperty("icon")]
public bool Icon { get; set; }

/// <summary>
/// <code>True</code> if logo is available, otherwise <code>False</code>.
/// <c>True</c> if logo is available, otherwise <c>False</c>.
/// </summary>
[JsonProperty("logo")]
public bool Logo { get; set; }
Expand Down Expand Up @@ -50,7 +50,7 @@ public class AddonObject
public string State { get; set; }

/// <summary>
/// <code>True</code> if update is available, otherwise <code>False</code>.
/// <c>True</c> if update is available, otherwise <c>False</c>.
/// </summary>
[JsonProperty("update_available")]
public bool UpdateAvailable { get; set; }
Expand Down
16 changes: 8 additions & 8 deletions HADotNet.Core/Models/HostInfoObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,43 +9,43 @@ namespace HADotNet.Core.Models
public class HostInfoObject
{
/// <summary>
/// Gets or sets the chassis.
/// Gets or sets the chassis type.
/// </summary>
[JsonProperty("chassis")]
public string Chassis { get; set; }

/// <summary>
/// Gets or sets the CPE.
/// Gets or sets the CPE string.
/// </summary>
[JsonProperty("cpe")]
public string Cpe { get; set; }

/// <summary>
/// Gets or sets the deployment.
/// Gets or sets the deployment type (e.g. production).
/// </summary>
[JsonProperty("deployment")]
public string Deployment { get; set; }

/// <summary>
/// Gets or sets the disk free.
/// Gets or sets the disk free, expressed in GB.
/// </summary>
[JsonProperty("disk_free")]
public double DiskFree { get; set; }

/// <summary>
/// Gets or sets the disk total.
/// Gets or sets the disk total, expressed in GB.
/// </summary>
[JsonProperty("disk_total")]
public double DiskTotal { get; set; }

/// <summary>
/// Gets or sets the disk used.
/// Gets or sets the disk used, expressed in GB.
/// </summary>
[JsonProperty("disk_used")]
public double DiskUsed { get; set; }

/// <summary>
/// Gets or sets the features.
/// Gets or sets the feature list.
/// </summary>
[JsonProperty("features")]
public IList<string> Features { get; set; }
Expand All @@ -57,7 +57,7 @@ public class HostInfoObject
public string Hostname { get; set; }

/// <summary>
/// Gets or sets the kernel.
/// Gets or sets the kernel version.
/// </summary>
[JsonProperty("kernel")]
public string Kernel { get; set; }
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ A simple, straighforward .NET Standard library for the [Home Assistant](https://
* Logbook API
* Services API
* States API
* Supervisor API (Supervisor-based installations only)
* Template API

## Getting Started
Expand Down

0 comments on commit d7271cb

Please sign in to comment.