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

Add new DeviceClient implementation #476

Merged
merged 1 commit into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
32 changes: 32 additions & 0 deletions Source/v2/Meadow.Cloud.Client/Devices/AddDeviceRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
namespace Meadow.Cloud.Client.Devices;

public class AddDeviceRequest(string id, string orgId, string publicKey)
{
public AddDeviceRequest(string id, string name, string orgId, string publicKey)
: this(id, orgId, publicKey)
{
Name = name;
}

public AddDeviceRequest(string id, string name, string orgId, string collectionId, string publicKey)
: this(id, orgId, publicKey)
{
Name = name;
CollectionId = collectionId;
}

[JsonPropertyName("id")]
public string Id { get; set; } = id;

[JsonPropertyName("name")]
public string? Name { get; set; }

[JsonPropertyName("orgId")]
public string OrgId { get; set; } = orgId;

[JsonPropertyName("collectionId")]
public string? CollectionId { get; set; }

[JsonPropertyName("publicKey")]
public string PublicKey { get; set; } = publicKey;
}
16 changes: 16 additions & 0 deletions Source/v2/Meadow.Cloud.Client/Devices/AddDeviceResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace Meadow.Cloud.Client.Devices;

public class AddDeviceResponse(string id, string name, string orgId, string collectionId)
{
[JsonPropertyName("id")]
public string Id { get; set; } = id;

[JsonPropertyName("name")]
public string? Name { get; set; } = name;

[JsonPropertyName("orgId")]
public string OrgId { get; set; } = orgId;

[JsonPropertyName("collectionId")]
public string? CollectionId { get; set; } = collectionId;
}
23 changes: 18 additions & 5 deletions Source/v2/Meadow.Cloud.Client/Devices/DeviceClient.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
namespace Meadow.Cloud.Client.Devices;

public interface IDeviceClient
public class DeviceClient : MeadowCloudClientBase, IDeviceClient
{
}
public DeviceClient(MeadowCloudContext meadowCloudContext, ILogger logger)
: base(meadowCloudContext, logger)
{
}

public class DeviceClient : IDeviceClient
{
}
public async Task<AddDeviceResponse> AddDevice(AddDeviceRequest request, CancellationToken cancellationToken = default)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}

using var httpRequest = CreateHttpRequestMessage(HttpMethod.Post, "api/v1/devices", request);
using var httpResponse = await HttpClient.SendAsync(httpRequest, cancellationToken);

return await ProcessResponse<AddDeviceResponse>(httpResponse, cancellationToken);
}
}
6 changes: 6 additions & 0 deletions Source/v2/Meadow.Cloud.Client/Devices/IDeviceClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Meadow.Cloud.Client.Devices;

