diff --git a/src/SQL2Linked/Implementations/ProjectStructuralData.cs b/src/SQL2Linked/Implementations/ProjectStructuralData.cs index 876f9dbfc91d8dd72793a5c4e3768e95f1b3e7a9..b50b0b1884c7e18c83b9dc8b807c78cd1ffe3764 100644 --- a/src/SQL2Linked/Implementations/ProjectStructuralData.cs +++ b/src/SQL2Linked/Implementations/ProjectStructuralData.cs @@ -1,179 +1,166 @@ -using Coscine.Database.DataModel; -using Coscine.Database.Models; -using Coscine.Database.Util; +using Coscine.ApiClient; +using Coscine.ApiClient.Core.Model; +using SQL2Linked.Utils; using VDS.RDF; -namespace SQL2Linked.Implementations +namespace SQL2Linked.Implementations; + +/// <summary> +/// Class responsible for converting project data into linked data graphs. +/// It retrieves project information from the API and then transforms this data into a series of RDF graphs, +/// making use of predefined URIs and RDF constructs. +/// </summary> +public class ProjectStructuralData : StructuralData<ProjectAdminDto> { - public class ProjectStructuralData : StructuralData<Project, ProjectModel> + /// <summary> + /// Asynchronously retrieves all project data, including deleted projects. + /// </summary> + /// <returns>A <see cref="Task"/> that represents the asynchronous operation and returns a collection of <see cref="ProjectAdminDto"/>.</returns> + /// <remarks>This override allows for the inclusion of deleted projects.</remarks> + public override async Task<IEnumerable<ProjectAdminDto>> GetAll() { - public readonly Uri org = new("http://www.w3.org/ns/org#"); - public readonly Uri dcat = new("http://www.w3.org/ns/dcat#"); - public readonly Uri dcterms = new("http://purl.org/dc/terms/"); - public readonly Uri foaf = new("http://xmlns.com/foaf/0.1/"); - public readonly Uri vcard = new("http://www.w3.org/2006/vcard/ns#"); - public readonly Uri rdf = new("http://www.w3.org/1999/02/22-rdf-syntax-ns#"); - public readonly Uri cosc = new("https://purl.org/coscine/"); - - private readonly VisibilityModel VisibilityModel = new(); - private readonly ProjectRoleModel ProjectRoleModel = new(); - private readonly ProjectResourceModel ProjectResourceModel = new(); - private readonly ProjectInstituteModel ProjectInstituteModel = new(); - - // Override to also receive deleted projects - public override IEnumerable<Project> GetAll() - { - return DatabaseConnection.ConnectToDatabase((db) => - { - return - (from tableEntry in Model.GetITableFromDatabase(db) - select tableEntry).ToList(); - }); - } + return await RequestUtil.WrapPagedRequest<ProjectAdminDtoPagedResponse, ProjectAdminDto>( + (currentPage) => _adminApi.GetAllProjectsAsync(includeDeleted: true, pageNumber: currentPage, pageSize: 250) + ); + } - public override IEnumerable<IGraph> ConvertToLinkedData(IEnumerable<Project> entries) + /// <summary> + /// Converts a collection of project data entries into a set of RDF graphs. + /// Each project is transformed into a graph, with RDF triples representing various properties of the project. + /// </summary> + /// <param name="entries">A collection of <see cref="ProjectAdminDto"/> instances representing project data.</param> + /// <returns>A collection of <see cref="IGraph"/> instances, each representing an RDF graph of a project.</returns> + public override async Task<IEnumerable<IGraph>> ConvertToLinkedDataAsync(IEnumerable<ProjectAdminDto> entries) + { + var graphs = new List<IGraph>(); + var coscineHandlePrefix = UriHelper.TryCombineUri(RdfUris.HandlePrefix, _pidConfiguration.Prefix) + ?? throw new Exception("Could not combine handle prefix with PID prefix"); + + var coscineGraph = new Graph { - IEnumerable<Visibility> visibilities = VisibilityModel.GetAll(); - IEnumerable<ProjectRole> projectRoles = ProjectRoleModel.GetAll(); - IEnumerable<ProjectResource> projectResources = ProjectResourceModel.GetAll(); - IEnumerable<ProjectInstitute> projectInstitutes = ProjectInstituteModel.GetAll(); + BaseUri = RdfUris.CoscinePrefix + }; + AssertToGraphUriNode(coscineGraph, RdfUris.CoscinePrefix, RdfUris.DcatCatalog, RdfUris.CoscineProjects); + AssertToGraphUriNode(coscineGraph, RdfUris.CoscinePrefix, RdfUris.DcatCatalog, RdfUris.CoscineResources); + graphs.Add(coscineGraph); - var graphs = new List<IGraph>(); - var projectUrlHandlePrefix = "https://hdl.handle.net/" + Prefix; - var projectUrlPrefix = "https://purl.org/coscine/projects"; - var resourceUrlPrefix = "https://purl.org/coscine/resources"; + foreach (var entry in entries) + { + var projectGraphName = UriHelper.TryCombineUri(RdfUris.CoscineProjects, entry.Id) + ?? throw new Exception("Could not combine projects prefix with project ID"); + var projectHandleName = UriHelper.TryCombineUri(coscineHandlePrefix, entry.Id); - var coscineGraph = new Graph + var graph = new Graph { - BaseUri = cosc + BaseUri = projectGraphName }; - AssertToGraphUriNode(coscineGraph, cosc.AbsoluteUri, dcat + "catalog", projectUrlPrefix + "/"); - AssertToGraphUriNode(coscineGraph, cosc.AbsoluteUri, dcat + "catalog", resourceUrlPrefix + "/"); - graphs.Add(coscineGraph); - foreach (var entry in entries) - { - var projectGraphName = $"{projectUrlPrefix}/{entry.Id}"; - var projectHandleName = $"{projectUrlHandlePrefix}/{entry.Id}"; - - var graph = new Graph - { - BaseUri = new Uri(projectGraphName) - }; + AssertToGraphUriNode(graph, projectGraphName, RdfUris.A, RdfUris.DcatCatalogClass); + Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {RdfUris.A} {RdfUris.DcatCatalogClass}'. "); - AssertToGraphUriNode(graph, projectGraphName, rdf + "type", dcat + "Catalog"); - Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {rdf}type {dcat}Catalog'. "); + AssertToGraphUriNode(graph, projectGraphName, RdfUris.A, RdfUris.OrgOrganizationalCollaborationClass); + Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {RdfUris.A} {RdfUris.OrgOrganizationalCollaborationClass}'. "); - AssertToGraphUriNode(graph, projectGraphName, rdf + "type", org + "OrganizationalCollaboration"); - Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {rdf}type {org}OrganizationalCollaboration'. "); + AssertToGraphUriNode(graph, projectGraphName, RdfUris.A, RdfUris.VcardGroupClass); + Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {RdfUris.A} {RdfUris.VcardGroupClass}'. "); - AssertToGraphUriNode(graph, projectGraphName, rdf + "type", vcard + "Group"); - Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {rdf}type {vcard}Group'. "); + AssertToGraphLiteralNode(graph, projectGraphName, RdfUris.DcTermsTitle, entry.Name); + Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {RdfUris.DcTermsTitle} {entry.Name}'. "); - AssertToGraphLiteralNode(graph, projectGraphName, dcterms + "title", entry.ProjectName); - Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {dcterms}title {entry.ProjectName}'. "); + AssertToGraphLiteralNode(graph, projectGraphName, RdfUris.DcTermsDescription, entry.Description); + Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {RdfUris.DcTermsDescription} {entry.Description}'. "); - AssertToGraphLiteralNode(graph, projectGraphName, dcterms + "description", entry.Description); - Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {dcterms}description {entry.Description}'. "); + AssertToGraphLiteralNode(graph, projectGraphName, RdfUris.DcTermsStartDate, entry.StartDate.ToString(), new Uri("http://www.w3.org/2001/XMLSchema#dateTime")); + Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {RdfUris.DcTermsStartDate} {entry.StartDate}'. "); - AssertToGraphLiteralNode(graph, projectGraphName, dcat + "startDate", entry.StartDate.ToString(), new Uri("http://www.w3.org/2001/XMLSchema#dateTime")); - Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {dcat}startDate {entry.StartDate}'. "); + AssertToGraphLiteralNode(graph, projectGraphName, RdfUris.DcTermsEndDate, entry.EndDate.ToString(), new Uri("http://www.w3.org/2001/XMLSchema#dateTime")); + Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {RdfUris.DcTermsEndDate} {entry.EndDate}'. "); - AssertToGraphLiteralNode(graph, projectGraphName, dcat + "endDate", entry.EndDate.ToString(), new Uri("http://www.w3.org/2001/XMLSchema#dateTime")); - Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {dcat}endDate {entry.EndDate}'. "); - - if (!string.IsNullOrWhiteSpace(entry.Keywords)) + if (entry.Keywords.Count > 0) + { + foreach (var keyword in entry.Keywords) { - var listKeywords = entry.Keywords.Split(';').ToList(); - foreach (var keyword in listKeywords) - { - AssertToGraphLiteralNode(graph, projectGraphName, dcterms + "subject", keyword); - Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {dcterms}subject {keyword}'. "); - } + AssertToGraphLiteralNode(graph, projectGraphName, RdfUris.DcTermsSubject, keyword); + Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {RdfUris.DcTermsSubject} {keyword}'. "); } + } - AssertToGraphLiteralNode(graph, projectGraphName, dcterms + "alternative", entry.DisplayName); - Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {dcterms}alternative {entry.DisplayName}'. "); + AssertToGraphLiteralNode(graph, projectGraphName, RdfUris.DcTermsAlternative, entry.DisplayName); + Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {RdfUris.DcTermsAlternative} {entry.DisplayName}'. "); - AssertToGraphLiteralNode(graph, projectGraphName, dcterms + "rightsHolder", entry.PrincipleInvestigators); - Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {dcterms}rightsHolder {entry.PrincipleInvestigators}'. "); + AssertToGraphLiteralNode(graph, projectGraphName, RdfUris.DcTermsRightsHolder, entry.PrincipleInvestigators); + Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {RdfUris.DcTermsRightsHolder} {entry.PrincipleInvestigators}'. "); - AssertToGraphLiteralNode(graph, projectGraphName, "https://schema.org/funding", entry.GrantId); - Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} https://schema.org/funding {entry.GrantId}'. "); + AssertToGraphLiteralNode(graph, projectGraphName, RdfUris.SchemaFunding, entry.GrantId); + Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {RdfUris.SchemaFunding} {entry.GrantId}'. "); - foreach (var visibility in visibilities) - { - if (entry.VisibilityId == visibility.Id && visibility.DisplayName.Contains("Public")) - { - AssertToGraphUriNode(graph, projectGraphName, cosc + "terms/project#visibility", cosc + $"terms/visibility#public"); - Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {cosc}terms/project#visibility {cosc}terms/visibility#public'. "); - break; - } - else if (entry.VisibilityId == visibility.Id && visibility.DisplayName.Contains("Project Members")) - { - AssertToGraphUriNode(graph, projectGraphName, cosc + "terms/project#visibility", cosc + $"terms/visibility#projectMember"); - Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {cosc}terms/project#visibility {cosc}terms/visibility#projectMember'. "); - break; - } - } + if (entry.Visibility.DisplayName.Contains("Public")) + { + AssertToGraphUriNode(graph, projectGraphName, RdfUris.CoscineTermsProjectVisibility, RdfUris.CoscineTermsVisibilityPublic); + Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {RdfUris.CoscineTermsProjectVisibility} {RdfUris.CoscineTermsVisibilityPublic}'. "); + } + else if (entry.Visibility.DisplayName.Contains("Project Members")) + { + AssertToGraphUriNode(graph, projectGraphName, RdfUris.CoscineTermsProjectVisibility, RdfUris.CoscineTermsVisibilityProjectMember); + Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {RdfUris.CoscineTermsProjectVisibility} {RdfUris.CoscineTermsVisibilityProjectMember}'. "); + } - AssertToGraphLiteralNode(graph, projectGraphName, cosc + "terms/project#deleted", entry.Deleted.ToString().ToLower(), new Uri("http://www.w3.org/2001/XMLSchema#boolean")); - Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {cosc}terms/project#deleted {entry.Deleted}'. "); + AssertToGraphLiteralNode(graph, projectGraphName, RdfUris.CoscineTermsProjectDeleted, entry.Deleted.ToString().ToLower(), RdfUris.XsdBoolean); + Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {RdfUris.CoscineTermsProjectDeleted} {entry.Deleted}'. "); - AssertToGraphLiteralNode(graph, projectGraphName, cosc + "terms/project#slug", entry.Slug); - Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {cosc}terms/project#slug {entry.Slug}'. "); + AssertToGraphLiteralNode(graph, projectGraphName, RdfUris.CoscineTermsProjectSlug, entry.Slug); + Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {RdfUris.CoscineTermsProjectSlug} {entry.Slug}'. "); - AssertToGraphUriNode(graph, projectGraphName, foaf + "homepage", projectHandleName); - Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {foaf}homepage {projectHandleName}'. "); + AssertToGraphUriNode(graph, projectGraphName, RdfUris.FoafHomepage, projectHandleName); + Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {RdfUris.FoafHomepage} {projectHandleName}'. "); - foreach (var projectRole in projectRoles.Where(p => p.ProjectId.Equals(entry.Id))) - { - AssertToGraphUriNode(graph, projectGraphName, vcard + "hasMember", cosc + "users/" + projectRole.UserId); - Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {vcard}hasMember {cosc}users/{projectRole.UserId}'. "); - - var blankNode = graph.CreateBlankNode(); + foreach (var projectRole in entry.ProjectRoles) + { + AssertToGraphUriNode(graph, projectGraphName, RdfUris.VcardHasMember, UriHelper.TryCombineUri(RdfUris.CoscineUsers, projectRole.UserId)); + Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {RdfUris.VcardHasMember} {UriHelper.TryCombineUri(RdfUris.CoscineUsers, projectRole.UserId)}'. "); - AssertToGraphBlankAndUriNode(graph, blankNode, rdf + "type", org + "Membership"); - Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{blankNode} {rdf}type {org}Membership'. "); + var blankNode = graph.CreateBlankNode(); - AssertToGraphBlankAndUriNode(graph, blankNode, org + "organization", projectGraphName); - Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{blankNode} {org}organization {projectGraphName}'. "); + AssertToGraphBlankAndUriNode(graph, blankNode, RdfUris.A, RdfUris.OrgMembershipClass); + Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{blankNode} {RdfUris.A} {RdfUris.OrgMembershipClass}'. "); - AssertToGraphBlankAndUriNode(graph, blankNode, org + "role", cosc + "roles/" + projectRole.RoleId); - Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{blankNode} {org}role {cosc}roles/{projectRole.RoleId}'. "); + AssertToGraphBlankAndUriNode(graph, blankNode, RdfUris.OrgOrganization, projectGraphName); + Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{blankNode} {RdfUris.OrgOrganization} {projectGraphName}'. "); - AssertToGraphBlankAndUriNode(graph, blankNode, org + "member", cosc + "users/" + projectRole.UserId); - Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{blankNode} {org}member {cosc}users/{projectRole.UserId}'. "); - } + AssertToGraphBlankAndUriNode(graph, blankNode, RdfUris.OrgRole, UriHelper.TryCombineUri(RdfUris.CoscineRoles, projectRole.RoleId)); + Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{blankNode} {RdfUris.OrgRole} {UriHelper.TryCombineUri(RdfUris.CoscineRoles, projectRole.RoleId)}'. "); - foreach (var projectResource in projectResources.Where(p => p.ProjectId.Equals(entry.Id))) - { - AssertToGraphUriNode(graph, projectGraphName, dcat + "catalog", $"{resourceUrlPrefix}/{projectResource.ResourceId}"); - Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {dcat}catalog {resourceUrlPrefix}/{projectResource.ResourceId}'. "); - } + AssertToGraphBlankAndUriNode(graph, blankNode, RdfUris.OrgMember, UriHelper.TryCombineUri(RdfUris.CoscineUsers, projectRole.UserId)); + Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{blankNode} {RdfUris.OrgMember} {UriHelper.TryCombineUri(RdfUris.CoscineUsers, projectRole.UserId)}'. "); + } - foreach (var projectInstitute in projectInstitutes.Where(p => p.ProjectId.Equals(entry.Id))) - { - AssertToGraphUriNode(graph, projectGraphName, org + "organization", projectInstitute.OrganizationUrl); - Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {org}organization {projectInstitute.OrganizationUrl}'. "); - } + foreach (var projectResource in entry.ProjectResources) + { + AssertToGraphUriNode(graph, projectGraphName, RdfUris.DcatCatalog, UriHelper.TryCombineUri(RdfUris.CoscineResources, projectResource.ResourceId)); + Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {RdfUris.DcatCatalog} {UriHelper.TryCombineUri(RdfUris.CoscineResources, projectResource.ResourceId)}'. "); + } - if (entry.Creator is not null) - { - AssertToGraphUriNode(graph, projectGraphName, dcterms + "creator", cosc + $"users/{entry.Creator}"); - Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {dcterms}creator {cosc}users/{entry.Creator}'. "); - } + foreach (var projectInstitute in entry.Organizations) + { + AssertToGraphUriNode(graph, projectGraphName, RdfUris.OrgOrganization, new Uri(projectInstitute.Uri)); + Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {RdfUris.OrgOrganization} {projectInstitute.Uri}'. "); + } + if (entry.Creator is not null) + { + AssertToGraphUriNode(graph, projectGraphName, RdfUris.DcTermsCreator, UriHelper.TryCombineUri(RdfUris.CoscineUsers, entry.Creator.Id)); + Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {RdfUris.DcTermsCreator} {UriHelper.TryCombineUri(RdfUris.CoscineUsers, entry.Creator.Id)}'. "); + } - if (entry.DateCreated is not null && entry.DateCreated.HasValue) - { - AssertToGraphLiteralNode(graph, projectGraphName, dcterms + "created", entry.DateCreated.Value.ToString(), new Uri("http://www.w3.org/2001/XMLSchema#dateTime")); - Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {dcterms}created {entry.DateCreated}'. "); - } - graphs.Add(graph); + if (entry.CreationDate is not null && entry.CreationDate.HasValue) + { + AssertToGraphLiteralNode(graph, projectGraphName, RdfUris.DcTermsCreated, entry.CreationDate.Value.ToString(), RdfUris.XsdDateTime); + Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{projectGraphName} {RdfUris.DcTermsCreated} {entry.CreationDate}'. "); } - return graphs; + + graphs.Add(graph); } + return await Task.FromResult(graphs); } } \ No newline at end of file diff --git a/src/SQL2Linked/Implementations/ResourceStructuralData.cs b/src/SQL2Linked/Implementations/ResourceStructuralData.cs index 53b8ab2a5811a691139456563c129d6576e4e421..67eb96c17c8de89a259cc314ee9a28a332a2dbd4 100644 --- a/src/SQL2Linked/Implementations/ResourceStructuralData.cs +++ b/src/SQL2Linked/Implementations/ResourceStructuralData.cs @@ -1,189 +1,162 @@ -using Coscine.Database.DataModel; -using Coscine.Database.Models; -using Coscine.Database.Util; +using Coscine.ApiClient; +using Coscine.ApiClient.Core.Model; +using Newtonsoft.Json; +using SQL2Linked.Utils; using VDS.RDF; -using VDS.RDF.Query; -namespace SQL2Linked.Implementations +namespace SQL2Linked.Implementations; + +/// <summary> +/// Class responsible for converting resource data into linked data graphs. +/// It retrieves resource information from the API and then transforms this data into a series of RDF graphs, +/// making use of predefined URIs and RDF constructs. +/// </summary> +public class ResourceStructuralData : StructuralData<ResourceAdminDto> { - public class ResourceStructuralData : StructuralData<Resource, ResourceModel> + /// <summary> + /// Asynchronously retrieves all resource data, including deleted resources. + /// </summary> + /// <returns>A <see cref="Task"/> that represents the asynchronous operation and returns a collection of <see cref="ResourceAdminDto"/>.</returns> + /// <remarks>This override allows for the inclusion of deleted resources.</remarks> + public override async Task<IEnumerable<ResourceAdminDto>> GetAll() { - public readonly Uri org = new("http://www.w3.org/ns/org#"); - public readonly Uri dcat = new("http://www.w3.org/ns/dcat#"); - public readonly Uri dcterms = new("http://purl.org/dc/terms/"); - public readonly Uri acl = new("http://www.w3.org/ns/auth/acl#"); - public readonly Uri foaf = new("http://xmlns.com/foaf/0.1/"); - public readonly Uri pim = new("http://www.w3.org/ns/pim/space#"); - public readonly Uri rdf = new("http://www.w3.org/1999/02/22-rdf-syntax-ns#"); - public readonly Uri cosc = new("https://purl.org/coscine/"); - private VisibilityModel VisibilityModel = new VisibilityModel(); - private ProjectResourceModel ProjectResourceModel = new ProjectResourceModel(); - private LicenseModel LicenseModel = new LicenseModel(); - - private readonly Dictionary<string, string> _targetClassMap = new Dictionary<string, string>(); - - // Override to also receive deleted resources - public override IEnumerable<Resource> GetAll() - { - return DatabaseConnection.ConnectToDatabase((db) => - { - return - (from tableEntry in Model.GetITableFromDatabase(db) - select tableEntry).ToList(); - }); - } + return await RequestUtil.WrapPagedRequest<ResourceAdminDtoPagedResponse, ResourceAdminDto>( + (currentPage) => _adminApi.GetAllResourcesAsync(includeDeleted: true, pageNumber: currentPage, pageSize: 250) + ); + } - public override IEnumerable<IGraph> ConvertToLinkedData(IEnumerable<Resource> entries) - { - IEnumerable<Visibility> visibilities = VisibilityModel.GetAll(); - IEnumerable<ProjectResource> projectResources = ProjectResourceModel.GetAll(); - IEnumerable<License> licenses = LicenseModel.GetAll(); + /// <summary> + /// Converts a collection of resource data entries into a set of RDF graphs. + /// Each resource is transformed into a graph, with RDF triples representing various properties of the resource. + /// </summary> + /// <param name="entries">A collection of <see cref="ResourceAdminDto"/> instances representing resource data.</param> + /// <returns>A collection of <see cref="IGraph"/> instances, each representing an RDF graph of a resource.</returns> + public override async Task<IEnumerable<IGraph>> ConvertToLinkedDataAsync(IEnumerable<ResourceAdminDto> entries) + { + var graphs = new List<IGraph>(); + var coscineHandlePrefix = UriHelper.TryCombineUri(RdfUris.HandlePrefix, _pidConfiguration.Prefix) + ?? throw new Exception("Could not combine handle prefix with PID prefix"); - var graphs = new List<IGraph>(); - var resourceUrlHandlePrefix = "https://hdl.handle.net/" + Prefix; - var projectUrlPrefix = "https://purl.org/coscine/projects"; - var resourceUrlPrefix = "https://purl.org/coscine/resources"; + foreach (var entry in entries) + { + var resourceGraphName = UriHelper.TryCombineUri(RdfUris.CoscineResources, entry.Id) + ?? throw new Exception("Could not combine resources prefix with resource ID"); + var resourceHandleName = UriHelper.TryCombineUri(coscineHandlePrefix, entry.Id); - foreach (var entry in entries) + var graph = new Graph { - var resourceGraphName = $"{resourceUrlPrefix}/{entry.Id}"; - var resourceHandleName = $"{resourceUrlHandlePrefix}/{entry.Id}"; + BaseUri = resourceGraphName + }; - var graph = new Graph(); - graph.BaseUri = new Uri(resourceGraphName); + AssertToGraphUriNode(graph, resourceGraphName, RdfUris.A, RdfUris.DcatCatalogClass); + Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{resourceGraphName} {RdfUris.A} {RdfUris.DcatCatalogClass}'. "); - AssertToGraphUriNode(graph, resourceGraphName, rdf + "type", dcat + "Catalog"); - Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{resourceGraphName} {rdf}type {dcat}Catalog'. "); + AssertToGraphUriNode(graph, resourceGraphName, RdfUris.A, RdfUris.PimStorageClass); + Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{resourceGraphName} {RdfUris.A} {RdfUris.PimStorageClass}'. "); - AssertToGraphUriNode(graph, resourceGraphName, rdf + "type", pim + "Storage"); - Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{resourceGraphName} {rdf}type {pim}Storage'. "); + AssertToGraphUriNode(graph, resourceGraphName, RdfUris.DcatService, UriHelper.TryCombineUri(RdfUris.CoscineResourceTypes, entry.Type.Id)); + Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{resourceGraphName} {RdfUris.DcatService} {UriHelper.TryCombineUri(RdfUris.CoscineResourceTypes, entry.Type.Id)}'. "); - AssertToGraphUriNode(graph, resourceGraphName, dcat + "service", cosc + $"resourcetypes/{entry.TypeId}"); - Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{resourceGraphName} {dcat}service {cosc}resourcetypes/{entry.TypeId}'. "); + AssertToGraphLiteralNode(graph, resourceGraphName, RdfUris.DcTermsTitle, entry.Name); + Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{resourceGraphName} {RdfUris.DcTermsTitle} {entry.Name}'. "); - AssertToGraphLiteralNode(graph, resourceGraphName, dcterms + "title", entry.ResourceName); - Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{resourceGraphName} {dcterms}title {entry.ResourceName}'. "); + AssertToGraphLiteralNode(graph, resourceGraphName, RdfUris.DcTermsAlternative, entry.DisplayName); + Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{resourceGraphName} {RdfUris.DcTermsAlternative} {entry.DisplayName}'. "); - AssertToGraphLiteralNode(graph, resourceGraphName, dcterms + "alternative", entry.DisplayName); - Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{resourceGraphName} {dcterms}alternative {entry.DisplayName}'. "); - - foreach (var visibility in visibilities) - { - if (entry.VisibilityId == visibility.Id && visibility.DisplayName.Contains("Public")) - { - AssertToGraphUriNode(graph, resourceGraphName, cosc + "terms/resource#visibility", cosc + $"terms/visibility#public"); - Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{resourceGraphName} {cosc}terms/resource#visibility {cosc}terms/visibility#public'. "); - break; - } - else if (entry.VisibilityId == visibility.Id && visibility.DisplayName.Contains("Project Members")) - { - AssertToGraphUriNode(graph, resourceGraphName, cosc + "terms/resource#visibility", cosc + $"terms/visibility#projectMember"); - Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{resourceGraphName} {cosc}terms/resource#visibility {cosc}terms/visibility#projectMember'. "); - break; - } - } + if (entry.Visibility.DisplayName.Contains("Public")) + { + AssertToGraphUriNode(graph, resourceGraphName, RdfUris.CoscineTermsResourceVisibility, RdfUris.CoscineTermsVisibilityPublic); + Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{resourceGraphName} {RdfUris.CoscineTermsResourceVisibility} {RdfUris.CoscineTermsVisibilityPublic}'. "); + } + else if (entry.Visibility.DisplayName.Contains("Project Members")) + { + AssertToGraphUriNode(graph, resourceGraphName, RdfUris.CoscineTermsResourceVisibility, RdfUris.CoscineTermsVisibilityProjectMember); + Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{resourceGraphName} {RdfUris.CoscineTermsResourceVisibility} {RdfUris.CoscineTermsVisibilityProjectMember}'. "); + } - foreach (var license in licenses) - { - if (entry.LicenseId == license.Id) - { - AssertToGraphLiteralNode(graph, resourceGraphName, dcterms + "license", license.DisplayName); - Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{resourceGraphName} {dcterms}license {license.DisplayName}'. "); - break; - } - } + if (entry.License is not null) + { + AssertToGraphLiteralNode(graph, resourceGraphName, RdfUris.DcTermsLicense, entry.License.DisplayName); + Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{resourceGraphName} {RdfUris.DcTermsLicense} {entry.License.DisplayName}'. "); + } - if (!string.IsNullOrWhiteSpace(entry.Keywords)) + if (entry.Keywords.Count > 0) + { + foreach (var keyword in entry.Keywords) { - var listKeywords = entry.Keywords.Split(';').ToList(); - foreach (var keyword in listKeywords) - { - AssertToGraphLiteralNode(graph, resourceGraphName, dcterms + "subject", keyword); - Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{resourceGraphName} {dcterms}subject {keyword}'. "); - } + AssertToGraphLiteralNode(graph, resourceGraphName, RdfUris.DcTermsSubject, keyword); + Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{resourceGraphName} {RdfUris.DcTermsSubject} {keyword}'. "); } + } - AssertToGraphLiteralNode(graph, resourceGraphName, dcterms + "rights", entry.UsageRights); - Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{resourceGraphName} {dcterms}rights {entry.UsageRights}'. "); + AssertToGraphLiteralNode(graph, resourceGraphName, RdfUris.DcTermsRights, entry.UsageRights); + Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{resourceGraphName} {RdfUris.DcTermsRights} {entry.UsageRights}'. "); - AssertToGraphLiteralNode(graph, resourceGraphName, dcterms + "description", entry.Description); - Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{resourceGraphName} {dcterms}description {entry.Description}'. "); + AssertToGraphLiteralNode(graph, resourceGraphName, RdfUris.DcTermsDescription, entry.Description); + Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{resourceGraphName} {RdfUris.DcTermsDescription} {entry.Description}'. "); - // Skipping broken resources - if (string.IsNullOrWhiteSpace(entry.ApplicationProfile)) - { - continue; - } - - AssertToGraphUriNode(graph, resourceGraphName, dcterms + "conformsTo", entry.ApplicationProfile); - Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{resourceGraphName} {dcterms}conformsTo {entry.ApplicationProfile}'. "); + // Skipping broken resources + if (string.IsNullOrWhiteSpace(entry.ApplicationProfile.Uri)) + { + continue; + } - AssertToGraphLiteralNode(graph, resourceGraphName, cosc + "terms/resource#fixedValues", entry.FixedValues); - Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{resourceGraphName} {cosc}terms/resource#fixedValues {entry.FixedValues}'. "); + AssertToGraphUriNode(graph, resourceGraphName, RdfUris.DcTermsConformsTo, new Uri(entry.ApplicationProfile.Uri)); + Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{resourceGraphName} {RdfUris.DcTermsConformsTo} {entry.ApplicationProfile}'. "); - if (entry.Creator is not null) - { - AssertToGraphUriNode(graph, resourceGraphName, dcterms + "creator", cosc + $"users/{entry.Creator}"); - Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{resourceGraphName} {dcterms}creator {cosc}users/{entry.Creator}'. "); - } + AssertToGraphLiteralNode(graph, resourceGraphName, RdfUris.CoscineTermsResourceFixedValues, JsonConvert.SerializeObject(entry.FixedValues)); + Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{resourceGraphName} {RdfUris.CoscineTermsResourceFixedValues} {JsonConvert.SerializeObject(entry.FixedValues)}'. "); - AssertToGraphLiteralNode(graph, resourceGraphName, cosc + "terms/resource#archived", entry.Archived.ToString().ToLower(), new Uri("http://www.w3.org/2001/XMLSchema#boolean")); - Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{resourceGraphName} {cosc}terms/resource#archived {entry.Archived}'. "); + if (entry.Creator is not null) + { + AssertToGraphUriNode(graph, resourceGraphName, RdfUris.DcTermsCreator, UriHelper.TryCombineUri(RdfUris.CoscineUsers, entry.Creator.Id)); + Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{resourceGraphName} {RdfUris.DcTermsCreator} {UriHelper.TryCombineUri(RdfUris.CoscineUsers, entry.Creator.Id)}'. "); + } - AssertToGraphUriNode(graph, resourceGraphName, foaf + "homepage", resourceHandleName); - Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{resourceGraphName} {foaf}homepage {resourceGraphName}'. "); + AssertToGraphLiteralNode(graph, resourceGraphName, RdfUris.CoscineTermsResourceArchived, entry.Archived.ToString().ToLower(), new Uri("http://www.w3.org/2001/XMLSchema#boolean")); + Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{resourceGraphName} {RdfUris.CoscineTermsResourceArchived} {entry.Archived}'. "); - AssertToGraphLiteralNode(graph, resourceGraphName, cosc + "terms/resource#deleted", entry.Deleted.ToString().ToLower(), new Uri("http://www.w3.org/2001/XMLSchema#boolean")); - Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{resourceGraphName} {cosc}terms/resource#deleted {entry.Deleted}'. "); + AssertToGraphUriNode(graph, resourceGraphName, RdfUris.FoafHomepage, resourceHandleName); + Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{resourceGraphName} {RdfUris.FoafHomepage} {resourceGraphName}'. "); - // Reinstate the catalog assignments - var cmdString = new SparqlParameterizedString - { - CommandText = "SELECT DISTINCT ?o WHERE { <" + resourceGraphName + "> <http://www.w3.org/ns/dcat#catalog> ?o }" - }; - var resultSet = WrapRequest(() => RdfStoreConnector.QueryEndpoint.QueryWithResultSet(cmdString.ToString())); - foreach (var result in resultSet) - { - AssertToGraphUriNode(graph, resourceGraphName, dcat + "catalog", result[0].ToString()); - Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{resourceGraphName} {dcat}catalog {result[0].ToString()}'. "); - } + AssertToGraphLiteralNode(graph, resourceGraphName, RdfUris.CoscineTermsResourceDeleted, entry.Deleted.ToString().ToLower(), new Uri("http://www.w3.org/2001/XMLSchema#boolean")); + Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{resourceGraphName} {RdfUris.CoscineTermsResourceDeleted} {entry.Deleted}'. "); - foreach (var projectResource in projectResources) + foreach (var projectResource in entry.ProjectResources) + { + if (entry.Id == projectResource.ResourceId) { - if (entry.Id == projectResource.ResourceId) - { - var blankNode = graph.CreateBlankNode(); - - AssertToGraphBlankAndUriNode(graph, blankNode, rdf + "type", acl + "Authorization"); - Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{blankNode} {rdf}type {acl}Authorization'. "); + var blankNode = graph.CreateBlankNode(); - AssertToGraphBlankAndUriNode(graph, blankNode, acl + "agentGroup", $"{projectUrlPrefix}/{projectResource.ProjectId}"); - Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{blankNode} {acl}agentGroup {projectUrlPrefix}/{projectResource.ProjectId}'. "); + AssertToGraphBlankAndUriNode(graph, blankNode, RdfUris.A, RdfUris.AclAuthorizationClass); + Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{blankNode} {RdfUris.A} {RdfUris.AclAuthorizationClass}'. "); - AssertToGraphBlankAndUriNode(graph, blankNode, acl + "accessTo", resourceGraphName); - Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{blankNode} {acl}accessTo {resourceGraphName}'. "); + AssertToGraphBlankAndUriNode(graph, blankNode, RdfUris.AclAgentGroup, UriHelper.TryCombineUri(RdfUris.CoscineProjects, projectResource.ProjectId)); + Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{blankNode} {RdfUris.AclAgentGroup} {UriHelper.TryCombineUri(RdfUris.CoscineProjects, projectResource.ProjectId)}'. "); - AssertToGraphBlankAndUriNode(graph, blankNode, acl + "default", resourceGraphName); - Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{blankNode} {acl}default {resourceGraphName}'. "); + AssertToGraphBlankAndUriNode(graph, blankNode, RdfUris.AclAccessTo, resourceGraphName); + Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{blankNode} {RdfUris.AclAccessTo} {resourceGraphName}'. "); - AssertToGraphBlankAndUriNode(graph, blankNode, acl + "mode", acl + "Read"); - Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{blankNode} {acl}accessTo {acl}Read'. "); + AssertToGraphBlankAndUriNode(graph, blankNode, RdfUris.AclDefault, resourceGraphName); + Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{blankNode} {RdfUris.AclDefault} {resourceGraphName}'. "); - AssertToGraphBlankAndUriNode(graph, blankNode, acl + "mode", acl + "Write"); - Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{blankNode} {acl}accessTo {acl}Write'. "); - } - } + AssertToGraphBlankAndUriNode(graph, blankNode, RdfUris.AclMode, RdfUris.AclReadClass); + Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{blankNode} {RdfUris.AclMode} {RdfUris.AclReadClass}'. "); - if (entry.DateCreated is not null && entry.DateCreated.HasValue) - { - AssertToGraphLiteralNode(graph, resourceGraphName, dcterms + "created", entry.DateCreated.Value.ToString(), new Uri("http://www.w3.org/2001/XMLSchema#dateTime")); - Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{resourceGraphName} {dcterms}created {entry.DateCreated}'. "); + AssertToGraphBlankAndUriNode(graph, blankNode, RdfUris.AclMode, RdfUris.AclWriteClass); + Console.WriteLine($"For resource '{entry.DisplayName}' will migrate triple '{blankNode} {RdfUris.AclMode} {RdfUris.AclWriteClass}'. "); } + } - graphs.Add(graph); + if (entry.DateCreated is not null && entry.DateCreated.HasValue) + { + AssertToGraphLiteralNode(graph, resourceGraphName, RdfUris.DcTermsCreated, entry.DateCreated.Value.ToString(), new Uri("http://www.w3.org/2001/XMLSchema#dateTime")); + Console.WriteLine($"For project '{entry.DisplayName}' will migrate triple '{resourceGraphName} {RdfUris.DcTermsCreated} {entry.DateCreated}'. "); } - return graphs; + graphs.Add(graph); } + + return await Task.FromResult(graphs); } } diff --git a/src/SQL2Linked/Implementations/ResourceTypeStructuralData.cs b/src/SQL2Linked/Implementations/ResourceTypeStructuralData.cs index d2536f60901c4bc2d31ed73058c60906585c7f82..db049aeb726692fb5588926c958b40aad4655b78 100644 --- a/src/SQL2Linked/Implementations/ResourceTypeStructuralData.cs +++ b/src/SQL2Linked/Implementations/ResourceTypeStructuralData.cs @@ -1,57 +1,97 @@ -using Coscine.Database.DataModel; -using Coscine.Database.Models; +using Coscine.ApiClient.Core.Api; +using Coscine.ApiClient.Core.Model; +using SQL2Linked.Utils; using VDS.RDF; +using VDS.RDF.Parsing; -namespace SQL2Linked.Implementations +namespace SQL2Linked.Implementations; + +/// <summary> +/// Class responsible for converting resource type data into linked data graphs. +/// It retrieves resource type information from the API and then transforms this data into a series of RDF graphs, +/// making use of predefined URIs and RDF constructs. +/// </summary> +public class ResourceTypeStructuralData : StructuralData<ResourceTypeInformationDto> { - public class ResourceTypeStructuralData : StructuralData<ResourceType, ResourceTypeModel> + /// <summary> + /// Asynchronously retrieves all resource type data. + /// </summary> + /// <returns>A <see cref="Task"/> that represents the asynchronous operation and returns a collection of <see cref="ResourceTypeInformationDto"/>.</returns> + public override async Task<IEnumerable<ResourceTypeInformationDto>> GetAll() + { + var resourceTypeApi = new ResourceTypeApi(_apiConfiguration); + var resourceTypeInformationsResponse = await resourceTypeApi.GetAllResourceTypesInformationAsync(); + return resourceTypeInformationsResponse.Data; + } + + /// <summary> + /// Converts a collection of resource type data entries into a set of RDF graphs. + /// Each resource type is transformed into a graph, with RDF triples representing various properties of the resource type. + /// </summary> + /// <param name="entries">A collection of <see cref="ResourceTypeInformationDto"/> instances representing resource type data.</param> + /// <returns>A collection of <see cref="IGraph"/> instances, each representing an RDF graph of a resource type.</returns> + public override async Task<IEnumerable<IGraph>> ConvertToLinkedDataAsync(IEnumerable<ResourceTypeInformationDto> entries) { - public readonly string ResourceTypeUrlPrefix = "https://purl.org/coscine/resourcetypes"; - public readonly Uri rdf = new("http://www.w3.org/1999/02/22-rdf-syntax-ns#"); - public readonly Uri dcat = new("http://www.w3.org/ns/dcat#"); - public readonly Uri dcterms = new("http://purl.org/dc/terms/"); - public override IEnumerable<IGraph> ConvertToLinkedData(IEnumerable<ResourceType> entries) + var graphs = new List<IGraph>(); + + foreach (var entry in entries) { + var resourceTypeGraphName = UriHelper.TryCombineUri(RdfUris.CoscineResourceTypes, entry.Id) + ?? throw new Exception("Could not combine resource types prefix with resource type ID"); + var response = _adminApi.GetMetadataGraph(resourceTypeGraphName.AbsoluteUri, RdfFormat.TextTurtle); + + var graph = new Graph() + { + BaseUri = resourceTypeGraphName + }; + + graph.LoadFromString(response.Data.Content, new TurtleParser()); - var graphs = new List<IGraph>(); - - foreach (var entry in entries) - { - var resourceTypeGraphName = $"{ResourceTypeUrlPrefix}/{entry.Id}"; - var graph = RdfStoreConnector.GetGraph(resourceTypeGraphName); - - // check if a triple with a dcat:DataService already exists in the resourcetype graph - var getTriplesDcatDataService = graph.GetTriplesWithObject(new Uri(dcat + "DataService")); - - if (!getTriplesDcatDataService.Any()) - { - AssertToGraphUriNode(graph, resourceTypeGraphName, rdf + "type", dcat + "DataService"); - Console.WriteLine($"For resource type '{entry.DisplayName}' will migrate triple '{graph.BaseUri} {rdf}type {dcat}DataService'. "); - } - else - { - Console.WriteLine($"For resource type '{entry.DisplayName}' will NOT migrate triple '{graph.BaseUri} {rdf}type {dcat}DataService'. "); - } - - // check if a triple with dcterms:title '{entry.DisplayName}' already exists in the role graph - var getTriplesDctermsTitle = graph.GetTriplesWithPredicate(new Uri(dcterms + "title")); - - if (!getTriplesDctermsTitle.Any()) - { - AssertToGraphLiteralNode(graph, resourceTypeGraphName, dcterms + "title", entry.DisplayName); - Console.WriteLine($"For resource type '{entry.DisplayName}' will migrate triple '{graph.BaseUri} {dcterms}title {entry.DisplayName}'. "); - } - else - { - Console.WriteLine($"For resource type '{entry.DisplayName}' will NOT migrate triple '{graph.BaseUri} {dcterms}title {entry.DisplayName}'. "); - } - if (!getTriplesDcatDataService.Any() || !getTriplesDctermsTitle.Any()) - { - graphs.Add(graph); - } + // check if a triple with a dcat:DataService already exists in the resourcetype graph + var getTriplesDcatDataService = graph.GetTriplesWithObject(RdfUris.DcatDataServiceClass); + + if (!getTriplesDcatDataService.Any()) + { + AssertToGraphUriNode(graph, resourceTypeGraphName, RdfUris.A, RdfUris.DcatDataServiceClass); + Console.WriteLine($"For resource type '{entry.SpecificType}' will migrate triple '{graph.BaseUri} {RdfUris.A} {RdfUris.DcatDataServiceClass}'. "); + } + else + { + Console.WriteLine($"For resource type '{entry.SpecificType}' will NOT migrate triple '{graph.BaseUri} {RdfUris.A} {RdfUris.DcatDataServiceClass}'. "); + } + + // check if a triple with dcterms:title '{entry.DisplayName}' already exists in the role graph + var getTriplesDctermsTitle = graph.GetTriplesWithPredicate(RdfUris.DcTermsTitle); + + if (!getTriplesDctermsTitle.Any()) + { + AssertToGraphLiteralNode(graph, resourceTypeGraphName, RdfUris.DcTermsTitle, entry.SpecificType); + Console.WriteLine($"For resource type '{entry.SpecificType}' will migrate triple '{graph.BaseUri} {RdfUris.DcTermsTitle} {entry.SpecificType}'. "); + } + else + { + Console.WriteLine($"For resource type '{entry.SpecificType}' will NOT migrate triple '{graph.BaseUri} {RdfUris.DcTermsTitle} {entry.SpecificType}'. "); + } + + + // check if a triple with dcterms:title '{entry.DisplayName}' already exists in the role graph + var getTriplesDctermsAlternative = graph.GetTriplesWithPredicate(RdfUris.DcTermsAlternative); + + if (!getTriplesDctermsAlternative.Any()) + { + AssertToGraphLiteralNode(graph, resourceTypeGraphName, RdfUris.DcTermsAlternative, entry.GeneralType); + Console.WriteLine($"For resource type '{entry.SpecificType}' will migrate triple '{graph.BaseUri} {RdfUris.DcTermsAlternative} {entry.GeneralType}'. "); + } + else + { + Console.WriteLine($"For resource type '{entry.SpecificType}' will NOT migrate triple '{graph.BaseUri} {RdfUris.DcTermsAlternative} {entry.GeneralType}'. "); + } + + if (!getTriplesDcatDataService.Any() || !getTriplesDctermsTitle.Any()) + { + graphs.Add(graph); } - return graphs; } + return await Task.FromResult(graphs); } - } \ No newline at end of file diff --git a/src/SQL2Linked/Implementations/RoleStructuralData.cs b/src/SQL2Linked/Implementations/RoleStructuralData.cs index 04b3176b724bb25d132cb20d9b7ad4e367af14d7..dcd81ce0407a6a7a787caa878820632656bbb73a 100644 --- a/src/SQL2Linked/Implementations/RoleStructuralData.cs +++ b/src/SQL2Linked/Implementations/RoleStructuralData.cs @@ -1,57 +1,84 @@ -using Coscine.Database.DataModel; -using Coscine.Database.Models; +using Coscine.ApiClient; +using Coscine.ApiClient.Core.Api; +using Coscine.ApiClient.Core.Model; +using SQL2Linked.Utils; using VDS.RDF; +using VDS.RDF.Parsing; -namespace SQL2Linked.Implementations +namespace SQL2Linked.Implementations; + +/// <summary> +/// Class responsible for converting role data into linked data graphs. +/// It retrieves role information from the API and then transforms this data into a series of RDF graphs, +/// making use of predefined URIs and RDF constructs. +/// </summary> +public class RoleStructuralData : StructuralData<RoleDto> { - public class RoleStructuralData : StructuralData<Role, RoleModel> + /// <summary> + /// Asynchronously retrieves all role data. + /// </summary> + /// <returns>A <see cref="Task"/> that represents the asynchronous operation and returns a collection of <see cref="RoleDto"/>.</returns> + public override async Task<IEnumerable<RoleDto>> GetAll() { - public readonly string RoleUrlPrefix = "https://purl.org/coscine/roles"; - public readonly Uri cosc = new("https://purl.org/coscine/"); - public readonly Uri rdf = new("http://www.w3.org/1999/02/22-rdf-syntax-ns#"); - public readonly Uri org = new("http://www.w3.org/ns/org#"); - public readonly Uri dcterms = new("http://purl.org/dc/terms/"); + var roleApi = new RoleApi(_apiConfiguration); + return await RequestUtil.WrapPagedRequest<RoleDtoPagedResponse, RoleDto>( + (currentPage) => roleApi.GetRolesAsync(pageNumber: currentPage) + ); + } - public override IEnumerable<IGraph> ConvertToLinkedData(IEnumerable<Role> entries) + /// <summary> + /// Converts a collection of role data entries into a set of RDF graphs. + /// Each role is transformed into a graph, with RDF triples representing various properties of the role. + /// </summary> + /// <param name="entries">A collection of <see cref="RoleDto"/> instances representing role data.</param> + /// <returns>A collection of <see cref="IGraph"/> instances, each representing an RDF graph of a role.</returns> + public override async Task<IEnumerable<IGraph>> ConvertToLinkedDataAsync(IEnumerable<RoleDto> entries) + { + var graphs = new List<IGraph>(); + + foreach (var entry in entries) { - var graphs = new List<IGraph>(); + var roleGraphName = UriHelper.TryCombineUri(RdfUris.CoscineRoles, entry.Id) + ?? throw new Exception("Could not combine role prefix with role ID"); + var response = await _adminApi.GetMetadataGraphAsync(roleGraphName.AbsoluteUri, RdfFormat.TextTurtle); + + var graph = new Graph() + { + BaseUri = roleGraphName + }; + + graph.LoadFromString(response.Data.Content, new TurtleParser()); + + // check if a triple with a org:role already exists in the role graph + var getTriplesOrgRole = graph.GetTriplesWithObject(RdfUris.OrgRoleClass); + + if (!getTriplesOrgRole.Any()) + { + AssertToGraphUriNode(graph, roleGraphName, RdfUris.A, RdfUris.OrgRoleClass); + Console.WriteLine($"For role '{entry.DisplayName}' will migrate triple '{graph.BaseUri} {RdfUris.A} {RdfUris.OrgRoleClass}'. "); + } + else + { + Console.WriteLine($"For role '{entry.DisplayName}' will NOT migrate triple '{graph.BaseUri} {RdfUris.A} {RdfUris.OrgRoleClass}'. "); + } + + // check if a triple with dcterms:title '{entry.DisplayName}' already exists in the role graph + var getTriplesDctermsTitle = graph.GetTriplesWithPredicate(RdfUris.DcTermsTitle); - foreach (var entry in entries) + if (!getTriplesDctermsTitle.Any()) + { + AssertToGraphLiteralNode(graph, roleGraphName, RdfUris.DcTermsTitle, entry.DisplayName); + Console.WriteLine($"For role '{entry.DisplayName}' will migrate triple '{graph.BaseUri} {RdfUris.DcTermsTitle} {entry.DisplayName}'. "); + } + else + { + Console.WriteLine($"For role '{entry.DisplayName}' will NOT migrate triple '{graph.BaseUri} {RdfUris.DcTermsTitle} {entry.DisplayName}'. "); + } + if (!getTriplesOrgRole.Any() || !getTriplesDctermsTitle.Any()) { - var roleGraphName = $"{RoleUrlPrefix}/{entry.Id}"; - var graph = RdfStoreConnector.GetGraph(roleGraphName); - - // check if a triple with a org:role already exists in the role graph - var getTriplesOrgRole = graph.GetTriplesWithObject(new Uri(org + "Role")); - - if (!getTriplesOrgRole.Any()) - { - AssertToGraphUriNode(graph, roleGraphName, rdf + "type", org + "Role"); - Console.WriteLine($"For role '{entry.DisplayName}' will migrate triple '{graph.BaseUri} {rdf}type {org}Role'. "); - } - else - { - Console.WriteLine($"For role '{entry.DisplayName}' will NOT migrate triple '{graph.BaseUri} {rdf}type {org}Role'. "); - } - - // check if a triple with dcterms:title '{entry.DisplayName}' already exists in the role graph - var getTriplesDctermsTitle = graph.GetTriplesWithPredicate(new Uri(dcterms + "title")); - - if (!getTriplesDctermsTitle.Any()) - { - AssertToGraphLiteralNode(graph, roleGraphName, dcterms + "title", entry.DisplayName); - Console.WriteLine($"For role '{entry.DisplayName}' will migrate triple '{graph.BaseUri} {dcterms}title {entry.DisplayName}'. "); - } - else - { - Console.WriteLine($"For role '{entry.DisplayName}' will NOT migrate triple '{graph.BaseUri} {dcterms}title {entry.DisplayName}'. "); - } - if (!getTriplesOrgRole.Any() || !getTriplesDctermsTitle.Any()) - { - graphs.Add(graph); - } + graphs.Add(graph); } - return graphs; } + return graphs; } } \ No newline at end of file diff --git a/src/SQL2Linked/Implementations/UserStructuralData.cs b/src/SQL2Linked/Implementations/UserStructuralData.cs index 59a6999999e5f24a7a89cb95e2c6c866c10880b4..374205cf49bec15db8636778402a3cadb243be29 100644 --- a/src/SQL2Linked/Implementations/UserStructuralData.cs +++ b/src/SQL2Linked/Implementations/UserStructuralData.cs @@ -1,53 +1,75 @@ -using Coscine.Database.DataModel; -using Coscine.Database.Models; -using Coscine.Metadata; +using Coscine.ApiClient; +using Coscine.ApiClient.Core.Model; +using SQL2Linked.Utils; using VDS.RDF; +using VDS.RDF.Parsing; -namespace SQL2Linked.Implementations +namespace SQL2Linked.Implementations; + +/// <summary> +/// Class responsible for converting user data into linked data graphs. +/// It retrieves user information from the API and then transforms this data into a series of RDF graphs, +/// making use of predefined URIs and RDF constructs. +/// </summary> +public class UserStructuralData : StructuralData<UserDto> { - public class UserStructuralData : StructuralData<User, UserModel> + /// <summary> + /// Asynchronously retrieves all user data only for users who have accepted the ToS. + /// </summary> + /// <returns>A <see cref="Task"/> that represents the asynchronous operation and returns a collection of <see cref="UserDto"/>.</returns> + /// <remarks>This override allows for the retrieval of users who have accepted the ToS.</remarks> + public override async Task<IEnumerable<UserDto>> GetAll() { - public readonly string UserUrlPrefix = "https://purl.org/coscine/users"; - public readonly Uri rdf = new("http://www.w3.org/1999/02/22-rdf-syntax-ns#"); - public readonly Uri foaf = new("http://xmlns.com/foaf/0.1/"); + return await RequestUtil.WrapPagedRequest<UserDtoPagedResponse, UserDto>( + (currentPage) => _adminApi.GetAllUsersAsync(tosAccepted: true, pageNumber: currentPage, pageSize: 250) + ); + } - // Override to only transform users which have accepted the TOS - public override IEnumerable<User> GetAll() - { - return Model.GetAllWhere((user) => user.Tosaccepteds.Any()); - } + /// <summary> + /// Converts a collection of user data entries into a set of RDF graphs. + /// Each user is transformed into a graph, with RDF triples representing various properties of the user. + /// </summary> + /// <param name="entries">A collection of <see cref="UserDto"/> instances representing user data.</param> + /// <returns>A collection of <see cref="IGraph"/> instances, each representing an RDF graph of a user.</returns> + public override async Task<IEnumerable<IGraph>> ConvertToLinkedDataAsync(IEnumerable<UserDto> entries) + { + var graphs = new List<IGraph>(); - public override IEnumerable<IGraph> ConvertToLinkedData(IEnumerable<User> entries) + foreach (var entry in entries) { - var graphs = new List<IGraph>(); + var userGraphName = UriHelper.TryCombineUri(RdfUris.CoscineUsers, entry.Id) + ?? throw new Exception("Could not combine users prefix with user ID"); + var response = _adminApi.GetMetadataGraph(userGraphName.AbsoluteUri, RdfFormat.TextTurtle); - foreach (var entry in entries) + var graph = new Graph() { - var userGraphName = $"{UserUrlPrefix}/{entry.Id}"; - var graph = RdfStoreConnector.GetGraph(userGraphName); + BaseUri = userGraphName + }; - // check if a triple with a foaf:Person already exists in the user graph - var getTriples = graph.GetTriplesWithObject(new Uri(foaf + "Person")); - // check if the current display name is already applied in the user graph - var getTriplesName = graph.GetTriplesWithPredicate(new Uri(foaf + "name")); + graph.LoadFromString(response.Data.Content, new TurtleParser()); - if (!getTriples.Any() || !getTriplesName.Any((triple) => triple.Object.ToString() == entry.DisplayName)) - { - AssertToGraphUriNode(graph, userGraphName, rdf + "type", foaf + "Person"); + // check if a triple with a foaf:Person already exists in the user graph + var getTriples = graph.GetTriplesWithObject(RdfUris.FoafPersonClass); + // check if the current display name is already applied in the user graph + var getTriplesName = graph.GetTriplesWithPredicate(RdfUris.FoafName); - graph.Retract(getTriplesName); - AssertToGraphLiteralNode(graph, userGraphName, foaf + "name", entry.DisplayName); + if (!getTriples.Any() || !getTriplesName.Any((triple) => (triple.Object as ILiteralNode)?.Value == entry.DisplayName)) + { + AssertToGraphUriNode(graph, userGraphName, RdfUris.A, RdfUris.FoafPersonClass); + Console.WriteLine($"For user '{entry.DisplayName}' will migrate triple '{userGraphName} {RdfUris.A} {RdfUris.FoafPersonClass}'. "); + + graph.Retract(getTriplesName); + AssertToGraphLiteralNode(graph, userGraphName, RdfUris.FoafName, entry.DisplayName); - graphs.Add(graph); + graphs.Add(graph); - Console.WriteLine($"Will migrate user '{entry.DisplayName}' with id '{entry.Id}'."); - } - else - { - Console.WriteLine($"Will NOT migrate user '{entry.DisplayName}' with id '{entry.Id}'."); - } + Console.WriteLine($"Will migrate user '{entry.DisplayName}' with id '{entry.Id}'."); + } + else + { + Console.WriteLine($"Will NOT migrate user '{entry.DisplayName}' with id '{entry.Id}'."); } - return graphs; } + return await Task.FromResult(graphs); } } diff --git a/src/SQL2Linked/Models/ConfigurationModels/PidConfiguration.cs b/src/SQL2Linked/Models/ConfigurationModels/PidConfiguration.cs new file mode 100644 index 0000000000000000000000000000000000000000..ebdea928ae26add825237136a7a8681821f9edbd --- /dev/null +++ b/src/SQL2Linked/Models/ConfigurationModels/PidConfiguration.cs @@ -0,0 +1,17 @@ +namespace SQL2Linked.Models.ConfigurationModels; + +/// <summary> +/// Represents the configuration settings for the PID ePIC API used in the application. +/// </summary> +public class PidConfiguration +{ + /// <summary> + /// The section name in the configuration file. + /// </summary> + public static readonly string Section = "PidConfiguration"; + + /// <summary> + /// Prefix for the PID ePIC API. + /// </summary> + public string Prefix { get; init; } = null!; +} diff --git a/src/SQL2Linked/Models/ConfigurationModels/SQL2LinkedConfiguration.cs b/src/SQL2Linked/Models/ConfigurationModels/SQL2LinkedConfiguration.cs new file mode 100644 index 0000000000000000000000000000000000000000..178a2b42b3d0177516c48e4c35feaa31fde36f9d --- /dev/null +++ b/src/SQL2Linked/Models/ConfigurationModels/SQL2LinkedConfiguration.cs @@ -0,0 +1,17 @@ +namespace SQL2Linked.Models.ConfigurationModels; + +/// <summary> +/// Represents the configuration settings used in the application. +/// </summary> +public class SQL2LinkedConfiguration +{ + /// <summary> + /// The section name in the configuration file. + /// </summary> + public static readonly string Section = "SQL2LinkedConfiguration"; + + /// <summary> + /// Value indicating whether SQL2Linked is enabled. + /// </summary> + public bool IsEnabled { get; init; } +} \ No newline at end of file diff --git a/src/SQL2Linked/Program.cs b/src/SQL2Linked/Program.cs index b9d0aa6a150c349541fa09512110b842c5e6e833..bd2579cbf71722efa0be3fbb961cbe37a112cdc9 100644 --- a/src/SQL2Linked/Program.cs +++ b/src/SQL2Linked/Program.cs @@ -1,27 +1,98 @@ -using SQL2Linked.Implementations; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using SQL2Linked.Implementations; +using SQL2Linked.Models.ConfigurationModels; +using Winton.Extensions.Configuration.Consul; -var dummyMode = !(args.Length > 0 && args[0] == "--noDryRun"); -if (dummyMode) +namespace SQL2Linked; + +public class Program { - Console.WriteLine("\n DUMMY MODE \n"); - Console.WriteLine(" To exit dummy mode, execute with the \"--noDryRun\" argument"); -} + private static IServiceProvider _serviceProvider = null!; + + private static async Task Main(string[] args) + { + InitializeServices(); + + var sql2LinkedConfiguration = _serviceProvider.GetRequiredService<IOptionsMonitor<SQL2LinkedConfiguration>>().CurrentValue; + if (!sql2LinkedConfiguration.IsEnabled) + { + Console.WriteLine("SQL 2 Linked Data migration is disabled. To enable it, set the \"IsEnabled\" property to \"true\" in the configuration."); + return; + } + + var dummyMode = !(args.Length > 0 && args[0] == "--noDryRun"); + if (dummyMode) + { + Console.WriteLine("\n DUMMY MODE \n"); + Console.WriteLine(" To exit dummy mode, execute with the \"--noDryRun\" argument"); + } + + Console.WriteLine("\nBegin SQL 2 Linked Data migration"); + + var roleStructuralData = _serviceProvider.GetRequiredService<RoleStructuralData>(); + await roleStructuralData.Migrate(dummyMode); + + var userStructuralData = _serviceProvider.GetRequiredService<UserStructuralData>(); + await userStructuralData.Migrate(dummyMode); + + var resourceTypeStructuralData = _serviceProvider.GetRequiredService<ResourceTypeStructuralData>(); + await resourceTypeStructuralData.Migrate(dummyMode); + + var projectStructuralData = _serviceProvider.GetRequiredService<ProjectStructuralData>(); + await projectStructuralData.Migrate(dummyMode); + + var resourceStructuralData = _serviceProvider.GetRequiredService<ResourceStructuralData>(); + await resourceStructuralData.Migrate(dummyMode); + + Console.WriteLine("\n Finished."); + } + + private static void InitializeServices() + { + // Create a new instance of ConfigurationBuilder + var configBuilder = new ConfigurationBuilder(); -Console.WriteLine("\nBegin SQL 2 Linked Data migration"); + // Define the Consul URL + var consulUrl = Environment.GetEnvironmentVariable("CONSUL_URL") ?? "http://localhost:8500"; -var roleStructuralData = new RoleStructuralData(); -roleStructuralData.Migrate(dummyMode); + // Remove the default sources + configBuilder.Sources.Clear(); -var userStructuralData = new UserStructuralData(); -userStructuralData.Migrate(dummyMode); + // Add Consul as a configuration source + var configuration = configBuilder + .AddConsul( + "coscine/Coscine.Infrastructure/SQL2Linked/appsettings", + options => + { + options.ConsulConfigurationOptions = + cco => cco.Address = new Uri(consulUrl); + options.Optional = true; + options.ReloadOnChange = true; + options.PollWaitTime = TimeSpan.FromSeconds(5); + options.OnLoadException = exceptionContext => exceptionContext.Ignore = true; + } + ) + .AddEnvironmentVariables() + .Build(); -var resourceTypeStructuralData = new ResourceTypeStructuralData(); -resourceTypeStructuralData.Migrate(dummyMode); + var services = new ServiceCollection() + .AddSingleton<IConfiguration>(configuration); -var projectStructuralData = new ProjectStructuralData(); -projectStructuralData.Migrate(dummyMode); + // Add the configuration to the service collection + services.Configure<SQL2LinkedConfiguration>(settings => + { + configuration.GetSection(SQL2LinkedConfiguration.Section).Bind(settings); + }); -var resourceStructuralData = new ResourceStructuralData(); -resourceStructuralData.Migrate(dummyMode); + // Add the services to the service collection + services.AddTransient<ProjectStructuralData>(); + services.AddTransient<ResourceStructuralData>(); + services.AddTransient<ResourceTypeStructuralData>(); + services.AddTransient<RoleStructuralData>(); + services.AddTransient<UserStructuralData>(); -Console.WriteLine("\n Finished."); + _serviceProvider = services.BuildServiceProvider(); + } +} \ No newline at end of file diff --git a/src/SQL2Linked/SQL2Linked.csproj b/src/SQL2Linked/SQL2Linked.csproj index 9c2df07e0f2f6de9e1d8eff1b1d08000f131b014..7d22d4c3652cc4e7d198e1c17d2d841ef60148c7 100644 --- a/src/SQL2Linked/SQL2Linked.csproj +++ b/src/SQL2Linked/SQL2Linked.csproj @@ -1,16 +1,25 @@ -<Project Sdk="Microsoft.NET.Sdk"> +<Project Sdk="Microsoft.NET.Sdk"> - <PropertyGroup> - <OutputType>Exe</OutputType> - <TargetFramework>net6.0</TargetFramework> - <ImplicitUsings>enable</ImplicitUsings> - <Nullable>enable</Nullable> - <Version>0.1.19</Version></PropertyGroup> + <PropertyGroup> + <OutputType>Exe</OutputType> + <TargetFramework>net8.0</TargetFramework> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>enable</Nullable> + <Version>0.1.19</Version> + </PropertyGroup> - <ItemGroup> - <PackageReference Include="Coscine.Database" Version="2.*-*" /> - <PackageReference Include="Coscine.Metadata" Version="2.*-*" /> - <PackageReference Include="Polly" Version="7.2.3" /> - </ItemGroup> + <PropertyGroup> + <Authors>RWTH Aachen University</Authors> + <Company>IT Center, RWTH Aachen University</Company> + <Copyright>©2024 IT Center, RWTH Aachen University</Copyright> + <Description>SQL2Linked is a part of the Coscine group.</Description> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="Coscine.ApiClient" Version="1.3.0-issue-2666-admin0011" /> + <PackageReference Include="dotNetRdf" Version="3.1.1" /> + <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" /> + <PackageReference Include="Microsoft.Extensions.Options" Version="8.0.1" /> + </ItemGroup> </Project> diff --git a/src/SQL2Linked/StructuralData.cs b/src/SQL2Linked/StructuralData.cs index ea4d2d8cb4b558cf1328870789d22962f81d0084..70dc878a877359d4a74081a71f12eaf896d8035a 100644 --- a/src/SQL2Linked/StructuralData.cs +++ b/src/SQL2Linked/StructuralData.cs @@ -1,172 +1,178 @@ -using Coscine.Configuration; -using Coscine.Database.Models; -using Coscine.Metadata; -using Polly; +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 VDS.RDF; -namespace SQL2Linked +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> { - public abstract class StructuralData<S, T> where S : class where T : DatabaseModel<S>, new() + 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() { - public T Model { get; init; } - public ConsulConfiguration Configuration { get; init; } - public RdfStoreConnector RdfStoreConnector { get; init; } - public static string Prefix { get; set; } + // 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(); - public StructuralData() + // 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() { - Configuration = new ConsulConfiguration(); - RdfStoreConnector = new RdfStoreConnector(Configuration.GetStringAndWait("coscine/local/virtuoso/additional/url")); - Model = new T(); - Prefix = Configuration.GetStringAndWait("coscine/global/epic/prefix"); - // 100 second timeout - var timeout = 100000; - RdfStoreConnector.QueryEndpoint.Timeout = timeout; - RdfStoreConnector.UpdateEndpoint.Timeout = timeout; - RdfStoreConnector.ReadWriteSparqlConnector.Timeout = timeout; - } + BasePath = "http://localhost:7206/coscine", + ApiKeyPrefix = { { "Authorization", "Bearer" } }, + ApiKey = { { "Authorization", _adminToken } }, + }; + _adminApi = new AdminApi(_apiConfiguration); + } - public abstract IEnumerable<IGraph> ConvertToLinkedData(IEnumerable<S> entries); + /// <summary> + /// Converts the given entries to linked data graphs. + /// </summary> + /// <param name="entries">The entries to convert.</param> + /// <returns>An enumerable collection of graphs representing the linked data.</returns> + public abstract Task<IEnumerable<IGraph>> ConvertToLinkedDataAsync(IEnumerable<S> entries); - public virtual IEnumerable<S> GetAll() - { - return Model.GetAll(); - } + /// <summary> + /// Retrieves all entries of type <typeparamref name="S"/>. + /// </summary> + /// <returns>A task that represents the asynchronous operation. The task result contains an enumerable of all entries.</returns> + public abstract Task<IEnumerable<S>> GetAll(); - public void Migrate(bool dummyMode) + /// <summary> + /// Migrates the data, optionally in a dummy mode where changes are not persisted. + /// </summary> + /// <param name="dummyMode">If set to <c>true</c>, the migration is simulated but not executed.</param> + /// <returns>A task representing the asynchronous 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 = await ConvertToLinkedDataAsync(await GetAll()); + if (!dummyMode) { - var spacer = new string('-', 35); - Console.WriteLine($"\n{spacer}\n{typeof(T).Name}\n{spacer}"); - var graphs = ConvertToLinkedData(GetAll()); - if (!dummyMode) - { - StoreGraphs(graphs); - } + await StoreGraphs(graphs); } + } - public void StoreGraphs(IEnumerable<IGraph> graphs) - { - foreach (var graph in graphs) - { - try - { - Console.WriteLine($" ({graph.BaseUri})"); - - if (graph is WrapperGraph) - { - var wrapperGraph = (WrapperGraph)graph; - // Chunking since the size otherwise can be too large - foreach (var triples in wrapperGraph.AssertList.Chunk(100)) - { - WrapRequest(() => RdfStoreConnector.ReadWriteSparqlConnector.UpdateGraph(graph.BaseUri, triples, new List<Triple>())); - } - // Chunking since the size otherwise can be too large - foreach (var triples in wrapperGraph.RetractList.Chunk(100)) - { - WrapRequest(() => RdfStoreConnector.ReadWriteSparqlConnector.UpdateGraph(graph.BaseUri, new List<Triple>(), triples)); - } - } - else - { - var exists = WrapRequest(() => RdfStoreConnector.HasGraph(graph.BaseUri)); - if (exists) - { - Console.WriteLine($" - Graph {graph.BaseUri} exists"); - - // Clear the existing graph from the store - WrapRequest(() => RdfStoreConnector.ClearGraph(graph.BaseUri)); - Console.WriteLine($" - Cleared Graph {graph.BaseUri}"); - } - - // Chunking since the size otherwise can be too large - // Don't change to only addition of triples, otherwise this could break things - foreach (var triples in graph.Triples.Chunk(100)) - { - WrapRequest(() => RdfStoreConnector.ReadWriteSparqlConnector.UpdateGraph(graph.BaseUri, triples, new List<Triple>())); - } - } - - 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(); - } - } - } + /// <summary> + /// Stores the given graphs in the underlying data store. + /// </summary> + /// <param name="graphs">The graphs to be stored.</param> + /// <returns>A task representing the asynchronous operation.</returns> + public async Task StoreGraphs(IEnumerable<IGraph> graphs) + { + var formatEnum = RdfFormat.TextTurtle; + var format = "text/turtle"; - public void AssertToGraphUriNode(IGraph graph, string graphSubject, string graphPredicate, string? graphObject) + foreach (var graph in graphs) { - if (graphObject != null) + Console.WriteLine($" ({graph.BaseUri})"); + + try { - graph.Assert( - new Triple( - graph.CreateUriNode(new Uri(graphSubject)), - graph.CreateUriNode(new Uri(graphPredicate)), - graph.CreateUriNode(new Uri(graphObject)) - ) + var rdfWriter = MimeTypesHelper.GetWriter(format); + var content = VDS.RDF.Writing.StringWriter.Write(graph, rdfWriter); + + await _adminApi.UpdateMetadataGraphAsync( + graph.BaseUri.AbsoluteUri, + new MetadataUpdateAdminParameters(new RdfDefinitionForManipulationDto(content, formatEnum)) ); - } - } - public void AssertToGraphLiteralNode(IGraph graph, string graphSubject, string graphPredicate, string? graphObject, Uri? objectType = null) - { - if (graphObject != null) + Console.WriteLine($" - Graph {graph.BaseUri} added successfully"); + Console.WriteLine(); + } + catch (Exception e) { - graph.Assert( - new Triple( - graph.CreateUriNode(new Uri(graphSubject)), - graph.CreateUriNode(new Uri(graphPredicate)), - graph.CreateLiteralNode(graphObject, objectType) - ) - ); + Console.Error.WriteLine($"Error on ({graph.BaseUri}):"); + Console.Error.WriteLine(e); + Console.Error.WriteLine(e.InnerException); + Console.Error.WriteLine(); } } + } - public void AssertToGraphBlankAndUriNode(IGraph graph, IBlankNode graphSubject, string graphPredicate, string? graphObject) + /// <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) { - if (graphObject != null) - { - graph.Assert( - new Triple( - graphSubject, - graph.CreateUriNode(new Uri(graphPredicate)), - graph.CreateUriNode(new Uri(graphObject)) - ) - ); - } + graph.Assert( + new Triple( + graph.CreateUriNode(graphSubject), + graph.CreateUriNode(graphPredicate), + graph.CreateUriNode(graphObject) + ) + ); } + } - /// <summary> - /// Retry Virtuoso Requests since they sometimes just fail - /// </summary> - /// <typeparam name="W"></typeparam> - /// <param name="function"></param> - /// <returns></returns> - public void WrapRequest(Action action) + /// <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) { - Policy - .Handle<Exception>() - .WaitAndRetry(5, retryNumber => TimeSpan.FromMilliseconds(200)) - .Execute(() => action.Invoke()); + graph.Assert( + new Triple( + graph.CreateUriNode(graphSubject), + graph.CreateUriNode(graphPredicate), + objectType is not null ? graph.CreateLiteralNode(graphObject, objectType) : graph.CreateLiteralNode(graphObject) + ) + ); } + } - /// <summary> - /// Retry Virtuoso Requests since they sometimes just fail - /// </summary> - /// <typeparam name="W"></typeparam> - /// <param name="function"></param> - /// <returns></returns> - public W WrapRequest<W>(Func<W> function) + /// <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) { - return Policy - .Handle<Exception>() - .WaitAndRetry(5, retryNumber => TimeSpan.FromMilliseconds(200)) - .ExecuteAndCapture(() => function.Invoke()).Result; + graph.Assert( + new Triple( + graphSubject, + graph.CreateUriNode(graphPredicate), + graph.CreateUriNode(graphObject) + ) + ); } } } diff --git a/src/SQL2Linked/Utils/RdfUris.cs b/src/SQL2Linked/Utils/RdfUris.cs new file mode 100644 index 0000000000000000000000000000000000000000..e5b1dd414848ca640f4b0f1fa0bce9182e4ab9e5 --- /dev/null +++ b/src/SQL2Linked/Utils/RdfUris.cs @@ -0,0 +1,215 @@ +// Ignore Spelling: Foaf Dcat Dcterms Ebocore Fdp Ldp Xsd + +/// ------------------ +/// TAKEN FROM THE API +/// ------------------ + +namespace SQL2Linked.Utils; + +/// <summary> +/// Contains URIs relevant to RDF (Resource Description Framework) and related schemas/ontologies. +/// </summary> +public static class RdfUris +{ + // ACL + public static readonly Uri AclPrefix = new("http://www.w3.org/ns/auth/acl#"); + + public static readonly Uri AclAccessTo = new("http://www.w3.org/ns/auth/acl#accessTo"); + public static readonly Uri AclAgentGroup = new("http://www.w3.org/ns/auth/acl#agentGroup"); + public static readonly Uri AclDefault = new("http://www.w3.org/ns/auth/acl#default"); + public static readonly Uri AclMode = new("http://www.w3.org/ns/auth/acl#mode"); + + public static readonly Uri AclAuthorizationClass = new("http://www.w3.org/ns/auth/acl#Authorization"); + public static readonly Uri AclReadClass = new("http://www.w3.org/ns/auth/acl#Read"); + public static readonly Uri AclWriteClass = new("http://www.w3.org/ns/auth/acl#Write"); + + // Coscine + public static readonly Uri CoscinePrefix = new("https://purl.org/coscine/"); + + public static readonly Uri CoscineApplicationProfile = new("https://purl.org/coscine/ap/"); + public static readonly Uri CoscineFixedValue = new("https://purl.org/coscine/fixedValue"); + public static readonly Uri CoscineMetadataExtractionVersion = new("https://purl.org/coscine/terms/metatadataextraction#version"); + public static readonly Uri CoscineProjects = new("https://purl.org/coscine/projects/"); + public static readonly Uri CoscineResourceTypes = new("https://purl.org/coscine/resourcetypes/"); + public static readonly Uri CoscineResources = new("https://purl.org/coscine/resources/"); + public static readonly Uri CoscineRoles = new("https://purl.org/coscine/roles/"); + public static readonly Uri CoscineUsers = new("https://purl.org/coscine/users/"); + + [Obsolete($"Beware the side-effects before changing! Use {nameof(CoscineUsers)} instead.")] + public static readonly Uri CoscineUserLegacy = new("https://coscine.rwth-aachen.de/u/"); + + // Coscine Terms + public static readonly Uri CoscineTermsPrefix = new("https://purl.org/coscine/terms/"); + + public static readonly Uri CoscineTermsLinkedBody = new("https://purl.org/coscine/terms/linked#body"); + public static readonly Uri CoscineTermsMetadataTrackerAgent = new("https://purl.org/coscine/terms/metadatatracker#Agent"); + public static readonly Uri CoscineTermsProject = new("https://purl.org/coscine/terms/project#"); + public static readonly Uri CoscineTermsProjectDeleted = new("https://purl.org/coscine/terms/project#deleted"); + public static readonly Uri CoscineTermsProjectSlug = new("https://purl.org/coscine/terms/project#slug"); + public static readonly Uri CoscineTermsProjectVisibility = new("https://purl.org/coscine/terms/project#visibility"); + public static readonly Uri CoscineTermsResource = new("https://purl.org/coscine/terms/resource#"); + public static readonly Uri CoscineTermsResourceArchived = new("https://purl.org/coscine/terms/resource#archived"); + public static readonly Uri CoscineTermsResourceDeleted = new("https://purl.org/coscine/terms/resource#deleted"); + public static readonly Uri CoscineTermsResourceFixedValues = new("https://purl.org/coscine/terms/resource#fixedValues"); + public static readonly Uri CoscineTermsResourceVisibility = new("https://purl.org/coscine/terms/resource#visibility"); + public static readonly Uri CoscineTermsTypes = new("https://purl.org/coscine/terms/types#"); + public static readonly Uri CoscineTermsUserAgent = new("https://purl.org/coscine/terms/user#Agent"); + public static readonly Uri CoscineTermsVisibility = new("https://purl.org/coscine/terms/visibility#"); + public static readonly Uri CoscineTermsVisibilityPublic = new("https://purl.org/coscine/terms/visibility#public"); + public static readonly Uri CoscineTermsVisibilityProjectMember = new("https://purl.org/coscine/terms/visibility#projectMember"); + public static readonly Uri CoscineTermsRole = new("https://purl.org/coscine/terms/role#"); + + // CSMD + public static readonly Uri CsmdPrefix = new("http://www.purl.org/net/CSMD/4.0#"); + + // DCAT + public static readonly Uri DcatPrefix = new("http://www.w3.org/ns/dcat#"); + + public static readonly Uri DcatCatalog = new("http://www.w3.org/ns/dcat#catalog"); + public static readonly Uri DcatDataset = new("http://www.w3.org/ns/dcat#dataset"); + public static readonly Uri DcatDistribution = new("http://www.w3.org/ns/dcat#distribution"); + public static readonly Uri DcatService = new("http://www.w3.org/ns/dcat#service"); + + public static readonly Uri DcatCatalogClass = new("http://www.w3.org/ns/dcat#Catalog"); + public static readonly Uri DcatDataServiceClass = new("http://www.w3.org/ns/dcat#DataService"); + public static readonly Uri DcatDatasetClass = new("http://www.w3.org/ns/dcat#Dataset"); + + // DCMI Type + public static readonly Uri DcmiTypePrefix = new("http://purl.org/dc/dcmitype/"); + + // DC + public static readonly Uri DcPrefix = new("http://purl.org/dc/elements/1.1/"); + + // DC Terms + public static readonly Uri DcTermsPrefix = new("http://purl.org/dc/terms/"); + + public static readonly Uri DcTermsAlternative = new("http://purl.org/dc/terms/alternative"); + public static readonly Uri DcTermsConformsTo = new("http://purl.org/dc/terms/conformsTo"); + public static readonly Uri DcTermsCreator = new("http://purl.org/dc/terms/creator"); + public static readonly Uri DcTermsCreated = new("http://purl.org/dc/terms/created"); + public static readonly Uri DcTermsDescription = new("http://purl.org/dc/terms/description"); + public static readonly Uri DcTermsEndDate = new("http://purl.org/dc/terms/endDate"); + public static readonly Uri DcTermsIdentifier = new("http://purl.org/dc/terms/identifier"); + public static readonly Uri DcTermsIsPartOf = new("http://purl.org/dc/terms/isPartOf"); + public static readonly Uri DcTermsLicense = new("http://purl.org/dc/terms/license"); + public static readonly Uri DcTermsModified = new("http://purl.org/dc/terms/modified"); + public static readonly Uri DcTermsRights = new("http://purl.org/dc/terms/rights"); + public static readonly Uri DcTermsRightsHolder = new("http://purl.org/dc/terms/rightsHolder"); + public static readonly Uri DcTermsStartDate = new("http://purl.org/dc/terms/startDate"); + public static readonly Uri DcTermsSubject = new("http://purl.org/dc/terms/subject"); + public static readonly Uri DcTermsTitle = new("http://purl.org/dc/terms/title"); + + // EBUCORE + public static readonly Uri EbocorePrefix = new("http://www.ebu.ch/metadata/ontologies/ebucore/ebucore#"); + + public static readonly Uri EbocoreHashFunction = new("http://www.ebu.ch/metadata/ontologies/ebucore/ebucore#hashFunction"); + public static readonly Uri EbocoreHashType = new("http://www.ebu.ch/metadata/ontologies/ebucore/ebucore#hashType"); + public static readonly Uri EbocoreHashValue = new("http://www.ebu.ch/metadata/ontologies/ebucore/ebucore#hashValue"); + + // FDP + public static readonly Uri FdpPrefix = new("http://purl.org/fdp/fdp-o#"); + + public static readonly Uri FdpHasMetadata = new("http://purl.org/fdp/fdp-o#hasMetadata"); + public static readonly Uri FdpIsMetadataOf = new("http://purl.org/fdp/fdp-o#isMetadataOf"); + + public static readonly Uri FdpMetadataServiceClass = new("http://purl.org/fdp/fdp-o#MetadataService"); + + // FOAF + public static readonly Uri FoafPrefix = new("http://xmlns.com/foaf/0.1/"); + + public static readonly Uri FoafHomepage = new("http://xmlns.com/foaf/0.1/homepage"); + public static readonly Uri FoafName = new("http://xmlns.com/foaf/0.1/name"); + + public static readonly Uri FoafPersonClass = new("http://xmlns.com/foaf/0.1/Person"); + + // Handle + public static readonly Uri HandlePrefix = new("https://hdl.handle.net/"); + + // LDP + public static readonly Uri LdpPrefix = new("http://www.w3.org/ns/ldp#"); + + public static readonly Uri LdpDescribedBy = new("http://www.w3.org/ns/ldp#describedBy"); + + public static readonly Uri LdpBasicContainerClass = new("http://www.w3.org/ns/ldp#BasicContainer"); + public static readonly Uri LdpNonRDFSourceClass = new("http://www.w3.org/ns/ldp#NonRDFSource"); + public static readonly Uri LdpRDFSourceClass = new("http://www.w3.org/ns/ldp#RDFSource"); + + // ORG + public static readonly Uri OrgPrefix = new("http://www.w3.org/ns/org#"); + + public static readonly Uri OrgMember = new("http://www.w3.org/ns/org#member"); + public static readonly Uri OrgOrganization = new("http://www.w3.org/ns/org#organization"); + public static readonly Uri OrgRole = new("http://www.w3.org/ns/org#role"); + + public static readonly Uri OrgMembershipClass = new("http://www.w3.org/ns/org#Membership"); + public static readonly Uri OrgOrganizationalCollaborationClass = new("http://www.w3.org/ns/org#OrganizationalCollaboration"); + public static readonly Uri OrgRoleClass = new("http://www.w3.org/ns/org#Role"); + + // OWL + public static readonly Uri OwlPrefix = new("http://www.w3.org/2002/07/owl#"); + + public static readonly Uri OwlImports = new("http://www.w3.org/2002/07/owl#imports"); + + // PIM + public static readonly Uri PimPrefix = new("http://www.w3.org/ns/pim/space#"); + + public static readonly Uri PimStorageClass = new("http://www.w3.org/ns/pim/space#Storage"); + + // PROV + public static readonly Uri ProvPrefix = new("http://www.w3.org/ns/prov#"); + + public static readonly Uri ProvEntityClass = new("http://www.w3.org/ns/prov#Entity"); + + public static readonly Uri ProvGeneratedAtTime = new("http://www.w3.org/ns/prov#generatedAtTime"); + public static readonly Uri ProvWasInvalidatedBy = new("http://www.w3.org/ns/prov#wasInvalidatedBy"); + public static readonly Uri ProvWasRevisionOf = new("http://www.w3.org/ns/prov#wasRevisionOf"); + + // RDF + public static readonly Uri RdfUrlPrefix = new("http://www.w3.org/1999/02/22-rdf-syntax-ns#"); + + public static readonly Uri A = new("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"); + + // RDFS + public static readonly Uri RdfsUrlPrefix = new("http://www.w3.org/2000/01/rdf-schema#"); + + public static readonly Uri SubClassOf = new("http://www.w3.org/2000/01/rdf-schema#subClassOf"); + + // ROR IDs + /// <summary> + /// Represents the RWTH Aachen's ROR (Research Organization Registry) ID. + /// </summary> + public static readonly Uri RwthRorId = new("https://ror.org/04xfq0f34"); + + // SCHEMA + public static readonly Uri SchemaPrefix = new("http://schema.org/"); + + public static readonly Uri SchemaFunding = new("http://schema.org/funding"); + + // SHACL + public static readonly Uri ShaclPrefix = new("http://www.w3.org/ns/shacl#"); + + public static readonly Uri ShaclDatatype = new("http://www.w3.org/ns/shacl#datatype"); + public static readonly Uri ShaclPath = new("http://www.w3.org/ns/shacl#path"); + public static readonly Uri ShaclProperty = new("http://www.w3.org/ns/shacl#property"); + public static readonly Uri ShaclTargetClass = new("http://www.w3.org/ns/shacl#targetClass"); + public static readonly Uri Class = new("http://www.w3.org/ns/shacl#class"); + + // Trellis + public static readonly Uri TrellisPrefix = new("http://www.trellisldp.org/ns/trellis#"); + + public static readonly Uri TrellisGraph = new("http://www.trellisldp.org/ns/trellis#PreferServerManaged"); + + // Vcard + public static readonly Uri VcardPrefix = new("http://www.w3.org/2006/vcard/ns#"); + + public static readonly Uri VcardHasMember = new("http://www.w3.org/2006/vcard/ns#hasMember"); + + public static readonly Uri VcardGroupClass = new("http://www.w3.org/2006/vcard/ns#Group"); + + // XSD + public static readonly Uri XsdPrefix = new("http://www.w3.org/2001/XMLSchema#"); + + public static readonly Uri XsdDateTime = new("http://www.w3.org/2001/XMLSchema#dateTime"); + public static readonly Uri XsdBoolean = new("http://www.w3.org/2001/XMLSchema#boolean"); + public static readonly Uri XsdHexBinary = new("http://www.w3.org/2001/XMLSchema#hexBinary"); +} \ No newline at end of file diff --git a/src/SQL2Linked/Utils/UriHelper.cs b/src/SQL2Linked/Utils/UriHelper.cs new file mode 100644 index 0000000000000000000000000000000000000000..e0e8f068a924d00ca3eeaca62d7c26c1ce53ad51 --- /dev/null +++ b/src/SQL2Linked/Utils/UriHelper.cs @@ -0,0 +1,31 @@ +namespace SQL2Linked.Utils; + +/// <summary> +/// +/// </summary> +public static class UriHelper +{ + /// <summary> + /// + /// </summary> + /// <param name="baseUri"></param> + /// <param name="relativePath"></param> + /// <returns></returns> + public static Uri? TryCombineUri(Uri baseUri, string relativePath) + { + Uri.TryCreate(baseUri, relativePath, out Uri? result); + return result; + } + + /// <summary> + /// + /// </summary> + /// <param name="baseUri"></param> + /// <param name="relativePath"></param> + /// <returns></returns> + public static Uri? TryCombineUri(Uri baseUri, Guid relativePath) + { + Uri.TryCreate(baseUri, relativePath.ToString(), out Uri? result); + return result; + } +} diff --git a/src/SQL2Linked/WrapperGraph.cs b/src/SQL2Linked/WrapperGraph.cs deleted file mode 100644 index ce318c397a4e103f9d2369f8aad893bf9ad3677c..0000000000000000000000000000000000000000 --- a/src/SQL2Linked/WrapperGraph.cs +++ /dev/null @@ -1,40 +0,0 @@ -using VDS.RDF; - -namespace SQL2Linked -{ - public class WrapperGraph : Graph - { - public List<Triple> AssertList { get; set; } - public List<Triple> RetractList { get; set; } - - public WrapperGraph() : base() - { - AssertList = new List<Triple>(); - RetractList = new List<Triple>(); - } - - public override bool Assert(Triple t) - { - AssertList.Add(t); - return base.Assert(t); - } - - public override bool Assert(IEnumerable<Triple> triples) - { - AssertList.AddRange(triples); - return base.Assert(triples); - } - - public override bool Retract(Triple t) - { - RetractList.Add(t); - return base.Retract(t); - } - - public override bool Retract(IEnumerable<Triple> triples) - { - RetractList.AddRange(triples); - return base.Retract(triples); - } - } -}