Skip to content

Encode concept URIs in API calls with URLSearchParams (fix hash namespace issues)#1835

Merged
osma merged 3 commits intomainfrom
issue1828-mapping-hash-namespace
Dec 2, 2025
Merged

Encode concept URIs in API calls with URLSearchParams (fix hash namespace issues)#1835
osma merged 3 commits intomainfrom
issue1828-mapping-hash-namespace

Conversation

@osma
Copy link
Member

@osma osma commented Nov 26, 2025

Reasons for creating this PR

It was reported in #1828 that concept mappings are not shown on concept pages where the concept URI contains a # character. This turned out to be an URL encoding problem that affects many API calls and also breaks e.g. the hierarchy display.

This PR changes the way REST API URLs are constructed to ensure that all arguments are properly URL encoded. String concatenation is replaced with URLSearchParams which handles encoding automatically.

The PR also includes a small test vocabulary which uses a hash-style namespace, and a Cypress test that verifies that mappings are loaded for these concepts (and indirectly also that the hierarchy is working).

Link to relevant issue(s), if any

Description of the changes in this PR

  • change all URL construction expressions in JS front-end code to use URLSearchParams
  • add hash test vocabulary
  • add Cypress test that verifies that the mappings and hiearchy are working for the hash vocabulary concepts

Known problems or uncertainties in this PR

I tried to dig through all our JS front-end code looking for problematic ways to construct URLs and replaced them all with URLSearchParams. I don't think I've missed any, but it's possible. I'm a bit unsure about get-concept-url.js ; it's quite ugly in its current form and doesn't use URLSearchParams, but it seems to me that it is still encoding URL parameters properly using encodeURIComponent calls, so I didn't touch it.

Checklist

  • phpUnit tests pass locally with my changes
  • I have added tests that show that the new code works, or tests are not relevant for this PR (e.g. only HTML/CSS changes)
  • The PR doesn't reduce accessibility of the front-end code (e.g. tab focus, scaling to different resolutions, use of .sr-only class, color contrast)
  • The PR doesn't introduce unintended code changes (e.g. empty lines or useless reindentation)

@osma osma added this to the 3.0 milestone Nov 26, 2025
@osma osma self-assigned this Nov 26, 2025
@osma osma added the bug label Nov 26, 2025
@osma osma requested a review from miguelvaara November 26, 2025 11:59
@osma osma moved this to Needs review in Skosmos 3.x Backlog Nov 26, 2025
Copy link
Contributor

@miguelvaara miguelvaara left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there possibly a need to use URLSearchParams here in the same way as in the other corresponding cases?

@osma
Copy link
Member Author

osma commented Nov 26, 2025

@miguelvaara it is used there (one line below the one you linked)

Copy link
Contributor

@miguelvaara miguelvaara left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here the toString() function is used even though it is inside backticks (as I understand, the function should not be needed in that case).

Byt, for the sake of consistency, I think it would be good to either use the toString() function in all cases or leave it out everywhere. Regardless if it is actually needed or not, if it is used it would be good to use it in all cases of remove the one case with the function.

Here is the list of my quick glance:

https://github.com/NatLibFi/Skosmos/blob/dace6a80082fb4f21d7863005ff26d22d57b675f/resource/js/concept-mappings.js#L32C6-L32C82

https://github.com/NatLibFi/Skosmos/blob/dace6a80082fb4f21d7863005ff26d22d57b675f/resource/js/tab-alpha.js#L78C9-L78C80

const url = `rest/v1/${window.SKOSMOS.vocab}/index/${this.selectedLetter}?${params}`

fetch(`rest/v1/${window.SKOSMOS.vocab}/new?${params}`)

fetch(`rest/v1/${window.SKOSMOS.vocab}/new?${params}`)

fetch(`rest/v1/${window.SKOSMOS.vocab}/groupMembers/?${params}`)

fetch(`rest/v1/${window.SKOSMOS.vocab}/groupMembers/?${params}`)

fetch(`rest/v1/${window.SKOSMOS.vocab}/topConcepts?${params}`)

fetch(`rest/v1/${window.SKOSMOS.vocab}/children?${params}`)

const res = await fetch(`rest/v1/${window.SKOSMOS.vocab}/topConcepts?${params}`)

const res = await fetch(`rest/v1/${window.SKOSMOS.vocab}/hierarchy/?${params}`)

Copy link
Contributor

@miguelvaara miguelvaara left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After many checks and a fairly extensive review, I can see that everything works as it should. In my view, it can be merged once the toString cases have been considered and the related fixes have been made (either removed everywhere or included everywhere)

Great job!

@miguelvaara
Copy link
Contributor

@miguelvaara it is used there (one line below the one you linked)

You right - my bad - sorry!

@sonarqubecloud
Copy link

@osma
Copy link
Member Author

osma commented Nov 26, 2025

I dropped the extra toString() from one of the expressions.

Note that since the search URL construction functionality is implemented in a different style (string concatenation, not string interpolation expressions), toString() is still needed there (I think) and I didn't touch that code in the PR as it already handled URL encoding properly.

Copy link
Contributor

@miguelvaara miguelvaara left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dc:title "Hash URI namespace"@en ;
skosmos:language "en";
skosmos:showTopConcepts "true";
skosmos:sparqlGraph <http://www.skosmos.skos/hash/>;
void:uriSpace "http://www.skosmos.skos/hash#" .

I think you should add the following:
skosmos:loadExternalResources "true";

After that tests pass and the issue disappears ...

// check the first mapping property name
cy.get('.prop-mapping h2').eq(0).contains('Exactly matching concepts')
// check the first mapping property values
cy.get('.prop-mapping').eq(0).find('.prop-mapping-label').eq(0).contains('number sign')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

      cy.get('.prop-mapping').eq(0).find('.prop-mapping-label').eq(0).contains('number sign')

I cannot get it to pass. It fails with the following error because it expects the string ‘number sign’.

containsnumber sign / AssertionError

Timed out retrying after 4000ms: Expected to find content: 'number sign' within the element: <div.col-5.prop-mapping-label> but never did.

tests/cypress/template/concept-full-vs-partial.cy.js:164:71

cy.get('.prop-mapping').eq(0).find('.prop-mapping-label').eq(0).contains('number sign')

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

skosmos:loadExternalResources defaults to true, so I don't think that can be the problem.

Copy link
Contributor

@miguelvaara miguelvaara left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cause of the Wikidata label issue remains unknown. Nevertheless all tests now pass, so the merge can proceed.

Great work!

@osma osma merged commit 274e0dc into main Dec 2, 2025
30 checks passed
@osma osma deleted the issue1828-mapping-hash-namespace branch December 2, 2025 12:56
@github-project-automation github-project-automation bot moved this from Needs review to Issue/PR closed in Skosmos 3.x Backlog Dec 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Mapping endpoint call fails with 500

2 participants