|
1 | 1 | import { useEffect } from 'react'
|
2 | 2 | import { useRouter } from 'next/router'
|
3 | 3 |
|
4 |
| -import restApiOverrides from '../../lib/redirects/static/client-side-rest-api-redirects.json' |
5 |
| -import productOverrides from '../../lib/redirects/static/client-side-product-redirects.json' |
6 |
| -const overrideRedirects: Record<string, string> = { ...restApiOverrides, ...productOverrides } |
7 |
| - |
| 4 | +// We recently moved several rest api operations around |
| 5 | +// in the docs. That means that we are out of sync with |
| 6 | +// the urls defined in the OpenAPI. We will eventually |
| 7 | +// update those urls but for now we want to ensure that |
| 8 | +// we have client-side redirects in place for any urls |
| 9 | +// in the product that link to the rest docs (e.g., error |
| 10 | +// code urls from the apis). |
| 11 | +// The client-side redirects can consist of operation urls |
| 12 | +// being redirected to the new operation url or headings |
| 13 | +// on a page that need to be redirected to the new page (e.g., |
| 14 | +// /rest/reference/repos#statuses to |
| 15 | +// /rest/reference/commits#commit-statuses) |
8 | 16 | export default function ClientSideRedirectExceptions() {
|
9 | 17 | const router = useRouter()
|
10 | 18 | useEffect(() => {
|
11 |
| - // We have some one-off redirects for rest api docs |
12 |
| - // currently those are limited to the repos page, but |
13 |
| - // that will grow soon as we restructure the rest api docs. |
14 |
| - // This is a workaround to updating the hardcoded links |
15 |
| - // directly in the REST API code in a separate repo, which |
16 |
| - // requires many file changes and teams to sign off. |
17 |
| - // While the organization is turbulent, we can do this. |
18 |
| - // Once it's more settled, we can refactor the rest api code |
19 |
| - // to leverage the OpenAPI urls rather than hardcoded urls. |
20 |
| - const { hash, pathname } = window.location |
| 19 | + // Because we have an async call to fetch, it's possible that this |
| 20 | + // component unmounts before we perform the redirect, however, React |
| 21 | + // will still try to perform the redirect even after the component |
| 22 | + // is unmounted. To prevent this, we can use the AbortController signal |
| 23 | + // to abort the Web request when the component unmounts. |
| 24 | + const controller = new AbortController() |
| 25 | + const signal = controller.signal |
21 | 26 |
|
22 |
| - // The `hash` will start with a `#` but all the keys in |
23 |
| - // `overrideRedirects` do not. Hence, this slice. |
24 |
| - const combined = pathname + hash |
25 |
| - const overrideKey = combined |
| 27 | + const { hash, pathname } = window.location |
| 28 | + // path without a version or language |
| 29 | + const barePath = pathname |
26 | 30 | .replace(`/${router.locale}`, '')
|
27 | 31 | .replace(`/${router.query.versionId || ''}`, '')
|
28 |
| - const redirectToName = overrideRedirects[overrideKey] |
29 |
| - if (redirectToName) { |
30 |
| - const newPathname = combined.replace(overrideKey, redirectToName) |
31 |
| - router.replace(newPathname) |
| 32 | + |
| 33 | + async function getRedirect() { |
| 34 | + try { |
| 35 | + const sp = new URLSearchParams() |
| 36 | + sp.set('path', barePath) |
| 37 | + sp.set('hash', hash.replace(/^#/, '')) |
| 38 | + |
| 39 | + // call the anchor-redirect endpoint to get the redirect url |
| 40 | + const response = await fetch(`/anchor-redirect?${sp.toString()}`, { |
| 41 | + signal, |
| 42 | + }) |
| 43 | + |
| 44 | + // the response status will always be 200 unless there |
| 45 | + // was a problem with the fetch request. When the |
| 46 | + // redirect doesn't exist the json response will be empty |
| 47 | + if (response.ok) { |
| 48 | + const { to } = await response.json() |
| 49 | + if (to) { |
| 50 | + // we want to redirect with the language and version in tact |
| 51 | + // so we'll replace the full url's path and hash |
| 52 | + const fromUrl = pathname + hash |
| 53 | + const bareUrl = barePath + hash |
| 54 | + const toUrl = fromUrl.replace(bareUrl, to) |
| 55 | + router.replace(toUrl) |
| 56 | + } |
| 57 | + } |
| 58 | + } catch (error) { |
| 59 | + console.warn('Unable to fetch client-side redirect:', error) |
| 60 | + } |
32 | 61 | }
|
| 62 | + getRedirect() |
| 63 | + |
| 64 | + return () => controller.abort() |
33 | 65 | }, [])
|
34 | 66 |
|
35 | 67 | return null
|
|
0 commit comments