From e002a3b828214ccc226a7051b2ca8d243dc28e58 Mon Sep 17 00:00:00 2001 From: Kevin BEAUGRAND Date: Tue, 23 May 2023 13:47:42 +0200 Subject: [PATCH] Fix #2099 + fix issue when creating a ne edge device --- .../Components/Devices/EditDevice.razor | 106 +++++++++--------- .../DeviceModels/CreateDeviceModelPage.razor | 46 ++++---- .../DeviceModels/DeviceModelDetailPage.razor | 78 ++++++------- .../EdgeDevices/CreateEdgeDevicePage.razor | 56 ++++----- .../EdgeDevices/EdgeDeviceDetailPage.razor | 63 ++++++----- .../EdgeModels/CreateEdgeModelsPage.razor | 4 +- .../EdgeModels/EdgeModelDetailPage.razor | 4 +- .../Models/v1.0/IoTEdgeDevice.cs | 2 +- .../EdgeDevices/CreateEdgeDevicePageTest.cs | 2 +- .../EdgeDevices/EdgeDeviceDetailPageTests.cs | 38 +++++++ .../EdgeModels/EdgeModelDetailPageTest.cs | 4 +- 11 files changed, 228 insertions(+), 175 deletions(-) diff --git a/src/AzureIoTHub.Portal.Client/Components/Devices/EditDevice.razor b/src/AzureIoTHub.Portal.Client/Components/Devices/EditDevice.razor index 1ba801cc9..f63ed6d21 100644 --- a/src/AzureIoTHub.Portal.Client/Components/Devices/EditDevice.razor +++ b/src/AzureIoTHub.Portal.Client/Components/Devices/EditDevice.razor @@ -38,7 +38,9 @@
- + + +
@if (context.Equals(CreateEditMode.Edit)) @@ -99,18 +101,18 @@ { this.DeviceModel) - Variant="Variant.Outlined" - ToStringFunc="@(x => x?.Name)" - ResetValueOnEmptyText=true - Immediate=true - Clearable=true - CoerceText=true - CoerceValue=false> + @bind-Value="DeviceModel" + SearchFunc="@Search" + Label="Model*" + Dense=true + For=@(() => this.DeviceModel) + Variant="Variant.Outlined" + ToStringFunc="@(x => x?.Name)" + ResetValueOnEmptyText=true + Immediate=true + Clearable=true + CoerceText=true + CoerceValue=false> @context.Name @@ -133,22 +135,22 @@ { + Label="Device ID / DevEUI" + Variant="Variant.Outlined" + Validation=@(loraValidator.ValidateValue) + For="@(()=> Device.DeviceID)" + Mask="@maskLoRaDeviceID" + HelperText="Device EUI must contain 16 hexadecimal characters (numbers from 0 to 9 and/or letters from A to F)" /> } else { + Label="Device ID" + Variant="Variant.Outlined" + Validation=@(standardValidator.ValidateValue) + For="@(()=> Device.DeviceID)" + HelperText="The device identifier should be of ASCII 7-bit alphanumeric characters plus certain special characters" /> } } @@ -157,12 +159,12 @@ + Label="Device ID" + Variant="Variant.Outlined" + For="@(()=> Device.DeviceID)" + Required="true" + ReadOnly="true" + HelperText="DeviceID must contain 16 hexadecimal characters (numbers from 0 to 9 and/or letters from A to F)" /> } @@ -214,8 +216,8 @@ { + Required="@tag.Required" + Variant="Variant.Outlined" /> } else if (context.Equals(CreateEditMode.Edit)) @@ -226,8 +228,8 @@ Device.Tags.Add(tag.Name, ""); } + Required="@tag.Required" + Variant="Variant.Outlined" /> } } @@ -277,37 +279,37 @@ case DevicePropertyType.Double: string.IsNullOrEmpty(c) || double.TryParse(c, out var result)) - Clearable="true" /> + Label="@item.DisplayName" + Variant="Variant.Outlined" + Validation=@((string c) => string.IsNullOrEmpty(c) || double.TryParse(c, out var result)) + Clearable="true" /> break; case DevicePropertyType.Float: string.IsNullOrEmpty(c) || float.TryParse(c, out var result)) - Clearable="true" /> + Label="@item.DisplayName" + Variant="Variant.Outlined" + Validation=@((string c) => string.IsNullOrEmpty(c) || float.TryParse(c, out var result)) + Clearable="true" /> break; case DevicePropertyType.Integer: string.IsNullOrEmpty(c) || int.TryParse(c, out var result)) - Clearable="true" /> + Label="@item.DisplayName" + Variant="Variant.Outlined" + Validation=@((string c) => string.IsNullOrEmpty(c) || int.TryParse(c, out var result)) + Clearable="true" /> break; case DevicePropertyType.Long: string.IsNullOrEmpty(c) || long.TryParse(c, out var result)) - Clearable="true" /> + Label="@item.DisplayName" + Variant="Variant.Outlined" + Validation=@((string c) => string.IsNullOrEmpty(c) || long.TryParse(c, out var result)) + Clearable="true" /> break; case DevicePropertyType.String: @@ -712,9 +714,9 @@ PageNumber = 0, PageSize = 100, OrderBy = new string[] - { + { string.Empty - } + } }; return (await DeviceModelsClientService.GetDeviceModels(filter)).Items.ToList(); } diff --git a/src/AzureIoTHub.Portal.Client/Pages/DeviceModels/CreateDeviceModelPage.razor b/src/AzureIoTHub.Portal.Client/Pages/DeviceModels/CreateDeviceModelPage.razor index ca55dead3..731ef9696 100644 --- a/src/AzureIoTHub.Portal.Client/Pages/DeviceModels/CreateDeviceModelPage.razor +++ b/src/AzureIoTHub.Portal.Client/Pages/DeviceModels/CreateDeviceModelPage.razor @@ -22,7 +22,9 @@
- + + +
@@ -94,35 +96,35 @@ + @bind-Value="@item.DisplayName" + Label="Display name" + Variant="Variant.Outlined" + For="@(()=> item.DisplayName)" + Required="true" /> + Label="Name" + Variant="Variant.Outlined" + For="@(()=> item.Name)" + Required="true" /> + @bind-Value="@item.Order" + Label="Order" + Variant="Variant.Outlined" + Min=0 + For="@(()=> item.Order)" + Required="true" /> + @bind-Value="@item.PropertyType" + Label="Type" + Variant="Variant.Outlined" + For="@(()=> item.PropertyType)" + Required="true"> @foreach (DevicePropertyType item in Enum.GetValues(typeof(DevicePropertyType))) { @item @@ -163,7 +165,7 @@ { + Commands="Commands" /> } diff --git a/src/AzureIoTHub.Portal.Client/Pages/DeviceModels/DeviceModelDetailPage.razor b/src/AzureIoTHub.Portal.Client/Pages/DeviceModels/DeviceModelDetailPage.razor index 3814afd4c..42694ae3c 100644 --- a/src/AzureIoTHub.Portal.Client/Pages/DeviceModels/DeviceModelDetailPage.razor +++ b/src/AzureIoTHub.Portal.Client/Pages/DeviceModels/DeviceModelDetailPage.razor @@ -15,10 +15,10 @@ @inject ILoRaWanDeviceModelsClientService LoRaWanDeviceModelsClientService - + - Device Model + Device Model @@ -27,7 +27,9 @@
- + + +
@@ -84,49 +86,49 @@ + @bind-Value="@item.DisplayName" + Label="Display name" + Variant="Variant.Outlined" + For="@(()=> item.DisplayName)" + Required="true" /> + Label="Name" + Variant="Variant.Outlined" + For="@(()=> item.Name)" + Required="true" /> + @bind-Value="@item.Order" + Label="Order" + Variant="Variant.Outlined" + Min=0 + For="@(()=> item.Order)" + Required="true" /> + @bind-Value="@item.PropertyType" + Label="Type" + Variant="Variant.Outlined" + For="@(()=> item.PropertyType)" + Required="true"> @foreach (DevicePropertyType item in Enum.GetValues(typeof(DevicePropertyType))) { - @item + @item } - - - - - Writable - - - Remove - - +
+
+ + + Writable + + + Remove + +
} @@ -152,7 +154,7 @@ { + Commands="Commands" /> } @@ -319,7 +321,7 @@ } // Check validation error in commands - foreach(var cmd in Commands) + foreach (var cmd in Commands) { if (!CommandValidator.Validate(cmd).IsValid) cmdValidationError = true; @@ -382,7 +384,7 @@ { isProcessing = false; } - + } private async Task DeleteDeviceModel() @@ -406,4 +408,4 @@ // Go back to the list of devices after the deletion NavigationManager.NavigateTo("device-models"); } - } +} diff --git a/src/AzureIoTHub.Portal.Client/Pages/EdgeDevices/CreateEdgeDevicePage.razor b/src/AzureIoTHub.Portal.Client/Pages/EdgeDevices/CreateEdgeDevicePage.razor index bfe6c2051..9b9e71c1d 100644 --- a/src/AzureIoTHub.Portal.Client/Pages/EdgeDevices/CreateEdgeDevicePage.razor +++ b/src/AzureIoTHub.Portal.Client/Pages/EdgeDevices/CreateEdgeDevicePage.razor @@ -25,7 +25,9 @@
- + + +
@@ -51,26 +53,26 @@ - @if (duplicateDevice) + @if (duplicateDevice) { } else { this.edgeModel) - Variant="Variant.Outlined" - ToStringFunc="@(x => x?.Name)" - ResetValueOnEmptyText=true - Immediate=true - Clearable=true - CoerceText=true - CoerceValue=false> + id="@nameof(IoTEdgeModel.ModelId)" + @bind-Value="edgeModel" + SearchFunc="@Search" + Label="Model*" + Dense=true + For=@(() => this.edgeModel) + Variant="Variant.Outlined" + ToStringFunc="@(x => x?.Name)" + ResetValueOnEmptyText=true + Immediate=true + Clearable=true + CoerceText=true + CoerceValue=false> @context.Name @@ -86,7 +88,7 @@ - + Required="@tag.Required" + Variant="Variant.Outlined" /> } @@ -154,7 +156,7 @@ - + @@ -306,18 +308,18 @@ if (string.IsNullOrEmpty(value)) return edgeModelList .Select(m => new IoTEdgeModel - { - ModelId = m.ModelId, - Name = m.Name - });; + { + ModelId = m.ModelId, + Name = m.Name + }); ; return edgeModelList .Where(x => x.Name.Contains(value, StringComparison.InvariantCultureIgnoreCase)) .Select(m => new IoTEdgeModel - { - ModelId = m.ModelId, - Name = m.Name - }); + { + ModelId = m.ModelId, + Name = m.Name + }); } internal async Task ChangeModel(IoTEdgeModel edgeModel) diff --git a/src/AzureIoTHub.Portal.Client/Pages/EdgeDevices/EdgeDeviceDetailPage.razor b/src/AzureIoTHub.Portal.Client/Pages/EdgeDevices/EdgeDeviceDetailPage.razor index 7f9e17d5f..f8c3f77eb 100644 --- a/src/AzureIoTHub.Portal.Client/Pages/EdgeDevices/EdgeDeviceDetailPage.razor +++ b/src/AzureIoTHub.Portal.Client/Pages/EdgeDevices/EdgeDeviceDetailPage.razor @@ -13,10 +13,10 @@ @inject IDeviceTagSettingsClientService DeviceTagSettingsClientService - + - Edge Device Details + Edge Device Details @@ -27,15 +27,17 @@ Model: @edgeModel.Name @(string.IsNullOrEmpty(edgeDevice.DeviceName) ? edgeDevice.DeviceId : edgeDevice.DeviceName) - - - -
- -
-
- - @if (isLoaded) + + + +
+ + + +
+
+ + @if (isLoaded) { Connect } @@ -121,22 +123,23 @@ - + Last deployment - @if (edgeDevice.LastDeployment != null){ - - - - - - - - - - - - } + @if (edgeDevice.LastDeployment != null) + { + + + + + + + + + + + + } @@ -188,8 +191,8 @@ edgeDevice.Tags.Add(tag.Name, ""); } + Required="@tag.Required" + Variant="Variant.Outlined" /> } @@ -387,16 +390,16 @@ public async Task ShowConnectionString() { - var parameters = new DialogParameters {{nameof(ConnectionStringDialog.deviceId), this.deviceId}}; + var parameters = new DialogParameters { { nameof(ConnectionStringDialog.deviceId), this.deviceId } }; - _ = await DialogService.Show("Edge Device Connection String", parameters).Result; + _ = await DialogService.Show("Edge Device Connection String", parameters).Result; } public async Task ShowDeleteModal() { isProcessing = true; - var parameter = new DialogParameters {{nameof(edgeDevice.DeviceId), edgeDevice.DeviceId}}; + var parameter = new DialogParameters { { nameof(edgeDevice.DeviceId), edgeDevice.DeviceId } }; var result = await DialogService.Show("Edge device deletion confirmation", parameter).Result; diff --git a/src/AzureIoTHub.Portal.Client/Pages/EdgeModels/CreateEdgeModelsPage.razor b/src/AzureIoTHub.Portal.Client/Pages/EdgeModels/CreateEdgeModelsPage.razor index 0fba80663..10cc8c650 100644 --- a/src/AzureIoTHub.Portal.Client/Pages/EdgeModels/CreateEdgeModelsPage.razor +++ b/src/AzureIoTHub.Portal.Client/Pages/EdgeModels/CreateEdgeModelsPage.razor @@ -21,7 +21,9 @@
- + + +
diff --git a/src/AzureIoTHub.Portal.Client/Pages/EdgeModels/EdgeModelDetailPage.razor b/src/AzureIoTHub.Portal.Client/Pages/EdgeModels/EdgeModelDetailPage.razor index 1d199a89d..eea4b846c 100644 --- a/src/AzureIoTHub.Portal.Client/Pages/EdgeModels/EdgeModelDetailPage.razor +++ b/src/AzureIoTHub.Portal.Client/Pages/EdgeModels/EdgeModelDetailPage.razor @@ -27,7 +27,9 @@
- + + +
diff --git a/src/AzureIoTHub.Portal.Shared/Models/v1.0/IoTEdgeDevice.cs b/src/AzureIoTHub.Portal.Shared/Models/v1.0/IoTEdgeDevice.cs index f2326e5d1..448abefbd 100644 --- a/src/AzureIoTHub.Portal.Shared/Models/v1.0/IoTEdgeDevice.cs +++ b/src/AzureIoTHub.Portal.Shared/Models/v1.0/IoTEdgeDevice.cs @@ -69,7 +69,7 @@ public class IoTEdgeDevice /// /// The IoT Edge configuraton. /// - public ConfigItem LastDeployment { get; set; } = new ConfigItem(); + public ConfigItem LastDeployment { get; set; } /// /// The IoT Edge modules. diff --git a/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/EdgeDevices/CreateEdgeDevicePageTest.cs b/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/EdgeDevices/CreateEdgeDevicePageTest.cs index ac771f91f..29d07c8f7 100644 --- a/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/EdgeDevices/CreateEdgeDevicePageTest.cs +++ b/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/EdgeDevices/CreateEdgeDevicePageTest.cs @@ -342,7 +342,7 @@ public async Task ChangeEdgeModelShouldDisplayModelImage() await cut.Instance.ChangeModel(edgeModel); // Assert - Assert.IsFalse(string.IsNullOrEmpty(ModelImageElement.InnerHtml)); + Assert.AreEqual(edgeModel.ImageUrl, ModelImageElement.Attributes["src"].Value); cut.WaitForAssertion(() => MockRepository.VerifyAll()); } diff --git a/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/EdgeDevices/EdgeDeviceDetailPageTests.cs b/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/EdgeDevices/EdgeDeviceDetailPageTests.cs index 6788d4ad8..586f1174d 100644 --- a/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/EdgeDevices/EdgeDeviceDetailPageTests.cs +++ b/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/EdgeDevices/EdgeDeviceDetailPageTests.cs @@ -21,6 +21,7 @@ namespace AzureIoTHub.Portal.Tests.Unit.Client.Pages.EdgeDevices using MudBlazor.Services; using NUnit.Framework; using UnitTests.Mocks; + using AutoFixture; [TestFixture] public class EdgeDeviceDetailPageTests : BlazorUnitTest @@ -358,6 +359,43 @@ public void EdgeDeviceDetailPageShouldProcessProblemDetailsExceptionWhenIssueOcc cut.WaitForAssertion(() => MockRepository.VerifyAll()); } + [Test] + public void EdgeDeviceDetailPageShouldDisplayLastDeploymentStatus() + { + // Arrange + var mockIoTEdgeModule = new IoTEdgeModule() + { + ModuleName = Guid.NewGuid().ToString() + }; + + var mockIoTEdgeDevice = new IoTEdgeDevice() + { + DeviceId = this.mockdeviceId, + ConnectionState = "Connected", + Modules= new List(){mockIoTEdgeModule}, + ModelId = Guid.NewGuid().ToString(), + LastDeployment = Fixture.Create() + }; + + _ = this.mockEdgeDeviceClientService.Setup(service => service.GetDevice(this.mockdeviceId)) + .ReturnsAsync(mockIoTEdgeDevice); + + _ = this.mockIEdgeModelClientService + .Setup(service => service.GetIoTEdgeModel(It.Is(x => x.Equals(mockIoTEdgeDevice.ModelId, StringComparison.Ordinal)))) + .ReturnsAsync(new IoTEdgeModel()); + + _ = this.mockDeviceTagSettingsClientService.Setup(service => service.GetDeviceTags()).ReturnsAsync(new List()); + + // Act + var cut = RenderComponent(ComponentParameter.CreateParameter("deviceId", this.mockdeviceId)); + + // Assert + cut.WaitForAssertion(() => Assert.AreEqual(mockIoTEdgeDevice.LastDeployment.Name, cut.WaitForElement("#lastDeploymentName").GetAttribute("value"))); + cut.WaitForAssertion(() => Assert.AreEqual(mockIoTEdgeDevice.LastDeployment.DateCreation.Date, DateTime.TryParse(cut.WaitForElement("#lastDeploymentDate").GetAttribute("value"), out var dateTime) ? dateTime : null)); + cut.WaitForAssertion(() => Assert.AreEqual(mockIoTEdgeDevice.LastDeployment.Status, cut.WaitForElement("#lastDeploymentStatus").GetAttribute("value"))); + cut.WaitForAssertion(() => MockRepository.VerifyAll()); + } + [Test] public void ClickOnLogsShouldDisplayLogs() { diff --git a/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/EdgeModels/EdgeModelDetailPageTest.cs b/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/EdgeModels/EdgeModelDetailPageTest.cs index 5bbdc22c9..929428907 100644 --- a/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/EdgeModels/EdgeModelDetailPageTest.cs +++ b/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/EdgeModels/EdgeModelDetailPageTest.cs @@ -359,7 +359,7 @@ public void DeleteAvatarShouldRemoveTheImage() // Act var cut = RenderComponent(ComponentParameter.CreateParameter("ModelID", this.mockEdgeModleId)); - cut.WaitForAssertion(() => Assert.AreEqual(1, cut.Find($"#{nameof(IoTEdgeModel.ImageUrl)}").ChildElementCount)); + cut.WaitForAssertion(() => Assert.IsFalse(string.IsNullOrEmpty(cut.Find($"#{nameof(IoTEdgeModel.ImageUrl)}").Attributes["src"]?.Value))); var avatar = cut.WaitForElement($"#{nameof(IoTEdgeModel.ImageUrl)}"); Assert.IsNotNull(avatar); @@ -368,7 +368,7 @@ public void DeleteAvatarShouldRemoveTheImage() deleteAvatarBtn.Click(); // Assert - cut.WaitForAssertion(() => Assert.AreEqual(0, cut.Find($"#{nameof(IoTEdgeModel.ImageUrl)}").ChildElementCount)); + cut.WaitForAssertion(() => Assert.IsTrue(string.IsNullOrEmpty(cut.Find($"#{nameof(IoTEdgeModel.ImageUrl)}").Attributes["src"]?.Value))); cut.WaitForAssertion(() => MockRepository.VerifyAll()); }