Skip to content

Commit

Permalink
#2075 Add EdgeDevice properties
Browse files Browse the repository at this point in the history
- NbModules / NbDevices / ConnectionState
  • Loading branch information
delager authored and kbeaugrand committed Jun 5, 2023
1 parent 8af360f commit e2f6eed
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,14 @@ namespace AzureIoTHub.Portal.Infrastructure.Jobs.AWS
using Amazon.GreengrassV2.Model;
using Amazon.IoT;
using Amazon.IoT.Model;
using Amazon.SecretsManager.Model;
using AutoMapper;
using AzureIoTHub.Portal.Application.Services;
using AzureIoTHub.Portal.Application.Services.AWS;
using AzureIoTHub.Portal.Domain;
using AzureIoTHub.Portal.Domain.Entities;
using AzureIoTHub.Portal.Domain.Repositories;
using AzureIoTHub.Portal.Models.v10;
using Microsoft.Extensions.Logging;
using Quartz;
using Quartz.Util;
Expand All @@ -30,6 +34,8 @@ public class SyncGreenGrassDevicesJob : IJob
private readonly IDeviceTagValueRepository deviceTagValueRepository;
private readonly IAmazonIoT amazonIoTClient;
private readonly IAmazonGreengrassV2 amazonGreenGrass;
private readonly IConfigService configService;
private readonly IAWSExternalDeviceService awsExternalDevicesService;

public SyncGreenGrassDevicesJob(
ILogger<SyncGreenGrassDevicesJob> logger,
Expand All @@ -39,7 +45,9 @@ public SyncGreenGrassDevicesJob(
IEdgeDeviceModelRepository edgeDeviceModelRepository,
IDeviceTagValueRepository deviceTagValueRepository,
IAmazonIoT amazonIoTClient,
IAmazonGreengrassV2 amazonGreenGrass)
IAmazonGreengrassV2 amazonGreenGrass,
IConfigService configService,
IAWSExternalDeviceService awsExternalDevicesService)
{
this.mapper = mapper;
this.unitOfWork = unitOfWork;
Expand All @@ -48,6 +56,8 @@ public SyncGreenGrassDevicesJob(
this.deviceTagValueRepository = deviceTagValueRepository;
this.amazonIoTClient = amazonIoTClient;
this.amazonGreenGrass = amazonGreenGrass;
this.configService = configService;
this.awsExternalDevicesService = awsExternalDevicesService;
this.logger = logger;
}

Expand Down Expand Up @@ -96,8 +106,31 @@ private async Task SyncGreenGrassDevicesAsEdgeDevices()
continue;
}

//Map with EdgeDevice
var edgeDevice = this.mapper.Map<EdgeDevice>(thing);
edgeDevice.DeviceModelId = edgeDeviceModel.Id;
//EdgeDevices properties that are not present in the thing
try
{
var modules = await this.configService.GetConfigModuleList(edgeDevice.DeviceModelId);
edgeDevice.NbDevices = await this.awsExternalDevicesService.GetEdgeDeviceNbDevices(this.mapper.Map<IoTEdgeDevice>(edgeDevice));
edgeDevice.NbModules = modules.Count;
var coreDevice = await amazonGreenGrass.GetCoreDeviceAsync(new GetCoreDeviceRequest() { CoreDeviceThingName = thing.ThingName });
if (coreDevice.HttpStatusCode != HttpStatusCode.OK)
{
this.logger.LogWarning($"Cannot import Greengrass device '{thing.ThingName}' due to an error retrieving core device in the Amazon IoT Data API : {coreDevice.HttpStatusCode}");
continue;
}
edgeDevice.ConnectionState = coreDevice.Status == CoreDeviceStatus.HEALTHY ? "Connected" : "Disconnected";
}
catch (Exception e)
{
this.logger.LogWarning($"Cannot import Greengrass device '{thing.ThingName}' due to an error retrieving Greengrass device properties in the Amazon IoT Data API.", e);
continue;
}

//Create or update the Edge Device
await CreateOrUpdateGreenGrassDevice(thing, edgeDeviceModel);
await CreateOrUpdateGreenGrassDevice(edgeDevice);
}

