Skip to content

Commit

Permalink
Fix #2100 - Sync thing type depending on iotEdge tag (#2107)
Browse files Browse the repository at this point in the history
  • Loading branch information
kbeaugrand committed Jun 18, 2023
1 parent c3564fe commit 16bb5c7
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 96 deletions.
239 changes: 143 additions & 96 deletions src/AzureIoTHub.Portal.Infrastructure/Jobs/AWS/SyncThingTypesJob.cs
Original file line number Diff line number Diff line change
@@ -1,77 +1,124 @@
// Copyright (c) CGI France. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace AzureIoTHub.Portal.Infrastructure.Jobs.AWS
{
using System.Threading.Tasks;
using Amazon.IoT;
using Amazon.IoT.Model;
using AutoMapper;
using AzureIoTHub.Portal.Application.Managers;
// Copyright (c) CGI France. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace AzureIoTHub.Portal.Infrastructure.Jobs.AWS
{
using System.Threading.Tasks;
using Amazon.IoT;
using Amazon.IoT.Model;
using AutoMapper;
using AzureIoTHub.Portal.Application.Managers;
using AzureIoTHub.Portal.Domain;
using AzureIoTHub.Portal.Domain.Entities;
using AzureIoTHub.Portal.Domain.Repositories;
using Microsoft.Extensions.Logging;
using Quartz;

[DisallowConcurrentExecution]
public class SyncThingTypesJob : IJob
using Microsoft.Extensions.Logging;
using Quartz;

[DisallowConcurrentExecution]
public class SyncThingTypesJob : IJob
{

private readonly ILogger<SyncThingTypesJob> logger;
private readonly IMapper mapper;
private readonly IUnitOfWork unitOfWork;
private readonly IDeviceModelRepository deviceModelRepository;
private readonly IAmazonIoT amazonIoTClient;
private readonly IDeviceModelImageManager deviceModelImageManager;

public SyncThingTypesJob(
ILogger<SyncThingTypesJob> logger,
IMapper mapper,
private readonly ILogger<SyncThingTypesJob> logger;
private readonly IMapper mapper;
private readonly IUnitOfWork unitOfWork;
private readonly IDeviceModelRepository deviceModelRepository;
private readonly IAmazonIoT amazonIoTClient;
private readonly IDeviceModelImageManager deviceModelImageManager;

public SyncThingTypesJob(
ILogger<SyncThingTypesJob> logger,
IMapper mapper,
IUnitOfWork unitOfWork,
IDeviceModelRepository deviceModelRepository,
IDeviceModelRepository deviceModelRepository,
IAmazonIoT amazonIoTClient,
IDeviceModelImageManager awsImageManager)
{
this.deviceModelImageManager = awsImageManager;
this.mapper = mapper;
IDeviceModelImageManager awsImageManager)
{
this.deviceModelImageManager = awsImageManager;
this.mapper = mapper;
this.unitOfWork = unitOfWork;
this.deviceModelRepository = deviceModelRepository;
this.amazonIoTClient = amazonIoTClient;
this.logger = logger;
}


public async Task Execute(IJobExecutionContext context)
{
try
{
this.logger.LogInformation("Start of sync Thing Types job");

await SyncThingTypesAsDeviceModels();

this.logger.LogInformation("End of sync Thing Types job");
}
catch (Exception e)
{
this.logger.LogError(e, "Sync Thing Types job has failed");
}
}

private async Task SyncThingTypesAsDeviceModels()
{
this.deviceModelRepository = deviceModelRepository;
this.amazonIoTClient = amazonIoTClient;
this.logger = logger;
}


public async Task Execute(IJobExecutionContext context)
{
try
{
this.logger.LogInformation("Start of sync Thing Types job");

await SyncThingTypesAsDeviceModels();

this.logger.LogInformation("End of sync Thing Types job");
}
catch (Exception e)
{
this.logger.LogError(e, "Sync Thing Types job has failed");
}
}

private async Task SyncThingTypesAsDeviceModels()
{
var thingTypes = await GetAllThingTypes();

foreach (var thingType in thingTypes)
{
await CreateOrUpdateDeviceModel(thingType);
}

//Delete in Database AWS deleted thing types
await DeleteThingTypes(thingTypes);
}

private async Task<List<DescribeThingTypeResponse>> GetAllThingTypes()
var isEdge = await IsEdgeThingType(thingType);

// Cannot know if the thing type was created for an iotEdge or not, so skipping...
if (!isEdge.HasValue)
{
continue;
}

if (isEdge == true)
{
// TODO: Implement CreateOrUpdateEdgeModel here.
}
else
{
await CreateOrUpdateDeviceModel(thingType);
}
}

// Delete in Database AWS deleted thing types
await DeleteThingTypes(thingTypes);
}

private async Task<bool?> IsEdgeThingType(DescribeThingTypeResponse thingType)
{
var response = await this.amazonIoTClient.ListTagsForResourceAsync(new ListTagsForResourceRequest
{
ResourceArn = thingType.ThingTypeArn
});

do
{
if (response == null || !response.Tags.Any())
{
return null;
}

var iotEdgeTag = response.Tags.Where(c => c.Key.Equals("iotEdge", StringComparison.OrdinalIgnoreCase));

if (!iotEdgeTag.Any())
{
response = await this.amazonIoTClient.ListTagsForResourceAsync(new ListTagsForResourceRequest
{
ResourceArn = thingType.ThingTypeArn,
NextToken = response.NextToken
});

continue;
}

return bool.TryParse(iotEdgeTag.Single().Value, out var result) ? result : null;

} while (true);
}

private async Task<List<DescribeThingTypeResponse>> GetAllThingTypes()
{
var thingTypes = new List<DescribeThingTypeResponse>();

Expand All @@ -88,47 +135,47 @@ private async Task<List<DescribeThingTypeResponse>> GetAllThingTypes()

foreach (var thingType in response.ThingTypes)
{
var requestDescribeThingType = new DescribeThingTypeRequest
{
ThingTypeName = thingType.ThingTypeName,
var requestDescribeThingType = new DescribeThingTypeRequest
{
ThingTypeName = thingType.ThingTypeName,
};

thingTypes.Add(await this.amazonIoTClient.DescribeThingTypeAsync(requestDescribeThingType));
}

nextToken = response.NextToken;
}
while (!string.IsNullOrEmpty(nextToken));

return thingTypes;
}

private async Task CreateOrUpdateDeviceModel(DescribeThingTypeResponse thingType)
while (!string.IsNullOrEmpty(nextToken));

return thingTypes;
}

private async Task CreateOrUpdateDeviceModel(DescribeThingTypeResponse thingType)
{
if (thingType.ThingTypeMetadata.Deprecated)
{
return;
}


var deviceModel = this.mapper.Map<DeviceModel>(thingType);

var existingDeviceModel = await this.deviceModelRepository.GetByIdAsync(deviceModel.Id);

if (existingDeviceModel == null)
{
await this.deviceModelRepository.InsertAsync(deviceModel);
_ = await this.deviceModelImageManager.SetDefaultImageToModel(deviceModel.Id);
}
else
{
_ = this.mapper.Map(deviceModel, existingDeviceModel);
this.deviceModelRepository.Update(existingDeviceModel);
}
await this.unitOfWork.SaveAsync();

}

private async Task DeleteThingTypes(List<DescribeThingTypeResponse> thingTypes)
var existingDeviceModel = await this.deviceModelRepository.GetByIdAsync(deviceModel.Id);

if (existingDeviceModel == null)
{
await this.deviceModelRepository.InsertAsync(deviceModel);
_ = await this.deviceModelImageManager.SetDefaultImageToModel(deviceModel.Id);
}
else
{
_ = this.mapper.Map(deviceModel, existingDeviceModel);
this.deviceModelRepository.Update(existingDeviceModel);
}

await this.unitOfWork.SaveAsync();
}

