Skip to content

Commit

Permalink
BugFix/1415_-_Unable configure system modules on existing edge models (
Browse files Browse the repository at this point in the history
…#1420)

* resolve #1415

* fix unit test

* add new test for configService

* add new test

* add new test
  • Loading branch information
Sben65 authored Oct 26, 2022
1 parent 1ce4299 commit 3d6ff94
Show file tree
Hide file tree
Showing 7 changed files with 291 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -69,6 +70,43 @@
</MudItem>
</MudGrid>

<MudGrid>
<MudItem xs="12">
<MudExpansionPanel Text="System Modules" IsInitiallyExpanded="true">
<TitleContent><MudText Typo="Typo.h6">System Modules</MudText></TitleContent>
<ChildContent>
<MudItem xs="12">
<MudTable Items="EdgeModel.SystemModules" Dense=true Hover=true Bordered=true Striped=true Elevation=0>
<ColGroup>
<col style="width: 15%;" />
<col style="width: 50%;" />
<col style="width: 10%;" />
</ColGroup>

<HeaderContent>
<MudTh Style="text-align: center">Module name</MudTh>
<MudTh Style="text-align: center">Image URI</MudTh>
<MudTh Style="text-align: center">Detail</MudTh>
</HeaderContent>

<RowTemplate Context="sysModule">
<MudTd DataLabel="System module Name" Style="word-break: break-all;">
<MudTextField id=@nameof(EdgeModelSystemModule.Name) @bind-Value="@sysModule.Name" Margin="Margin.Dense" Label="Module name" For="@(() => sysModule.Name )" Variant="Variant.Outlined" Disabled />
</MudTd>
<MudTd DataLabel="System module Image URI" Style="word-break: break-all;">
<MudTextField id=@nameof(EdgeModelSystemModule.ImageUri) @bind-Value="@sysModule.ImageUri" Margin="Margin.Dense" Label="Image URI" For="@(() => sysModule.ImageUri )" Variant="Variant.Outlined" />
</MudTd>
<MudTd DataLabel="See detail" Style="text-align: center;">
<MudButton Variant="Variant.Filled" id="@("editSystModuleButton_"+sysModule.Name)" OnClick="@(async () => await ShowSystemModuleDetail(sysModule))">Detail</MudButton>
</MudTd>
</RowTemplate>
</MudTable>
</MudItem>
</ChildContent>
</MudExpansionPanel>
</MudItem>
</MudGrid>

<MudGrid>
<MudItem xs="12">
<MudExpansionPanel Text="Modules" IsInitiallyExpanded="true">
Expand Down Expand Up @@ -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<SystemModuleDialog>(systemModule.Name, parameters, options).Result;

if (result.Cancelled)
{
return;
}
}
}
43 changes: 43 additions & 0 deletions src/AzureIoTHub.Portal.Server/Services/ConfigService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -105,6 +106,48 @@ public async Task<List<IoTEdgeModule>> GetConfigModuleList(string modelId)
return moduleList;
}

public async Task<List<EdgeModelSystemModule>> 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<EdgeModelSystemModule>();

// 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<JProperty>().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<List<IoTEdgeRoute>> GetConfigRouteList(string modelId)
{
var configList = await GetIoTEdgeConfigurations();
Expand Down
4 changes: 3 additions & 1 deletion src/AzureIoTHub.Portal.Server/Services/EdgeModelService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ public async Task<IoTEdgeModel> 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();

Expand All @@ -176,7 +177,8 @@ public async Task<IoTEdgeModel> GetEdgeModel(string modelId)
Name = edgeModelEntity.Name,
Description = edgeModelEntity.Description,
EdgeModules = modules,
EdgeRoutes = routes
EdgeRoutes = routes,
SystemModules = sysModules,
};

foreach (var command in commands)
Expand Down
3 changes: 3 additions & 0 deletions src/AzureIoTHub.Portal.Server/Services/IConfigService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -28,6 +29,8 @@ public interface IConfigService

Task<List<IoTEdgeModule>> GetConfigModuleList(string modelId);

Task<List<EdgeModelSystemModule>> GetModelSystemModule(string modelId);

Task<List<IoTEdgeRoute>> GetConfigRouteList(string modelId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -295,6 +296,60 @@ public void ClickShowEditEdgeModuleDialogShouldDisplayEditModuleDialogAndReturnI
cut.WaitForAssertion(() => MockRepository.VerifyAll());
}

