Add prefixes for cloud url params (#9649)

This PR adds an ability to exclude some keys in URLSearchParams from being parsed by GUI.
This commit is contained in:
Sergei Garin 2024-04-09 10:38:06 +03:00 committed by GitHub
parent fc557f8fd2
commit 2c78f4eefd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 57 additions and 7 deletions

View File

@ -11,6 +11,14 @@ async function runApp(
_metadata?: object | undefined,
pinia?: Pinia | undefined,
) {
const ignoreParamsRegex = (() => {
if (_metadata)
if ('ignoreParamsRegex' in _metadata)
if (_metadata['ignoreParamsRegex'] instanceof RegExp) return _metadata['ignoreParamsRegex']
return null
})()
running = true
const { mountProjectApp } = await import('./createApp')
if (!running) return
@ -19,7 +27,11 @@ async function runApp(
function onUnrecognizedOption(path: string[]) {
unrecognizedOptions.push(path.join('.'))
}
const intermediateConfig = mergeConfig(baseConfig, urlParams(), { onUnrecognizedOption })
const intermediateConfig = mergeConfig(
baseConfig,
urlParams({ ignoreKeysRegExp: ignoreParamsRegex }),
{ onUnrecognizedOption },
)
const appConfig = mergeConfig(intermediateConfig, config ?? {})
unmount = await mountProjectApp({ config: appConfig, accessToken, unrecognizedOptions }, pinia)
}

View File

@ -1,13 +1,25 @@
import type { StringConfig } from './config'
export interface UrlParamsProps {
ignoreKeysRegExp?: RegExp | null
}
/** Returns the parameters passed in the URL query string. */
export function urlParams(): StringConfig {
export function urlParams(props: UrlParamsProps = {}): StringConfig {
const { ignoreKeysRegExp } = props
const params: StringConfig = {}
const urlParams = new URLSearchParams(window.location.search)
for (const [name, value] of urlParams.entries()) {
let obj = params
const path = name.split('.')
const lastSegment = path.pop()
if (ignoreKeysRegExp != null && ignoreKeysRegExp.test(name)) {
continue
}
if (lastSegment == null) {
console.error(`Invalid URL parameter name: '${name}'`)
} else {

View File

@ -4,6 +4,10 @@
// === Constants ===
// =================
// =============
// === Paths ===
// =============
/** Path to the root of the app (i.e., the Cloud dashboard). */
export const DASHBOARD_PATH = '/'
/** Path to the login page. */
@ -28,3 +32,9 @@ export const ALL_PATHS_REGEX = new RegExp(
`${FORGOT_PASSWORD_PATH}|${RESET_PASSWORD_PATH}|${SET_USERNAME_PATH}|` +
`${ENTER_OFFLINE_MODE_PATH}|${SUBSCRIBE_PATH})$`
)
// ===========
// === URL ===
// ===========
export const SEARCH_PARAMS_PREFIX = 'cloud-ide_'

View File

@ -7,6 +7,8 @@ import * as React from 'react'
import * as reactRouterDom from 'react-router-dom'
import * as appUtils from '#/appUtils'
import * as eventCallback from '#/hooks/eventCallbackHooks'
import * as lazyMemo from '#/hooks/useLazyMemoHooks'
@ -32,22 +34,31 @@ export function useSearchParamsState<T = unknown>(
): SearchParamsStateReturnType<T> {
const [searchParams, setSearchParams] = reactRouterDom.useSearchParams()
const prefixedKey = `${appUtils.SEARCH_PARAMS_PREFIX}${key}`
const lazyDefaultValueInitializer = lazyMemo.useLazyMemoHooks(defaultValue, [])
const predicateEventCallback = eventCallback.useEventCallback(predicate)
const clear = eventCallback.useEventCallback((replace: boolean = false) => {
searchParams.delete(key)
searchParams.delete(prefixedKey)
setSearchParams(searchParams, { replace })
})
const unprefixedValue = searchParams.get(key)
if (unprefixedValue != null) {
searchParams.set(prefixedKey, unprefixedValue)
searchParams.delete(key)
setSearchParams(searchParams)
}
const rawValue = React.useMemo<T>(() => {
const maybeValue = searchParams.get(key)
const maybeValue = searchParams.get(prefixedKey)
const defaultValueFrom = lazyDefaultValueInitializer()
return maybeValue != null
? safeJsonParse.safeJsonParse(maybeValue, defaultValueFrom, (unknown): unknown is T => true)
: defaultValueFrom
}, [key, lazyDefaultValueInitializer, searchParams])
}, [prefixedKey, lazyDefaultValueInitializer, searchParams])
const isValueValid = predicateEventCallback(rawValue)
@ -71,7 +82,7 @@ export function useSearchParamsState<T = unknown>(
if (nextValue === lazyDefaultValueInitializer()) {
clear()
} else {
searchParams.set(key, JSON.stringify(nextValue))
searchParams.set(prefixedKey, JSON.stringify(nextValue))
setSearchParams(searchParams)
}
})

View File

@ -1,6 +1,8 @@
/** @file The container that launches the IDE. */
import * as React from 'react'
import * as appUtils from '#/appUtils'
import * as toastAndLogHooks from '#/hooks/toastAndLogHooks'
import * as backendModule from '#/services/Backend'
@ -114,7 +116,10 @@ export default function Editor(props: EditorProps) {
},
},
accessToken,
{ projectId: project.projectId }
{
projectId: project.projectId,
ignoreParamsRegex: new RegExp(`^${appUtils.SEARCH_PARAMS_PREFIX}(.+)$`),
}
)
} catch (error) {
toastAndLog('openEditorError', error)