private async Task DeleteThingTypes(List<DescribeThingTypeResponse> thingTypes)
{
// Get all device models that are not in AWS anymore or that are deprecated
var deviceModelsToDelete = (await this.deviceModelRepository.GetAllAsync())
Expand All @@ -140,10 +187,10 @@ private async Task DeleteThingTypes(List<DescribeThingTypeResponse> thingTypes)
{
await this.deviceModelImageManager.DeleteDeviceModelImageAsync(deviceModel.Id);
this.deviceModelRepository.Delete(deviceModel.Id);
});

await this.unitOfWork.SaveAsync();
}

}
}
});

await this.unitOfWork.SaveAsync();
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ public async Task<ExternalDeviceModelDto> CreateDeviceModel(ExternalDeviceModelD

var createThingTypeRequest = this.mapper.Map<CreateThingTypeRequest>(thingType);

createThingTypeRequest.Tags.Add(new Tag
{
Key = "iotEdge",
Value = "False"
});

var response = await this.amazonIoTClient.CreateThingTypeAsync(createThingTypeRequest);

deviceModel.Id = response.ThingTypeId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,20 +88,23 @@ public async Task Execute_SyncNewAndExistingAndDepprecatedThingTypes_DeviceModel

var existingThingType = new DescribeThingTypeResponse
{
ThingTypeArn = Fixture.Create<string>(),
ThingTypeId = existingDeviceModelName,
ThingTypeName = existingDeviceModelName,
ThingTypeMetadata = new ThingTypeMetadata()
};

var newThingType = new DescribeThingTypeResponse
{
ThingTypeArn = Fixture.Create<string>(),
ThingTypeId = newDeviceModelName,
ThingTypeName = newDeviceModelName,
ThingTypeMetadata = new ThingTypeMetadata()
};

var depcrecatedThingType = new DescribeThingTypeResponse
{
ThingTypeArn = Fixture.Create<string>(),
ThingTypeId = depcrecatedDeviceModelName,
ThingTypeName = depcrecatedDeviceModelName,
ThingTypeMetadata = new ThingTypeMetadata
Expand All @@ -117,6 +120,20 @@ public async Task Execute_SyncNewAndExistingAndDepprecatedThingTypes_DeviceModel
_ = this.iaAmazon.Setup(client => client.DescribeThingTypeAsync(It.Is<DescribeThingTypeRequest>(c => c.ThingTypeName == depcrecatedThingType.ThingTypeName), It.IsAny<CancellationToken>()))
.ReturnsAsync(depcrecatedThingType);

_ = this.iaAmazon.Setup(client => client.ListTagsForResourceAsync(It.IsAny<ListTagsForResourceRequest>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new ListTagsForResourceResponse
{
NextToken = Fixture.Create<string>(),
Tags = new List<Tag>
{
new Tag
{
Key = "iotEdge",
Value = "False"
}
}
});

var existingDeviceModel = new DeviceModel
{
Id = existingDeviceModelName,
Expand Down

0 comments on commit 16bb5c7

Please sign in to comment.