Skip to content
Snippets Groups Projects
Select Git revision
  • 1503b902bcbe833affc7c6279f4dcad9eb3474e5
  • main default protected
  • gitkeep
  • dev protected
  • Issue/2914-trellisMigrator
  • Issue/2847-reporting
  • Hotfix/2776-workingNewVersion
  • Hotfix/xxxx-correctAssignments
  • Issue/2666-adminCronjobs-theSequal
  • Issue/2666-adminCronjobs
  • Issue/2518-docs
  • Hotfix/xxxx-coscineGraph
  • Issue/2304-virtuosoRoars
  • Fix/v0.1.7-dependencies
  • Hotfix/2212-fixFiles
  • Issue/2222-resourceDateCreated
  • Issue/2221-projectDateCreated
  • Hotfix/xxxx-changeUrls
  • Issue/1321-pidEnquiryOverhaul
  • Issue/1782-structualDataIntegration
  • Issue/2084-migrateResourceStructuralData
  • v0.1.24
  • v0.1.23
  • v0.1.22
  • v0.1.21
  • v0.1.20
  • v0.1.19
  • v0.1.18
  • v0.1.17
  • v0.1.16
  • v0.1.15
  • v0.1.14
  • v0.1.13
  • v0.1.12
  • v0.1.11
  • v0.1.10
  • v0.1.9
  • v0.1.7
  • v0.1.8
  • v0.1.6
  • v0.1.5
41 results

StructuralData.cs