[Test]
public void ClickOnShowSystemModuleDetailShouldShowDialog()
{
// Arrange
_ = SetupLoadEdgeModel();

var mockDialogReference = MockRepository.Create<IDialogReference>();
_ = mockDialogReference.Setup(c => c.Result).ReturnsAsync(DialogResult.Ok("Ok"));

_ = this.mockDialogService
.Setup(c => c.Show<SystemModuleDialog>(It.IsAny<string>(), It.IsAny<DialogParameters>(), It.IsAny<DialogOptions>()))
.Returns(mockDialogReference.Object);

// Act
var cut = RenderComponent<EdgeModelDetailPage>(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<IDialogReference>();
_ = mockDialogReference.Setup(c => c.Result).ReturnsAsync(DialogResult.Cancel());

_ = this.mockDialogService
.Setup(c => c.Show<SystemModuleDialog>(It.IsAny<string>(), It.IsAny<DialogParameters>(), It.IsAny<DialogOptions>()))
.Returns(mockDialogReference.Object);

// Act
var cut = RenderComponent<EdgeModelDetailPage>(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()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Configuration>()
{
configTest
};

var edgeAgentPropertiesDesired = new EdgeAgentPropertiesDesired();
var modules = new Dictionary<string, ConfigModule>()
{
{"module test 01", new ConfigModule() },
};

edgeAgentPropertiesDesired.Modules = modules;

var mockConfigEnumerator = this.mockRepository.Create<IEnumerable<Configuration>>();

configTest.Content.ModulesContent = new Dictionary<string, IDictionary<string, object>>()
{
{
"$edgeAgent", new Dictionary<string, object>()
{
{
"properties.desired", JObject.Parse(JsonConvert.SerializeObject(edgeAgentPropertiesDesired))
}
}
}
};

_ = mockConfigEnumerator.Setup(x => x.GetEnumerator()).Returns(listConfig.GetEnumerator);

_ = this.mockRegistryManager.Setup(c => c.GetConfigurationsAsync(It.Is<int>(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<Configuration>()
{
};

var mockConfigEnumerator = this.mockRepository.Create<IEnumerable<Configuration>>();

_ = mockConfigEnumerator.Setup(x => x.GetEnumerator()).Returns(listConfig.GetEnumerator);

_ = this.mockRegistryManager.Setup(c => c.GetConfigurationsAsync(It.Is<int>(x => x == 0)))
.ReturnsAsync(mockConfigEnumerator.Object);

// Act
var result = async () => await configService.GetModelSystemModule(configTest.Id);

// Assert
_ = result.Should().ThrowAsync<InternalServerErrorException>();

this.mockRepository.VerifyAll();
}

[Test]
public void WhenPropertiesDesiredIsInWrongFormatConfigIsNullGetModelSystemModuleShouldThrowInvalidOperationException()
{
// Arrange
var configService = CreateConfigsServices();

var configTest = new Configuration(Guid.NewGuid().ToString());
var listConfig = new List<Configuration>()
{
configTest
};

var edgeAgentPropertiesDesired = new EdgeAgentPropertiesDesired();
var modules = new Dictionary<string, ConfigModule>()
{
{"module test 01", new ConfigModule() },
{"module test 02", new ConfigModule() }
};

edgeAgentPropertiesDesired.Modules = modules;

var mockConfigEnumerator = this.mockRepository.Create<IEnumerable<Configuration>>();

configTest.Content.ModulesContent = new Dictionary<string, IDictionary<string, object>>()
{
{
"$edgeAgent", new Dictionary<string, object>()
{
{
"properties.desired", edgeAgentPropertiesDesired
}
}
}
};

_ = mockConfigEnumerator.Setup(x => x.GetEnumerator()).Returns(listConfig.GetEnumerator);

_ = this.mockRegistryManager.Setup(c => c.GetConfigurationsAsync(It.Is<int>(x => x == 0)))
.ReturnsAsync(mockConfigEnumerator.Object);

// Act
var result = async () => await configService.GetModelSystemModule(configTest.Id);

// Assert
_ = result.Should().ThrowAsync<InvalidOperationException>();

this.mockRepository.VerifyAll();
}

[Test]
public async Task GetConfigRouteListShouldReturnAList()
{
Expand Down
Loading

0 comments on commit 3d6ff94

Please sign in to comment.