Skip to content

Commit

Permalink
Feature: Improve edge models for AWS (#2142)
Browse files Browse the repository at this point in the history
* Add edge module id to store greengrass public components

* Add GetPublicEdgeModules

* Add dialogs for aws json component + public components

* Update create and edit edge model pages to handle aws greengrass components

* Update GetPublicEdgeModules to load all public greengrass components + fix loading public component

* Update aws greengrass dialogs

* Fi edge model create/edit pages to handle public and private edge modules (aws components)

* Remove dead code from AwsConfigService

* Fix class AwsConfigService after rebase

* Fix #2145 - Move Savechanges at last step of CRUD in edge model service

* Fix duplication issue after selecting public components

* Fix unit tests

* Fix deployment model synchronization

* Fix Edgedevice load and update

* Add unit tests on GetPublicEdgeModules

* Fix codeql warning on CreateEdgeModelShouldThrowInternalServerErrorExceptionIfDbUpdateExceptionOccurs

* Rename AwsGreengrassPublicComponents to AwsGreengrassPublicComponentsDialog

* Add unit tests on AwsGreengrassComponentDialog and AwsGreengrassPublicComponentsDialog

* Add UT GetPublicEdgeModules_GetPublicEdgeModules_EdgeModulesReturned

* Add UT GetPublicEdgeModules_GetPublicEdgeModules_EdgeModulesReturned on EdgeModelService

* Add UT GetPublicEdgeModules_GetPublicEdgeModules_EdgeModulesReturned on EdgeModelClientService

* Add unit tests on EdgeModelDetailPage and CreateEdgeModelsPage on add edge module and public edge modules

* Fix codeql warning

---------

Co-authored-by: Kevin BEAUGRAND <contact@kbeaugrand.fr>
  • Loading branch information
hocinehacherouf and kbeaugrand committed Jun 20, 2023
1 parent 24f1289 commit 466dca2
Show file tree
Hide file tree
Showing 32 changed files with 2,479 additions and 1,761 deletions.
12 changes: 7 additions & 5 deletions src/AzureIoTHub.Portal.Application/Services/IConfigService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
namespace AzureIoTHub.Portal.Application.Services
{
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Threading.Tasks;
using AzureIoTHub.Portal.Models.v10;
using AzureIoTHub.Portal.Shared.Models.v10;
using Microsoft.Azure.Devices;
Expand All @@ -15,13 +15,13 @@ public interface IConfigService

Task<IEnumerable<Configuration>> GetDevicesConfigurations();

Task RollOutDeviceModelConfiguration(string modelId, Dictionary<string, object> desiredProperties);
Task<string> RollOutDeviceModelConfiguration(string modelId, Dictionary<string, object> desiredProperties);

Task DeleteDeviceModelConfigurationByConfigurationNamePrefix(string configurationNamePrefix);

Task RollOutEdgeModelConfiguration(IoTEdgeModel edgeModel);
Task<string> RollOutEdgeModelConfiguration(IoTEdgeModel edgeModel);

Task RollOutDeviceConfiguration(string modelId, Dictionary<string, object> desiredProperties, string configurationId, Dictionary<string, string> targetTags, int priority = 0);
Task<string> RollOutDeviceConfiguration(string modelId, Dictionary<string, object> desiredProperties, string configurationId, Dictionary<string, string> targetTags, int priority = 0);

Task<Configuration> GetConfigItem(string id);

Expand All @@ -33,6 +33,8 @@ public interface IConfigService

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

Task<List<IoTEdgeRoute>> GetConfigRouteList(string modelId);
Task<List<IoTEdgeRoute>> GetConfigRouteList(string modelId);

Task<IEnumerable<IoTEdgeModule>> GetPublicEdgeModules();
}
}
62 changes: 31 additions & 31 deletions src/AzureIoTHub.Portal.Application/Services/IEdgeModelService.cs
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
// Copyright (c) CGI France. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace AzureIoTHub.Portal.Application.Services
{
using System.Collections.Generic;
using System.Threading.Tasks;
using AzureIoTHub.Portal.Models.v10;
using AzureIoTHub.Portal.Shared.Models.v10.Filters;
using Microsoft.AspNetCore.Http;

public interface IEdgeModelService
{
Task<IEnumerable<IoTEdgeModelListItem>> GetEdgeModels(EdgeModelFilter edgeModelFilter);

Task<IoTEdgeModel> GetEdgeModel(string modelId);

Task CreateEdgeModel(IoTEdgeModel edgeModel);
Task UpdateEdgeModel(IoTEdgeModel edgeModel);

Task DeleteEdgeModel(string edgeModelId);

Task<string> GetEdgeModelAvatar(string edgeModelId);

Task<string> UpdateEdgeModelAvatar(string edgeModelId, IFormFile file);

Task DeleteEdgeModelAvatar(string edgeModelId);

Task SaveModuleCommands(IoTEdgeModel deviceModelObject);
}
}
// Copyright (c) CGI France. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace AzureIoTHub.Portal.Application.Services
{
using System.Collections.Generic;
using System.Threading.Tasks;
using AzureIoTHub.Portal.Models.v10;
using AzureIoTHub.Portal.Shared.Models.v10.Filters;
using Microsoft.AspNetCore.Http;

public interface IEdgeModelService
{
Task<IEnumerable<IoTEdgeModelListItem>> GetEdgeModels(EdgeModelFilter edgeModelFilter);

Task<IoTEdgeModel> GetEdgeModel(string modelId);

Task CreateEdgeModel(IoTEdgeModel edgeModel);
Task UpdateEdgeModel(IoTEdgeModel edgeModel);

Task DeleteEdgeModel(string edgeModelId);

Task<string> GetEdgeModelAvatar(string edgeModelId);

Task<string> UpdateEdgeModelAvatar(string edgeModelId, IFormFile file);

Task DeleteEdgeModelAvatar(string edgeModelId);

Task<IEnumerable<IoTEdgeModule>> GetPublicEdgeModules();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
@using AzureIoTHub.Portal.Models.v10;
@using System.Text.Json;

<MudDialog>
<DialogContent>
<MudForm @ref="form" @bind-IsValid="@formIsValid" >
<MudContainer Style="max-height: 600px; overflow-y: scroll">
<MudTextField id="greengrass-component-recipe-json" T="string" Label="Recipe as JSON" Variant="Variant.Text" @bind-Value="@jsonRecipe" Lines="20"
Validation="@(new Func<string, IEnumerable<string>>(ValidateJsonRecipe))" />
</MudContainer>
</MudForm>
</DialogContent>
<DialogActions>
<MudButton id="greengrass-component-cancel" OnClick="Cancel">Cancel</MudButton>
<MudButton id="greengrass-component-submit" Color="Color.Primary" OnClick="Submit" Disabled="@(!formIsValid)">Submit</MudButton>
</DialogActions>
</MudDialog>
@code {
[CascadingParameter]
MudDialogInstance MudDialog { get; set; } = default!;

[Parameter]
public IoTEdgeModule Module { get; set; } = default!;

[Parameter]
public List<IoTEdgeModule> EdgeModules { get; set; } = default!;

[Parameter]
public Context Context { get; set; } = default!;

private bool formIsValid;
private MudForm form = default!;

private string currentModuleName = default!;
private string currentModuleVersion = default!;
private string jsonRecipe = default!;

protected override void OnInitialized()
{
if (Context == Context.Edit)
{
jsonRecipe = Module.ContainerCreateOptions;
}
}

private IEnumerable<string> ValidateJsonRecipe(string json)
{
var jsonProperties = JsonSerializer.Deserialize<Dictionary<string, object>>(json) ?? new Dictionary<string, object>();

if (!jsonProperties.ContainsKey("ComponentName"))
{
currentModuleName = string.Empty;
yield return "ComponentName is missing";
}
else if (string.IsNullOrEmpty(jsonProperties["ComponentName"].ToString()))
{
currentModuleName = string.Empty;
yield return "ComponentName is empty";
}
else
{
if (Context == Context.Create && EdgeModules.Any(m => m.ModuleName.Equals(jsonProperties["ComponentName"].ToString())))
{
yield return $"Component {jsonProperties["ComponentName"].ToString()} is already used";
}
currentModuleName = jsonProperties["ComponentName"].ToString();
}

if (!jsonProperties.ContainsKey("ComponentVersion"))
{
currentModuleVersion = string.Empty;
yield return "ComponentVersion is missing";
}
else if (string.IsNullOrEmpty(jsonProperties["ComponentVersion"].ToString()))
{
currentModuleVersion = string.Empty;
yield return "ComponentVersion is empty";
}
else
{
currentModuleVersion = jsonProperties["ComponentVersion"].ToString();
}
}

void Cancel() => MudDialog.Cancel();

public async Task Submit()
{
await form.Validate();
if (!form.IsValid) return;

if(Context == Context.Create)
{
EdgeModules.Add(new IoTEdgeModule
{
ModuleName = currentModuleName,
Version = currentModuleVersion,
ImageURI = "example.com",
ContainerCreateOptions = jsonRecipe
});
}
else
{
Module.ModuleName = currentModuleName;
Module.Version = currentModuleVersion;
// ImageURI is required, but not used for Greengrass components
Module.ImageURI = "example.com";
Module.ContainerCreateOptions = jsonRecipe;
}

MudDialog.Close(DialogResult.Ok(true));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
@using AzureIoTHub.Portal.Models.v10;

@inject IEdgeModelClientService EdgeModelClientService

<MudDialog>
<DialogContent>
<MudTable T="IoTEdgeModule" Items="@publicComponents" MultiSelection="true" SelectOnRowClick="true"
@bind-SelectedItems="selectedPublicComponents" Hover="true">
<HeaderContent>
<MudTh>Name</MudTh>
<MudTh>Version</MudTh>
</HeaderContent>
<RowTemplate>
<MudTd DataLabel="Name">@context.ModuleName</MudTd>
<MudTd DataLabel="Position">@context.Version</MudTd>
</RowTemplate>
<PagerContent>
<MudTablePager PageSizeOptions="new int[]{50, 100}" />
</PagerContent>
</MudTable>
</DialogContent>
<DialogActions>
<MudButton id="greengrass-public-components-cancel" OnClick="Cancel">Cancel</MudButton>
<MudButton id="greengrass-public-components-submit" Color="Color.Primary" OnClick="Submit">Submit</MudButton>
</DialogActions>
</MudDialog>
@code {
[CascadingParameter]
MudDialogInstance MudDialog { get; set; } = default!;

[Parameter]
public List<IoTEdgeModule> EdgeModules { get; set; } = default!;

private List<IoTEdgeModule> publicComponents = new();
private HashSet<IoTEdgeModule> selectedPublicComponents = new HashSet<IoTEdgeModule>();

protected async override Task OnInitializedAsync()
{
publicComponents = await EdgeModelClientService.GetPublicEdgeModules();
selectedPublicComponents = new HashSet<IoTEdgeModule>(publicComponents.Where(m => EdgeModules.Any(e => m.Id.Equals(e.Id))));
}

private void Cancel() => MudDialog.Cancel();

private void Submit()
{
EdgeModules.RemoveAll(e => !string.IsNullOrEmpty(e.Id));
EdgeModules.AddRange(selectedPublicComponents.ToList());

MudDialog.Close(DialogResult.Ok(true));
}

}
11 changes: 11 additions & 0 deletions src/AzureIoTHub.Portal.Client/Enums/Context.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright (c) CGI France. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace AzureIoTHub.Portal.Client.Enums
{
public enum Context
{
Create,
Edit
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@
Label="Device name"
Variant="Variant.Outlined"
For="@(()=> edgeDevice.DeviceName)"
Required="true" />
Required="true"
ReadOnly=@(Portal.CloudProvider == CloudProviders.AWS)/>
</MudItem>
</MudGrid>
</ChildContent>
Expand Down
Loading

0 comments on commit 466dca2

Please sign in to comment.