diff --git a/src/AzureIoTHub.Portal.Application/Managers/IDeviceModelImageManager.cs b/src/AzureIoTHub.Portal.Application/Managers/IDeviceModelImageManager.cs index e386521a3..0cd808c01 100644 --- a/src/AzureIoTHub.Portal.Application/Managers/IDeviceModelImageManager.cs +++ b/src/AzureIoTHub.Portal.Application/Managers/IDeviceModelImageManager.cs @@ -18,5 +18,7 @@ public interface IDeviceModelImageManager Task InitializeDefaultImageBlob(); Task SyncImagesCacheControl(); + + Task SetDefaultImageToModel(string deviceModelId); } } diff --git a/src/AzureIoTHub.Portal.Infrastructure/AzureIoTHub.Portal.Infrastructure.csproj b/src/AzureIoTHub.Portal.Infrastructure/AzureIoTHub.Portal.Infrastructure.csproj index 562324a4a..309355960 100644 --- a/src/AzureIoTHub.Portal.Infrastructure/AzureIoTHub.Portal.Infrastructure.csproj +++ b/src/AzureIoTHub.Portal.Infrastructure/AzureIoTHub.Portal.Infrastructure.csproj @@ -1,4 +1,4 @@ - + net7.0 diff --git a/src/AzureIoTHub.Portal.Infrastructure/Managers/DeviceModelImageManager.cs b/src/AzureIoTHub.Portal.Infrastructure/Managers/DeviceModelImageManager.cs index 7c29b751b..00ef9f75f 100644 --- a/src/AzureIoTHub.Portal.Infrastructure/Managers/DeviceModelImageManager.cs +++ b/src/AzureIoTHub.Portal.Infrastructure/Managers/DeviceModelImageManager.cs @@ -53,6 +53,26 @@ public async Task ChangeDeviceModelImageAsync(string deviceModelId, Stre return blobClient.Uri.ToString(); } + public async Task SetDefaultImageToModel(string deviceModelId) + { + var blobContainer = this.blobService.GetBlobContainerClient(this.deviceModelImageOptions.Value.ImageContainerName); + + var blobClient = blobContainer.GetBlobClient(deviceModelId); + + this.logger.LogInformation($"Uploading to Blob storage as blob:\n\t {blobClient.Uri}\n"); + + var currentAssembly = Assembly.GetExecutingAssembly(); + + var defaultImageStream = currentAssembly + .GetManifestResourceStream($"{currentAssembly.GetName().Name}.Resources.{this.deviceModelImageOptions.Value.DefaultImageName}"); + + _ = await blobClient.UploadAsync(defaultImageStream, true); + + _ = await blobClient.SetHttpHeadersAsync(new BlobHttpHeaders { CacheControl = $"max-age={this.configHandler.StorageAccountDeviceModelImageMaxAge}, must-revalidate" }); + + return blobClient.Uri.ToString(); + } + public async Task DeleteDeviceModelImageAsync(string deviceModelId) { var blobContainer = this.blobService.GetBlobContainerClient(this.deviceModelImageOptions.Value.ImageContainerName); diff --git a/src/AzureIoTHub.Portal.Server/Services/DeviceModelService.cs b/src/AzureIoTHub.Portal.Server/Services/DeviceModelService.cs index 125b93b88..5f5380d97 100644 --- a/src/AzureIoTHub.Portal.Server/Services/DeviceModelService.cs +++ b/src/AzureIoTHub.Portal.Server/Services/DeviceModelService.cs @@ -92,6 +92,8 @@ public async Task CreateDeviceModel(TModel deviceModel) await this.deviceModelRepository.InsertAsync(deviceModelEntity); await this.unitOfWork.SaveAsync(); + _ = this.deviceModelImageManager.SetDefaultImageToModel(deviceModel.ModelId); + await CreateDeviceModelConfiguration(deviceModel); } diff --git a/src/AzureIoTHub.Portal.Server/Services/EdgeModelService.cs b/src/AzureIoTHub.Portal.Server/Services/EdgeModelService.cs index 70d536b1b..da247818b 100644 --- a/src/AzureIoTHub.Portal.Server/Services/EdgeModelService.cs +++ b/src/AzureIoTHub.Portal.Server/Services/EdgeModelService.cs @@ -99,6 +99,8 @@ public async Task CreateEdgeModel(IoTEdgeModel edgeModel) throw new ResourceAlreadyExistsException($"The edge model with id {edgeModel?.ModelId} already exists"); } + _ = await this.deviceModelImageManager.SetDefaultImageToModel(edgeModel?.ModelId); + await SaveModuleCommands(edgeModel); await this.configService.RollOutEdgeModelConfiguration(edgeModel); } diff --git a/src/AzureIoTHub.Portal.Tests.Unit/Infrastructure/Managers/DeviceModelImageManagerTest.cs b/src/AzureIoTHub.Portal.Tests.Unit/Infrastructure/Managers/DeviceModelImageManagerTest.cs index 5d8e16208..ebd00b1d1 100644 --- a/src/AzureIoTHub.Portal.Tests.Unit/Infrastructure/Managers/DeviceModelImageManagerTest.cs +++ b/src/AzureIoTHub.Portal.Tests.Unit/Infrastructure/Managers/DeviceModelImageManagerTest.cs @@ -103,6 +103,54 @@ public async Task ChangeDeviceModelImageShouldUploadImageAndReturnItsUri() MockRepository.VerifyAll(); } + [Test] + public async Task ChangeDeviceModelImageShouldSetDefaultImageToModel() + { + // Arrange + var deviceModelId = Fixture.Create(); + var expectedImageUri = Fixture.Create(); + using var imageAsMemoryStream = new MemoryStream(Encoding.UTF8.GetBytes(Fixture.Create())); + + var mockOptions = new DeviceModelImageOptions() + { + BaseUri = Fixture.Create() + }; + + _ = this.mockDeviceModelImageOptions.Setup(x => x.Value).Returns(mockOptions); + + _ = this.mockBlobServiceClient + .Setup(x => x.GetBlobContainerClient(It.IsAny())) + .Returns(this.mockBlobContainerClient.Object); + + _ = this.mockBlobContainerClient + .Setup(x => x.GetBlobClient(deviceModelId)) + .Returns(this.mockBlobClient.Object); + + _ = this.mockBlobClient + .Setup(client => + client.SetHttpHeadersAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(Response.FromValue(BlobsModelFactory.BlobInfo(ETag.All, DateTimeOffset.Now), Mock.Of())); + + _ = this.mockBlobClient + .Setup(client => client.UploadAsync(It.IsAny(), true, It.IsAny())) + .ReturnsAsync(Response.FromValue( + BlobsModelFactory.BlobContentInfo(ETag.All, DateTimeOffset.Now, Array.Empty(), string.Empty, + 1L), Mock.Of())); + + _ = this.mockBlobClient + .Setup(client => client.Uri) + .Returns(expectedImageUri); + + _ = this.mockConfigHandler.Setup(handler => handler.StorageAccountDeviceModelImageMaxAge).Returns(3600); + + // Act + var result = await this.deviceModelImageManager.SetDefaultImageToModel(deviceModelId); + + // Assert + _ = result.Should().Be(expectedImageUri.ToString()); + MockRepository.VerifyAll(); + } + [Test] public async Task WhenDeleteAsyncFailedDeleteDeviceModelImageAsyncShouldThrowAnInternalServerErrorException() { diff --git a/src/AzureIoTHub.Portal.Tests.Unit/Server/Services/DeviceModelServiceTests.cs b/src/AzureIoTHub.Portal.Tests.Unit/Server/Services/DeviceModelServiceTests.cs index f291aa176..6d7d9e5cc 100644 --- a/src/AzureIoTHub.Portal.Tests.Unit/Server/Services/DeviceModelServiceTests.cs +++ b/src/AzureIoTHub.Portal.Tests.Unit/Server/Services/DeviceModelServiceTests.cs @@ -145,6 +145,7 @@ public async Task CreateDeviceModelShouldCreateDeviceModel() { // Arrange var deviceModelDto = Fixture.Create(); + var expectedAvatarUrl = Fixture.Create(); _ = this.mockDeviceModelRepository.Setup(repository => repository.InsertAsync(It.IsAny())) .Returns(Task.CompletedTask); @@ -166,6 +167,10 @@ public async Task CreateDeviceModelShouldCreateDeviceModel() It.IsAny>())) .Returns(Task.CompletedTask); + _ = this.mockDeviceModelImageManager.Setup(manager => + manager.SetDefaultImageToModel(deviceModelDto.ModelId)) + .ReturnsAsync(expectedAvatarUrl); + // Act await this.deviceModelService.CreateDeviceModel(deviceModelDto); diff --git a/src/AzureIoTHub.Portal.Tests.Unit/Server/Services/EdgeModelServiceTest.cs b/src/AzureIoTHub.Portal.Tests.Unit/Server/Services/EdgeModelServiceTest.cs index ba8eabb24..cf8b9f74f 100644 --- a/src/AzureIoTHub.Portal.Tests.Unit/Server/Services/EdgeModelServiceTest.cs +++ b/src/AzureIoTHub.Portal.Tests.Unit/Server/Services/EdgeModelServiceTest.cs @@ -180,6 +180,7 @@ public async Task CreateEdgeModelShouldCreateEdgeModel() { // Arrange var edgeDeviceModel = Fixture.Create(); + var expectedImageUri = Fixture.Create(); _ = this.mockEdgeDeviceModelRepository.Setup(x => x.GetByIdAsync(It.IsAny())) .ReturnsAsync((EdgeDeviceModel)null); @@ -202,6 +203,10 @@ public async Task CreateEdgeModelShouldCreateEdgeModel() _ = this.mockConfigService.Setup(x => x.RollOutEdgeModelConfiguration(It.IsAny())) .Returns(Task.CompletedTask); + _ = this.mockDeviceModelImageManager.Setup(manager => + manager.SetDefaultImageToModel(It.IsAny())) + .ReturnsAsync(expectedImageUri.ToString()); + // Act await this.edgeDeviceModelService.CreateEdgeModel(edgeDeviceModel);