From e45eb958122799732c32e3ffa7e6589f96ed128d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Falksk=C3=A4r?= <457523+edvard-falkskar@users.noreply.github.com> Date: Tue, 14 Jan 2025 12:27:13 +0100 Subject: [PATCH] Correlations: Expose correlations logic through correlations service (#98124) * Correlations: Expose correlations logic through correlations service * Resolve remaining todos * Marked new items as @alpha --- packages/grafana-runtime/src/index.ts | 9 ++ .../src/services/CorrelationsService.ts | 126 ++++++++++++++++++ public/app/app.ts | 3 + .../app/core/services/CorrelationsService.ts | 22 +++ .../correlations/CorrelationsPage.tsx | 4 +- .../Forms/ConfigureCorrelationTargetForm.tsx | 5 +- .../app/features/correlations/Forms/types.ts | 7 +- public/app/features/correlations/types.ts | 36 +---- .../features/correlations/useCorrelations.ts | 21 +-- .../app/features/correlations/utils.test.ts | 3 +- public/app/features/correlations/utils.ts | 19 +-- .../features/explore/state/correlations.ts | 3 +- .../app/features/explore/state/explorePane.ts | 2 +- .../features/explore/utils/decorators.test.ts | 2 +- .../app/features/explore/utils/decorators.ts | 3 +- public/app/types/explore.ts | 3 +- 16 files changed, 187 insertions(+), 81 deletions(-) create mode 100644 packages/grafana-runtime/src/services/CorrelationsService.ts create mode 100644 public/app/core/services/CorrelationsService.ts diff --git a/packages/grafana-runtime/src/index.ts b/packages/grafana-runtime/src/index.ts index ed6bbb27128..0c64456428b 100644 --- a/packages/grafana-runtime/src/index.ts +++ b/packages/grafana-runtime/src/index.ts @@ -57,3 +57,12 @@ export { hasPermission, hasPermissionInMetadata, hasAllPermissions, hasAnyPermis export { QueryEditorWithMigration } from './components/QueryEditorWithMigration'; export { type MigrationHandler, isMigrationHandler, migrateQuery, migrateRequest } from './utils/migrationHandler'; export { usePluginUserStorage } from './utils/userStorage'; +export { + type CorrelationsService, + type CorrelationData, + type CorrelationsData, + type CorrelationExternal, + type CorrelationQuery, + getCorrelationsService, + setCorrelationsService, +} from './services/CorrelationsService'; diff --git a/packages/grafana-runtime/src/services/CorrelationsService.ts b/packages/grafana-runtime/src/services/CorrelationsService.ts new file mode 100644 index 00000000000..3cd8da0ec52 --- /dev/null +++ b/packages/grafana-runtime/src/services/CorrelationsService.ts @@ -0,0 +1,126 @@ +import { + DataFrame, + DataLinkPostProcessor, + DataLinkTransformationConfig, + DataSourceInstanceSettings, + TimeRange, +} from '@grafana/data'; + +export type CorrelationConfigQuery = { + field: string; + target: object; // for queries, this contains anything that would go in the query editor, so any extension off DataQuery a datasource would have, and needs to be generic. + transformations?: DataLinkTransformationConfig[]; +}; + +export type CorrelationConfigExternal = { + field: string; + target: { + url: string; // For external, this simply contains a URL + }; + transformations?: DataLinkTransformationConfig[]; +}; + +type CorrelationBase = { + uid: string; + sourceUID: string; + label?: string; + description?: string; + provisioned: boolean; + orgId?: number; +}; + +/** + * @alpha + */ +export type CorrelationExternal = CorrelationBase & { + type: 'external'; + config: CorrelationConfigExternal; +}; + +/** + * @alpha + */ +export type CorrelationQuery = CorrelationBase & { + type: 'query'; + config: CorrelationConfigQuery; + targetUID: string; +}; + +/** + * @alpha + */ +export type CorrelationData = + | (Omit & { + source: DataSourceInstanceSettings; + }) + | (Omit & { + source: DataSourceInstanceSettings; + target: DataSourceInstanceSettings; + }); + +/** + * @alpha + */ +export interface CorrelationsData { + correlations: CorrelationData[]; + page: number; + limit: number; + totalCount: number; +} + +/** + * Used to work with user defined correlations. + * Should be accessed via {@link getCorrelationsService} function. + * + * @alpha + */ +export interface CorrelationsService { + /** + * Creates data links in data frames from provided correlations + * + * @param dataFrames list of data frames to be processed + * @param correlations list of of possible correlations that can be applied + * @param dataFrameRefIdToDataSourceUid a map that for provided refId references corresponding data source ui + */ + attachCorrelationsToDataFrames: ( + dataFrames: DataFrame[], + correlations: CorrelationData[], + dataFrameRefIdToDataSourceUid: Record + ) => DataFrame[]; + + /** + * Creates a link post processor function that handles correlation transformations + * + * @param timeRange The current time range + */ + correlationsDataLinkPostProcessorFactory: (timeRange: TimeRange) => DataLinkPostProcessor; + + /** + * Loads all the correlations defined for the given data sources. + * + * @param sourceUIDs Data source UIDs + */ + getCorrelationsBySourceUIDs: (sourceUIDs: string[]) => Promise; +} + +let singletonInstance: CorrelationsService; + +/** + * Used during startup by Grafana to set the CorrelationsService so it is available + * via {@link getCorrelationsService} to the rest of the application. + * + * @internal + */ +export function setCorrelationsService(instance: CorrelationsService) { + console.log('setCorrelationsService', instance); + singletonInstance = instance; +} + +/** + * Used to retrieve the {@link CorrelationsService}. + * + * @alpha + */ +export function getCorrelationsService(): CorrelationsService { + return singletonInstance; +} diff --git a/public/app/app.ts b/public/app/app.ts index 17ba4c04d73..7cd4d4fbd69 100644 --- a/public/app/app.ts +++ b/public/app/app.ts @@ -40,6 +40,7 @@ import { setCurrentUser, setChromeHeaderHeightHook, setPluginLinksHook, + setCorrelationsService, } from '@grafana/runtime'; import { setPanelDataErrorView } from '@grafana/runtime/src/components/PanelDataErrorView'; import { setPanelRenderer } from '@grafana/runtime/src/components/PanelRenderer'; @@ -60,6 +61,7 @@ import { initializeCrashDetection } from './core/crash'; import { initializeI18n } from './core/internationalization'; import { setMonacoEnv } from './core/monacoEnv'; import { interceptLinkClicks } from './core/navigation/patch/interceptLinkClicks'; +import { CorrelationsService } from './core/services/CorrelationsService'; import { NewFrontendAssetsChecker } from './core/services/NewFrontendAssetsChecker'; import { backendSrv } from './core/services/backend_srv'; import { contextSrv, RedirectToUrlKey } from './core/services/context_srv'; @@ -146,6 +148,7 @@ export class GrafanaApp { setPluginPage(PluginPage); setPanelDataErrorView(PanelDataErrorView); setLocationSrv(locationService); + setCorrelationsService(new CorrelationsService()); setEmbeddedDashboard(EmbeddedDashboardLazy); setTimeZoneResolver(() => config.bootData.user.timezone); initGrafanaLive(); diff --git a/public/app/core/services/CorrelationsService.ts b/public/app/core/services/CorrelationsService.ts new file mode 100644 index 00000000000..d4332a86e05 --- /dev/null +++ b/public/app/core/services/CorrelationsService.ts @@ -0,0 +1,22 @@ +import { DataFrame, TimeRange } from '@grafana/data'; +import type { CorrelationData, CorrelationsService as CorrelationsServiceInterface } from '@grafana/runtime'; +import { attachCorrelationsToDataFrames, getCorrelationsBySourceUIDs } from 'app/features/correlations/utils'; +import { exploreDataLinkPostProcessorFactory } from 'app/features/explore/utils/links'; + +export class CorrelationsService implements CorrelationsServiceInterface { + attachCorrelationsToDataFrames( + dataFrames: DataFrame[], + correlations: CorrelationData[], + dataFrameRefIdToDataSourceUid: Record + ) { + return attachCorrelationsToDataFrames(dataFrames, correlations, dataFrameRefIdToDataSourceUid); + } + + correlationsDataLinkPostProcessorFactory(timeRange: TimeRange) { + return exploreDataLinkPostProcessorFactory(undefined, timeRange); + } + + getCorrelationsBySourceUIDs(sourceUIDs: string[]) { + return getCorrelationsBySourceUIDs(sourceUIDs); + } +} diff --git a/public/app/features/correlations/CorrelationsPage.tsx b/public/app/features/correlations/CorrelationsPage.tsx index 163f18b6435..9028fba1446 100644 --- a/public/app/features/correlations/CorrelationsPage.tsx +++ b/public/app/features/correlations/CorrelationsPage.tsx @@ -3,7 +3,7 @@ import { negate } from 'lodash'; import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { DataSourceInstanceSettings, GrafanaTheme2 } from '@grafana/data'; -import { isFetchError, reportInteraction } from '@grafana/runtime'; +import { CorrelationData, isFetchError, reportInteraction } from '@grafana/runtime'; import { Badge, Button, @@ -28,7 +28,7 @@ import { AddCorrelationForm } from './Forms/AddCorrelationForm'; import { EditCorrelationForm } from './Forms/EditCorrelationForm'; import { EmptyCorrelationsCTA } from './components/EmptyCorrelationsCTA'; import type { Correlation, RemoveCorrelationParams } from './types'; -import { CorrelationData, useCorrelations } from './useCorrelations'; +import { useCorrelations } from './useCorrelations'; const sortDatasource: SortByFn = (a, b, column) => a.values[column].name.localeCompare(b.values[column].name); diff --git a/public/app/features/correlations/Forms/ConfigureCorrelationTargetForm.tsx b/public/app/features/correlations/Forms/ConfigureCorrelationTargetForm.tsx index 75f9aac81aa..2818bebb4b8 100644 --- a/public/app/features/correlations/Forms/ConfigureCorrelationTargetForm.tsx +++ b/public/app/features/correlations/Forms/ConfigureCorrelationTargetForm.tsx @@ -2,11 +2,12 @@ import { css } from '@emotion/css'; import { Controller, FieldError, useFormContext, useWatch } from 'react-hook-form'; import { DataSourceInstanceSettings, GrafanaTheme2 } from '@grafana/data'; +import { CorrelationExternal } from '@grafana/runtime'; import { Field, FieldSet, Input, Select, useStyles2 } from '@grafana/ui'; import { Trans, t } from 'app/core/internationalization'; import { DataSourcePicker } from 'app/features/datasources/components/picker/DataSourcePicker'; -import { CorrelationType, ExternalTypeTarget } from '../types'; +import { CorrelationType } from '../types'; import { QueryEditorField } from './QueryEditorField'; import { useCorrelationsFormContext } from './correlationsFormContext'; @@ -146,7 +147,7 @@ export const ConfigureCorrelationTargetForm = () => { }, }} render={({ field: { onChange, value } }) => { - const castVal = value as ExternalTypeTarget; // the target under "query" type can contain anything a datasource query contains + const castVal = value as CorrelationExternal['config']['target']; // the target under "query" type can contain anything a datasource query contains return ( & { - source: DataSourceInstanceSettings; - }) - | (Omit & { - source: DataSourceInstanceSettings; - target: DataSourceInstanceSettings; - }); - -export interface CorrelationsData { - correlations: CorrelationData[]; - page: number; - limit: number; - totalCount: number; -} - const toEnrichedCorrelationData = ({ sourceUID, ...correlation }: Correlation): CorrelationData | undefined => { const sourceDatasource = getDataSourceSrv().getInstanceSettings(sourceUID); const targetDatasource = diff --git a/public/app/features/correlations/utils.test.ts b/public/app/features/correlations/utils.test.ts index f182b3c85c3..4caf7b60b82 100644 --- a/public/app/features/correlations/utils.test.ts +++ b/public/app/features/correlations/utils.test.ts @@ -1,7 +1,6 @@ import { DataFrame, DataFrameType, DataSourceInstanceSettings, FieldType, toDataFrame } from '@grafana/data'; -import { config } from '@grafana/runtime'; +import { config, CorrelationData } from '@grafana/runtime'; -import { CorrelationData } from './useCorrelations'; import { attachCorrelationsToDataFrames } from './utils'; describe('correlations utils', () => { diff --git a/public/app/features/correlations/utils.ts b/public/app/features/correlations/utils.ts index f7778bb16bb..1a6f97157da 100644 --- a/public/app/features/correlations/utils.ts +++ b/public/app/features/correlations/utils.ts @@ -1,20 +1,21 @@ import { lastValueFrom } from 'rxjs'; import { DataFrame, DataLinkConfigOrigin } from '@grafana/data'; -import { config, createMonitoringLogger, getBackendSrv, getDataSourceSrv } from '@grafana/runtime'; +import { + config, + CorrelationData, + CorrelationsData, + createMonitoringLogger, + getBackendSrv, + getDataSourceSrv, +} from '@grafana/runtime'; import { ExploreItemState } from 'app/types'; import { formatValueName } from '../explore/PrometheusListView/ItemLabels'; import { parseLogsFrame } from '../logs/logsFrame'; import { CreateCorrelationParams, CreateCorrelationResponse } from './types'; -import { - CorrelationData, - CorrelationsData, - CorrelationsResponse, - getData, - toEnrichedCorrelationsData, -} from './useCorrelations'; +import { CorrelationsResponse, getData, toEnrichedCorrelationsData } from './useCorrelations'; type DataFrameRefIdToDataSourceUid = Record; @@ -85,7 +86,7 @@ const decorateDataFrameWithInternalDataLinks = (dataFrame: DataFrame, correlatio }; /* -If a correlation was made based on the log line field prior to the loki data plane, they would use the field "Line" +If a correlation was made based on the log line field prior to the loki data plane, they would use the field "Line" Change it to use whatever the body field name is post-loki data plane */ diff --git a/public/app/features/explore/state/correlations.ts b/public/app/features/explore/state/correlations.ts index cbf50fa81f6..4e093ca3669 100644 --- a/public/app/features/explore/state/correlations.ts +++ b/public/app/features/explore/state/correlations.ts @@ -1,11 +1,10 @@ import { Observable } from 'rxjs'; import { DataLinkTransformationConfig } from '@grafana/data'; -import { getDataSourceSrv, reportInteraction } from '@grafana/runtime'; +import { CorrelationData, getDataSourceSrv, reportInteraction } from '@grafana/runtime'; import { notifyApp } from 'app/core/actions'; import { createErrorNotification } from 'app/core/copy/appNotification'; import { CreateCorrelationParams } from 'app/features/correlations/types'; -import { CorrelationData } from 'app/features/correlations/useCorrelations'; import { getCorrelationsBySourceUIDs, createCorrelation, generateDefaultLabel } from 'app/features/correlations/utils'; import { store } from 'app/store/store'; import { ThunkResult } from 'app/types'; diff --git a/public/app/features/explore/state/explorePane.ts b/public/app/features/explore/state/explorePane.ts index b83eb938b3b..601ceea2dae 100644 --- a/public/app/features/explore/state/explorePane.ts +++ b/public/app/features/explore/state/explorePane.ts @@ -11,9 +11,9 @@ import { ExploreCorrelationHelperData, EventBusExtended, } from '@grafana/data'; +import { CorrelationData } from '@grafana/runtime'; import { DataQuery, DataSourceRef } from '@grafana/schema'; import { getQueryKeys } from 'app/core/utils/explore'; -import { CorrelationData } from 'app/features/correlations/useCorrelations'; import { getCorrelationsBySourceUIDs } from 'app/features/correlations/utils'; import { getTimeZone } from 'app/features/profile/state/selectors'; import { createAsyncThunk, ThunkResult } from 'app/types'; diff --git a/public/app/features/explore/utils/decorators.test.ts b/public/app/features/explore/utils/decorators.test.ts index b61a87f89ec..5e2e1517eee 100644 --- a/public/app/features/explore/utils/decorators.test.ts +++ b/public/app/features/explore/utils/decorators.test.ts @@ -10,9 +10,9 @@ import { DataSourceApi, DataSourceInstanceSettings, } from '@grafana/data'; +import { CorrelationData } from '@grafana/runtime'; import { DataSourceJsonData, DataQuery } from '@grafana/schema'; import TableModel from 'app/core/TableModel'; -import { CorrelationData } from 'app/features/correlations/useCorrelations'; import { ExplorePanelData } from 'app/types'; import { diff --git a/public/app/features/explore/utils/decorators.ts b/public/app/features/explore/utils/decorators.ts index b09f0b61b61..08dc24b6a7d 100644 --- a/public/app/features/explore/utils/decorators.ts +++ b/public/app/features/explore/utils/decorators.ts @@ -14,12 +14,11 @@ import { getRawDisplayProcessor, DataSourceApi, } from '@grafana/data'; -import { config } from '@grafana/runtime'; +import { config, CorrelationData } from '@grafana/runtime'; import { DataQuery } from '@grafana/schema'; import { refreshIntervalToSortOrder } from '../../../core/utils/explore'; import { ExplorePanelData } from '../../../types'; -import { CorrelationData } from '../../correlations/useCorrelations'; import { attachCorrelationsToDataFrames } from '../../correlations/utils'; import { dataFrameToLogsModel } from '../../logs/logsModel'; import { sortLogsResult } from '../../logs/utils'; diff --git a/public/app/types/explore.ts b/public/app/types/explore.ts index 865d656e3dc..11ac0ad84d0 100644 --- a/public/app/types/explore.ts +++ b/public/app/types/explore.ts @@ -19,10 +19,9 @@ import { ExploreCorrelationHelperData, DataLinkTransformationConfig, } from '@grafana/data'; +import { CorrelationData } from '@grafana/runtime'; import { RichHistorySearchFilters, RichHistorySettings } from 'app/core/utils/richHistoryTypes'; -import { CorrelationData } from '../features/correlations/useCorrelations'; - export type ExploreQueryParams = UrlQueryMap; export enum CORRELATION_EDITOR_POST_CONFIRM_ACTION {