Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions x-pack/plugins/transform/common/api_schemas/transforms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type { ES_FIELD_TYPES } from '../../../../../src/plugins/data/common';
import type { Dictionary } from '../types/common';
import type { PivotAggDict } from '../types/pivot_aggs';
import type { PivotGroupByDict } from '../types/pivot_group_by';
import type { TransformId, TransformPivotConfig } from '../types/transform';
import type { TransformId, TransformConfigUnion } from '../types/transform';

import { transformStateSchema, runtimeMappingsSchema } from './common';

Expand All @@ -33,7 +33,7 @@ export type GetTransformsRequestSchema = TypeOf<typeof getTransformsRequestSchem

export interface GetTransformsResponseSchema {
count: number;
transforms: TransformPivotConfig[];
transforms: TransformConfigUnion[];
}

// schemas shared by parts of the preview, create and update endpoint
Expand Down
4 changes: 3 additions & 1 deletion x-pack/plugins/transform/common/types/alerting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import type { AlertTypeParams } from '../../../alerting/common';
import type { Alert, AlertTypeParams } from '../../../alerting/common';

export type TransformHealthRuleParams = {
includeTransforms?: string[];
Expand All @@ -20,3 +20,5 @@ export type TransformHealthRuleParams = {
export type TransformHealthRuleTestsConfig = TransformHealthRuleParams['testsConfig'];

export type TransformHealthTests = keyof Exclude<TransformHealthRuleTestsConfig, null | undefined>;

export type TransformHealthAlertRule = Omit<Alert<TransformHealthRuleParams>, 'apiKey'>;
17 changes: 14 additions & 3 deletions x-pack/plugins/transform/common/types/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
* 2.0.
*/

import { EuiComboBoxOptionOption } from '@elastic/eui/src/components/combo_box/types';
import type { EuiComboBoxOptionOption } from '@elastic/eui/src/components/combo_box/types';
import type { LatestFunctionConfig, PutTransformsRequestSchema } from '../api_schemas/transforms';
import { isPopulatedObject } from '../shared_imports';
import { PivotGroupByDict } from './pivot_group_by';
import { PivotAggDict } from './pivot_aggs';
import type { PivotGroupByDict } from './pivot_group_by';
import type { PivotAggDict } from './pivot_aggs';
import type { TransformHealthAlertRule } from './alerting';

export type IndexName = string;
export type IndexPattern = string;
Expand All @@ -22,6 +23,7 @@ export type TransformBaseConfig = PutTransformsRequestSchema & {
id: TransformId;
create_time?: number;
version?: string;
alerting_rules?: TransformHealthAlertRule[];
};

export interface PivotConfigDefinition {
Expand All @@ -45,6 +47,11 @@ export type TransformLatestConfig = Omit<TransformBaseConfig, 'pivot'> & {

export type TransformConfigUnion = TransformPivotConfig | TransformLatestConfig;

export type ContinuousTransform = Omit<TransformConfigUnion, 'sync'> &
Required<{
sync: TransformConfigUnion['sync'];
}>;

export function isPivotTransform(transform: unknown): transform is TransformPivotConfig {
return isPopulatedObject(transform, ['pivot']);
}
Expand All @@ -53,6 +60,10 @@ export function isLatestTransform(transform: unknown): transform is TransformLat
return isPopulatedObject(transform, ['latest']);
}

export function isContinuousTransform(transform: unknown): transform is ContinuousTransform {
return isPopulatedObject(transform, ['sync']);
}

export interface LatestFunctionConfigUI {
unique_key: Array<EuiComboBoxOptionOption<string>> | undefined;
sort: EuiComboBoxOptionOption<string> | undefined;
Expand Down
127 changes: 127 additions & 0 deletions x-pack/plugins/transform/public/alerting/transform_alerting_flyout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React, { createContext, FC, useContext, useMemo } from 'react';
import { memoize } from 'lodash';
import { BehaviorSubject, Observable } from 'rxjs';
import { pluck } from 'rxjs/operators';
import useObservable from 'react-use/lib/useObservable';
import { useAppDependencies } from '../app/app_dependencies';
import { TransformHealthAlertRule, TransformHealthRuleParams } from '../../common/types/alerting';
import { TRANSFORM_RULE_TYPE } from '../../common';

interface TransformAlertFlyoutProps {
initialAlert?: TransformHealthAlertRule | null;
ruleParams?: TransformHealthRuleParams | null;
onSave?: () => void;
onCloseFlyout: () => void;
}

export const TransformAlertFlyout: FC<TransformAlertFlyoutProps> = ({
initialAlert,
ruleParams,
onCloseFlyout,
onSave,
}) => {
const { triggersActionsUi } = useAppDependencies();

const AlertFlyout = useMemo(() => {
if (!triggersActionsUi) return;

const commonProps = {
onClose: () => {
onCloseFlyout();
},
onSave: async () => {
if (onSave) {
onSave();
}
},
};

if (initialAlert) {
return triggersActionsUi.getEditAlertFlyout({
...commonProps,
initialAlert,
});
}

return triggersActionsUi.getAddAlertFlyout({
...commonProps,
consumer: 'stackAlerts',
canChangeTrigger: false,
alertTypeId: TRANSFORM_RULE_TYPE.TRANSFORM_HEALTH,
metadata: {},
initialValues: {
params: ruleParams!,
},
});
// deps on id to avoid re-rendering on auto-refresh
}, [triggersActionsUi, initialAlert, ruleParams, onCloseFlyout, onSave]);

return <>{AlertFlyout}</>;
};

interface AlertRulesManage {
editAlertRule$: Observable<TransformHealthAlertRule | null>;
createAlertRule$: Observable<TransformHealthRuleParams | null>;
setEditAlertRule: (alertRule: TransformHealthAlertRule) => void;
setCreateAlertRule: (transformId: string) => void;
hideAlertFlyout: () => void;
}

export const getAlertRuleManageContext = memoize(function (): AlertRulesManage {
const ruleState$ = new BehaviorSubject<{
editAlertRule: null | TransformHealthAlertRule;
createAlertRule: null | TransformHealthRuleParams;
}>({
editAlertRule: null,
createAlertRule: null,
});
return {
editAlertRule$: ruleState$.pipe(pluck('editAlertRule')),
createAlertRule$: ruleState$.pipe(pluck('createAlertRule')),
setEditAlertRule: (initialRule) => {
ruleState$.next({
createAlertRule: null,
editAlertRule: initialRule,
});
},
setCreateAlertRule: (transformId: string) => {
ruleState$.next({
createAlertRule: { includeTransforms: [transformId] },
editAlertRule: null,
});
},
hideAlertFlyout: () => {
ruleState$.next({
createAlertRule: null,
editAlertRule: null,
});
},
};
});

export const AlertRulesManageContext = createContext<AlertRulesManage>(getAlertRuleManageContext());

export function useAlertRuleFlyout(): AlertRulesManage {
return useContext(AlertRulesManageContext);
}

export const TransformAlertFlyoutWrapper = () => {
const { editAlertRule$, createAlertRule$, hideAlertFlyout } = useAlertRuleFlyout();
const editAlertRule = useObservable(editAlertRule$);
const createAlertRule = useObservable(createAlertRule$);

return editAlertRule || createAlertRule ? (
<TransformAlertFlyout
initialAlert={editAlertRule}
ruleParams={createAlertRule!}
onCloseFlyout={hideAlertFlyout}
/>
) : null;
};
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { Storage } from '../../../../../../src/plugins/kibana_utils/public';
import type { AppDependencies } from '../app_dependencies';
import { MlSharedContext } from './shared_context';
import type { GetMlSharedImportsReturnType } from '../../shared_imports';
import type { TriggersAndActionsUIPublicPluginStart } from '../../../../triggers_actions_ui/public';

const coreSetup = coreMock.createSetup();
const coreStart = coreMock.createStart();
Expand All @@ -43,6 +44,7 @@ const appDependencies: AppDependencies = {
savedObjectsPlugin: savedObjectsPluginMock.createStartContract(),
share: { urlGenerators: { getUrlGenerator: jest.fn() } } as unknown as SharePluginStart,
ml: {} as GetMlSharedImportsReturnType,
triggersActionsUi: {} as jest.Mocked<TriggersAndActionsUIPublicPluginStart>,
};

export const useAppDependencies = () => {
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/transform/public/app/app_dependencies.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { useKibana } from '../../../../../src/plugins/kibana_react/public';
import type { Storage } from '../../../../../src/plugins/kibana_utils/public';

import type { GetMlSharedImportsReturnType } from '../shared_imports';
import type { TriggersAndActionsUIPublicPluginStart } from '../../../triggers_actions_ui/public';

export interface AppDependencies {
application: CoreStart['application'];
Expand All @@ -34,6 +35,7 @@ export interface AppDependencies {
share: SharePluginStart;
ml: GetMlSharedImportsReturnType;
spaces?: SpacesPluginStart;
triggersActionsUi: TriggersAndActionsUIPublicPluginStart;
}

export const useAppDependencies = () => {
Expand Down
9 changes: 5 additions & 4 deletions x-pack/plugins/transform/public/app/common/transform_list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
* 2.0.
*/

import { EuiTableActionsColumnType } from '@elastic/eui';

import { TransformConfigUnion, TransformId } from '../../../common/types/transform';
import { TransformStats } from '../../../common/types/transform_stats';
import type { EuiTableActionsColumnType } from '@elastic/eui';
import type { TransformConfigUnion, TransformId } from '../../../common/types/transform';
import type { TransformStats } from '../../../common/types/transform_stats';
import type { TransformHealthAlertRule } from '../../../common/types/alerting';

// Used to pass on attribute names to table columns
export enum TRANSFORM_LIST_COLUMN {
Expand All @@ -21,6 +21,7 @@ export interface TransformListRow {
config: TransformConfigUnion;
mode?: string; // added property on client side to allow filtering by this field
stats: TransformStats;
alerting_rules?: TransformHealthAlertRule[];
}

// The single Action type is not exported as is
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ export const useGetTransforms = (
mode:
typeof config.sync !== 'undefined' ? TRANSFORM_MODE.CONTINUOUS : TRANSFORM_MODE.BATCH,
stats,
alerting_rules: config.alerting_rules,
});
return reducedtableRows;
}, [] as TransformListRow[]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ interface Authorization {
capabilities: Capabilities;
}

const initialCapabalities: Capabilities = {
const initialCapabilities: Capabilities = {
canGetTransform: false,
canDeleteTransform: false,
canPreviewTransform: false,
canCreateTransform: false,
canStartStopTransform: false,
canCreateTransformAlerts: false,
canUseTransformAlerts: false,
};

const initialValue: Authorization = {
Expand All @@ -35,7 +37,7 @@ const initialValue: Authorization = {
hasAllPrivileges: false,
missingPrivileges: {},
},
capabilities: initialCapabalities,
capabilities: initialCapabilities,
};

export const AuthorizationContext = createContext<Authorization>({ ...initialValue });
Expand All @@ -58,7 +60,7 @@ export const AuthorizationProvider = ({ privilegesEndpoint, children }: Props) =
const value = {
isLoading,
privileges: isLoading ? { ...initialValue.privileges } : privilegesData,
capabilities: { ...initialCapabalities },
capabilities: { ...initialCapabilities },
apiError: error ? (error as Error) : null,
};

Expand All @@ -85,6 +87,10 @@ export const AuthorizationProvider = ({ privilegesEndpoint, children }: Props) =
hasPrivilege(['cluster', 'cluster:admin/transform/start_task']) &&
hasPrivilege(['cluster', 'cluster:admin/transform/stop']);

value.capabilities.canCreateTransformAlerts = value.capabilities.canCreateTransform;

value.capabilities.canUseTransformAlerts = value.capabilities.canGetTransform;

return (
<AuthorizationContext.Provider value={{ ...value }}>{children}</AuthorizationContext.Provider>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export interface Capabilities {
canPreviewTransform: boolean;
canCreateTransform: boolean;
canStartStopTransform: boolean;
canCreateTransformAlerts: boolean;
canUseTransformAlerts: boolean;
}

export type Privilege = [string, string];
Expand Down Expand Up @@ -67,6 +69,14 @@ export function createCapabilityFailureMessage(
defaultMessage: 'You do not have permission to create transforms.',
});
break;
case 'canCreateTransformAlerts':
message = i18n.translate(
'xpack.transform.capability.noPermission.canCreateTransformAlertsTooltip',
{
defaultMessage: 'You do not have permission to create transform alert rules.',
}
);
break;
case 'canStartStopTransform':
message = i18n.translate(
'xpack.transform.capability.noPermission.startOrStopTransformTooltip',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export async function mountManagementSection(
const startServices = await getStartServices();
const [core, plugins] = startServices;
const { application, chrome, docLinks, i18n, overlays, savedObjects, uiSettings } = core;
const { data, share, spaces } = plugins;
const { data, share, spaces, triggersActionsUi } = plugins;
const { docTitle } = chrome;

// Initialize services
Expand All @@ -55,6 +55,7 @@ export async function mountManagementSection(
share,
spaces,
ml: await getMlSharedImports(),
triggersActionsUi,
};

const unmountAppCallback = renderApp(element, appDependencies);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
} from '@elastic/eui';

import { APP_CREATE_TRANSFORM_CLUSTER_PRIVILEGES } from '../../../../common/constants';
import { TransformPivotConfig } from '../../../../common/types/transform';
import { TransformConfigUnion } from '../../../../common/types/transform';

import { isHttpFetchError } from '../../common/request';
import { useApi } from '../../hooks/use_api';
Expand Down Expand Up @@ -50,7 +50,7 @@ export const CloneTransformSection: FC<Props> = ({ match, location }) => {

const transformId = match.params.transformId;

const [transformConfig, setTransformConfig] = useState<TransformPivotConfig>();
const [transformConfig, setTransformConfig] = useState<TransformConfigUnion>();
const [errorMessage, setErrorMessage] = useState<string>();
const [isInitialized, setIsInitialized] = useState(false);
const { error: searchItemsError, searchItems, setSavedObjectId } = useSearchItems(undefined);
Expand Down
Loading