mirror of
https://github.com/grafana/grafana.git
synced 2025-09-28 22:04:01 +08:00
DataFrame: Add cached response notice for X-Cache: HIT header (#45564)
This commit is contained in:
@ -1,6 +1,6 @@
|
|||||||
import { DataQuery, toDataFrameDTO, DataFrame } from '@grafana/data';
|
import { DataQuery, toDataFrameDTO, DataFrame } from '@grafana/data';
|
||||||
import { FetchError, FetchResponse } from 'src/services';
|
import { FetchError, FetchResponse } from 'src/services';
|
||||||
import { BackendDataSourceResponse, toDataQueryResponse, toTestingStatus } from './queryResponse';
|
import { BackendDataSourceResponse, cachedResponseNotice, toDataQueryResponse, toTestingStatus } from './queryResponse';
|
||||||
|
|
||||||
const resp = {
|
const resp = {
|
||||||
data: {
|
data: {
|
||||||
@ -277,6 +277,55 @@ describe('Query Response parser', () => {
|
|||||||
expect(ids).toEqual(['A', 'B']);
|
expect(ids).toEqual(['A', 'B']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Cache notice', () => {
|
||||||
|
let resp: any;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
resp = {
|
||||||
|
url: '',
|
||||||
|
type: 'basic',
|
||||||
|
config: { url: '' },
|
||||||
|
status: 200,
|
||||||
|
statusText: 'OK',
|
||||||
|
ok: true,
|
||||||
|
redirected: false,
|
||||||
|
headers: new Headers(),
|
||||||
|
data: {
|
||||||
|
results: {
|
||||||
|
A: { frames: [{ schema: { fields: [] } }] },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
test('adds notice for responses with X-Cache: HIT header', () => {
|
||||||
|
const queries: DataQuery[] = [{ refId: 'A' }];
|
||||||
|
resp.headers.set('X-Cache', 'HIT');
|
||||||
|
expect(toDataQueryResponse(resp, queries).data[0].meta.notices).toStrictEqual([cachedResponseNotice]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('does not remove existing notices', () => {
|
||||||
|
const queries: DataQuery[] = [{ refId: 'A' }];
|
||||||
|
resp.headers.set('X-Cache', 'HIT');
|
||||||
|
resp.data.results.A.frames[0].schema.meta = { notices: [{ severity: 'info', text: 'Example' }] };
|
||||||
|
expect(toDataQueryResponse(resp, queries).data[0].meta.notices).toStrictEqual([
|
||||||
|
{ severity: 'info', text: 'Example' },
|
||||||
|
cachedResponseNotice,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('does not add notice for responses with X-Cache: MISS header', () => {
|
||||||
|
const queries: DataQuery[] = [{ refId: 'A' }];
|
||||||
|
resp.headers.set('X-Cache', 'MISS');
|
||||||
|
expect(toDataQueryResponse(resp, queries).data[0].meta?.notices).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('does not add notice for responses without X-Cache header', () => {
|
||||||
|
const queries: DataQuery[] = [{ refId: 'A' }];
|
||||||
|
expect(toDataQueryResponse(resp, queries).data[0].meta?.notices).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('resultWithError', () => {
|
test('resultWithError', () => {
|
||||||
// Generated from:
|
// Generated from:
|
||||||
// qdr.Responses[q.GetRefID()] = backend.DataResponse{
|
// qdr.Responses[q.GetRefID()] = backend.DataResponse{
|
||||||
|
@ -12,10 +12,13 @@ import {
|
|||||||
DataQuery,
|
DataQuery,
|
||||||
DataFrameJSON,
|
DataFrameJSON,
|
||||||
dataFrameFromJSON,
|
dataFrameFromJSON,
|
||||||
|
QueryResultMetaNotice,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { FetchError, FetchResponse } from '../services';
|
import { FetchError, FetchResponse } from '../services';
|
||||||
import { toDataQueryError } from './toDataQueryError';
|
import { toDataQueryError } from './toDataQueryError';
|
||||||
|
|
||||||
|
export const cachedResponseNotice: QueryResultMetaNotice = { severity: 'info', text: 'Cached response' };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Single response object from a backend data source. Properties are optional but response should contain at least
|
* Single response object from a backend data source. Properties are optional but response should contain at least
|
||||||
* an error or a some data (but can contain both). Main way to send data is with dataframes attribute as series and
|
* an error or a some data (but can contain both). Main way to send data is with dataframes attribute as series and
|
||||||
@ -62,6 +65,7 @@ export function toDataQueryResponse(
|
|||||||
if ((res as FetchResponse).data?.results) {
|
if ((res as FetchResponse).data?.results) {
|
||||||
const results = (res as FetchResponse).data.results;
|
const results = (res as FetchResponse).data.results;
|
||||||
const refIDs = queries?.length ? queries.map((q) => q.refId) : Object.keys(results);
|
const refIDs = queries?.length ? queries.map((q) => q.refId) : Object.keys(results);
|
||||||
|
const cachedResponse = isCachedResponse(res as FetchResponse);
|
||||||
const data: DataResponse[] = [];
|
const data: DataResponse[] = [];
|
||||||
|
|
||||||
for (const refId of refIDs) {
|
for (const refId of refIDs) {
|
||||||
@ -85,7 +89,10 @@ export function toDataQueryResponse(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (dr.frames?.length) {
|
if (dr.frames?.length) {
|
||||||
for (const js of dr.frames) {
|
for (let js of dr.frames) {
|
||||||
|
if (cachedResponse) {
|
||||||
|
js = addCacheNotice(js);
|
||||||
|
}
|
||||||
const df = dataFrameFromJSON(js);
|
const df = dataFrameFromJSON(js);
|
||||||
if (!df.refId) {
|
if (!df.refId) {
|
||||||
df.refId = dr.refId;
|
df.refId = dr.refId;
|
||||||
@ -128,6 +135,28 @@ export function toDataQueryResponse(
|
|||||||
return rsp;
|
return rsp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isCachedResponse(res: FetchResponse<BackendDataSourceResponse | undefined>): boolean {
|
||||||
|
const headers = res?.headers;
|
||||||
|
if (!headers || !headers.get) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return headers.get('X-Cache') === 'HIT';
|
||||||
|
}
|
||||||
|
|
||||||
|
function addCacheNotice(frame: DataFrameJSON): DataFrameJSON {
|
||||||
|
return {
|
||||||
|
...frame,
|
||||||
|
schema: {
|
||||||
|
...frame.schema,
|
||||||
|
fields: [...(frame.schema?.fields ?? [])],
|
||||||
|
meta: {
|
||||||
|
...frame.schema?.meta,
|
||||||
|
notices: [...(frame.schema?.meta?.notices ?? []), cachedResponseNotice],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data sources using api/ds/query to test data sources can use this function to
|
* Data sources using api/ds/query to test data sources can use this function to
|
||||||
* handle errors and convert them to TestingStatus object.
|
* handle errors and convert them to TestingStatus object.
|
||||||
|
Reference in New Issue
Block a user