From 0280bce1c3bac5c319d45fe7a4127e35e286277a Mon Sep 17 00:00:00 2001 From: ismail simsek Date: Sun, 25 May 2025 15:38:47 +0200 Subject: [PATCH] provide interface --- .../src/language_provider.test.ts | 130 ---------------- .../src/language_provider.ts | 139 +++++++++++++++--- 2 files changed, 122 insertions(+), 147 deletions(-) diff --git a/packages/grafana-prometheus/src/language_provider.test.ts b/packages/grafana-prometheus/src/language_provider.test.ts index d1e88b6cc47..26307532aff 100644 --- a/packages/grafana-prometheus/src/language_provider.test.ts +++ b/packages/grafana-prometheus/src/language_provider.test.ts @@ -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', () => { diff --git a/packages/grafana-prometheus/src/language_provider.ts b/packages/grafana-prometheus/src/language_provider.ts index be6555d163d..8316ae7c03a 100644 --- a/packages/grafana-prometheus/src/language_provider.ts +++ b/packages/grafana-prometheus/src/language_provider.ts @@ -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; + + fetchSuggestions: ( + timeRange?: TimeRange, + queries?: PromQuery[], + scopes?: Scope[], + adhocFilters?: AdHocVariableFilter[], + labelName?: string, + limit?: number, + requestId?: string + ) => Promise; +} + +/** + * @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>; + fetchLabelValues: (range: TimeRange, key: string, limit?: string) => Promise; + fetchLabels: (timeRange: TimeRange, queries?: PromQuery[], limit?: string) => Promise; + getSeriesValues: (timeRange: TimeRange, labelName: string, selector: string) => Promise; + fetchSeriesValuesWithMatch: ( + timeRange: TimeRange, + name: string, + match?: string, + requestId?: string, + withLimit?: string + ) => Promise; + getSeriesLabels: (timeRange: TimeRange, selector: string, otherLabels: Label[]) => Promise; + fetchLabelsWithMatch: ( + timeRange: TimeRange, + name: string, + withName?: boolean, + withLimit?: string + ) => Promise>; + fetchSeriesLabels: ( + timeRange: TimeRange, + name: string, + withName?: boolean, + withLimit?: string + ) => Promise>; + fetchSeriesLabelsMatch: (timeRange: TimeRange, name: string, withLimit?: string) => Promise>; + fetchSeries: (timeRange: TimeRange, match: string) => Promise>>; + fetchDefaultSeries: (timeRange: TimeRange) => Promise<{}>; +} + +export interface PrometheusLanguageProvider extends PrometheusBaseLanguageProvider, PrometheusLegacyLanguageProvider { + retrieveMetricsMetadata: () => PromMetricsMetadata | undefined; + retrieveHistogramMetrics: () => string[]; + retrieveMetrics: () => string[]; + retrieveLabelKeys: () => string[]; + + queryMetricsMetadata: () => Promise; + 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; 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) { @@ -120,7 +207,7 @@ export default class PromQlLanguageProvider extends LanguageProvider { * * @returns {Promise} 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 => { - 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 => { + 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);