foreach (var item in (await this.edgeDeviceRepository.GetAllAsync(
Expand Down Expand Up @@ -141,11 +174,9 @@ private async Task<List<DescribeThingResponse>> GetAllGreenGrassDevices()
return devices;
}

private async Task CreateOrUpdateGreenGrassDevice(DescribeThingResponse greengrassDevice, EdgeDeviceModel edgeModelDevice)
private async Task CreateOrUpdateGreenGrassDevice(EdgeDevice edgeDevice)
{
var edgeDevice = this.mapper.Map<EdgeDevice>(greengrassDevice);
var edgeDeviceEntity = await this.edgeDeviceRepository.GetByIdAsync(edgeDevice.Id, d => d.Tags);
edgeDevice.DeviceModelId = edgeModelDevice.Id;

if (edgeDeviceEntity == null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ namespace AzureIoTHub.Portal.Infrastructure.Services
{
using System;
using System.Threading.Tasks;
using Amazon.GreengrassV2;
using Amazon.IoT.Model;
using AutoMapper;
using AzureIoTHub.Portal.Application.Managers;
Expand Down Expand Up @@ -137,6 +138,7 @@ public async Task<IoTEdgeDevice> GetEdgeDevice(string edgeDeviceId)
deviceDto.Modules = await this.configService.GetConfigModuleList(model.ExternalIdentifier!);
deviceDto.NbDevices = await this.awsExternalDevicesService.GetEdgeDeviceNbDevices(deviceDto);
deviceDto.NbModules = deviceDto.Modules.Count;
deviceDto.ConnectionState = deviceDto.RuntimeResponse == CoreDeviceStatus.HEALTHY ? "Connected" : "Disconnected";

return deviceDto;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ namespace AzureIoTHub.Portal.Tests.Unit.Infrastructure.Jobs.AWS
using Amazon.IoT;
using Amazon.IoT.Model;
using AutoFixture;
using AzureIoTHub.Portal.Application.Services.AWS;
using AzureIoTHub.Portal.Application.Services;
using AzureIoTHub.Portal.Domain;
using AzureIoTHub.Portal.Domain.Entities;
using AzureIoTHub.Portal.Domain.Repositories;
Expand All @@ -23,6 +25,7 @@ namespace AzureIoTHub.Portal.Tests.Unit.Infrastructure.Jobs.AWS
using Moq;
using NUnit.Framework;
using Quartz;
using AzureIoTHub.Portal.Models.v10;

public class SyncGreenGrassDevicesJobTests : BackendUnitTest
{
Expand All @@ -34,6 +37,8 @@ public class SyncGreenGrassDevicesJobTests : BackendUnitTest
private Mock<IEdgeDeviceRepository> mockDeviceRepository;
private Mock<IEdgeDeviceModelRepository> mockDeviceModelRepository;
private Mock<IDeviceTagValueRepository> mockDeviceTagValueRepository;
private Mock<IConfigService> mockConfigService;
private Mock<IAWSExternalDeviceService> mockAwsExternalDevicesService;

public override void Setup()
{
Expand All @@ -45,13 +50,17 @@ public override void Setup()
this.mockDeviceTagValueRepository = MockRepository.Create<IDeviceTagValueRepository>();
this.amazonIoTClient = MockRepository.Create<IAmazonIoT>();
this.amazonGreenGrass = MockRepository.Create<IAmazonGreengrassV2>();
this.mockConfigService = MockRepository.Create<IConfigService>();
this.mockAwsExternalDevicesService = MockRepository.Create<IAWSExternalDeviceService>();

_ = ServiceCollection.AddSingleton(this.mockUnitOfWork.Object);
_ = ServiceCollection.AddSingleton(this.mockDeviceRepository.Object);
_ = ServiceCollection.AddSingleton(this.mockDeviceModelRepository.Object);
_ = ServiceCollection.AddSingleton(this.mockDeviceTagValueRepository.Object);
_ = ServiceCollection.AddSingleton(this.amazonIoTClient.Object);
_ = ServiceCollection.AddSingleton(this.amazonGreenGrass.Object);
_ = ServiceCollection.AddSingleton(this.mockConfigService.Object);
_ = ServiceCollection.AddSingleton(this.mockAwsExternalDevicesService.Object);
_ = ServiceCollection.AddSingleton<IJob, SyncGreenGrassDevicesJob>();


Expand Down Expand Up @@ -104,6 +113,15 @@ public async Task ExecuteNewEdgeDeviceEdgeDeviceCreated()
.Setup(x => x.GetByNameAsync(newDevice.DeviceModel.Name))
.ReturnsAsync(expectedDeviceModel);

_ = this.mockConfigService.Setup(x => x.GetConfigModuleList(expectedDeviceModel.Id))
.ReturnsAsync(new List<IoTEdgeModule>());

_ = this.mockAwsExternalDevicesService.Setup(x => x.GetEdgeDeviceNbDevices(It.IsAny<IoTEdgeDevice>()))
.ReturnsAsync(0);

_ = this.amazonGreenGrass.Setup(client => client.GetCoreDeviceAsync(It.IsAny<GetCoreDeviceRequest>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new GetCoreDeviceResponse() { HttpStatusCode = HttpStatusCode.OK });

_ = this.mockDeviceRepository.Setup(repository => repository.GetByIdAsync(newDevice.Id, d => d.Tags))
.ReturnsAsync((EdgeDevice)null);

Expand Down Expand Up @@ -184,6 +202,15 @@ public async Task ExecuteExistingDeviceWithHigherVersionDeviceUpdated()
.Setup(x => x.GetByNameAsync(existingDevice.DeviceModel.Name))
.ReturnsAsync(expectedDeviceModel);

_ = this.mockConfigService.Setup(x => x.GetConfigModuleList(expectedDeviceModel.Id))
.ReturnsAsync(new List<IoTEdgeModule>());

_ = this.mockAwsExternalDevicesService.Setup(x => x.GetEdgeDeviceNbDevices(It.IsAny<IoTEdgeDevice>()))
.ReturnsAsync(0);

_ = this.amazonGreenGrass.Setup(client => client.GetCoreDeviceAsync(It.IsAny<GetCoreDeviceRequest>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new GetCoreDeviceResponse() { HttpStatusCode = HttpStatusCode.OK });

_ = this.mockDeviceRepository.Setup(repository => repository.GetByIdAsync(existingDevice.Id, d => d.Tags))
.ReturnsAsync(existingDevice);

Expand Down Expand Up @@ -265,6 +292,15 @@ public async Task ExecuteExistingEdgeDeviceWithOlderVersionEdgeDeviceNotUpdated(
.Setup(x => x.GetByNameAsync(existingDevice.DeviceModel.Name))
.ReturnsAsync(expectedDeviceModel);

_ = this.mockConfigService.Setup(x => x.GetConfigModuleList(expectedDeviceModel.Id))
.ReturnsAsync(new List<IoTEdgeModule>());

_ = this.mockAwsExternalDevicesService.Setup(x => x.GetEdgeDeviceNbDevices(It.IsAny<IoTEdgeDevice>()))
.ReturnsAsync(0);

_ = this.amazonGreenGrass.Setup(client => client.GetCoreDeviceAsync(It.IsAny<GetCoreDeviceRequest>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new GetCoreDeviceResponse() { HttpStatusCode = HttpStatusCode.OK });

_ = this.mockDeviceRepository.Setup(repository => repository.GetByIdAsync(existingDevice.Id, d => d.Tags))
.ReturnsAsync(existingDevice);

Expand Down Expand Up @@ -338,6 +374,72 @@ public async Task ExecuteNewEdgeDeviceWithDescribeThingErrorSkipped()
MockRepository.VerifyAll();
}

[Test]
public async Task ExecuteNewEdgeDeviceWithGetCoreDeviceErrorSkipped()
{
// Arrange
var mockJobExecutionContext = MockRepository.Create<IJobExecutionContext>();

var expectedDeviceModel = Fixture.Create<EdgeDeviceModel>();
var existingDevice = new EdgeDevice
{
Id = Fixture.Create<string>(),
Name = Fixture.Create<string>(),
DeviceModel = expectedDeviceModel,
DeviceModelId = expectedDeviceModel.Id,
Version = 1
};

var greenGrassDevices = new ListCoreDevicesResponse
{
CoreDevices = new List<CoreDevice>()
{
new CoreDevice
{
CoreDeviceThingName = existingDevice.Name
}
}
};

_ = this.amazonGreenGrass.Setup(client => client.ListCoreDevicesAsync(It.IsAny<ListCoreDevicesRequest>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(greenGrassDevices);

_ = this.amazonIoTClient.Setup(client => client.DescribeThingAsync(It.IsAny<DescribeThingRequest>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new DescribeThingResponse()
{
ThingId = existingDevice.Id,
ThingName = existingDevice.Name,
ThingTypeName = existingDevice.DeviceModel.Name,
Version = 1,
HttpStatusCode = HttpStatusCode.OK
});

_ = this.mockDeviceModelRepository
.Setup(x => x.GetByNameAsync(existingDevice.DeviceModel.Name))
.ReturnsAsync(expectedDeviceModel);

_ = this.mockConfigService.Setup(x => x.GetConfigModuleList(expectedDeviceModel.Id))
.ReturnsAsync(new List<IoTEdgeModule>());

_ = this.mockAwsExternalDevicesService.Setup(x => x.GetEdgeDeviceNbDevices(It.IsAny<IoTEdgeDevice>()))
.ReturnsAsync(0);

_ = this.amazonGreenGrass.Setup(client => client.GetCoreDeviceAsync(It.IsAny<GetCoreDeviceRequest>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new GetCoreDeviceResponse() { HttpStatusCode = HttpStatusCode.RequestTimeout });

_ = this.mockDeviceRepository.Setup(x => x.GetAllAsync(It.IsAny<Expression<Func<EdgeDevice, bool>>>(), It.IsAny<CancellationToken>(), d => d.Tags, d => d.Labels))
.ReturnsAsync(new List<EdgeDevice>());

_ = this.mockUnitOfWork.Setup(work => work.SaveAsync())
.Returns(Task.CompletedTask);

// Act
await this.syncGreenGrassDevicesJob.Execute(mockJobExecutionContext.Object);

// Assert
MockRepository.VerifyAll();
}

[Test]
public async Task ExecuteNewDeviceWithoutThingTypeSkipped()
{
Expand Down

0 comments on commit e2f6eed

Please sign in to comment.