diff --git a/.gitignore b/.gitignore index c2ec55e..a16a548 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,10 @@ _textdb/ ## ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore +# .NET C# +appsettings.json + + # User-specific files *.rsuser *.suo diff --git a/Deployment/images/services/tester_app_document_id.png b/Deployment/images/services/tester_app_document_id.png new file mode 100644 index 0000000..7d8d2eb Binary files /dev/null and b/Deployment/images/services/tester_app_document_id.png differ diff --git a/Deployment/images/services/tester_app_ui.png b/Deployment/images/services/tester_app_ui.png new file mode 100644 index 0000000..8ca6132 Binary files /dev/null and b/Deployment/images/services/tester_app_ui.png differ diff --git a/Services/testapp/Choice.cs b/Services/testapp/Choice.cs new file mode 100644 index 0000000..7ec573c --- /dev/null +++ b/Services/testapp/Choice.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT License. +using System.ComponentModel; + +namespace Tester.ConsoleApp +{ + enum Choice + { + [Description("1. List Registered Documents")] + ListRegisteredDocuments, + [Description("2. Register Documents")] + RegisterDocuments, + [Description("3. Delete Registered Documents in Azure")] + DeleteDocuments, + [Description("4. Perform Gap Analysis")] + GapAnalysis, + [Description("5. Get All Gap Analysis Results")] + GetAllGapAnalysisResults, + [Description("6. Perform Benchmark Analysis")] + BenchMarks, + [Description("7. Get All Benchmark Analysis Results")] + GetAllBenchMarksResults, + [Description("8. Test API Connection")] + TestApiConnection, + [Description("9. Exit")] + Exit + } +} diff --git a/Services/testapp/Functions/ApiConnection.cs b/Services/testapp/Functions/ApiConnection.cs new file mode 100644 index 0000000..d15e079 --- /dev/null +++ b/Services/testapp/Functions/ApiConnection.cs @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT License. + +using System.Security.Cryptography.X509Certificates; +using Spectre.Console; + +namespace Tester.ConsoleApp.Functions +{ + public static class ApiConnection + { + public static async void TestConnection(Uri uri) + { + // Create a custom HttpClientHandler to handle SSL certificate validation + var handler = new HttpClientHandler + { + ServerCertificateCustomValidationCallback = (message, cert, chain, sslPolicyErrors) => + { + // Here you can add custom logic to validate the certificate + // For example, you can check the certificate thumbprint, issuer, etc. + // Returning true will bypass the SSL certificate validation + if (cert == null) + { + AnsiConsole.WriteLine("No Server Certificate Found"); + return false; + } + return ValidateCertificate(cert); + } + }; + + // Create an HttpClient using the custom handler + using (var client = new HttpClient(handler)) + { + // Set the base address of the API + client.BaseAddress = uri; + try + { + // Make a GET request to the API + HttpResponseMessage response = await client.GetAsync("/api/endpoint"); + // Check if the response is successful + if (response.IsSuccessStatusCode) + { + // Read the response content + string content = await response.Content.ReadAsStringAsync(); + //AnsiConsole.WriteLine("\nTest Connection Response Content: " + content); + } + AnsiConsole.WriteLine("API Connection is successful."); + } + catch (Exception ex) + { + //AnsiConsole.WriteLine("\nTest Connection Failed with Exception (check your appsettings.json and services): " + ex.Message); + AnsiConsole.WriteLine("Test Connection Failed with Exceptions. Check your appsettings.json and API services."); + } + } + } + + // Validate the SSL certificate + static bool ValidateCertificate(X509Certificate2 cert) + { + // Add custom validation logic here + // For example, check the certificate thumbprint, issuer, expiration date, etc. + //AnsiConsole.WriteLine("Certificate Subject: " + cert.Subject); + //AnsiConsole.WriteLine("Certificate Issuer: " + cert.Issuer); + //AnsiConsole.WriteLine("Certificate Thumbprint: " + cert.Thumbprint); + //AnsiConsole.WriteLine("Certificate Expiration: " + cert.NotAfter); + //AnsiConsole.WriteLine(); // Write a new line + + // Example: Validate the certificate thumbprint. This is just an example, you should use a valid thumbprint. + string expectedThumbprint = "B89BB8B0BEF4B6CF59A472284B4F8F234525302B"; + if (cert.Thumbprint == expectedThumbprint) + { + return true; + } + + // Example: Validate the certificate issuer. This is just an example, you should use a valid issuer. + string expectedIssuer = "Kubernetes Ingress Controller Fake Certificate"; + if (cert.Issuer == expectedIssuer) + { + return true; + } + + // Example: Validate the certificate expiration date + if (DateTime.Now < cert.NotAfter) + { + return true; + } + + // If none of the validation checks pass, return false + return false; + } + } +} diff --git a/Services/testapp/Functions/AppConfig.cs b/Services/testapp/Functions/AppConfig.cs new file mode 100644 index 0000000..f573672 --- /dev/null +++ b/Services/testapp/Functions/AppConfig.cs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT License. + +namespace Tester.ConsoleApp.Functions +{ + public class AppConfig + { + public string JobOwner { get; set; } + public string Type { get; set; } + public string disclosureNumber { get; set; } + public string disclosureName { get; set; } + public string disclosureRequirement { get; set; } + public string disclosureRequirementDetail { get; set; } + public string disclosureAnnex { get; set; } + + // Constructor to initialize properties + public AppConfig(string myJobOwner, string myType, string myDisclosureNumber, string myDisclosureName, string myDisclosureRequirement, string myDisclosureRequirementDetail, string myDisclosureAnnex) + { + JobOwner = myJobOwner; + Type = myType; + disclosureNumber = myDisclosureNumber; + disclosureName = myDisclosureName; + disclosureRequirement = myDisclosureRequirement; + disclosureRequirementDetail = myDisclosureRequirementDetail; + disclosureAnnex = myDisclosureAnnex; + } + + // Parameterless constructor for deserialization + public AppConfig() + { + JobOwner = string.Empty; + Type = string.Empty; + disclosureNumber = string.Empty; + disclosureName = string.Empty; + disclosureRequirement = string.Empty; + disclosureRequirementDetail = string.Empty; + disclosureAnnex = string.Empty; + } + } +} diff --git a/Services/testapp/Functions/BenchMarks.cs b/Services/testapp/Functions/BenchMarks.cs new file mode 100644 index 0000000..7eb4617 --- /dev/null +++ b/Services/testapp/Functions/BenchMarks.cs @@ -0,0 +1,100 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.Extensions.Configuration; +using Newtonsoft.Json; +using RestSharp; +using Spectre.Console; +using System.Text; + +namespace Tester.ConsoleApp.Functions +{ + public static class BenchMarks + { + public static async Task PerformBenchmarks(Uri baseUri, IConfiguration config) + { + + var standardType = string.Empty; + while (standardType != "CSRD" && standardType != "GRI") + { + standardType = AnsiConsole.Ask("CSRD or GRI?:"); + if (standardType != "CSRD" && standardType != "GRI") + { + AnsiConsole.MarkupLine("[red]Invalid input. Valid values: 'CSRD' or 'GRI'.[/]"); + } + } + + AppConfig appConfig = new AppConfig(); + appConfig = Helpers.ConfigHelper.GetAppConfig(standardType, config); + + var client = new HttpClient(); + client.BaseAddress = baseUri; + + var myJobName = AnsiConsole.Ask("Enter a Job Name for Benchmarks Analysis:"); + + // user input for documentIds + var myDocumentIds = new List(); + while (true) + { + var myDocumentId = AnsiConsole.Ask("Enter a document ID or the word 'Finished' to end:"); + if (myDocumentId.Equals("Finished", StringComparison.OrdinalIgnoreCase)) + { + break; + } + myDocumentIds.Add(myDocumentId); + } + var requestBody = new + { + jobName = myJobName, + documentIds = myDocumentIds, + jobOwner = config["AppConfig:JobOwner"] ?? string.Empty, + disclosureNumber = config["AppConfig:disclosureNumber"] ?? string.Empty, + disclosureName = config["AppConfig:disclosureName"] ?? string.Empty, + disclosureRequirement = config["AppConfig:disclosureRequirement"] ?? string.Empty, + disclosureRequirementDetail = config["AppConfig:disclosureRequirementDetail"] ?? string.Empty, + disclosureAnnex = config["AppConfig:disclosureAnnex"] ?? string.Empty + }; + + var json = JsonConvert.SerializeObject(requestBody); + var content = new StringContent(json, Encoding.UTF8, "application/json"); + + var response = await client.PostAsync("ESRS/ESRSDisclosureBenchmarkOnQueue", content); + + if (response.IsSuccessStatusCode) + { + var responseContent = await response.Content.ReadAsStringAsync(); + AnsiConsole.MarkupLine("[green]Benchmark documents request was successful.[/]"); + AnsiConsole.WriteLine(responseContent); + } + else + { + AnsiConsole.MarkupLine("[red]Benchmark documents request failed.[/]"); + AnsiConsole.WriteLine(response.ReasonPhrase ?? "No reason phrase provided."); + } + } + + public static void GetAllBenchmarksResults(Uri uri) + { + var handler = new HttpClientHandler + { + ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true + }; + + var client = new RestClient(new RestClientOptions(uri) + { + ConfigureMessageHandler = _ => handler + }); + + //BaseUrl/ESRS/GetAllESRSBenchmarkResults + var request = new RestRequest("ESRS/GetAllESRSBenchmarkResults", Method.Get); + + RestResponse response = client.Execute(request); + var content = response.Content; + + AnsiConsole.WriteLine("GetAllESRSBenchmarkResults Response Status: " + response.StatusCode); + AnsiConsole.WriteLine("GetAllESRSBenchmarkResults Response Content: " + content); + + } + + } +} diff --git a/Services/testapp/Functions/ConfigHelper.cs b/Services/testapp/Functions/ConfigHelper.cs new file mode 100644 index 0000000..fcfe643 --- /dev/null +++ b/Services/testapp/Functions/ConfigHelper.cs @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.Extensions.Configuration; +using Tester.ConsoleApp.Functions; + +namespace Tester.ConsoleApp.Helpers +{ + public static class ConfigHelper + { + public static AppConfig GetAppConfig(string standardType, IConfiguration config) + { + var appConfig = new AppConfig(); + + if (standardType.Equals("CSRD", StringComparison.OrdinalIgnoreCase)) + { + appConfig.JobOwner = config["CSRD:JobOwner"] ?? string.Empty; + appConfig.Type = config["CSRD:Type"] ?? string.Empty; + appConfig.disclosureName = config["CSRD:disclosureName"] ?? string.Empty; + appConfig.disclosureNumber = config["CSRD:disclosureNumber"] ?? string.Empty; + appConfig.disclosureRequirement = config["CSRD:disclosureRequirement"] ?? string.Empty; + appConfig.disclosureRequirementDetail = config["CSRD:disclosureRequirementDetail"] ?? string.Empty; + } + else if (standardType.Equals("GRI", StringComparison.OrdinalIgnoreCase)) + { + appConfig.JobOwner = config["GRI:JobOwner"] ?? string.Empty; + appConfig.Type = config["GRI:Type"] ?? string.Empty; + appConfig.disclosureName = config["GRI:disclosureName"] ?? string.Empty; + appConfig.disclosureNumber = config["GRI:disclosureNumber"] ?? string.Empty; + appConfig.disclosureRequirement = config["GRI:disclosureRequirement"] ?? string.Empty; + appConfig.disclosureRequirementDetail = config["GRI:disclosureRequirementDetail"] ?? string.Empty; + appConfig.disclosureAnnex = config["GRI:disclosureAnnex"] ?? string.Empty; + } + else + { + throw new ArgumentException($"Invalid input {standardType}. Valid values: 'CSRD' or 'GRI'."); + } + + return appConfig; + } + } +} diff --git a/Services/testapp/Functions/GapAnalysis.cs b/Services/testapp/Functions/GapAnalysis.cs new file mode 100644 index 0000000..1897b92 --- /dev/null +++ b/Services/testapp/Functions/GapAnalysis.cs @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.Extensions.Configuration; +using Spectre.Console; +using RestSharp; + + +namespace Tester.ConsoleApp.Functions +{ + public static class GapAnalysis + { + public static void PerformGapAnalysis(Uri baseUri, IConfiguration config) + { + var standardType = string.Empty; + while (standardType != "CSRD" && standardType != "GRI") + { + standardType = AnsiConsole.Ask("CSRD or GRI?:"); + if (standardType != "CSRD" && standardType != "GRI") + { + AnsiConsole.MarkupLine("[red]Invalid input. Valid values: 'CSRD' or 'GRI'.[/]"); + } + } + + AppConfig appConfig = new AppConfig(); + appConfig = Helpers.ConfigHelper.GetAppConfig(standardType, config); + + var docID = AnsiConsole.Ask("Please enter the document ID for Gap Analysis:"); + // Construct the JSON body + var jsonBody = $@" + {{ + ""JobOwner"": ""{appConfig.JobOwner}"", + ""disclosureName"": ""{appConfig.disclosureName}"", + ""disclosureNumber"": ""{appConfig.disclosureNumber}"", + ""jobOwner"": ""{appConfig.JobOwner}"", + ""disclosureRequirement"": ""{appConfig.disclosureRequirement}"", + ""disclosureRequirementDetail"": ""{appConfig.disclosureRequirementDetail}"", + ""disclosureAnnex"": ""{appConfig.disclosureAnnex}"", + ""documentId"": ""{docID}"" + }}"; + + + // Create a custom HttpClientHandler to ignore SSL certificate errors + var handler = new HttpClientHandler + { + ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true + }; + + var client = new RestClient(new RestClientOptions(baseUri) + { + ConfigureMessageHandler = _ => handler + }); + + ////BaseUrl/ESRS/ESRSGapAnalyzerOnQueue + var request = new RestRequest("ESRS/ESRSGapAnalyzerOnQueue", Method.Post); + + //// Add the JSON body to the request + request.AddStringBody(jsonBody, DataFormat.Json); + + var response = client.Execute(request); + + AnsiConsole.WriteLine("ESRSGapAnalyzerOnQueue Response Status: " + response.StatusCode); + AnsiConsole.WriteLine("Response Content: " + response.Content); + } + + + public static void GetAllGapAnalysisResults(Uri uri) + { + + var handler = new HttpClientHandler + { + ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true + }; + + var client = new RestClient(new RestClientOptions(uri) + { + ConfigureMessageHandler = _ => handler + }); + //BaseUrl/ESRS/GetAllESRSGapAnalysisResults + var request = new RestRequest("ESRS/GetAllESRSGapAnalysisResults", Method.Get); + + RestResponse response = client.Execute(request); + var content = response.Content; + + AnsiConsole.WriteLine("GetAllESRSGapAnalysisResults Response Status: " + response.StatusCode); + AnsiConsole.WriteLine("GetAllESRSGapAnalysisResults Response Content: " + content); + } + } +} diff --git a/Services/testapp/Functions/ManageDocuments.cs b/Services/testapp/Functions/ManageDocuments.cs new file mode 100644 index 0000000..f72d5c2 --- /dev/null +++ b/Services/testapp/Functions/ManageDocuments.cs @@ -0,0 +1,154 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT License. + +using RestSharp; +using Newtonsoft.Json.Linq; +using Spectre.Console; + + +namespace Tester.ConsoleApp.Functions +{ + public static class ManageDocuments + { + public static void ListDocuments(Uri uri) + + { + var handler = new HttpClientHandler + { + ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true + }; + + var client = new RestClient(new RestClientOptions(uri) + { + ConfigureMessageHandler = _ => handler + }); + + //GET / DocumentManager / GetAllDocuments + var request = new RestRequest("DocumentManager/GetAllDocuments", Method.Get); + + RestResponse response = client.Execute(request); + var content = response.Content; + + AnsiConsole.WriteLine("Response Status: " + response.StatusCode); + AnsiConsole.WriteLine("Response Content: " + content); + } + + public static async void RegisterDocument(Uri uri) + { + + // Create a custom HttpClientHandler to ignore SSL certificate errors + var handler = new HttpClientHandler + { + ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true + }; + + var client = new RestClient(new RestClientOptions(uri) + { + ConfigureMessageHandler = _ => handler + }); + + var request = new RestRequest("DocumentManager/RegisterDocument", Method.Post); + + // Prompt user to input the full file path + var filePath = AnsiConsole.Ask(" Please enter the file location/path only:"); + //var filePath = "C:\\temp\\data\\sustainability"; + AnsiConsole.WriteLine($"The File path is: {filePath}"); + + // Prompt user to input the file name + var fileName = AnsiConsole.Ask(" Please enter the file name only:"); + + var fullFilePath = Path.Combine(filePath, fileName); + + if (File.Exists(fullFilePath)) + { + byte[] fileData = File.ReadAllBytes(fullFilePath); + request.AddFile("fileBinary", fileData, fileName, "application/octet-stream"); + } + else + { + AnsiConsole.WriteLine("File not found: " + fullFilePath); + return; + } + + // Prompt user to input tag values + + // Prompt user to input tag values + var classification = AnsiConsole.Ask(" Please enter the Classification which can be the company name or the word competitor:"); + var title = AnsiConsole.Ask(" Please enter the Title which is the company name:"); + var fy = AnsiConsole.Ask(" Please enter the FY such as FY2024:"); + var year = AnsiConsole.Ask(" Please enter the Year such as 2024:"); + var type = AnsiConsole.Ask(" Please enter the Type that can be ESG or Sustainability:"); + + + // Add tags as form data + var tags = $@" + [ + {{ ""key"": ""Classification"", ""value"": ""{classification}"" }}, + {{ ""key"": ""Title"", ""value"": ""{title}"" }}, + {{ ""key"": ""FY"", ""value"": ""{fy}"" }}, + {{ ""key"": ""Year"", ""value"": ""{year}"" }}, + {{ ""key"": ""Type"", ""value"": ""{type}"" }} + ]"; + + request.AddParameter("Tags", tags, ParameterType.GetOrPost); + + RestResponse response = await client.ExecuteAsync(request); + var content = response.Content; + + AnsiConsole.WriteLine("Response Status: " + response.StatusCode); + AnsiConsole.WriteLine("Response Content: " + content); + + // Check if the response is successful and content is not empty + if (response.IsSuccessful && !string.IsNullOrEmpty(content)) + { + try + { + // Parse JSON response + var json = JObject.Parse(content); + AnsiConsole.WriteLine("Title: " + json["title"]); + } + catch (Exception ex) + { + AnsiConsole.WriteLine("Error parsing JSON: " + ex.Message); + } + } + else + { + AnsiConsole.WriteLine("Failed to get a valid response or content is empty."); + AnsiConsole.WriteLine("Status Code: " + response.StatusCode); + AnsiConsole.WriteLine("Error Message: " + response.ErrorMessage); + AnsiConsole.WriteLine("Error Exception: " + response.ErrorException); + } + } + + public static void DeleteDocumentById(Uri uri) + { + var docId = AnsiConsole.Ask(" Please provide document ID:"); + + // Create a custom HttpClientHandler to ignore SSL certificate errors + var handler = new HttpClientHandler + { + ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true + }; + + var client = new RestClient(new RestClientOptions(uri) + { + ConfigureMessageHandler = _ => handler + }); + + //Post / DocumentManager / DeleteDocumentByDocumentId /docId + var request = new RestRequest("DocumentManager/DeleteDocumentByDocumentId", Method.Post); + + request.AddParameter("documentId", docId, ParameterType.GetOrPost); + + RestResponse response = client.Execute(request); + var content = response.Content; + + AnsiConsole.WriteLine("Response Status: " + response.StatusCode); + AnsiConsole.WriteLine("Response Content: " + content); + + } + + } + +} diff --git a/Services/testapp/LICENSE_SpectreConsole b/Services/testapp/LICENSE_SpectreConsole new file mode 100644 index 0000000..19aa70c --- /dev/null +++ b/Services/testapp/LICENSE_SpectreConsole @@ -0,0 +1,27 @@ + Spectre.Console library is used in this test app. Spectre.Console is under the MIT License. More details below: + + Spectre.Console License + https://github.com/spectreconsole/spectre.console/blob/main/LICENSE.md + + MIT License + + Copyright (c) 2020 Patrik Svensson + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + diff --git a/Services/testapp/Prerequisites.md b/Services/testapp/Prerequisites.md new file mode 100644 index 0000000..750b12f --- /dev/null +++ b/Services/testapp/Prerequisites.md @@ -0,0 +1,32 @@ +# Project set up for new .NET C# users +## Prerequisites + +1. .NET 8 SDK: Ensure that you have the .NET 8 SDK installed on your machine. You can download it from [.NET Download page](https://dotnet.microsoft.com/en-us/download/dotnet/8.0). +2. Install either Visual Studio Code or Visual Studio if not already installed. + + Visual Studio Code: Install [Visual Studio Code](https://code.visualstudio.com/), Install the following extensions: [C#](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp), [NuGet Package Manager](https://marketplace.visualstudio.com/items?itemName=jmrog.vscode-nuget-package-manager) + + Visual Studio: Install [Visual Studio](https://visualstudio.microsoft.com/vs/). During installation, ensure you select the ".NET desktop development" workload. + +## Instructions for Visual Studio Code + +1. Open the Project: Open Visual Studio Code and select File > Open Folder..., then navigate to the Tester-App folder and open it. +2. Restore Dependencies: Open the integrated terminal in Visual Studio Code (View > Terminal) and run the following command to restore the project dependencies: `dotnet restore` + +3. Build the Project: In the terminal, run the following command to build the project: `dotnet build` + +4. Run the Project: To run the project, use the following command: `dotnet run --project Tester-App` + +## Instructions for Visual Studio + +1. Open the Solution: Open Visual Studio 2022 or later and select File > Open > Project/Solution..., then navigate to the Tester-App folder and open the Tester-App.sln file. + +2. Restore Dependencies: Visual Studio will automatically restore the project dependencies when you open the solution. If it doesn't, you can manually restore them by right-clicking on the solution in the Solution Explorer and selecting Restore NuGet Packages. +3. Build the Project: To build the project, select Build > Build Solution from the menu or press Ctrl+Shift+B. +4. Run the Project: To run the project, press F5 or select Debug > Start Debugging from the menu. + +## Additional Notes + +Ensure that the `appsettings.json` and `appconfig.json` files are present in the output directory. These files are configured to be copied to the output directory during the build process. + +If you encounter any issues with missing dependencies or build errors, ensure that all required NuGet packages are restored and that you have the correct version of the .NET SDK installed. diff --git a/Services/testapp/Program.cs b/Services/testapp/Program.cs new file mode 100644 index 0000000..a91fc48 --- /dev/null +++ b/Services/testapp/Program.cs @@ -0,0 +1,126 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT License. + +using Spectre.Console; +using Microsoft.Extensions.Configuration; +using System.ComponentModel; +using System.Reflection; +using Tester.ConsoleApp.Functions; +using Microsoft.Extensions.Primitives; + +// This program implements a console application that allows the user to interact with a REST API. +// The user can list registered documents, register new documents, delete documents, +// perform gap analysis, and perform benchmarks. Below REST APIs are tested +//GET / DocumentManager / GetAllDocuments +//POST / DocumentManager / DeleteDocumentByDocumentId / docId +//POST / DocumentManager / RegisterDocument +//POST / ESRS / ESRSDisclosureBenchmarkOnQueue +//GET / ESRS / GetAllESRSDisclosureBenchmarkResults +//POST / ESRS / ESRSGapAnalyzerOnQueue +//GET / ESRS / GetAllESRSBenchmarkResults + + +namespace Tester.ConsoleApp +{ + class Program + { + + static async Task Main(string[] args) + { + + Uri uri; + var config = new ConfigurationBuilder() + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddJsonFile("appconfig.json", optional: false, reloadOnChange: true) + .Build(); + + var urlString = config["UrlSettings:UrlString"]; + + try + { + uri = new Uri(urlString ?? throw new InvalidOperationException("URL string cannot be null")); + } + catch (Exception ex) + { + AnsiConsole.WriteLine($"Error: {ex.Message}"); + return; + } + + // Subscribe to configuration changes + ChangeToken.OnChange(() => config.GetReloadToken(), () => + { + AnsiConsole.WriteLine("Configuration has been reloaded."); + // Handle any specific actions you need to take when the configuration is reloaded + }); + + while (true) + { + var selection = AnsiConsole.Prompt( + new SelectionPrompt() + .Title("\nWhat would you like to do (use arrow keys to move cursor)?") + .PageSize(10) + .AddChoices(new[] { + Choice.ListRegisteredDocuments, + Choice.RegisterDocuments, + Choice.DeleteDocuments, + Choice.GapAnalysis, + Choice.GetAllGapAnalysisResults, + Choice.BenchMarks, + Choice.GetAllBenchMarksResults, + Choice.TestApiConnection, + Choice.Exit + }) + .UseConverter(choice => + { + var fieldInfo = choice.GetType().GetField(choice.ToString()); + var attribute = fieldInfo?.GetCustomAttribute(typeof(DescriptionAttribute)) as DescriptionAttribute; + + return attribute?.Description ?? choice.ToString(); + })); + + switch (selection) + { + case Choice.ListRegisteredDocuments: + ManageDocuments.ListDocuments(uri); + break; + + case Choice.RegisterDocuments: + ManageDocuments.RegisterDocument(uri); + break; + + case Choice.DeleteDocuments: + ManageDocuments.DeleteDocumentById(uri); + break; + + case Choice.GapAnalysis: + GapAnalysis.PerformGapAnalysis(uri, config); + break; + + case Choice.GetAllGapAnalysisResults: + GapAnalysis.GetAllGapAnalysisResults(uri); + break; + + case Choice.BenchMarks: + await BenchMarks.PerformBenchmarks(uri, config); + break; + + case Choice.GetAllBenchMarksResults: + BenchMarks.GetAllBenchmarksResults(uri); + break; + + case Choice.TestApiConnection: + ApiConnection.TestConnection(uri); + break; + + case Choice.Exit: + return; + + default: + AnsiConsole.WriteLine("Sorry. We didn't understand your selection."); + break; + }; + AnsiConsole.WriteLine(); + } + } + } +} diff --git a/Services/testapp/README.md b/Services/testapp/README.md new file mode 100644 index 0000000..fb87aff --- /dev/null +++ b/Services/testapp/README.md @@ -0,0 +1,20 @@ +# Test App to check on Azure Services +This .NET C# Console app was developed using .NET 8 and Visual Studio 2022. You can use either Visual Studio or Visual Studio Code to open the `Tester-App.sln` to build and run the Console App. If you are new to .NET and C#, please review [prerequisites](./Prerequisites.md) to set up your project. + +When you run the app, you will see below user interface which summarizes the functions of this app: + +![Tester App UI](../../Deployment/images/services/tester_app_ui.png) + +## Steps to set up the app and then test + +**Step 1**: Use Visual Studio or Visual Studio Code to open the `Tester-App.sln`. + +**Step 2**: Use the format of `sample_appsettings.json` to create your `appsettings.json` in the root folder, and replace the value of `UrlString` with the actual API URL of the Azure services you deployed using the [deployAzureResources.ps1](https://github.com/microsoft/Comparative-Analysis-for-Sustainability-Solution-Accelerator/blob/gri-prompt/Deployment/scripts/deployAzureResources.ps1). You will need to make sure `appsettings.json` is copied to your runtime environment in your project build process. + +For your connivence, a working `appconfig.json` is created for you, ready to be used. It has one set of values for GRI and another for CSRD. Later, you can replace with different values in the `Disclosure Details.xslx` located in the `Client` directory of this solution accelerator. + +**Step 3**: Build and run the .NET C# solution. Use your keyboard down and up arrow keys to select the function to test . + +Please note that you will need to register documents first before you can perform gap analysis or benchmark analysis. Once the documents are registered, you can find the document IDs in the azure storage account you deployed with your backend azure resources, as illustrated in below figure. **You will be asked to provide document IDs when performing gap analysis or benchmark analysis.** + +![Tester App UI](../../Deployment/images/services/tester_app_document_id.png) diff --git a/Services/testapp/Tester-App.csproj b/Services/testapp/Tester-App.csproj new file mode 100644 index 0000000..c39d9de --- /dev/null +++ b/Services/testapp/Tester-App.csproj @@ -0,0 +1,40 @@ + + + + Exe + net8.0 + Tester_App + enable + enable + + + + + + + + + + + + + + + + + + + Always + + + Always + + + Never + + + Never + + + + diff --git a/Services/testapp/Tester-App.sln b/Services/testapp/Tester-App.sln new file mode 100644 index 0000000..cdc2a26 --- /dev/null +++ b/Services/testapp/Tester-App.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35514.174 d17.12 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tester-App", "Tester-App.csproj", "{0707BF42-6512-4159-912A-B214B3F886E0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0707BF42-6512-4159-912A-B214B3F886E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0707BF42-6512-4159-912A-B214B3F886E0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0707BF42-6512-4159-912A-B214B3F886E0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0707BF42-6512-4159-912A-B214B3F886E0}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Services/testapp/appconfig.json b/Services/testapp/appconfig.json new file mode 100644 index 0000000..1151b01 --- /dev/null +++ b/Services/testapp/appconfig.json @@ -0,0 +1,20 @@ +{ + "GRI": { + "JobOwner": "Sustainability Manager", + "Type": "GRI", + "disclosureNumber": "GRI Disclosure 2-1", + "disclosureName": "Disclosure of organizational details", + "disclosureRequirement": "Disclosure of reporting in accordance with the GRI Standards", + "disclosureRequirementDetail": "The reporting organization shall provide contextual information about its organization to allow for a better understanding of its sustainability impacts and performance.", + "disclosureAnnex": "Reporting organization details includes information such as:\n- Legal name and form of the organization\n- Location of the organization's headquarters\n- The countries where the organization operates, and the names of the countries where it has significant operations and/or that are specifically relevant to the sustainability topics covered in the report\n- Nature of ownership and legal form\n- Markets served (including geographic breakdown, sectors served, and types of customers and beneficiaries)\n- The scale of the organization (e.g., number of employees, net sales, total capitalization broken down in terms of debt and equity, and quantity of products or services provided)\nThe details provided should offer a comprehensive context about the organization's size, structure, market presence, and location, which will aid stakeholders in understanding the scope and impact of the organization's activities on sustainability topics." + }, + "CSRD": { + "JobOwner": "Chief Sustainability Officer", + "Type": "CSRD", + "disclosureNumber": "CSRD E1-1 14", + "disclosureName": "Transition plan for climate change mitigation", + "disclosureRequirement": "Disclosure of transition plan for climate change mitigation", + "disclosureRequirementDetail": "The undertaking shall disclose its transition plan for climate change mitigation.", + "disclosureAnnex": "A transition plan relates to the undertaking’s efforts in climate change mitigation. When disclosing its transition plan, the undertaking is expected to provide a high-level explanation of how it will adjust its strategy and business model to ensure compatibility with the transition to a sustainable economy and with the limiting of global warming to 1.5°C in line with the Paris Agreement (or an updated inter national agreement on climate change) and the objective of achieving climate neutrality by 2050 with no or limited overshoot as established in Regulation (EU) 2021/1119 (European Climate Law), and where applicable, how it will adjust its exposure to coal, and oil and gas-related activities. " + } +} diff --git a/Services/testapp/sample_appsettings.json b/Services/testapp/sample_appsettings.json new file mode 100644 index 0000000..041524e --- /dev/null +++ b/Services/testapp/sample_appsettings.json @@ -0,0 +1,5 @@ +{ + "UrlSettings": { + "UrlString": "https://esg###analysis-###.centralus.cloudapp.azure.com" + } +} \ No newline at end of file