mirror of https://github.com/apache/druid.git
Web console: make it possible to namespace local storage, auto flatten spec generator should deal better with bad data (#12238)
* improve computeFlattenExprsForData * allow local storage namespacing * add test
This commit is contained in:
parent
ced1389d4c
commit
090c429c8c
|
@ -49,6 +49,7 @@ describe('spec-utils', () => {
|
||||||
],
|
],
|
||||||
value: 2,
|
value: 2,
|
||||||
},
|
},
|
||||||
|
'Curveball' as any,
|
||||||
];
|
];
|
||||||
|
|
||||||
it('works for path, ignore-arrays', () => {
|
it('works for path, ignore-arrays', () => {
|
||||||
|
|
|
@ -82,13 +82,15 @@ export function computeFlattenExprsForData(
|
||||||
data: Record<string, any>[],
|
data: Record<string, any>[],
|
||||||
exprType: ExprType,
|
exprType: ExprType,
|
||||||
arrayHandling: ArrayHandling,
|
arrayHandling: ArrayHandling,
|
||||||
|
includeTopLevel = false,
|
||||||
): string[] {
|
): string[] {
|
||||||
const seenPaths: Record<string, boolean> = {};
|
const seenPaths: Record<string, boolean> = {};
|
||||||
for (const datum of data) {
|
for (const datum of data) {
|
||||||
|
if (!datum || typeof datum !== 'object') continue;
|
||||||
const datumKeys = Object.keys(datum);
|
const datumKeys = Object.keys(datum);
|
||||||
for (const datumKey of datumKeys) {
|
for (const datumKey of datumKeys) {
|
||||||
const datumValue = datum[datumKey];
|
const datumValue = datum[datumKey];
|
||||||
if (isNested(datumValue)) {
|
if (includeTopLevel || isNested(datumValue)) {
|
||||||
addPath(
|
addPath(
|
||||||
seenPaths,
|
seenPaths,
|
||||||
exprType === 'path' ? `$.${datumKey}` : `.${datumKey}`,
|
exprType === 'path' ? `$.${datumKey}` : `.${datumKey}`,
|
||||||
|
|
|
@ -29,6 +29,7 @@ import { bootstrapReactTable } from './bootstrap/react-table-defaults';
|
||||||
import { ConsoleApplication } from './console-application';
|
import { ConsoleApplication } from './console-application';
|
||||||
import { Links, setLinkOverrides } from './links';
|
import { Links, setLinkOverrides } from './links';
|
||||||
import { Api, UrlBaser } from './singletons';
|
import { Api, UrlBaser } from './singletons';
|
||||||
|
import { setLocalStorageNamespace } from './utils';
|
||||||
|
|
||||||
import './entry.scss';
|
import './entry.scss';
|
||||||
|
|
||||||
|
@ -63,6 +64,9 @@ interface ConsoleConfig {
|
||||||
|
|
||||||
// Allow for link overriding to different docs
|
// Allow for link overriding to different docs
|
||||||
linkOverrides?: Links;
|
linkOverrides?: Links;
|
||||||
|
|
||||||
|
// Allow for namespacing the local storage in case multiple clusters share a URL due to proxying
|
||||||
|
localStorageNamespace?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const consoleConfig: ConsoleConfig = (window as any).consoleConfig;
|
const consoleConfig: ConsoleConfig = (window as any).consoleConfig;
|
||||||
|
@ -89,6 +93,10 @@ if (consoleConfig.linkOverrides) {
|
||||||
setLinkOverrides(consoleConfig.linkOverrides);
|
setLinkOverrides(consoleConfig.linkOverrides);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (consoleConfig.localStorageNamespace) {
|
||||||
|
setLocalStorageNamespace(consoleConfig.localStorageNamespace);
|
||||||
|
}
|
||||||
|
|
||||||
QueryRunner.defaultQueryExecutor = (payload, isSql, cancelToken) => {
|
QueryRunner.defaultQueryExecutor = (payload, isSql, cancelToken) => {
|
||||||
return Api.instance.post(`/druid/v2${isSql ? '/sql' : ''}`, payload, { cancelToken });
|
return Api.instance.post(`/druid/v2${isSql ? '/sql' : ''}`, payload, { cancelToken });
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,28 +16,22 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as JSONBig from 'json-bigint-native';
|
|
||||||
import { Dispatch, SetStateAction, useState } from 'react';
|
import { Dispatch, SetStateAction, useState } from 'react';
|
||||||
|
|
||||||
|
import { localStorageGetJson, LocalStorageKeys, localStorageSetJson } from '../utils';
|
||||||
|
|
||||||
export function useLocalStorageState<T>(
|
export function useLocalStorageState<T>(
|
||||||
key: string,
|
key: LocalStorageKeys,
|
||||||
initialValue?: T,
|
initialValue?: T,
|
||||||
): [T, Dispatch<SetStateAction<T>>] {
|
): [T, Dispatch<SetStateAction<T>>] {
|
||||||
const [state, setState] = useState(() => {
|
const [state, setState] = useState(() => {
|
||||||
try {
|
return localStorageGetJson(key) || initialValue;
|
||||||
const item = window.localStorage.getItem(key);
|
|
||||||
return item ? JSONBig.parse(item) : initialValue;
|
|
||||||
} catch {
|
|
||||||
return initialValue;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const setValue: Dispatch<SetStateAction<T>> = (value: T | ((prevState: T) => T)) => {
|
const setValue: Dispatch<SetStateAction<T>> = (value: T | ((prevState: T) => T)) => {
|
||||||
const valueToStore = value instanceof Function ? value(state) : value;
|
const valueToStore = value instanceof Function ? value(state) : value;
|
||||||
setState(valueToStore);
|
setState(valueToStore);
|
||||||
try {
|
localStorageSetJson(key, valueToStore);
|
||||||
window.localStorage.setItem(key, JSONBig.stringify(valueToStore));
|
|
||||||
} catch {}
|
|
||||||
};
|
};
|
||||||
return [state, setValue];
|
return [state, setValue];
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,10 +44,20 @@ export type LocalStorageKeys = typeof LocalStorageKeys[keyof typeof LocalStorage
|
||||||
|
|
||||||
// ----------------------------
|
// ----------------------------
|
||||||
|
|
||||||
|
let localStorageNamespace: string | undefined;
|
||||||
|
|
||||||
|
export function setLocalStorageNamespace(namespace: string) {
|
||||||
|
localStorageNamespace = namespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
function prependNamespace(key: string): string {
|
||||||
|
return localStorageNamespace ? `${localStorageNamespace}:${key}` : key;
|
||||||
|
}
|
||||||
|
|
||||||
export function localStorageSet(key: LocalStorageKeys, value: string): void {
|
export function localStorageSet(key: LocalStorageKeys, value: string): void {
|
||||||
if (typeof localStorage === 'undefined') return;
|
if (typeof localStorage === 'undefined') return;
|
||||||
try {
|
try {
|
||||||
localStorage.setItem(key, value);
|
localStorage.setItem(prependNamespace(key), value);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Issue setting local storage key', e);
|
console.error('Issue setting local storage key', e);
|
||||||
}
|
}
|
||||||
|
@ -60,7 +70,7 @@ export function localStorageSetJson(key: LocalStorageKeys, value: any): void {
|
||||||
export function localStorageGet(key: LocalStorageKeys): string | undefined {
|
export function localStorageGet(key: LocalStorageKeys): string | undefined {
|
||||||
if (typeof localStorage === 'undefined') return;
|
if (typeof localStorage === 'undefined') return;
|
||||||
try {
|
try {
|
||||||
return localStorage.getItem(key) || undefined;
|
return localStorage.getItem(prependNamespace(key)) || localStorage.getItem(key) || undefined;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Issue getting local storage key', e);
|
console.error('Issue getting local storage key', e);
|
||||||
return;
|
return;
|
||||||
|
@ -80,7 +90,7 @@ export function localStorageGetJson(key: LocalStorageKeys): any {
|
||||||
export function localStorageRemove(key: LocalStorageKeys): void {
|
export function localStorageRemove(key: LocalStorageKeys): void {
|
||||||
if (typeof localStorage === 'undefined') return;
|
if (typeof localStorage === 'undefined') return;
|
||||||
try {
|
try {
|
||||||
localStorage.removeItem(key);
|
localStorage.removeItem(prependNamespace(key));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Issue removing local storage key', e);
|
console.error('Issue removing local storage key', e);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue