diff --git a/src/AzureIoTHub.Portal.Client/Pages/EdgeModels/EdgeModelDetailPage.razor b/src/AzureIoTHub.Portal.Client/Pages/EdgeModels/EdgeModelDetailPage.razor
index eee6d5cf2..8e05f6d87 100644
--- a/src/AzureIoTHub.Portal.Client/Pages/EdgeModels/EdgeModelDetailPage.razor
+++ b/src/AzureIoTHub.Portal.Client/Pages/EdgeModels/EdgeModelDetailPage.razor
@@ -4,6 +4,7 @@
@using AzureIoTHub.Portal.Client.Pages.EdgeModels.EdgeModule
@using System.Net.Http.Headers
@using AzureIoTHub.Portal.Models.v10
+@using AzureIoTHub.Portal.Shared.Models.v10
@using AzureIoTHub.Portal.Client.Validators
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@@ -69,6 +70,43 @@
+
+
+
+ System Modules
+
+
+
+
+
+
+
+
+
+
+ Module name
+ Image URI
+ Detail
+
+
+
+
+
+
+
+
+
+
+ Detail
+
+
+
+
+
+
+
+
+
@@ -362,4 +400,19 @@
EdgeModel.EdgeRoutes.Add(new IoTEdgeRoute());
}
}
+
+ private async Task ShowSystemModuleDetail(EdgeModelSystemModule systemModule)
+ {
+ var parameters = new DialogParameters();
+ parameters.Add("module", systemModule);
+
+ DialogOptions options = new DialogOptions() { MaxWidth = MaxWidth.Medium, FullWidth = true, CloseButton = true };
+
+ var result = await DialogService.Show(systemModule.Name, parameters, options).Result;
+
+ if (result.Cancelled)
+ {
+ return;
+ }
+ }
}
diff --git a/src/AzureIoTHub.Portal.Server/Services/ConfigService.cs b/src/AzureIoTHub.Portal.Server/Services/ConfigService.cs
index c79520e6d..8ba2518a5 100644
--- a/src/AzureIoTHub.Portal.Server/Services/ConfigService.cs
+++ b/src/AzureIoTHub.Portal.Server/Services/ConfigService.cs
@@ -12,6 +12,7 @@ namespace AzureIoTHub.Portal.Server.Services
using AzureIoTHub.Portal.Domain.Exceptions;
using AzureIoTHub.Portal.Models.v10;
using AzureIoTHub.Portal.Server.Helpers;
+ using AzureIoTHub.Portal.Shared.Models.v10;
using Extensions;
using Microsoft.Azure.Devices;
using Microsoft.Azure.Devices.Common.Extensions;
@@ -105,6 +106,48 @@ public async Task> GetConfigModuleList(string modelId)
return moduleList;
}
+ public async Task> GetModelSystemModule(string modelId)
+ {
+ var configList = await GetIoTEdgeConfigurations();
+
+ var config = configList.FirstOrDefault((x) => x.Id.StartsWith(modelId, StringComparison.Ordinal));
+
+ if (config == null)
+ {
+ throw new InternalServerErrorException("Config does not exist.");
+ }
+
+ var moduleList = new List();
+
+ // Details of every modules are stored within the EdgeAgent module data
+ if (config.Content.ModulesContent != null
+ && config.Content.ModulesContent.TryGetValue("$edgeAgent", out var edgeAgentModule)
+ && edgeAgentModule.TryGetValue("properties.desired", out var edgeAgentDesiredProperties))
+ {
+ // Converts the object to a JObject to access its properties more easily
+ if (edgeAgentDesiredProperties is not JObject modObject)
+ {
+ throw new InvalidOperationException($"Could not parse properties.desired for the configuration id {config.Id}");
+ }
+
+ // Adds regular modules to the list of modules
+ if (modObject.TryGetValue("systemModules", out var modules))
+ {
+ foreach (var newModule in modules.Values().Select(module => ConfigHelper.CreateGatewayModule(config, module)))
+ {
+ moduleList.Add(new EdgeModelSystemModule(newModule.ModuleName)
+ {
+ ImageUri = newModule.ImageURI,
+ EnvironmentVariables = newModule.EnvironmentVariables,
+ ContainerCreateOptions = newModule.ContainerCreateOptions,
+ });
+ }
+ }
+ }
+
+ return moduleList;
+ }
+
public async Task> GetConfigRouteList(string modelId)
{
var configList = await GetIoTEdgeConfigurations();
diff --git a/src/AzureIoTHub.Portal.Server/Services/EdgeModelService.cs b/src/AzureIoTHub.Portal.Server/Services/EdgeModelService.cs
index c46006d6b..56d9cfb50 100644
--- a/src/AzureIoTHub.Portal.Server/Services/EdgeModelService.cs
+++ b/src/AzureIoTHub.Portal.Server/Services/EdgeModelService.cs
@@ -164,6 +164,7 @@ public async Task GetEdgeModel(string modelId)
}
var modules = await this.configService.GetConfigModuleList(modelId);
+ var sysModules = await this.configService.GetModelSystemModule(modelId);
var routes = await this.configService.GetConfigRouteList(modelId);
var commands = this.commandRepository.GetAll().Where(x => x.EdgeDeviceModelId == modelId).ToList();
@@ -176,7 +177,8 @@ public async Task GetEdgeModel(string modelId)
Name = edgeModelEntity.Name,
Description = edgeModelEntity.Description,
EdgeModules = modules,
- EdgeRoutes = routes
+ EdgeRoutes = routes,
+ SystemModules = sysModules,
};
foreach (var command in commands)
diff --git a/src/AzureIoTHub.Portal.Server/Services/IConfigService.cs b/src/AzureIoTHub.Portal.Server/Services/IConfigService.cs
index bb2b48a4f..03fb4bdd1 100644
--- a/src/AzureIoTHub.Portal.Server/Services/IConfigService.cs
+++ b/src/AzureIoTHub.Portal.Server/Services/IConfigService.cs
@@ -6,6 +6,7 @@ namespace AzureIoTHub.Portal.Server.Services
using System.Collections.Generic;
using System.Threading.Tasks;
using AzureIoTHub.Portal.Models.v10;
+ using AzureIoTHub.Portal.Shared.Models.v10;
using Microsoft.Azure.Devices;
public interface IConfigService
@@ -28,6 +29,8 @@ public interface IConfigService
Task> GetConfigModuleList(string modelId);
+ Task> GetModelSystemModule(string modelId);
+
Task> GetConfigRouteList(string modelId);
}
}
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 d54508e9e..7d4f4e3b2 100644
--- a/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/EdgeModels/EdgeModelDetailPageTest.cs
+++ b/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/EdgeModels/EdgeModelDetailPageTest.cs
@@ -12,6 +12,7 @@ namespace AzureIoTHub.Portal.Tests.Unit.Client.Pages.EdgeModels
using AzureIoTHub.Portal.Client.Pages.EdgeModels.EdgeModule;
using AzureIoTHub.Portal.Client.Services;
using AzureIoTHub.Portal.Models.v10;
+ using AzureIoTHub.Portal.Shared.Models.v10;
using AzureIoTHub.Portal.Tests.Unit.UnitTests.Bases;
using Bunit;
using Bunit.TestDoubles;
@@ -295,6 +296,60 @@ public void ClickShowEditEdgeModuleDialogShouldDisplayEditModuleDialogAndReturnI
cut.WaitForAssertion(() => MockRepository.VerifyAll());
}
+ [Test]
+ public void ClickOnShowSystemModuleDetailShouldShowDialog()
+ {
+ // Arrange
+ _ = SetupLoadEdgeModel();
+
+ var mockDialogReference = MockRepository.Create();
+ _ = mockDialogReference.Setup(c => c.Result).ReturnsAsync(DialogResult.Ok("Ok"));
+
+ _ = this.mockDialogService
+ .Setup(c => c.Show(It.IsAny(), It.IsAny(), It.IsAny()))
+ .Returns(mockDialogReference.Object);
+
+ // Act
+ var cut = RenderComponent(ComponentParameter.CreateParameter("ModelID", this.mockEdgeModleId));
+
+ cut.WaitForAssertion(() => Assert.AreEqual(1, cut.FindAll("#editSystModuleButton_edgeAgent").Count));
+ var editEdgeAgentButton = cut.WaitForElement("#editSystModuleButton_edgeAgent");
+
+ cut.WaitForElement($"#{nameof(EdgeModelSystemModule.ImageUri)}").Change("image/test");
+
+ editEdgeAgentButton.Click();
+
+ // Assert
+ cut.WaitForAssertion(() => MockRepository.VerifyAll());
+ }
+
+ [Test]
+ public void ClickOnShowSystemModuleDetailShouldShowDialogAndReturnIfAborted()
+ {
+ // Arrange
+ _ = SetupLoadEdgeModel();
+
+ var mockDialogReference = MockRepository.Create();
+ _ = mockDialogReference.Setup(c => c.Result).ReturnsAsync(DialogResult.Cancel());
+
+ _ = this.mockDialogService
+ .Setup(c => c.Show(It.IsAny(), It.IsAny(), It.IsAny()))
+ .Returns(mockDialogReference.Object);
+
+ // Act
+ var cut = RenderComponent(ComponentParameter.CreateParameter("ModelID", this.mockEdgeModleId));
+
+ cut.WaitForAssertion(() => Assert.AreEqual(1, cut.FindAll("#editSystModuleButton_edgeAgent").Count));
+ var editEdgeAgentButton = cut.WaitForElement("#editSystModuleButton_edgeAgent");
+
+ cut.WaitForElement($"#{nameof(EdgeModelSystemModule.ImageUri)}").Change("image/test");
+
+ editEdgeAgentButton.Click();
+
+ // Assert
+ cut.WaitForAssertion(() => MockRepository.VerifyAll());
+ }
+
[Test]
public void DeleteAvatarShouldRemoveTheImage()
{
diff --git a/src/AzureIoTHub.Portal.Tests.Unit/Server/Services/ConfigServiceTests.cs b/src/AzureIoTHub.Portal.Tests.Unit/Server/Services/ConfigServiceTests.cs
index bf2f4d0ad..c4532b859 100644
--- a/src/AzureIoTHub.Portal.Tests.Unit/Server/Services/ConfigServiceTests.cs
+++ b/src/AzureIoTHub.Portal.Tests.Unit/Server/Services/ConfigServiceTests.cs
@@ -611,6 +611,131 @@ public void WhenPropertiesDesiredIsInWrongFormatGetConfigModuleListShouldThrowIn
this.mockRepository.VerifyAll();
}
+ [Test]
+ public async Task GetModelSystemModuleReturnList()
+ {
+ // Arrange
+ var configService = CreateConfigsServices();
+
+ var configTest = new Configuration(Guid.NewGuid().ToString());
+ var listConfig = new List()
+ {
+ configTest
+ };
+
+ var edgeAgentPropertiesDesired = new EdgeAgentPropertiesDesired();
+ var modules = new Dictionary()
+ {
+ {"module test 01", new ConfigModule() },
+ };
+
+ edgeAgentPropertiesDesired.Modules = modules;
+
+ var mockConfigEnumerator = this.mockRepository.Create>();
+
+ configTest.Content.ModulesContent = new Dictionary>()
+ {
+ {
+ "$edgeAgent", new Dictionary()
+ {
+ {
+ "properties.desired", JObject.Parse(JsonConvert.SerializeObject(edgeAgentPropertiesDesired))
+ }
+ }
+ }
+ };
+
+ _ = mockConfigEnumerator.Setup(x => x.GetEnumerator()).Returns(listConfig.GetEnumerator);
+
+ _ = this.mockRegistryManager.Setup(c => c.GetConfigurationsAsync(It.Is(x => x == 0)))
+ .ReturnsAsync(mockConfigEnumerator.Object);
+
+ // Act
+ var result = await configService.GetModelSystemModule(configTest.Id);
+
+ // Assert
+ Assert.IsNotNull(result);
+ Assert.AreEqual(2, result.Count);
+
+ this.mockRepository.VerifyAll();
+ }
+
+ [Test]
+ public void WhenGetConfigIsNullGetModelSystemModuleShouldThrowInternalServerErrorException()
+ {
+ // Arrange
+ var configService = CreateConfigsServices();
+
+ var configTest = new Configuration(Guid.NewGuid().ToString());
+ var listConfig = new List()
+ {
+ };
+
+ var mockConfigEnumerator = this.mockRepository.Create>();
+
+ _ = mockConfigEnumerator.Setup(x => x.GetEnumerator()).Returns(listConfig.GetEnumerator);
+
+ _ = this.mockRegistryManager.Setup(c => c.GetConfigurationsAsync(It.Is(x => x == 0)))
+ .ReturnsAsync(mockConfigEnumerator.Object);
+
+ // Act
+ var result = async () => await configService.GetModelSystemModule(configTest.Id);
+
+ // Assert
+ _ = result.Should().ThrowAsync();
+
+ this.mockRepository.VerifyAll();
+ }
+
+ [Test]
+ public void WhenPropertiesDesiredIsInWrongFormatConfigIsNullGetModelSystemModuleShouldThrowInvalidOperationException()
+ {
+ // Arrange
+ var configService = CreateConfigsServices();
+
+ var configTest = new Configuration(Guid.NewGuid().ToString());
+ var listConfig = new List()
+ {
+ configTest
+ };
+
+ var edgeAgentPropertiesDesired = new EdgeAgentPropertiesDesired();
+ var modules = new Dictionary()
+ {
+ {"module test 01", new ConfigModule() },
+ {"module test 02", new ConfigModule() }
+ };
+
+ edgeAgentPropertiesDesired.Modules = modules;
+
+ var mockConfigEnumerator = this.mockRepository.Create>();
+
+ configTest.Content.ModulesContent = new Dictionary>()
+ {
+ {
+ "$edgeAgent", new Dictionary()
+ {
+ {
+ "properties.desired", edgeAgentPropertiesDesired
+ }
+ }
+ }
+ };
+
+ _ = mockConfigEnumerator.Setup(x => x.GetEnumerator()).Returns(listConfig.GetEnumerator);
+
+ _ = this.mockRegistryManager.Setup(c => c.GetConfigurationsAsync(It.Is(x => x == 0)))
+ .ReturnsAsync(mockConfigEnumerator.Object);
+
+ // Act
+ var result = async () => await configService.GetModelSystemModule(configTest.Id);
+
+ // Assert
+ _ = result.Should().ThrowAsync();
+
+ this.mockRepository.VerifyAll();
+ }
+
[Test]
public async Task GetConfigRouteListShouldReturnAList()
{
diff --git a/src/AzureIoTHub.Portal.Tests.Unit/Server/Services/EdgeModelServiceTest.cs b/src/AzureIoTHub.Portal.Tests.Unit/Server/Services/EdgeModelServiceTest.cs
index a0deeebf7..17d0be16e 100644
--- a/src/AzureIoTHub.Portal.Tests.Unit/Server/Services/EdgeModelServiceTest.cs
+++ b/src/AzureIoTHub.Portal.Tests.Unit/Server/Services/EdgeModelServiceTest.cs
@@ -17,6 +17,7 @@ namespace AzureIoTHub.Portal.Tests.Unit.Server.Services
using AzureIoTHub.Portal.Models.v10;
using AzureIoTHub.Portal.Server.Managers;
using AzureIoTHub.Portal.Server.Services;
+ using AzureIoTHub.Portal.Shared.Models.v10;
using AzureIoTHub.Portal.Tests.Unit.UnitTests.Bases;
using FluentAssertions;
using Microsoft.AspNetCore.Http;
@@ -98,6 +99,7 @@ public async Task GetEdgeModelShouldReturnValueAsync()
// Arrange
var expectedModules = Fixture.CreateMany(2).ToList();
var expectedRoutes = Fixture.CreateMany(2).ToList();
+ var expectedSysModule = Fixture.CreateMany(2).ToList();
var expectedImageUri = Fixture.Create();
var expectedEdgeDeviceModel = new IoTEdgeModel()
@@ -107,7 +109,8 @@ public async Task GetEdgeModelShouldReturnValueAsync()
ImageUrl = expectedImageUri,
Description = Guid.NewGuid().ToString(),
EdgeModules = expectedModules,
- EdgeRoutes = expectedRoutes
+ EdgeRoutes = expectedRoutes,
+ SystemModules = expectedSysModule
};
var expectedCommands = Fixture.CreateMany(5).Select(command =>
@@ -127,8 +130,13 @@ public async Task GetEdgeModelShouldReturnValueAsync()
_ = this.mockConfigService.Setup(x => x.GetConfigModuleList(It.IsAny()))
.ReturnsAsync(expectedModules);
+
+ _ = this.mockConfigService.Setup(x => x.GetModelSystemModule(It.IsAny()))
+ .ReturnsAsync(expectedSysModule);
+
_ = this.mockConfigService.Setup(x => x.GetConfigRouteList(It.IsAny()))
.ReturnsAsync(expectedRoutes);
+
_ = this.mockEdgeDeviceModelCommandRepository.Setup(x => x.GetAll())
.Returns(expectedCommands);