Blame
  • Benedikt Heinrichs's avatar
    Benedikt Heinrichs authored and Sirieam Marie Hunke committed
    1503b902
    History
    Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    StructuralData.cs 8.66 KiB
    using Coscine.ApiClient;
    using Coscine.ApiClient.Core.Api;
    using Coscine.ApiClient.Core.Client;
    using Coscine.ApiClient.Core.Model;
    using Microsoft.Extensions.Configuration;
    using SQL2Linked.Models.ConfigurationModels;
    using SQL2Linked.Utils;
    using System.Globalization;
    using VDS.RDF;
    using VDS.RDF.Parsing;
    
    namespace SQL2Linked;
    
    /// <summary>
    /// Provides an abstract base class for handling structural data transformations and migrations.
    /// </summary>
    /// <typeparam name="S">The type of data to be transformed or migrated.</typeparam>
    /// <remarks>
    /// This class is partially shared between this script and the Trellis Migrator script.
    /// </remarks>
    public abstract class StructuralData<S>
    {
        private readonly string _adminToken;
        protected readonly Configuration _apiConfiguration;
        protected readonly AdminApi _adminApi;
        protected readonly PidConfiguration _pidConfiguration; // Comes from the API Client, not from the application's own configuration
    
        /// <summary>
        /// Initializes a new instance of the <see cref="StructuralData{S}"/> class.
        /// </summary>
        public StructuralData()
        {
            // Retrieve the configuration settings for the PID ePIC API from the API Client
            var apiConfiguration = ApiConfigurationUtil.RetrieveApiConfiguration();
            _pidConfiguration = apiConfiguration.GetSection(PidConfiguration.Section).Get<PidConfiguration>() ?? new();
    
            // Ensiure that the prefix is not null or empty.
            ArgumentException.ThrowIfNullOrWhiteSpace(_pidConfiguration.Prefix, nameof(_pidConfiguration.Prefix));
    
            // Generate an admin token for the API Client
            var jwtConfiguration = ApiConfigurationUtil.RetrieveJwtConfiguration();
            _adminToken = ApiConfigurationUtil.GenerateAdminToken(jwtConfiguration);
            _apiConfiguration = new Configuration()
            {
                BasePath = "http://localhost:7206/coscine",
                ApiKeyPrefix = { { "Authorization", "Bearer" } },
                ApiKey = { { "Authorization", _adminToken } },
                Timeout = 300000, // 5 minutes
            };
            _adminApi = new AdminApi(_apiConfiguration);
        }
    
        /// <summary>
        /// Asynchronously converts a collection of entries into linked data graphs.
        /// </summary>
        /// <param name="entries">A collection of entries of type <typeparamref name="S"/> to convert.</param>
        /// <typeparam name="S">The type of the entries to be converted into linked data graphs.</typeparam>
        /// <returns>An asynchronous enumerable (<see cref="IAsyncEnumerable{IGraph}"/>) of graphs, where each graph represents the linked data derived from an entry.</returns>
        public abstract IAsyncEnumerable<IGraph> ConvertToLinkedDataAsync(IAsyncEnumerable<S> entries);
    
        /// <summary>
        /// Asynchronously retrieves all entries of type <typeparamref name="S"/>.
        /// </summary>
        /// <typeparam name="S">The type of the entries to retrieve.</typeparam>
        /// <returns>An <see cref="IAsyncEnumerable{S}"/> representing an asynchronous stream of entries.</returns>
        public abstract IAsyncEnumerable<S> GetAll();
    
        /// <summary>
        /// Asynchronously migrates data, with an option to simulate the migration process without making any changes (dummy mode).
        /// </summary>
        /// <param name="dummyMode">If set to <c>true</c>, the method simulates the migration process without executing any changes to the data store.</param>
        /// <returns>A task representing the asynchronous migration operation.</returns>
        public async Task Migrate(bool dummyMode)
        {
            var spacer = new string('-', 35);
            Console.WriteLine($"\n{spacer}\n{typeof(S).Name}\n{spacer}");
            var graphs = ConvertToLinkedDataAsync(GetAll());
            if (!dummyMode)
            {
                await StoreGraphs(graphs);
            }
        }
    
        /// <summary>
        /// Asynchronously stores a collection of RDF graphs in the underlying data store.
        /// </summary>
        /// <param name="graphs">An asynchronous enumerable of RDF graphs (<see cref="IAsyncEnumerable{IGraph}"/>) to be stored.</param>
        /// <returns>A task representing the asynchronous operation of storing the graphs.</returns>
        public async Task StoreGraphs(IAsyncEnumerable<IGraph> graphs)
        {
            var formatEnum = RdfFormat.TextTurtle;
            var format = "text/turtle";
    
            await foreach (var graph in graphs)
            {
                Console.WriteLine($" ({graph.BaseUri})");
    
                try
                {
                    var rdfWriter = MimeTypesHelper.GetWriter(format);
                    var content = VDS.RDF.Writing.StringWriter.Write(graph, rdfWriter);
    
                    if (graph is PatchGraph patchGraph)
                    {
                        var patchOperations = new List<RdfPatchOperationDto>
                        {
                            new(RdfPatchOperationType.A, new RdfDefinitionForManipulationDto(content, formatEnum))
                        };
    
                        await _adminApi.PatchMetadataAsync(
                            graph.BaseUri.AbsoluteUri,
                            new RdfPatchDocumentDto(patchOperations)
                        );
                    }
                    else
                    {
                        await _adminApi.UpdateMetadataGraphAsync(
                            graph.BaseUri.AbsoluteUri,
                            new MetadataUpdateAdminParameters(new RdfDefinitionForManipulationDto(content, formatEnum))
                        );
                    }
    
                    Console.WriteLine($" - Graph {graph.BaseUri} added successfully");
                    Console.WriteLine();
                }
                catch (Exception e)
                {
                    Console.Error.WriteLine($"Error on ({graph.BaseUri}):");
                    Console.Error.WriteLine(e);
                    Console.Error.WriteLine(e.InnerException);
                    Console.Error.WriteLine();
                }
            }
        }
    
        public void AddModifiedDate(IGraph graph, Uri rootUri)
        {
            AssertToGraphLiteralNode(
                graph,
                rootUri,
                RdfUris.DcTermsModified,
                DateTime.UtcNow.ToString("o", CultureInfo.InvariantCulture),
                new Uri(XmlSpecsHelper.XmlSchemaDataTypeDateTime)
            );
        }
    
        /// <summary>
        /// Asserts a triple to the graph with a URI node object.
        /// </summary>
        /// <param name="graph">The graph to assert to.</param>
        /// <param name="graphSubject">The subject URI of the triple.</param>
        /// <param name="graphPredicate">The predicate URI of the triple.</param>
        /// <param name="graphObject">The object URI of the triple.</param>
        public void AssertToGraphUriNode(IGraph graph, Uri graphSubject, Uri graphPredicate, Uri? graphObject)
        {
            if (graphObject != null)
            {
                graph.Assert(
                    new Triple(
                        graph.CreateUriNode(graphSubject),
                        graph.CreateUriNode(graphPredicate),
                        graph.CreateUriNode(graphObject)
                    )
                );
            }
        }
    
        /// <summary>
        /// Asserts a triple to the graph with a literal node object.
        /// </summary>
        /// <param name="graph">The graph to assert to.</param>
        /// <param name="graphSubject">The subject URI of the triple.</param>
        /// <param name="graphPredicate">The predicate URI of the triple.</param>
        /// <param name="graphObject">The literal object of the triple.</param>
        /// <param name="objectType">The data type URI of the literal object, if any.</param>
        public void AssertToGraphLiteralNode(IGraph graph, Uri graphSubject, Uri graphPredicate, string? graphObject, Uri? objectType = null)
        {
            if (graphObject != null)
            {
                graph.Assert(
                    new Triple(
                        graph.CreateUriNode(graphSubject),
                        graph.CreateUriNode(graphPredicate),
                        objectType is not null ? graph.CreateLiteralNode(graphObject, objectType) : graph.CreateLiteralNode(graphObject)
                    )
                );
            }
        }
    
        /// <summary>
        /// Asserts a triple to the graph with a blank node subject and a URI node object.
        /// </summary>
        /// <param name="graph">The graph to assert to.</param>
        /// <param name="graphSubject">The blank node subject of the triple.</param>
        /// <param name="graphPredicate">The predicate URI of the triple.</param>
        /// <param name="graphObject">The object URI of the triple.</param>
        public void AssertToGraphBlankAndUriNode(IGraph graph, IBlankNode graphSubject, Uri graphPredicate, Uri? graphObject)
        {
            if (graphObject != null)
            {
                graph.Assert(
                    new Triple(
                        graphSubject,
                        graph.CreateUriNode(graphPredicate),
                        graph.CreateUriNode(graphObject)
                    )
                );
            }
        }
    }