provide interface

This commit is contained in:
ismail simsek
2025-05-25 15:38:47 +02:00
parent d8b9221768
commit 0280bce1c3
2 changed files with 122 additions and 147 deletions
@@ -724,136 +724,6 @@ describe('Prometheus Language Provider', () => {
);
});
});
describe('getMetricsMetadata', () => {
it('should fetch metadata if not already loaded', async () => {
const mockMetadata = {
metric1: [
{
type: 'counter',
help: 'help text 1',
},
],
metric2: [
{
type: 'gauge',
help: 'help text 2',
},
],
};
const expectedMetadata = {
metric1: {
type: 'counter',
help: 'help text 1',
},
metric2: {
type: 'gauge',
help: 'help text 2',
},
};
const languageProvider = new LanguageProvider({
...defaultDatasource,
metadataRequest: () => ({ data: { data: mockMetadata } }),
} as unknown as PrometheusDatasource);
const requestSpy = jest.spyOn(languageProvider, 'request');
const result = await languageProvider.getMetricsMetadata();
expect(requestSpy).toHaveBeenCalledWith(
'/api/v1/metadata',
{},
{
showErrorAlert: false,
headers: { 'X-Grafana-Cache': 'private, max-age=86400' },
}
);
expect(result).toEqual(expect.objectContaining(expectedMetadata));
});
it('should return cached metadata without fetching if already loaded', async () => {
const mockMetadata = {
metric1: [
{
type: 'counter',
help: 'help text 1',
},
],
};
const expectedMetadata = {
metric1: {
type: 'counter',
help: 'help text 1',
},
};
const languageProvider = new LanguageProvider({
...defaultDatasource,
metadataRequest: () => ({ data: { data: mockMetadata } }),
} as unknown as PrometheusDatasource);
// First call to load metadata
const firstResult = await languageProvider.getMetricsMetadata();
expect(firstResult).toEqual(expect.objectContaining(expectedMetadata));
const requestSpy = jest.spyOn(languageProvider, 'request');
// Second call should use cached data
const result = await languageProvider.getMetricsMetadata();
expect(requestSpy).not.toHaveBeenCalled();
expect(result).toEqual(expect.objectContaining(expectedMetadata));
});
it('should handle empty metadata response', async () => {
const languageProvider = new LanguageProvider({
...defaultDatasource,
metadataRequest: () => ({ data: { data: {} } }),
} as unknown as PrometheusDatasource);
const result = await languageProvider.getMetricsMetadata();
// Even with empty input, fixSummariesMetadata might add some default entries
expect(Object.keys(result).length).toBeGreaterThanOrEqual(0);
});
it('should handle error during metadata fetch', async () => {
jest.spyOn(console, 'error').mockImplementation();
const languageProvider = new LanguageProvider({
...defaultDatasource,
metadataRequest: () => Promise.reject(new Error('Metadata fetch failed')),
} as unknown as PrometheusDatasource);
const result = await languageProvider.getMetricsMetadata();
expect(result).toEqual(undefined);
});
it('should use configured cache duration from datasource', async () => {
const mockDays = 7;
const languageProvider = new LanguageProvider({
...defaultDatasource,
getDaysToCacheMetadata: () => mockDays,
metadataRequest: () => ({ data: { data: {} } }),
} as unknown as PrometheusDatasource);
const requestSpy = jest.spyOn(languageProvider, 'request');
await languageProvider.getMetricsMetadata();
expect(requestSpy).toHaveBeenCalledWith(
'/api/v1/metadata',
{},
{
showErrorAlert: false,
headers: { 'X-Grafana-Cache': `private, max-age=${mockDays * 86400}` },
}
);
});
});
});
describe('removeQuotesIfExist', () => {
@@ -39,7 +39,7 @@ const EMPTY_SELECTOR = '{}';
export const SUGGESTIONS_LIMIT = 10000;
/**
* Prometheus API endpoints for fetching resoruces
* Prometheus API endpoints for fetching resources
*/
const API_V1 = {
METADATA: '/api/v1/metadata',
@@ -55,19 +55,106 @@ type UrlParamsType = {
limit?: string;
};
export default class PromQlLanguageProvider extends LanguageProvider {
export interface PrometheusBaseLanguageProvider {
datasource: PrometheusDatasource;
/**
* When no timeRange provided, we will use the default time range (now/now-6h)
* @param timeRange
*/
start: (timeRange?: TimeRange) => Promise<any[]>;
fetchSuggestions: (
timeRange?: TimeRange,
queries?: PromQuery[],
scopes?: Scope[],
adhocFilters?: AdHocVariableFilter[],
labelName?: string,
limit?: number,
requestId?: string
) => Promise<string[]>;
}
/**
* @deprecated This interface is deprecated and will be removed.
*/
export interface PrometheusLegacyLanguageProvider {
/**
* @deprecated Use retrieveHistogramMetrics() method instead
*/
histogramMetrics: string[];
/**
* @deprecated Use retrieveMetrics() method instead
*/
metrics: string[];
/**
* @deprecated Use retrieveMetricsMetadata() method instead
*/
metricsMetadata?: PromMetricsMetadata;
/**
* @deprecated Use retrieveLabelKeys() method instead
*/
labelKeys: string[];
/**
* @deprecated Use queryMetricsMetadata() method instead. If you want existing metadata use retrieveMetricsMetadata() method.
*/
loadMetricsMetadata: () => void;
getLabelKeys: () => string[];
getSeries: (timeRange: TimeRange, selector: string, withName?: boolean) => Promise<Record<string, string[]>>;
fetchLabelValues: (range: TimeRange, key: string, limit?: string) => Promise<string[]>;
fetchLabels: (timeRange: TimeRange, queries?: PromQuery[], limit?: string) => Promise<string[]>;
getSeriesValues: (timeRange: TimeRange, labelName: string, selector: string) => Promise<string[]>;
fetchSeriesValuesWithMatch: (
timeRange: TimeRange,
name: string,
match?: string,
requestId?: string,
withLimit?: string
) => Promise<string[]>;
getSeriesLabels: (timeRange: TimeRange, selector: string, otherLabels: Label[]) => Promise<string[]>;
fetchLabelsWithMatch: (
timeRange: TimeRange,
name: string,
withName?: boolean,
withLimit?: string
) => Promise<Record<string, string[]>>;
fetchSeriesLabels: (
timeRange: TimeRange,
name: string,
withName?: boolean,
withLimit?: string
) => Promise<Record<string, string[]>>;
fetchSeriesLabelsMatch: (timeRange: TimeRange, name: string, withLimit?: string) => Promise<Record<string, string[]>>;
fetchSeries: (timeRange: TimeRange, match: string) => Promise<Array<Record<string, string>>>;
fetchDefaultSeries: (timeRange: TimeRange) => Promise<{}>;
}
export interface PrometheusLanguageProvider extends PrometheusBaseLanguageProvider, PrometheusLegacyLanguageProvider {
retrieveMetricsMetadata: () => PromMetricsMetadata | undefined;
retrieveHistogramMetrics: () => string[];
retrieveMetrics: () => string[];
retrieveLabelKeys: () => string[];
queryMetricsMetadata: () => Promise<PromMetricsMetadata>;
queryLabelKeys: (timeRange: TimeRange, match?: string, limit?: string) => string[];
queryLabelValues: (timeRange: TimeRange, labelKey: string, match?: string, limit?: string) => string[];
}
export default class PromQlLanguageProvider extends LanguageProvider implements PrometheusLanguageProvider {
declare startTask: Promise<any>;
declare labelFetchTs: number;
private _metricsMetadata?: PromMetricsMetadata;
private _histogramMetrics: string[] = [];
private _metrics: string[] = [];
private _labelKeys: string[] = [];
datasource: PrometheusDatasource;
histogramMetrics: string[];
metrics: string[];
/**
* @deprecated Use getMetricsMetadata() method instead
*/
metricsMetadata?: PromMetricsMetadata;
datasource: PrometheusDatasource;
labelKeys: string[] = [];
constructor(datasource: PrometheusDatasource, initialValues?: Partial<PromQlLanguageProvider>) {
@@ -120,7 +207,7 @@ export default class PromQlLanguageProvider extends LanguageProvider {
*
* @returns {Promise<PromMetricsMetadata>} Promise that resolves when metadata has been fetched
*/
private _fetchMetadata = async () => {
private _queryMetadata = async () => {
const secondsInDay = 86400;
const headers = buildCacheHeaders(getDaysToCacheMetadata(this.datasource.cacheLevel) * secondsInDay);
const metadata = await this.request(
@@ -158,8 +245,8 @@ export default class PromQlLanguageProvider extends LanguageProvider {
const res = await this.request(url, searchParams, getDefaultCacheHeaders(this.datasource.cacheLevel));
if (Array.isArray(res)) {
this.labelKeys = res.slice().sort();
return this.labelKeys.slice();
this._labelKeys = res.slice().sort();
return this._labelKeys.slice();
}
return [];
@@ -233,19 +320,37 @@ export default class PromQlLanguageProvider extends LanguageProvider {
// ======================================================================
public getMetricsMetadata = async (): Promise<PromMetricsMetadata> => {
if (!this._metricsMetadata) {
this._metricsMetadata = await this._fetchMetadata();
}
public retrieveMetricsMetadata = (): PromMetricsMetadata | undefined => {
return this._metricsMetadata;
};
public retrieveHistogramMetrics = (): string[] => {
return this._histogramMetrics;
};
public retrieveMetrics = (): string[] => {
return this._metrics;
};
public retrieveLabelKeys = (): string[] => {
return this._labelKeys;
};
public queryMetricsMetadata = async (): Promise<PromMetricsMetadata> => {
this._metricsMetadata = await this._queryMetadata();
return this._metricsMetadata;
};
public queryLabelKeys = (timeRange: TimeRange, match?: string, limit?: string): string[] => {
return [];
};
public queryLabelValues = (timeRange: TimeRange, labelKey: string, match?: string, limit?: string): string[] => {
return [];
};
// ======================================================================
/**
* @deprecated Use _fetchMetadata instead. If you want metadata itself use getMetricsMetadata() method.
*/
async loadMetricsMetadata() {
const secondsInDay = 86400;
const headers = buildCacheHeaders(getDaysToCacheMetadata(this.datasource.cacheLevel) * secondsInDay);