The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
grafana/public/app/core/utils/richHistory.ts

238 lines
7.5 KiB

import { omit } from 'lodash';
import { DataQuery, DataSourceApi, dateTimeFormat, ExploreUrlState, urlUtil } from '@grafana/data';
import { serializeStateToUrlParam } from '@grafana/data/src/utils/url';
import { getDataSourceSrv } from '@grafana/runtime';
import { notifyApp } from 'app/core/actions';
import { createErrorNotification, createWarningNotification } from 'app/core/copy/appNotification';
import { dispatch } from 'app/store/store';
import { RichHistoryQuery } from 'app/types/explore';
import {
RichHistoryServiceError,
RichHistoryStorageWarning,
RichHistoryStorageWarningDetails,
} from '../history/RichHistoryStorage';
import { getRichHistoryStorage } from '../history/richHistoryStorageProvider';
Query History: Move filtering to service layer (#48008) * Load Rich History when the container is opened * Store rich history for each pane separately * Do not update currently opened query history when an item is added It's impossible to figure out if the item should be added or not, because filters are applied in the backend. We don't want to replicate that filtering logic in frontend. One way to make it work could be by refreshing both panes. * Test starring and deleting query history items when both panes are open * Remove e2e dependency on ExploreId * Fix unit test * Assert exact queries * Simplify test * Fix e2e tests * Fix toolbar a11y * Reload the history after an item is added * Fix unit test * Remove references to Explore from generic PageToolbar component * Update test name * Fix test assertion * Add issue item to TODO * Improve test assertion * Simplify test setup * Move query history settings to persistence layer * Fix test import * Fix unit test * Fix unit test * Test local storage settings API * Code formatting * Fix linting errors * Add an integration test * Add missing aria role * Fix a11y issues * Fix a11y issues * Use divs instead of ul/li Otherwis,e pa11y-ci reports the error below claiming there are no children with role=tab: Certain ARIA roles must contain particular children (https://dequeuniversity.com/rules/axe/4.3/aria-required-children?application=axeAPI) (#reactRoot > div > main > div:nth-child(3) > div > div:nth-child(1) > div > div:nth-child(1) > div > div > nav > div:nth-child(2) > ul) <ul class="css-af3vye" role="tablist"><li class="css-1ciwanz"><a href...</ul> * Clean up settings tab * Remove redundant aria label * Remove redundant container * Clean up test assertions * Move filtering to persistence layer * Move filtering to persistence layer * Simplify applying filters * Split applying filters and reloading the history * Debounce updating filters * Update tests * Fix waiting for debounced results * Clear results when switching tabs * Improve test coverage * Update docs * Revert extra handling for uid (will be added when we introduce remote storage) * Fix betterer conflicts * Fix imports * Fix imports * Simplify test setup * Simplify assertion * Improve readability * Remove unnecessary casting * Mock backend in integration tests
3 years ago
import { RichHistorySearchFilters, RichHistorySettings, SortOrder } from './richHistoryTypes';
Query History: Move filtering to service layer (#48008) * Load Rich History when the container is opened * Store rich history for each pane separately * Do not update currently opened query history when an item is added It's impossible to figure out if the item should be added or not, because filters are applied in the backend. We don't want to replicate that filtering logic in frontend. One way to make it work could be by refreshing both panes. * Test starring and deleting query history items when both panes are open * Remove e2e dependency on ExploreId * Fix unit test * Assert exact queries * Simplify test * Fix e2e tests * Fix toolbar a11y * Reload the history after an item is added * Fix unit test * Remove references to Explore from generic PageToolbar component * Update test name * Fix test assertion * Add issue item to TODO * Improve test assertion * Simplify test setup * Move query history settings to persistence layer * Fix test import * Fix unit test * Fix unit test * Test local storage settings API * Code formatting * Fix linting errors * Add an integration test * Add missing aria role * Fix a11y issues * Fix a11y issues * Use divs instead of ul/li Otherwis,e pa11y-ci reports the error below claiming there are no children with role=tab: Certain ARIA roles must contain particular children (https://dequeuniversity.com/rules/axe/4.3/aria-required-children?application=axeAPI) (#reactRoot > div > main > div:nth-child(3) > div > div:nth-child(1) > div > div:nth-child(1) > div > div > nav > div:nth-child(2) > ul) <ul class="css-af3vye" role="tablist"><li class="css-1ciwanz"><a href...</ul> * Clean up settings tab * Remove redundant aria label * Remove redundant container * Clean up test assertions * Move filtering to persistence layer * Move filtering to persistence layer * Simplify applying filters * Split applying filters and reloading the history * Debounce updating filters * Update tests * Fix waiting for debounced results * Clear results when switching tabs * Improve test coverage * Update docs * Revert extra handling for uid (will be added when we introduce remote storage) * Fix betterer conflicts * Fix imports * Fix imports * Simplify test setup * Simplify assertion * Improve readability * Remove unnecessary casting * Mock backend in integration tests
3 years ago
export { RichHistorySearchFilters, RichHistorySettings, SortOrder };
/*
* Add queries to rich history. Save only queries within the retention period, or that are starred.
* Side-effect: store history in local storage
*/
export async function addToRichHistory(
datasourceUid: string,
datasourceName: string | null,
queries: DataQuery[],
starred: boolean,
comment: string | null,
showQuotaExceededError: boolean,
showLimitExceededWarning: boolean
): Promise<{ richHistoryStorageFull?: boolean; limitExceeded?: boolean }> {
/* Save only queries, that are not falsy (e.g. empty object, null, ...) */
const newQueriesToSave: DataQuery[] = queries && queries.filter((query) => notEmptyQuery(query));
if (newQueriesToSave.length > 0) {
let richHistoryStorageFull = false;
let limitExceeded = false;
let warning: RichHistoryStorageWarningDetails | undefined;
try {
const result = await getRichHistoryStorage().addToRichHistory({
datasourceUid: datasourceUid,
datasourceName: datasourceName ?? '',
queries: newQueriesToSave,
starred,
comment: comment ?? '',
});
warning = result.warning;
} catch (error) {
if (error.name === RichHistoryServiceError.StorageFull) {
richHistoryStorageFull = true;
showQuotaExceededError && dispatch(notifyApp(createErrorNotification(error.message)));
} else if (error.name !== RichHistoryServiceError.DuplicatedEntry) {
dispatch(notifyApp(createErrorNotification('Rich History update failed', error.message)));
}
// Saving failed. Do not add new entry.
return { richHistoryStorageFull, limitExceeded };
}
// Limit exceeded but new entry was added. Notify that old entries have been removed.
if (warning && warning.type === RichHistoryStorageWarning.LimitExceeded) {
limitExceeded = true;
showLimitExceededWarning && dispatch(notifyApp(createWarningNotification(warning.message)));
}
return { richHistoryStorageFull, limitExceeded };
}
// Nothing to change
return {};
}
Query History: Move filtering to service layer (#48008) * Load Rich History when the container is opened * Store rich history for each pane separately * Do not update currently opened query history when an item is added It's impossible to figure out if the item should be added or not, because filters are applied in the backend. We don't want to replicate that filtering logic in frontend. One way to make it work could be by refreshing both panes. * Test starring and deleting query history items when both panes are open * Remove e2e dependency on ExploreId * Fix unit test * Assert exact queries * Simplify test * Fix e2e tests * Fix toolbar a11y * Reload the history after an item is added * Fix unit test * Remove references to Explore from generic PageToolbar component * Update test name * Fix test assertion * Add issue item to TODO * Improve test assertion * Simplify test setup * Move query history settings to persistence layer * Fix test import * Fix unit test * Fix unit test * Test local storage settings API * Code formatting * Fix linting errors * Add an integration test * Add missing aria role * Fix a11y issues * Fix a11y issues * Use divs instead of ul/li Otherwis,e pa11y-ci reports the error below claiming there are no children with role=tab: Certain ARIA roles must contain particular children (https://dequeuniversity.com/rules/axe/4.3/aria-required-children?application=axeAPI) (#reactRoot > div > main > div:nth-child(3) > div > div:nth-child(1) > div > div:nth-child(1) > div > div > nav > div:nth-child(2) > ul) <ul class="css-af3vye" role="tablist"><li class="css-1ciwanz"><a href...</ul> * Clean up settings tab * Remove redundant aria label * Remove redundant container * Clean up test assertions * Move filtering to persistence layer * Move filtering to persistence layer * Simplify applying filters * Split applying filters and reloading the history * Debounce updating filters * Update tests * Fix waiting for debounced results * Clear results when switching tabs * Improve test coverage * Update docs * Revert extra handling for uid (will be added when we introduce remote storage) * Fix betterer conflicts * Fix imports * Fix imports * Simplify test setup * Simplify assertion * Improve readability * Remove unnecessary casting * Mock backend in integration tests
3 years ago
export async function getRichHistory(filters: RichHistorySearchFilters): Promise<RichHistoryQuery[]> {
return await getRichHistoryStorage().getRichHistory(filters);
}
Query History: Move local storage specific settings to persistence layer (#47500) * Load Rich History when the container is opened * Store rich history for each pane separately * Do not update currently opened query history when an item is added It's impossible to figure out if the item should be added or not, because filters are applied in the backend. We don't want to replicate that filtering logic in frontend. One way to make it work could be by refreshing both panes. * Test starring and deleting query history items when both panes are open * Remove e2e dependency on ExploreId * Fix unit test * Assert exact queries * Simplify test * Fix e2e tests * Fix toolbar a11y * Reload the history after an item is added * Fix unit test * Remove references to Explore from generic PageToolbar component * Update test name * Fix test assertion * Add issue item to TODO * Improve test assertion * Simplify test setup * Move query history settings to persistence layer * Fix test import * Fix unit test * Fix unit test * Test local storage settings API * Code formatting * Fix linting errors * Add an integration test * Add missing aria role * Fix a11y issues * Fix a11y issues * Use divs instead of ul/li Otherwis,e pa11y-ci reports the error below claiming there are no children with role=tab: Certain ARIA roles must contain particular children (https://dequeuniversity.com/rules/axe/4.3/aria-required-children?application=axeAPI) (#reactRoot > div > main > div:nth-child(3) > div > div:nth-child(1) > div > div:nth-child(1) > div > div > nav > div:nth-child(2) > ul) <ul class="css-af3vye" role="tablist"><li class="css-1ciwanz"><a href...</ul> * Clean up settings tab * Remove redundant aria label * Remove redundant container * Clean up test assertions
3 years ago
export async function updateRichHistorySettings(settings: RichHistorySettings): Promise<void> {
await getRichHistoryStorage().updateSettings(settings);
}
export async function getRichHistorySettings(): Promise<RichHistorySettings> {
return await getRichHistoryStorage().getSettings();
}
export async function deleteAllFromRichHistory(): Promise<void> {
return getRichHistoryStorage().deleteAll();
}
export async function updateStarredInRichHistory(id: string, starred: boolean) {
try {
return await getRichHistoryStorage().updateStarred(id, starred);
} catch (error) {
dispatch(notifyApp(createErrorNotification('Saving rich history failed', error.message)));
return undefined;
}
}
export async function updateCommentInRichHistory(id: string, newComment: string | undefined) {
try {
return await getRichHistoryStorage().updateComment(id, newComment);
} catch (error) {
dispatch(notifyApp(createErrorNotification('Saving rich history failed', error.message)));
return undefined;
}
}
export async function deleteQueryInRichHistory(id: string) {
try {
await getRichHistoryStorage().deleteRichHistory(id);
return id;
} catch (error) {
dispatch(notifyApp(createErrorNotification('Saving rich history failed', error.message)));
return undefined;
}
}
export const createUrlFromRichHistory = (query: RichHistoryQuery) => {
const exploreState: ExploreUrlState = {
/* Default range, as we are not saving timerange in rich history */
range: { from: 'now-1h', to: 'now' },
datasource: query.datasourceName,
queries: query.queries,
context: 'explore',
};
const serializedState = serializeStateToUrlParam(exploreState);
const baseUrl = /.*(?=\/explore)/.exec(`${window.location.href}`)![0];
const url = urlUtil.renderUrl(`${baseUrl}/explore`, { left: serializedState });
return url;
};
/* Needed for slider in Rich history to map numerical values to meaningful strings */
export const mapNumbertoTimeInSlider = (num: number) => {
let str;
switch (num) {
case 0:
str = 'today';
break;
case 1:
str = 'yesterday';
break;
case 7:
str = 'a week ago';
break;
case 14:
str = 'two weeks ago';
break;
default:
str = `${num} days ago`;
}
return str;
};
export function createDateStringFromTs(ts: number) {
DateTime: adding support to select preferred timezone for presentation of date and time values. (#23586) * added moment timezone package. * added a qnd way of selecting timezone. * added a first draft to display how it can be used. * fixed failing tests. * made moment.local to be in utc when running tests. * added tests to verify that the timeZone support works as expected. * Fixed so we use the formatter in the graph context menu. * changed so we will format d3 according to timeZone. * changed from class base to function based for easier consumption. * fixed so tests got green. * renamed to make it shorter. * fixed formatting in logRow. * removed unused value. * added time formatter to flot. * fixed failing tests. * changed so history will use the formatting with support for timezone. * added todo. * added so we append the correct abbrivation behind time. * added time zone abbrevation in timepicker. * adding timezone in rangeutil tool. * will use timezone when formatting range. * changed so we use new functions to format date so timezone is respected. * wip - dashboard settings. * changed so the time picker settings is in react. * added force update. * wip to get the react graph to work. * fixed formatting and parsing on the timepicker. * updated snap to be correct. * fixed so we format values properly in time picker. * make sure we pass timezone on all the proper places. * fixed so we use correct timeZone in explore. * fixed failing tests. * fixed so we always parse from local to selected timezone. * removed unused variable. * reverted back. * trying to fix issue with directive. * fixed issue. * fixed strict null errors. * fixed so we still can select default. * make sure we reads the time zone from getTimezone
5 years ago
return dateTimeFormat(ts, {
format: 'MMMM D',
});
}
export function getQueryDisplayText(query: DataQuery): string {
/* If datasource doesn't have getQueryDisplayText, create query display text by
* stringifying query that was stripped of key, refId and datasource for nicer
* formatting and improved readability
*/
const strippedQuery = omit(query, ['key', 'refId', 'datasource']);
return JSON.stringify(strippedQuery);
}
export function createQueryHeading(query: RichHistoryQuery, sortOrder: SortOrder) {
let heading = '';
if (sortOrder === SortOrder.DatasourceAZ || sortOrder === SortOrder.DatasourceZA) {
heading = query.datasourceName;
} else {
heading = createDateStringFromTs(query.createdAt);
}
return heading;
}
export function createQueryText(query: DataQuery, queryDsInstance: DataSourceApi | undefined) {
/* query DatasourceInstance is necessary because we use its getQueryDisplayText method
* to format query text
*/
if (queryDsInstance?.getQueryDisplayText) {
return queryDsInstance.getQueryDisplayText(query);
}
return getQueryDisplayText(query);
}
export function mapQueriesToHeadings(query: RichHistoryQuery[], sortOrder: SortOrder) {
let mappedQueriesToHeadings: any = {};
query.forEach((q) => {
let heading = createQueryHeading(q, sortOrder);
if (!(heading in mappedQueriesToHeadings)) {
mappedQueriesToHeadings[heading] = [q];
} else {
mappedQueriesToHeadings[heading] = [...mappedQueriesToHeadings[heading], q];
}
});
return mappedQueriesToHeadings;
}
Query History: Move filtering to service layer (#48008) * Load Rich History when the container is opened * Store rich history for each pane separately * Do not update currently opened query history when an item is added It's impossible to figure out if the item should be added or not, because filters are applied in the backend. We don't want to replicate that filtering logic in frontend. One way to make it work could be by refreshing both panes. * Test starring and deleting query history items when both panes are open * Remove e2e dependency on ExploreId * Fix unit test * Assert exact queries * Simplify test * Fix e2e tests * Fix toolbar a11y * Reload the history after an item is added * Fix unit test * Remove references to Explore from generic PageToolbar component * Update test name * Fix test assertion * Add issue item to TODO * Improve test assertion * Simplify test setup * Move query history settings to persistence layer * Fix test import * Fix unit test * Fix unit test * Test local storage settings API * Code formatting * Fix linting errors * Add an integration test * Add missing aria role * Fix a11y issues * Fix a11y issues * Use divs instead of ul/li Otherwis,e pa11y-ci reports the error below claiming there are no children with role=tab: Certain ARIA roles must contain particular children (https://dequeuniversity.com/rules/axe/4.3/aria-required-children?application=axeAPI) (#reactRoot > div > main > div:nth-child(3) > div > div:nth-child(1) > div > div:nth-child(1) > div > div > nav > div:nth-child(2) > ul) <ul class="css-af3vye" role="tablist"><li class="css-1ciwanz"><a href...</ul> * Clean up settings tab * Remove redundant aria label * Remove redundant container * Clean up test assertions * Move filtering to persistence layer * Move filtering to persistence layer * Simplify applying filters * Split applying filters and reloading the history * Debounce updating filters * Update tests * Fix waiting for debounced results * Clear results when switching tabs * Improve test coverage * Update docs * Revert extra handling for uid (will be added when we introduce remote storage) * Fix betterer conflicts * Fix imports * Fix imports * Simplify test setup * Simplify assertion * Improve readability * Remove unnecessary casting * Mock backend in integration tests
3 years ago
/*
* Create a list of all available data sources
*/
Query History: Move filtering to service layer (#48008) * Load Rich History when the container is opened * Store rich history for each pane separately * Do not update currently opened query history when an item is added It's impossible to figure out if the item should be added or not, because filters are applied in the backend. We don't want to replicate that filtering logic in frontend. One way to make it work could be by refreshing both panes. * Test starring and deleting query history items when both panes are open * Remove e2e dependency on ExploreId * Fix unit test * Assert exact queries * Simplify test * Fix e2e tests * Fix toolbar a11y * Reload the history after an item is added * Fix unit test * Remove references to Explore from generic PageToolbar component * Update test name * Fix test assertion * Add issue item to TODO * Improve test assertion * Simplify test setup * Move query history settings to persistence layer * Fix test import * Fix unit test * Fix unit test * Test local storage settings API * Code formatting * Fix linting errors * Add an integration test * Add missing aria role * Fix a11y issues * Fix a11y issues * Use divs instead of ul/li Otherwis,e pa11y-ci reports the error below claiming there are no children with role=tab: Certain ARIA roles must contain particular children (https://dequeuniversity.com/rules/axe/4.3/aria-required-children?application=axeAPI) (#reactRoot > div > main > div:nth-child(3) > div > div:nth-child(1) > div > div:nth-child(1) > div > div > nav > div:nth-child(2) > ul) <ul class="css-af3vye" role="tablist"><li class="css-1ciwanz"><a href...</ul> * Clean up settings tab * Remove redundant aria label * Remove redundant container * Clean up test assertions * Move filtering to persistence layer * Move filtering to persistence layer * Simplify applying filters * Split applying filters and reloading the history * Debounce updating filters * Update tests * Fix waiting for debounced results * Clear results when switching tabs * Improve test coverage * Update docs * Revert extra handling for uid (will be added when we introduce remote storage) * Fix betterer conflicts * Fix imports * Fix imports * Simplify test setup * Simplify assertion * Improve readability * Remove unnecessary casting * Mock backend in integration tests
3 years ago
export function createDatasourcesList() {
return getDataSourceSrv()
.getList()
.map((dsSettings) => {
return {
name: dsSettings.name,
uid: dsSettings.uid,
imgUrl: dsSettings.meta.info.logos.small,
Query History: Move filtering to service layer (#48008) * Load Rich History when the container is opened * Store rich history for each pane separately * Do not update currently opened query history when an item is added It's impossible to figure out if the item should be added or not, because filters are applied in the backend. We don't want to replicate that filtering logic in frontend. One way to make it work could be by refreshing both panes. * Test starring and deleting query history items when both panes are open * Remove e2e dependency on ExploreId * Fix unit test * Assert exact queries * Simplify test * Fix e2e tests * Fix toolbar a11y * Reload the history after an item is added * Fix unit test * Remove references to Explore from generic PageToolbar component * Update test name * Fix test assertion * Add issue item to TODO * Improve test assertion * Simplify test setup * Move query history settings to persistence layer * Fix test import * Fix unit test * Fix unit test * Test local storage settings API * Code formatting * Fix linting errors * Add an integration test * Add missing aria role * Fix a11y issues * Fix a11y issues * Use divs instead of ul/li Otherwis,e pa11y-ci reports the error below claiming there are no children with role=tab: Certain ARIA roles must contain particular children (https://dequeuniversity.com/rules/axe/4.3/aria-required-children?application=axeAPI) (#reactRoot > div > main > div:nth-child(3) > div > div:nth-child(1) > div > div:nth-child(1) > div > div > nav > div:nth-child(2) > ul) <ul class="css-af3vye" role="tablist"><li class="css-1ciwanz"><a href...</ul> * Clean up settings tab * Remove redundant aria label * Remove redundant container * Clean up test assertions * Move filtering to persistence layer * Move filtering to persistence layer * Simplify applying filters * Split applying filters and reloading the history * Debounce updating filters * Update tests * Fix waiting for debounced results * Clear results when switching tabs * Improve test coverage * Update docs * Revert extra handling for uid (will be added when we introduce remote storage) * Fix betterer conflicts * Fix imports * Fix imports * Simplify test setup * Simplify assertion * Improve readability * Remove unnecessary casting * Mock backend in integration tests
3 years ago
};
});
}
export function notEmptyQuery(query: DataQuery) {
/* Check if query has any other properties besides key, refId and datasource.
* If not, then we consider it empty query.
*/
const strippedQuery = omit(query, ['key', 'refId', 'datasource']);
const queryKeys = Object.keys(strippedQuery);
if (queryKeys.length > 0) {
return true;
}
return false;
}