Correlations: Expose correlations logic through correlations service (#98124)

* Correlations: Expose correlations logic through correlations service

* Resolve remaining todos

* Marked new items as @alpha
This commit is contained in:
Edvard Falkskär 2025-01-14 12:27:13 +01:00 committed by GitHub
parent 33a91f22c0
commit e45eb95812
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 187 additions and 81 deletions

View File

@ -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';

View File

@ -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<CorrelationExternal, 'sourceUID'> & {
source: DataSourceInstanceSettings;
})
| (Omit<CorrelationQuery, 'sourceUID' | 'targetUID'> & {
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<string, string>
) => 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<CorrelationsData>;
}
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;
}

View File

@ -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();

View File

@ -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<string, string>
) {
return attachCorrelationsToDataFrames(dataFrames, correlations, dataFrameRefIdToDataSourceUid);
}
correlationsDataLinkPostProcessorFactory(timeRange: TimeRange) {
return exploreDataLinkPostProcessorFactory(undefined, timeRange);
}
getCorrelationsBySourceUIDs(sourceUIDs: string[]) {
return getCorrelationsBySourceUIDs(sourceUIDs);
}
}

View File

@ -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<CorrelationData> = (a, b, column) =>
a.values[column].name.localeCompare(b.values[column].name);

View File

@ -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 (
<Field
label={t('correlations.target-form.target-label', 'Target')}

View File

@ -1,16 +1,17 @@
import { DeepMap, FieldError, FieldErrors } from 'react-hook-form';
import { SupportedTransformationType } from '@grafana/data';
import { CorrelationExternal, CorrelationQuery } from '@grafana/runtime';
import { t } from 'app/core/internationalization';
import { CorrelationConfigExternal, CorrelationConfigQuery, OmitUnion } from '../types';
import { OmitUnion } from '../types';
export interface FormExternalDTO {
sourceUID: string;
label: string;
description: string;
type: 'external';
config: CorrelationConfigExternal;
config: CorrelationExternal['config'];
}
export interface FormQueryDTO {
@ -19,7 +20,7 @@ export interface FormQueryDTO {
label: string;
description: string;
type: 'query';
config: CorrelationConfigQuery;
config: CorrelationQuery['config'];
}
export type FormDTO = FormExternalDTO | FormQueryDTO;

View File

@ -1,4 +1,4 @@
import { DataLinkTransformationConfig } from '@grafana/data';
import { CorrelationExternal, CorrelationQuery } from '@grafana/runtime';
export interface AddCorrelationResponse {
correlation: Correlation;
@ -28,40 +28,6 @@ export interface RemoveCorrelationResponse {
export type CorrelationType = 'query' | 'external';
export type ExternalTypeTarget = { url: string };
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: ExternalTypeTarget; // For external, this simply contains a URL
transformations?: DataLinkTransformationConfig[];
};
type CorrelationBase = {
uid: string;
sourceUID: string;
label?: string;
description?: string;
provisioned: boolean;
orgId?: number;
};
export type CorrelationExternal = CorrelationBase & {
type: 'external';
config: CorrelationConfigExternal;
};
export type CorrelationQuery = CorrelationBase & {
type: 'query';
config: CorrelationConfigQuery;
targetUID: string;
};
export type Correlation = CorrelationExternal | CorrelationQuery;
export type GetCorrelationsParams = {

View File

@ -1,14 +1,11 @@
import { useAsyncFn } from 'react-use';
import { lastValueFrom } from 'rxjs';
import { DataSourceInstanceSettings } from '@grafana/data';
import { getDataSourceSrv, FetchResponse } from '@grafana/runtime';
import { getDataSourceSrv, FetchResponse, CorrelationData, CorrelationsData } from '@grafana/runtime';
import { useGrafana } from 'app/core/context/GrafanaContext';
import {
Correlation,
CorrelationExternal,
CorrelationQuery,
CreateCorrelationParams,
CreateCorrelationResponse,
GetCorrelationsParams,
@ -26,22 +23,6 @@ export interface CorrelationsResponse {
totalCount: number;
}
export type CorrelationData =
| (Omit<CorrelationExternal, 'sourceUID'> & {
source: DataSourceInstanceSettings;
})
| (Omit<CorrelationQuery, 'sourceUID' | 'targetUID'> & {
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 =

View File

@ -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', () => {

View File

@ -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<string, string>;

View File

@ -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';

View File

@ -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';

View File

@ -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 {

View File

@ -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';

View File

@ -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 {