From 2e8018c091d76c7deefca41139ebeaf83e35ca64 Mon Sep 17 00:00:00 2001 From: salim ben ahben <40862545+Sben65@users.noreply.github.com> Date: Thu, 24 Feb 2022 20:58:41 +0100 Subject: [PATCH] Issue #174 activate lora feature with setting (#302) * add of GetLoRaActivationSetting method * rename method * hide lora menu * hide switch on model creation * fix #174 * fix #174 * add of some unit test * Fix Unit test on filter Co-authored-by: Beaugrand, Kevin --- .../v1.0/ConcentratorsControllerTests.cs | 237 ------------------ .../LoRaWANConcentratorsControllerTest.cs | 150 ++++++++++- .../v1.0/SettingsControllerTest.cs | 96 +++++++ .../LoRaFeatureActiveFilterAttributeTest.cs | 114 +++++++++ .../DeviceModels/CreateDeviceModelPage.razor | 36 ++- .../Pages/Devices/CreateDevicePage.razor | 7 + .../Client/Shared/MainLayout.razor | 9 +- .../Client/Shared/NavMenu.razor | 15 +- .../v1.0/LoRaWAN/LoRaWANCommandsController.cs | 2 + .../LoRaWAN/LoRaWANConcentratorsController.cs | 2 + .../LoRaWAN/LoRaWANDeviceModelsController.cs | 2 + .../v1.0/LoRaWAN/LoRaWANDevicesController.cs | 4 +- .../Controllers/v1.0/SettingsController.cs | 21 +- .../LoRaFeatureActiveFilterAttribute.cs | 27 ++ .../Server/Properties/InternalsVisibility.cs | 4 + src/AzureIoTHub.Portal/Server/Startup.cs | 8 + 16 files changed, 473 insertions(+), 261 deletions(-) delete mode 100644 src/AzureIoTHub.Portal.Server.Tests/Controllers/v1.0/ConcentratorsControllerTests.cs create mode 100644 src/AzureIoTHub.Portal.Server.Tests/Controllers/v1.0/SettingsControllerTest.cs create mode 100644 src/AzureIoTHub.Portal.Server.Tests/Filters/LoRaFeatureActiveFilterAttributeTest.cs create mode 100644 src/AzureIoTHub.Portal/Server/Filters/LoRaFeatureActiveFilterAttribute.cs create mode 100644 src/AzureIoTHub.Portal/Server/Properties/InternalsVisibility.cs diff --git a/src/AzureIoTHub.Portal.Server.Tests/Controllers/v1.0/ConcentratorsControllerTests.cs b/src/AzureIoTHub.Portal.Server.Tests/Controllers/v1.0/ConcentratorsControllerTests.cs deleted file mode 100644 index 30f51dd12..000000000 --- a/src/AzureIoTHub.Portal.Server.Tests/Controllers/v1.0/ConcentratorsControllerTests.cs +++ /dev/null @@ -1,237 +0,0 @@ -using AzureIoTHub.Portal.Server.Controllers.V10.LoRaWAN; -using AzureIoTHub.Portal.Server.Managers; -using AzureIoTHub.Portal.Server.Mappers; -using AzureIoTHub.Portal.Server.Services; -using AzureIoTHub.Portal.Shared.Models.V10.LoRaWAN.Concentrator; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Azure.Devices; -using Microsoft.Azure.Devices.Shared; -using Microsoft.Extensions.Logging; -using Moq; -using NUnit.Framework; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace AzureIoTHub.Portal.Server.Tests.Controllers.V10 -{ - [TestFixture] - public class ConcentratorsControllerTests - { - private MockRepository mockRepository; - - private Mock mockRouterManager; - private Mock mockConcentratorTwinMapper; - private Mock mockDeviceService; - private Mock> mockLogger; - - [SetUp] - public void SetUp() - { - this.mockRepository = new MockRepository(MockBehavior.Strict); - - this.mockLogger = this.mockRepository.Create>(); - this.mockRouterManager = this.mockRepository.Create(); - this.mockConcentratorTwinMapper = this.mockRepository.Create(); - this.mockDeviceService = this.mockRepository.Create(); - } - - private LoRaWANConcentratorsController CreateController() - { - return new LoRaWANConcentratorsController( - this.mockLogger.Object, - this.mockDeviceService.Object, - this.mockRouterManager.Object, - this.mockConcentratorTwinMapper.Object); - } - - [Test] - public async Task GetAllDeviceConcentrator_WithNoArgument_ReturnConcentratorList() - { - // Arrange - var twin = new Twin("aaa"); - twin.Tags["deviceType"] = "LoRa Concentrator"; - var concentrator = new Concentrator - { - DeviceId = twin.DeviceId, - DeviceType = "LoRa Concentrator" - }; - - this.mockDeviceService.Setup(x => x.GetAllDevice( - It.Is(x => x == "LoRa Concentrator"), - It.Is(x => string.IsNullOrEmpty(x)))) - .ReturnsAsync(new[] - { - twin - }); - - this.mockConcentratorTwinMapper.Setup(x => x.CreateDeviceDetails(It.Is(c => c.DeviceId == twin.DeviceId))) - .Returns(concentrator); - - var concentratorController = this.CreateController(); - - // Act - var response = await concentratorController.GetAllDeviceConcentrator(); - - // Assert - Assert.IsNotNull(response); - Assert.IsAssignableFrom(response.Result); - var okObjectResult = response.Result as ObjectResult; - - Assert.IsNotNull(okObjectResult); - Assert.AreEqual(200, okObjectResult.StatusCode); - Assert.IsNotNull(okObjectResult.Value); - var deviceList = okObjectResult.Value as IEnumerable; - Assert.IsNotNull(deviceList); - Assert.AreEqual(1, deviceList.Count()); - var device = deviceList.First(); - Assert.IsNotNull(device); - Assert.AreEqual(twin.DeviceId, device.DeviceId); - Assert.AreEqual("LoRa Concentrator", device.DeviceType); - - this.mockRepository.VerifyAll(); - } - - [Test] - public async Task GetDeviceConcentrator_With_Valid_Argument_Should_Return_Concentrator() - { - // Arrange - var twin = new Twin("aaa"); - twin.Tags["deviceType"] = "LoRa Concentrator"; - var concentrator = new Concentrator - { - DeviceId = twin.DeviceId, - DeviceType = "LoRa Concentrator" - }; - - this.mockDeviceService.Setup(x => x.GetDeviceTwin(It.Is(c => c == twin.DeviceId))) - .ReturnsAsync(twin); - - this.mockConcentratorTwinMapper.Setup(x => x.CreateDeviceDetails(It.Is(c => c.DeviceId == twin.DeviceId))) - .Returns(concentrator); - - var concentratorController = this.CreateController(); - - // Act - var response = await concentratorController.GetDeviceConcentrator(twin.DeviceId); - - // Assert - Assert.IsNotNull(response); - Assert.IsAssignableFrom(response.Result); - var okObjectResult = response.Result as OkObjectResult; - - Assert.IsNotNull(okObjectResult); - Assert.AreEqual(200, okObjectResult.StatusCode); - Assert.IsNotNull(okObjectResult.Value); - Assert.IsAssignableFrom(okObjectResult.Value); - var device = okObjectResult.Value as Concentrator; - Assert.IsNotNull(device); - Assert.AreEqual(twin.DeviceId, device.DeviceId); - Assert.AreEqual("LoRa Concentrator", device.DeviceType); - - this.mockRepository.VerifyAll(); - } - - [Test] - public async Task CreateDeviceAsync_With_Valid_Argument_Should_Return_OkResult() - { - // Arrange - var concentratorController = this.CreateController(); - var concentrator = new Concentrator - { - DeviceId = "4512457896451156", - LoraRegion = Guid.NewGuid().ToString(), - IsEnabled = true - }; - - var routerConfig = new RouterConfig(); - var mockResult = new BulkRegistryOperationResult - { - IsSuccessful = true - }; - - var twin = new Twin - { - DeviceId = concentrator.DeviceId, - }; - - this.mockDeviceService.Setup(c => c.CreateDeviceWithTwin( - It.Is(x => x == twin.DeviceId), - It.Is(x => !x), - It.Is(x => x.DeviceId == twin.DeviceId), - It.Is(x => x == DeviceStatus.Enabled))) - .ReturnsAsync(mockResult); - - this.mockRouterManager.Setup(x => x.GetRouterConfig(It.Is(c => c == concentrator.LoraRegion))) - .ReturnsAsync(routerConfig); - - this.mockConcentratorTwinMapper.Setup(x => x.UpdateTwin(It.Is(c => c.DeviceId == twin.DeviceId), It.Is(c => c.DeviceId == concentrator.DeviceId))); - this.mockLogger.Setup(x => x.Log(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>())); - - // Act - var result = await concentratorController.CreateDeviceAsync(concentrator); - - // Assert - Assert.IsNotNull(result); - Assert.IsAssignableFrom(result); - var okObjectResult = result as ObjectResult; - Assert.IsNotNull(okObjectResult); - Assert.AreEqual(200, okObjectResult.StatusCode); - Assert.IsNotNull(okObjectResult.Value); - Assert.IsAssignableFrom(okObjectResult.Value); - Assert.AreEqual(mockResult, okObjectResult.Value); - } - - [Test] - public async Task UpdateDeviceAsync_With_Valid_Argument_Should_Return_OkResult() - { - // Arrange - var concentratorController = this.CreateController(); - var concentrator = new Concentrator - { - DeviceId = "4512457896451156", - LoraRegion = Guid.NewGuid().ToString(), - IsEnabled = true, - RouterConfig = new RouterConfig() - }; - - var twin = new Twin - { - DeviceId = concentrator.DeviceId, - }; - - var device = new Device(concentrator.DeviceId); - - this.mockRouterManager.Setup(x => x.GetRouterConfig(It.Is(c => c == concentrator.LoraRegion))) - .ReturnsAsync(concentrator.RouterConfig); - this.mockConcentratorTwinMapper.Setup(x => x.UpdateTwin(It.Is(c => c.DeviceId == twin.DeviceId), It.Is(c => c.DeviceId == concentrator.DeviceId))); - - this.mockDeviceService.Setup(x => x.GetDevice(It.Is(c => c == concentrator.DeviceId))) - .ReturnsAsync(device); - this.mockDeviceService.Setup(x => x.UpdateDevice(It.Is(c => c.Id == concentrator.DeviceId))) - .ReturnsAsync(device); - this.mockDeviceService.Setup(x => x.GetDeviceTwin(It.Is(c => c == concentrator.DeviceId))) - .ReturnsAsync(twin); - this.mockDeviceService.Setup(x => x.UpdateDeviceTwin(It.Is(c => c == concentrator.DeviceId), It.Is(c => c.DeviceId == concentrator.DeviceId))) - .ReturnsAsync(twin); - - // Act - var result = await concentratorController.UpdateDeviceAsync(concentrator); - - // Assert - Assert.IsNotNull(result); - Assert.IsAssignableFrom(result); - var okObjectResult = result as ObjectResult; - Assert.IsNotNull(okObjectResult); - Assert.AreEqual(200, okObjectResult.StatusCode); - } - - [Test] - public async Task DeleteDeviceAsync() - { - await Task.CompletedTask; - Assert.Inconclusive(); - } - } -} diff --git a/src/AzureIoTHub.Portal.Server.Tests/Controllers/v1.0/LoRaWAN/LoRaWANConcentratorsControllerTest.cs b/src/AzureIoTHub.Portal.Server.Tests/Controllers/v1.0/LoRaWAN/LoRaWANConcentratorsControllerTest.cs index 368606a1e..c9682e0fc 100644 --- a/src/AzureIoTHub.Portal.Server.Tests/Controllers/v1.0/LoRaWAN/LoRaWANConcentratorsControllerTest.cs +++ b/src/AzureIoTHub.Portal.Server.Tests/Controllers/v1.0/LoRaWAN/LoRaWANConcentratorsControllerTest.cs @@ -9,6 +9,7 @@ using AzureIoTHub.Portal.Shared.Models.V10.LoRaWAN.Concentrator; using AzureIoTHub.Portal.Shared.Models.V10.LoRaWAN.LoRaDevice; using AzureIoTHub.Portal.Shared.Models.V10.LoRaWAN.LoRaDeviceModel; +using FluentAssertions; using Microsoft.AspNetCore.Mvc; using Microsoft.Azure.Devices; using Microsoft.Azure.Devices.Shared; @@ -22,6 +23,8 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using static AzureIoTHub.Portal.Server.Startup; + namespace AzureIoTHub.Portal.Server.Tests.Controllers.v10.LoRaWAN { [TestFixture] @@ -33,6 +36,7 @@ public class LoRaWANConcentratorsControllerTest private Mock mockDeviceService; private Mock mockRouterConfigManager; private Mock mockConcentratorTwinMapper; + private Mock mockConfigHandler; [SetUp] public void SetUp() @@ -42,6 +46,7 @@ public void SetUp() this.mockDeviceService = this.mockRepository.Create(); this.mockRouterConfigManager = this.mockRepository.Create(); this.mockConcentratorTwinMapper = this.mockRepository.Create(); + this.mockConfigHandler = this.mockRepository.Create(); } private LoRaWANConcentratorsController CreateLoRaWANConcentratorsController() @@ -101,8 +106,6 @@ public async Task GetAllDeviceConcentrator_With_no_deviceType_Should_return_empt { // Arrange var concentratorsController = CreateLoRaWANConcentratorsController(); - int count = 100; - TwinCollection twinCollection = new TwinCollection(); this.mockDeviceService.Setup(c => c.GetAllDevice( It.Is(x => x == "LoRa Concentrator"), @@ -122,13 +125,152 @@ public async Task GetAllDeviceConcentrator_With_no_deviceType_Should_return_empt Assert.IsNotNull(okObjectResult); Assert.AreEqual(200, okObjectResult.StatusCode); - // Assert.IsNull(okObjectResult.Value); var deviceList = okObjectResult.Value as IEnumerable; Assert.IsNull(deviceList); - // Assert.IsFalse(deviceList.Any()); this.mockRepository.VerifyAll(); } + + [Test] + public async Task GetDeviceConcentrator_With_Valid_Argument_Should_Return_Concentrator() + { + // Arrange + var twin = new Twin("aaa"); + twin.Tags["deviceType"] = "LoRa Concentrator"; + var concentrator = new Concentrator + { + DeviceId = twin.DeviceId, + DeviceType = "LoRa Concentrator" + }; + + this.mockDeviceService.Setup(x => x.GetDeviceTwin(It.Is(c => c == twin.DeviceId))) + .ReturnsAsync(twin); + + this.mockConcentratorTwinMapper.Setup(x => x.CreateDeviceDetails(It.Is(c => c.DeviceId == twin.DeviceId))) + .Returns(concentrator); + + var concentratorController = this.CreateLoRaWANConcentratorsController(); + + // Act + var response = await concentratorController.GetDeviceConcentrator(twin.DeviceId); + + // Assert + Assert.IsNotNull(response); + Assert.IsAssignableFrom(response.Result); + var okObjectResult = response.Result as OkObjectResult; + + Assert.IsNotNull(okObjectResult); + Assert.AreEqual(200, okObjectResult.StatusCode); + Assert.IsNotNull(okObjectResult.Value); + Assert.IsAssignableFrom(okObjectResult.Value); + var device = okObjectResult.Value as Concentrator; + Assert.IsNotNull(device); + Assert.AreEqual(twin.DeviceId, device.DeviceId); + Assert.AreEqual("LoRa Concentrator", device.DeviceType); + + this.mockRepository.VerifyAll(); + } + + [Test] + public async Task CreateDeviceAsync_With_Valid_Argument_Should_Return_OkResult() + { + // Arrange + var concentratorController = this.CreateLoRaWANConcentratorsController(); + var concentrator = new Concentrator + { + DeviceId = "4512457896451156", + LoraRegion = Guid.NewGuid().ToString(), + IsEnabled = true + }; + + var routerConfig = new RouterConfig(); + var mockResult = new BulkRegistryOperationResult + { + IsSuccessful = true + }; + + var twin = new Twin + { + DeviceId = concentrator.DeviceId, + }; + + this.mockDeviceService.Setup(c => c.CreateDeviceWithTwin( + It.Is(x => x == twin.DeviceId), + It.Is(x => !x), + It.Is(x => x.DeviceId == twin.DeviceId), + It.Is(x => x == DeviceStatus.Enabled))) + .ReturnsAsync(mockResult); + + this.mockRouterConfigManager.Setup(x => x.GetRouterConfig(It.Is(c => c == concentrator.LoraRegion))) + .ReturnsAsync(routerConfig); + + this.mockConcentratorTwinMapper.Setup(x => x.UpdateTwin(It.Is(c => c.DeviceId == twin.DeviceId), It.Is(c => c.DeviceId == concentrator.DeviceId))); + this.mockLogger.Setup(x => x.Log(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>())); + + // Act + var result = await concentratorController.CreateDeviceAsync(concentrator); + + // Assert + Assert.IsNotNull(result); + Assert.IsAssignableFrom(result); + var okObjectResult = result as ObjectResult; + Assert.IsNotNull(okObjectResult); + Assert.AreEqual(200, okObjectResult.StatusCode); + Assert.IsNotNull(okObjectResult.Value); + Assert.IsAssignableFrom(okObjectResult.Value); + Assert.AreEqual(mockResult, okObjectResult.Value); + } + + [Test] + public async Task UpdateDeviceAsync_With_Valid_Argument_Should_Return_OkResult() + { + // Arrange + var concentratorController = this.CreateLoRaWANConcentratorsController(); + var concentrator = new Concentrator + { + DeviceId = "4512457896451156", + LoraRegion = Guid.NewGuid().ToString(), + IsEnabled = true, + RouterConfig = new RouterConfig() + }; + + var twin = new Twin + { + DeviceId = concentrator.DeviceId, + }; + + var device = new Device(concentrator.DeviceId); + + this.mockRouterConfigManager.Setup(x => x.GetRouterConfig(It.Is(c => c == concentrator.LoraRegion))) + .ReturnsAsync(concentrator.RouterConfig); + this.mockConcentratorTwinMapper.Setup(x => x.UpdateTwin(It.Is(c => c.DeviceId == twin.DeviceId), It.Is(c => c.DeviceId == concentrator.DeviceId))); + + this.mockDeviceService.Setup(x => x.GetDevice(It.Is(c => c == concentrator.DeviceId))) + .ReturnsAsync(device); + this.mockDeviceService.Setup(x => x.UpdateDevice(It.Is(c => c.Id == concentrator.DeviceId))) + .ReturnsAsync(device); + this.mockDeviceService.Setup(x => x.GetDeviceTwin(It.Is(c => c == concentrator.DeviceId))) + .ReturnsAsync(twin); + this.mockDeviceService.Setup(x => x.UpdateDeviceTwin(It.Is(c => c == concentrator.DeviceId), It.Is(c => c.DeviceId == concentrator.DeviceId))) + .ReturnsAsync(twin); + + // Act + var result = await concentratorController.UpdateDeviceAsync(concentrator); + + // Assert + Assert.IsNotNull(result); + Assert.IsAssignableFrom(result); + var okObjectResult = result as ObjectResult; + Assert.IsNotNull(okObjectResult); + Assert.AreEqual(200, okObjectResult.StatusCode); + } + + [Test] + public async Task DeleteDeviceAsync() + { + await Task.CompletedTask; + Assert.Inconclusive(); + } } } diff --git a/src/AzureIoTHub.Portal.Server.Tests/Controllers/v1.0/SettingsControllerTest.cs b/src/AzureIoTHub.Portal.Server.Tests/Controllers/v1.0/SettingsControllerTest.cs new file mode 100644 index 000000000..ab331de8b --- /dev/null +++ b/src/AzureIoTHub.Portal.Server.Tests/Controllers/v1.0/SettingsControllerTest.cs @@ -0,0 +1,96 @@ +using AzureIoTHub.Portal.Server.Controllers.V10; +using AzureIoTHub.Portal.Server.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; +using Moq; +using NUnit.Framework; +using System; +using static AzureIoTHub.Portal.Server.Startup; + +namespace AzureIoTHub.Portal.Server.Tests.Controllers.V10 +{ + [TestFixture] + public class SettingsControllerTest + { + private MockRepository mockRepository; + + private Mock> mockConfiguration; + private Mock mockConfigHandler; + + [SetUp] + public void SetUp() + { + this.mockRepository = new MockRepository(MockBehavior.Strict); + + this.mockConfiguration = this.mockRepository.Create>(); + this.mockConfigHandler = this.mockRepository.Create(); + } + + private SettingsController CreateController() + { + return new SettingsController(this.mockConfiguration.Object, this.mockConfigHandler.Object); + } + + [Test] + public void GetOIDCSettings_Should_return_value() + { + // Arrange + var clientApiIndentityOptions = new ClientApiIndentityOptions + { + Authority = Guid.NewGuid().ToString(), + ClientId = Guid.NewGuid().ToString(), + MetadataUrl = Guid.NewGuid().ToString(), + Scope = Guid.NewGuid().ToString(), + }; + + this.mockConfiguration + .SetupGet(c => c.Value) + .Returns(value: clientApiIndentityOptions); + + var controller = CreateController(); + + // Act + var response = controller.GetOIDCSettings(); + + // Assert + Assert.IsNotNull(response); + Assert.IsAssignableFrom(response); + var okObjectResult = response as ObjectResult; + + Assert.AreEqual(200, okObjectResult.StatusCode); + Assert.IsNotNull(okObjectResult.Value); + Assert.AreEqual(clientApiIndentityOptions, okObjectResult.Value); + + this.mockRepository.VerifyAll(); + } + + [Test] + public void GetLoRaActivationSetting_Should_return_true_to_string() + { + // Arrange + bool loraFeatureStatus = true; + + this.mockConfiguration.SetupGet(c => c.Value).Returns(value: null); + + this.mockConfigHandler + .SetupGet(c => c.IsLoRaEnabled) + .Returns(loraFeatureStatus); + + var controller = this.CreateController(); + + // Act + var response = controller.GetLoRaActivationSetting(); + + // Assert + Assert.IsNotNull(response); + Assert.IsAssignableFrom(response); + var okObjectResult = response as ObjectResult; + + Assert.AreEqual(200, okObjectResult.StatusCode); + Assert.IsNotNull(okObjectResult.Value); + Assert.AreEqual(loraFeatureStatus, okObjectResult.Value); + + this.mockRepository.VerifyAll(); + } + } +} diff --git a/src/AzureIoTHub.Portal.Server.Tests/Filters/LoRaFeatureActiveFilterAttributeTest.cs b/src/AzureIoTHub.Portal.Server.Tests/Filters/LoRaFeatureActiveFilterAttributeTest.cs new file mode 100644 index 000000000..8d32bccc4 --- /dev/null +++ b/src/AzureIoTHub.Portal.Server.Tests/Filters/LoRaFeatureActiveFilterAttributeTest.cs @@ -0,0 +1,114 @@ +using AzureIoTHub.Portal.Server.Filters; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Abstractions; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; +using Moq; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static AzureIoTHub.Portal.Server.Startup; + +namespace AzureIoTHub.Portal.Server.Tests.Filters +{ + [TestFixture] + public class LoRaFeatureActiveFilterAttributeTest + { + private MockRepository mockRepository; + private Mock mockConfigHandler; + + [SetUp] + public void SetUp() + { + this.mockRepository = new MockRepository(MockBehavior.Strict); + + // this.mockActionExecutingContext = this.mockRepository.Create(); + this.mockConfigHandler = this.mockRepository.Create(); + } + + private LoRaFeatureActiveFilterAttribute CreateAttributeFilter() + { + return new LoRaFeatureActiveFilterAttribute(); + } + + [Test] + public void When_LoRa_Is_Disabled_Should_Return_Http_400() + { + // Arrange + this.mockConfigHandler + .SetupGet(c => c.IsLoRaEnabled) + .Returns(false); + + var serviceProviderMock = new Mock(); + serviceProviderMock.Setup(provider => provider.GetService(typeof(ConfigHandler))) + .Returns(this.mockConfigHandler.Object); + + var httpContextMock = new Mock(); + httpContextMock.SetupGet(context => context.RequestServices) + .Returns(serviceProviderMock.Object); + + var actionContext = new ActionContext( + httpContextMock.Object, + Mock.Of(), + Mock.Of()); + + var actionExecutingContext = new ActionExecutingContext( + actionContext, + new List(), + new Dictionary(), + Mock.Of()); + + var actionFilter = this.CreateAttributeFilter(); + + // Act + actionFilter.OnActionExecuting(actionExecutingContext); + + // Assert + Assert.IsAssignableFrom(actionExecutingContext.Result); + this.mockRepository.VerifyAll(); + } + + [Test] + public void When_LoRa_Is_Enabled_Should_Return_Null() + { + // Arrange + this.mockConfigHandler + .SetupGet(c => c.IsLoRaEnabled) + .Returns(true); + + var serviceProviderMock = new Mock(); + serviceProviderMock.Setup(provider => provider.GetService(typeof(ConfigHandler))) + .Returns(this.mockConfigHandler.Object); + + var httpContextMock = new Mock(); + httpContextMock.SetupGet(context => context.RequestServices) + .Returns(serviceProviderMock.Object); + + var actionContext = new ActionContext( + httpContextMock.Object, + Mock.Of(), + Mock.Of()); + + var actionExecutingContext = new ActionExecutingContext( + actionContext, + new List(), + new Dictionary(), + Mock.Of()); + + var actionFilter = this.CreateAttributeFilter(); + + // Act + actionFilter.OnActionExecuting(actionExecutingContext); + + // Assert + Assert.IsNull(actionExecutingContext.Result); + this.mockRepository.VerifyAll(); + } + } +} diff --git a/src/AzureIoTHub.Portal/Client/Pages/DeviceModels/CreateDeviceModelPage.razor b/src/AzureIoTHub.Portal/Client/Pages/DeviceModels/CreateDeviceModelPage.razor index 99e9774cb..a803558ee 100644 --- a/src/AzureIoTHub.Portal/Client/Pages/DeviceModels/CreateDeviceModelPage.razor +++ b/src/AzureIoTHub.Portal/Client/Pages/DeviceModels/CreateDeviceModelPage.razor @@ -60,21 +60,24 @@ - - - LoRa Device - - - @if (IsLoRa) - { - The device is a LoRa Device. - } - else - { - The device is not a LoRa Device. - } + @if(activateLoRaFeature) + { + + + LoRa Device + + + @if (IsLoRa) + { + The device is a LoRa Device. + } + else + { + The device is not a LoRa Device. + } + } @@ -121,6 +124,13 @@ } } + private bool activateLoRaFeature; + + protected override async Task OnInitializedAsync() + { + this.activateLoRaFeature = await httpClient.GetFromJsonAsync("api/settings/lora"); + } + private void SetLoRaDeviceModel() { Model = new LoRaDeviceModel(Model); diff --git a/src/AzureIoTHub.Portal/Client/Pages/Devices/CreateDevicePage.razor b/src/AzureIoTHub.Portal/Client/Pages/Devices/CreateDevicePage.razor index e4dea3e5b..6687b92cd 100644 --- a/src/AzureIoTHub.Portal/Client/Pages/Devices/CreateDevicePage.razor +++ b/src/AzureIoTHub.Portal/Client/Pages/Devices/CreateDevicePage.razor @@ -131,6 +131,8 @@ private bool IsLoRa => this.Model?.SupportLoRaFeatures ?? false; + // private bool activateLoRaFeature; + [Parameter] public string DeviceID { get; set; } @@ -142,11 +144,16 @@ protected override async Task OnInitializedAsync() { + // var activateLoRaFeature; + //var result = await Http.GetFromJsonAsync("api/settings/lora"); + //Boolean.TryParse(result, out var activateLoRaFeature); + // Enable device by default Device.IsEnabled = true; // Gets a list of device model previously registered to Azure to allow autocomplete field in the form DeviceModelList = await Http.GetFromJsonAsync("api/models"); + } /// diff --git a/src/AzureIoTHub.Portal/Client/Shared/MainLayout.razor b/src/AzureIoTHub.Portal/Client/Shared/MainLayout.razor index b4825f860..c638d1f40 100644 --- a/src/AzureIoTHub.Portal/Client/Shared/MainLayout.razor +++ b/src/AzureIoTHub.Portal/Client/Shared/MainLayout.razor @@ -5,6 +5,7 @@ @inject SignOutSessionStateManager SignOutManager @inject NavigationManager Navigation +@inject HttpClient httpClient @@ -35,7 +36,7 @@ - + @@ -53,6 +54,12 @@ @code { public bool _drawerOpen = true; + public bool activateLoRaFeature; + + protected async override void OnInitialized() + { + activateLoRaFeature = await this.httpClient.GetFromJsonAsync("api/settings/lora"); + } void DrawerToggle() { diff --git a/src/AzureIoTHub.Portal/Client/Shared/NavMenu.razor b/src/AzureIoTHub.Portal/Client/Shared/NavMenu.razor index 3af4cc442..e81426f7d 100644 --- a/src/AzureIoTHub.Portal/Client/Shared/NavMenu.razor +++ b/src/AzureIoTHub.Portal/Client/Shared/NavMenu.razor @@ -11,13 +11,20 @@ Devices Configuration - - LoRaWAN Management - Concentrators + @if (activateLoRaFeature) + { + LoRaWAN Management + Concentrators + } Device Models List - \ No newline at end of file + + +@code { + [Parameter] + public bool activateLoRaFeature { get; set; } +} \ No newline at end of file diff --git a/src/AzureIoTHub.Portal/Server/Controllers/v1.0/LoRaWAN/LoRaWANCommandsController.cs b/src/AzureIoTHub.Portal/Server/Controllers/v1.0/LoRaWAN/LoRaWANCommandsController.cs index dd0b0446b..d53352f6f 100644 --- a/src/AzureIoTHub.Portal/Server/Controllers/v1.0/LoRaWAN/LoRaWANCommandsController.cs +++ b/src/AzureIoTHub.Portal/Server/Controllers/v1.0/LoRaWAN/LoRaWANCommandsController.cs @@ -9,6 +9,7 @@ namespace AzureIoTHub.Portal.Server.Controllers.V10.LoRaWAN using Azure; using Azure.Data.Tables; using AzureIoTHub.Portal.Server.Factories; + using AzureIoTHub.Portal.Server.Filters; using AzureIoTHub.Portal.Server.Mappers; using AzureIoTHub.Portal.Shared.Models.V10.LoRaWAN.LoRaDeviceModel; using Microsoft.AspNetCore.Http; @@ -19,6 +20,7 @@ namespace AzureIoTHub.Portal.Server.Controllers.V10.LoRaWAN [ApiVersion("1.0")] [Route("api/lorawan/models/{id}/commands")] [ApiExplorerSettings(GroupName = "LoRa WAN")] + [LoRaFeatureActiveFilter] public class LoRaWANCommandsController : ControllerBase { /// diff --git a/src/AzureIoTHub.Portal/Server/Controllers/v1.0/LoRaWAN/LoRaWANConcentratorsController.cs b/src/AzureIoTHub.Portal/Server/Controllers/v1.0/LoRaWAN/LoRaWANConcentratorsController.cs index 8c7185f27..4f6100d0e 100644 --- a/src/AzureIoTHub.Portal/Server/Controllers/v1.0/LoRaWAN/LoRaWANConcentratorsController.cs +++ b/src/AzureIoTHub.Portal/Server/Controllers/v1.0/LoRaWAN/LoRaWANConcentratorsController.cs @@ -7,6 +7,7 @@ namespace AzureIoTHub.Portal.Server.Controllers.V10.LoRaWAN using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; + using AzureIoTHub.Portal.Server.Filters; using AzureIoTHub.Portal.Server.Managers; using AzureIoTHub.Portal.Server.Mappers; using AzureIoTHub.Portal.Server.Services; @@ -22,6 +23,7 @@ namespace AzureIoTHub.Portal.Server.Controllers.V10.LoRaWAN [ApiVersion("1.0")] [Route("api/lorawan/concentrators")] [ApiExplorerSettings(GroupName = "LoRa WAN")] + [LoRaFeatureActiveFilter] public class LoRaWANConcentratorsController : ControllerBase { /// diff --git a/src/AzureIoTHub.Portal/Server/Controllers/v1.0/LoRaWAN/LoRaWANDeviceModelsController.cs b/src/AzureIoTHub.Portal/Server/Controllers/v1.0/LoRaWAN/LoRaWANDeviceModelsController.cs index bd4f293fc..6cac0abc5 100644 --- a/src/AzureIoTHub.Portal/Server/Controllers/v1.0/LoRaWAN/LoRaWANDeviceModelsController.cs +++ b/src/AzureIoTHub.Portal/Server/Controllers/v1.0/LoRaWAN/LoRaWANDeviceModelsController.cs @@ -4,6 +4,7 @@ namespace AzureIoTHub.Portal.Server.Controllers.V10.LoRaWAN { using AzureIoTHub.Portal.Server.Factories; + using AzureIoTHub.Portal.Server.Filters; using AzureIoTHub.Portal.Server.Managers; using AzureIoTHub.Portal.Server.Services; using AzureIoTHub.Portal.Shared.Models.V10.DeviceModel; @@ -18,6 +19,7 @@ namespace AzureIoTHub.Portal.Server.Controllers.V10.LoRaWAN [ApiVersion("1.0")] [Route("api/lorawan/models")] [ApiExplorerSettings(GroupName = "LoRa WAN")] + [LoRaFeatureActiveFilter] public class LoRaWANDeviceModelsController : DeviceModelsControllerBase { /// diff --git a/src/AzureIoTHub.Portal/Server/Controllers/v1.0/LoRaWAN/LoRaWANDevicesController.cs b/src/AzureIoTHub.Portal/Server/Controllers/v1.0/LoRaWAN/LoRaWANDevicesController.cs index 14273cd4c..894b6bbf5 100644 --- a/src/AzureIoTHub.Portal/Server/Controllers/v1.0/LoRaWAN/LoRaWANDevicesController.cs +++ b/src/AzureIoTHub.Portal/Server/Controllers/v1.0/LoRaWAN/LoRaWANDevicesController.cs @@ -5,6 +5,7 @@ namespace AzureIoTHub.Portal.Server.Controllers.V10 { using Azure.Data.Tables; using AzureIoTHub.Portal.Server.Factories; + using AzureIoTHub.Portal.Server.Filters; using AzureIoTHub.Portal.Server.Managers; using AzureIoTHub.Portal.Server.Mappers; using AzureIoTHub.Portal.Server.Services; @@ -22,6 +23,7 @@ namespace AzureIoTHub.Portal.Server.Controllers.V10 [ApiVersion("1.0")] [Route("api/lorawan/devices")] [ApiExplorerSettings(GroupName = "LoRa WAN")] + [LoRaFeatureActiveFilter] public class LoRaWANDevicesController : DevicesControllerBase { private readonly ITableClientFactory tableClientFactory; @@ -35,7 +37,7 @@ public LoRaWANDevicesController( ITableClientFactory tableClientFactory, ILoraDeviceMethodManager loraDeviceMethodManager, IDeviceModelCommandMapper deviceModelCommandMapper) - : base (logger, devicesService, deviceTwinMapper) + : base(logger, devicesService, deviceTwinMapper) { this.tableClientFactory = tableClientFactory; this.loraDeviceMethodManager = loraDeviceMethodManager; diff --git a/src/AzureIoTHub.Portal/Server/Controllers/v1.0/SettingsController.cs b/src/AzureIoTHub.Portal/Server/Controllers/v1.0/SettingsController.cs index ac2541fe6..7d3f452ab 100644 --- a/src/AzureIoTHub.Portal/Server/Controllers/v1.0/SettingsController.cs +++ b/src/AzureIoTHub.Portal/Server/Controllers/v1.0/SettingsController.cs @@ -8,6 +8,7 @@ namespace AzureIoTHub.Portal.Server.Controllers.V10 using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; + using static AzureIoTHub.Portal.Server.Startup; [ApiController] [AllowAnonymous] @@ -22,13 +23,17 @@ public class SettingsController : ControllerBase /// private readonly ClientApiIndentityOptions configuration; + private readonly ConfigHandler configHandler; + /// /// Initializes a new instance of the class. /// /// The configuration. - public SettingsController(IOptions configuration) + /// The configHandler. + public SettingsController(IOptions configuration, ConfigHandler configHandler) { this.configuration = configuration.Value; + this.configHandler = configHandler; } /// @@ -44,5 +49,19 @@ public IActionResult GetOIDCSettings() { return this.Ok(this.configuration); } + + /// + /// Get the a boolean for LoRa feature enable on the portal or not. + /// + /// The LoRa support setting. + /// Returns the LoRa support setting. + /// Internal server error. + [HttpGet("lora", Name = "GET LoRa settings")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public IActionResult GetLoRaActivationSetting() + { + return this.Ok(this.configHandler.IsLoRaEnabled); + } } } diff --git a/src/AzureIoTHub.Portal/Server/Filters/LoRaFeatureActiveFilterAttribute.cs b/src/AzureIoTHub.Portal/Server/Filters/LoRaFeatureActiveFilterAttribute.cs new file mode 100644 index 000000000..d511f59b8 --- /dev/null +++ b/src/AzureIoTHub.Portal/Server/Filters/LoRaFeatureActiveFilterAttribute.cs @@ -0,0 +1,27 @@ +// Copyright (c) CGI France. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.DependencyInjection; +using System; +using static AzureIoTHub.Portal.Server.Startup; + +namespace AzureIoTHub.Portal.Server.Filters +{ + public class LoRaFeatureActiveFilterAttribute : ActionFilterAttribute + { + public override void OnActionExecuting(ActionExecutingContext context) + { + var configHandler = context.HttpContext.RequestServices.GetService(); + + if (configHandler.IsLoRaEnabled == false) + { + context.Result = new BadRequestObjectResult(context.ModelState) + { + Value = "LoRa features are disabled." + }; + } + } + } +} diff --git a/src/AzureIoTHub.Portal/Server/Properties/InternalsVisibility.cs b/src/AzureIoTHub.Portal/Server/Properties/InternalsVisibility.cs new file mode 100644 index 000000000..af99ba687 --- /dev/null +++ b/src/AzureIoTHub.Portal/Server/Properties/InternalsVisibility.cs @@ -0,0 +1,4 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("AzureIoTHub.Portal.Server.Tests")] +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] \ No newline at end of file diff --git a/src/AzureIoTHub.Portal/Server/Startup.cs b/src/AzureIoTHub.Portal/Server/Startup.cs index b627fe609..fb9116423 100644 --- a/src/AzureIoTHub.Portal/Server/Startup.cs +++ b/src/AzureIoTHub.Portal/Server/Startup.cs @@ -258,6 +258,8 @@ public abstract class ConfigHandler protected const string OIDCClientIdKey = "OIDC:ClientId"; protected const string OIDCApiClientIdKey = "OIDC:ApiClientId"; + protected const string LoRaFeatureSettingKey = "LoRaFeatureSetting"; + protected const string StorageAccountConnectionStringKey = "StorageAccount:ConnectionString"; protected const string StorageAccountBlobContainerNameKey = "StorageAccount:BlobContainerName"; protected const string StorageAccountBlobContainerPartitionKeyKey = "StorageAccount:BlobContainerPartitionKey"; @@ -296,6 +298,8 @@ internal static ConfigHandler Create(IWebHostEnvironment env, IConfiguration con internal abstract string OIDCAuthority { get; } + internal abstract bool IsLoRaEnabled { get; } + internal abstract string StorageAccountBlobContainerName { get; } internal abstract string StorageAccountBlobContainerPartitionKey { get; } @@ -336,6 +340,8 @@ internal ProductionConfigHandler(IConfiguration config) internal override string OIDCApiClientId => this.config[OIDCApiClientIdKey]; + internal override bool IsLoRaEnabled => bool.Parse(this.config[LoRaFeatureSettingKey]); + internal override string StorageAccountBlobContainerName => this.config[StorageAccountBlobContainerNameKey]; internal override string StorageAccountBlobContainerPartitionKey => this.config[StorageAccountBlobContainerPartitionKeyKey]; @@ -376,6 +382,8 @@ internal DevelopmentConfigHandler(IConfiguration config) internal override string OIDCApiClientId => this.config[OIDCApiClientIdKey]; + internal override bool IsLoRaEnabled => bool.Parse(this.config[LoRaFeatureSettingKey]); + internal override string StorageAccountBlobContainerName => this.config[StorageAccountBlobContainerNameKey]; internal override string StorageAccountBlobContainerPartitionKey => this.config[StorageAccountBlobContainerPartitionKeyKey];