From bd9e43cf2c95e3700b11dd549f52cadc4bdb379b Mon Sep 17 00:00:00 2001 From: ssgueye2 <127868584+ssgueye2@users.noreply.github.com> Date: Fri, 26 May 2023 14:28:33 +0200 Subject: [PATCH] Update and delete green grass deployment (#2113) * retrieve json attribute from Recipe * Update creation Deployment method + Get Deployment method * FIX Existing Failed Tests * Update method done (But not tested) * Fix Failed Tests + All tests for the update and the Get Deployment Done * retrieve json attribute from Recipe * Update creation Deployment method + Get Deployment method * retrieve json attribute from Recipe * Update creation Deployment method + Get Deployment method * Change IdProvider to ExternalIdentifier + Delete Deployment (DONE) --- .../EdgeModels/CreateEdgeModelsPage.razor | 20 +- .../EdgeModels/EdgeModelDetailPage.razor | 181 +++-- .../EdgeModels/EdgeModule/ModuleDialog.razor | 22 +- .../ConfigHandler.cs | 1 + .../Entities/EdgeDeviceModel.cs | 1 + .../ConfigHandlerBase.cs | 1 + .../DevelopmentConfigHandler.cs | 1 + .../ProductionAWSConfigHandler.cs | 2 + .../ProductionAzureConfigHandler.cs | 2 + .../Services/AWS/AwsConfigService.cs | 225 +++++- .../Services/EdgeModelService.cs | 50 +- .../Startup/AWSServiceCollectionExtension.cs | 8 +- ...525074805_Add Aws DeploymentId.Designer.cs | 590 ++++++++++++++ .../20230525074805_Add Aws DeploymentId.cs | 30 + ...Provider to ExternalIdentifier.Designer.cs | 590 ++++++++++++++ ...Change IdProvider to ExternalIdentifier.cs | 30 + .../PortalDbContextModelSnapshot.cs | 745 +++++++++--------- .../Models/v1.0/IoTEdgeModelListItem.cs | 6 + .../Models/v1.0/IoTEdgeModule.cs | 3 + .../EdgeModels/EdgeModelDetailPageTest.cs | 22 +- .../Services/AWS_Tests/AwsConfigTests.cs | 116 +++ .../Server/Services/EdgeModelServiceTest.cs | 117 +++ 22 files changed, 2257 insertions(+), 506 deletions(-) create mode 100644 src/AzureIoTHub.Portal.Postgres/Migrations/20230525074805_Add Aws DeploymentId.Designer.cs create mode 100644 src/AzureIoTHub.Portal.Postgres/Migrations/20230525074805_Add Aws DeploymentId.cs create mode 100644 src/AzureIoTHub.Portal.Postgres/Migrations/20230526083144_Change IdProvider to ExternalIdentifier.Designer.cs create mode 100644 src/AzureIoTHub.Portal.Postgres/Migrations/20230526083144_Change IdProvider to ExternalIdentifier.cs diff --git a/src/AzureIoTHub.Portal.Client/Pages/EdgeModels/CreateEdgeModelsPage.razor b/src/AzureIoTHub.Portal.Client/Pages/EdgeModels/CreateEdgeModelsPage.razor index e436c0c23..7226764f4 100644 --- a/src/AzureIoTHub.Portal.Client/Pages/EdgeModels/CreateEdgeModelsPage.razor +++ b/src/AzureIoTHub.Portal.Client/Pages/EdgeModels/CreateEdgeModelsPage.razor @@ -196,17 +196,17 @@ - - - - Labels - - - - - - } + + + + Labels + + + + + + diff --git a/src/AzureIoTHub.Portal.Client/Pages/EdgeModels/EdgeModelDetailPage.razor b/src/AzureIoTHub.Portal.Client/Pages/EdgeModels/EdgeModelDetailPage.razor index 1d199a89d..d5538a896 100644 --- a/src/AzureIoTHub.Portal.Client/Pages/EdgeModels/EdgeModelDetailPage.razor +++ b/src/AzureIoTHub.Portal.Client/Pages/EdgeModels/EdgeModelDetailPage.razor @@ -7,12 +7,15 @@ @using AzureIoTHub.Portal.Shared.Models.v10 @using AzureIoTHub.Portal.Client.Validators @using Microsoft.AspNetCore.Components.WebAssembly.Authentication +@using AzureIoTHub.Portal.Shared.Constants + @attribute [Authorize] @inject NavigationManager NavigationManager @inject IEdgeModelClientService EdgeModelService @inject ISnackbar Snackbar @inject IDialogService DialogService +@inject PortalSettings Portal @@ -69,44 +72,46 @@ - - - - - System Modules - - - - - - - - - - - Module name - Image URI - Detail - - - - - - - - - - - Detail - - - - - - - - - + @if (Portal.CloudProvider.Equals(CloudProviders.Azure)) + { + + + + System Modules + + + + + + + + + + + Module name + Image URI + Detail + + + + + + + + + + + Detail + + + + + + + + + } + @@ -149,55 +154,57 @@ - - - - - Routes - - - - - - - - - - - - Name - Value - Priority - Time To Live (Secs) - Delete - - - - - - - - - - - - - - - - - - - - Add new route - - - - - - - - + @if (Portal.CloudProvider.Equals(CloudProviders.Azure)) + { + + + + Routes + + + + + + + + + + + + Name + Value + Priority + Time To Live (Secs) + Delete + + + + + + + + + + + + + + + + + + + + Add new route + + + + + + + + } + diff --git a/src/AzureIoTHub.Portal.Client/Pages/EdgeModels/EdgeModule/ModuleDialog.razor b/src/AzureIoTHub.Portal.Client/Pages/EdgeModels/EdgeModule/ModuleDialog.razor index d807c792f..2d691ea42 100644 --- a/src/AzureIoTHub.Portal.Client/Pages/EdgeModels/EdgeModule/ModuleDialog.razor +++ b/src/AzureIoTHub.Portal.Client/Pages/EdgeModels/EdgeModule/ModuleDialog.razor @@ -39,7 +39,21 @@ How to configure container create options for IoT Edge modules } - + else + { + + + + + The Version Number should be incremented when updating the component + + } + @@ -77,6 +91,7 @@ private string currentModuleName = default!; private string currentImageUri = default!; private string currentContainerCreateOptions = default!; + private string currentNumVersion = "1.0.0"; private List currentEnvironmentVariables = new(); private List currentModuleIdentityTwinSettings = new(); @@ -92,6 +107,7 @@ currentModuleName = Module.ModuleName; currentImageUri = Module.ImageURI; currentContainerCreateOptions = Module.ContainerCreateOptions; + currentNumVersion = Module.Version; currentEnvironmentVariables = new List(Module.EnvironmentVariables.ToArray()); currentModuleIdentityTwinSettings = new List(Module.ModuleIdentityTwinSettings.ToArray()); currentCommands = new List(Module.Commands.ToArray()); @@ -104,6 +120,10 @@ Module.ModuleName = currentModuleName; Module.ImageURI = currentImageUri; Module.ContainerCreateOptions = currentContainerCreateOptions; + + if (Portal.CloudProvider.Equals(CloudProviders.Azure)) { Module.Version = " "; } + else { Module.Version = currentNumVersion; } + Module.EnvironmentVariables = new List(currentEnvironmentVariables.ToArray()); Module.ModuleIdentityTwinSettings = new List(currentModuleIdentityTwinSettings.ToArray()); Module.Commands = new List(currentCommands.ToArray()); diff --git a/src/AzureIoTHub.Portal.Domain/ConfigHandler.cs b/src/AzureIoTHub.Portal.Domain/ConfigHandler.cs index c38f5d5b3..97724719b 100644 --- a/src/AzureIoTHub.Portal.Domain/ConfigHandler.cs +++ b/src/AzureIoTHub.Portal.Domain/ConfigHandler.cs @@ -80,5 +80,6 @@ public abstract class ConfigHandler public abstract string AWSRegion { get; } public abstract string AWSS3StorageConnectionString { get; } public abstract string AWSBucketName { get; } + public abstract string AWSAccountId { get; } } } diff --git a/src/AzureIoTHub.Portal.Domain/Entities/EdgeDeviceModel.cs b/src/AzureIoTHub.Portal.Domain/Entities/EdgeDeviceModel.cs index 12b29c723..14f9c1622 100644 --- a/src/AzureIoTHub.Portal.Domain/Entities/EdgeDeviceModel.cs +++ b/src/AzureIoTHub.Portal.Domain/Entities/EdgeDeviceModel.cs @@ -10,6 +10,7 @@ public class EdgeDeviceModel : EntityBase public string Name { get; set; } = default!; public string? Description { get; set; } + public string? ExternalIdentifier { get; set; } /// /// Labels diff --git a/src/AzureIoTHub.Portal.Infrastructure/ConfigHandlerBase.cs b/src/AzureIoTHub.Portal.Infrastructure/ConfigHandlerBase.cs index f5b8f3454..a87743b92 100644 --- a/src/AzureIoTHub.Portal.Infrastructure/ConfigHandlerBase.cs +++ b/src/AzureIoTHub.Portal.Infrastructure/ConfigHandlerBase.cs @@ -57,6 +57,7 @@ internal abstract class ConfigHandlerBase : ConfigHandler internal const string AWSRegionKey = "AWS:Region"; internal const string AWSS3StorageConnectionStringKey = "AWS:S3Storage:ConnectionString"; internal const string AWSBucketNameKey = "AWS:BucketName"; + internal const string AWSAccountIdKey = "AWS:AccountId"; } } diff --git a/src/AzureIoTHub.Portal.Infrastructure/DevelopmentConfigHandler.cs b/src/AzureIoTHub.Portal.Infrastructure/DevelopmentConfigHandler.cs index 1581123d9..0360bff0c 100644 --- a/src/AzureIoTHub.Portal.Infrastructure/DevelopmentConfigHandler.cs +++ b/src/AzureIoTHub.Portal.Infrastructure/DevelopmentConfigHandler.cs @@ -89,5 +89,6 @@ internal DevelopmentConfigHandler(IConfiguration config) public override string AWSRegion => this.config[AWSRegionKey]!; public override string AWSS3StorageConnectionString => this.config[AWSS3StorageConnectionStringKey]!; public override string AWSBucketName => this.config[AWSBucketNameKey]!; + public override string AWSAccountId => this.config[AWSAccountIdKey]!; } } diff --git a/src/AzureIoTHub.Portal.Infrastructure/ProductionAWSConfigHandler.cs b/src/AzureIoTHub.Portal.Infrastructure/ProductionAWSConfigHandler.cs index 7e378a9c6..02966e67e 100644 --- a/src/AzureIoTHub.Portal.Infrastructure/ProductionAWSConfigHandler.cs +++ b/src/AzureIoTHub.Portal.Infrastructure/ProductionAWSConfigHandler.cs @@ -89,5 +89,7 @@ internal ProductionAWSConfigHandler(IConfiguration config) public override string AWSRegion => this.config[AWSRegionKey]!; public override string AWSS3StorageConnectionString => this.config[AWSS3StorageConnectionStringKey]!; public override string AWSBucketName => this.config[AWSBucketNameKey]!; + public override string AWSAccountId => this.config[AWSAccountIdKey]!; + } } diff --git a/src/AzureIoTHub.Portal.Infrastructure/ProductionAzureConfigHandler.cs b/src/AzureIoTHub.Portal.Infrastructure/ProductionAzureConfigHandler.cs index 8aba668b6..052d7670a 100644 --- a/src/AzureIoTHub.Portal.Infrastructure/ProductionAzureConfigHandler.cs +++ b/src/AzureIoTHub.Portal.Infrastructure/ProductionAzureConfigHandler.cs @@ -92,5 +92,7 @@ internal ProductionAzureConfigHandler(IConfiguration config) public override string AWSS3StorageConnectionString => throw new NotImplementedException(); public override string AWSBucketName => throw new NotImplementedException(); + public override string AWSAccountId => throw new NotImplementedException(); + } } diff --git a/src/AzureIoTHub.Portal.Infrastructure/Services/AWS/AwsConfigService.cs b/src/AzureIoTHub.Portal.Infrastructure/Services/AWS/AwsConfigService.cs index cefd2231d..8b3d9ef29 100644 --- a/src/AzureIoTHub.Portal.Infrastructure/Services/AWS/AwsConfigService.cs +++ b/src/AzureIoTHub.Portal.Infrastructure/Services/AWS/AwsConfigService.cs @@ -11,8 +11,11 @@ namespace AzureIoTHub.Portal.Infrastructure.Services.AWS using Amazon.GreengrassV2.Model; using Amazon.IoT; using Amazon.IoT.Model; + using AutoMapper; using AzureIoTHub.Portal.Application.Services; + using AzureIoTHub.Portal.Domain; using AzureIoTHub.Portal.Domain.Exceptions; + using AzureIoTHub.Portal.Domain.Repositories; using AzureIoTHub.Portal.Models.v10; using AzureIoTHub.Portal.Shared.Models.v10; using Newtonsoft.Json.Linq; @@ -23,16 +26,30 @@ public class AwsConfigService : IConfigService private readonly IAmazonGreengrassV2 greengras; private readonly IAmazonIoT iotClient; + private readonly IUnitOfWork unitOfWork; + private readonly IEdgeDeviceModelRepository edgeModelRepository; + private readonly ConfigHandler config; + private readonly IMapper mapper; + public AwsConfigService( IAmazonGreengrassV2 greengras, - IAmazonIoT iotClient) + IAmazonIoT iot, + IMapper mapper, + IUnitOfWork unitOfWork, + IEdgeDeviceModelRepository edgeModelRepository, + ConfigHandler config) { this.greengras = greengras; - this.iotClient = iotClient; + this.iotClient = iot; + this.mapper = mapper; + this.unitOfWork = unitOfWork; + this.edgeModelRepository = edgeModelRepository; + this.config = config; } public async Task RollOutEdgeModelConfiguration(IoTEdgeModel edgeModel) { + var createDeploymentRequest = new CreateDeploymentRequest { DeploymentName = edgeModel?.Name, @@ -47,6 +64,25 @@ public async Task RollOutEdgeModelConfiguration(IoTEdgeModel edgeModel) throw new InternalServerErrorException("The deployment creation failed due to an error in the Amazon IoT API."); } + else + { + var edgeModelEntity = await this.edgeModelRepository.GetByIdAsync(edgeModel?.ModelId!); + if (edgeModelEntity == null) + { + throw new Domain.Exceptions.ResourceNotFoundException($"The edge model with id {edgeModel?.ModelId} not found"); + + } + else + { + edgeModel!.ExternalIdentifier = createDeploymentResponse.DeploymentId; + + _ = this.mapper.Map(edgeModel, edgeModelEntity); + + this.edgeModelRepository.Update(edgeModelEntity); + await this.unitOfWork.SaveAsync(); + } + + } } private async Task GetThingGroupArn(IoTEdgeModel edgeModel) @@ -118,12 +154,12 @@ private async Task> CreateG InlineRecipe = recipeStream }; var response = await greengras.CreateComponentVersionAsync(componentVersion); - if (response.HttpStatusCode != System.Net.HttpStatusCode.Created) + if (response.HttpStatusCode != HttpStatusCode.Created) { throw new InternalServerErrorException("The component creation failed due to an error in the Amazon IoT API."); } - listcomponentName.Add(component.ModuleName, new ComponentDeploymentSpecification { ComponentVersion = "1.0.0" }); + listcomponentName.Add(component.ModuleName, new ComponentDeploymentSpecification { ComponentVersion = component.Version }); } return listcomponentName; @@ -141,7 +177,7 @@ private static JObject JsonCreateComponent(IoTEdgeModule component) var recipeJson =new JObject( new JProperty("RecipeFormatVersion", "2020-01-25"), new JProperty("ComponentName", component.ModuleName), - new JProperty("ComponentVersion", "1.0.0"), + new JProperty("ComponentVersion", component.Version), new JProperty("ComponentPublisher", "IotHub"), new JProperty("ComponentDependencies", new JObject( @@ -204,9 +240,45 @@ public Task GetConfigItem(string id) throw new NotImplementedException(); } - public Task DeleteConfiguration(string configId) + public async Task DeleteConfiguration(string modelId) { - throw new NotImplementedException(); + var modules = await GetConfigModuleList(modelId); + foreach (var module in modules) + { + var deletedComponentResponse = await this.greengras.DeleteComponentAsync(new DeleteComponentRequest + { + Arn = $"arn:aws:greengrass:{config.AWSRegion}:{config.AWSAccountId}:components:{module.ModuleName}:versions:{module.Version}" + }); + + if (deletedComponentResponse.HttpStatusCode != HttpStatusCode.NoContent) + { + throw new InternalServerErrorException("The deletion of the component failed due to an error in the Amazon IoT API."); + + } + } + + var cancelDeploymentResponse = await this.greengras.CancelDeploymentAsync(new CancelDeploymentRequest + { + DeploymentId = modelId + }); + if (cancelDeploymentResponse.HttpStatusCode != HttpStatusCode.OK) + { + throw new InternalServerErrorException("The cancellation of the deployment failed due to an error in the Amazon IoT API."); + + } + else + { + var deleteDeploymentResponse = await this.greengras.DeleteDeploymentAsync(new DeleteDeploymentRequest + { + DeploymentId = modelId + }); + + if (deleteDeploymentResponse.HttpStatusCode != HttpStatusCode.NoContent) + { + throw new InternalServerErrorException("The deletion of the deployment failed due to an error in the Amazon IoT API."); + } + } + } public Task GetFailedDeploymentsCount() @@ -214,10 +286,143 @@ public Task GetFailedDeploymentsCount() throw new NotImplementedException(); } - public Task> GetConfigModuleList(string modelId) + public async Task> GetConfigModuleList(string modelId) { - // To be implemented with the update method in EdgeModelService - throw new NotImplementedException(); + + var moduleList = new List(); + + var getDeployement = new GetDeploymentRequest + { + DeploymentId = modelId, + }; + try + { + var response = await this.greengras.GetDeploymentAsync(getDeployement); + + foreach (var compoenent in response.Components) + { + var responseComponent = await this.greengras.GetComponentAsync(new GetComponentRequest + { + Arn = $"arn:aws:greengrass:{config.AWSRegion}:{config.AWSAccountId}:components:{compoenent.Key}:versions:{compoenent.Value.ComponentVersion}", + RecipeOutputFormat = RecipeOutputFormat.JSON + }); + + // Read the Recipe which is in JSON Format + using var reader = new StreamReader(responseComponent.Recipe); + var recipeJsonString = reader.ReadToEnd(); + + // Extract the imageUri from the 'Run' JSON object + var uriImage = retreiveImageUri("Lifecycle", "Run", recipeJsonString); + // Extract the environment Variables from the 'Environment' JSON object + var env = retreiveEnvVariableAttr("Lifecycle", "Environment", recipeJsonString); + + var iotEdgeModule = new IoTEdgeModule + { + ModuleName = compoenent.Key, + ImageURI = uriImage, + EnvironmentVariables = env, + Version = compoenent.Value.ComponentVersion + }; + + moduleList.Add(iotEdgeModule); + + } + return moduleList; + } + catch (Amazon.IoT.Model.ResourceNotFoundException) + { + throw new InternalServerErrorException("The deployment is not found"); + + } + } + + private static string retreiveImageUri(string parent, string child, string recipeJsonString) + { + var uriImage = ""; + // Parse the string as a JSON object + var recipeJsonObject = JObject.Parse(recipeJsonString); + + // Extract the "Manifests" array + var jArray = recipeJsonObject["Manifests"] as JArray; + var manifests = jArray; + + if (manifests != null && manifests.Count > 0) + { + // Get the first manifest in the array + var firstManifest = manifests[0] as JObject; + + // Extract the "Lifecycle" object + var jObject = firstManifest?[parent] as JObject; + var lifecycle = jObject; + + if (lifecycle != null) + { + // Extract the value of "Run" + var runValue = lifecycle[child]?.ToString(); + + // Search the index of the 1st whitespace + var firstSpaceIndex = runValue.IndexOf(' '); + + if (firstSpaceIndex != -1) + { + // // Search the index of the 2nd whitespace + var secondSpaceIndex = runValue.IndexOf(' ', firstSpaceIndex + 1); + + if (secondSpaceIndex != -1) + { + // Extract the URI iamge + uriImage = runValue[(secondSpaceIndex + 1)..]; + } + + } + } + } + + return uriImage; + } + + private static List retreiveEnvVariableAttr(string parent, string child, string recipeJsonString) + { + + // Parse the string as a JSON object + var recipeJsonObject = JObject.Parse(recipeJsonString); + + var environmentVariables = new List(); + + // Extract the "Manifests" array + var jArray = recipeJsonObject["Manifests"] as JArray; + var manifests = jArray; + + if (manifests != null && manifests.Count > 0) + { + // Get the first manifest in the array + var firstManifest = manifests[0] as JObject; + + // Extract the "Lifecycle" object + var jObject = firstManifest?[parent] as JObject; + var lifecycle = jObject; + + if (lifecycle != null) + { + // Extract the value of "Environment" + var env = lifecycle?[child] as JObject; + + // Convert Environment JSON Object as a dictionnary + var keyValuePairs = env!.ToObject>(); + + foreach (var kvp in keyValuePairs!) + { + var iotEnvVariable = new IoTEdgeModuleEnvironmentVariable + { + Name = kvp.Key, + Value = kvp.Value + }; + + environmentVariables.Add(iotEnvVariable); + } + } + } + return environmentVariables; } public Task> GetModelSystemModule(string modelId) diff --git a/src/AzureIoTHub.Portal.Infrastructure/Services/EdgeModelService.cs b/src/AzureIoTHub.Portal.Infrastructure/Services/EdgeModelService.cs index 83fe5de22..96087e3a9 100644 --- a/src/AzureIoTHub.Portal.Infrastructure/Services/EdgeModelService.cs +++ b/src/AzureIoTHub.Portal.Infrastructure/Services/EdgeModelService.cs @@ -117,11 +117,14 @@ public async Task CreateEdgeModel(IoTEdgeModel edgeModel) _ = await this.deviceModelImageManager.SetDefaultImageToModel(edgeModel?.ModelId); + if (this.config.CloudProvider.Equals(CloudProviders.Azure, StringComparison.Ordinal)) { await SaveModuleCommands(edgeModel); } + await this.configService.RollOutEdgeModelConfiguration(edgeModel); + } /// @@ -171,20 +174,20 @@ public async Task GetEdgeModel(string modelId) } if (config.CloudProvider.Equals(CloudProviders.Azure, StringComparison.OrdinalIgnoreCase)) { - return await GetAzureEdgeModel(modelId, edgeModelEntity); + return await GetAzureEdgeModel(edgeModelEntity); } else { - return await GetAwsEdgeModel(modelId, edgeModelEntity); + return await GetAwsEdgeModel(edgeModelEntity); } } - private async Task GetAzureEdgeModel(string modelId, EdgeDeviceModel edgeModelEntity) + private async Task GetAzureEdgeModel(EdgeDeviceModel edgeModelEntity) { - 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(); + var modules = await this.configService.GetConfigModuleList(edgeModelEntity.Id); + var sysModules = await this.configService.GetModelSystemModule(edgeModelEntity.Id); + var routes = await this.configService.GetConfigRouteList(edgeModelEntity.Id); + var commands = this.commandRepository.GetAll().Where(x => x.EdgeDeviceModelId == edgeModelEntity.Id).ToList(); //TODO : User a mapper //Previously return this.edgeDeviceModelMapper.CreateEdgeDeviceModel(query.Value, modules, routes, commands); @@ -214,9 +217,9 @@ private async Task GetAzureEdgeModel(string modelId, EdgeDeviceMod return result; } - private async Task GetAwsEdgeModel(string modelId, EdgeDeviceModel edgeModelEntity) + private async Task GetAwsEdgeModel(EdgeDeviceModel edgeModelEntity) { - var modules = await this.configService.GetConfigModuleList(modelId); + var modules = await this.configService.GetConfigModuleList(edgeModelEntity.ExternalIdentifier!); //TODO : User a mapper //Previously return this.edgeDeviceModelMapper.CreateEdgeDeviceModel(query.Value, modules, routes, commands); var result = new IoTEdgeModel @@ -257,7 +260,11 @@ public async Task UpdateEdgeModel(IoTEdgeModel edgeModel) await this.unitOfWork.SaveAsync(); - await SaveModuleCommands(edgeModel); + if (this.config.CloudProvider.Equals(CloudProviders.Azure, StringComparison.Ordinal)) + { + await SaveModuleCommands(edgeModel); + } + await this.configService.RollOutEdgeModelConfiguration(edgeModel); } @@ -275,17 +282,24 @@ public async Task DeleteEdgeModel(string edgeModelId) return; } - var config = this.configService.GetIoTEdgeConfigurations().Result.FirstOrDefault(x => x.Id.StartsWith(edgeModelId, StringComparison.Ordinal)); - - if (config != null) + if (this.config.CloudProvider.Equals(CloudProviders.Azure, StringComparison.Ordinal)) { - await this.configService.DeleteConfiguration(config.Id); - } + var config = this.configService.GetIoTEdgeConfigurations().Result.FirstOrDefault(x => x.Id.StartsWith(edgeModelId, StringComparison.Ordinal)); - var existingCommands = this.commandRepository.GetAll().Where(x => x.EdgeDeviceModelId == edgeModelId).ToList(); - foreach (var command in existingCommands) + if (config != null) + { + await this.configService.DeleteConfiguration(config.Id); + } + + var existingCommands = this.commandRepository.GetAll().Where(x => x.EdgeDeviceModelId == edgeModelId).ToList(); + foreach (var command in existingCommands) + { + this.commandRepository.Delete(command.Id); + } + } + else { - this.commandRepository.Delete(command.Id); + await this.configService.DeleteConfiguration(edgeModelEntity.ExternalIdentifier!); } foreach (var labelEntity in edgeModelEntity.Labels) diff --git a/src/AzureIoTHub.Portal.Infrastructure/Startup/AWSServiceCollectionExtension.cs b/src/AzureIoTHub.Portal.Infrastructure/Startup/AWSServiceCollectionExtension.cs index 0db5dbd6b..b2e501114 100644 --- a/src/AzureIoTHub.Portal.Infrastructure/Startup/AWSServiceCollectionExtension.cs +++ b/src/AzureIoTHub.Portal.Infrastructure/Startup/AWSServiceCollectionExtension.cs @@ -29,8 +29,7 @@ public static IServiceCollection AddAWSInfrastructureLayer(this IServiceCollecti .ConfigureAWSClient(configuration).Result .ConfigureAWSServices() .ConfigureAWSDeviceModelImages() - .ConfigureAWSSyncJobs(configuration) - .ConfigureOtherDependencies(); + .ConfigureAWSSyncJobs(configuration); } private static async Task ConfigureAWSClient(this IServiceCollection services, ConfigHandler configuration) { @@ -88,10 +87,5 @@ private static IServiceCollection ConfigureAWSSyncJobs(this IServiceCollection s }); } - private static IServiceCollection ConfigureOtherDependencies(this IServiceCollection services) - { - _ = services.AddSingleton(new PortalSettings()); - return services; - } } } diff --git a/src/AzureIoTHub.Portal.Postgres/Migrations/20230525074805_Add Aws DeploymentId.Designer.cs b/src/AzureIoTHub.Portal.Postgres/Migrations/20230525074805_Add Aws DeploymentId.Designer.cs new file mode 100644 index 000000000..64895a388 --- /dev/null +++ b/src/AzureIoTHub.Portal.Postgres/Migrations/20230525074805_Add Aws DeploymentId.Designer.cs @@ -0,0 +1,590 @@ +// +using System; +using AzureIoTHub.Portal.Infrastructure; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace AzureIoTHub.Portal.Postgres.Migrations +{ + [DbContext(typeof(PortalDbContext))] + [Migration("20230525074805_Add Aws DeploymentId")] + partial class AddAwsDeploymentId + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.4") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.Concentrator", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ClientThumbprint") + .HasColumnType("text"); + + b.Property("DeviceType") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsConnected") + .HasColumnType("boolean"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("LoraRegion") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("Concentrators"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.Device", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("DeviceModelId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsConnected") + .HasColumnType("boolean"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("StatusUpdatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Version") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DeviceModelId"); + + b.ToTable("Devices", (string)null); + + b.UseTptMappingStrategy(); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.DeviceModel", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ABPRelaxMode") + .HasColumnType("boolean"); + + b.Property("AppEUI") + .HasColumnType("text"); + + b.Property("ClassType") + .HasColumnType("integer"); + + b.Property("Deduplication") + .HasColumnType("integer"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Downlink") + .HasColumnType("boolean"); + + b.Property("IsBuiltin") + .HasColumnType("boolean"); + + b.Property("KeepAliveTimeout") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("PreferredWindow") + .HasColumnType("integer"); + + b.Property("RXDelay") + .HasColumnType("integer"); + + b.Property("SensorDecoder") + .HasColumnType("text"); + + b.Property("SupportLoRaFeatures") + .HasColumnType("boolean"); + + b.Property("UseOTAA") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("DeviceModels"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.DeviceModelCommand", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Confirmed") + .HasColumnType("boolean"); + + b.Property("DeviceModelId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Frame") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsBuiltin") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Port") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("DeviceModelCommands"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.DeviceModelProperty", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("DisplayName") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsWritable") + .HasColumnType("boolean"); + + b.Property("ModelId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("PropertyType") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("DeviceModelProperties"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.DeviceTag", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Label") + .IsRequired() + .HasColumnType("text"); + + b.Property("Required") + .HasColumnType("boolean"); + + b.Property("Searchable") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("DeviceTags"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.DeviceTagValue", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("DeviceId") + .HasColumnType("text"); + + b.Property("EdgeDeviceId") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Value") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DeviceId"); + + b.HasIndex("EdgeDeviceId"); + + b.ToTable("DeviceTagValues"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.EdgeDevice", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ConnectionState") + .IsRequired() + .HasColumnType("text"); + + b.Property("DeviceModelId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("NbDevices") + .HasColumnType("integer"); + + b.Property("NbModules") + .HasColumnType("integer"); + + b.Property("Scope") + .HasColumnType("text"); + + b.Property("Version") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DeviceModelId"); + + b.ToTable("EdgeDevices"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.EdgeDeviceModel", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("IdProvider") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("EdgeDeviceModels"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.EdgeDeviceModelCommand", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("EdgeDeviceModelId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ModuleName") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("EdgeDeviceModelCommands"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.Label", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Color") + .IsRequired() + .HasColumnType("text"); + + b.Property("DeviceId") + .HasColumnType("text"); + + b.Property("DeviceModelId") + .HasColumnType("text"); + + b.Property("EdgeDeviceId") + .HasColumnType("text"); + + b.Property("EdgeDeviceModelId") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DeviceId"); + + b.HasIndex("DeviceModelId"); + + b.HasIndex("EdgeDeviceId"); + + b.HasIndex("EdgeDeviceModelId"); + + b.ToTable("Labels"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.LoRaDeviceTelemetry", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("EnqueuedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("LorawanDeviceId") + .HasColumnType("text"); + + b.Property("Telemetry") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("LorawanDeviceId"); + + b.ToTable("LoRaDeviceTelemetry"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.LorawanDevice", b => + { + b.HasBaseType("AzureIoTHub.Portal.Domain.Entities.Device"); + + b.Property("ABPRelaxMode") + .HasColumnType("boolean"); + + b.Property("AlreadyLoggedInOnce") + .HasColumnType("boolean"); + + b.Property("AppEUI") + .HasColumnType("text"); + + b.Property("AppKey") + .HasColumnType("text"); + + b.Property("AppSKey") + .HasColumnType("text"); + + b.Property("ClassType") + .HasColumnType("integer"); + + b.Property("DataRate") + .HasColumnType("text"); + + b.Property("Deduplication") + .HasColumnType("integer"); + + b.Property("DevAddr") + .HasColumnType("text"); + + b.Property("Downlink") + .HasColumnType("boolean"); + + b.Property("FCntDownStart") + .HasColumnType("integer"); + + b.Property("FCntResetCounter") + .HasColumnType("integer"); + + b.Property("FCntUpStart") + .HasColumnType("integer"); + + b.Property("GatewayID") + .HasColumnType("text"); + + b.Property("KeepAliveTimeout") + .HasColumnType("integer"); + + b.Property("NbRep") + .HasColumnType("text"); + + b.Property("NwkSKey") + .HasColumnType("text"); + + b.Property("PreferredWindow") + .HasColumnType("integer"); + + b.Property("RX1DROffset") + .HasColumnType("integer"); + + b.Property("RX2DataRate") + .HasColumnType("integer"); + + b.Property("RXDelay") + .HasColumnType("integer"); + + b.Property("ReportedRX1DROffset") + .HasColumnType("text"); + + b.Property("ReportedRX2DataRate") + .HasColumnType("text"); + + b.Property("ReportedRXDelay") + .HasColumnType("text"); + + b.Property("SensorDecoder") + .HasColumnType("text"); + + b.Property("Supports32BitFCnt") + .HasColumnType("boolean"); + + b.Property("TxPower") + .HasColumnType("text"); + + b.Property("UseOTAA") + .HasColumnType("boolean"); + + b.ToTable("LorawanDevices", (string)null); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.Device", b => + { + b.HasOne("AzureIoTHub.Portal.Domain.Entities.DeviceModel", "DeviceModel") + .WithMany() + .HasForeignKey("DeviceModelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DeviceModel"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.DeviceTagValue", b => + { + b.HasOne("AzureIoTHub.Portal.Domain.Entities.Device", null) + .WithMany("Tags") + .HasForeignKey("DeviceId"); + + b.HasOne("AzureIoTHub.Portal.Domain.Entities.EdgeDevice", null) + .WithMany("Tags") + .HasForeignKey("EdgeDeviceId"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.EdgeDevice", b => + { + b.HasOne("AzureIoTHub.Portal.Domain.Entities.EdgeDeviceModel", "DeviceModel") + .WithMany() + .HasForeignKey("DeviceModelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DeviceModel"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.Label", b => + { + b.HasOne("AzureIoTHub.Portal.Domain.Entities.Device", null) + .WithMany("Labels") + .HasForeignKey("DeviceId"); + + b.HasOne("AzureIoTHub.Portal.Domain.Entities.DeviceModel", null) + .WithMany("Labels") + .HasForeignKey("DeviceModelId"); + + b.HasOne("AzureIoTHub.Portal.Domain.Entities.EdgeDevice", null) + .WithMany("Labels") + .HasForeignKey("EdgeDeviceId"); + + b.HasOne("AzureIoTHub.Portal.Domain.Entities.EdgeDeviceModel", null) + .WithMany("Labels") + .HasForeignKey("EdgeDeviceModelId"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.LoRaDeviceTelemetry", b => + { + b.HasOne("AzureIoTHub.Portal.Domain.Entities.LorawanDevice", null) + .WithMany("Telemetry") + .HasForeignKey("LorawanDeviceId"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.LorawanDevice", b => + { + b.HasOne("AzureIoTHub.Portal.Domain.Entities.Device", null) + .WithOne() + .HasForeignKey("AzureIoTHub.Portal.Domain.Entities.LorawanDevice", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.Device", b => + { + b.Navigation("Labels"); + + b.Navigation("Tags"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.DeviceModel", b => + { + b.Navigation("Labels"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.EdgeDevice", b => + { + b.Navigation("Labels"); + + b.Navigation("Tags"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.EdgeDeviceModel", b => + { + b.Navigation("Labels"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.LorawanDevice", b => + { + b.Navigation("Telemetry"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/AzureIoTHub.Portal.Postgres/Migrations/20230525074805_Add Aws DeploymentId.cs b/src/AzureIoTHub.Portal.Postgres/Migrations/20230525074805_Add Aws DeploymentId.cs new file mode 100644 index 000000000..c7b3d7174 --- /dev/null +++ b/src/AzureIoTHub.Portal.Postgres/Migrations/20230525074805_Add Aws DeploymentId.cs @@ -0,0 +1,30 @@ +// Copyright (c) CGI France. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +#nullable disable + +namespace AzureIoTHub.Portal.Postgres.Migrations +{ + using Microsoft.EntityFrameworkCore.Migrations; + + /// + public partial class AddAwsDeploymentId : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + _ = migrationBuilder.AddColumn( + name: "IdProvider", + table: "EdgeDeviceModels", + type: "text", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + _ = migrationBuilder.DropColumn( + name: "IdProvider", + table: "EdgeDeviceModels"); + } + } +} diff --git a/src/AzureIoTHub.Portal.Postgres/Migrations/20230526083144_Change IdProvider to ExternalIdentifier.Designer.cs b/src/AzureIoTHub.Portal.Postgres/Migrations/20230526083144_Change IdProvider to ExternalIdentifier.Designer.cs new file mode 100644 index 000000000..3757b91f6 --- /dev/null +++ b/src/AzureIoTHub.Portal.Postgres/Migrations/20230526083144_Change IdProvider to ExternalIdentifier.Designer.cs @@ -0,0 +1,590 @@ +// +using System; +using AzureIoTHub.Portal.Infrastructure; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace AzureIoTHub.Portal.Postgres.Migrations +{ + [DbContext(typeof(PortalDbContext))] + [Migration("20230526083144_Change IdProvider to ExternalIdentifier")] + partial class ChangeIdProvidertoExternalIdentifier + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.4") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.Concentrator", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ClientThumbprint") + .HasColumnType("text"); + + b.Property("DeviceType") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsConnected") + .HasColumnType("boolean"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("LoraRegion") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("Concentrators"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.Device", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("DeviceModelId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsConnected") + .HasColumnType("boolean"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("StatusUpdatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Version") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DeviceModelId"); + + b.ToTable("Devices", (string)null); + + b.UseTptMappingStrategy(); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.DeviceModel", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ABPRelaxMode") + .HasColumnType("boolean"); + + b.Property("AppEUI") + .HasColumnType("text"); + + b.Property("ClassType") + .HasColumnType("integer"); + + b.Property("Deduplication") + .HasColumnType("integer"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Downlink") + .HasColumnType("boolean"); + + b.Property("IsBuiltin") + .HasColumnType("boolean"); + + b.Property("KeepAliveTimeout") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("PreferredWindow") + .HasColumnType("integer"); + + b.Property("RXDelay") + .HasColumnType("integer"); + + b.Property("SensorDecoder") + .HasColumnType("text"); + + b.Property("SupportLoRaFeatures") + .HasColumnType("boolean"); + + b.Property("UseOTAA") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("DeviceModels"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.DeviceModelCommand", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Confirmed") + .HasColumnType("boolean"); + + b.Property("DeviceModelId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Frame") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsBuiltin") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Port") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("DeviceModelCommands"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.DeviceModelProperty", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("DisplayName") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsWritable") + .HasColumnType("boolean"); + + b.Property("ModelId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("PropertyType") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("DeviceModelProperties"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.DeviceTag", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Label") + .IsRequired() + .HasColumnType("text"); + + b.Property("Required") + .HasColumnType("boolean"); + + b.Property("Searchable") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("DeviceTags"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.DeviceTagValue", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("DeviceId") + .HasColumnType("text"); + + b.Property("EdgeDeviceId") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Value") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DeviceId"); + + b.HasIndex("EdgeDeviceId"); + + b.ToTable("DeviceTagValues"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.EdgeDevice", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ConnectionState") + .IsRequired() + .HasColumnType("text"); + + b.Property("DeviceModelId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("NbDevices") + .HasColumnType("integer"); + + b.Property("NbModules") + .HasColumnType("integer"); + + b.Property("Scope") + .HasColumnType("text"); + + b.Property("Version") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DeviceModelId"); + + b.ToTable("EdgeDevices"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.EdgeDeviceModel", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("ExternalIdentifier") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("EdgeDeviceModels"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.EdgeDeviceModelCommand", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("EdgeDeviceModelId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ModuleName") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("EdgeDeviceModelCommands"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.Label", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Color") + .IsRequired() + .HasColumnType("text"); + + b.Property("DeviceId") + .HasColumnType("text"); + + b.Property("DeviceModelId") + .HasColumnType("text"); + + b.Property("EdgeDeviceId") + .HasColumnType("text"); + + b.Property("EdgeDeviceModelId") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DeviceId"); + + b.HasIndex("DeviceModelId"); + + b.HasIndex("EdgeDeviceId"); + + b.HasIndex("EdgeDeviceModelId"); + + b.ToTable("Labels"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.LoRaDeviceTelemetry", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("EnqueuedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("LorawanDeviceId") + .HasColumnType("text"); + + b.Property("Telemetry") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("LorawanDeviceId"); + + b.ToTable("LoRaDeviceTelemetry"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.LorawanDevice", b => + { + b.HasBaseType("AzureIoTHub.Portal.Domain.Entities.Device"); + + b.Property("ABPRelaxMode") + .HasColumnType("boolean"); + + b.Property("AlreadyLoggedInOnce") + .HasColumnType("boolean"); + + b.Property("AppEUI") + .HasColumnType("text"); + + b.Property("AppKey") + .HasColumnType("text"); + + b.Property("AppSKey") + .HasColumnType("text"); + + b.Property("ClassType") + .HasColumnType("integer"); + + b.Property("DataRate") + .HasColumnType("text"); + + b.Property("Deduplication") + .HasColumnType("integer"); + + b.Property("DevAddr") + .HasColumnType("text"); + + b.Property("Downlink") + .HasColumnType("boolean"); + + b.Property("FCntDownStart") + .HasColumnType("integer"); + + b.Property("FCntResetCounter") + .HasColumnType("integer"); + + b.Property("FCntUpStart") + .HasColumnType("integer"); + + b.Property("GatewayID") + .HasColumnType("text"); + + b.Property("KeepAliveTimeout") + .HasColumnType("integer"); + + b.Property("NbRep") + .HasColumnType("text"); + + b.Property("NwkSKey") + .HasColumnType("text"); + + b.Property("PreferredWindow") + .HasColumnType("integer"); + + b.Property("RX1DROffset") + .HasColumnType("integer"); + + b.Property("RX2DataRate") + .HasColumnType("integer"); + + b.Property("RXDelay") + .HasColumnType("integer"); + + b.Property("ReportedRX1DROffset") + .HasColumnType("text"); + + b.Property("ReportedRX2DataRate") + .HasColumnType("text"); + + b.Property("ReportedRXDelay") + .HasColumnType("text"); + + b.Property("SensorDecoder") + .HasColumnType("text"); + + b.Property("Supports32BitFCnt") + .HasColumnType("boolean"); + + b.Property("TxPower") + .HasColumnType("text"); + + b.Property("UseOTAA") + .HasColumnType("boolean"); + + b.ToTable("LorawanDevices", (string)null); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.Device", b => + { + b.HasOne("AzureIoTHub.Portal.Domain.Entities.DeviceModel", "DeviceModel") + .WithMany() + .HasForeignKey("DeviceModelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DeviceModel"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.DeviceTagValue", b => + { + b.HasOne("AzureIoTHub.Portal.Domain.Entities.Device", null) + .WithMany("Tags") + .HasForeignKey("DeviceId"); + + b.HasOne("AzureIoTHub.Portal.Domain.Entities.EdgeDevice", null) + .WithMany("Tags") + .HasForeignKey("EdgeDeviceId"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.EdgeDevice", b => + { + b.HasOne("AzureIoTHub.Portal.Domain.Entities.EdgeDeviceModel", "DeviceModel") + .WithMany() + .HasForeignKey("DeviceModelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DeviceModel"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.Label", b => + { + b.HasOne("AzureIoTHub.Portal.Domain.Entities.Device", null) + .WithMany("Labels") + .HasForeignKey("DeviceId"); + + b.HasOne("AzureIoTHub.Portal.Domain.Entities.DeviceModel", null) + .WithMany("Labels") + .HasForeignKey("DeviceModelId"); + + b.HasOne("AzureIoTHub.Portal.Domain.Entities.EdgeDevice", null) + .WithMany("Labels") + .HasForeignKey("EdgeDeviceId"); + + b.HasOne("AzureIoTHub.Portal.Domain.Entities.EdgeDeviceModel", null) + .WithMany("Labels") + .HasForeignKey("EdgeDeviceModelId"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.LoRaDeviceTelemetry", b => + { + b.HasOne("AzureIoTHub.Portal.Domain.Entities.LorawanDevice", null) + .WithMany("Telemetry") + .HasForeignKey("LorawanDeviceId"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.LorawanDevice", b => + { + b.HasOne("AzureIoTHub.Portal.Domain.Entities.Device", null) + .WithOne() + .HasForeignKey("AzureIoTHub.Portal.Domain.Entities.LorawanDevice", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.Device", b => + { + b.Navigation("Labels"); + + b.Navigation("Tags"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.DeviceModel", b => + { + b.Navigation("Labels"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.EdgeDevice", b => + { + b.Navigation("Labels"); + + b.Navigation("Tags"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.EdgeDeviceModel", b => + { + b.Navigation("Labels"); + }); + + modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.LorawanDevice", b => + { + b.Navigation("Telemetry"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/AzureIoTHub.Portal.Postgres/Migrations/20230526083144_Change IdProvider to ExternalIdentifier.cs b/src/AzureIoTHub.Portal.Postgres/Migrations/20230526083144_Change IdProvider to ExternalIdentifier.cs new file mode 100644 index 000000000..d0bd13170 --- /dev/null +++ b/src/AzureIoTHub.Portal.Postgres/Migrations/20230526083144_Change IdProvider to ExternalIdentifier.cs @@ -0,0 +1,30 @@ +// Copyright (c) CGI France. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +#nullable disable + +namespace AzureIoTHub.Portal.Postgres.Migrations +{ + using Microsoft.EntityFrameworkCore.Migrations; + + /// + public partial class ChangeIdProvidertoExternalIdentifier : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + _ = migrationBuilder.RenameColumn( + name: "IdProvider", + table: "EdgeDeviceModels", + newName: "ExternalIdentifier"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + _ = migrationBuilder.RenameColumn( + name: "ExternalIdentifier", + table: "EdgeDeviceModels", + newName: "IdProvider"); + } + } +} diff --git a/src/AzureIoTHub.Portal.Postgres/Migrations/PortalDbContextModelSnapshot.cs b/src/AzureIoTHub.Portal.Postgres/Migrations/PortalDbContextModelSnapshot.cs index 2a1d1045c..27a95859e 100644 --- a/src/AzureIoTHub.Portal.Postgres/Migrations/PortalDbContextModelSnapshot.cs +++ b/src/AzureIoTHub.Portal.Postgres/Migrations/PortalDbContextModelSnapshot.cs @@ -1,4 +1,4 @@ -// +// using System; using AzureIoTHub.Portal.Infrastructure; using Microsoft.EntityFrameworkCore; @@ -23,561 +23,564 @@ protected override void BuildModel(ModelBuilder modelBuilder) NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.Concentrator", b => - { - b.Property("Id") - .HasColumnType("text"); + { + b.Property("Id") + .HasColumnType("text"); - b.Property("ClientThumbprint") - .HasColumnType("text"); + b.Property("ClientThumbprint") + .HasColumnType("text"); - b.Property("DeviceType") - .IsRequired() - .HasColumnType("text"); + b.Property("DeviceType") + .IsRequired() + .HasColumnType("text"); - b.Property("IsConnected") - .HasColumnType("boolean"); + b.Property("IsConnected") + .HasColumnType("boolean"); - b.Property("IsEnabled") - .HasColumnType("boolean"); + b.Property("IsEnabled") + .HasColumnType("boolean"); - b.Property("LoraRegion") - .IsRequired() - .HasColumnType("text"); + b.Property("LoraRegion") + .IsRequired() + .HasColumnType("text"); - b.Property("Name") - .IsRequired() - .HasColumnType("text"); + b.Property("Name") + .IsRequired() + .HasColumnType("text"); - b.Property("Version") - .HasColumnType("integer"); + b.Property("Version") + .HasColumnType("integer"); - b.HasKey("Id"); + b.HasKey("Id"); - b.ToTable("Concentrators"); - }); + b.ToTable("Concentrators"); + }); modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.Device", b => - { - b.Property("Id") - .HasColumnType("text"); + { + b.Property("Id") + .HasColumnType("text"); - b.Property("DeviceModelId") - .IsRequired() - .HasColumnType("text"); + b.Property("DeviceModelId") + .IsRequired() + .HasColumnType("text"); - b.Property("IsConnected") - .HasColumnType("boolean"); + b.Property("IsConnected") + .HasColumnType("boolean"); - b.Property("IsEnabled") - .HasColumnType("boolean"); + b.Property("IsEnabled") + .HasColumnType("boolean"); - b.Property("Name") - .IsRequired() - .HasColumnType("text"); + b.Property("Name") + .IsRequired() + .HasColumnType("text"); - b.Property("StatusUpdatedTime") - .HasColumnType("timestamp with time zone"); + b.Property("StatusUpdatedTime") + .HasColumnType("timestamp with time zone"); - b.Property("Version") - .HasColumnType("integer"); + b.Property("Version") + .HasColumnType("integer"); - b.HasKey("Id"); + b.HasKey("Id"); - b.HasIndex("DeviceModelId"); + b.HasIndex("DeviceModelId"); - b.ToTable("Devices", (string)null); + b.ToTable("Devices", (string)null); - b.UseTptMappingStrategy(); - }); + b.UseTptMappingStrategy(); + }); modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.DeviceModel", b => - { - b.Property("Id") - .HasColumnType("text"); + { + b.Property("Id") + .HasColumnType("text"); - b.Property("ABPRelaxMode") - .HasColumnType("boolean"); + b.Property("ABPRelaxMode") + .HasColumnType("boolean"); - b.Property("AppEUI") - .HasColumnType("text"); + b.Property("AppEUI") + .HasColumnType("text"); - b.Property("ClassType") - .HasColumnType("integer"); + b.Property("ClassType") + .HasColumnType("integer"); - b.Property("Deduplication") - .HasColumnType("integer"); + b.Property("Deduplication") + .HasColumnType("integer"); - b.Property("Description") - .HasColumnType("text"); + b.Property("Description") + .HasColumnType("text"); - b.Property("Downlink") - .HasColumnType("boolean"); + b.Property("Downlink") + .HasColumnType("boolean"); - b.Property("IsBuiltin") - .HasColumnType("boolean"); + b.Property("IsBuiltin") + .HasColumnType("boolean"); - b.Property("KeepAliveTimeout") - .HasColumnType("integer"); + b.Property("KeepAliveTimeout") + .HasColumnType("integer"); - b.Property("Name") - .IsRequired() - .HasColumnType("text"); + b.Property("Name") + .IsRequired() + .HasColumnType("text"); - b.Property("PreferredWindow") - .HasColumnType("integer"); + b.Property("PreferredWindow") + .HasColumnType("integer"); - b.Property("RXDelay") - .HasColumnType("integer"); + b.Property("RXDelay") + .HasColumnType("integer"); - b.Property("SensorDecoder") - .HasColumnType("text"); + b.Property("SensorDecoder") + .HasColumnType("text"); - b.Property("SupportLoRaFeatures") - .HasColumnType("boolean"); + b.Property("SupportLoRaFeatures") + .HasColumnType("boolean"); - b.Property("UseOTAA") - .HasColumnType("boolean"); + b.Property("UseOTAA") + .HasColumnType("boolean"); - b.HasKey("Id"); + b.HasKey("Id"); - b.ToTable("DeviceModels"); - }); + b.ToTable("DeviceModels"); + }); modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.DeviceModelCommand", b => - { - b.Property("Id") - .HasColumnType("text"); + { + b.Property("Id") + .HasColumnType("text"); - b.Property("Confirmed") - .HasColumnType("boolean"); + b.Property("Confirmed") + .HasColumnType("boolean"); - b.Property("DeviceModelId") - .IsRequired() - .HasColumnType("text"); + b.Property("DeviceModelId") + .IsRequired() + .HasColumnType("text"); - b.Property("Frame") - .IsRequired() - .HasColumnType("text"); + b.Property("Frame") + .IsRequired() + .HasColumnType("text"); - b.Property("IsBuiltin") - .HasColumnType("boolean"); + b.Property("IsBuiltin") + .HasColumnType("boolean"); - b.Property("Name") - .IsRequired() - .HasColumnType("text"); + b.Property("Name") + .IsRequired() + .HasColumnType("text"); - b.Property("Port") - .HasColumnType("integer"); + b.Property("Port") + .HasColumnType("integer"); - b.HasKey("Id"); + b.HasKey("Id"); - b.ToTable("DeviceModelCommands"); - }); + b.ToTable("DeviceModelCommands"); + }); modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.DeviceModelProperty", b => - { - b.Property("Id") - .HasColumnType("text"); + { + b.Property("Id") + .HasColumnType("text"); - b.Property("DisplayName") - .IsRequired() - .HasColumnType("text"); + b.Property("DisplayName") + .IsRequired() + .HasColumnType("text"); - b.Property("IsWritable") - .HasColumnType("boolean"); + b.Property("IsWritable") + .HasColumnType("boolean"); - b.Property("ModelId") - .IsRequired() - .HasColumnType("text"); + b.Property("ModelId") + .IsRequired() + .HasColumnType("text"); - b.Property("Name") - .IsRequired() - .HasColumnType("text"); + b.Property("Name") + .IsRequired() + .HasColumnType("text"); - b.Property("Order") - .HasColumnType("integer"); + b.Property("Order") + .HasColumnType("integer"); - b.Property("PropertyType") - .HasColumnType("integer"); + b.Property("PropertyType") + .HasColumnType("integer"); - b.HasKey("Id"); + b.HasKey("Id"); - b.ToTable("DeviceModelProperties"); - }); + b.ToTable("DeviceModelProperties"); + }); modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.DeviceTag", b => - { - b.Property("Id") - .HasColumnType("text"); + { + b.Property("Id") + .HasColumnType("text"); - b.Property("Label") - .IsRequired() - .HasColumnType("text"); + b.Property("Label") + .IsRequired() + .HasColumnType("text"); - b.Property("Required") - .HasColumnType("boolean"); + b.Property("Required") + .HasColumnType("boolean"); - b.Property("Searchable") - .HasColumnType("boolean"); + b.Property("Searchable") + .HasColumnType("boolean"); - b.HasKey("Id"); + b.HasKey("Id"); - b.ToTable("DeviceTags"); - }); + b.ToTable("DeviceTags"); + }); modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.DeviceTagValue", b => - { - b.Property("Id") - .HasColumnType("text"); + { + b.Property("Id") + .HasColumnType("text"); - b.Property("DeviceId") - .HasColumnType("text"); + b.Property("DeviceId") + .HasColumnType("text"); - b.Property("EdgeDeviceId") - .HasColumnType("text"); + b.Property("EdgeDeviceId") + .HasColumnType("text"); - b.Property("Name") - .IsRequired() - .HasColumnType("text"); + b.Property("Name") + .IsRequired() + .HasColumnType("text"); - b.Property("Value") - .IsRequired() - .HasColumnType("text"); + b.Property("Value") + .IsRequired() + .HasColumnType("text"); - b.HasKey("Id"); + b.HasKey("Id"); - b.HasIndex("DeviceId"); + b.HasIndex("DeviceId"); - b.HasIndex("EdgeDeviceId"); + b.HasIndex("EdgeDeviceId"); - b.ToTable("DeviceTagValues"); - }); + b.ToTable("DeviceTagValues"); + }); modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.EdgeDevice", b => - { - b.Property("Id") - .HasColumnType("text"); + { + b.Property("Id") + .HasColumnType("text"); - b.Property("ConnectionState") - .IsRequired() - .HasColumnType("text"); + b.Property("ConnectionState") + .IsRequired() + .HasColumnType("text"); - b.Property("DeviceModelId") - .IsRequired() - .HasColumnType("text"); + b.Property("DeviceModelId") + .IsRequired() + .HasColumnType("text"); - b.Property("IsEnabled") - .HasColumnType("boolean"); + b.Property("IsEnabled") + .HasColumnType("boolean"); - b.Property("Name") - .IsRequired() - .HasColumnType("text"); + b.Property("Name") + .IsRequired() + .HasColumnType("text"); - b.Property("NbDevices") - .HasColumnType("integer"); + b.Property("NbDevices") + .HasColumnType("integer"); - b.Property("NbModules") - .HasColumnType("integer"); + b.Property("NbModules") + .HasColumnType("integer"); - b.Property("Scope") - .HasColumnType("text"); + b.Property("Scope") + .HasColumnType("text"); - b.Property("Version") - .HasColumnType("integer"); + b.Property("Version") + .HasColumnType("integer"); - b.HasKey("Id"); + b.HasKey("Id"); - b.HasIndex("DeviceModelId"); + b.HasIndex("DeviceModelId"); - b.ToTable("EdgeDevices"); - }); + b.ToTable("EdgeDevices"); + }); modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.EdgeDeviceModel", b => - { - b.Property("Id") - .HasColumnType("text"); + { + b.Property("Id") + .HasColumnType("text"); - b.Property("Description") - .HasColumnType("text"); + b.Property("Description") + .HasColumnType("text"); - b.Property("Name") - .IsRequired() - .HasColumnType("text"); + b.Property("ExternalIdentifier") + .HasColumnType("text"); - b.HasKey("Id"); + b.Property("Name") + .IsRequired() + .HasColumnType("text"); - b.ToTable("EdgeDeviceModels"); - }); + b.HasKey("Id"); + + b.ToTable("EdgeDeviceModels"); + }); modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.EdgeDeviceModelCommand", b => - { - b.Property("Id") - .HasColumnType("text"); + { + b.Property("Id") + .HasColumnType("text"); - b.Property("EdgeDeviceModelId") - .IsRequired() - .HasColumnType("text"); + b.Property("EdgeDeviceModelId") + .IsRequired() + .HasColumnType("text"); - b.Property("ModuleName") - .IsRequired() - .HasColumnType("text"); + b.Property("ModuleName") + .IsRequired() + .HasColumnType("text"); - b.Property("Name") - .IsRequired() - .HasColumnType("text"); + b.Property("Name") + .IsRequired() + .HasColumnType("text"); - b.HasKey("Id"); + b.HasKey("Id"); - b.ToTable("EdgeDeviceModelCommands"); - }); + b.ToTable("EdgeDeviceModelCommands"); + }); modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.Label", b => - { - b.Property("Id") - .HasColumnType("text"); + { + b.Property("Id") + .HasColumnType("text"); - b.Property("Color") - .IsRequired() - .HasColumnType("text"); + b.Property("Color") + .IsRequired() + .HasColumnType("text"); - b.Property("DeviceId") - .HasColumnType("text"); + b.Property("DeviceId") + .HasColumnType("text"); - b.Property("DeviceModelId") - .HasColumnType("text"); + b.Property("DeviceModelId") + .HasColumnType("text"); - b.Property("EdgeDeviceId") - .HasColumnType("text"); + b.Property("EdgeDeviceId") + .HasColumnType("text"); - b.Property("EdgeDeviceModelId") - .HasColumnType("text"); + b.Property("EdgeDeviceModelId") + .HasColumnType("text"); - b.Property("Name") - .IsRequired() - .HasColumnType("text"); + b.Property("Name") + .IsRequired() + .HasColumnType("text"); - b.HasKey("Id"); + b.HasKey("Id"); - b.HasIndex("DeviceId"); + b.HasIndex("DeviceId"); - b.HasIndex("DeviceModelId"); + b.HasIndex("DeviceModelId"); - b.HasIndex("EdgeDeviceId"); + b.HasIndex("EdgeDeviceId"); - b.HasIndex("EdgeDeviceModelId"); + b.HasIndex("EdgeDeviceModelId"); - b.ToTable("Labels"); - }); + b.ToTable("Labels"); + }); modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.LoRaDeviceTelemetry", b => - { - b.Property("Id") - .HasColumnType("text"); + { + b.Property("Id") + .HasColumnType("text"); - b.Property("EnqueuedTime") - .HasColumnType("timestamp with time zone"); + b.Property("EnqueuedTime") + .HasColumnType("timestamp with time zone"); - b.Property("LorawanDeviceId") - .HasColumnType("text"); + b.Property("LorawanDeviceId") + .HasColumnType("text"); - b.Property("Telemetry") - .IsRequired() - .HasColumnType("text"); + b.Property("Telemetry") + .IsRequired() + .HasColumnType("text"); - b.HasKey("Id"); + b.HasKey("Id"); - b.HasIndex("LorawanDeviceId"); + b.HasIndex("LorawanDeviceId"); - b.ToTable("LoRaDeviceTelemetry"); - }); + b.ToTable("LoRaDeviceTelemetry"); + }); modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.LorawanDevice", b => - { - b.HasBaseType("AzureIoTHub.Portal.Domain.Entities.Device"); + { + b.HasBaseType("AzureIoTHub.Portal.Domain.Entities.Device"); - b.Property("ABPRelaxMode") - .HasColumnType("boolean"); + b.Property("ABPRelaxMode") + .HasColumnType("boolean"); - b.Property("AlreadyLoggedInOnce") - .HasColumnType("boolean"); + b.Property("AlreadyLoggedInOnce") + .HasColumnType("boolean"); - b.Property("AppEUI") - .HasColumnType("text"); + b.Property("AppEUI") + .HasColumnType("text"); - b.Property("AppKey") - .HasColumnType("text"); + b.Property("AppKey") + .HasColumnType("text"); - b.Property("AppSKey") - .HasColumnType("text"); + b.Property("AppSKey") + .HasColumnType("text"); - b.Property("ClassType") - .HasColumnType("integer"); + b.Property("ClassType") + .HasColumnType("integer"); - b.Property("DataRate") - .HasColumnType("text"); + b.Property("DataRate") + .HasColumnType("text"); - b.Property("Deduplication") - .HasColumnType("integer"); + b.Property("Deduplication") + .HasColumnType("integer"); - b.Property("DevAddr") - .HasColumnType("text"); + b.Property("DevAddr") + .HasColumnType("text"); - b.Property("Downlink") - .HasColumnType("boolean"); + b.Property("Downlink") + .HasColumnType("boolean"); - b.Property("FCntDownStart") - .HasColumnType("integer"); + b.Property("FCntDownStart") + .HasColumnType("integer"); - b.Property("FCntResetCounter") - .HasColumnType("integer"); + b.Property("FCntResetCounter") + .HasColumnType("integer"); - b.Property("FCntUpStart") - .HasColumnType("integer"); + b.Property("FCntUpStart") + .HasColumnType("integer"); - b.Property("GatewayID") - .HasColumnType("text"); + b.Property("GatewayID") + .HasColumnType("text"); - b.Property("KeepAliveTimeout") - .HasColumnType("integer"); + b.Property("KeepAliveTimeout") + .HasColumnType("integer"); - b.Property("NbRep") - .HasColumnType("text"); + b.Property("NbRep") + .HasColumnType("text"); - b.Property("NwkSKey") - .HasColumnType("text"); + b.Property("NwkSKey") + .HasColumnType("text"); - b.Property("PreferredWindow") - .HasColumnType("integer"); + b.Property("PreferredWindow") + .HasColumnType("integer"); - b.Property("RX1DROffset") - .HasColumnType("integer"); + b.Property("RX1DROffset") + .HasColumnType("integer"); - b.Property("RX2DataRate") - .HasColumnType("integer"); + b.Property("RX2DataRate") + .HasColumnType("integer"); - b.Property("RXDelay") - .HasColumnType("integer"); + b.Property("RXDelay") + .HasColumnType("integer"); - b.Property("ReportedRX1DROffset") - .HasColumnType("text"); + b.Property("ReportedRX1DROffset") + .HasColumnType("text"); - b.Property("ReportedRX2DataRate") - .HasColumnType("text"); + b.Property("ReportedRX2DataRate") + .HasColumnType("text"); - b.Property("ReportedRXDelay") - .HasColumnType("text"); + b.Property("ReportedRXDelay") + .HasColumnType("text"); - b.Property("SensorDecoder") - .HasColumnType("text"); + b.Property("SensorDecoder") + .HasColumnType("text"); - b.Property("Supports32BitFCnt") - .HasColumnType("boolean"); + b.Property("Supports32BitFCnt") + .HasColumnType("boolean"); - b.Property("TxPower") - .HasColumnType("text"); + b.Property("TxPower") + .HasColumnType("text"); - b.Property("UseOTAA") - .HasColumnType("boolean"); + b.Property("UseOTAA") + .HasColumnType("boolean"); - b.ToTable("LorawanDevices", (string)null); - }); + b.ToTable("LorawanDevices", (string)null); + }); modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.Device", b => - { - b.HasOne("AzureIoTHub.Portal.Domain.Entities.DeviceModel", "DeviceModel") - .WithMany() - .HasForeignKey("DeviceModelId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); + { + b.HasOne("AzureIoTHub.Portal.Domain.Entities.DeviceModel", "DeviceModel") + .WithMany() + .HasForeignKey("DeviceModelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); - b.Navigation("DeviceModel"); - }); + b.Navigation("DeviceModel"); + }); modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.DeviceTagValue", b => - { - b.HasOne("AzureIoTHub.Portal.Domain.Entities.Device", null) - .WithMany("Tags") - .HasForeignKey("DeviceId"); + { + b.HasOne("AzureIoTHub.Portal.Domain.Entities.Device", null) + .WithMany("Tags") + .HasForeignKey("DeviceId"); - b.HasOne("AzureIoTHub.Portal.Domain.Entities.EdgeDevice", null) - .WithMany("Tags") - .HasForeignKey("EdgeDeviceId"); - }); + b.HasOne("AzureIoTHub.Portal.Domain.Entities.EdgeDevice", null) + .WithMany("Tags") + .HasForeignKey("EdgeDeviceId"); + }); modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.EdgeDevice", b => - { - b.HasOne("AzureIoTHub.Portal.Domain.Entities.EdgeDeviceModel", "DeviceModel") - .WithMany() - .HasForeignKey("DeviceModelId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); + { + b.HasOne("AzureIoTHub.Portal.Domain.Entities.EdgeDeviceModel", "DeviceModel") + .WithMany() + .HasForeignKey("DeviceModelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); - b.Navigation("DeviceModel"); - }); + b.Navigation("DeviceModel"); + }); modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.Label", b => - { - b.HasOne("AzureIoTHub.Portal.Domain.Entities.Device", null) - .WithMany("Labels") - .HasForeignKey("DeviceId"); + { + b.HasOne("AzureIoTHub.Portal.Domain.Entities.Device", null) + .WithMany("Labels") + .HasForeignKey("DeviceId"); - b.HasOne("AzureIoTHub.Portal.Domain.Entities.DeviceModel", null) - .WithMany("Labels") - .HasForeignKey("DeviceModelId"); + b.HasOne("AzureIoTHub.Portal.Domain.Entities.DeviceModel", null) + .WithMany("Labels") + .HasForeignKey("DeviceModelId"); - b.HasOne("AzureIoTHub.Portal.Domain.Entities.EdgeDevice", null) - .WithMany("Labels") - .HasForeignKey("EdgeDeviceId"); + b.HasOne("AzureIoTHub.Portal.Domain.Entities.EdgeDevice", null) + .WithMany("Labels") + .HasForeignKey("EdgeDeviceId"); - b.HasOne("AzureIoTHub.Portal.Domain.Entities.EdgeDeviceModel", null) - .WithMany("Labels") - .HasForeignKey("EdgeDeviceModelId"); - }); + b.HasOne("AzureIoTHub.Portal.Domain.Entities.EdgeDeviceModel", null) + .WithMany("Labels") + .HasForeignKey("EdgeDeviceModelId"); + }); modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.LoRaDeviceTelemetry", b => - { - b.HasOne("AzureIoTHub.Portal.Domain.Entities.LorawanDevice", null) - .WithMany("Telemetry") - .HasForeignKey("LorawanDeviceId"); - }); + { + b.HasOne("AzureIoTHub.Portal.Domain.Entities.LorawanDevice", null) + .WithMany("Telemetry") + .HasForeignKey("LorawanDeviceId"); + }); modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.LorawanDevice", b => - { - b.HasOne("AzureIoTHub.Portal.Domain.Entities.Device", null) - .WithOne() - .HasForeignKey("AzureIoTHub.Portal.Domain.Entities.LorawanDevice", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); + { + b.HasOne("AzureIoTHub.Portal.Domain.Entities.Device", null) + .WithOne() + .HasForeignKey("AzureIoTHub.Portal.Domain.Entities.LorawanDevice", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.Device", b => - { - b.Navigation("Labels"); + { + b.Navigation("Labels"); - b.Navigation("Tags"); - }); + b.Navigation("Tags"); + }); modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.DeviceModel", b => - { - b.Navigation("Labels"); - }); + { + b.Navigation("Labels"); + }); modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.EdgeDevice", b => - { - b.Navigation("Labels"); + { + b.Navigation("Labels"); - b.Navigation("Tags"); - }); + b.Navigation("Tags"); + }); modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.EdgeDeviceModel", b => - { - b.Navigation("Labels"); - }); + { + b.Navigation("Labels"); + }); modelBuilder.Entity("AzureIoTHub.Portal.Domain.Entities.LorawanDevice", b => - { - b.Navigation("Telemetry"); - }); + { + b.Navigation("Telemetry"); + }); #pragma warning restore 612, 618 } } diff --git a/src/AzureIoTHub.Portal.Shared/Models/v1.0/IoTEdgeModelListItem.cs b/src/AzureIoTHub.Portal.Shared/Models/v1.0/IoTEdgeModelListItem.cs index 5f88b2d47..43c5620be 100644 --- a/src/AzureIoTHub.Portal.Shared/Models/v1.0/IoTEdgeModelListItem.cs +++ b/src/AzureIoTHub.Portal.Shared/Models/v1.0/IoTEdgeModelListItem.cs @@ -28,6 +28,12 @@ public class IoTEdgeModelListItem public Uri ImageUrl { get; set; } = default!; + /// + /// The aws deployment ID. + /// + public string ExternalIdentifier { get; set; } = default!; + + /// /// Gets the edge model labels. /// diff --git a/src/AzureIoTHub.Portal.Shared/Models/v1.0/IoTEdgeModule.cs b/src/AzureIoTHub.Portal.Shared/Models/v1.0/IoTEdgeModule.cs index 41222c7fe..643b710f5 100644 --- a/src/AzureIoTHub.Portal.Shared/Models/v1.0/IoTEdgeModule.cs +++ b/src/AzureIoTHub.Portal.Shared/Models/v1.0/IoTEdgeModule.cs @@ -45,5 +45,8 @@ public class IoTEdgeModule /// The module commands. /// public List Commands { get; set; } = new List(); + + [Required(ErrorMessage = "The component version is required.", AllowEmptyStrings = true)] + public string Version { get; set; } = default!; } } 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..8ca91684a 100644 --- a/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/EdgeModels/EdgeModelDetailPageTest.cs +++ b/src/AzureIoTHub.Portal.Tests.Unit/Client/Pages/EdgeModels/EdgeModelDetailPageTest.cs @@ -44,6 +44,8 @@ public override void Setup() _ = Services.AddSingleton(this.mockEdgeModelService.Object); _ = Services.AddSingleton(this.mockDialogService.Object); _ = Services.AddSingleton(this.mockSnackbarService.Object); + _ = Services.AddSingleton(new PortalSettings { CloudProvider = "Azure" }); + this.mockNavigationManager = Services.GetRequiredService(); } @@ -52,6 +54,7 @@ public override void Setup() public void ClickOnReturnButtonMustNavigateToPreviousPage() { // Arrange + _ = SetupLoadEdgeModel(); var cut = RenderComponent(ComponentParameter.CreateParameter("ModelID", this.mockEdgeModleId)); @@ -68,6 +71,7 @@ public void ClickOnReturnButtonMustNavigateToPreviousPage() public void ClickOnSaveChangesShouldUpdateTheData() { // Arrange + var edgeModel = SetupLoadEdgeModel(); _ = this.mockEdgeModelService @@ -91,6 +95,7 @@ public void ClickOnSaveChangesShouldUpdateTheData() public void WhenModuleRequiredFieldEmptyClickOnSaveShouldProcessValidationError() { // Arrange + var edgeModel = SetupLoadEdgeModel(); _ = this.mockSnackbarService @@ -118,6 +123,7 @@ public void WhenModuleRequiredFieldEmptyClickOnSaveShouldProcessValidationError( public void WhenRoutesRequiredFieldEmptyClickOnSaveShouldProcessValidationError() { // Arrange + var edgeModel = SetupLoadEdgeModel(); _ = this.mockSnackbarService @@ -164,6 +170,7 @@ public void WhenClickOnSaveChangesShouldProssessProblem() public void ClickOnAddModuleShouldAddModuleOnEdgeModelData() { // Arrange + _ = SetupLoadEdgeModel(); // Act @@ -201,6 +208,7 @@ public void ClickOnDeleteModuleShouldRemoveModule() public void ClickOnDeleteEdgeModelButtonShouldShowDeleteDialogAndRedirectIfOk() { // Arrange + _ = SetupLoadEdgeModel(); var mockDialogReference = MockRepository.Create(); @@ -225,6 +233,7 @@ public void ClickOnDeleteEdgeModelButtonShouldShowDeleteDialogAndRedirectIfOk() public void ClickOnDeleteEdgeModelButtonShouldShowDeleteDialogAndReturnIfAborted() { // Arrange + _ = SetupLoadEdgeModel(); var mockDialogReference = MockRepository.Create(); @@ -248,6 +257,7 @@ public void ClickOnDeleteEdgeModelButtonShouldShowDeleteDialogAndReturnIfAborted public void ClickOnShowEditEdgeModuleDialogShouldShowDialog() { // Arrange + _ = SetupLoadEdgeModel(); var mockDialogReference = MockRepository.Create(); @@ -274,6 +284,7 @@ public void ClickOnShowEditEdgeModuleDialogShouldShowDialog() public void ClickShowEditEdgeModuleDialogShouldDisplayEditModuleDialogAndReturnIfAborted() { // Arrange + _ = SetupLoadEdgeModel(); var mockDialogReference = MockRepository.Create(); @@ -300,6 +311,7 @@ public void ClickShowEditEdgeModuleDialogShouldDisplayEditModuleDialogAndReturnI public void ClickOnShowSystemModuleDetailShouldShowDialog() { // Arrange + _ = SetupLoadEdgeModel(); var mockDialogReference = MockRepository.Create(); @@ -327,6 +339,7 @@ public void ClickOnShowSystemModuleDetailShouldShowDialog() public void ClickOnShowSystemModuleDetailShouldShowDialogAndReturnIfAborted() { // Arrange + _ = SetupLoadEdgeModel(); var mockDialogReference = MockRepository.Create(); @@ -354,6 +367,7 @@ public void ClickOnShowSystemModuleDetailShouldShowDialogAndReturnIfAborted() public void DeleteAvatarShouldRemoveTheImage() { // Arrange + _ = SetupLoadEdgeModel(); // Act @@ -384,10 +398,12 @@ public IoTEdgeModel SetupLoadEdgeModel() new IoTEdgeModule() { ModuleName = "module_Test", - ImageURI = "image_test" + ImageURI = "image_test", + Version = "1.0.1" } }, - ImageUrl = new Uri($"http://fake.local/{this.mockEdgeModleId}") + ImageUrl = new Uri($"http://fake.local/{this.mockEdgeModleId}"), + }; _ = this.mockEdgeModelService @@ -405,6 +421,7 @@ public IoTEdgeModel SetupLoadEdgeModel() public void ClickOnAddRouteShouldAddRouteOnEdgeModelData() { // Arrange + _ = SetupLoadEdgeModel(); // Act @@ -422,6 +439,7 @@ public void ClickOnAddRouteShouldAddRouteOnEdgeModelData() public void ClickOnDeleteRouteShouldRemoveRouteFromEdgeModelData() { // Arrange + _ = SetupLoadEdgeModel(); // Act diff --git a/src/AzureIoTHub.Portal.Tests.Unit/Infrastructure/Services/AWS_Tests/AwsConfigTests.cs b/src/AzureIoTHub.Portal.Tests.Unit/Infrastructure/Services/AWS_Tests/AwsConfigTests.cs index 2c7fed7b6..ce13873fa 100644 --- a/src/AzureIoTHub.Portal.Tests.Unit/Infrastructure/Services/AWS_Tests/AwsConfigTests.cs +++ b/src/AzureIoTHub.Portal.Tests.Unit/Infrastructure/Services/AWS_Tests/AwsConfigTests.cs @@ -22,6 +22,11 @@ namespace AzureIoTHub.Portal.Tests.Unit.Infrastructure.Services.AWS_Tests using System.Threading.Tasks; using Amazon.IoT; using Amazon.IoT.Model; + using AzureIoTHub.Portal.Domain.Entities; + using System.Collections.Generic; + using System.IO; + using System.Text; + using Newtonsoft.Json.Linq; [TestFixture] public class AwsConfigTests : BackendUnitTest @@ -31,6 +36,8 @@ public class AwsConfigTests : BackendUnitTest private Mock mocDeviceModelImageManager; private Mock mockEdgeModelRepository; private Mock mockUnitOfWork; + private Mock mockConfigHandler; + private IConfigService awsConfigService; @@ -43,6 +50,8 @@ public void SetUp() this.mockGreengrasClient = MockRepository.Create(); this.mockIotClient = MockRepository.Create(); this.mocDeviceModelImageManager = MockRepository.Create(); + this.mockConfigHandler = MockRepository.Create(); + _ = ServiceCollection.AddSingleton(this.mockEdgeModelRepository.Object); _ = ServiceCollection.AddSingleton(this.mockGreengrasClient.Object); @@ -50,6 +59,8 @@ public void SetUp() _ = ServiceCollection.AddSingleton(this.mockUnitOfWork.Object); _ = ServiceCollection.AddSingleton(this.mocDeviceModelImageManager.Object); _ = ServiceCollection.AddSingleton(); + _ = ServiceCollection.AddSingleton(this.mockConfigHandler.Object); + Services = ServiceCollection.BuildServiceProvider(); @@ -61,7 +72,9 @@ public void SetUp() public async Task CreateDeploymentWithComponentsAndExistingThingGroupAndThingTypeShouldCreateTheDeployment() { //Act + var edge = Fixture.Create(); + var edgeDeviceModelEntity = Mapper.Map(edge); _ = this.mockIotClient.Setup(s3 => s3.DescribeThingGroupAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new DescribeThingGroupResponse @@ -83,6 +96,13 @@ public async Task CreateDeploymentWithComponentsAndExistingThingGroupAndThingTyp HttpStatusCode = HttpStatusCode.Created }); + _ = this.mockEdgeModelRepository.Setup(x => x.GetByIdAsync(It.IsAny())) + .ReturnsAsync(edgeDeviceModelEntity); + + _ = this.mockEdgeModelRepository.Setup(repository => repository.Update(It.IsAny())); + _ = this.mockUnitOfWork.Setup(work => work.SaveAsync()) + .Returns(Task.CompletedTask); + //Arrange await this.awsConfigService.RollOutEdgeModelConfiguration(edge); @@ -96,6 +116,7 @@ public async Task CreateDeploymentWithComponentsAndNonExistingThingGroupAndThing { //Act var edge = Fixture.Create(); + var edgeDeviceModelEntity = Mapper.Map(edge); _ = this.mockIotClient.Setup(s3 => s3.DescribeThingGroupAsync(It.IsAny(), It.IsAny())) .ThrowsAsync(new Amazon.IoT.Model.ResourceNotFoundException("Resource Not found")); @@ -120,13 +141,108 @@ public async Task CreateDeploymentWithComponentsAndNonExistingThingGroupAndThing HttpStatusCode = HttpStatusCode.Created }); + _ = this.mockEdgeModelRepository.Setup(x => x.GetByIdAsync(It.IsAny())) + .ReturnsAsync(edgeDeviceModelEntity); + + _ = this.mockEdgeModelRepository.Setup(repository => repository.Update(It.IsAny())); + _ = this.mockUnitOfWork.Setup(work => work.SaveAsync()) + .Returns(Task.CompletedTask); + //Arrange await this.awsConfigService.RollOutEdgeModelConfiguration(edge); //Assert MockRepository.VerifyAll(); + } + + [Test] + public async Task GetAllDeploymentComponentsShouldRetreiveImageUriAndEnvironmentVariables() + { + //Act + var edge = Fixture.Create(); + using var recipeAsMemoryStream = new MemoryStream(Encoding.UTF8.GetBytes(Fixture.Create().ToString())); + + _ = this.mockConfigHandler.Setup(handler => handler.AWSRegion).Returns("eu-west-1"); + _ = this.mockConfigHandler.Setup(handler => handler.AWSAccountId).Returns("00000000"); + + _ = this.mockGreengrasClient.Setup(s3 => s3.GetDeploymentAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new GetDeploymentResponse + { + HttpStatusCode = HttpStatusCode.OK, + Components = new Dictionary + { + {"test", new ComponentDeploymentSpecification()} + + } + }); + + _ = this.mockGreengrasClient.Setup(s3 => s3.GetComponentAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new GetComponentResponse + { + HttpStatusCode = HttpStatusCode.OK, + Recipe = recipeAsMemoryStream, + RecipeOutputFormat = RecipeOutputFormat.JSON + }); + + //Arrange + _ = await this.awsConfigService.GetConfigModuleList(edge.ExternalIdentifier); + + //Assert + MockRepository.VerifyAll(); + } + + [Test] + public async Task DeleteDeploymentShouldDeleteTheDeploymentVersionAndAllItsComponentsVersions() + { + //Act + var edge = Fixture.Create(); + using var recipeAsMemoryStream = new MemoryStream(Encoding.UTF8.GetBytes(Fixture.Create().ToString())); + + _ = this.mockConfigHandler.Setup(handler => handler.AWSRegion).Returns("eu-west-1"); + _ = this.mockConfigHandler.Setup(handler => handler.AWSAccountId).Returns("00000000"); + + _ = this.mockGreengrasClient.Setup(s3 => s3.GetDeploymentAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new GetDeploymentResponse + { + HttpStatusCode = HttpStatusCode.OK, + Components = new Dictionary + { + {"test", new ComponentDeploymentSpecification()} + + } + }); + + _ = this.mockGreengrasClient.Setup(s3 => s3.GetComponentAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new GetComponentResponse + { + HttpStatusCode = HttpStatusCode.OK, + Recipe = recipeAsMemoryStream, + RecipeOutputFormat = RecipeOutputFormat.JSON + }); + _ = this.mockGreengrasClient.Setup(s3 => s3.DeleteComponentAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new DeleteComponentResponse + { + HttpStatusCode = HttpStatusCode.NoContent + }); + + _ = this.mockGreengrasClient.Setup(s3 => s3.CancelDeploymentAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new CancelDeploymentResponse + { + HttpStatusCode = HttpStatusCode.OK + }); + + _ = this.mockGreengrasClient.Setup(s3 => s3.DeleteDeploymentAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new DeleteDeploymentResponse + { + HttpStatusCode = HttpStatusCode.NoContent + }); + //Arrange + await this.awsConfigService.DeleteConfiguration(edge.ExternalIdentifier); + + //Assert + MockRepository.VerifyAll(); } } diff --git a/src/AzureIoTHub.Portal.Tests.Unit/Server/Services/EdgeModelServiceTest.cs b/src/AzureIoTHub.Portal.Tests.Unit/Server/Services/EdgeModelServiceTest.cs index ccc884f8d..6857ad796 100644 --- a/src/AzureIoTHub.Portal.Tests.Unit/Server/Services/EdgeModelServiceTest.cs +++ b/src/AzureIoTHub.Portal.Tests.Unit/Server/Services/EdgeModelServiceTest.cs @@ -171,6 +171,59 @@ public async Task GetEdgeModelShouldReturnValueAsync() _ = result.Should().BeEquivalentTo(expectedEdgeDeviceModel); } + [Test] + public async Task GetEdgeModelForAwsShouldReturnValueAsync() + { + // Arrange + _ = this.mockConfigHandler.Setup(handler => handler.CloudProvider).Returns("AWS"); + + var expectedModules = Fixture.CreateMany(2).ToList(); + + var expectedImageUri = Fixture.Create(); + + var expectedEdgeDeviceModel = new IoTEdgeModel() + { + ModelId = Guid.NewGuid().ToString(), + Name = Guid.NewGuid().ToString(), + ImageUrl = expectedImageUri, + Description = Guid.NewGuid().ToString(), + EdgeModules = expectedModules + }; + + var expectedCommands = Fixture.CreateMany(5).Select(command => + { + command.EdgeDeviceModelId = expectedEdgeDeviceModel.ModelId; + command.ModuleName = expectedModules[0].ModuleName; + return command; + }) .ToList(); + + expectedCommands.Add(new EdgeDeviceModelCommand + { + EdgeDeviceModelId = expectedEdgeDeviceModel.ModelId, + Id = Guid.NewGuid().ToString(), + Name = Guid.NewGuid().ToString(), + ModuleName = Guid.NewGuid().ToString() + }); + + var expectedEdgeDeviceModelEntity = Mapper.Map(expectedEdgeDeviceModel); + + _ = this.mockDeviceModelImageManager.Setup(c => c.ComputeImageUri(It.IsAny())) + .Returns(expectedImageUri); + + _ = this.mockEdgeDeviceModelRepository.Setup(x => x.GetByIdAsync(It.IsAny(), d => d.Labels)) + .ReturnsAsync(expectedEdgeDeviceModelEntity); + + _ = this.mockConfigService.Setup(x => x.GetConfigModuleList(It.IsAny())) + .ReturnsAsync(expectedModules); + + // Act + var result = await this.edgeDeviceModelService.GetEdgeModel(expectedEdgeDeviceModel.ModelId); + + // Assert + Assert.IsNotNull(result); + _ = result.Should().BeEquivalentTo(expectedEdgeDeviceModel); + } + [Test] public void GetEdgeModelShouldThrowResourceNotFoundExceptionIfModelDoesNotExist() { @@ -299,6 +352,8 @@ public void CreateEdgeModelShouldThrowInternalServerErrorExceptionIfDbUpdateExce public async Task UpdateEdgeModelShouldUpdateEdgeModel() { // Arrange + _ = this.mockConfigHandler.Setup(handler => handler.CloudProvider).Returns("Azure"); + var edgeDeviceModel = Fixture.Create(); var edgeDeviceModelEntity = Mapper.Map(edgeDeviceModel); @@ -333,6 +388,37 @@ public async Task UpdateEdgeModelShouldUpdateEdgeModel() MockRepository.VerifyAll(); } + + [Test] + public async Task UpdateEdgeModelForAWSShouldUpdateEdgeModel() + { + // Arrange + _ = this.mockConfigHandler.Setup(handler => handler.CloudProvider).Returns("AWS"); + + var edgeDeviceModel = Fixture.Create(); + var edgeDeviceModelEntity = Mapper.Map(edgeDeviceModel); + + _ = this.mockEdgeDeviceModelRepository.Setup(x => x.GetByIdAsync(It.IsAny(), d => d.Labels)) + .ReturnsAsync(edgeDeviceModelEntity); + + this.mockLabelRepository.Setup(repository => repository.Delete(It.IsAny())) + .Verifiable(); + + _ = this.mockEdgeDeviceModelRepository.Setup(repository => repository.Update(It.IsAny())); + _ = this.mockUnitOfWork.Setup(work => work.SaveAsync()) + .Returns(Task.CompletedTask); + + + _ = this.mockConfigService.Setup(x => x.RollOutEdgeModelConfiguration(It.IsAny())) + .Returns(Task.CompletedTask); + + // Act + await this.edgeDeviceModelService.UpdateEdgeModel(edgeDeviceModel); + + // Assert + MockRepository.VerifyAll(); + } + [Test] public void UpdateEdgeModelShouldThrowResourceNotFoundExceptionIfModelDoesNotExist() { @@ -373,6 +459,8 @@ public void UpdateEdgeModelShouldThrowInternalServerErrorExceptionIfDbUpdateExce public async Task DeleteEdgeModelShouldDeleteEdgeModel() { // Arrange + _ = this.mockConfigHandler.Setup(handler => handler.CloudProvider).Returns("Azure"); + var edgeDeviceModel = Fixture.Create(); var edgeDeviceModelEntity = Mapper.Map(edgeDeviceModel); @@ -411,6 +499,35 @@ public async Task DeleteEdgeModelShouldDeleteEdgeModel() MockRepository.VerifyAll(); } + [Test] + public async Task DeleteEdgeModelForAwsShouldDeleteEdgeModel() + { + // Arrange + _ = this.mockConfigHandler.Setup(handler => handler.CloudProvider).Returns("AWS"); + + var edgeDeviceModel = Fixture.Create(); + var edgeDeviceModelEntity = Mapper.Map(edgeDeviceModel); + + _ = this.mockEdgeDeviceModelRepository.Setup(repository => repository.GetByIdAsync(edgeDeviceModelEntity.Id, d => d.Labels)) + .ReturnsAsync(edgeDeviceModelEntity); + + this.mockLabelRepository.Setup(repository => repository.Delete(It.IsAny())) + .Verifiable(); + + _ = this.mockEdgeDeviceModelRepository.Setup(repository => repository.Delete(It.IsAny())); + _ = this.mockUnitOfWork.Setup(work => work.SaveAsync()) + .Returns(Task.CompletedTask); + + _ = this.mockConfigService.Setup(x => x.DeleteConfiguration(It.IsAny())) + .Returns(Task.CompletedTask); + + // Act + await this.edgeDeviceModelService.DeleteEdgeModel(edgeDeviceModel.ModelId); + + // Assert + MockRepository.VerifyAll(); + } + [Test] public async Task DeleteEdgeModel_ModelDoesntExist_NothingIsDone() {