Move text, url and location utils to grafana-data

This commit is contained in:
Dominik Prokop
2020-04-15 14:31:13 +02:00
parent 3acee36eb2
commit f806b44966
40 changed files with 238 additions and 140 deletions
@@ -22,6 +22,7 @@ import { getDisplayProcessor } from './displayProcessor';
import { guessFieldTypeForField } from '../dataframe';
import { standardFieldConfigEditorRegistry } from './standardFieldConfigEditorRegistry';
import { FieldConfigOptionsRegistry } from './FieldConfigOptionsRegistry';
import { DataLinkBuiltInVars, locationUtil } from '../utils';
interface OverrideProps {
match: FieldMatcher;
+8
View File
@@ -1,3 +1,11 @@
export * from './string';
export * from './markdown';
export * from './text';
import { escapeHtml, hasAnsiCodes, sanitize, sanitizeUrl } from './sanitize';
export const textUtil = {
escapeHtml,
hasAnsiCodes,
sanitize,
sanitizeUrl,
};
+3
View File
@@ -12,3 +12,6 @@ export { PanelOptionsEditorBuilder, FieldConfigEditorBuilder } from './OptionsUI
export { getMappedValue } from './valueMappings';
export { getFlotPairs, getFlotPairsConstant } from './flotPairs';
export { locationUtil } from './location';
export { urlUtil } from './url';
export { DataLinkBuiltInVars } from './dataLinks';
@@ -0,0 +1,21 @@
import { locationUtil } from './location';
describe('locationUtil', () => {
beforeAll(() => {
locationUtil.initialise({
getConfig: () => {
return { appSubUrl: '/subUrl' } as any;
},
// @ts-ignore
buildParamsFromVariables: () => {},
// @ts-ignore
getTimeRangeForUrl: () => {},
});
});
describe('With /subUrl as appSubUrl', () => {
it('/subUrl should be stripped', () => {
const urlWithoutMaster = locationUtil.stripBaseFromUrl('/subUrl/grafana/');
expect(urlWithoutMaster).toBe('/grafana/');
});
});
});
@@ -0,0 +1,75 @@
import { GrafanaConfig, RawTimeRange, ScopedVars } from '../types';
import { urlUtil } from './url';
import { textUtil } from '../text';
let grafanaConfig: () => GrafanaConfig;
let getTimeRangeUrlParams: () => RawTimeRange;
let getVariablesUrlParams: (params?: Record<string, any>, scopedVars?: ScopedVars) => string;
/**
*
* @param url
* @internal
*/
const stripBaseFromUrl = (url: string): string => {
const appSubUrl = grafanaConfig ? grafanaConfig().appSubUrl : '';
const stripExtraChars = appSubUrl.endsWith('/') ? 1 : 0;
const urlWithoutBase =
url.length > 0 && url.indexOf(appSubUrl) === 0 ? url.slice(appSubUrl.length - stripExtraChars) : url;
return urlWithoutBase;
};
/**
*
* @param url
* @internal
*/
const assureBaseUrl = (url: string) => {
if (url.startsWith('/')) {
return `${grafanaConfig ? grafanaConfig().appSubUrl : ''}${stripBaseFromUrl(url)}`;
}
return url;
};
export const locationUtil = {
/**
*
* @param getConfig
* @param buildParamsFromVariables
* @param getTimeRangeForUrl
* @internal
*/
initialise: ({
getConfig,
buildParamsFromVariables,
getTimeRangeForUrl,
}: {
getConfig: () => GrafanaConfig;
getTimeRangeForUrl: () => RawTimeRange;
buildParamsFromVariables: (params: any, scopedVars?: ScopedVars) => string;
}) => {
grafanaConfig = getConfig;
getTimeRangeUrlParams = getTimeRangeForUrl;
getVariablesUrlParams = buildParamsFromVariables;
},
stripBaseFromUrl,
assureBaseUrl,
getTimeRangeUrlParams: () => {
if (!getTimeRangeUrlParams) {
return null;
}
return urlUtil.toUrlParams(getTimeRangeUrlParams());
},
getVariablesUrlParams: (scopedVars?: ScopedVars) => {
if (!getVariablesUrlParams) {
return null;
}
const params = {};
getVariablesUrlParams(params, scopedVars);
return urlUtil.toUrlParams(params);
},
processUrl: (url: string) => {
return grafanaConfig().disableSanitizeHtml ? url : textUtil.sanitizeUrl(url);
},
};
@@ -1,8 +1,8 @@
import { toUrlParams } from '../utils/url';
import { urlUtil } from './url';
describe('toUrlParams', () => {
it('should encode object properties as url parameters', () => {
const url = toUrlParams({
const url = urlUtil.toUrlParams({
server: 'backend-01',
hasSpace: 'has space',
many: ['1', '2', '3'],
@@ -17,7 +17,7 @@ describe('toUrlParams', () => {
describe('toUrlParams', () => {
it('should encode the same way as angularjs', () => {
const url = toUrlParams({
const url = urlUtil.toUrlParams({
server: ':@',
});
expect(url).toBe('server=:@');
@@ -4,14 +4,14 @@
import { UrlQueryMap } from '@grafana/runtime';
export function renderUrl(path: string, query: UrlQueryMap | undefined): string {
function renderUrl(path: string, query: UrlQueryMap | undefined): string {
if (query && Object.keys(query).length > 0) {
path += '?' + toUrlParams(query);
}
return path;
}
export function encodeURIComponentAsAngularJS(val: string, pctEncodeSpaces?: boolean) {
function encodeURIComponentAsAngularJS(val: string, pctEncodeSpaces?: boolean) {
return encodeURIComponent(val)
.replace(/%40/gi, '@')
.replace(/%3A/gi, ':')
@@ -21,7 +21,7 @@ export function encodeURIComponentAsAngularJS(val: string, pctEncodeSpaces?: boo
.replace(/%20/g, pctEncodeSpaces ? '%20' : '+');
}
export function toUrlParams(a: any) {
function toUrlParams(a: any) {
const s: any[] = [];
const rbracket = /\[\]$/;
@@ -72,7 +72,7 @@ export function toUrlParams(a: any) {
return buildParams('', a).join('&');
}
export function appendQueryToUrl(url: string, stringToAppend: string) {
function appendQueryToUrl(url: string, stringToAppend: string) {
if (stringToAppend !== undefined && stringToAppend !== null && stringToAppend !== '') {
const pos = url.indexOf('?');
if (pos !== -1) {
@@ -91,7 +91,7 @@ export function appendQueryToUrl(url: string, stringToAppend: string) {
/**
* Return search part (as object) of current url
*/
export function getUrlSearchParams() {
function getUrlSearchParams() {
const search = window.location.search.substring(1);
const searchParamsSegments = search.split('&');
const params: any = {};
@@ -110,3 +110,10 @@ export function getUrlSearchParams() {
}
return params;
}
export const urlUtil = {
renderUrl,
toUrlParams,
appendQueryToUrl,
getUrlSearchParams,
};
@@ -1,7 +1,7 @@
import React, { useState, useMemo, useContext, useRef, RefObject, memo, useEffect } from 'react';
import usePrevious from 'react-use/lib/usePrevious';
import { DataLinkSuggestions } from './DataLinkSuggestions';
import { ThemeContext, DataLinkBuiltInVars, makeValue } from '../../index';
import { ThemeContext, makeValue } from '../../index';
import { SelectionReference } from './SelectionReference';
import { Portal } from '../index';
@@ -14,7 +14,7 @@ import { css } from 'emotion';
import { SlatePrism } from '../../slate-plugins';
import { SCHEMA } from '../../utils/slate';
import { stylesFactory } from '../../themes';
import { GrafanaTheme, VariableSuggestion, VariableOrigin } from '@grafana/data';
import { GrafanaTheme, VariableSuggestion, VariableOrigin, DataLinkBuiltInVars } from '@grafana/data';
const modulo = (a: number, n: number) => a - n * Math.floor(a / n);
+3 -2
View File
@@ -25,9 +25,10 @@ import {
getFlotPairs,
TimeZone,
getDisplayProcessor,
textUtil,
} from '@grafana/data';
import { getThemeColor } from 'app/core/utils/colors';
import { hasAnsiCodes } from 'app/core/utils/text';
import { sortInAscendingOrder, deduplicateLogRowsById } from 'app/core/utils/explore';
import { getGraphSeriesModel } from 'app/plugins/panel/graph2/getGraphSeriesModel';
@@ -313,7 +314,7 @@ export function logSeriesToLogsModel(logSeries: DataFrame[]): LogsModel | undefi
// This should be string but sometimes isn't (eg elastic) because the dataFrame is not strongly typed.
const message: string = typeof messageValue === 'string' ? messageValue : JSON.stringify(messageValue);
const hasAnsi = hasAnsiCodes(message);
const hasAnsi = textUtil.hasAnsiCodes(message);
const searchWords = series.meta && series.meta.searchWords ? series.meta.searchWords : [];
let logLevel = LogLevel.unknown;
+2 -2
View File
@@ -3,7 +3,7 @@ import { Action, createAction } from '@reduxjs/toolkit';
import { LocationUpdate } from '@grafana/runtime';
import { LocationState } from 'app/types';
import { renderUrl } from 'app/core/utils/url';
import { urlUtil } from '@grafana/data';
export const initialState: LocationState = {
url: '',
@@ -33,7 +33,7 @@ export const locationReducer = (state: LocationState = initialState, action: Act
}
return {
url: renderUrl(path || state.path, query),
url: urlUtil.renderUrl(path || state.path, query),
path: path || state.path,
query: { ...query },
routeParams: routeParams || state.routeParams,
+1 -1
View File
@@ -1,7 +1,6 @@
import coreModule from 'app/core/core_module';
import appEvents from 'app/core/app_events';
import { store } from 'app/store/store';
import locationUtil from 'app/core/utils/location_util';
import { updateLocation } from 'app/core/actions';
import { ITimeoutService, ILocationService, IWindowService } from 'angular';
import { CoreEvents } from 'app/types';
@@ -9,6 +8,7 @@ import { GrafanaRootScope } from 'app/routes/GrafanaCtrl';
import { UrlQueryMap } from '@grafana/runtime';
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
import { VariableSrv } from 'app/features/templating/all';
import { locationUtil } from '@grafana/data';
// Services that handles angular -> redux store sync & other react <-> angular sync
export class BridgeSrv {
+1 -1
View File
@@ -3,7 +3,6 @@ import _ from 'lodash';
import coreModule from 'app/core/core_module';
import appEvents from 'app/core/app_events';
import { getExploreUrl } from 'app/core/utils/explore';
import locationUtil from 'app/core/utils/location_util';
import { store } from 'app/store/store';
import { AppEventEmitter, CoreEvents } from 'app/types';
@@ -16,6 +15,7 @@ import { getLocationSrv } from '@grafana/runtime';
import { DashboardModel } from '../../features/dashboard/state';
import { ShareModal } from 'app/features/dashboard/components/ShareModal';
import { SaveDashboardModalProxy } from '../../features/dashboard/components/SaveDashboard/SaveDashboardModalProxy';
import { locationUtil } from '@grafana/data';
export class KeybindingSrv {
helpModal: boolean;
@@ -1,16 +0,0 @@
import locationUtil from 'app/core/utils/location_util';
jest.mock('app/core/config', () => {
return {
getConfig: () => ({ appSubUrl: '/subUrl' }),
};
});
describe('locationUtil', () => {
describe('With /subUrl as appSubUrl', () => {
it('/subUrl should be stripped', () => {
const urlWithoutMaster = locationUtil.stripBaseFromUrl('/subUrl/grafana/');
expect(urlWithoutMaster).toBe('/grafana/');
});
});
});
+2 -2
View File
@@ -21,8 +21,8 @@ import {
TimeZone,
toUtc,
ExploreMode,
urlUtil,
} from '@grafana/data';
import { renderUrl } from 'app/core/utils/url';
import store from 'app/core/store';
import kbn from 'app/core/utils/kbn';
import { getNextRefIdChar } from './query';
@@ -106,7 +106,7 @@ export async function getExploreUrl(args: GetExploreUrlArguments): Promise<strin
}
const exploreState = JSON.stringify({ ...state, originPanelId: panel.getSavedId() });
url = renderUrl('/explore', { left: exploreState });
url = urlUtil.renderUrl('/explore', { left: exploreState });
}
return url;
}
-19
View File
@@ -1,19 +0,0 @@
import { getConfig } from 'app/core/config';
export const stripBaseFromUrl = (url: string): string => {
const appSubUrl = getConfig().appSubUrl;
const stripExtraChars = appSubUrl.endsWith('/') ? 1 : 0;
const urlWithoutBase =
url.length > 0 && url.indexOf(appSubUrl) === 0 ? url.slice(appSubUrl.length - stripExtraChars) : url;
return urlWithoutBase;
};
export const assureBaseUrl = (url: string) => {
if (url.startsWith('/')) {
return `${getConfig().appSubUrl}${stripBaseFromUrl(url)}`;
}
return url;
};
export default { stripBaseFromUrl, assureBaseUrl };
+2 -3
View File
@@ -2,8 +2,7 @@
import _ from 'lodash';
// Services & Utils
import { DataQuery, ExploreMode, dateTime } from '@grafana/data';
import { renderUrl } from 'app/core/utils/url';
import { DataQuery, ExploreMode, dateTime, urlUtil } from '@grafana/data';
import store from 'app/core/store';
import { serializeStateToUrlParam, SortOrder } from './explore';
import { getExploreDatasources } from '../../features/explore/state/selectors';
@@ -172,7 +171,7 @@ export const createUrlFromRichHistory = (query: RichHistoryQuery) => {
const serializedState = serializeStateToUrlParam(exploreState, true);
const baseUrl = /.*(?=\/explore)/.exec(`${window.location.href}`)[0];
const url = renderUrl(`${baseUrl}/explore`, { left: serializedState });
const url = urlUtil.renderUrl(`${baseUrl}/explore`, { left: serializedState });
return url;
};
@@ -1,5 +1,5 @@
// Libaries
import React, { PureComponent, FC } from 'react';
import React, { FC, PureComponent } from 'react';
import { connect } from 'react-redux';
import { css } from 'emotion';
// Utils & Services
@@ -8,7 +8,8 @@ import { PlaylistSrv } from 'app/features/playlist/playlist_srv';
// Components
import { DashNavButton } from './DashNavButton';
import { DashNavTimeControls } from './DashNavTimeControls';
import { ModalsController, Icon } from '@grafana/ui';
import { Icon, ModalsController } from '@grafana/ui';
import { textUtil } from '@grafana/data';
import { BackButton } from 'app/core/components/BackButton/BackButton';
// State
import { updateLocation } from 'app/core/actions';
@@ -17,7 +18,6 @@ import { DashboardModel } from '../../state';
import { CoreEvents, StoreState } from 'app/types';
import { ShareModal } from 'app/features/dashboard/components/ShareModal';
import { SaveDashboardModalProxy } from 'app/features/dashboard/components/SaveDashboard/SaveDashboardModalProxy';
import { sanitizeUrl } from 'app/core/utils/text';
export interface OwnProps {
dashboard: DashboardModel;
@@ -244,8 +244,8 @@ class DashNav extends PureComponent<Props> {
<DashNavButton
tooltip="Open original dashboard"
classSuffix="snapshot-origin"
href={textUtil.sanitizeUrl(snapshotUrl)}
icon="link"
href={sanitizeUrl(snapshotUrl)}
/>
)}
@@ -10,9 +10,8 @@ import { backendSrv } from 'app/core/services/backend_srv';
import { DashboardSrv } from '../../services/DashboardSrv';
import { CoreEvents } from 'app/types';
import { GrafanaRootScope } from 'app/routes/GrafanaCtrl';
import { AppEvents } from '@grafana/data';
import { AppEvents, locationUtil } from '@grafana/data';
import { promiseToDigest } from '../../../../core/utils/promiseToDigest';
import locationUtil from 'app/core/utils/location_util';
export class SettingsCtrl {
dashboard: DashboardModel;
@@ -1,11 +1,10 @@
import { useEffect } from 'react';
import useAsyncFn from 'react-use/lib/useAsyncFn';
import { AppEvents } from '@grafana/data';
import { AppEvents, locationUtil } from '@grafana/data';
import { useDispatch, useSelector } from 'react-redux';
import { SaveDashboardOptions } from './types';
import { CoreEvents, StoreState } from 'app/types';
import appEvents from 'app/core/app_events';
import locationUtil from 'app/core/utils/location_util';
import { updateLocation } from 'app/core/reducers/location';
import { DashboardModel } from 'app/features/dashboard/state';
import { getBackendSrv } from 'app/core/services/backend_srv';
@@ -1,8 +1,7 @@
import { config } from '@grafana/runtime';
import { appendQueryToUrl, toUrlParams, getUrlSearchParams } from 'app/core/utils/url';
import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv';
import templateSrv from 'app/features/templating/template_srv';
import { PanelModel, dateTime } from '@grafana/data';
import { PanelModel, dateTime, urlUtil } from '@grafana/data';
export function buildParams(
useCurrentTimeRange: boolean,
@@ -10,7 +9,7 @@ export function buildParams(
selectedTheme?: string,
panel?: PanelModel
) {
const params = getUrlSearchParams();
const params = urlUtil.getUrlSearchParams();
const range = getTimeSrv().timeRange();
params.from = range.from.valueOf();
@@ -61,7 +60,7 @@ export function buildShareUrl(
const baseUrl = buildBaseUrl();
const params = buildParams(useCurrentTimeRange, includeTemplateVars, selectedTheme, panel);
return appendQueryToUrl(baseUrl, toUrlParams(params));
return urlUtil.appendQueryToUrl(baseUrl, urlUtil.toUrlParams(params));
}
export function buildSoloUrl(
@@ -77,7 +76,7 @@ export function buildSoloUrl(
soloUrl = soloUrl.replace(config.appSubUrl + '/d/', config.appSubUrl + '/d-solo/');
delete params.fullscreen;
delete params.edit;
return appendQueryToUrl(soloUrl, toUrlParams(params));
return urlUtil.appendQueryToUrl(soloUrl, urlUtil.toUrlParams(params));
}
export function buildImageUrl(
@@ -1,10 +1,9 @@
import _ from 'lodash';
import angular, { ILocationService, IScope } from 'angular';
import locationUtil from 'app/core/utils/location_util';
import { DashboardModel } from '../../state/DashboardModel';
import { CalculateDiffOptions, HistoryListOpts, HistorySrv, RevisionsModel } from './HistorySrv';
import { AppEvents, dateTime, DateTimeInput, toUtc } from '@grafana/data';
import { AppEvents, dateTime, DateTimeInput, locationUtil, toUtc } from '@grafana/data';
import { GrafanaRootScope } from 'app/routes/GrafanaCtrl';
import { CoreEvents } from 'app/types';
import { promiseToDigest } from '../../../../core/utils/promiseToDigest';
@@ -3,7 +3,7 @@ import { DashboardModel } from '../state/DashboardModel';
import { PanelModel } from '../state/PanelModel';
import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN } from 'app/core/constants';
import { expect } from 'test/lib/common';
import { DataLinkBuiltInVars } from '@grafana/ui';
import { DataLinkBuiltInVars } from '@grafana/data';
jest.mock('app/core/services/context_srv', () => ({}));
@@ -2,12 +2,11 @@
import _ from 'lodash';
// Utils
import getFactors from 'app/core/utils/factors';
import { appendQueryToUrl } from 'app/core/utils/url';
import kbn from 'app/core/utils/kbn';
// Types
import { PanelModel } from './PanelModel';
import { DashboardModel } from './DashboardModel';
import { DataLink } from '@grafana/data';
import { DataLink, urlUtil, DataLinkBuiltInVars } from '@grafana/data';
// Constants
import {
DEFAULT_PANEL_SPAN,
@@ -17,7 +16,6 @@ import {
GRID_COLUMN_COUNT,
MIN_PANEL_HEIGHT,
} from 'app/core/constants';
import { DataLinkBuiltInVars } from '@grafana/ui';
import { isMulti } from 'app/features/variables/guard';
import { alignCurrentWithMulti } from 'app/features/variables/shared/multiOptions';
@@ -737,15 +735,15 @@ function upgradePanelLink(link: any): DataLink {
}
if (link.keepTime) {
url = appendQueryToUrl(url, `$${DataLinkBuiltInVars.keepTime}`);
url = urlUtil.appendQueryToUrl(url, `$${DataLinkBuiltInVars.keepTime}`);
}
if (link.includeVars) {
url = appendQueryToUrl(url, `$${DataLinkBuiltInVars.includeVars}`);
url = urlUtil.appendQueryToUrl(url, `$${DataLinkBuiltInVars.includeVars}`);
}
if (link.params) {
url = appendQueryToUrl(url, link.params);
url = urlUtil.appendQueryToUrl(url, link.params);
}
return {
@@ -9,7 +9,6 @@ import { VariableSrv } from 'app/features/templating/variable_srv';
import { KeybindingSrv } from 'app/core/services/keybindingSrv';
// Actions
import { notifyApp, updateLocation } from 'app/core/actions';
import locationUtil from 'app/core/utils/location_util';
import {
clearDashboardQueriesToUpdateOnLoad,
dashboardInitCompleted,
@@ -21,7 +20,7 @@ import {
// Types
import { DashboardDTO, DashboardRouteInfo, StoreState, ThunkDispatch, ThunkResult } from 'app/types';
import { DashboardModel } from './DashboardModel';
import { DataQuery } from '@grafana/data';
import { DataQuery, locationUtil } from '@grafana/data';
import { getConfig } from '../../../core/config';
import { initDashboardTemplating, processVariables, completeDashboardTemplating } from '../../variables/state/actions';
import { emitDashboardViewEvent } from './analyticsProcessor';
@@ -1,9 +1,9 @@
import { ILocationService, IScope } from 'angular';
import { FolderPageLoader } from './services/FolderPageLoader';
import locationUtil from 'app/core/utils/location_util';
import { NavModelSrv } from 'app/core/core';
import { promiseToDigest } from '../../core/utils/promiseToDigest';
import { locationUtil } from '@grafana/data';
export default class FolderDashboardsCtrl {
navModel: any;
@@ -1,4 +1,4 @@
import { AppEvents, DataSourceInstanceSettings, DataSourceSelectItem } from '@grafana/data';
import { AppEvents, DataSourceInstanceSettings, DataSourceSelectItem, locationUtil } from '@grafana/data';
import { getBackendSrv } from '@grafana/runtime';
import config from 'app/core/config';
import {
@@ -9,7 +9,6 @@ import {
InputType,
ImportDashboardDTO,
} from './reducers';
import locationUtil from 'app/core/utils/location_util';
import { updateLocation } from 'app/core/actions';
import { ThunkResult } from 'app/types';
import { appEvents } from '../../../core/core';
+2 -3
View File
@@ -17,8 +17,7 @@ import { updateLocation } from 'app/core/actions';
import { connect } from 'react-redux';
import { hot } from 'react-hot-loader';
import { appEvents } from 'app/core/core';
import { AppEvents } from '@grafana/data';
import { assureBaseUrl } from 'app/core/utils/location_util';
import { AppEvents, locationUtil } from '@grafana/data';
const roles = [
{ label: 'Viewer', value: OrgRole.Viewer },
@@ -77,7 +76,7 @@ export const UserInviteForm: FC<Props> = ({ updateLocation }) => {
</Field>
<HorizontalGroup>
<Button type="submit">Submit</Button>
<LinkButton href={assureBaseUrl(getConfig().appSubUrl + '/org/users')} variant="secondary">
<LinkButton href={locationUtil.assureBaseUrl(getConfig().appSubUrl + '/org/users')} variant="secondary">
Back
</LinkButton>
</HorizontalGroup>
@@ -2,23 +2,23 @@ import _ from 'lodash';
import { TimeSrv } from 'app/features/dashboard/services/TimeSrv';
import templateSrv, { TemplateSrv } from 'app/features/templating/template_srv';
import coreModule from 'app/core/core_module';
import { appendQueryToUrl, toUrlParams } from 'app/core/utils/url';
import { sanitizeUrl } from 'app/core/utils/text';
import { getConfig } from 'app/core/config';
import locationUtil from 'app/core/utils/location_util';
import { DataLinkBuiltInVars } from '@grafana/ui';
import {
DataFrame,
DataLink,
DataLinkBuiltInVars,
deprecationWarning,
Field,
FieldType,
KeyValue,
LinkModel,
locationUtil,
ScopedVars,
VariableOrigin,
VariableSuggestion,
VariableSuggestionsScope,
urlUtil,
textUtil,
} from '@grafana/data';
const timeRangeVars = [
@@ -253,8 +253,8 @@ export class LinkSrv implements LinkService {
this.templateSrv.fillVariableValuesForUrl(params);
}
url = appendQueryToUrl(url, toUrlParams(params));
return getConfig().disableSanitizeHtml ? url : sanitizeUrl(url);
url = urlUtil.appendQueryToUrl(url, urlUtil.toUrlParams(params));
return getConfig().disableSanitizeHtml ? url : textUtil.sanitizeUrl(url);
}
getAnchorInfo(link: any) {
@@ -269,7 +269,7 @@ export class LinkSrv implements LinkService {
*/
getDataLinkUIModel = <T>(link: DataLink, scopedVars: ScopedVars, origin: T): LinkModel<T> => {
const params: KeyValue = {};
const timeRangeUrl = toUrlParams(this.timeSrv.timeRangeForUrl());
const timeRangeUrl = urlUtil.toUrlParams(this.timeSrv.timeRangeForUrl());
let href = link.url;
@@ -302,7 +302,7 @@ export class LinkSrv implements LinkService {
this.templateSrv.fillVariableValuesForUrl(params, scopedVars);
const variablesQuery = toUrlParams(params);
const variablesQuery = urlUtil.toUrlParams(params);
info.href = this.templateSrv.replace(info.href, {
...scopedVars,
@@ -316,7 +316,7 @@ export class LinkSrv implements LinkService {
},
});
info.href = getConfig().disableSanitizeHtml ? info.href : sanitizeUrl(info.href);
info.href = getConfig().disableSanitizeHtml ? info.href : textUtil.sanitizeUrl(info.href);
return info;
};
@@ -1,9 +1,11 @@
import { LinkSrv } from '../link_srv';
import { DataLinkBuiltInVars } from '@grafana/ui';
import { DataLinkBuiltInVars, locationUtil, VariableModel } from '@grafana/data';
import { TimeSrv } from 'app/features/dashboard/services/TimeSrv';
import { TemplateSrv } from 'app/features/templating/template_srv';
import { advanceTo } from 'jest-date-mock';
import { updateConfig } from '../../../../core/config';
import { variableAdapters } from '../../../variables/adapters';
import { createQueryVariableAdapter } from '../../../variables/query/adapter';
jest.mock('app/core/core', () => ({
appEvents: {
@@ -46,29 +48,48 @@ describe('linkSrv', () => {
timeSrv.setTime({ from: 'now-1h', to: 'now' });
_dashboard.refresh = false;
const _templateSrv = new TemplateSrv();
_templateSrv.init([
const variablesMock = [
{
type: 'query',
name: 'test1',
label: 'Test1',
hide: false,
current: { value: 'val1' },
skipUrlSync: false,
getValueForUrl: function() {
return this.current.value;
return 'val1';
},
},
} as VariableModel,
{
type: 'query',
name: 'test2',
label: 'Test2',
hide: false,
current: { value: 'val2' },
skipUrlSync: false,
getValueForUrl: function() {
return this.current.value;
return 'val2';
},
} as VariableModel,
];
const _templateSrv = new TemplateSrv({
// @ts-ignore
getVariables: () => {
return variablesMock;
},
]);
// @ts-ignore
getVariableWithName: (name: string) => {
return variablesMock.filter(v => v.name === name)[0];
},
});
linkSrv = new LinkSrv(_templateSrv, timeSrv);
}
beforeAll(() => {
variableAdapters.register(createQueryVariableAdapter());
});
beforeEach(() => {
initLinkSrv();
advanceTo(1000000000);
@@ -217,8 +238,14 @@ describe('linkSrv', () => {
`(
"when link '$url' and config.appSubUrl set to '$appSubUrl' then result should be '$expected'",
({ url, appSubUrl, expected }) => {
updateConfig({
appSubUrl,
locationUtil.initialise({
getConfig: () => {
return { appSubUrl } as any;
},
// @ts-ignore
buildParamsFromVariables: () => {},
// @ts-ignore
getTimeRangeForUrl: () => {},
});
const link = linkSrv.getDataLinkUIModel(
+3 -3
View File
@@ -2,14 +2,14 @@
import _ from 'lodash';
// Utils
import { toUrlParams } from 'app/core/utils/url';
import coreModule from '../../core/core_module';
import appEvents from 'app/core/app_events';
import locationUtil from 'app/core/utils/location_util';
import kbn from 'app/core/utils/kbn';
import { store } from 'app/store/store';
import { CoreEvents } from 'app/types';
import { getBackendSrv } from '@grafana/runtime';
import { locationUtil, urlUtil } from '@grafana/data';
export const queryParamsToPreserve: { [key: string]: boolean } = {
kiosk: true,
@@ -55,7 +55,7 @@ export class PlaylistSrv {
// this is done inside timeout to make sure digest happens after
// as this can be called from react
this.$timeout(() => {
this.$location.url(nextDashboardUrl + '?' + toUrlParams(filteredParams));
this.$location.url(nextDashboardUrl + '?' + urlUtil.toUrlParams(filteredParams));
});
this.index++;
@@ -1,7 +1,6 @@
import kbn from 'app/core/utils/kbn';
import _ from 'lodash';
import { escapeHtml } from 'app/core/utils/text';
import { deprecationWarning, ScopedVars, TimeRange } from '@grafana/data';
import { deprecationWarning, ScopedVars, textUtil, TimeRange } from '@grafana/data';
import { getFilteredVariables, getVariables, getVariableWithName } from '../variables/state/selectors';
import { getConfig } from 'app/core/config';
import { variableRegex } from './utils';
@@ -194,9 +193,9 @@ export class TemplateSrv implements BaseTemplateSrv {
}
case 'html': {
if (_.isArray(value)) {
return escapeHtml(value.join(', '));
return textUtil.escapeHtml(value.join(', '));
}
return escapeHtml(value);
return textUtil.escapeHtml(value);
}
case 'json': {
return JSON.stringify(value);
@@ -1,7 +1,7 @@
import $ from 'jquery';
import { appEvents } from 'app/core/core';
import { CoreEvents } from 'app/types';
import { sanitize } from 'app/core/utils/text';
import { textUtil } from '@grafana/data';
export default function GraphTooltip(this: any, elem: any, dashboard: any, scope: any, getSeriesFn: any) {
const self = this;
@@ -268,10 +268,10 @@ export default function GraphTooltip(this: any, elem: any, dashboard: any, scope
}
series = seriesList[hoverInfo.index];
value = sanitize(series.formatValue(hoverInfo.value));
value = textUtil.sanitize(series.formatValue(hoverInfo.value));
const color = sanitize(hoverInfo.color);
const label = sanitize(hoverInfo.label);
const color = textUtil.sanitize(hoverInfo.color);
const label = textUtil.sanitize(hoverInfo.label);
seriesHtml +=
'<div class="graph-tooltip-list-item ' + highlightClass + '"><div class="graph-tooltip-series-name">';
@@ -283,7 +283,7 @@ export default function GraphTooltip(this: any, elem: any, dashboard: any, scope
self.renderAndShow(absoluteTime, seriesHtml, pos, xMode);
} else if (item) {
// single series tooltip
const color = sanitize(item.series.color);
const color = textUtil.sanitize(item.series.color);
series = seriesList[item.seriesIndex];
group = '<div class="graph-tooltip-list-item"><div class="graph-tooltip-series-name">';
group += '<i class="fa fa-minus" style="color:' + color + ';"></i> ' + series.aliasEscaped + ':</div>';
@@ -294,7 +294,7 @@ export default function GraphTooltip(this: any, elem: any, dashboard: any, scope
value = item.datapoint[1];
}
value = sanitize(series.formatValue(value));
value = textUtil.sanitize(series.formatValue(value));
absoluteTime = dashboard.formatDate(item.datapoint[0], tooltipFormat);
group += '<div class="graph-tooltip-value">' + value + '</div>';
+5 -6
View File
@@ -1,19 +1,18 @@
// Libraries
import React, { PureComponent } from 'react';
import { css } from 'emotion';
// Utils & Services
import { GrafanaTheme } from '@grafana/data';
import { stylesFactory, CustomScrollbar } from '@grafana/ui';
import { CustomScrollbar, stylesFactory } from '@grafana/ui';
import config from 'app/core/config';
import { feedToDataFrame } from './utils';
import { sanitize } from 'app/core/utils/text';
import { loadRSSFeed } from './rss';
// Types
import { PanelProps, DataFrameView, dateTime } from '@grafana/data';
import { PanelProps, DataFrameView, dateTime, GrafanaTheme, textUtil } from '@grafana/data';
import { NewsOptions, NewsItem } from './types';
import { DEFAULT_FEED_URL, PROXY_PREFIX } from './constants';
import { css } from 'emotion';
interface Props extends PanelProps<NewsOptions> {}
@@ -83,7 +82,7 @@ export class NewsPanel extends PureComponent<Props, State> {
<div className={styles.title}>{item.title}</div>
<div className={styles.date}>{dateTime(item.date).format('MMM DD')} </div>
</a>
<div className={styles.content} dangerouslySetInnerHTML={{ __html: sanitize(item.content) }} />
<div className={styles.content} dangerouslySetInnerHTML={{ __html: textUtil.sanitize(item.content) }} />
</div>
);
})}
@@ -4,7 +4,6 @@ import $ from 'jquery';
import 'vendor/flot/jquery.flot';
import 'vendor/flot/jquery.flot.gauge';
import 'app/features/panel/panellinks/link_srv';
import locationUtil from 'app/core/utils/location_util';
import {
DataFrame,
@@ -23,6 +22,7 @@ import {
getColorFromHexRgbOrName,
PanelEvents,
formattedValueToString,
locationUtil,
} from '@grafana/data';
import { convertOldAngularValueMapping } from '@grafana/ui';
@@ -9,12 +9,12 @@ import {
ScopedVars,
stringStartsAsRegEx,
stringToJsRegex,
textUtil,
unEscapeStringFromRegex,
} from '@grafana/data';
import { TemplateSrv } from 'app/features/templating/template_srv';
import { ColumnRender, TableRenderModel, ColumnStyle } from './types';
import { ColumnOptionsCtrl } from './column_options';
import { sanitizeUrl } from 'app/core/utils/text';
export class TableRenderer {
formatters: any[];
@@ -298,7 +298,7 @@ export class TableRenderer {
scopedVars['__cell'] = { value: value, text: value ? value.toString() : '' };
const cellLink = this.templateSrv.replace(column.style.linkUrl, scopedVars, encodeURIComponent);
const sanitizedCellLink = sanitizeUrl(cellLink);
const sanitizedCellLink = textUtil.sanitizeUrl(cellLink);
const cellLinkTooltip = this.templateSrv.replace(column.style.linkTooltip, scopedVars);
const cellTarget = column.style.linkTargetBlank ? '_blank' : '';
+3 -4
View File
@@ -1,11 +1,10 @@
import _ from 'lodash';
import { PanelCtrl } from 'app/plugins/sdk';
import { sanitize, escapeHtml } from 'app/core/utils/text';
import config from 'app/core/config';
import { auto, ISCEService } from 'angular';
import { TemplateSrv } from 'app/features/templating/template_srv';
import { PanelEvents } from '@grafana/data';
import { PanelEvents, textUtil } from '@grafana/data';
import { renderMarkdown } from '@grafana/data';
const defaultContent = `
@@ -78,7 +77,7 @@ export class TextPanelCtrl extends PanelCtrl {
}
renderText(content: string) {
const safeContent = escapeHtml(content).replace(/\n/g, '<br/>');
const safeContent = textUtil.escapeHtml(content).replace(/\n/g, '<br/>');
this.updateContent(safeContent);
}
@@ -95,7 +94,7 @@ export class TextPanelCtrl extends PanelCtrl {
console.log('Text panel error: ', e);
}
this.content = this.$sce.trustAsHtml(config.disableSanitizeHtml ? html : sanitize(html));
this.content = this.$sce.trustAsHtml(config.disableSanitizeHtml ? html : textUtil.sanitize(html));
}
}
+2 -6
View File
@@ -1,15 +1,11 @@
// Libraries
import React, { PureComponent } from 'react';
import { debounce } from 'lodash';
import { renderMarkdown } from '@grafana/data';
import { PanelProps, renderMarkdown, textUtil } from '@grafana/data';
// Utils
import { sanitize } from 'app/core/utils/text';
import config from 'app/core/config';
// Types
import { TextOptions } from './types';
import { PanelProps } from '@grafana/data';
interface Props extends PanelProps<TextOptions> {}
interface State {
@@ -43,7 +39,7 @@ export class TextPanel extends PureComponent<Props, State> {
html = replaceVariables(html, {}, 'html');
return config.disableSanitizeHtml ? html : sanitize(html);
return config.disableSanitizeHtml ? html : textUtil.sanitize(html);
}
prepareText(content: string): string {
+10 -3
View File
@@ -6,12 +6,12 @@ import Drop from 'tether-drop';
// Utils and servies
import { colors } from '@grafana/ui';
import { setBackendSrv, setDataSourceSrv } from '@grafana/runtime';
import { getTemplateSrv, setBackendSrv, setDataSourceSrv } from '@grafana/runtime';
import config from 'app/core/config';
import coreModule from 'app/core/core_module';
import { profiler } from 'app/core/profiler';
import appEvents from 'app/core/app_events';
import { TimeSrv, setTimeSrv } from 'app/features/dashboard/services/TimeSrv';
import { TimeSrv, setTimeSrv, getTimeSrv } from 'app/features/dashboard/services/TimeSrv';
import { DatasourceSrv } from 'app/features/plugins/datasource_srv';
import { KeybindingSrv, setKeybindingSrv } from 'app/core/services/keybindingSrv';
import { AngularLoader, setAngularLoader } from 'app/core/services/AngularLoader';
@@ -29,7 +29,7 @@ import { BridgeSrv } from 'app/core/services/bridge_srv';
import { PlaylistSrv } from 'app/features/playlist/playlist_srv';
import { DashboardSrv, setDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
import { ILocationService, ITimeoutService, IRootScopeService, IAngularEvent } from 'angular';
import { AppEvent, AppEvents } from '@grafana/data';
import { AppEvent, AppEvents, locationUtil } from '@grafana/data';
import { backendSrv } from 'app/core/services/backend_srv';
export type GrafanaRootScope = IRootScopeService & AppEventEmitter & AppEventConsumer & { colors: string[] };
@@ -58,6 +58,13 @@ export class GrafanaCtrl {
setKeybindingSrv(keybindingSrv);
setDashboardSrv(dashboardSrv);
locationUtil.initialise({
getConfig: () => config,
getTimeRangeForUrl: getTimeSrv().timeRangeForUrl,
// @ts-ignore
buildParamsFromVariables: getTemplateSrv().fillVariableValuesForUrl,
});
const store = configureStore();
setLocationSrv({
update: (opt: LocationUpdate) => {
+1 -1
View File
@@ -1,10 +1,10 @@
import coreModule from 'app/core/core_module';
import locationUtil from 'app/core/utils/location_util';
import { UrlQueryMap } from '@grafana/runtime';
import { DashboardLoaderSrv } from 'app/features/dashboard/services/DashboardLoaderSrv';
import { ILocationService } from 'angular';
import { Scope, CoreEvents, AppEventEmitter } from 'app/types';
import { backendSrv } from 'app/core/services/backend_srv';
import { locationUtil } from '@grafana/data';
export class LoadDashboardCtrl {
/** @ngInject */