Repository navigation
OpenConceptLab/ocl_online#374 | using path routing in place of hash routing - #61
snyaggarwal wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Reviewed against OpenConceptLab/ocl_online#374, and ran it locally against prod data: old /#/ links redirect (including ?utm_…#/…), in-app clicks and Back don't reload, and dotted concept URLs refresh. The inline suggestions were checked in that build, and lint passes with them. Codex's review follows.
Before merge
- Forced logout loses the page's query after its first parameter (
utils.jsL681). history.jsshould accept only single-slash routes (L13, L31). Details in OpenConceptLab/ocl_online#374.
Small
3. ?origin=openmrs now redirects from every path (index.html L47).
4. A relative #/… link picks up the current page's query (history.js L61).
5. Still /#: toFullURL ("view in source" on concepts and mappings) and AddReferencesDialog.jsx:313: drop the #. toMapperURL can keep it until both apps are live.
Two of Codex's points don't happen today: no OCL app puts referrer before the # (all put it inside the hash), and every blob: link has download.
| const redirectURL = forced ? | ||
| window.location.origin + '/#/signin?returnTo=' + encodeURIComponent(returnTo) : | ||
| window.location.origin + '/signin?returnTo=' + encodeURIComponent(returnTo) : |
There was a problem hiding this comment.
oclapi2 decodes post_logout_redirect_uri and passes it to Keycloak raw, so Keycloak splits it at each &: /search/?q=x&type=concepts comes back as /search/?q=x, even if getSSOLogoutURL encodes it (Codex's fix). As a fragment, the pre-PR form, it never reaches oclapi2 or Keycloak, so their redirects carry it through intact; the legacy-hash redirect then turns it into /signin?returnTo=…. Checked against a mock of both redirects. Keycloak's post-logout URIs then need no change.
| const redirectURL = forced ? | |
| window.location.origin + '/#/signin?returnTo=' + encodeURIComponent(returnTo) : | |
| window.location.origin + '/signin?returnTo=' + encodeURIComponent(returnTo) : | |
| // A fragment never reaches oclapi2 or Keycloak, so it survives their redirects intact (a query is decoded and | |
| // split on '&'). Back in the app, the legacy-hash redirect turns it into /signin?returnTo=… | |
| const redirectURL = forced ? | |
| window.location.origin + '/#/signin?returnTo=' + encodeURIComponent(returnTo) : |
| if(!path) | ||
| return | ||
| const routePath = toRoutePath(path) |
There was a problem hiding this comment.
Same-site paths only. Details in OpenConceptLab/ocl_online#374.
| if(!path) | |
| return | |
| const routePath = toRoutePath(path) | |
| const routePath = toRoutePath(path) | |
| // Same-site paths only: '//host' or '/\host' would leave the site, or make pushState throw. | |
| if(!/^\/(?![/\\])/.test(routePath || '')) | |
| return |
|
|
||
| export const legacyHashRoute = (location=window.location) => { | ||
| const { hash, search } = location | ||
| if(!hash.startsWith('#/') || /[?&]referrer=/.test(search)) |
There was a problem hiding this comment.
Skip #//… and #/\…. Details in OpenConceptLab/ocl_online#374.
| if(!hash.startsWith('#/') || /[?&]referrer=/.test(search)) | |
| if(!hash.startsWith('#/') || /^#\/[/\\]/.test(hash) || /[?&]referrer=/.test(search)) |
| const url = new URL(anchor.href, window.location.href) | ||
| if(url.origin !== window.location.origin) |
There was a problem hiding this comment.
A relative #/… href resolves against this page, so legacyHashRoute merges this page's query into the route: on /search/?q=malaria&type=concepts, #/orgs/CIEL/ goes to /orgs/CIEL/?q=malaria&type=concepts. Also limits routing to http(s) links.
| const url = new URL(anchor.href, window.location.href) | |
| if(url.origin !== window.location.origin) | |
| // Resolved against this page, a relative '#/…' link would pick up this page's query. | |
| if(href.startsWith('#/')) | |
| return href.slice(1) | |
| const url = new URL(anchor.href, window.location.href) | |
| if(url.origin !== window.location.origin || !/^https?:$/.test(url.protocol)) |
| go('/oidc/login/' + loc.search + '&next=' + loc.pathname); | ||
| } else if(loc.href.includes('/?session_state=')) { | ||
| go('/'); | ||
| } else if((loc.search || '').includes('origin=openmrs')) { |
There was a problem hiding this comment.
Under hash routing only / (and /search) URLs carried origin=openmrs outside the hash. Now /orgs/CIEL/sources/CIEL/?origin=openmrs lands on the org search.
| } else if((loc.search || '').includes('origin=openmrs')) { | |
| } else if((loc.pathname === '/' || loc.pathname.startsWith('/search')) && (loc.search || '').includes('origin=openmrs')) { |
paynejd
left a comment
There was a problem hiding this comment.
Codex adversarial review, pass 1 (codex-cli 0.160.1, commit a01e34d)
I found five correctness issues and one incomplete migration. No files were modified.
-
[P1] Forced logout corrupts the return URL’s query string.
src/common/utils.js:682 encodesreturnTo, butgetSSOLogoutURLinterpolates the resulting URL intopost_logout_redirect_uriwithout encoding that outer value.Reproduced: after ordinary query decoding at both boundaries,
/search/?q=a%26b&type=conceptsbecomes/search/?q=a&b. The search term changes andtypedisappears. Actual Keycloak behavior was not exercised.Fix: construct the logout query with
URLSearchParams, encoding the completepost_logout_redirect_urias one parameter. -
[P1] Legacy URLs containing an outer
referrerparameter lose their destination.
src/common/history.js:31 skips conversion whenever the outer query containsreferrer.Reproduced:
/?referrer=https%3A%2F%2Fopenconceptlab.org%2F#/orgs/CIEL/sources/CIEL/returns no legacy route. BrowserRouter consequently sees/; authenticated startup also removes the hash, leaving the dashboard.Fix: distinguish actual legacy routing fragments from fragments embedded in an unencoded referrer. An encoded referrer should not prevent conversion of a real legacy route.
-
[P2] Legacy-route validation. Moved to OpenConceptLab/ocl_online#374 (private).
-
[P2] The click interceptor treats every same-origin resource as an app route.
src/common/history.js:61 checks origin without checking protocol or whether the destination belongs to the SPA.Reproduced: ordinary clicks on
/env-config.js,/bootstrap.min.css, and same-originblob:URLs are prevented and sent to router history. A resource link in About/Markdown content consequently cannot open its resource normally.Fix: intercept only HTTP(S) destinations covered by app routes, with native navigation retained for resources and other destinations.
-
[P2] Real document fragments are discarded or fail to scroll.
src/components/app/App.jsx:131 removes every hash on authenticated startup outside the callback route. A deep link ending in#licenseloses its fragment, including one preserved by legacy conversion.Separately, the global handler prevents native navigation for a pathname-qualified fragment link and pushes history without scrolling to its target.
Verified: fragment removal and interception are present in the code; actual rendered scrolling was not tested.
Fix: preserve document fragments, remove only recognized routing/referrer fragments, and handle scrolling after destination content renders.
-
[P3] Several producers still emit hash URLs.
The completesrc/andpublic/search found:- src/common/utils.js:124:
toFullURL, still used by concept/mapping “view in source” actions. - The same file at line 1135:
toMapperURL, used by the Mapper menu link. - src/components/collections/AddReferencesDialog.jsx:313: “open repository” still constructs
/#…. - The sibling PR’s
toV3URLlikewise still emits/#….
Fix: update these producers to path URLs. Keep
toV2URL’s/#and legacy-input parsing, as required. - src/common/utils.js:124:
Verification completed:
- Ran 31 in-memory Node checks against the history helpers and extracted authentication/bootstrap code. These confirmed ordinary legacy query/fragment preservation, dotted IDs, and skipping modifier clicks, middle clicks, named targets,
_blank, downloads, and external links. - Reviewed Dockerfile, startup script, webpack, and nginx. Absolute bundle paths and the existing
try_files $uri /index.htmlsupport deep paths containing dots. The production image was not executed. - Compared main since the merge base: three commits changed versioning, copied locale external IDs, and feed-error handling. They introduce no new hash links or apparent routing conflicts.
node_modulesis absent; there are no test files or test script, and the PR adds no helper tests.git diff --checkpassed; the worktree remains clean.
Analytics remains unverified: the manual page-view call runs at App mount, and setUpRecentHistory has no callers. GA4 enhanced measurement may cover history transitions, but its configuration and emitted events need checking in DebugView. Google’s SPA guidance
Full browser back/forward behavior, React/MUI propagation, Keycloak sign-in/signup/logout, email verification/reset, and production serving remain untested because browser tooling was unavailable and dependencies were absent.
Overall verdict: merge after fixes.
Linked Issue
Refs OpenConceptLab/ocl_online#374
oclmap PR: OpenConceptLab/oclmap#91