diff --git a/src/AssemblyInfo.cs b/src/AssemblyInfo.cs
index ec13ee64b..86f714527 100644
--- a/src/AssemblyInfo.cs
+++ b/src/AssemblyInfo.cs
@@ -6,6 +6,7 @@
[assembly: System.CLSCompliant(false)]
[assembly: InternalsVisibleTo("AzureIoTHub.Portal.Shared")]
+[assembly: InternalsVisibleTo("AzureIoTHub.Portal.Infrastructure")]
[assembly: InternalsVisibleTo("AzureIoTHub.Portal.Server")]
[assembly: InternalsVisibleTo("AzureIoTHub.Portal.Client")]
[assembly: InternalsVisibleTo("AzureIoTHub.Portal.Tests.Unit")]
diff --git a/src/AzureIoTHub.Portal.Application/AzureIoTHub.Portal.Application.csproj b/src/AzureIoTHub.Portal.Application/AzureIoTHub.Portal.Application.csproj
index 36d5cb127..3cf8623b6 100644
--- a/src/AzureIoTHub.Portal.Application/AzureIoTHub.Portal.Application.csproj
+++ b/src/AzureIoTHub.Portal.Application/AzureIoTHub.Portal.Application.csproj
@@ -8,6 +8,7 @@
+
diff --git a/src/AzureIoTHub.Portal.Application/Mappers/AWS/ThingTypeProfile.cs b/src/AzureIoTHub.Portal.Application/Mappers/AWS/ThingTypeProfile.cs
new file mode 100644
index 000000000..b8035b8ce
--- /dev/null
+++ b/src/AzureIoTHub.Portal.Application/Mappers/AWS/ThingTypeProfile.cs
@@ -0,0 +1,51 @@
+// Copyright (c) CGI France. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace AzureIoTHub.Portal.Application.Mappers.AWS
+{
+ using AutoMapper;
+ using AzureIoTHub.Portal.Domain.Entities.AWS;
+ using AzureIoTHub.Portal.Models.v10.AWS;
+ using Amazon.IoT.Model;
+ public class ThingTypeProfile : Profile
+ {
+ public ThingTypeProfile()
+ {
+ _ = CreateMap()
+ .ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.ThingTypeID))
+ .ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.ThingTypeName))
+ .ForMember(dest => dest.Description, opts => opts.MapFrom(src => src.ThingTypeDescription))
+ .ForMember(dest => dest.ThingTypeSearchableAttributes, opts => opts.MapFrom(src => src.ThingTypeSearchableAttDtos.Select(pair => new ThingTypeSearchableAtt
+ {
+ Name = pair.Name
+ })))
+ .ForMember(dest => dest.Tags, opts => opts.MapFrom(src => src.Tags.Select(pair => new ThingTypeTag
+ {
+ Key = pair.Key,
+ Value = pair.Value
+ })));
+
+ _ = CreateMap()
+ .ForMember(dest => dest.ThingTypeID, opts => opts.MapFrom(src => src.Id))
+ .ForMember(dest => dest.ThingTypeName, opts => opts.MapFrom(src => src.Name))
+ .ForMember(dest => dest.ThingTypeDescription, opts => opts.MapFrom(src => src.Description))
+ .ForMember(dest => dest.Tags, opts => opts.MapFrom(src => src.Tags != null ? src.Tags.ToList() : null))
+ .ForMember(dest => dest.ThingTypeSearchableAttDtos, opts => opts.MapFrom(
+ src => src.ThingTypeSearchableAttributes != null ? src.ThingTypeSearchableAttributes.ToList() : null));
+
+ _ = CreateMap()
+ .ForMember(dest => dest.ThingTypeName, opts => opts.MapFrom(src => src.ThingTypeName))
+ .ForMember(dest => dest.ThingTypeProperties, opts => opts.MapFrom(src => new ThingTypeProperties
+ {
+ ThingTypeDescription = src.ThingTypeDescription,
+ SearchableAttributes = src.ThingTypeSearchableAttDtos.Select(pair => pair.Name).ToList() ?? new List()
+ }))
+ .ForMember(dest => dest.Tags, opts => opts.MapFrom(src => src.Tags.Select(pair => new Tag
+ {
+ Key = pair.Key,
+ Value = pair.Value
+ }).ToList() ?? new List()));
+
+ }
+ }
+}
diff --git a/src/AzureIoTHub.Portal.Application/Mappers/AWS/ThingTypeSearchableAttProfile.cs b/src/AzureIoTHub.Portal.Application/Mappers/AWS/ThingTypeSearchableAttProfile.cs
new file mode 100644
index 000000000..1b2845ca0
--- /dev/null
+++ b/src/AzureIoTHub.Portal.Application/Mappers/AWS/ThingTypeSearchableAttProfile.cs
@@ -0,0 +1,20 @@
+// Copyright (c) CGI France. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace AzureIoTHub.Portal.Application.Mappers.AWS
+{
+ using AutoMapper;
+ using AzureIoTHub.Portal.Domain.Entities.AWS;
+ using AzureIoTHub.Portal.Models.v10.AWS;
+
+ public class ThingTypeSearchableAttProfile : Profile
+ {
+ public ThingTypeSearchableAttProfile()
+ {
+ _ = CreateMap()
+ .ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.Name))
+ .ReverseMap();
+
+ }
+ }
+}
diff --git a/src/AzureIoTHub.Portal.Application/Services/AWS/IThingTypeService.cs b/src/AzureIoTHub.Portal.Application/Services/AWS/IThingTypeService.cs
new file mode 100644
index 000000000..60cdc0290
--- /dev/null
+++ b/src/AzureIoTHub.Portal.Application/Services/AWS/IThingTypeService.cs
@@ -0,0 +1,20 @@
+// Copyright (c) CGI France. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace AzureIoTHub.Portal.Application.Services.AWS
+{
+ using AzureIoTHub.Portal.Models.v10.AWS;
+ using Microsoft.AspNetCore.Http;
+
+ public interface IThingTypeService
+ {
+ //Create a thing type
+ Task CreateThingType(ThingTypeDto thingType);
+
+ Task GetThingTypeAvatar(string thingTypeId);
+
+ Task UpdateThingTypeAvatar(string thingTypeId, IFormFile file);
+
+ Task DeleteThingTypeAvatar(string thingTypeId);
+ }
+}
diff --git a/src/AzureIoTHub.Portal.Client/AzureIoTHub.Portal.Client.csproj b/src/AzureIoTHub.Portal.Client/AzureIoTHub.Portal.Client.csproj
index 4a68e8824..2fc035073 100644
--- a/src/AzureIoTHub.Portal.Client/AzureIoTHub.Portal.Client.csproj
+++ b/src/AzureIoTHub.Portal.Client/AzureIoTHub.Portal.Client.csproj
@@ -39,5 +39,9 @@
+
+
+
+
diff --git a/src/AzureIoTHub.Portal.Client/Pages/DeviceModels/CreateDeviceModelPage.razor b/src/AzureIoTHub.Portal.Client/Pages/DeviceModels/CreateDeviceModelPage.razor
index ca55dead3..50340a98e 100644
--- a/src/AzureIoTHub.Portal.Client/Pages/DeviceModels/CreateDeviceModelPage.razor
+++ b/src/AzureIoTHub.Portal.Client/Pages/DeviceModels/CreateDeviceModelPage.razor
@@ -1,11 +1,15 @@
@page "/device-models/new"
+
@using System.Net.Http.Headers
@using AzureIoTHub.Portal.Client.Pages.DeviceModels
@using AzureIoTHub.Portal.Client.Validators
+@using AzureIoTHub.Portal.Shared.Models
@using AzureIoTHub.Portal.Models
@using AzureIoTHub.Portal.Models.v10
@using AzureIoTHub.Portal.Models.v10.LoRaWAN
-@using AzureIoTHub.Portal.Shared.Models
+@using AzureIoTHub.Portal.Models.v10.AWS
+@using AzureIoTHub.Portal.Shared.Constants
+@using AzureIoTHub.Portal.Client.Services.AWS
@attribute [Authorize]
@inject NavigationManager NavigationManager
@@ -13,8 +17,19 @@
@inject ISnackbar Snackbar
@inject IDeviceModelsClientService DeviceModelsClientService
@inject ILoRaWanDeviceModelsClientService LoRaWanDeviceModelsClientService
+@inject ILoRaWanDeviceModelsClientService LoRaWanDeviceModelsClientService
+@inject IThingTypeClientService ThingTypeClientService
-Create Device Model
+@if (Portal.CloudProvider.Equals(CloudProviders.AWS))
+{
+ Create Thing Type
+
+}
+else
+{
+ Create Device Model
+
+}
@@ -37,6 +52,7 @@
}
+
Save Changes
@@ -52,26 +68,44 @@
-
+ @if (Portal.CloudProvider.Equals(CloudProviders.AWS))
+ {
+
+
+ }
+ else
+ {
+
+
+ }
-
-
- @if (Portal.IsLoRaSupported)
- {
-
-
- LoRa Device
-
-
-
- @if (IsLoRa)
+ @if (Portal.CloudProvider.Equals(CloudProviders.AWS))
+ {
+
+
+ }
+ else
+ {
+
+ @if (Portal.IsLoRaSupported)
{
- The device is a LoRa Device.
-
+
+
+ LoRa Device
+
+
+
+ @if (IsLoRa)
+ {
+ The device is a LoRa Device.
+
+ }
+
}
-
- }
+ }
+
+
@@ -79,83 +113,164 @@
@if (!IsLoRa)
{
-
-
-
- Properties
-
-
-
- Add property
-
-
- @foreach (var item in this.Properties.OrderBy(x => x.Order))
- {
-
-
-
+
+
+ Tags
+
+
+ Add tag
+
+ @foreach (var tag in this.ThingTypeTags)
+ {
+
+
+
+
+
+
+
+
+
+ Remove
+
+
+ }
+
+
+
+
+
+
+ }
+ else
+ {
+
+
+
+ Properties
+
+
+
+ Add property
+
+
+ @foreach (var item in this.Properties.OrderBy(x => x.Order))
+ {
+
+
+
-
-
-
+
+
-
-
-
+
+
-
-
-
+
+
- @foreach (DevicePropertyType item in Enum.GetValues(typeof(DevicePropertyType)))
- {
- @item
- }
-
-
-
-
+ @foreach (DevicePropertyType item in Enum.GetValues(typeof(DevicePropertyType)))
+ {
+ @item
+ }
+
+
+
+
+
+
+
+ Remove
+
+
+ }
+
+
+
+
+
+
+ }
+ }
+ @if (Portal.CloudProvider.Equals(CloudProviders.AWS))
+ {
+
+
+
+ Searchable Attribute
+
+
+
+ Add Attribute
+
+
+ @foreach (var search in this.searchableAtt)
+ {
+
+
+
+
- Remove
+ Remove
}
-
+
+
+
+
+
+ }
+ else
+ {
+
+
+
+ Labels
+
+
}
-
-
-
-
- Labels
-
-
-
-
-
-
@@ -178,12 +293,15 @@
private MudForm form = default!;
private DeviceModelValidator standardValidator = new DeviceModelValidator();
+ private ThingTypeValidator thingTypeValidator = new ThingTypeValidator();
private LoRaDeviceModelValidator loraValidator = new LoRaDeviceModelValidator();
private DevicePropertyValidator propertiesValidator = new DevicePropertyValidator();
private List Commands { get; set; } = new();
private List Properties { get; set; } = new List();
-
+ private List searchableAtt { get; set; } = new List();
+ private List ThingTypeTags { get; set; } = new List();
private LoRaDeviceModelCommandValidator CommandValidator = new LoRaDeviceModelCommandValidator();
+ private ThingTypeDto ThingType { get; set; } = new ThingTypeDto();
private bool isProcessing;
@@ -243,11 +361,22 @@
private bool CheckGeneralValidation()
{
- if (!IsLoRa && this.Model is DeviceModelDto deviceModel)
+ if (Portal.CloudProvider.Equals(CloudProviders.AWS))
{
- return !this.standardValidator.Validate(deviceModel).IsValid;
+ if(this.ThingType != null)
+ {
+ return !this.thingTypeValidator.Validate(this.ThingType).IsValid;
+ }
+ }
+ else
+ {
+ if (!IsLoRa && this.Model is DeviceModelDto deviceModel)
+ {
+ return !this.standardValidator.Validate(deviceModel).IsValid;
+ }
}
+
return CheckLoRaValidation();
}
@@ -277,6 +406,19 @@
}
private async Task Save()
+ {
+
+ if(Portal.CloudProvider.Equals(CloudProviders.AWS))
+ {
+ await this.SaveThingType();
+ }
+ else
+ {
+ await this.SaveDeviceModel();
+ }
+ }
+
+ private async Task SaveDeviceModel()
{
isProcessing = true;
@@ -330,9 +472,12 @@
}
else
{
+
await DeviceModelsClientService.CreateDeviceModel((Model as DeviceModelDto)!);
await DeviceModelsClientService.SetDeviceModelModelProperties(Model.ModelId, Properties);
+
+
}
if (content is not null)
@@ -349,6 +494,53 @@
Snackbar.Add("Device model successfully created.", Severity.Success);
+
+ // Go back to the list of devices
+ NavigationManager.NavigateTo("device-models");
+ }
+
+ catch (ProblemDetailsException exception)
+ {
+ Error?.ProcessProblemDetails(exception);
+ }
+ finally
+ {
+ isProcessing = false;
+ }
+ }
+
+ private async Task SaveThingType()
+ {
+ isProcessing = true;
+
+ // Displays validation error message for each field
+ await form.Validate();
+
+
+ if (!thingTypeValidator.Validate(ThingType).IsValid)
+ {
+ Snackbar.Add("One or more validation errors occurred", Severity.Error);
+
+ isProcessing = false;
+
+ return;
+ }
+
+ try
+ {
+
+ ThingType.Tags = this.ThingTypeTags;
+ ThingType.ThingTypeSearchableAttDtos = searchableAtt;
+
+ var GetThingTypeId = await ThingTypeClientService.CreateThingType(ThingType);
+
+ if (content is not null)
+ {
+ _ = this.ThingTypeClientService.ChangeAvatar(GetThingTypeId, content);
+ }
+
+ Snackbar.Add("Thing Type successfully created.", Severity.Success);
+
// Go back to the list of devices
NavigationManager.NavigateTo("device-models");
}
diff --git a/src/AzureIoTHub.Portal.Client/Pages/Devices/CreateDevicePage.razor b/src/AzureIoTHub.Portal.Client/Pages/Devices/CreateDevicePage.razor
index 7674f9b22..dd7757230 100644
--- a/src/AzureIoTHub.Portal.Client/Pages/Devices/CreateDevicePage.razor
+++ b/src/AzureIoTHub.Portal.Client/Pages/Devices/CreateDevicePage.razor
@@ -8,7 +8,7 @@
@using AzureIoTHub.Portal.Shared.Models
@using AzureIoTHub.Portal.Shared.Models.v10;
@using AzureIoTHub.Portal.Shared.Models.v10.Filters;
-@using Microsoft.AspNetCore.Components
+@using AzureIoTHub.Portal.Shared.Constants;
@attribute [Authorize]
@inject ISnackbar Snackbar
@@ -22,6 +22,7 @@
@implements IDisposable
+
Create Device
diff --git a/src/AzureIoTHub.Portal.Client/Program.cs b/src/AzureIoTHub.Portal.Client/Program.cs
index c3db5e4a5..6dd47c209 100644
--- a/src/AzureIoTHub.Portal.Client/Program.cs
+++ b/src/AzureIoTHub.Portal.Client/Program.cs
@@ -5,6 +5,7 @@
using AzureIoTHub.Portal.Client;
using AzureIoTHub.Portal.Client.Handlers;
using AzureIoTHub.Portal.Client.Services;
+using AzureIoTHub.Portal.Client.Services.AWS;
using AzureIoTHub.Portal.Models.v10;
using AzureIoTHub.Portal.Settings;
using Blazored.LocalStorage;
@@ -87,6 +88,10 @@
_ = builder.Services.AddScoped();
_ = builder.Services.AddScoped();
+//AWS Dependency injection
+_ = builder.Services.AddScoped();
+
+
await ConfigurePortalSettings(builder);
// Enable loading bar
diff --git a/src/AzureIoTHub.Portal.Client/Services/AWS/IThingTypeClientService.cs b/src/AzureIoTHub.Portal.Client/Services/AWS/IThingTypeClientService.cs
new file mode 100644
index 000000000..35bbc234d
--- /dev/null
+++ b/src/AzureIoTHub.Portal.Client/Services/AWS/IThingTypeClientService.cs
@@ -0,0 +1,17 @@
+// Copyright (c) CGI France. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace AzureIoTHub.Portal.Client.Services.AWS
+{
+ using AzureIoTHub.Portal.Models.v10.AWS;
+
+ public interface IThingTypeClientService
+ {
+ Task CreateThingType(ThingTypeDto thingType);
+
+ Task GetAvatarUrl(string thingTypeId);
+
+ Task ChangeAvatar(string thingTypeId, MultipartFormDataContent avatar);
+
+ }
+}
diff --git a/src/AzureIoTHub.Portal.Client/Services/AWS/ThingTypeClientService.cs b/src/AzureIoTHub.Portal.Client/Services/AWS/ThingTypeClientService.cs
new file mode 100644
index 000000000..9a36037c5
--- /dev/null
+++ b/src/AzureIoTHub.Portal.Client/Services/AWS/ThingTypeClientService.cs
@@ -0,0 +1,39 @@
+// Copyright (c) CGI France. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace AzureIoTHub.Portal.Client.Services.AWS
+{
+ using System.Threading.Tasks;
+ using AzureIoTHub.Portal.Models.v10.AWS;
+
+ public class ThingTypeClientService : IThingTypeClientService
+ {
+ private readonly HttpClient http;
+
+ public ThingTypeClientService(HttpClient http)
+ {
+ this.http = http;
+ }
+
+ public async Task CreateThingType(ThingTypeDto thingType)
+ {
+ var response = await this.http.PostAsJsonAsync("api/aws/thingtypes", thingType);
+ _ = response.EnsureSuccessStatusCode();
+
+ return await response.Content.ReadAsStringAsync();
+ }
+
+ public Task GetAvatarUrl(string thingTypeId)
+ {
+ return this.http.GetStringAsync($"api/aws/thingtypes/{thingTypeId}/avatar");
+ }
+
+ public async Task ChangeAvatar(string thingTypeId, MultipartFormDataContent avatar)
+ {
+ var result = await this.http.PostAsync($"api/aws/thingtypes/{thingTypeId}/avatar", avatar);
+
+ _ = result.EnsureSuccessStatusCode();
+ }
+
+ }
+}
diff --git a/src/AzureIoTHub.Portal.Client/Validators/AWS/ThingTypeValidator.cs b/src/AzureIoTHub.Portal.Client/Validators/AWS/ThingTypeValidator.cs
new file mode 100644
index 000000000..42d1b7725
--- /dev/null
+++ b/src/AzureIoTHub.Portal.Client/Validators/AWS/ThingTypeValidator.cs
@@ -0,0 +1,18 @@
+// Copyright (c) CGI France. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace AzureIoTHub.Portal.Client.Validators
+{
+ using AzureIoTHub.Portal.Models.v10.AWS;
+ using FluentValidation;
+
+ public class ThingTypeValidator : AbstractValidator
+ {
+ public ThingTypeValidator()
+ {
+ _ = RuleFor(x => x.ThingTypeName)
+ .NotEmpty()
+ .WithMessage("Model name is required.");
+ }
+ }
+}
diff --git a/src/AzureIoTHub.Portal.Domain/Entities/AWS/ThingType.cs b/src/AzureIoTHub.Portal.Domain/Entities/AWS/ThingType.cs
new file mode 100644
index 000000000..92b64e511
--- /dev/null
+++ b/src/AzureIoTHub.Portal.Domain/Entities/AWS/ThingType.cs
@@ -0,0 +1,17 @@
+// 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.Domain.Entities.AWS
+{
+ using System.ComponentModel.DataAnnotations;
+ using AzureIoTHub.Portal.Domain.Base;
+
+ public class ThingType : EntityBase
+ {
+ [Required]
+ public string Name { get; set; } = default!;
+ public string? Description { get; set; } = default!;
+ public ICollection? Tags { get; set; } = default!;
+ public ICollection? ThingTypeSearchableAttributes { get; set; } = default!;
+ }
+}
diff --git a/src/AzureIoTHub.Portal.Domain/Entities/AWS/ThingTypeSearchableAtt.cs b/src/AzureIoTHub.Portal.Domain/Entities/AWS/ThingTypeSearchableAtt.cs
new file mode 100644
index 000000000..bdb36c649
--- /dev/null
+++ b/src/AzureIoTHub.Portal.Domain/Entities/AWS/ThingTypeSearchableAtt.cs
@@ -0,0 +1,13 @@
+// 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.Domain.Entities.AWS
+{
+ using AzureIoTHub.Portal.Domain.Base;
+
+ public class ThingTypeSearchableAtt : EntityBase
+ {
+ public string Name { get; set; } = default!;
+
+ }
+}
diff --git a/src/AzureIoTHub.Portal.Domain/Entities/AWS/ThingTypeTag.cs b/src/AzureIoTHub.Portal.Domain/Entities/AWS/ThingTypeTag.cs
new file mode 100644
index 000000000..568a4fa70
--- /dev/null
+++ b/src/AzureIoTHub.Portal.Domain/Entities/AWS/ThingTypeTag.cs
@@ -0,0 +1,14 @@
+// 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.Domain.Entities.AWS
+{
+ using AzureIoTHub.Portal.Domain.Base;
+
+ public class ThingTypeTag : EntityBase
+ {
+ public string Key { get; set; } = default!;
+ public string Value { get; set; } = default!;
+
+ }
+}
diff --git a/src/AzureIoTHub.Portal.Domain/IRepository.cs b/src/AzureIoTHub.Portal.Domain/IRepository.cs
index b80607aae..6d559624c 100644
--- a/src/AzureIoTHub.Portal.Domain/IRepository.cs
+++ b/src/AzureIoTHub.Portal.Domain/IRepository.cs
@@ -18,6 +18,7 @@ public interface IRepository where T : EntityBase
Task GetByIdAsync(object id, params Expression>[] includes);
Task InsertAsync(T obj);
+ Task InsertAndGetIdAsync(T obj);
void Update(T obj);
diff --git a/src/AzureIoTHub.Portal.Domain/Repositories/AWS/IThingTypeRepository.cs b/src/AzureIoTHub.Portal.Domain/Repositories/AWS/IThingTypeRepository.cs
new file mode 100644
index 000000000..b711e2698
--- /dev/null
+++ b/src/AzureIoTHub.Portal.Domain/Repositories/AWS/IThingTypeRepository.cs
@@ -0,0 +1,11 @@
+// Copyright (c) CGI France. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace AzureIoTHub.Portal.Domain.Repositories
+{
+ using AzureIoTHub.Portal.Domain.Entities.AWS;
+
+ public interface IThingTypeRepository : IRepository
+ {
+ }
+}
diff --git a/src/AzureIoTHub.Portal.Domain/Repositories/AWS/IThingTypeSearchableAttRepository.cs b/src/AzureIoTHub.Portal.Domain/Repositories/AWS/IThingTypeSearchableAttRepository.cs
new file mode 100644
index 000000000..d689e8bab
--- /dev/null
+++ b/src/AzureIoTHub.Portal.Domain/Repositories/AWS/IThingTypeSearchableAttRepository.cs
@@ -0,0 +1,11 @@
+// Copyright (c) CGI France. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace AzureIoTHub.Portal.Domain.Repositories.AWS
+{
+ using AzureIoTHub.Portal.Domain.Entities.AWS;
+
+ internal interface IThingTypeSearchableAttRepository : IRepository
+ {
+ }
+}
diff --git a/src/AzureIoTHub.Portal.Domain/Repositories/AWS/IThingTypeTagRepository.cs b/src/AzureIoTHub.Portal.Domain/Repositories/AWS/IThingTypeTagRepository.cs
new file mode 100644
index 000000000..4fc67abba
--- /dev/null
+++ b/src/AzureIoTHub.Portal.Domain/Repositories/AWS/IThingTypeTagRepository.cs
@@ -0,0 +1,11 @@
+// Copyright (c) CGI France. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace AzureIoTHub.Portal.Domain.Repositories.AWS
+{
+ using AzureIoTHub.Portal.Domain.Entities.AWS;
+
+ internal interface IThingTypeTagRepository : IRepository
+ {
+ }
+}
diff --git a/src/AzureIoTHub.Portal.Infrastructure/AzureIoTHub.Portal.Infrastructure.csproj b/src/AzureIoTHub.Portal.Infrastructure/AzureIoTHub.Portal.Infrastructure.csproj
index a5e13a1c0..08f55962c 100644
--- a/src/AzureIoTHub.Portal.Infrastructure/AzureIoTHub.Portal.Infrastructure.csproj
+++ b/src/AzureIoTHub.Portal.Infrastructure/AzureIoTHub.Portal.Infrastructure.csproj
@@ -106,9 +106,9 @@
-
+
-
+
@@ -143,6 +143,7 @@
+
diff --git a/src/AzureIoTHub.Portal.Infrastructure/ConfigHandlerFactory.cs b/src/AzureIoTHub.Portal.Infrastructure/ConfigHandlerFactory.cs
index 07bb00b9b..c6357e8ae 100644
--- a/src/AzureIoTHub.Portal.Infrastructure/ConfigHandlerFactory.cs
+++ b/src/AzureIoTHub.Portal.Infrastructure/ConfigHandlerFactory.cs
@@ -7,6 +7,7 @@ namespace AzureIoTHub.Portal.Infrastructure
using AzureIoTHub.Portal.Domain;
using AzureIoTHub.Portal.Domain.Exceptions;
using AzureIoTHub.Portal.Domain.Shared.Constants;
+ using AzureIoTHub.Portal.Shared.Constants;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
diff --git a/src/AzureIoTHub.Portal.Infrastructure/Managers/AwsDeviceModelImageManager.cs b/src/AzureIoTHub.Portal.Infrastructure/Managers/AwsDeviceModelImageManager.cs
index 663e7c294..756c03cf0 100644
--- a/src/AzureIoTHub.Portal.Infrastructure/Managers/AwsDeviceModelImageManager.cs
+++ b/src/AzureIoTHub.Portal.Infrastructure/Managers/AwsDeviceModelImageManager.cs
@@ -4,8 +4,8 @@
namespace AzureIoTHub.Portal.Infrastructure.Managers
{
using System;
+ using System.Reflection;
using System.Threading.Tasks;
- using Amazon;
using Amazon.S3;
using Amazon.S3.Model;
using Azure;
@@ -40,6 +40,7 @@ public async Task ChangeDeviceModelImageAsync(string deviceModelId, Stre
{
this.logger.LogInformation($"Uploading Image to AWS S3 storage");
+
//Portal must be able to upload images to Amazon S3
var putObjectRequest = new PutObjectRequest
{
@@ -49,7 +50,6 @@ public async Task ChangeDeviceModelImageAsync(string deviceModelId, Stre
ContentType = "image/*",
Headers = {CacheControl = $"max-age={this.configHandler.StorageAccountDeviceModelImageMaxAge}, must-revalidate" }
};
-
var putObjectResponse = await this.s3Client.PutObjectAsync(putObjectRequest);
if (putObjectResponse.HttpStatusCode == System.Net.HttpStatusCode.OK)
@@ -64,25 +64,21 @@ public async Task ChangeDeviceModelImageAsync(string deviceModelId, Stre
var putACLResponse = await this.s3Client.PutACLAsync(putAclRequest);
return putACLResponse.HttpStatusCode == System.Net.HttpStatusCode.OK
- ? ComputeImageUrl(deviceModelId)
+ ? ComputeImageUri(deviceModelId).ToString()
: throw new InternalServerErrorException("Error by setting the image access to public and read-only");
}
else
{
throw new InternalServerErrorException("Error by uploading the image in S3 Storage");
}
-
}
public Uri ComputeImageUri(string deviceModelId)
{
- throw new NotImplementedException();
+ var url = $"https://{this.configHandler.AWSBucketName}.s3.{this.configHandler.AWSRegion}.amazonaws.com/{deviceModelId}";
+ return new Uri(url);
}
- private string ComputeImageUrl(string deviceModelId)
- {
- return $"https://{this.configHandler.AWSBucketName}.s3.{RegionEndpoint.GetBySystemName(this.configHandler.AWSRegion)}.amazonaws.com/{deviceModelId}";
- }
public async Task DeleteDeviceModelImageAsync(string deviceModelId)
{
@@ -107,13 +103,17 @@ public async Task DeleteDeviceModelImageAsync(string deviceModelId)
public async Task SetDefaultImageToModel(string deviceModelId)
{
this.logger.LogInformation($"Uploading Default Image to AWS S3 storage");
+ var currentAssembly = Assembly.GetExecutingAssembly();
+ var defaultImageStream = currentAssembly
+ .GetManifestResourceStream($"{currentAssembly.GetName().Name}.Resources.{this.imageOptions.Value.DefaultImageName}");
+
//Portal must be able to upload images to Amazon S3
var putObjectRequest = new PutObjectRequest
{
BucketName = this.configHandler.AWSBucketName,
Key = deviceModelId,
- FilePath = $"../Resources/{this.imageOptions.Value.DefaultImageName}",
+ InputStream = defaultImageStream,
ContentType = "image/*", // image content type
Headers = {CacheControl = $"max-age={this.configHandler.StorageAccountDeviceModelImageMaxAge}, must-revalidate" }
@@ -133,7 +133,7 @@ public async Task SetDefaultImageToModel(string deviceModelId)
var putACLResponse = await this.s3Client.PutACLAsync(putAclRequest);
return putACLResponse.HttpStatusCode == System.Net.HttpStatusCode.OK
- ? ComputeImageUrl(deviceModelId)
+ ? ComputeImageUri(deviceModelId).ToString()
: throw new InternalServerErrorException("Error by setting the image access to public and read-only");
}
else
@@ -148,11 +148,15 @@ public async Task InitializeDefaultImageBlob()
this.logger.LogInformation($"Initializing default Image to AWS S3 storage");
+ var currentAssembly = Assembly.GetExecutingAssembly();
+
+ var defaultImageStream = currentAssembly
+ .GetManifestResourceStream($"{currentAssembly.GetName().Name}.Resources.{this.imageOptions.Value.DefaultImageName}");
var putObjectRequest = new PutObjectRequest
{
BucketName = this.configHandler.AWSBucketName,
Key = this.imageOptions.Value.DefaultImageName,
- FilePath = $"../Resources/{this.imageOptions.Value.DefaultImageName}",
+ InputStream = defaultImageStream,
ContentType = "image/*", // image content type
Headers = {CacheControl = $"max-age={this.configHandler.StorageAccountDeviceModelImageMaxAge}, must-revalidate" }
diff --git a/src/AzureIoTHub.Portal.Infrastructure/Managers/DeviceModelImageManager.cs b/src/AzureIoTHub.Portal.Infrastructure/Managers/DeviceModelImageManager.cs
index 00ef9f75f..db405d39b 100644
--- a/src/AzureIoTHub.Portal.Infrastructure/Managers/DeviceModelImageManager.cs
+++ b/src/AzureIoTHub.Portal.Infrastructure/Managers/DeviceModelImageManager.cs
@@ -121,5 +121,6 @@ public async Task SyncImagesCacheControl()
_ = await blobClient.SetHttpHeadersAsync(new BlobHttpHeaders { CacheControl = $"max-age={this.configHandler.StorageAccountDeviceModelImageMaxAge}, must-revalidate" });
}
}
+
}
}
diff --git a/src/AzureIoTHub.Portal.Infrastructure/PortalDbContext.cs b/src/AzureIoTHub.Portal.Infrastructure/PortalDbContext.cs
index 2cd8457bb..12a2bcea8 100644
--- a/src/AzureIoTHub.Portal.Infrastructure/PortalDbContext.cs
+++ b/src/AzureIoTHub.Portal.Infrastructure/PortalDbContext.cs
@@ -3,6 +3,7 @@
namespace AzureIoTHub.Portal.Infrastructure
{
+ using AzureIoTHub.Portal.Domain.Entities.AWS;
using Domain.Entities;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
@@ -22,6 +23,9 @@ public class PortalDbContext : DbContext
public DbSet Concentrators { get; set; }
public DbSet LoRaDeviceTelemetry { get; set; }
public DbSet