mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge branch 'master' into core/theming
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import _ from 'lodash';
|
||||
import coreModule from 'app/core/core_module';
|
||||
import appEvents from 'app/core/app_events';
|
||||
import config from 'app/core/config';
|
||||
import { DashboardModel } from 'app/features/dashboard/state/DashboardModel';
|
||||
|
||||
export class BackendSrv {
|
||||
@@ -103,10 +104,17 @@ export class BackendSrv {
|
||||
err => {
|
||||
// handle unauthorized
|
||||
if (err.status === 401 && this.contextSrv.user.isSignedIn && firstAttempt) {
|
||||
return this.loginPing().then(() => {
|
||||
options.retry = 1;
|
||||
return this.request(options);
|
||||
});
|
||||
return this.loginPing()
|
||||
.then(() => {
|
||||
options.retry = 1;
|
||||
return this.request(options);
|
||||
})
|
||||
.catch(err => {
|
||||
if (err.status === 401) {
|
||||
window.location.href = config.appSubUrl + '/logout';
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.$timeout(this.requestErrorHandler.bind(this, err), 50);
|
||||
@@ -184,13 +192,20 @@ export class BackendSrv {
|
||||
|
||||
// handle unauthorized for backend requests
|
||||
if (requestIsLocal && firstAttempt && err.status === 401) {
|
||||
return this.loginPing().then(() => {
|
||||
options.retry = 1;
|
||||
if (canceler) {
|
||||
canceler.resolve();
|
||||
}
|
||||
return this.datasourceRequest(options);
|
||||
});
|
||||
return this.loginPing()
|
||||
.then(() => {
|
||||
options.retry = 1;
|
||||
if (canceler) {
|
||||
canceler.resolve();
|
||||
}
|
||||
return this.datasourceRequest(options);
|
||||
})
|
||||
.catch(err => {
|
||||
if (err.status === 401) {
|
||||
window.location.href = config.appSubUrl + '/logout';
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// populate error obj on Internal Error
|
||||
|
||||
@@ -13,6 +13,11 @@ const DEFAULT_EXPLORE_STATE: ExploreUrlState = {
|
||||
datasource: null,
|
||||
queries: [],
|
||||
range: DEFAULT_RANGE,
|
||||
ui: {
|
||||
showingGraph: true,
|
||||
showingTable: true,
|
||||
showingLogs: true,
|
||||
}
|
||||
};
|
||||
|
||||
describe('state functions', () => {
|
||||
@@ -69,9 +74,11 @@ describe('state functions', () => {
|
||||
to: 'now',
|
||||
},
|
||||
};
|
||||
|
||||
expect(serializeStateToUrlParam(state)).toBe(
|
||||
'{"datasource":"foo","queries":[{"expr":"metric{test=\\"a/b\\"}"},' +
|
||||
'{"expr":"super{foo=\\"x/z\\"}"}],"range":{"from":"now-5h","to":"now"}}'
|
||||
'{"expr":"super{foo=\\"x/z\\"}"}],"range":{"from":"now-5h","to":"now"},' +
|
||||
'"ui":{"showingGraph":true,"showingTable":true,"showingLogs":true}}'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -93,7 +100,7 @@ describe('state functions', () => {
|
||||
},
|
||||
};
|
||||
expect(serializeStateToUrlParam(state, true)).toBe(
|
||||
'["now-5h","now","foo",{"expr":"metric{test=\\"a/b\\"}"},{"expr":"super{foo=\\"x/z\\"}"}]'
|
||||
'["now-5h","now","foo",{"expr":"metric{test=\\"a/b\\"}"},{"expr":"super{foo=\\"x/z\\"}"},{"ui":[true,true,true]}]'
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -118,7 +125,28 @@ describe('state functions', () => {
|
||||
};
|
||||
const serialized = serializeStateToUrlParam(state);
|
||||
const parsed = parseUrlState(serialized);
|
||||
expect(state).toMatchObject(parsed);
|
||||
});
|
||||
|
||||
it('can parse the compact serialized state into the original state', () => {
|
||||
const state = {
|
||||
...DEFAULT_EXPLORE_STATE,
|
||||
datasource: 'foo',
|
||||
queries: [
|
||||
{
|
||||
expr: 'metric{test="a/b"}',
|
||||
},
|
||||
{
|
||||
expr: 'super{foo="x/z"}',
|
||||
},
|
||||
],
|
||||
range: {
|
||||
from: 'now - 5h',
|
||||
to: 'now',
|
||||
},
|
||||
};
|
||||
const serialized = serializeStateToUrlParam(state, true);
|
||||
const parsed = parseUrlState(serialized);
|
||||
expect(state).toMatchObject(parsed);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,7 +11,7 @@ import { colors } from '@grafana/ui';
|
||||
import TableModel, { mergeTablesIntoModel } from 'app/core/table_model';
|
||||
|
||||
// Types
|
||||
import { RawTimeRange, IntervalValues, DataQuery } from '@grafana/ui/src/types';
|
||||
import { RawTimeRange, IntervalValues, DataQuery, DataSourceApi } from '@grafana/ui/src/types';
|
||||
import TimeSeries from 'app/core/time_series2';
|
||||
import {
|
||||
ExploreUrlState,
|
||||
@@ -27,6 +27,12 @@ export const DEFAULT_RANGE = {
|
||||
to: 'now',
|
||||
};
|
||||
|
||||
export const DEFAULT_UI_STATE = {
|
||||
showingTable: true,
|
||||
showingGraph: true,
|
||||
showingLogs: true,
|
||||
};
|
||||
|
||||
const MAX_HISTORY_ITEMS = 100;
|
||||
|
||||
export const LAST_USED_DATASOURCE_KEY = 'grafana.explore.datasource';
|
||||
@@ -147,7 +153,12 @@ export function buildQueryTransaction(
|
||||
|
||||
export const clearQueryKeys: ((query: DataQuery) => object) = ({ key, refId, ...rest }) => rest;
|
||||
|
||||
const isMetricSegment = (segment: { [key: string]: string }) => segment.hasOwnProperty('expr');
|
||||
const isUISegment = (segment: { [key: string]: string }) => segment.hasOwnProperty('ui');
|
||||
|
||||
export function parseUrlState(initial: string | undefined): ExploreUrlState {
|
||||
let uiState = DEFAULT_UI_STATE;
|
||||
|
||||
if (initial) {
|
||||
try {
|
||||
const parsed = JSON.parse(decodeURI(initial));
|
||||
@@ -160,20 +171,41 @@ export function parseUrlState(initial: string | undefined): ExploreUrlState {
|
||||
to: parsed[1],
|
||||
};
|
||||
const datasource = parsed[2];
|
||||
const queries = parsed.slice(3);
|
||||
return { datasource, queries, range };
|
||||
let queries = [];
|
||||
|
||||
parsed.slice(3).forEach(segment => {
|
||||
if (isMetricSegment(segment)) {
|
||||
queries = [...queries, segment];
|
||||
}
|
||||
|
||||
if (isUISegment(segment)) {
|
||||
uiState = {
|
||||
showingGraph: segment.ui[0],
|
||||
showingLogs: segment.ui[1],
|
||||
showingTable: segment.ui[2],
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
return { datasource, queries, range, ui: uiState };
|
||||
}
|
||||
return parsed;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
return { datasource: null, queries: [], range: DEFAULT_RANGE };
|
||||
return { datasource: null, queries: [], range: DEFAULT_RANGE, ui: uiState };
|
||||
}
|
||||
|
||||
export function serializeStateToUrlParam(urlState: ExploreUrlState, compact?: boolean): string {
|
||||
if (compact) {
|
||||
return JSON.stringify([urlState.range.from, urlState.range.to, urlState.datasource, ...urlState.queries]);
|
||||
return JSON.stringify([
|
||||
urlState.range.from,
|
||||
urlState.range.to,
|
||||
urlState.datasource,
|
||||
...urlState.queries,
|
||||
{ ui: [!!urlState.ui.showingGraph, !!urlState.ui.showingLogs, !!urlState.ui.showingTable] },
|
||||
]);
|
||||
}
|
||||
return JSON.stringify(urlState);
|
||||
}
|
||||
@@ -304,3 +336,12 @@ export function clearHistory(datasourceId: string) {
|
||||
const historyKey = `grafana.explore.history.${datasourceId}`;
|
||||
store.delete(historyKey);
|
||||
}
|
||||
|
||||
export const getQueryKeys = (queries: DataQuery[], datasourceInstance: DataSourceApi): string[] => {
|
||||
const queryKeys = queries.reduce((newQueryKeys, query, index) => {
|
||||
const primaryKey = datasourceInstance && datasourceInstance.name ? datasourceInstance.name : query.key;
|
||||
return newQueryKeys.concat(`${primaryKey}-${index}`);
|
||||
}, []);
|
||||
|
||||
return queryKeys;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user