From dae31776ff587cd83fbed66da89b405eb810d9ea Mon Sep 17 00:00:00 2001 From: Martin Vanbrabant Date: Thu, 25 Sep 2025 08:42:33 +0200 Subject: [PATCH] Link in table result header is no longer arbitrary if more than one predicate has the same object --- CHANGELOG.md | 1 + main/configs/test/config.json | 26 +++++++++++++++++++ .../test/public/queries/schema_name.rq | 6 +++++ .../public/queries/schema_name_rdfs_label.rq | 7 +++++ .../TableHeader/TableHeader.jsx | 22 +++++++++------- main/src/dataProvider/SparqlDataProvider.js | 10 ++++--- test/cypress/e2e/column-header.cy.js | 24 +++++++++++++---- test/initial-pod-data/names-labels$.ttl | 8 ++++++ test/initial-pod-data/names-labels.acl | 14 ++++++++++ 9 files changed, 100 insertions(+), 18 deletions(-) create mode 100644 main/configs/test/public/queries/schema_name.rq create mode 100644 main/configs/test/public/queries/schema_name_rdfs_label.rq create mode 100644 test/initial-pod-data/names-labels$.ttl create mode 100644 test/initial-pod-data/names-labels.acl diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ddc8e34..a1dc7aed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Link in table result header is no longer arbitrary if more than one predicate has the same object (#230.) - Avoided "Error getting variable options..." in templated queries with indirect sources to which the user has no read access (#231). - Corrected fetch status in templated queries with indirect sources to which the user has no read access (#232). diff --git a/main/configs/test/config.json b/main/configs/test/config.json index 67ef934a..4fd5b2ce 100644 --- a/main/configs/test/config.json +++ b/main/configs/test/config.json @@ -581,6 +581,32 @@ "http://localhost:8080/example/favourite-musicians-file-does-not-exist" ] } + }, + { + "id": "9200", + "queryGroupId": "gr-test", + "queryLocation": "schema_name.rq", + "name": "A query that looks for names that are the object of predicate schema:name", + "description": "Tests a single link in the 'name' column header.", + "comunicaContext": { + "sources": [ + "http://localhost:8080/example/names-labels" + ], + "lenient": true + } + }, + { + "id": "9201", + "queryGroupId": "gr-test", + "queryLocation": "schema_name_rdfs_label.rq", + "name": "A query that looks for names that are both the objects of predicates schema:name and rdfs:label", + "description": "Tests two links in the 'name' column header.", + "comunicaContext": { + "sources": [ + "http://localhost:8080/example/names-labels" + ], + "lenient": true + } } ] } \ No newline at end of file diff --git a/main/configs/test/public/queries/schema_name.rq b/main/configs/test/public/queries/schema_name.rq new file mode 100644 index 00000000..2f393ab0 --- /dev/null +++ b/main/configs/test/public/queries/schema_name.rq @@ -0,0 +1,6 @@ +PREFIX schema: +PREFIX rdfs: + +SELECT ?name WHERE { + ?s schema:name ?name. +} \ No newline at end of file diff --git a/main/configs/test/public/queries/schema_name_rdfs_label.rq b/main/configs/test/public/queries/schema_name_rdfs_label.rq new file mode 100644 index 00000000..1cf2ce2b --- /dev/null +++ b/main/configs/test/public/queries/schema_name_rdfs_label.rq @@ -0,0 +1,7 @@ +PREFIX schema: +PREFIX rdfs: + +SELECT ?name WHERE { + ?s schema:name ?name. + ?s rdfs:label ?name. +} \ No newline at end of file diff --git a/main/src/components/ListResultTable/QueryResultList/TableHeader/TableHeader.jsx b/main/src/components/ListResultTable/QueryResultList/TableHeader/TableHeader.jsx index 75fff512..c8fb5acf 100644 --- a/main/src/components/ListResultTable/QueryResultList/TableHeader/TableHeader.jsx +++ b/main/src/components/ListResultTable/QueryResultList/TableHeader/TableHeader.jsx @@ -60,16 +60,18 @@ function TableHeader({ children }) { } {!!variableOntology && variableOntology[child.props.source] && ( - *": { verticalAlign: "middle" } }} - > - - + variableOntology[child.props.source].map((link) => ( + *": { verticalAlign: "middle" } }} + > + + + )) )} {sort.field === child.props.source && ( <> diff --git a/main/src/dataProvider/SparqlDataProvider.js b/main/src/dataProvider/SparqlDataProvider.js index 6691498f..00e54ffa 100644 --- a/main/src/dataProvider/SparqlDataProvider.js +++ b/main/src/dataProvider/SparqlDataProvider.js @@ -189,10 +189,10 @@ function replaceVariables(rawText, variableValues) { } /** - * Given a query and an object, this function returns the predicate of the object in the query. + * Given a query and an object, this function returns the predicates of the object in the query. * * @param {object} query - the parsed query in which the predicate is to be looked for. - * @returns {object} an object with the variable as key and the predicate as value. + * @returns {object} an object with the variable as key and as value an array of predicates. */ function findPredicates(query) { const ontologyMapper = {}; @@ -204,7 +204,11 @@ function findPredicates(query) { if (part.triples) { for (const triple of part.triples) { if (triple.predicate.termType !== "Variable") { - ontologyMapper[triple.object.value] = triple.predicate.value; + if (!ontologyMapper[triple.object.value]) { + ontologyMapper[triple.object.value] = [triple.predicate.value]; + } else if (!ontologyMapper[triple.object.value].includes(triple.predicate.value)) { + ontologyMapper[triple.object.value].push(triple.predicate.value); + } } } } diff --git a/test/cypress/e2e/column-header.cy.js b/test/cypress/e2e/column-header.cy.js index c1d41d91..048488db 100644 --- a/test/cypress/e2e/column-header.cy.js +++ b/test/cypress/e2e/column-header.cy.js @@ -1,9 +1,23 @@ describe("Column header", () => { - it("Variables link to ontology", () => { + it("One link to ontology", () => { cy.visit("/"); - cy.contains("Example queries").click(); - cy.contains("A query about musicians").click(); + cy.contains("For testing only").click(); + cy.contains("A query that looks for names that are the object of predicate schema:name").click(); cy.contains("Finished in:"); - cy.get('a[href="http://schema.org/name"]'); - }) + cy.get('th').contains("name").parent().within(() => { + cy.get('a[href="http://schema.org/name"]'); + }); + }); + + it("Two links to ontology", () => { + cy.visit("/"); + cy.contains("For testing only").click(); + cy.contains("A query that looks for names that are both the objects of predicates schema:name and rdfs:label").click(); + cy.contains("Finished in:"); + cy.get('th').contains("name").parent().within(() => { + cy.get('a[href="http://schema.org/name"]'); + cy.get('a[href="http://www.w3.org/2000/01/rdf-schema#label"]'); + }); + }); + }); diff --git a/test/initial-pod-data/names-labels$.ttl b/test/initial-pod-data/names-labels$.ttl new file mode 100644 index 00000000..c84ca716 --- /dev/null +++ b/test/initial-pod-data/names-labels$.ttl @@ -0,0 +1,8 @@ +PREFIX schema: +PREFIX rdfs: +PREFIX ex: + +ex:1 schema:name "A name, given with predicates schema:name and rdfs:label" ; + rdfs:label "A name, given with predicates schema:name and rdfs:label" . +ex:2 schema:name "A name, given with predicate schema:name" ; + rdfs:label "A name, given with predicate rdfs:label" . diff --git a/test/initial-pod-data/names-labels.acl b/test/initial-pod-data/names-labels.acl new file mode 100644 index 00000000..bd3221fe --- /dev/null +++ b/test/initial-pod-data/names-labels.acl @@ -0,0 +1,14 @@ +@prefix acl: . +@prefix foaf: . + +<#public> + a acl:Authorization; + acl:accessTo <./names-labels>; + acl:agentClass foaf:Agent; + acl:mode acl:Read. + +<#owner> + a acl:Authorization; + acl:accessTo <./names-labels>; + acl:agent ; + acl:mode acl:Read, acl:Write, acl:Control.