grafana/public/app/features/explore/state/utils.ts

175 lines
5.0 KiB
TypeScript
Raw Normal View History

import { isEmpty, isObject, mapValues, omitBy } from 'lodash';
import {
Loki: Full range logs volume (#39327) * Basic implementation of getLogsVolumeQuery method * Add todos * Add a switcher to automatically load logs volume * De-scope dismissing logs volume panel * De-scope logs volume query cancellation * Remove todo * Aggregate logs volume components in single panel * Show logs volume only when it's available * Aggregate logs volume by level * Simplify aggregation * Handle no logs volume data * Add error handling * Do not show auto-load logs volume switcher when loading logs volume is not available * Remove old logs volume graph * Clean up * Make getting data provider more generic * Provide complete logs volume data (error, isLoading) * Display more specific error message * Add missing props to mocks * Remove setRequest method * Mark getQueryRelatedDataProviders as internal * Add missing dataQueryRequest and add a todo * Remove redundant loading state * Do not mutate existing queries * Apply fix for zooming-in from main * Post-merge fixes * Create collection for data provider results * Use more generic names * Move aggregation logic to Loki logs volume provider * Move LogsVolume to common types * Update tests * Post-merge fixes * Fix mapping related data values * Simplify prop mappings * Add docs * Fix property name * Clean-up * Mark new types as internal * Reduce number of providers to logs volume only * Simplify data structure to DataQueryResponse * Move Logs Volume panel to a separate component * Test logsVolumeProvider.ts * Add observable version of datasource mock * Test getLogsVolumeDataProvider method * Test LogsVolumePanel * Test logs volume reducer * Clean up * Clean up * Fix test * Use sum by to use level field directly * Fix strict type errors * Fix strict type errors * Use "logs" instead of "unknown" if only one level was detected * Add docs about logs volume * Rename histogramRequest to logsVolumeRequest * Use LogsVolumeContentWrapper all content types * Move `autoLoadLogsVolume` local storage handling * Fix strict error * Move getting autoLoadLogsVolume to initial state * Cancel current logs volume subscription * Test cancelling subscriptions * Update docs/sources/datasources/loki.md Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com> * Update packages/grafana-data/src/types/explore.ts Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com> * Inline container styles * Ensure logs volume is aggregated per each subscription separately * Simplify logs volume provider * Type-guard support for logs volume provider * Simplify event handlers to avoid casting * Clean up and docs * Move auto-load switcher to logs volume panel * Fix test * Move DataSourceWithLogsVolumeSupport to avoid cross referencing * Simplify interface * Bring back old histogram and hide the new one behind a feature flag * Add missing props to logs histogram panel * Clean up the provider when it's not supported * Simplify storing autoLoadLogsVolume * Remove docs * Update packages/grafana-data/src/types/logsVolume.ts Co-authored-by: Andrej Ocenas <mr.ocenas@gmail.com> * Skip dataframes without fields (instant queries) * Revert styles changes * Revert styles changes * Add release tag Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com> Co-authored-by: Andrej Ocenas <mr.ocenas@gmail.com>
2021-09-30 08:46:11 -05:00
AbsoluteTimeRange,
DataSourceApi,
DataSourceRef,
EventBusExtended,
ExploreUrlState,
getDefaultTimeRange,
HistoryItem,
LoadingState,
PanelData,
} from '@grafana/data';
import { ExplorePanelData } from 'app/types';
import { ExploreItemState } from 'app/types/explore';
import store from '../../../core/store';
import { clearQueryKeys, lastUsedDatasourceKeyForOrgId } from '../../../core/utils/explore';
import { getDatasourceSrv } from '../../plugins/datasource_srv';
import { SETTINGS_KEYS } from '../utils/logs';
import { toRawTimeRange } from '../utils/time';
export const DEFAULT_RANGE = {
from: 'now-6h',
to: 'now',
};
const GRAPH_STYLE_KEY = 'grafana.explore.style.graph';
export const storeGraphStyle = (graphStyle: string): void => {
store.set(GRAPH_STYLE_KEY, graphStyle);
};
const LOGS_VOLUME_ENABLED_KEY = SETTINGS_KEYS.enableVolumeHistogram;
export const storeLogsVolumeEnabled = (enabled: boolean): void => {
store.set(LOGS_VOLUME_ENABLED_KEY, enabled ? 'true' : 'false');
};
const loadLogsVolumeEnabled = (): boolean => {
const data = store.get(LOGS_VOLUME_ENABLED_KEY);
// we default to `enabled=true`
if (data === 'false') {
return false;
}
return true;
};
/**
* Returns a fresh Explore area state
*/
export const makeExplorePaneState = (): ExploreItemState => ({
containerWidth: 0,
datasourceInstance: null,
datasourceMissing: false,
history: [],
queries: [],
initialized: false,
range: {
from: null,
to: null,
raw: DEFAULT_RANGE,
} as any,
absoluteRange: {
from: null,
to: null,
} as any,
scanning: false,
loading: false,
queryKeys: [],
isLive: false,
isPaused: false,
queryResponse: createEmptyQueryResponse(),
tableResult: null,
graphResult: null,
logsResult: null,
eventBridge: null as unknown as EventBusExtended,
cache: [],
richHistory: [],
logsVolumeEnabled: loadLogsVolumeEnabled(),
Loki: Full range logs volume (#39327) * Basic implementation of getLogsVolumeQuery method * Add todos * Add a switcher to automatically load logs volume * De-scope dismissing logs volume panel * De-scope logs volume query cancellation * Remove todo * Aggregate logs volume components in single panel * Show logs volume only when it's available * Aggregate logs volume by level * Simplify aggregation * Handle no logs volume data * Add error handling * Do not show auto-load logs volume switcher when loading logs volume is not available * Remove old logs volume graph * Clean up * Make getting data provider more generic * Provide complete logs volume data (error, isLoading) * Display more specific error message * Add missing props to mocks * Remove setRequest method * Mark getQueryRelatedDataProviders as internal * Add missing dataQueryRequest and add a todo * Remove redundant loading state * Do not mutate existing queries * Apply fix for zooming-in from main * Post-merge fixes * Create collection for data provider results * Use more generic names * Move aggregation logic to Loki logs volume provider * Move LogsVolume to common types * Update tests * Post-merge fixes * Fix mapping related data values * Simplify prop mappings * Add docs * Fix property name * Clean-up * Mark new types as internal * Reduce number of providers to logs volume only * Simplify data structure to DataQueryResponse * Move Logs Volume panel to a separate component * Test logsVolumeProvider.ts * Add observable version of datasource mock * Test getLogsVolumeDataProvider method * Test LogsVolumePanel * Test logs volume reducer * Clean up * Clean up * Fix test * Use sum by to use level field directly * Fix strict type errors * Fix strict type errors * Use "logs" instead of "unknown" if only one level was detected * Add docs about logs volume * Rename histogramRequest to logsVolumeRequest * Use LogsVolumeContentWrapper all content types * Move `autoLoadLogsVolume` local storage handling * Fix strict error * Move getting autoLoadLogsVolume to initial state * Cancel current logs volume subscription * Test cancelling subscriptions * Update docs/sources/datasources/loki.md Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com> * Update packages/grafana-data/src/types/explore.ts Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com> * Inline container styles * Ensure logs volume is aggregated per each subscription separately * Simplify logs volume provider * Type-guard support for logs volume provider * Simplify event handlers to avoid casting * Clean up and docs * Move auto-load switcher to logs volume panel * Fix test * Move DataSourceWithLogsVolumeSupport to avoid cross referencing * Simplify interface * Bring back old histogram and hide the new one behind a feature flag * Add missing props to logs histogram panel * Clean up the provider when it's not supported * Simplify storing autoLoadLogsVolume * Remove docs * Update packages/grafana-data/src/types/logsVolume.ts Co-authored-by: Andrej Ocenas <mr.ocenas@gmail.com> * Skip dataframes without fields (instant queries) * Revert styles changes * Revert styles changes * Add release tag Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com> Co-authored-by: Andrej Ocenas <mr.ocenas@gmail.com>
2021-09-30 08:46:11 -05:00
logsVolumeDataProvider: undefined,
logsVolumeData: undefined,
panelsState: {},
});
export const createEmptyQueryResponse = (): ExplorePanelData => ({
state: LoadingState.NotStarted,
series: [],
timeRange: getDefaultTimeRange(),
graphFrames: [],
logsFrames: [],
traceFrames: [],
nodeGraphFrames: [],
flameGraphFrames: [],
tableFrames: [],
graphResult: null,
logsResult: null,
tableResult: null,
});
export async function loadAndInitDatasource(
orgId: number,
datasource: DataSourceRef | string
): Promise<{ history: HistoryItem[]; instance: DataSourceApi }> {
let instance;
try {
// let datasource be a ref if we have the info, otherwise a name or uid will do for lookup
instance = await getDatasourceSrv().get(datasource);
} catch (error) {
// Falling back to the default data source in case the provided data source was not found.
// It may happen if last used data source or the data source provided in the URL has been
// removed or it is not provisioned anymore.
instance = await getDatasourceSrv().get();
}
if (instance.init) {
try {
instance.init();
} catch (err) {
// TODO: should probably be handled better
console.error(err);
}
}
const historyKey = `grafana.explore.history.${instance.meta?.id}`;
const history = store.getObject<HistoryItem[]>(historyKey, []);
// Save last-used datasource
store.set(lastUsedDatasourceKeyForOrgId(orgId), instance.uid);
return { history, instance };
}
// recursively walks an object, removing keys where the value is undefined
// if the resulting object is empty, returns undefined
function pruneObject(obj: object): object | undefined {
let pruned = mapValues(obj, (value) => (isObject(value) ? pruneObject(value) : value));
pruned = omitBy<typeof pruned>(pruned, isEmpty);
if (isEmpty(pruned)) {
return undefined;
}
return pruned;
}
export function getUrlStateFromPaneState(pane: ExploreItemState): ExploreUrlState {
return {
// datasourceInstance should not be undefined anymore here but in case there is some path for it to be undefined
// lets just fallback instead of crashing.
datasource: pane.datasourceInstance?.uid || '',
queries: pane.queries.map(clearQueryKeys),
range: toRawTimeRange(pane.range),
// don't include panelsState in the url unless a piece of state is actually set
panelsState: pruneObject(pane.panelsState),
};
}
export function createCacheKey(absRange: AbsoluteTimeRange) {
const params = {
from: absRange.from,
to: absRange.to,
};
const cacheKey = Object.entries(params)
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v.toString())}`)
.join('&');
return cacheKey;
}
export function getResultsFromCache(
cache: Array<{ key: string; value: PanelData }>,
absoluteRange: AbsoluteTimeRange
): PanelData | undefined {
const cacheKey = createCacheKey(absoluteRange);
const cacheIdx = cache.findIndex((c) => c.key === cacheKey);
const cacheValue = cacheIdx >= 0 ? cache[cacheIdx].value : undefined;
return cacheValue;
}