public interface IDeviceClient
{
Task<AddDeviceResponse> AddDevice(AddDeviceRequest request, CancellationToken cancellationToken = default);
}
2 changes: 1 addition & 1 deletion Source/v2/Meadow.Cloud.Client/Firmware/FirmwareClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public async Task<IEnumerable<GetFirmwareVersionsResponse>> GetVersions(string t

if (response.StatusCode == HttpStatusCode.NotFound)
{
return Enumerable.Empty<GetFirmwareVersionsResponse>();
return [];
}

return await ProcessResponse<IEnumerable<GetFirmwareVersionsResponse>>(response, cancellationToken);
Expand Down
6 changes: 4 additions & 2 deletions Source/v2/Meadow.Cloud.Client/MeadowCloudClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public class MeadowCloudClient : IMeadowCloudClient
public const string DefaultHost = "https://www.meadowcloud.co";
public static readonly Uri DefaultHostUri = new(DefaultHost);

private readonly Lazy<DeviceClient> _deviceClient;
private readonly Lazy<FirmwareClient> _firmwareClient;
private readonly MeadowCloudContext _meadowCloudContext;
private readonly IdentityManager _identityManager;
Expand All @@ -22,15 +23,16 @@ public MeadowCloudClient(HttpClient httpClient, IdentityManager identityManager,
loggerFactory ??= NullLoggerFactory.Instance;

_meadowCloudContext = new(httpClient, userAgent);


_deviceClient = new Lazy<DeviceClient>(() => new DeviceClient(_meadowCloudContext, loggerFactory.CreateLogger<DeviceClient>()));
_firmwareClient = new Lazy<FirmwareClient>(() => new FirmwareClient(_meadowCloudContext, loggerFactory.CreateLogger<FirmwareClient>()));
_identityManager = identityManager;
}

public IApiTokenClient ApiToken => throw new NotImplementedException("This client is not implemented yet. Please use the 'ApiTokenService' instead.");
public ICollectionClient Collection => throw new NotImplementedException("This client is not implemented yet. Please use the 'CollectionService' instead.");
public ICommandClient Command => throw new NotImplementedException("This client is not implemented yet. Please use the 'CommandService' instead.");
public IDeviceClient Device => throw new NotImplementedException("This client is not implemented yet. Please use the 'DeviceService' instead.");
public IDeviceClient Device => _deviceClient.Value;
public IFirmwareClient Firmware => _firmwareClient.Value;
public IPackageClient Package => throw new NotImplementedException("This client is not implemented yet. Please use the 'PackageService' instead.");
public IUserClient User => throw new NotImplementedException("This client is not implemented yet. Please use the 'UserService' instead.");
Expand Down
19 changes: 18 additions & 1 deletion Source/v2/Meadow.Cloud.Client/MeadowCloudClientBase.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace Meadow.Cloud.Client;
using System.Net.Http;

namespace Meadow.Cloud.Client;

public abstract class MeadowCloudClientBase
{
Expand Down Expand Up @@ -54,6 +56,21 @@ protected HttpRequestMessage CreateHttpRequestMessage(HttpMethod method, string
return CreateHttpRequestMessage(method, new Uri(urlBuilder.ToString(), UriKind.Relative));
}

protected HttpRequestMessage CreateHttpRequestMessage<T>(HttpMethod method, string requestUri, T request)
{
var json = JsonSerializer.SerializeToUtf8Bytes(request);
var content = new ByteArrayContent(json);
content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");

var httpRequest = new HttpRequestMessage(method, new Uri(MeadowCloudContext.BaseAddress, requestUri))
{
Content = content
};
SetHeaders(httpRequest);

return httpRequest;
}

private static IReadOnlyDictionary<string, IEnumerable<string>> GetHeaders(HttpResponseMessage response)
{
var headers = new Dictionary<string, IEnumerable<string>>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
namespace Meadow.Cloud.Client.Unit.Tests.DeviceClientTests;

public class AddDeviceTests
{
private readonly FakeableHttpMessageHandler _handler;
private readonly DeviceClient _deviceClient;

public AddDeviceTests()
{
_handler = A.Fake<FakeableHttpMessageHandler>();
var httpClient = new HttpClient(_handler) { BaseAddress = new Uri("https://example.org") };

A.CallTo(() => _handler
.FakeSendAsync(A<HttpRequestMessage>.Ignored, A<CancellationToken>.Ignored))
.Returns(new HttpResponseMessage(HttpStatusCode.NotFound));

var context = new MeadowCloudContext(httpClient, new Uri("https://example.org"), new MeadowCloudUserAgent("Meadow.Cloud.Client.Unit.Tests"));
_deviceClient = new DeviceClient(context, NullLogger.Instance);
}

[Fact]
public async Task AddDevice_WithNullRequest_ShouldThrowException()
{
// Act/Assert
await Assert.ThrowsAsync<ArgumentNullException>(() => _deviceClient.AddDevice(null!));
}

[Theory]
[InlineData(HttpStatusCode.BadRequest)]
[InlineData(HttpStatusCode.Conflict)]
[InlineData(HttpStatusCode.NotFound)]
[InlineData(HttpStatusCode.Unauthorized)]
[InlineData(HttpStatusCode.InternalServerError)]
public async Task AddDevice_WithUnsuccessfulResponse_ShouldThrowException(HttpStatusCode httpStatusCode)
{
// Arrange
var addDeviceRequest = new AddDeviceRequest("id", "orgId", "publicKey");

A.CallTo(() => _handler
.FakeSendAsync(
A<HttpRequestMessage>.That.Matches(r => r.RequestUri!.AbsolutePath == $"/api/v1/devices"),
A<CancellationToken>.Ignored))
.Returns(new HttpResponseMessage(httpStatusCode));

// Act/Assert
var ex = await Assert.ThrowsAsync<MeadowCloudException>(() => _deviceClient.AddDevice(addDeviceRequest));
Assert.Equal(httpStatusCode, ex.StatusCode);
}

[Fact]
public async Task AddDevice_WithResponse_ShouldReturnResult()
{
// Arrange
var addDeviceRequest = new AddDeviceRequest("device-id", "device-org-id", "device-public-key");

A.CallTo(() => _handler
.FakeSendAsync(
A<HttpRequestMessage>.That.Matches(r => r.RequestUri!.AbsolutePath == $"/api/v1/devices"),
A<CancellationToken>.Ignored))
.Returns(new HttpResponseMessage(HttpStatusCode.OK)
{
Content = JsonContent.Create(new AddDeviceResponse("device-id", "name", "device-org-id", "device-collection-id"))
});

// Act
var response = await _deviceClient.AddDevice(addDeviceRequest);

// Assert
Assert.NotNull(response);
Assert.Equal("device-id", response.Id);
}
}
2 changes: 2 additions & 0 deletions Source/v2/Tests/Meadow.Cloud.Client.Unit.Tests/Usings.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
global using FakeItEasy;
global using Meadow.Cloud.Client.Devices;
global using Meadow.Cloud.Client.Firmware;
global using Meadow.Cloud.Client.Unit.Tests.Builders;
global using Microsoft.Extensions.Logging.Abstractions;
global using System.Net;
global using System.Net.Http.Headers;
global using System.Net.Http.Json;
Expand Down
Loading