Explore: Refactor & centralize URL/state sync (#66286)

Co-authored-by: Piotr Jamróz <pm.jamroz@gmail.com>
This commit is contained in:
Giordano Ricci
2023-06-06 15:31:39 +01:00
committed by GitHub
parent 6900336f09
commit 067bbcbe56
42 changed files with 1360 additions and 1441 deletions

View File

@@ -1,4 +1,4 @@
import { flatten, omit, uniq } from 'lodash';
import { omit } from 'lodash';
import { Unsubscribable } from 'rxjs';
import { v4 as uuidv4 } from 'uuid';
@@ -8,21 +8,16 @@ import {
DataQueryRequest,
DataSourceApi,
DataSourceRef,
dateMath,
DateTime,
DefaultTimeZone,
ExploreUrlState,
HistoryItem,
IntervalValues,
isDateTime,
LogsDedupStrategy,
LogsSortOrder,
rangeUtil,
RawTimeRange,
TimeFragment,
TimeRange,
TimeZone,
toUtc,
urlUtil,
} from '@grafana/data';
import { DataSourceSrv, getDataSourceSrv } from '@grafana/runtime';
@@ -48,8 +43,12 @@ export const DEFAULT_UI_STATE = {
const MAX_HISTORY_ITEMS = 100;
export const LAST_USED_DATASOURCE_KEY = 'grafana.explore.datasource';
export const lastUsedDatasourceKeyForOrgId = (orgId: number) => `${LAST_USED_DATASOURCE_KEY}.${orgId}`;
const LAST_USED_DATASOURCE_KEY = 'grafana.explore.datasource';
const lastUsedDatasourceKeyForOrgId = (orgId: number) => `${LAST_USED_DATASOURCE_KEY}.${orgId}`;
export const getLastUsedDatasourceUID = (orgId: number) =>
store.getObject<string>(lastUsedDatasourceKeyForOrgId(orgId));
export const setLastUsedDatasourceUID = (orgId: number, datasourceUID: string) =>
store.setObject(lastUsedDatasourceKeyForOrgId(orgId), datasourceUID);
export interface GetExploreUrlArguments {
panel: PanelModel;
@@ -223,8 +222,7 @@ export function parseUrlState(initial: string | undefined): ExploreUrlState {
}
if (!Array.isArray(parsed)) {
const urlState = { ...parsed, isFromCompactUrl: false };
return urlState;
return { queries: [], range: DEFAULT_RANGE, ...parsed };
}
if (parsed.length <= ParseUrlStateIndex.SegmentsStart) {
@@ -241,7 +239,7 @@ export function parseUrlState(initial: string | undefined): ExploreUrlState {
const queries = parsedSegments.filter((segment) => !isSegment(segment, 'ui', 'mode', '__panelsState'));
const panelsState = parsedSegments.find((segment) => isSegment(segment, '__panelsState'))?.__panelsState;
return { datasource, queries, range, panelsState, isFromCompactUrl: true };
return { datasource, queries, range, panelsState };
}
export function generateKey(index = 0): string {
@@ -285,15 +283,6 @@ export const generateNewKeyAndAddRefIdIfMissing = (target: DataQuery, queries: D
return { ...target, refId, key };
};
export const queryDatasourceDetails = (queries: DataQuery[]) => {
const allUIDs = queries.map((query) => query.datasource?.uid);
return {
allHaveDatasource: allUIDs.length === queries.length,
noneHaveDatasource: allUIDs.length === 0,
allDatasourceSame: allUIDs.every((val, i, arr) => val === arr[0]),
};
};
/**
* Ensure at least one target exists and that targets have the necessary keys
*
@@ -396,11 +385,6 @@ export function updateHistory<T extends DataQuery>(
}
}
export function clearHistory(datasourceId: string) {
const historyKey = `grafana.explore.history.${datasourceId}`;
store.delete(historyKey);
}
export const getQueryKeys = (queries: DataQuery[]): string[] => {
const queryKeys = queries.reduce<string[]>((newQueryKeys, query, index) => {
const primaryKey = query.datasource?.uid || query.key;
@@ -420,105 +404,6 @@ export const getTimeRange = (timeZone: TimeZone, rawRange: RawTimeRange, fiscalY
return range;
};
const parseRawTime = (value: string | DateTime): TimeFragment | null => {
if (value === null) {
return null;
}
if (isDateTime(value)) {
return value;
}
if (value.indexOf('now') !== -1) {
return value;
}
if (value.length === 8) {
return toUtc(value, 'YYYYMMDD');
}
if (value.length === 15) {
return toUtc(value, 'YYYYMMDDTHHmmss');
}
// Backward compatibility
if (value.length === 19) {
return toUtc(value, 'YYYY-MM-DD HH:mm:ss');
}
// This should handle cases where value is an epoch time as string
if (value.match(/^\d+$/)) {
const epoch = parseInt(value, 10);
return toUtc(epoch);
}
// This should handle ISO strings
const time = toUtc(value);
if (time.isValid()) {
return time;
}
return null;
};
export const getTimeRangeFromUrl = (
range: RawTimeRange,
timeZone: TimeZone,
fiscalYearStartMonth: number
): TimeRange => {
const raw = {
from: parseRawTime(range.from)!,
to: parseRawTime(range.to)!,
};
return {
from: dateMath.parse(raw.from, false, timeZone)!,
to: dateMath.parse(raw.to, true, timeZone)!,
raw,
};
};
export const getValueWithRefId = (value?: any): any => {
if (!value || typeof value !== 'object') {
return undefined;
}
if (value.refId) {
return value;
}
const keys = Object.keys(value);
for (let index = 0; index < keys.length; index++) {
const key = keys[index];
const refId = getValueWithRefId(value[key]);
if (refId) {
return refId;
}
}
return undefined;
};
export const getRefIds = (value: any): string[] => {
if (!value) {
return [];
}
if (typeof value !== 'object') {
return [];
}
const keys = Object.keys(value);
const refIds = [];
for (let index = 0; index < keys.length; index++) {
const key = keys[index];
if (key === 'refId') {
refIds.push(value[key]);
continue;
}
refIds.push(getRefIds(value[key]));
}
return uniq(flatten(refIds));
};
export const refreshIntervalToSortOrder = (refreshInterval?: string) =>
RefreshPicker.isLive(refreshInterval) ? LogsSortOrder.Ascending : LogsSortOrder.Descending;