Explore: use numeric ids for panel ids when building query transactions (#80135)

pull/80447/head
Giordano Ricci 1 year ago committed by GitHub
parent cc050ca317
commit e471064cda
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      .betterer.results
  2. 1
      jest.config.js
  3. 1
      package.json
  4. 29
      public/app/core/utils/explore.ts
  5. 16
      public/app/features/explore/hooks/useStateSync/migrators/v1.ts
  6. 13
      public/app/features/explore/state/main.ts
  7. 10
      yarn.lock

@ -1188,9 +1188,7 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "0"] [0, 0, 0, "Unexpected any. Specify a different type.", "0"]
], ],
"public/app/core/utils/explore.ts:5381": [ "public/app/core/utils/explore.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "0"]
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"]
], ],
"public/app/core/utils/fetch.ts:5381": [ "public/app/core/utils/fetch.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Do not use any type assertions.", "0"],

@ -12,6 +12,7 @@ const esModules = [
'internmap', 'internmap',
'robust-predicates', 'robust-predicates',
'leven', 'leven',
'nanoid',
].join('|'); ].join('|');
module.exports = { module.exports = {

@ -356,6 +356,7 @@
"mousetrap": "1.6.5", "mousetrap": "1.6.5",
"mousetrap-global-bind": "1.1.0", "mousetrap-global-bind": "1.1.0",
"moveable": "0.43.1", "moveable": "0.43.1",
"nanoid": "^5.0.4",
"node-forge": "^1.3.1", "node-forge": "^1.3.1",
"ol": "7.4.0", "ol": "7.4.0",
"ol-ext": "4.0.10", "ol-ext": "4.0.10",

@ -1,4 +1,4 @@
import { nanoid } from '@reduxjs/toolkit'; import { customAlphabet } from 'nanoid';
import { Unsubscribable } from 'rxjs'; import { Unsubscribable } from 'rxjs';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
@ -34,6 +34,9 @@ export const DEFAULT_UI_STATE = {
dedupStrategy: LogsDedupStrategy.none, dedupStrategy: LogsDedupStrategy.none,
}; };
export const ID_ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyz';
const nanoid = customAlphabet(ID_ALPHABET, 3);
const MAX_HISTORY_ITEMS = 100; const MAX_HISTORY_ITEMS = 100;
const LAST_USED_DATASOURCE_KEY = 'grafana.explore.datasource'; const LAST_USED_DATASOURCE_KEY = 'grafana.explore.datasource';
@ -52,7 +55,13 @@ export interface GetExploreUrlArguments {
} }
export function generateExploreId() { export function generateExploreId() {
return nanoid(3); while (true) {
const id = nanoid(3);
if (!/^\d+$/.test(id)) {
return id;
}
}
} }
/** /**
@ -101,19 +110,9 @@ export function buildQueryTransaction(
timeZone?: TimeZone, timeZone?: TimeZone,
scopedVars?: ScopedVars scopedVars?: ScopedVars
): QueryTransaction { ): QueryTransaction {
const key = queries.reduce((combinedKey, query) => { const panelId = Number.parseInt(exploreId, 36);
combinedKey += query.key;
return combinedKey;
}, '');
const { interval, intervalMs } = getIntervals(range, queryOptions.minInterval, queryOptions.maxDataPoints); const { interval, intervalMs } = getIntervals(range, queryOptions.minInterval, queryOptions.maxDataPoints);
// Most datasource is using `panelId + query.refId` for cancellation logic.
// Using `format` here because it relates to the view panel that the request is for.
// However, some datasources don't use `panelId + query.refId`, but only `panelId`.
// Therefore panel id has to be unique.
const panelId = `${key}`;
const request: DataQueryRequest = { const request: DataQueryRequest = {
app: CoreApp.Explore, app: CoreApp.Explore,
// TODO probably should be taken from preferences but does not seem to be used anyway. // TODO probably should be taken from preferences but does not seem to be used anyway.
@ -121,9 +120,7 @@ export function buildQueryTransaction(
startTime: Date.now(), startTime: Date.now(),
interval, interval,
intervalMs, intervalMs,
// TODO: the query request expects number and we are using string here. Seems like it works so far but can create panelId,
// issues down the road.
panelId: panelId as any,
targets: queries, // Datasources rely on DataQueries being passed under the targets key. targets: queries, // Datasources rely on DataQueries being passed under the targets key.
range, range,
requestId: 'explore_' + exploreId, requestId: 'explore_' + exploreId,

@ -1,5 +1,5 @@
import { ExploreUrlState } from '@grafana/data'; import { ExploreUrlState } from '@grafana/data';
import { generateExploreId } from 'app/core/utils/explore'; import { ID_ALPHABET, generateExploreId } from 'app/core/utils/explore';
import { DEFAULT_RANGE } from 'app/features/explore/state/utils'; import { DEFAULT_RANGE } from 'app/features/explore/state/utils';
import { hasKey } from '../../utils'; import { hasKey } from '../../utils';
@ -48,9 +48,21 @@ export const v1Migrator: MigrationHandler<ExploreURLV0, ExploreURLV1> = {
const panes = Object.entries(rawPanes) const panes = Object.entries(rawPanes)
.map(([key, value]) => [key, applyDefaults(value)] as const) .map(([key, value]) => [key, applyDefaults(value)] as const)
.reduce<Record<string, ExploreUrlState>>((acc, [key, value]) => { .reduce<Record<string, ExploreUrlState>>((acc, [key, value]) => {
let newKey = key;
// Panes IDs must be 3 characters long and contain at least one letter
if (
newKey.length !== 3 ||
/^\d+$/.test(newKey) ||
newKey.split('').some((ch) => {
return ID_ALPHABET.indexOf(ch) === -1;
})
) {
newKey = generateExploreId();
}
return { return {
...acc, ...acc,
[key]: value, [newKey]: value,
}; };
}, {}); }, {});

@ -65,7 +65,7 @@ export const clearPanes = createAction('explore/clearPanes');
*/ */
export const splitOpen = createAsyncThunk( export const splitOpen = createAsyncThunk(
'explore/splitOpen', 'explore/splitOpen',
async (options: SplitOpenOptions | undefined, { getState, dispatch, requestId }) => { async (options: SplitOpenOptions | undefined, { getState, dispatch }) => {
// we currently support showing only 2 panes in explore, so if this action is dispatched we know it has been dispatched from the "first" pane. // we currently support showing only 2 panes in explore, so if this action is dispatched we know it has been dispatched from the "first" pane.
const originState = Object.values(getState().explore.panes)[0]; const originState = Object.values(getState().explore.panes)[0];
@ -80,9 +80,15 @@ export const splitOpen = createAsyncThunk(
const splitRange = options?.range || originState?.range.raw || DEFAULT_RANGE; const splitRange = options?.range || originState?.range.raw || DEFAULT_RANGE;
let newPaneId = generateExploreId();
// in case we have a duplicate id, generate a new one
while (getState().explore.panes[newPaneId]) {
newPaneId = generateExploreId();
}
await dispatch( await dispatch(
createNewSplitOpenPane({ createNewSplitOpenPane({
exploreId: requestId, exploreId: newPaneId,
datasource: options?.datasourceUid || originState?.datasourceInstance?.getRef(), datasource: options?.datasourceUid || originState?.datasourceInstance?.getRef(),
queries: withUniqueRefIds(queries), queries: withUniqueRefIds(queries),
range: splitRange, range: splitRange,
@ -95,9 +101,6 @@ export const splitOpen = createAsyncThunk(
if (originState?.range) { if (originState?.range) {
await dispatch(syncTimesAction({ syncedTimes: isEqual(originState.range.raw, splitRange) })); // if time ranges are equal, mark times as synced await dispatch(syncTimesAction({ syncedTimes: isEqual(originState.range.raw, splitRange) })); // if time ranges are equal, mark times as synced
} }
},
{
idGenerator: generateExploreId,
} }
); );

@ -17273,6 +17273,7 @@ __metadata:
moveable: "npm:0.43.1" moveable: "npm:0.43.1"
msw: "npm:1.3.2" msw: "npm:1.3.2"
mutationobserver-shim: "npm:0.3.7" mutationobserver-shim: "npm:0.3.7"
nanoid: "npm:^5.0.4"
ngtemplate-loader: "npm:2.1.0" ngtemplate-loader: "npm:2.1.0"
node-forge: "npm:^1.3.1" node-forge: "npm:^1.3.1"
node-notifier: "npm:10.0.1" node-notifier: "npm:10.0.1"
@ -21889,6 +21890,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"nanoid@npm:^5.0.4":
version: 5.0.4
resolution: "nanoid@npm:5.0.4"
bin:
nanoid: bin/nanoid.js
checksum: cf09cca3774f3147100948f7478f75f4c9ee97a4af65c328dd9abbd83b12f8bb35cf9f89a21c330f3b759d667a4cd0140ed84aa5fdd522c61e0d341aeaa7fb6f
languageName: node
linkType: hard
"natural-compare@npm:^1.4.0": "natural-compare@npm:^1.4.0":
version: 1.4.0 version: 1.4.0
resolution: "natural-compare@npm:1.4.0" resolution: "natural-compare@npm:1.4.0"

Loading…
Cancel
Save