From fbc588a66c3893cc22a88a6413e3f49f6875c252 Mon Sep 17 00:00:00 2001 From: lucas-mrq Date: Tue, 12 Mar 2024 14:22:08 +0100 Subject: [PATCH 1/8] Fix command issue Fix #1458 --- .../Services/LoRaWanManagementService.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/IoTHub.Portal.Infrastructure/Services/LoRaWanManagementService.cs b/src/IoTHub.Portal.Infrastructure/Services/LoRaWanManagementService.cs index e2199400d..be904a06b 100644 --- a/src/IoTHub.Portal.Infrastructure/Services/LoRaWanManagementService.cs +++ b/src/IoTHub.Portal.Infrastructure/Services/LoRaWanManagementService.cs @@ -10,15 +10,20 @@ namespace IoTHub.Portal.Infrastructure.Services using System.Text.Json; using System.Threading.Tasks; using IoTHub.Portal.Application.Services; + using IoTHub.Portal.Domain.Options; using IoTHub.Portal.Models.v10.LoRaWAN; + using Microsoft.Extensions.Options; internal class LoRaWanManagementService : ILoRaWanManagementService { private readonly HttpClient httpClient; - public LoRaWanManagementService(HttpClient httpClient) + public LoRaWanManagementService(HttpClient httpClient, IOptions loRaWANOptions) { this.httpClient = httpClient; + this.httpClient.BaseAddress = new Uri(loRaWANOptions?.Value.KeyManagementUrl); + this.httpClient.DefaultRequestHeaders.Add("x-functions-key", loRaWANOptions?.Value.KeyManagementCode); + this.httpClient.DefaultRequestHeaders.Add("api-version", "2022-03-04"); } public async Task GetRouterConfig(string loRaRegion) From bd510c7686bb52462911ef4ecdd80bbdcd0de62e Mon Sep 17 00:00:00 2001 From: lucas-mrq Date: Fri, 12 Apr 2024 14:40:18 +0200 Subject: [PATCH 2/8] Fix Command frame encoding --- .../Services/LoRaWanManagementService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IoTHub.Portal.Infrastructure/Services/LoRaWanManagementService.cs b/src/IoTHub.Portal.Infrastructure/Services/LoRaWanManagementService.cs index be904a06b..913c0797b 100644 --- a/src/IoTHub.Portal.Infrastructure/Services/LoRaWanManagementService.cs +++ b/src/IoTHub.Portal.Infrastructure/Services/LoRaWanManagementService.cs @@ -45,7 +45,7 @@ public async Task ExecuteLoRaDeviceMessage(string deviceId, var body = new LoRaCloudToDeviceMessage { - RawPayload = Convert.ToBase64String(Encoding.UTF8.GetBytes(commandDto.Frame)), + RawPayload = Convert.ToBase64String(Enumerable.Range(0, commandDto.Frame.Length / 2).Select(x => Convert.ToByte(commandDto.Frame.Substring(x * 2, 2), 16)).ToArray()), Fport = commandDto.Port, Confirmed = commandDto.Confirmed }; From 400f16ee1615cf304b688c9945a7849c5c4bf90c Mon Sep 17 00:00:00 2001 From: lucas-mrq Date: Fri, 12 Apr 2024 16:50:17 +0200 Subject: [PATCH 3/8] fix-command/update unit test --- .../Services/LoRaWanManagementServiceTests.cs | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/IoTHub.Portal.Tests.Unit/Infrastructure/Services/LoRaWanManagementServiceTests.cs b/src/IoTHub.Portal.Tests.Unit/Infrastructure/Services/LoRaWanManagementServiceTests.cs index dc63aaa19..fb1077389 100644 --- a/src/IoTHub.Portal.Tests.Unit/Infrastructure/Services/LoRaWanManagementServiceTests.cs +++ b/src/IoTHub.Portal.Tests.Unit/Infrastructure/Services/LoRaWanManagementServiceTests.cs @@ -18,16 +18,34 @@ namespace IoTHub.Portal.Tests.Unit.Infrastructure.Services using NUnit.Framework; using RichardSzalay.MockHttp; using UnitTests.Bases; + using IoTHub.Portal.Domain.Options; + using Microsoft.Extensions.Options; + using Moq; + using IoTHub.Portal.Models.v10; + using IoTHub.Portal.Application.Managers; + using System.Linq; + using Fare; [TestFixture] public class LoRaWanManagementServiceTests : BackendUnitTest { + private Mock> mockLoRaWANOptions; private ILoRaWanManagementService loRaWanManagementService; public override void Setup() { base.Setup(); + this.mockLoRaWANOptions = MockRepository.Create>(); + _ = ServiceCollection.AddSingleton(this.mockLoRaWANOptions.Object); + + _ = this.mockLoRaWANOptions.Setup(x => x.Value) + .Returns(new LoRaWANOptions + { + KeyManagementUrl = "http://fake.local", + KeyManagementCode = "" + }); + _ = ServiceCollection.AddSingleton(); Services = ServiceCollection.BuildServiceProvider(); @@ -63,14 +81,19 @@ public async Task ExecuteLoRaDeviceMessageMustBeSuccessfullWhenParametersAreProv { // Arrange var deviceId = Fixture.Create(); + + string regex = "[0-9A-F]{8,15}"; + + Xeger xeger = new Xeger(regex, new Random(0)); // Note zero in Random constructor + var command = new DeviceModelCommandDto { - Frame = Fixture.Create(), + Frame = xeger.Generate(), Confirmed = Fixture.Create(), Port = Fixture.Create() }; - var expectedRawPayload = Convert.ToBase64String(Encoding.UTF8.GetBytes(command.Frame)); + var expectedRawPayload = Convert.ToBase64String(Enumerable.Range(0, command.Frame.Length / 2).Select(x => Convert.ToByte(command.Frame.Substring(x * 2, 2), 16)).ToArray()); _ = MockHttpClient.When(HttpMethod.Post, $"/api/cloudtodevicemessage/{deviceId}") .With(m => From 20841dac0fb0ea237d3796e61508a55679fa00d0 Mon Sep 17 00:00:00 2001 From: lucas-mrq Date: Mon, 15 Apr 2024 09:30:12 +0200 Subject: [PATCH 4/8] fix-command/add KeyManagementApiVersion --- .../Services/LoRaWanManagementService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IoTHub.Portal.Infrastructure/Services/LoRaWanManagementService.cs b/src/IoTHub.Portal.Infrastructure/Services/LoRaWanManagementService.cs index 913c0797b..a4c8cc736 100644 --- a/src/IoTHub.Portal.Infrastructure/Services/LoRaWanManagementService.cs +++ b/src/IoTHub.Portal.Infrastructure/Services/LoRaWanManagementService.cs @@ -23,7 +23,7 @@ public LoRaWanManagementService(HttpClient httpClient, IOptions this.httpClient = httpClient; this.httpClient.BaseAddress = new Uri(loRaWANOptions?.Value.KeyManagementUrl); this.httpClient.DefaultRequestHeaders.Add("x-functions-key", loRaWANOptions?.Value.KeyManagementCode); - this.httpClient.DefaultRequestHeaders.Add("api-version", "2022-03-04"); + this.httpClient.DefaultRequestHeaders.Add("api-version", loRaWANOptions?.Value.KeyManagementApiVersion); } public async Task GetRouterConfig(string loRaRegion) From 9bf08f358eba60d60c1995ced7ffb8231b861064 Mon Sep 17 00:00:00 2001 From: lucas-mrq Date: Tue, 16 Apr 2024 14:20:17 +0200 Subject: [PATCH 5/8] fix-command/Delete Http issue --- .../Services/LoRaWanManagementService.cs | 17 +++++++++++------ .../Services/LoRaWanManagementServiceTests.cs | 16 ---------------- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/src/IoTHub.Portal.Infrastructure/Services/LoRaWanManagementService.cs b/src/IoTHub.Portal.Infrastructure/Services/LoRaWanManagementService.cs index a4c8cc736..7f479db1f 100644 --- a/src/IoTHub.Portal.Infrastructure/Services/LoRaWanManagementService.cs +++ b/src/IoTHub.Portal.Infrastructure/Services/LoRaWanManagementService.cs @@ -14,16 +14,13 @@ namespace IoTHub.Portal.Infrastructure.Services using IoTHub.Portal.Models.v10.LoRaWAN; using Microsoft.Extensions.Options; - internal class LoRaWanManagementService : ILoRaWanManagementService + public class LoRaWanManagementService : ILoRaWanManagementService { private readonly HttpClient httpClient; - public LoRaWanManagementService(HttpClient httpClient, IOptions loRaWANOptions) + public LoRaWanManagementService(HttpClient httpClient) { this.httpClient = httpClient; - this.httpClient.BaseAddress = new Uri(loRaWANOptions?.Value.KeyManagementUrl); - this.httpClient.DefaultRequestHeaders.Add("x-functions-key", loRaWANOptions?.Value.KeyManagementCode); - this.httpClient.DefaultRequestHeaders.Add("api-version", loRaWANOptions?.Value.KeyManagementApiVersion); } public async Task GetRouterConfig(string loRaRegion) @@ -43,9 +40,17 @@ public async Task ExecuteLoRaDeviceMessage(string deviceId, ArgumentNullException.ThrowIfNull(deviceId, nameof(deviceId)); ArgumentNullException.ThrowIfNull(commandDto, nameof(commandDto)); + // Convert the hex frame to a byte array + var hexFrame = Enumerable.Range(0, commandDto.Frame.Length / 2) + .Select(x => Convert.ToByte(commandDto.Frame.Substring(x * 2, 2), 16)) + .ToArray(); + + // Convert the byte array to a base64 string + var rawPayload = Convert.ToBase64String(hexFrame); + var body = new LoRaCloudToDeviceMessage { - RawPayload = Convert.ToBase64String(Enumerable.Range(0, commandDto.Frame.Length / 2).Select(x => Convert.ToByte(commandDto.Frame.Substring(x * 2, 2), 16)).ToArray()), + RawPayload = rawPayload, Fport = commandDto.Port, Confirmed = commandDto.Confirmed }; diff --git a/src/IoTHub.Portal.Tests.Unit/Infrastructure/Services/LoRaWanManagementServiceTests.cs b/src/IoTHub.Portal.Tests.Unit/Infrastructure/Services/LoRaWanManagementServiceTests.cs index fb1077389..29766af66 100644 --- a/src/IoTHub.Portal.Tests.Unit/Infrastructure/Services/LoRaWanManagementServiceTests.cs +++ b/src/IoTHub.Portal.Tests.Unit/Infrastructure/Services/LoRaWanManagementServiceTests.cs @@ -18,34 +18,18 @@ namespace IoTHub.Portal.Tests.Unit.Infrastructure.Services using NUnit.Framework; using RichardSzalay.MockHttp; using UnitTests.Bases; - using IoTHub.Portal.Domain.Options; - using Microsoft.Extensions.Options; - using Moq; - using IoTHub.Portal.Models.v10; - using IoTHub.Portal.Application.Managers; using System.Linq; using Fare; [TestFixture] public class LoRaWanManagementServiceTests : BackendUnitTest { - private Mock> mockLoRaWANOptions; private ILoRaWanManagementService loRaWanManagementService; public override void Setup() { base.Setup(); - this.mockLoRaWANOptions = MockRepository.Create>(); - _ = ServiceCollection.AddSingleton(this.mockLoRaWANOptions.Object); - - _ = this.mockLoRaWANOptions.Setup(x => x.Value) - .Returns(new LoRaWANOptions - { - KeyManagementUrl = "http://fake.local", - KeyManagementCode = "" - }); - _ = ServiceCollection.AddSingleton(); Services = ServiceCollection.BuildServiceProvider(); From 134b20e4653c4e7fe14b6b857a56a8b543f2608c Mon Sep 17 00:00:00 2001 From: lucas-mrq Date: Tue, 16 Apr 2024 14:25:41 +0200 Subject: [PATCH 6/8] fix-command/change-public to internal --- .../Services/LoRaWanManagementService.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/IoTHub.Portal.Infrastructure/Services/LoRaWanManagementService.cs b/src/IoTHub.Portal.Infrastructure/Services/LoRaWanManagementService.cs index 7f479db1f..dd4e7efce 100644 --- a/src/IoTHub.Portal.Infrastructure/Services/LoRaWanManagementService.cs +++ b/src/IoTHub.Portal.Infrastructure/Services/LoRaWanManagementService.cs @@ -10,11 +10,9 @@ namespace IoTHub.Portal.Infrastructure.Services using System.Text.Json; using System.Threading.Tasks; using IoTHub.Portal.Application.Services; - using IoTHub.Portal.Domain.Options; using IoTHub.Portal.Models.v10.LoRaWAN; - using Microsoft.Extensions.Options; - public class LoRaWanManagementService : ILoRaWanManagementService + internal class LoRaWanManagementService : ILoRaWanManagementService { private readonly HttpClient httpClient; From 8778957cb96c0e7c8619c0ca7a6f7b1f10c54f93 Mon Sep 17 00:00:00 2001 From: lucas-mrq Date: Tue, 16 Apr 2024 15:32:47 +0200 Subject: [PATCH 7/8] fix-command/add a new test for the payload --- .../Services/LoRaWanManagementServiceTests.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/IoTHub.Portal.Tests.Unit/Infrastructure/Services/LoRaWanManagementServiceTests.cs b/src/IoTHub.Portal.Tests.Unit/Infrastructure/Services/LoRaWanManagementServiceTests.cs index 29766af66..823a120a9 100644 --- a/src/IoTHub.Portal.Tests.Unit/Infrastructure/Services/LoRaWanManagementServiceTests.cs +++ b/src/IoTHub.Portal.Tests.Unit/Infrastructure/Services/LoRaWanManagementServiceTests.cs @@ -103,6 +103,22 @@ public async Task ExecuteLoRaDeviceMessageMustBeSuccessfullWhenParametersAreProv MockHttpClient.VerifyNoOutstandingExpectation(); } + [Test] + public void ExecuteLoRaDeviceMessageMustBeSuccessfullWhenParametersAndCommandAreProvided() + { + var command = "0113007801680100640064"; + + // Convert the hex frame to a byte array + var hexFrame = Enumerable.Range(0, command.Length / 2) + .Select(x => Convert.ToByte(command.Substring(x * 2, 2), 16)) + .ToArray(); + + // Convert the byte array to a base64 string + var rawPayload = Convert.ToBase64String(hexFrame); + + Assert.AreEqual("ARMAeAFoAQBkAGQ=", rawPayload); + } + [TestCase("CN_470_510_RP1")] [TestCase("CN_470_510_RP2")] [TestCase("EU_863_870")] From 4756237ddb2e8e57867cb5e0c948755a0dacc0a7 Mon Sep 17 00:00:00 2001 From: lucas-mrq Date: Tue, 16 Apr 2024 16:36:40 +0200 Subject: [PATCH 8/8] Change test to make it use the real Service --- .../Services/LoRaWanManagementServiceTests.cs | 43 +++++++++++++++---- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/src/IoTHub.Portal.Tests.Unit/Infrastructure/Services/LoRaWanManagementServiceTests.cs b/src/IoTHub.Portal.Tests.Unit/Infrastructure/Services/LoRaWanManagementServiceTests.cs index 823a120a9..b90a0318a 100644 --- a/src/IoTHub.Portal.Tests.Unit/Infrastructure/Services/LoRaWanManagementServiceTests.cs +++ b/src/IoTHub.Portal.Tests.Unit/Infrastructure/Services/LoRaWanManagementServiceTests.cs @@ -104,19 +104,44 @@ public async Task ExecuteLoRaDeviceMessageMustBeSuccessfullWhenParametersAreProv } [Test] - public void ExecuteLoRaDeviceMessageMustBeSuccessfullWhenParametersAndCommandAreProvided() + public async Task ExecuteLoRaDeviceMessageMustBeSuccessfullWhenParametersAndCommandAreProvided() { - var command = "0113007801680100640064"; + // Arrange + var deviceId = Fixture.Create(); - // Convert the hex frame to a byte array - var hexFrame = Enumerable.Range(0, command.Length / 2) - .Select(x => Convert.ToByte(command.Substring(x * 2, 2), 16)) - .ToArray(); + var commandHex = "0113007801680100640064"; + + var command = new DeviceModelCommandDto + { + Frame = commandHex, + Confirmed = Fixture.Create(), + Port = Fixture.Create() + }; + + var expectedRawPayload = "ARMAeAFoAQBkAGQ="; + + _ = MockHttpClient.When(HttpMethod.Post, $"/api/cloudtodevicemessage/{deviceId}") + .With(m => + { + _ = m.Content.Should().BeAssignableTo(); + var body = (JsonContent) m.Content; + var loRaCloudToDeviceMessage = (LoRaCloudToDeviceMessage)body?.Value; + _ = loRaCloudToDeviceMessage?.Should().NotBeNull(); + _ = loRaCloudToDeviceMessage?.Fport.Should().Be(command.Port); + _ = loRaCloudToDeviceMessage?.Confirmed.Should().Be(command.Confirmed); + _ = loRaCloudToDeviceMessage?.RawPayload.Should().Be(expectedRawPayload); + return true; + }) + .Respond(HttpStatusCode.Created); - // Convert the byte array to a base64 string - var rawPayload = Convert.ToBase64String(hexFrame); + // Act + var result = await this.loRaWanManagementService.ExecuteLoRaDeviceMessage(deviceId, command); - Assert.AreEqual("ARMAeAFoAQBkAGQ=", rawPayload); + // Assert + _ = result.Should().NotBeNull(); + _ = result.IsSuccessStatusCode.Should().BeTrue(); + MockHttpClient.VerifyNoOutstandingRequest(); + MockHttpClient.VerifyNoOutstandingExpectation(); } [TestCase("CN_470_510_RP1")]