diff --git a/src/KPI Generator/KPI Generator.csproj b/src/KPI Generator/KPI Generator.csproj index 037b55d6f69ab1fbe0e6b35f23aeb3c0ab239673..b74663d2503e5ae2003c2aadc097763b53e6ac74 100644 --- a/src/KPI Generator/KPI Generator.csproj +++ b/src/KPI Generator/KPI Generator.csproj @@ -21,9 +21,11 @@ <PackageReference Include="AutoMapper" Version="12.0.1" /> <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" /> <PackageReference Include="CommandLineParser" Version="2.9.1" /> - <PackageReference Include="Coscine.ApiClient" Version="1.2.1" /> + <PackageReference Include="Coscine.ApiClient" Version="1.3.0-issue-2666-admin0005" /> + <PackageReference Include="dotNetRdf.Core" Version="3.1.1" /> <PackageReference Include="GitLabApiClient" Version="1.8.1-beta.5" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" /> + <PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" /> <PackageReference Include="NLog" Version="5.1.0" /> <PackageReference Include="NLog.Extensions.Logging" Version="5.2.0" /> <PackageReference Include="Polly.Extensions.Http" Version="3.0.0" /> diff --git a/src/KPI Generator/Reportings/ApplicationProfile/ReturnObject.cs b/src/KPI Generator/Models/ApplicationProfileReport.cs similarity index 66% rename from src/KPI Generator/Reportings/ApplicationProfile/ReturnObject.cs rename to src/KPI Generator/Models/ApplicationProfileReport.cs index 6369623568ffb4c02f44ce096d5533aafa76fe59..7e517f97824547a01f1d5c705aadb4bc0044550a 100644 --- a/src/KPI Generator/Reportings/ApplicationProfile/ReturnObject.cs +++ b/src/KPI Generator/Models/ApplicationProfileReport.cs @@ -1,11 +1,11 @@ -namespace Coscine.KpiGenerator.Reportings.ApplicationProfile; +namespace Coscine.KpiGenerator.Models; /// <summary> /// Object containing the JSON structure for the reporting /// </summary> -public class ReturnObject +public class ApplicationProfileReport { - public List<string?> Titles { get; set; } = new(); + public List<string> Titles { get; set; } = new(); public string Uri { get; set; } = null!; public string? Publisher { get; set; } = null!; public string? Rights { get; set; } = null!; diff --git a/src/KPI Generator/Models/ConfigurationModels/KpiConfiguration.cs b/src/KPI Generator/Models/ConfigurationModels/KpiConfiguration.cs new file mode 100644 index 0000000000000000000000000000000000000000..489bd9dcbfa4e197852d1d7b55a848aec6771550 --- /dev/null +++ b/src/KPI Generator/Models/ConfigurationModels/KpiConfiguration.cs @@ -0,0 +1,59 @@ +namespace Coscine.KpiGenerator.Models.ConfigurationModels; + +/// <summary> +/// Represents the configuration settings for JSON Web Tokens (JWT) used in the application. +/// </summary> +public class KpiConfiguration +{ + /// <summary> + /// The section name in the configuration file. + /// </summary> + public static readonly string Section = "KpiConfiguration"; + + /// <summary> + /// Indicates whether the issuer should be validated during token validation. + /// </summary> + public ApplicationProfileKpi ApplicationProfileKpi { get; set; } = null!; + + /// <summary> + /// Indicates whether the audience should be validated during token validation. + /// </summary> + public ProjectKpi ProjectKpi { get; set; } = null!; + + /// <summary> + /// Indicates whether the lifetime should be validated during token validation. + /// </summary> + public ResourceKpi ResourceKpi { get; set; } = null!; + + /// <summary> + /// Indicates whether the issuer signing key should be validated during token validation. + /// </summary> + public SystemKpi SystemKpi { get; set; } = null!; + + /// <summary> + /// A list of valid issuers for the JWT. + /// </summary> + public UserKpi UserKpi { get; set; } = null!; +} + +public record ApplicationProfileKpi(string FileName); + +public record Maintenance +{ + public string Url { get; init; } = null!; + public string Username { get; init; } = null!; + public string Password { get; init; } = null!; + public string BasicAuthToken { get; init; } = null!; +} + +public record ProjectKpi(string FileName); + +public record ResourceKpi(string FileName); + +public record SystemKpi +{ + public string FileName { get; set; } = null!; + public Maintenance Maintenance { get; set; } = null!; +} + +public record UserKpi(string FileName); diff --git a/src/KPI Generator/Models/MaintenanceReport.cs b/src/KPI Generator/Models/MaintenanceReport.cs new file mode 100644 index 0000000000000000000000000000000000000000..eaa24ab40331c485723615ae525a35905baaf0f7 --- /dev/null +++ b/src/KPI Generator/Models/MaintenanceReport.cs @@ -0,0 +1,22 @@ +using System.Text.Json.Serialization; + +namespace Coscine.KpiGenerator.Models; + +public record MaintenanceReport +{ + [JsonPropertyName("id")] + public int Id { get; init; } + [JsonPropertyName("title")] + public string Title { get; init; } = null!; + [JsonPropertyName("short_message")] + public string ShortMessage { get; init; } = null!; + [JsonPropertyName("message")] + public string Message { get; init; } = null!; + [JsonPropertyName("status")] + public string Status { get; init; } = null!; + [JsonPropertyName("start_at")] + public DateTime? StartAt { get; init; } = null!; + [JsonPropertyName("end_at")] + public DateTime? EndAt { get; init; } = null!; +} + diff --git a/src/KPI Generator/Models/SystemReport.cs b/src/KPI Generator/Models/SystemReport.cs new file mode 100644 index 0000000000000000000000000000000000000000..45a0bb3535de90b0d289114e3ab0d16dc0fcaf18 --- /dev/null +++ b/src/KPI Generator/Models/SystemReport.cs @@ -0,0 +1,9 @@ +namespace Coscine.KpiGenerator.Models; + +/// <summary> +/// Object containing the JSON structure for the reporting +/// </summary> +public record SystemReport +{ + public IReadOnlyList<MaintenanceReport>? Banners { get; init; } = new List<MaintenanceReport>(); +} \ No newline at end of file diff --git a/src/KPI Generator/Program.cs b/src/KPI Generator/Program.cs index 14901767dd2707d903ac377215ba5316cd6f17c4..0fe1c8d0adb0b9fce8d3184e9fec8ca3d4cc886e 100644 --- a/src/KPI Generator/Program.cs +++ b/src/KPI Generator/Program.cs @@ -1,5 +1,6 @@ using CommandLine; using Coscine.KpiGenerator.Logging; +using Coscine.KpiGenerator.Models.ConfigurationModels; using Coscine.KpiGenerator.Reportings.Project; using Coscine.KpiGenerator.Reportings.Resource; using GitLabApiClient; @@ -39,8 +40,8 @@ public class Program (ProjectReportingOptions opts) => _serviceProvider.GetRequiredService<ProjectReporting>().RunAsync(opts).Result, (ResourceReportingOptions opts) => _serviceProvider.GetRequiredService<ResourceReporting>().RunAsync(opts).Result, (UserReportingOptions opts) => _serviceProvider.GetRequiredService<UserReporting>().RunAsync(opts).Result, - //(ApplicationProfileReportingOptions opts) => _serviceProvider.GetRequiredService<ApplicationProfileReporting>().Run(opts), - //(SystemReportingOptions opts) => _serviceProvider.GetRequiredService<SystemReporting>().Run(opts), + (SystemReportingOptions opts) => _serviceProvider.GetRequiredService<SystemReporting>().RunAsync(opts).Result, + (ApplicationProfileReportingOptions opts) => _serviceProvider.GetRequiredService<ApplicationProfileReporting>().RunAsync(opts).Result, HandleParseError ); @@ -82,9 +83,9 @@ public class Program configBuilder.Sources.Clear(); // Add Consul as a configuration source - configBuilder + var configuration = configBuilder .AddConsul( - "coscine/Coscine.Api/appsettings", + "coscine/Coscine.Infrastructure/KpiGenerator/appsettings", options => { options.ConsulConfigurationOptions = @@ -95,14 +96,18 @@ public class Program options.OnLoadException = exceptionContext => exceptionContext.Ignore = true; } ) - .AddEnvironmentVariables(); - - // Build the configuration - var configuration = configBuilder.Build(); - + .AddEnvironmentVariables() + .Build(); + var services = new ServiceCollection() .AddSingleton<IConfiguration>(configuration); + // Add the configuration to the service collection + services.Configure<KpiConfiguration>(settings => + { + configuration.GetSection(KpiConfiguration.Section).Bind(settings); + }); + // Add logging services.AddLogging(builder => { @@ -122,8 +127,8 @@ public class Program services.AddTransient<ProjectReporting>(); services.AddTransient<ResourceReporting>(); services.AddTransient<UserReporting>(); - //services.AddTransient<ApplicationProfileReporting>(); - //services.AddTransient<SystemReporting>(); + services.AddTransient<SystemReporting>(); + services.AddTransient<ApplicationProfileReporting>(); _serviceProvider = services.BuildServiceProvider(); } diff --git a/src/KPI Generator/Reportings/ApplicationProfile/ApplicationProfileReporting.cs b/src/KPI Generator/Reportings/ApplicationProfile/ApplicationProfileReporting.cs index 9ed9a5260235ec34aeff63f02410c26b8749360d..5bec3716b8ac9eee06fa638e77876dd0f561d0cc 100644 --- a/src/KPI Generator/Reportings/ApplicationProfile/ApplicationProfileReporting.cs +++ b/src/KPI Generator/Reportings/ApplicationProfile/ApplicationProfileReporting.cs @@ -1,90 +1,107 @@ -//using Coscine.KpiGenerator.Utils; -//using Microsoft.Extensions.Logging; -//using Newtonsoft.Json; -//using NLog.Extensions.Logging; -//using VDS.RDF.Query; -//using static KPIGenerator.Utils.CommandLineOptions; - -//namespace Coscine.KpiGenerator.Reportings.ApplicationProfile; - -//public class ApplicationProfileReporting : Reporting<ApplicationProfileReportingOptions> -//{ -// private readonly ILogger<ApplicationProfileReporting> _logger; - -// public ApplicationProfileReporting(ApplicationProfileReportingOptions options) : base(options) -// { -// ReportingFileName = "application_profiles.json"; -// _logger = LoggerFactory.Create(builder => builder.AddNLog()).CreateLogger<ApplicationProfileReporting>(); -// } - -// public override IEnumerable<ReportingFileObject> GenerateReportingAsync() -// { -// Console.WriteLine($" - {GetType().Name}: Begin reporting generation"); -// _logger.LogInformation("{Name}: Begin reporting generation", GetType().Name); -// var reportingFiles = new List<ReportingFileObject>(); -// var returnObjects = GetApplicationProfiles(); - -// // General File -// reportingFiles.Add(new ReportingFileObject -// { -// Path = GetReportingPathGeneral(ReportingFileName), -// Content = ConvertStringContentsToStream(JsonConvert.SerializeObject(returnObjects, Formatting.Indented)) -// }); -// Console.WriteLine($" - {GetType().Name}: \"{GetReportingPathGeneral(ReportingFileName)}\" generated successfully"); -// _logger.LogInformation("{Name}: Generated successfully. {reportingFiles}", GetType().Name, reportingFiles); - -// Console.WriteLine(); -// return reportingFiles; -// } - -// private List<ReturnObject> GetApplicationProfiles() -// { -// var _applicationProfile = "applicationProfile"; -// var _title = "title"; -// var _publisher = "publisher"; -// var _rights = "rights"; -// var _license = "license"; - -// var returnObjects = new List<ReturnObject>(); -// var queryString = new SparqlParameterizedString -// { -// CommandText = $@"PREFIX dcterms: <http://purl.org/dc/terms/> -// SELECT DISTINCT * WHERE {{ -// ?{_applicationProfile} a <http://www.w3.org/ns/shacl#NodeShape> . -// OPTIONAL {{ ?{_applicationProfile} dcterms:{_title} ?{_title}. }} -// OPTIONAL {{ ?{_applicationProfile} dcterms:{_publisher} ?{_publisher} . }} -// OPTIONAL {{ ?{_applicationProfile} dcterms:{_rights} ?{_rights} . }} -// OPTIONAL {{ ?{_applicationProfile} dcterms:{_license} ?{_license} . }} -// }}" -// }; -// using var result = Helpers.WrapRequest(() => RdfStoreConnector.QueryEndpoint.QueryWithResultSet(queryString.ToString())); - -// var grouped = result.GroupBy(ap => new -// { -// Uri = ap.Value(_applicationProfile).ToString(), -// Publisher = ap.HasValue(_publisher) && ap.Value(_publisher) is not null ? ap.Value(_publisher).ToString() : null, -// Rights = ap.HasValue(_rights) && ap.Value(_rights) is not null ? ap.Value(_rights).ToString() : null, -// License = ap.HasValue(_license) && ap.Value(_license) is not null ? ap.Value(_license).ToString() : null -// }, -// t => t.HasValue(_title) && t.Value(_title) is not null ? t.Value(_title).ToString() : null); -// foreach (var ap in grouped) -// { -// returnObjects.Add(new ReturnObject -// { -// Uri = ap.Key.Uri, -// Publisher = ap.Key.Publisher, -// Rights = ap.Key.Rights, -// License = ap.Key.License, -// Titles = ap.Select(t => -// { -// if (t is not null) -// { -// return t[..t.IndexOf('@')]; -// } -// return t; -// }).ToList() -// }); -// } -// return returnObjects; -// } -//} +using AutoMapper; +using Coscine.ApiClient; +using Coscine.ApiClient.Core.Api; +using Coscine.ApiClient.Core.Client; +using Coscine.ApiClient.Core.Model; +using Coscine.KpiGenerator.Models; +using Coscine.KpiGenerator.Models.ConfigurationModels; +using Coscine.KpiGenerator.Utils; +using Microsoft.Extensions.Options; +using Newtonsoft.Json; +using VDS.RDF; +using VDS.RDF.Nodes; +using VDS.RDF.Parsing; +using static KPIGenerator.Utils.CommandLineOptions; + +namespace Coscine.KpiGenerator.Reportings.Resource; + +public class ApplicationProfileReporting +{ + private readonly IMapper _mapper; + private readonly KpiConfiguration _kpiConfiguration; + + public static string AdminToken { get; set; } = null!; + public AdminApi AdminApi { get; init; } + public ProjectApi ProjectApi { get; init; } + public ApplicationProfileApi ApplicationProfileApi { get; } + public ApplicationProfileReportingOptions Options { get; private set; } = null!; + public string ReportingFileName { get; } + + public ApplicationProfileReporting(IMapper mapper, IOptionsMonitor<KpiConfiguration> kpiConfiguration) + { + _mapper = mapper; + _kpiConfiguration = kpiConfiguration.CurrentValue; + + ReportingFileName = _kpiConfiguration.ApplicationProfileKpi.FileName; + + var configuration = new Configuration() + { + BasePath = "http://localhost:7206/coscine", + ApiKeyPrefix = { { "Authorization", "Bearer" } }, + ApiKey = { { "Authorization", ApiConfigurationUtil.GenerateAdminToken(ApiConfigurationUtil.RetrieveJwtConfiguration()) } }, + }; + + AdminApi = new AdminApi(configuration); + ProjectApi = new ProjectApi(configuration); + ApplicationProfileApi = new ApplicationProfileApi(configuration); + } + + public async Task<bool> RunAsync(ApplicationProfileReportingOptions reportingOptions) + { + Options = reportingOptions; + var reportingFiles = await GenerateReportingAsync(); + return await GitLabUtils.PublishAsync(reportingFiles); + } + + public async Task<IEnumerable<ReportingFileObject>> GenerateReportingAsync() + { + var reportingFiles = new List<ReportingFileObject>(); + var returnObjects = new List<ApplicationProfileReport>(); + + var applicationProfiles = await RequestUtil.WrapPagedRequest<ApplicationProfileDtoPagedResponse, ApplicationProfileDto>((pageNumber) => ApplicationProfileApi.GetApplicationProfilesAsync(pageNumber: pageNumber, pageSize: 500)); + foreach(var ap in applicationProfiles) + { + var applicationProfile = await ApplicationProfileApi.GetApplicationProfileAsync(ap.Uri, RdfFormat.TextTurtle); + + var g = new Graph(); + StringParser.Parse(g, applicationProfile.Data.Definition.Content); + + var titleUri = new Uri("http://purl.org/dc/terms/title"); + var titles = g.GetTriplesWithPredicate(g.CreateUriNode(titleUri)) + .Select(triple => triple.Object.AsValuedNode().AsString()) + .ToList(); + + var publisherUri = new Uri("http://purl.org/dc/terms/publisher"); + var publisher = g.GetTriplesWithPredicate(g.CreateUriNode(publisherUri)) + .FirstOrDefault()?.Object.AsValuedNode().AsString(); + + var rightsUri = new Uri("http://purl.org/dc/terms/rights"); + var rights = g.GetTriplesWithPredicate(g.CreateUriNode(rightsUri)) + .FirstOrDefault()?.Object.AsValuedNode().AsString(); + + var licenseUri = new Uri("http://purl.org/dc/terms/license"); + var license = g.GetTriplesWithPredicate(g.CreateUriNode(licenseUri)) + .FirstOrDefault()?.Object.AsValuedNode().AsString(); + + returnObjects.Add(new ApplicationProfileReport + { + Uri = ap.Uri, + Titles = titles ?? new List<string>(), + Publisher = publisher, + Rights = rights, + License = license + }); + } + + // General File + reportingFiles.Add(new ReportingFileObject + { + Path = Helpers.GetReportingPathGeneral(ReportingFileName), + Content = Helpers.ConvertStringContentsToStream(JsonConvert.SerializeObject(returnObjects, Formatting.Indented)) + }); + + // No per organization file + + return reportingFiles; + } +} \ No newline at end of file diff --git a/src/KPI Generator/Reportings/Project/ProjectReporting.cs b/src/KPI Generator/Reportings/Project/ProjectReporting.cs index 83ff3d75bf8dffe7fe80073a8c65e569646230f6..a826fe0c2c59ee2eb078e0a9523a3d9fff41bed7 100644 --- a/src/KPI Generator/Reportings/Project/ProjectReporting.cs +++ b/src/KPI Generator/Reportings/Project/ProjectReporting.cs @@ -4,7 +4,9 @@ using Coscine.ApiClient.Core.Api; using Coscine.ApiClient.Core.Client; using Coscine.ApiClient.Core.Model; using Coscine.KpiGenerator.Models; +using Coscine.KpiGenerator.Models.ConfigurationModels; using Coscine.KpiGenerator.Utils; +using Microsoft.Extensions.Options; using Newtonsoft.Json; using static KPIGenerator.Utils.CommandLineOptions; @@ -13,6 +15,7 @@ namespace Coscine.KpiGenerator.Reportings.Project; public class ProjectReporting { private readonly IMapper _mapper; + private readonly KpiConfiguration _kpiConfiguration; public static string AdminToken { get; set; } = null!; public AdminApi AdminApi { get; init; } @@ -20,17 +23,17 @@ public class ProjectReporting public ProjectReportingOptions Options { get; private set; } = null!; public string ReportingFileName { get; } - public ProjectReporting(IMapper mapper) + public ProjectReporting(IMapper mapper, IOptionsMonitor<KpiConfiguration> kpiConfiguration) { _mapper = mapper; - - ReportingFileName = "projects.json"; + _kpiConfiguration = kpiConfiguration.CurrentValue; + ReportingFileName = _kpiConfiguration.ProjectKpi.FileName; var configuration = new Configuration() { BasePath = "http://localhost:7206/coscine", ApiKeyPrefix = { { "Authorization", "Bearer" } }, - ApiKey = { { "Authorization", JwtUtil.GenerateAdminToken(JwtUtil.RetrieveJwtConfiguration()) } }, + ApiKey = { { "Authorization", ApiConfigurationUtil.GenerateAdminToken(ApiConfigurationUtil.RetrieveJwtConfiguration()) } }, }; AdminApi = new AdminApi(configuration); diff --git a/src/KPI Generator/Reportings/Resource/ResourceReporting.cs b/src/KPI Generator/Reportings/Resource/ResourceReporting.cs index 79be9c2452c622cb9059e368ffee8bbf4b04dc77..d16519b11ebe97dd1a008021bfea361eb032a14d 100644 --- a/src/KPI Generator/Reportings/Resource/ResourceReporting.cs +++ b/src/KPI Generator/Reportings/Resource/ResourceReporting.cs @@ -4,7 +4,9 @@ using Coscine.ApiClient.Core.Api; using Coscine.ApiClient.Core.Client; using Coscine.ApiClient.Core.Model; using Coscine.KpiGenerator.Models; +using Coscine.KpiGenerator.Models.ConfigurationModels; using Coscine.KpiGenerator.Utils; +using Microsoft.Extensions.Options; using Newtonsoft.Json; using static KPIGenerator.Utils.CommandLineOptions; @@ -13,6 +15,7 @@ namespace Coscine.KpiGenerator.Reportings.Resource; public class ResourceReporting { private readonly IMapper _mapper; + private readonly KpiConfiguration _kpiConfiguration; public static string AdminToken { get; set; } = null!; public AdminApi AdminApi { get; init; } @@ -21,17 +24,17 @@ public class ResourceReporting public ResourceReportingOptions Options { get; private set; } = null!; public string ReportingFileName { get; } - public ResourceReporting(IMapper mapper) + public ResourceReporting(IMapper mapper, IOptionsMonitor<KpiConfiguration> kpiConfiguration) { _mapper = mapper; - - ReportingFileName = "resources.json"; + _kpiConfiguration = kpiConfiguration.CurrentValue; + ReportingFileName = _kpiConfiguration.ResourceKpi.FileName; var configuration = new Configuration() { BasePath = "http://localhost:7206/coscine", ApiKeyPrefix = { { "Authorization", "Bearer" } }, - ApiKey = { { "Authorization", JwtUtil.GenerateAdminToken(JwtUtil.RetrieveJwtConfiguration()) } }, + ApiKey = { { "Authorization", ApiConfigurationUtil.GenerateAdminToken(ApiConfigurationUtil.RetrieveJwtConfiguration()) } }, }; AdminApi = new AdminApi(configuration); diff --git a/src/KPI Generator/Reportings/System/MaintenanceReturnObject.cs b/src/KPI Generator/Reportings/System/MaintenanceReturnObject.cs deleted file mode 100644 index 7af2780672e51fd3ff6d8107a8c763a3f9cf1f6a..0000000000000000000000000000000000000000 --- a/src/KPI Generator/Reportings/System/MaintenanceReturnObject.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Newtonsoft.Json; - -namespace Coscine.KpiGenerator.Reportings.System; - -public class MaintenanceBannerObject -{ - [JsonProperty("id")] - public int Id { get; set; } - [JsonProperty("title")] - public string Title { get; set; } = null!; - [JsonProperty("short_message")] - public string ShortMessage { get; set; } = null!; - [JsonProperty("message")] - public string Message { get; set; } = null!; - [JsonProperty("status")] - public string Status { get; set; } = null!; - [JsonProperty("start_at")] - public DateTime? StartAt { get; set; } = null!; - [JsonProperty("end_at")] - public DateTime? EndAt { get; set; } = null!; -} - diff --git a/src/KPI Generator/Reportings/System/ReturnObject.cs b/src/KPI Generator/Reportings/System/ReturnObject.cs deleted file mode 100644 index 01b737e2134eadb760526ffcfff1975af61c805f..0000000000000000000000000000000000000000 --- a/src/KPI Generator/Reportings/System/ReturnObject.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Coscine.KpiGenerator.Reportings.System; - -/// <summary> -/// Object containing the JSON structure for the reporting -/// </summary> -public class ReturnObject -{ - public List<MaintenanceBannerObject>? Banners { get; set; } = new(); -} \ No newline at end of file diff --git a/src/KPI Generator/Reportings/System/SystemReporting.cs b/src/KPI Generator/Reportings/System/SystemReporting.cs index f6dfe4f74de1b2fb2854baecb9d2f691dc0cfff8..f71d3c7570ed334658896fcc199c9dbb136f525a 100644 --- a/src/KPI Generator/Reportings/System/SystemReporting.cs +++ b/src/KPI Generator/Reportings/System/SystemReporting.cs @@ -1,77 +1,95 @@ -//using Coscine.Configuration; -//using Coscine.KpiGenerator.Utils; -//using Microsoft.Extensions.Logging; -//using Newtonsoft.Json; -//using NLog.Extensions.Logging; -//using System.Net.Http.Headers; -//using System.Text; -//using static KPIGenerator.Utils.CommandLineOptions; +using AutoMapper; +using Coscine.ApiClient; +using Coscine.ApiClient.Core.Api; +using Coscine.ApiClient.Core.Client; +using Coscine.KpiGenerator.Models; +using Coscine.KpiGenerator.Models.ConfigurationModels; +using Coscine.KpiGenerator.Utils; +using Microsoft.Extensions.Options; +using Newtonsoft.Json; +using System.Net.Http.Headers; +using System.Text; +using static KPIGenerator.Utils.CommandLineOptions; -//namespace Coscine.KpiGenerator.Reportings.System; +namespace Coscine.KpiGenerator.Reportings.Resource; -//public class SystemReporting : Reporting<SystemReportingOptions> -//{ -// private readonly ILogger<SystemReporting> _logger; +public class SystemReporting +{ + private readonly IMapper _mapper; + private readonly KpiConfiguration _kpiConfiguration; + private readonly HttpClient _httpClient; -// private readonly ConsulConfiguration _configuration; + public static string AdminToken { get; set; } = null!; + public AdminApi AdminApi { get; init; } + public ProjectApi ProjectApi { get; init; } + public SystemReportingOptions Options { get; private set; } = null!; + public string ReportingFileName { get; } -// public SystemReporting(SystemReportingOptions options) : base(options) -// { -// ReportingFileName = "system_status.json"; -// _configuration = new ConsulConfiguration(); -// _logger = LoggerFactory.Create(builder => builder.AddNLog()).CreateLogger<SystemReporting>(); -// } + public SystemReporting(IMapper mapper, IHttpClientFactory httpClientFactory, IOptionsMonitor<KpiConfiguration> kpiConfiguration) + { + _mapper = mapper; + _kpiConfiguration = kpiConfiguration.CurrentValue; + _httpClient = httpClientFactory.CreateClient("MaintenanceClient"); -// public override IEnumerable<ReportingFileObject> GenerateReportingAsync() -// { -// Console.WriteLine($" - {GetType().Name}: Begin reporting generation"); -// _logger.LogInformation("{Name}: Begin reporting generation", GetType().Name); -// var reportingFiles = new List<ReportingFileObject>(); -// var returnObject = GetSystemStatus(); + ReportingFileName = _kpiConfiguration.SystemKpi.FileName; -// // General File -// reportingFiles.Add(new ReportingFileObject -// { -// Path = GetReportingPathGeneral(ReportingFileName), -// Content = ConvertStringContentsToStream(JsonConvert.SerializeObject(returnObject, Formatting.Indented)) -// }); -// Console.WriteLine($" - {GetType().Name}: \"{GetReportingPathGeneral(ReportingFileName)}\" generated successfully"); -// _logger.LogInformation("{Name}: Generated successfully. {reportingFiles}", GetType().Name, reportingFiles); + var configuration = new Configuration() + { + BasePath = "http://localhost:7206/coscine", + ApiKeyPrefix = { { "Authorization", "Bearer" } }, + ApiKey = { { "Authorization", ApiConfigurationUtil.GenerateAdminToken(ApiConfigurationUtil.RetrieveJwtConfiguration()) } }, + }; -// Console.WriteLine(); -// return reportingFiles; -// } + AdminApi = new AdminApi(configuration); + ProjectApi = new ProjectApi(configuration); + } -// private ReturnObject GetSystemStatus() -// { -// return new ReturnObject() -// { -// Banners = GetMaintenanceBannersAsync().Result -// // Extend here with other specific props in the future -// }; -// } + public async Task<bool> RunAsync(SystemReportingOptions reportingOptions) + { + Options = reportingOptions; + var reportingFiles = await GenerateReportingAsync(); + return await GitLabUtils.PublishAsync(reportingFiles); + } -// private async Task<List<MaintenanceBannerObject>?> GetMaintenanceBannersAsync() -// { -// var httpClient = new HttpClient(); -// var requestMessage = new HttpRequestMessage() -// { -// RequestUri = new Uri("https://noc-portal.rz.rwth-aachen.de/ticket/api/coscine/tickets"), -// Method = HttpMethod.Get -// }; + public async Task<IEnumerable<ReportingFileObject>> GenerateReportingAsync() + { + var reportingFiles = new List<ReportingFileObject>(); + var returnObject = new SystemReport() + { + Banners = await GetMaintenanceBannersAsync(), + }; -// // Add Basic Authentication Headers -// var authenticationString = $"{_configuration.GetStringAndWait("coscine/global/maintenance/user")}:{_configuration.GetStringAndWait("coscine/global/maintenance/password")}"; -// var base64EncodedAuthenticationString = Convert.ToBase64String(Encoding.ASCII.GetBytes(authenticationString)); -// requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Basic", base64EncodedAuthenticationString); + // General File + reportingFiles.Add(new ReportingFileObject + { + Path = Helpers.GetReportingPathGeneral(ReportingFileName), + Content = Helpers.ConvertStringContentsToStream(JsonConvert.SerializeObject(returnObject, Formatting.Indented)), + }); -// var result = await httpClient.SendAsync(requestMessage); -// var responseBody = JsonConvert.DeserializeObject<List<MaintenanceBannerObject>>(result.Content.ReadAsStringAsync().Result); -// if (responseBody is null) -// { -// return null; -// } -// return responseBody.OrderBy(e => e.StartAt).ToList(); -// } -//} + // No per organization file + return reportingFiles; + } + + private async Task<List<MaintenanceReport>?> GetMaintenanceBannersAsync() + { + var requestMessage = new HttpRequestMessage() + { + RequestUri = new Uri(_kpiConfiguration.SystemKpi.Maintenance.Url), + Method = System.Net.Http.HttpMethod.Get + }; + + // Add Basic Authentication Headers + var authenticationString = $"{_kpiConfiguration.SystemKpi.Maintenance.Username}:{_kpiConfiguration.SystemKpi.Maintenance.Password}"; + var base64EncodedAuthenticationString = Convert.ToBase64String(Encoding.ASCII.GetBytes(authenticationString)); + requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Basic", base64EncodedAuthenticationString); + + var result = await _httpClient.SendAsync(requestMessage); + var responseBody = JsonConvert.DeserializeObject<List<MaintenanceReport>>(result.Content.ReadAsStringAsync().Result); + if (responseBody is null) + { + return null; + } + return responseBody.OrderBy(e => e.StartAt).ToList(); + } +} \ No newline at end of file diff --git a/src/KPI Generator/Reportings/User/ReturnObject.cs b/src/KPI Generator/Reportings/User/ReturnObject.cs deleted file mode 100644 index ecca1f6cbe25f332d94897dc72746d2fc76222b1..0000000000000000000000000000000000000000 --- a/src/KPI Generator/Reportings/User/ReturnObject.cs +++ /dev/null @@ -1,24 +0,0 @@ -//using Coscine.Database.ReturnObjects; -//using Coscine.KpiGenerator.Utils; - -//namespace Coscine.KpiGenerator.Reportings.User; - -///// <summary> -///// Object containing the JSON structure for the reporting -///// </summary> -//public class ReturnObject -//{ -// public List<RelatedProject> RelatedProjects { get; set; } = new(); -// public List<Organization> Organizations { get; set; } = new(); -// public List<Organization> Institutes { get; set; } = new(); -// public List<DisciplineObject> Disciplines { get; set; } = new(); -// public List<ExternalAuthenticatorsObject> LoginProviders { get; set; } = new(); -// public DateTime? LatestActivity { get; set; } = null; - - -// public class RelatedProject -// { -// public Guid ProjectId { get; set; } -// public string Role { get; set; } = null!; -// } -//} diff --git a/src/KPI Generator/Reportings/User/UserReporting.cs b/src/KPI Generator/Reportings/User/UserReporting.cs index 7814b9e45f73970271b733e28aa79c943c793883..be632d92ab87ecb2aca4b921de801479d2bbfe05 100644 --- a/src/KPI Generator/Reportings/User/UserReporting.cs +++ b/src/KPI Generator/Reportings/User/UserReporting.cs @@ -4,7 +4,9 @@ using Coscine.ApiClient.Core.Api; using Coscine.ApiClient.Core.Client; using Coscine.ApiClient.Core.Model; using Coscine.KpiGenerator.Models; +using Coscine.KpiGenerator.Models.ConfigurationModels; using Coscine.KpiGenerator.Utils; +using Microsoft.Extensions.Options; using Newtonsoft.Json; using static KPIGenerator.Utils.CommandLineOptions; @@ -13,24 +15,26 @@ namespace Coscine.KpiGenerator.Reportings.Resource; public class UserReporting { private readonly IMapper _mapper; + private readonly KpiConfiguration _kpiConfiguration; public static string AdminToken { get; set; } = null!; public AdminApi AdminApi { get; init; } public ProjectApi ProjectApi { get; init; } public UserReportingOptions Options { get; private set; } = null!; + public KpiConfiguration KpiConfiguration { get; } = new(); public string ReportingFileName { get; } - public UserReporting(IMapper mapper) + public UserReporting(IMapper mapper, IOptionsMonitor<KpiConfiguration> kpiConfiguration) { _mapper = mapper; - - ReportingFileName = "users.json"; + _kpiConfiguration = kpiConfiguration.CurrentValue; + ReportingFileName = _kpiConfiguration.UserKpi.FileName; var configuration = new Configuration() { BasePath = "http://localhost:7206/coscine", ApiKeyPrefix = { { "Authorization", "Bearer" } }, - ApiKey = { { "Authorization", JwtUtil.GenerateAdminToken(JwtUtil.RetrieveJwtConfiguration()) } }, + ApiKey = { { "Authorization", ApiConfigurationUtil.GenerateAdminToken(ApiConfigurationUtil.RetrieveJwtConfiguration()) } }, }; AdminApi = new AdminApi(configuration); diff --git a/src/KPI Generator/Reportings/User/UserReporting2.cs b/src/KPI Generator/Reportings/User/UserReporting2.cs deleted file mode 100644 index cc12b1961e299364d488eabd799cc1eba672046c..0000000000000000000000000000000000000000 --- a/src/KPI Generator/Reportings/User/UserReporting2.cs +++ /dev/null @@ -1,260 +0,0 @@ -//using Coscine.Database.Models; -//using Coscine.Database.ReturnObjects; -//using Coscine.KpiGenerator.Utils; -//using Coscine.Metadata; -//using Microsoft.Extensions.Logging; -//using Newtonsoft.Json; -//using NLog.Extensions.Logging; -//using VDS.RDF.Query; -//using static KPIGenerator.Utils.CommandLineOptions; - -//namespace Coscine.KpiGenerator.Reportings.User; - -//public class UserReporting : Reporting<UserReportingOptions> -//{ -// private readonly ILogger<UserReporting> _logger; - -// private readonly ExternalIdModel _externalIdModel; -// private readonly ProjectRoleModel _projectRoleModel; -// private readonly ProjectModel _projectModel; -// private readonly RoleModel _roleModel; -// private readonly UserModel _userModel; -// private readonly LogModel _logModel; - -// public UserReporting(UserReportingOptions options) : base(options) -// { -// ReportingFileName = "users.json"; -// _externalIdModel = new ExternalIdModel(); -// _projectRoleModel = new ProjectRoleModel(); -// _projectModel = new ProjectModel(); -// _roleModel = new RoleModel(); -// _userModel = new UserModel(); -// _logModel = new LogModel(); - -// _logger = LoggerFactory.Create(builder => builder.AddNLog()).CreateLogger<UserReporting>(); -// } - -// public override IEnumerable<ReportingFileObject> GenerateReportingAsync() -// { -// Console.WriteLine($" - {GetType().Name}: Begin reporting generation"); -// _logger.LogInformation("{Name}: Begin reporting generation", GetType().Name); -// var users = _userModel.GetAllWhere((user) => user.Tosaccepteds.Any()); -// var reportingFiles = new List<ReportingFileObject>(); -// var returnObjects = Generate(users); - -// // General File -// reportingFiles.Add(new ReportingFileObject -// { -// Path = GetReportingPathGeneral(ReportingFileName), -// Content = ConvertStringContentsToStream(JsonConvert.SerializeObject(returnObjects, Formatting.Indented)) -// }); -// Console.WriteLine($" - {GetType().Name}: \"{GetReportingPathGeneral(ReportingFileName)}\" generated successfully"); -// _logger.LogInformation("{Name}: Generated successfully. {reportingFiles}", GetType().Name, reportingFiles); - -// // Per Organization -// reportingFiles.AddRange(GeneratePerOrganization(returnObjects)); - -// Console.WriteLine(); -// return reportingFiles; -// } - -// private List<ReturnObject> Generate(IEnumerable<Database.DataModel.User> users) -// { -// var returnObjects = new List<ReturnObject>(); -// foreach (var user in users) -// { -// var userReturnObject = _userModel.CreateReturnObjectFromDatabaseObject(user); -// var (organizations, institutes) = GetUserAffiliation(userReturnObject); -// var userReportEntry = new ReturnObject -// { -// RelatedProjects = GetRelatedProjects(user.Id), -// Organizations = organizations, -// Institutes = institutes, -// Disciplines = userReturnObject.Disciplines.ToList(), -// LoginProviders = userReturnObject.ExternalAuthenticators.ToList(), -// LatestActivity = GetLatestActivity(user.Id) -// }; -// returnObjects.Add(userReportEntry); -// } -// return returnObjects; -// } - -// private IEnumerable<ReportingFileObject> GeneratePerOrganization(List<ReturnObject> returnObjects) -// { -// var reportingFilesPerOrganization = new List<ReportingFileObject>(); -// var organizationsFromUsers = GetTopLevelOrganizationsFromEntries(returnObjects.SelectMany(ro => ro.Organizations)); -// foreach (var entry in organizationsFromUsers) -// { -// List<ReturnObject> returnObjectsForOrganization; - -// // Handling of "Other" organization -// if (entry.RorUrl.Equals("https://ror.org/") || entry.RorUrl.Equals("http://ror.org/")) -// { -// returnObjectsForOrganization = returnObjects.Where(ro => ro.Organizations.Select(o => o.RorUrl).Any(e => e.Equals("https://ror.org/") || e.Equals("http://ror.org/"))).ToList(); -// entry.RorUrl = _otherOrganization.RorUrl; -// } -// else -// { -// returnObjectsForOrganization = returnObjects.Where(ro => ro.Organizations.Select(o => o.RorUrl).Any(e => e.Contains(entry.RorUrl))).ToList(); -// } - -// var reportingFile = new ReportingFileObject -// { -// Path = GetReportingPathOrganization(entry.RorUrl, ReportingFileName), -// Content = ConvertStringContentsToStream(JsonConvert.SerializeObject(returnObjectsForOrganization, Formatting.Indented)) -// }; - -// reportingFilesPerOrganization.Add(reportingFile); - -// Console.WriteLine($" - {GetType().Name}: \"{GetReportingPathOrganization(entry.RorUrl, ReportingFileName)}\" generated successfully"); -// _logger.LogInformation("{Name}: Generated successfully {ReportingFile}.", GetType().Name, reportingFile); -// } -// return reportingFilesPerOrganization; -// } - -// private List<ReturnObject.RelatedProject> GetRelatedProjects(Guid id) -// { -// var result = new List<ReturnObject.RelatedProject>(); -// var projectRoles = _projectRoleModel.GetAllWhere(role => role.UserId.Equals(id)); -// if (projectRoles.Any()) -// { -// foreach (var projectRole in projectRoles) -// { -// if (_projectModel.GetById(projectRole.ProjectId) is not null) // null if project has been deleted -// { -// result.Add(new ReturnObject.RelatedProject -// { -// ProjectId = projectRole.ProjectId, -// Role = _roleModel.GetById(projectRole.RoleId).DisplayName -// }); -// } -// } -// } -// return result; -// } - -// private (List<Organization> organizations, List<Organization> institutes) GetUserAffiliation(UserObject user) -// { -// var affiliations = new List<Organization>(); - -// // Bellow code taken from Organizations API -// var externalIds = _externalIdModel.GetAllWhere((externalId) => externalId.UserId == user.Id); -// var externalIdList = new List<string>(); -// foreach (var externalId in externalIds) -// { -// externalIdList.Add(externalId.ExternalId1); -// } -// var externalOrganizations = externalIds.Select((externalId) => externalId.Organization); - -// var triples = Helpers.WrapRequest(() => RdfStoreConnector.GetTriples(null, null, null, 1, externalIdList).ToList()); -// foreach (var externalOrganization in externalOrganizations) -// { -// triples.AddRange(Helpers.WrapRequest(() => RdfStoreConnector.GetOrganizationByEntityId(externalOrganization))); -// } -// triples = triples.Distinct().ToList(); - -// foreach (var triple in triples) -// { -// affiliations.Add(new() -// { -// Name = triple.Object.ToString(), -// RorUrl = triple.Subject.ToString() -// }); -// } - -// // Clean up -// affiliations = CleanUpOther(affiliations); - -// // Split affiliations to "organizations" and "institutes" -// var organizations = affiliations.Where(a => !a.RorUrl.Contains('#')).ToList(); -// var institutes = affiliations.Where(a => a.RorUrl.Contains('#')).ToList(); - -// // Find the RoR of the organization based on its "rdfs:label". -// var orgFromSqlDb = TryGetOrganizationByLabel(user.Organization); -// if (orgFromSqlDb is not null) -// organizations.Add(orgFromSqlDb); - -// // Find the RoR of the institute based on its "rdfs:label". -// var instFromSqlDb = TryGetOrganizationByLabel(user.Institute); -// if (instFromSqlDb is not null) -// institutes.Add(instFromSqlDb); - -// return ( -// organizations.DistinctBy(o => o.RorUrl).ToList(), -// institutes.DistinctBy(i => i.RorUrl).ToList() -// ); -// } - -// private Organization? TryGetOrganizationByLabel(string rdfsLabel) -// { -// if (string.IsNullOrWhiteSpace(rdfsLabel)) -// { -// return null; -// } -// var _queryString = new SparqlParameterizedString() -// { -// CommandText = $@" -// PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> - -// SELECT DISTINCT ?ror -// WHERE {{ -// VALUES ?name -// {{ -// '{rdfsLabel}' -// }} . -// ?ror rdfs:label ?name . -// FILTER( CONTAINS( STR(?ror), 'ror.org' ) ) -// }} -// " -// }; -// using var results = Helpers.WrapRequest(() => RdfStoreConnector.QueryEndpoint.QueryWithResultSet(_queryString.ToString())); -// if (!results.IsEmpty) -// { -// var ror = results.Select(x => x.Value("ror").ToString()); // Get the value for ?ror -// if (ror.Any()) -// { -// return FetchOrganizationByRor(ror.ToList()[0]); -// } -// } -// return null; -// } - -// private DateTime? GetLatestActivity(Guid id) -// { -// var today = DateTime.Today; -// var latestLog = _logModel -// .GetAllWhere(l => -// l.LogLevel.Equals("Analytics") -// && l.UserId.Equals(id) -// && l.ServerTimestamp < today) -// .OrderByDescending(a => a.ServerTimestamp) -// .FirstOrDefault(); -// if (latestLog is not null) -// { -// return latestLog.ServerTimestamp; -// } -// else return null; -// } - -// private List<Organization> CleanUpOther(List<Organization> affiliations) -// { -// // Special case until `https://ror.org/%20https://ror.org/` is moved to `https://ror.org/_other` -// if (affiliations.Any(o => o.RorUrl.Equals("https://ror.org/%20https://ror.org/") || o.RorUrl.Equals("https://ror.org/") || o.RorUrl.Equals("http://ror.org/"))) -// { -// var cleanedUpAffiliations = affiliations; -// foreach (var entry in cleanedUpAffiliations) -// { -// if (entry.RorUrl.Equals("https://ror.org/%20https://ror.org/") || entry.RorUrl.Equals("https://ror.org/") || entry.RorUrl.Equals("http://ror.org/")) -// { -// cleanedUpAffiliations[cleanedUpAffiliations.IndexOf(entry)] = new() -// { -// Name = _otherOrganization.Name, -// RorUrl = _otherOrganization.RorUrl -// }; -// } -// } -// return cleanedUpAffiliations; -// } -// return affiliations; -// } -//} \ No newline at end of file