From 3ae1037f015344073009bdd011852d8584e1b2f7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?DELAGE=20Rapha=C3=ABl?=
 <36408929+delager@users.noreply.github.com>
Date: Mon, 27 Mar 2023 17:12:04 +0200
Subject: [PATCH] #1793 Add AmazonIoTClient and AmazonIotDataClient
 configurations (#1951)

* #1793 Add AmazonIoTClient and AmazonIotDataClient configurations

- Add "CloudProvider" in configuration
- Add AmazonIoTClient and AmazonIotDataClient configurations when CloudProvider is AWS

* Update src/AzureIoTHub.Portal.Server/Startup.cs

Add OrdinalIgnoreCase string comparison for CloudProvider

Co-authored-by: Kevin BEAUGRAND <9513635+kbeaugrand@users.noreply.github.com>

* Update src/AzureIoTHub.Portal.Server/Startup.cs

- Local variables camelCased
- Keep asynchronous
- Let the DI container build instances when it is called

Co-authored-by: Kevin BEAUGRAND <9513635+kbeaugrand@users.noreply.github.com>

* #1793 update src\AzureIoTHub.Portal.Server\Startup.cs missing reference

- Missing reference

* #1793 Add CloudProvider for E2E tests

Add CloudProvider config for E2E tests
Add Azure in the current test pipeline (a new AWS test pipeline will be created in the next feature)

---------

Co-authored-by: Kevin BEAUGRAND <9513635+kbeaugrand@users.noreply.github.com>
---
 .github/workflows/ci-tests.yml                |  2 ++
 .../ConfigHandler.cs                          |  1 +
 .../Shared/Constants/CloudProviders.cs        | 13 ++++++++++++
 .../ConfigHandlerBase.cs                      |  2 ++
 .../DevelopmentConfigHandler.cs               |  2 ++
 .../ProductionConfigHandler.cs                |  2 ++
 .../AzureIoTHub.Portal.Server.csproj          |  2 ++
 src/AzureIoTHub.Portal.Server/Startup.cs      | 20 +++++++++++++++++++
 .../DevelopmentConfigHandlerTests.cs          |  1 +
 .../ProductionConfigHandlerTests.cs           |  1 +
 src/e2e-docker-compose.yml                    |  1 +
 11 files changed, 47 insertions(+)
 create mode 100644 src/AzureIoTHub.Portal.Domain/Shared/Constants/CloudProviders.cs

diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml
index 946df4aa2..1de8bd307 100644
--- a/.github/workflows/ci-tests.yml
+++ b/.github/workflows/ci-tests.yml
@@ -111,6 +111,8 @@ jobs:
         IOTHUB__EVENTHUB__ENDPOINT: ${{ secrets.IOTHUB__EVENTHUB__ENDPOINT }}
         LORAKEYMANAGEMENT__CODE: ${{ secrets.LORAKEYMANAGEMENT__CODE }}
         IDEAS__AUTHENTICATION__TOKEN: ${{ secrets.IDEAS__AUTHENTICATION__TOKEN }}
+        
+        CLOUDPROVIDER: Azure
 
     - name: Wait until portal is up
       run: |
diff --git a/src/AzureIoTHub.Portal.Domain/ConfigHandler.cs b/src/AzureIoTHub.Portal.Domain/ConfigHandler.cs
index 5409d29a9..957f41ed6 100644
--- a/src/AzureIoTHub.Portal.Domain/ConfigHandler.cs
+++ b/src/AzureIoTHub.Portal.Domain/ConfigHandler.cs
@@ -74,6 +74,7 @@ public abstract class ConfigHandler
         public abstract string MySQLConnectionString { get; }
 
         public abstract string DbProvider { get; }
+        public abstract string CloudProvider { get; }
         public abstract string AWSAccess { get; }
         public abstract string AWSAccessSecret { get; }
         public abstract string AWSRegion { get; }
diff --git a/src/AzureIoTHub.Portal.Domain/Shared/Constants/CloudProviders.cs b/src/AzureIoTHub.Portal.Domain/Shared/Constants/CloudProviders.cs
new file mode 100644
index 000000000..c3a50d661
--- /dev/null
+++ b/src/AzureIoTHub.Portal.Domain/Shared/Constants/CloudProviders.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.Shared.Constants
+{
+
+    public static class CloudProviders
+    {
+        public const string Azure = "Azure";
+
+        public const string AWS = "AWS";
+    }
+}
diff --git a/src/AzureIoTHub.Portal.Infrastructure/ConfigHandlerBase.cs b/src/AzureIoTHub.Portal.Infrastructure/ConfigHandlerBase.cs
index 515bb4ca7..fe173c382 100644
--- a/src/AzureIoTHub.Portal.Infrastructure/ConfigHandlerBase.cs
+++ b/src/AzureIoTHub.Portal.Infrastructure/ConfigHandlerBase.cs
@@ -50,6 +50,8 @@ internal abstract class ConfigHandlerBase : ConfigHandler
         internal const string IdeasAuthenticationHeaderKey = "Ideas:Authentication:Header";
         internal const string IdeasAuthenticationTokenKey = "Ideas:Authentication:Token";
 
+        internal const string CloudProviderKey = "CloudProvider";
+
         internal const string AWSAccessKey = "AWS:Access";
         internal const string AWSAccessSecretKey = "AWS:AccessSecret";
         internal const string AWSRegionKey = "AWS:Region";
diff --git a/src/AzureIoTHub.Portal.Infrastructure/DevelopmentConfigHandler.cs b/src/AzureIoTHub.Portal.Infrastructure/DevelopmentConfigHandler.cs
index 1e9cdb170..2dd64285f 100644
--- a/src/AzureIoTHub.Portal.Infrastructure/DevelopmentConfigHandler.cs
+++ b/src/AzureIoTHub.Portal.Infrastructure/DevelopmentConfigHandler.cs
@@ -82,6 +82,8 @@ internal DevelopmentConfigHandler(IConfiguration config)
 
         public override string DbProvider => this.config.GetValue(DbProviderKey, DbProviders.PostgreSQL);
 
+        public override string CloudProvider => this.config[CloudProviderKey];
+
         public override string AWSAccess => this.config[AWSAccessKey];
         public override string AWSAccessSecret => this.config[AWSAccessSecretKey];
         public override string AWSRegion => this.config[AWSRegionKey];
diff --git a/src/AzureIoTHub.Portal.Infrastructure/ProductionConfigHandler.cs b/src/AzureIoTHub.Portal.Infrastructure/ProductionConfigHandler.cs
index b0f253f79..ba04bd317 100644
--- a/src/AzureIoTHub.Portal.Infrastructure/ProductionConfigHandler.cs
+++ b/src/AzureIoTHub.Portal.Infrastructure/ProductionConfigHandler.cs
@@ -82,6 +82,8 @@ internal ProductionConfigHandler(IConfiguration config)
         public override string IdeasAuthenticationHeader => this.config.GetValue(IdeasAuthenticationHeaderKey, "Ocp-Apim-Subscription-Key");
         public override string IdeasAuthenticationToken => this.config.GetValue(IdeasAuthenticationTokenKey, string.Empty);
 
+        public override string CloudProvider => this.config[CloudProviderKey];
+
         public override string AWSAccess => this.config[AWSAccessKey];
         public override string AWSAccessSecret => this.config[AWSAccessSecretKey];
         public override string AWSRegion => this.config[AWSRegionKey];
diff --git a/src/AzureIoTHub.Portal.Server/AzureIoTHub.Portal.Server.csproj b/src/AzureIoTHub.Portal.Server/AzureIoTHub.Portal.Server.csproj
index d269ebf10..69b6ccc2c 100644
--- a/src/AzureIoTHub.Portal.Server/AzureIoTHub.Portal.Server.csproj
+++ b/src/AzureIoTHub.Portal.Server/AzureIoTHub.Portal.Server.csproj
@@ -35,6 +35,8 @@
   <ItemGroup>
     <PackageReference Include="AutoMapper" Version="12.0.1" />
     <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.0" />
+    <PackageReference Include="AWSSDK.IoT" Version="3.7.105.14" />
+    <PackageReference Include="AWSSDK.IotData" Version="3.7.101.74" />
     <PackageReference Include="Azure.Data.Tables" Version="12.8.0" />
     <PackageReference Include="Azure.Messaging.EventHubs" Version="5.8.1" />
     <PackageReference Include="Azure.Messaging.EventHubs.Processor" Version="5.8.1" />
diff --git a/src/AzureIoTHub.Portal.Server/Startup.cs b/src/AzureIoTHub.Portal.Server/Startup.cs
index eee420db3..ce066c009 100644
--- a/src/AzureIoTHub.Portal.Server/Startup.cs
+++ b/src/AzureIoTHub.Portal.Server/Startup.cs
@@ -6,6 +6,9 @@ namespace AzureIoTHub.Portal.Server
     using System;
     using System.IO;
     using System.Threading.Tasks;
+    using Amazon;
+    using Amazon.IoT;
+    using Amazon.IotData;
     using AzureIoTHub.Portal.Application.Managers;
     using AzureIoTHub.Portal.Application.Services;
     using AzureIoTHub.Portal.Application.Startup;
@@ -77,6 +80,23 @@ public void ConfigureServices(IServiceCollection services)
             _ = services.AddSingleton(new PortalMetric());
             _ = services.AddSingleton(new LoRaGatewayIDList());
 
+            if (configuration.CloudProvider.Equals(CloudProviders.AWS, StringComparison.Ordinal))
+            {
+                _ = services.AddSingleton(() => new AmazonIoTClient(configuration.AWSAccess, configuration.AWSAccessSecret, RegionEndpoint.GetBySystemName(configuration.AWSRegion)));
+                _ = services.AddSingleton(async sp =>
+                {
+                    var endpoint = await sp.GetService<AmazonIoTClient>().DescribeEndpointAsync(new Amazon.IoT.Model.DescribeEndpointRequest
+                    {
+                        EndpointType = "iot:Data-ATS"
+                    });
+
+                    return new AmazonIotDataClient(configuration.AWSAccess, configuration.AWSAccessSecret, new AmazonIotDataConfig
+                    {
+                        ServiceURL = $"https://{endpoint.EndpointAddress}"
+                    });
+                });
+            }
+
             _ = services.AddRazorPages();
 
             _ = services.AddTransient<IExportManager, ExportManager>();
diff --git a/src/AzureIoTHub.Portal.Tests.Unit/Infrastructure/DevelopmentConfigHandlerTests.cs b/src/AzureIoTHub.Portal.Tests.Unit/Infrastructure/DevelopmentConfigHandlerTests.cs
index 5ed5eedc8..bb62f55ce 100644
--- a/src/AzureIoTHub.Portal.Tests.Unit/Infrastructure/DevelopmentConfigHandlerTests.cs
+++ b/src/AzureIoTHub.Portal.Tests.Unit/Infrastructure/DevelopmentConfigHandlerTests.cs
@@ -51,6 +51,7 @@ private DevelopmentConfigHandler CreateDevelopmentConfigHandler()
         [TestCase(ConfigHandlerBase.AWSAccessKey, nameof(ConfigHandlerBase.AWSAccess))]
         [TestCase(ConfigHandlerBase.AWSAccessSecretKey, nameof(ConfigHandlerBase.AWSAccessSecret))]
         [TestCase(ConfigHandlerBase.AWSRegionKey, nameof(ConfigHandlerBase.AWSRegion))]
+        [TestCase(ConfigHandlerBase.CloudProviderKey, nameof(ConfigHandlerBase.CloudProvider))]
         public void SettingsShouldGetValueFromAppSettings(string configKey, string configPropertyName)
         {
             // Arrange
diff --git a/src/AzureIoTHub.Portal.Tests.Unit/Infrastructure/ProductionConfigHandlerTests.cs b/src/AzureIoTHub.Portal.Tests.Unit/Infrastructure/ProductionConfigHandlerTests.cs
index df3eae374..217020387 100644
--- a/src/AzureIoTHub.Portal.Tests.Unit/Infrastructure/ProductionConfigHandlerTests.cs
+++ b/src/AzureIoTHub.Portal.Tests.Unit/Infrastructure/ProductionConfigHandlerTests.cs
@@ -76,6 +76,7 @@ public void SecretsShouldGetValueFromConnectionStrings(string configKey, string
         [TestCase(ConfigHandlerBase.AWSAccessKey, nameof(ConfigHandlerBase.AWSAccess))]
         [TestCase(ConfigHandlerBase.AWSAccessSecretKey, nameof(ConfigHandlerBase.AWSAccessSecret))]
         [TestCase(ConfigHandlerBase.AWSRegionKey, nameof(ConfigHandlerBase.AWSRegion))]
+        [TestCase(ConfigHandlerBase.CloudProviderKey, nameof(ConfigHandlerBase.CloudProvider))]
         public void SettingsShouldGetValueFromAppSettings(string configKey, string configPropertyName)
         {
             // Arrange
diff --git a/src/e2e-docker-compose.yml b/src/e2e-docker-compose.yml
index 4cb503cf9..a72e9a494 100644
--- a/src/e2e-docker-compose.yml
+++ b/src/e2e-docker-compose.yml
@@ -46,6 +46,7 @@ services:
       IoTHub__EventHub__Endpoint: "${IOTHUB__EVENTHUB__ENDPOINT}"
       LoRaKeyManagement__Code: "${LORAKEYMANAGEMENT__CODE}"
       Ideas__Authentication__Token: "${IDEAS__AUTHENTICATION__TOKEN}"
+      CloudProvider: "${CLOUDPROVIDER}"
     depends_on:
       - "database"
     ports: