[Alerting] Hides the alert
SavedObjects type (#66719)
* make alert saved object type hidden * fix support for hidden alert type in alerting tests * updated api docs * fixed some missing types and unused imports * fixed test broken by field rename * added support for including hidden types in saved objects client * fixed merge conflict * cleaned up some test descriptions * adds a getClient api to Encrypted Saved Objects * fixed alerts fixture * added missing plugin type in alerting * removed unused field * chaged ESO api to an options object as per Security teams request * fixed usage of eso client * fixed typos and oversights * split alerts file into two - for actions and alerts
This commit is contained in:
parent
6ad8c91894
commit
65370c70d2
|
@ -88,8 +88,8 @@ export interface AlertingPluginsSetup {
|
|||
}
|
||||
export interface AlertingPluginsStart {
|
||||
actions: ActionsPluginStartContract;
|
||||
encryptedSavedObjects: EncryptedSavedObjectsPluginStart;
|
||||
taskManager: TaskManagerStartContract;
|
||||
encryptedSavedObjects: EncryptedSavedObjectsPluginStart;
|
||||
}
|
||||
|
||||
export class AlertingPlugin {
|
||||
|
@ -126,6 +126,7 @@ export class AlertingPlugin {
|
|||
this.licenseState = new LicenseState(plugins.licensing.license$);
|
||||
this.spaces = plugins.spaces?.spacesService;
|
||||
this.security = plugins.security;
|
||||
|
||||
this.isESOUsingEphemeralEncryptionKey =
|
||||
plugins.encryptedSavedObjects.usingEphemeralEncryptionKey;
|
||||
|
||||
|
@ -164,7 +165,7 @@ export class AlertingPlugin {
|
|||
});
|
||||
}
|
||||
|
||||
core.http.registerRouteHandlerContext('alerting', this.createRouteHandlerContext());
|
||||
core.http.registerRouteHandlerContext('alerting', this.createRouteHandlerContext(core));
|
||||
|
||||
// Routes
|
||||
const router = core.http.createRouter();
|
||||
|
@ -201,7 +202,9 @@ export class AlertingPlugin {
|
|||
security,
|
||||
} = this;
|
||||
|
||||
const encryptedSavedObjectsClient = plugins.encryptedSavedObjects.getClient();
|
||||
const encryptedSavedObjectsClient = plugins.encryptedSavedObjects.getClient({
|
||||
includedHiddenTypes: ['alert'],
|
||||
});
|
||||
|
||||
alertsClientFactory.initialize({
|
||||
alertTypeRegistry: alertTypeRegistry!,
|
||||
|
@ -231,26 +234,32 @@ export class AlertingPlugin {
|
|||
return {
|
||||
listTypes: alertTypeRegistry!.list.bind(this.alertTypeRegistry!),
|
||||
// Ability to get an alerts client from legacy code
|
||||
getAlertsClientWithRequest(request: KibanaRequest) {
|
||||
getAlertsClientWithRequest: (request: KibanaRequest) => {
|
||||
if (isESOUsingEphemeralEncryptionKey === true) {
|
||||
throw new Error(
|
||||
`Unable to create alerts client due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml`
|
||||
);
|
||||
}
|
||||
return alertsClientFactory!.create(request, core.savedObjects.getScopedClient(request));
|
||||
return alertsClientFactory!.create(
|
||||
request,
|
||||
this.getScopedClientWithAlertSavedObjectType(core.savedObjects, request)
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private createRouteHandlerContext = (): IContextProvider<
|
||||
RequestHandler<unknown, unknown, unknown>,
|
||||
'alerting'
|
||||
> => {
|
||||
private createRouteHandlerContext = (
|
||||
core: CoreSetup
|
||||
): IContextProvider<RequestHandler<unknown, unknown, unknown>, 'alerting'> => {
|
||||
const { alertTypeRegistry, alertsClientFactory } = this;
|
||||
return async function alertsRouteHandlerContext(context, request) {
|
||||
return async (context, request) => {
|
||||
const [{ savedObjects }] = await core.getStartServices();
|
||||
return {
|
||||
getAlertsClient: () => {
|
||||
return alertsClientFactory!.create(request, context.core.savedObjects.client);
|
||||
return alertsClientFactory!.create(
|
||||
request,
|
||||
this.getScopedClientWithAlertSavedObjectType(savedObjects, request)
|
||||
);
|
||||
},
|
||||
listTypes: alertTypeRegistry!.list.bind(alertTypeRegistry!),
|
||||
};
|
||||
|
@ -263,7 +272,7 @@ export class AlertingPlugin {
|
|||
): (request: KibanaRequest) => Services {
|
||||
return request => ({
|
||||
callCluster: elasticsearch.legacy.client.asScoped(request).callAsCurrentUser,
|
||||
savedObjectsClient: savedObjects.getScopedClient(request),
|
||||
savedObjectsClient: this.getScopedClientWithAlertSavedObjectType(savedObjects, request),
|
||||
getScopedCallCluster(clusterClient: IClusterClient) {
|
||||
return clusterClient.asScoped(request).callAsCurrentUser;
|
||||
},
|
||||
|
@ -278,6 +287,13 @@ export class AlertingPlugin {
|
|||
return this.spaces && spaceId ? this.spaces.getBasePath(spaceId) : this.serverBasePath!;
|
||||
};
|
||||
|
||||
private getScopedClientWithAlertSavedObjectType(
|
||||
savedObjects: SavedObjectsServiceStart,
|
||||
request: KibanaRequest
|
||||
) {
|
||||
return savedObjects.getScopedClient(request, { includedHiddenTypes: ['alert'] });
|
||||
}
|
||||
|
||||
public stop() {
|
||||
if (this.licenseState) {
|
||||
this.licenseState.clean();
|
||||
|
|
|
@ -14,7 +14,7 @@ export function setupSavedObjects(
|
|||
) {
|
||||
savedObjects.registerType({
|
||||
name: 'alert',
|
||||
hidden: false,
|
||||
hidden: true,
|
||||
namespaceType: 'single',
|
||||
mappings: mappings.alert,
|
||||
});
|
||||
|
|
|
@ -68,7 +68,19 @@ router.get(
|
|||
...
|
||||
```
|
||||
|
||||
5. To retrieve Saved Object with decrypted content use the dedicated `getDecryptedAsInternalUser` API method.
|
||||
5. Instantiate an EncryptedSavedObjects client so that you can interact with Saved Objects whose content has been encrypted.
|
||||
|
||||
```typescript
|
||||
const esoClient = encryptedSavedObjects.getClient();
|
||||
```
|
||||
|
||||
If your SavedObject type is a _hidden_ type, then you will have to specify it as an included type:
|
||||
|
||||
```typescript
|
||||
const esoClient = encryptedSavedObjects.getClient({ includedHiddenTypes: ['myHiddenType'] });
|
||||
```
|
||||
|
||||
6. To retrieve Saved Object with decrypted content use the dedicated `getDecryptedAsInternalUser` API method.
|
||||
|
||||
**Note:** As name suggests the method will retrieve the encrypted values and decrypt them on behalf of the internal Kibana
|
||||
user to make it possible to use this method even when user request context is not available (e.g. in background tasks).
|
||||
|
@ -77,7 +89,7 @@ and preferably only as a part of the Kibana server routines that are outside of
|
|||
user has control over.
|
||||
|
||||
```typescript
|
||||
const savedObjectWithDecryptedContent = await encryptedSavedObjects.getDecryptedAsInternalUser(
|
||||
const savedObjectWithDecryptedContent = await esoClient.getDecryptedAsInternalUser(
|
||||
'my-saved-object-type',
|
||||
'saved-object-id'
|
||||
);
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import { EncryptedSavedObjectsPluginSetup, EncryptedSavedObjectsPluginStart } from './plugin';
|
||||
import { EncryptedSavedObjectsClient } from './saved_objects';
|
||||
import { EncryptedSavedObjectsClient, EncryptedSavedObjectsClientOptions } from './saved_objects';
|
||||
|
||||
function createEncryptedSavedObjectsSetupMock() {
|
||||
return {
|
||||
|
@ -18,11 +18,11 @@ function createEncryptedSavedObjectsSetupMock() {
|
|||
function createEncryptedSavedObjectsStartMock() {
|
||||
return {
|
||||
isEncryptionError: jest.fn(),
|
||||
getClient: jest.fn(() => createEncryptedSavedObjectsClienttMock()),
|
||||
getClient: jest.fn(opts => createEncryptedSavedObjectsClienttMock(opts)),
|
||||
} as jest.Mocked<EncryptedSavedObjectsPluginStart>;
|
||||
}
|
||||
|
||||
function createEncryptedSavedObjectsClienttMock() {
|
||||
function createEncryptedSavedObjectsClienttMock(opts?: EncryptedSavedObjectsClientOptions) {
|
||||
return {
|
||||
getDecryptedAsInternalUser: jest.fn(),
|
||||
} as jest.Mocked<EncryptedSavedObjectsClient>;
|
||||
|
|
|
@ -14,7 +14,7 @@ import {
|
|||
EncryptionError,
|
||||
} from './crypto';
|
||||
import { EncryptedSavedObjectsAuditLogger } from './audit';
|
||||
import { SavedObjectsSetup, setupSavedObjects } from './saved_objects';
|
||||
import { setupSavedObjects, ClientInstanciator } from './saved_objects';
|
||||
|
||||
export interface PluginsSetup {
|
||||
security?: SecurityPluginSetup;
|
||||
|
@ -28,7 +28,7 @@ export interface EncryptedSavedObjectsPluginSetup {
|
|||
|
||||
export interface EncryptedSavedObjectsPluginStart {
|
||||
isEncryptionError: (error: Error) => boolean;
|
||||
getClient: SavedObjectsSetup;
|
||||
getClient: ClientInstanciator;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -46,7 +46,7 @@ export interface LegacyAPI {
|
|||
*/
|
||||
export class Plugin {
|
||||
private readonly logger: Logger;
|
||||
private savedObjectsSetup!: SavedObjectsSetup;
|
||||
private savedObjectsSetup!: ClientInstanciator;
|
||||
|
||||
private legacyAPI?: LegacyAPI;
|
||||
private readonly getLegacyAPI = () => {
|
||||
|
@ -95,7 +95,7 @@ export class Plugin {
|
|||
this.logger.debug('Starting plugin');
|
||||
return {
|
||||
isEncryptionError: (error: Error) => error instanceof EncryptionError,
|
||||
getClient: (includedHiddenTypes?: string[]) => this.savedObjectsSetup(includedHiddenTypes),
|
||||
getClient: (options = {}) => this.savedObjectsSetup(options),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { SavedObjectsSetup, setupSavedObjects } from '.';
|
||||
import { ClientInstanciator, setupSavedObjects } from '.';
|
||||
|
||||
import {
|
||||
coreMock,
|
||||
|
@ -24,7 +24,7 @@ import {
|
|||
import { EncryptedSavedObjectsService } from '../crypto';
|
||||
|
||||
describe('#setupSavedObjects', () => {
|
||||
let setupContract: SavedObjectsSetup;
|
||||
let setupContract: ClientInstanciator;
|
||||
let coreStartMock: ReturnType<typeof coreMock.createStart>;
|
||||
let coreSetupMock: ReturnType<typeof coreMock.createSetup>;
|
||||
let mockSavedObjectsRepository: jest.Mocked<ISavedObjectsRepository>;
|
||||
|
@ -91,7 +91,7 @@ describe('#setupSavedObjects', () => {
|
|||
|
||||
describe('#setupContract', () => {
|
||||
it('includes hiddenTypes when specified', async () => {
|
||||
await setupContract(['hiddenType']);
|
||||
await setupContract({ includedHiddenTypes: ['hiddenType'] });
|
||||
expect(coreStartMock.savedObjects.createInternalRepository).toHaveBeenCalledWith([
|
||||
'hiddenType',
|
||||
]);
|
||||
|
|
|
@ -23,7 +23,13 @@ interface SetupSavedObjectsParams {
|
|||
getStartServices: StartServicesAccessor;
|
||||
}
|
||||
|
||||
export type SavedObjectsSetup = (includedHiddenTypes?: string[]) => EncryptedSavedObjectsClient;
|
||||
export type ClientInstanciator = (
|
||||
options?: EncryptedSavedObjectsClientOptions
|
||||
) => EncryptedSavedObjectsClient;
|
||||
|
||||
export interface EncryptedSavedObjectsClientOptions {
|
||||
includedHiddenTypes?: string[];
|
||||
}
|
||||
|
||||
export interface EncryptedSavedObjectsClient {
|
||||
getDecryptedAsInternalUser: <T = unknown>(
|
||||
|
@ -38,7 +44,7 @@ export function setupSavedObjects({
|
|||
savedObjects,
|
||||
security,
|
||||
getStartServices,
|
||||
}: SetupSavedObjectsParams): SavedObjectsSetup {
|
||||
}: SetupSavedObjectsParams): ClientInstanciator {
|
||||
// Register custom saved object client that will encrypt, decrypt and strip saved object
|
||||
// attributes where appropriate for any saved object repository request. We choose max possible
|
||||
// priority for this wrapper to allow all other wrappers to set proper `namespace` for the Saved
|
||||
|
@ -56,11 +62,11 @@ export function setupSavedObjects({
|
|||
})
|
||||
);
|
||||
|
||||
return (includedHiddenTypes?: string[]) => {
|
||||
return clientOpts => {
|
||||
const internalRepositoryAndTypeRegistryPromise = getStartServices().then(
|
||||
([core]) =>
|
||||
[
|
||||
core.savedObjects.createInternalRepository(includedHiddenTypes),
|
||||
core.savedObjects.createInternalRepository(clientOpts?.includedHiddenTypes),
|
||||
core.savedObjects.getTypeRegistry(),
|
||||
] as [ISavedObjectsRepository, ISavedObjectTypeRegistry]
|
||||
);
|
||||
|
|
|
@ -19,7 +19,6 @@ import { SpacesPluginSetup } from '../../../../../../../plugins/spaces/server';
|
|||
interface FixtureSetupDeps {
|
||||
spaces?: SpacesPluginSetup;
|
||||
}
|
||||
|
||||
interface FixtureStartDeps {
|
||||
encryptedSavedObjects: EncryptedSavedObjectsPluginStart;
|
||||
}
|
||||
|
@ -44,12 +43,14 @@ export class FixturePlugin implements Plugin<void, void, FixtureSetupDeps, Fixtu
|
|||
): Promise<IKibanaResponse<any>> {
|
||||
try {
|
||||
let namespace: string | undefined;
|
||||
const [, { encryptedSavedObjects }] = await core.getStartServices();
|
||||
if (spaces && req.body.spaceId) {
|
||||
namespace = spaces.spacesService.spaceIdToNamespace(req.body.spaceId);
|
||||
}
|
||||
const [, { encryptedSavedObjects }] = await core.getStartServices();
|
||||
await encryptedSavedObjects
|
||||
.getClient()
|
||||
.getClient({
|
||||
includedHiddenTypes: ['alert'],
|
||||
})
|
||||
.getDecryptedAsInternalUser(req.body.type, req.body.id, {
|
||||
namespace,
|
||||
});
|
||||
|
|
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { CoreSetup } from 'kibana/server';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { FixtureStartDeps, FixtureSetupDeps } from './plugin';
|
||||
import { ActionType, ActionTypeExecutorOptions } from '../../../../../../../plugins/actions/server';
|
||||
|
||||
export function defineActionTypes(
|
||||
core: CoreSetup<FixtureStartDeps>,
|
||||
{ actions }: Pick<FixtureSetupDeps, 'actions'>
|
||||
) {
|
||||
const clusterClient = core.elasticsearch.adminClient;
|
||||
|
||||
// Action types
|
||||
const noopActionType: ActionType = {
|
||||
id: 'test.noop',
|
||||
name: 'Test: Noop',
|
||||
minimumLicenseRequired: 'gold',
|
||||
async executor() {
|
||||
return { status: 'ok', actionId: '' };
|
||||
},
|
||||
};
|
||||
const indexRecordActionType: ActionType = {
|
||||
id: 'test.index-record',
|
||||
name: 'Test: Index Record',
|
||||
minimumLicenseRequired: 'gold',
|
||||
validate: {
|
||||
params: schema.object({
|
||||
index: schema.string(),
|
||||
reference: schema.string(),
|
||||
message: schema.string(),
|
||||
}),
|
||||
config: schema.object({
|
||||
unencrypted: schema.string(),
|
||||
}),
|
||||
secrets: schema.object({
|
||||
encrypted: schema.string(),
|
||||
}),
|
||||
},
|
||||
async executor({ config, secrets, params, services, actionId }: ActionTypeExecutorOptions) {
|
||||
await services.callCluster('index', {
|
||||
index: params.index,
|
||||
refresh: 'wait_for',
|
||||
body: {
|
||||
params,
|
||||
config,
|
||||
secrets,
|
||||
reference: params.reference,
|
||||
source: 'action:test.index-record',
|
||||
},
|
||||
});
|
||||
return { status: 'ok', actionId };
|
||||
},
|
||||
};
|
||||
const failingActionType: ActionType = {
|
||||
id: 'test.failing',
|
||||
name: 'Test: Failing',
|
||||
minimumLicenseRequired: 'gold',
|
||||
validate: {
|
||||
params: schema.object({
|
||||
index: schema.string(),
|
||||
reference: schema.string(),
|
||||
}),
|
||||
},
|
||||
async executor({ config, secrets, params, services }: ActionTypeExecutorOptions) {
|
||||
await services.callCluster('index', {
|
||||
index: params.index,
|
||||
refresh: 'wait_for',
|
||||
body: {
|
||||
params,
|
||||
config,
|
||||
secrets,
|
||||
reference: params.reference,
|
||||
source: 'action:test.failing',
|
||||
},
|
||||
});
|
||||
throw new Error(`expected failure for ${params.index} ${params.reference}`);
|
||||
},
|
||||
};
|
||||
const rateLimitedActionType: ActionType = {
|
||||
id: 'test.rate-limit',
|
||||
name: 'Test: Rate Limit',
|
||||
minimumLicenseRequired: 'gold',
|
||||
maxAttempts: 2,
|
||||
validate: {
|
||||
params: schema.object({
|
||||
index: schema.string(),
|
||||
reference: schema.string(),
|
||||
retryAt: schema.number(),
|
||||
}),
|
||||
},
|
||||
async executor({ config, params, services }: ActionTypeExecutorOptions) {
|
||||
await services.callCluster('index', {
|
||||
index: params.index,
|
||||
refresh: 'wait_for',
|
||||
body: {
|
||||
params,
|
||||
config,
|
||||
reference: params.reference,
|
||||
source: 'action:test.rate-limit',
|
||||
},
|
||||
});
|
||||
return {
|
||||
status: 'error',
|
||||
retry: new Date(params.retryAt),
|
||||
actionId: '',
|
||||
};
|
||||
},
|
||||
};
|
||||
const authorizationActionType: ActionType = {
|
||||
id: 'test.authorization',
|
||||
name: 'Test: Authorization',
|
||||
minimumLicenseRequired: 'gold',
|
||||
validate: {
|
||||
params: schema.object({
|
||||
callClusterAuthorizationIndex: schema.string(),
|
||||
savedObjectsClientType: schema.string(),
|
||||
savedObjectsClientId: schema.string(),
|
||||
index: schema.string(),
|
||||
reference: schema.string(),
|
||||
}),
|
||||
},
|
||||
async executor({ params, services, actionId }: ActionTypeExecutorOptions) {
|
||||
// Call cluster
|
||||
let callClusterSuccess = false;
|
||||
let callClusterError;
|
||||
try {
|
||||
await services.callCluster('index', {
|
||||
index: params.callClusterAuthorizationIndex,
|
||||
refresh: 'wait_for',
|
||||
body: {
|
||||
param1: 'test',
|
||||
},
|
||||
});
|
||||
callClusterSuccess = true;
|
||||
} catch (e) {
|
||||
callClusterError = e;
|
||||
}
|
||||
// Call scoped cluster
|
||||
const callScopedCluster = services.getScopedCallCluster(clusterClient);
|
||||
let callScopedClusterSuccess = false;
|
||||
let callScopedClusterError;
|
||||
try {
|
||||
await callScopedCluster('index', {
|
||||
index: params.callClusterAuthorizationIndex,
|
||||
refresh: 'wait_for',
|
||||
body: {
|
||||
param1: 'test',
|
||||
},
|
||||
});
|
||||
callScopedClusterSuccess = true;
|
||||
} catch (e) {
|
||||
callScopedClusterError = e;
|
||||
}
|
||||
// Saved objects client
|
||||
let savedObjectsClientSuccess = false;
|
||||
let savedObjectsClientError;
|
||||
try {
|
||||
await services.savedObjectsClient.get(
|
||||
params.savedObjectsClientType,
|
||||
params.savedObjectsClientId
|
||||
);
|
||||
savedObjectsClientSuccess = true;
|
||||
} catch (e) {
|
||||
savedObjectsClientError = e;
|
||||
}
|
||||
// Save the result
|
||||
await services.callCluster('index', {
|
||||
index: params.index,
|
||||
refresh: 'wait_for',
|
||||
body: {
|
||||
state: {
|
||||
callClusterSuccess,
|
||||
callClusterError,
|
||||
callScopedClusterSuccess,
|
||||
callScopedClusterError,
|
||||
savedObjectsClientSuccess,
|
||||
savedObjectsClientError,
|
||||
},
|
||||
params,
|
||||
reference: params.reference,
|
||||
source: 'action:test.authorization',
|
||||
},
|
||||
});
|
||||
return {
|
||||
actionId,
|
||||
status: 'ok',
|
||||
};
|
||||
},
|
||||
};
|
||||
actions.registerType(noopActionType);
|
||||
actions.registerType(indexRecordActionType);
|
||||
actions.registerType(failingActionType);
|
||||
actions.registerType(rateLimitedActionType);
|
||||
actions.registerType(authorizationActionType);
|
||||
}
|
|
@ -0,0 +1,298 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { CoreSetup } from 'kibana/server';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { times } from 'lodash';
|
||||
import { FixtureStartDeps, FixtureSetupDeps } from './plugin';
|
||||
import { AlertType, AlertExecutorOptions } from '../../../../../../../plugins/alerting/server';
|
||||
|
||||
export function defineAlertTypes(
|
||||
core: CoreSetup<FixtureStartDeps>,
|
||||
{ alerting }: Pick<FixtureSetupDeps, 'alerting'>
|
||||
) {
|
||||
const clusterClient = core.elasticsearch.adminClient;
|
||||
const alwaysFiringAlertType: AlertType = {
|
||||
id: 'test.always-firing',
|
||||
name: 'Test: Always Firing',
|
||||
actionGroups: [
|
||||
{ id: 'default', name: 'Default' },
|
||||
{ id: 'other', name: 'Other' },
|
||||
],
|
||||
producer: 'alerting',
|
||||
defaultActionGroupId: 'default',
|
||||
actionVariables: {
|
||||
state: [{ name: 'instanceStateValue', description: 'the instance state value' }],
|
||||
context: [{ name: 'instanceContextValue', description: 'the instance context value' }],
|
||||
},
|
||||
async executor(alertExecutorOptions: AlertExecutorOptions) {
|
||||
const {
|
||||
services,
|
||||
params,
|
||||
state,
|
||||
alertId,
|
||||
spaceId,
|
||||
namespace,
|
||||
name,
|
||||
tags,
|
||||
createdBy,
|
||||
updatedBy,
|
||||
} = alertExecutorOptions;
|
||||
let group = 'default';
|
||||
const alertInfo = { alertId, spaceId, namespace, name, tags, createdBy, updatedBy };
|
||||
|
||||
if (params.groupsToScheduleActionsInSeries) {
|
||||
const index = state.groupInSeriesIndex || 0;
|
||||
group = params.groupsToScheduleActionsInSeries[index];
|
||||
}
|
||||
|
||||
if (group) {
|
||||
services
|
||||
.alertInstanceFactory('1')
|
||||
.replaceState({ instanceStateValue: true })
|
||||
.scheduleActions(group, {
|
||||
instanceContextValue: true,
|
||||
});
|
||||
}
|
||||
await services.callCluster('index', {
|
||||
index: params.index,
|
||||
refresh: 'wait_for',
|
||||
body: {
|
||||
state,
|
||||
params,
|
||||
reference: params.reference,
|
||||
source: 'alert:test.always-firing',
|
||||
alertInfo,
|
||||
},
|
||||
});
|
||||
return {
|
||||
globalStateValue: true,
|
||||
groupInSeriesIndex: (state.groupInSeriesIndex || 0) + 1,
|
||||
};
|
||||
},
|
||||
};
|
||||
// Alert types
|
||||
const cumulativeFiringAlertType: AlertType = {
|
||||
id: 'test.cumulative-firing',
|
||||
name: 'Test: Cumulative Firing',
|
||||
actionGroups: [
|
||||
{ id: 'default', name: 'Default' },
|
||||
{ id: 'other', name: 'Other' },
|
||||
],
|
||||
producer: 'alerting',
|
||||
defaultActionGroupId: 'default',
|
||||
async executor(alertExecutorOptions: AlertExecutorOptions) {
|
||||
const { services, state } = alertExecutorOptions;
|
||||
const group = 'default';
|
||||
|
||||
const runCount = (state.runCount || 0) + 1;
|
||||
|
||||
times(runCount, index => {
|
||||
services
|
||||
.alertInstanceFactory(`instance-${index}`)
|
||||
.replaceState({ instanceStateValue: true })
|
||||
.scheduleActions(group);
|
||||
});
|
||||
|
||||
return {
|
||||
runCount,
|
||||
};
|
||||
},
|
||||
};
|
||||
const neverFiringAlertType: AlertType = {
|
||||
id: 'test.never-firing',
|
||||
name: 'Test: Never firing',
|
||||
actionGroups: [
|
||||
{
|
||||
id: 'default',
|
||||
name: 'Default',
|
||||
},
|
||||
],
|
||||
producer: 'alerting',
|
||||
defaultActionGroupId: 'default',
|
||||
async executor({ services, params, state }: AlertExecutorOptions) {
|
||||
await services.callCluster('index', {
|
||||
index: params.index,
|
||||
refresh: 'wait_for',
|
||||
body: {
|
||||
state,
|
||||
params,
|
||||
reference: params.reference,
|
||||
source: 'alert:test.never-firing',
|
||||
},
|
||||
});
|
||||
return {
|
||||
globalStateValue: true,
|
||||
};
|
||||
},
|
||||
};
|
||||
const failingAlertType: AlertType = {
|
||||
id: 'test.failing',
|
||||
name: 'Test: Failing',
|
||||
actionGroups: [
|
||||
{
|
||||
id: 'default',
|
||||
name: 'Default',
|
||||
},
|
||||
],
|
||||
producer: 'alerting',
|
||||
defaultActionGroupId: 'default',
|
||||
async executor({ services, params, state }: AlertExecutorOptions) {
|
||||
await services.callCluster('index', {
|
||||
index: params.index,
|
||||
refresh: 'wait_for',
|
||||
body: {
|
||||
state,
|
||||
params,
|
||||
reference: params.reference,
|
||||
source: 'alert:test.failing',
|
||||
},
|
||||
});
|
||||
throw new Error('Failed to execute alert type');
|
||||
},
|
||||
};
|
||||
const authorizationAlertType: AlertType = {
|
||||
id: 'test.authorization',
|
||||
name: 'Test: Authorization',
|
||||
actionGroups: [
|
||||
{
|
||||
id: 'default',
|
||||
name: 'Default',
|
||||
},
|
||||
],
|
||||
defaultActionGroupId: 'default',
|
||||
producer: 'alerting',
|
||||
validate: {
|
||||
params: schema.object({
|
||||
callClusterAuthorizationIndex: schema.string(),
|
||||
savedObjectsClientType: schema.string(),
|
||||
savedObjectsClientId: schema.string(),
|
||||
index: schema.string(),
|
||||
reference: schema.string(),
|
||||
}),
|
||||
},
|
||||
async executor({ services, params, state }: AlertExecutorOptions) {
|
||||
// Call cluster
|
||||
let callClusterSuccess = false;
|
||||
let callClusterError;
|
||||
try {
|
||||
await services.callCluster('index', {
|
||||
index: params.callClusterAuthorizationIndex,
|
||||
refresh: 'wait_for',
|
||||
body: {
|
||||
param1: 'test',
|
||||
},
|
||||
});
|
||||
callClusterSuccess = true;
|
||||
} catch (e) {
|
||||
callClusterError = e;
|
||||
}
|
||||
// Call scoped cluster
|
||||
const callScopedCluster = services.getScopedCallCluster(clusterClient);
|
||||
let callScopedClusterSuccess = false;
|
||||
let callScopedClusterError;
|
||||
try {
|
||||
await callScopedCluster('index', {
|
||||
index: params.callClusterAuthorizationIndex,
|
||||
refresh: 'wait_for',
|
||||
body: {
|
||||
param1: 'test',
|
||||
},
|
||||
});
|
||||
callScopedClusterSuccess = true;
|
||||
} catch (e) {
|
||||
callScopedClusterError = e;
|
||||
}
|
||||
// Saved objects client
|
||||
let savedObjectsClientSuccess = false;
|
||||
let savedObjectsClientError;
|
||||
try {
|
||||
await services.savedObjectsClient.get(
|
||||
params.savedObjectsClientType,
|
||||
params.savedObjectsClientId
|
||||
);
|
||||
savedObjectsClientSuccess = true;
|
||||
} catch (e) {
|
||||
savedObjectsClientError = e;
|
||||
}
|
||||
// Save the result
|
||||
await services.callCluster('index', {
|
||||
index: params.index,
|
||||
refresh: 'wait_for',
|
||||
body: {
|
||||
state: {
|
||||
callClusterSuccess,
|
||||
callClusterError,
|
||||
callScopedClusterSuccess,
|
||||
callScopedClusterError,
|
||||
savedObjectsClientSuccess,
|
||||
savedObjectsClientError,
|
||||
},
|
||||
params,
|
||||
reference: params.reference,
|
||||
source: 'alert:test.authorization',
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
const validationAlertType: AlertType = {
|
||||
id: 'test.validation',
|
||||
name: 'Test: Validation',
|
||||
actionGroups: [
|
||||
{
|
||||
id: 'default',
|
||||
name: 'Default',
|
||||
},
|
||||
],
|
||||
producer: 'alerting',
|
||||
defaultActionGroupId: 'default',
|
||||
validate: {
|
||||
params: schema.object({
|
||||
param1: schema.string(),
|
||||
}),
|
||||
},
|
||||
async executor({ services, params, state }: AlertExecutorOptions) {},
|
||||
};
|
||||
const noopAlertType: AlertType = {
|
||||
id: 'test.noop',
|
||||
name: 'Test: Noop',
|
||||
actionGroups: [{ id: 'default', name: 'Default' }],
|
||||
producer: 'alerting',
|
||||
defaultActionGroupId: 'default',
|
||||
async executor({ services, params, state }: AlertExecutorOptions) {},
|
||||
};
|
||||
const onlyContextVariablesAlertType: AlertType = {
|
||||
id: 'test.onlyContextVariables',
|
||||
name: 'Test: Only Context Variables',
|
||||
actionGroups: [{ id: 'default', name: 'Default' }],
|
||||
producer: 'alerting',
|
||||
defaultActionGroupId: 'default',
|
||||
actionVariables: {
|
||||
context: [{ name: 'aContextVariable', description: 'this is a context variable' }],
|
||||
},
|
||||
async executor(opts: AlertExecutorOptions) {},
|
||||
};
|
||||
const onlyStateVariablesAlertType: AlertType = {
|
||||
id: 'test.onlyStateVariables',
|
||||
name: 'Test: Only State Variables',
|
||||
actionGroups: [{ id: 'default', name: 'Default' }],
|
||||
producer: 'alerting',
|
||||
defaultActionGroupId: 'default',
|
||||
actionVariables: {
|
||||
state: [{ name: 'aStateVariable', description: 'this is a state variable' }],
|
||||
},
|
||||
async executor(opts: AlertExecutorOptions) {},
|
||||
};
|
||||
alerting.registerType(alwaysFiringAlertType);
|
||||
alerting.registerType(cumulativeFiringAlertType);
|
||||
alerting.registerType(neverFiringAlertType);
|
||||
alerting.registerType(failingAlertType);
|
||||
alerting.registerType(validationAlertType);
|
||||
alerting.registerType(authorizationAlertType);
|
||||
alerting.registerType(noopAlertType);
|
||||
alerting.registerType(onlyContextVariablesAlertType);
|
||||
alerting.registerType(onlyStateVariablesAlertType);
|
||||
}
|
|
@ -5,22 +5,21 @@
|
|||
*/
|
||||
|
||||
import { Plugin, CoreSetup } from 'kibana/server';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { times } from 'lodash';
|
||||
import { PluginSetupContract as ActionsPluginSetup } from '../../../../../../../plugins/actions/server/plugin';
|
||||
import { PluginSetupContract as AlertingPluginSetup } from '../../../../../../../plugins/alerting/server/plugin';
|
||||
import { EncryptedSavedObjectsPluginStart } from '../../../../../../../plugins/encrypted_saved_objects/server';
|
||||
import { PluginSetupContract as FeaturesPluginSetup } from '../../../../../../../plugins/features/server';
|
||||
import { ActionType, ActionTypeExecutorOptions } from '../../../../../../../plugins/actions/server';
|
||||
import { AlertType, AlertExecutorOptions } from '../../../../../../../plugins/alerting/server';
|
||||
import { defineAlertTypes } from './alert_types';
|
||||
import { defineActionTypes } from './action_types';
|
||||
import { defineRoutes } from './routes';
|
||||
|
||||
interface FixtureSetupDeps {
|
||||
export interface FixtureSetupDeps {
|
||||
features: FeaturesPluginSetup;
|
||||
actions: ActionsPluginSetup;
|
||||
alerting: AlertingPluginSetup;
|
||||
}
|
||||
|
||||
interface FixtureStartDeps {
|
||||
export interface FixtureStartDeps {
|
||||
encryptedSavedObjects: EncryptedSavedObjectsPluginStart;
|
||||
}
|
||||
|
||||
|
@ -29,7 +28,6 @@ export class FixturePlugin implements Plugin<void, void, FixtureSetupDeps, Fixtu
|
|||
core: CoreSetup<FixtureStartDeps>,
|
||||
{ features, actions, alerting }: FixtureSetupDeps
|
||||
) {
|
||||
const clusterClient = core.elasticsearch.adminClient;
|
||||
features.registerFeature({
|
||||
id: 'alerting',
|
||||
name: 'Alerting',
|
||||
|
@ -55,469 +53,10 @@ export class FixturePlugin implements Plugin<void, void, FixtureSetupDeps, Fixtu
|
|||
},
|
||||
},
|
||||
});
|
||||
// Action types
|
||||
const noopActionType: ActionType = {
|
||||
id: 'test.noop',
|
||||
name: 'Test: Noop',
|
||||
minimumLicenseRequired: 'gold',
|
||||
async executor() {
|
||||
return { status: 'ok', actionId: '' };
|
||||
},
|
||||
};
|
||||
const indexRecordActionType: ActionType = {
|
||||
id: 'test.index-record',
|
||||
name: 'Test: Index Record',
|
||||
minimumLicenseRequired: 'gold',
|
||||
validate: {
|
||||
params: schema.object({
|
||||
index: schema.string(),
|
||||
reference: schema.string(),
|
||||
message: schema.string(),
|
||||
}),
|
||||
config: schema.object({
|
||||
unencrypted: schema.string(),
|
||||
}),
|
||||
secrets: schema.object({
|
||||
encrypted: schema.string(),
|
||||
}),
|
||||
},
|
||||
async executor({ config, secrets, params, services, actionId }: ActionTypeExecutorOptions) {
|
||||
await services.callCluster('index', {
|
||||
index: params.index,
|
||||
refresh: 'wait_for',
|
||||
body: {
|
||||
params,
|
||||
config,
|
||||
secrets,
|
||||
reference: params.reference,
|
||||
source: 'action:test.index-record',
|
||||
},
|
||||
});
|
||||
return { status: 'ok', actionId };
|
||||
},
|
||||
};
|
||||
const failingActionType: ActionType = {
|
||||
id: 'test.failing',
|
||||
name: 'Test: Failing',
|
||||
minimumLicenseRequired: 'gold',
|
||||
validate: {
|
||||
params: schema.object({
|
||||
index: schema.string(),
|
||||
reference: schema.string(),
|
||||
}),
|
||||
},
|
||||
async executor({ config, secrets, params, services }: ActionTypeExecutorOptions) {
|
||||
await services.callCluster('index', {
|
||||
index: params.index,
|
||||
refresh: 'wait_for',
|
||||
body: {
|
||||
params,
|
||||
config,
|
||||
secrets,
|
||||
reference: params.reference,
|
||||
source: 'action:test.failing',
|
||||
},
|
||||
});
|
||||
throw new Error(`expected failure for ${params.index} ${params.reference}`);
|
||||
},
|
||||
};
|
||||
const rateLimitedActionType: ActionType = {
|
||||
id: 'test.rate-limit',
|
||||
name: 'Test: Rate Limit',
|
||||
minimumLicenseRequired: 'gold',
|
||||
maxAttempts: 2,
|
||||
validate: {
|
||||
params: schema.object({
|
||||
index: schema.string(),
|
||||
reference: schema.string(),
|
||||
retryAt: schema.number(),
|
||||
}),
|
||||
},
|
||||
async executor({ config, params, services }: ActionTypeExecutorOptions) {
|
||||
await services.callCluster('index', {
|
||||
index: params.index,
|
||||
refresh: 'wait_for',
|
||||
body: {
|
||||
params,
|
||||
config,
|
||||
reference: params.reference,
|
||||
source: 'action:test.rate-limit',
|
||||
},
|
||||
});
|
||||
return {
|
||||
status: 'error',
|
||||
retry: new Date(params.retryAt),
|
||||
actionId: '',
|
||||
};
|
||||
},
|
||||
};
|
||||
const authorizationActionType: ActionType = {
|
||||
id: 'test.authorization',
|
||||
name: 'Test: Authorization',
|
||||
minimumLicenseRequired: 'gold',
|
||||
validate: {
|
||||
params: schema.object({
|
||||
callClusterAuthorizationIndex: schema.string(),
|
||||
savedObjectsClientType: schema.string(),
|
||||
savedObjectsClientId: schema.string(),
|
||||
index: schema.string(),
|
||||
reference: schema.string(),
|
||||
}),
|
||||
},
|
||||
async executor({ params, services, actionId }: ActionTypeExecutorOptions) {
|
||||
// Call cluster
|
||||
let callClusterSuccess = false;
|
||||
let callClusterError;
|
||||
try {
|
||||
await services.callCluster('index', {
|
||||
index: params.callClusterAuthorizationIndex,
|
||||
refresh: 'wait_for',
|
||||
body: {
|
||||
param1: 'test',
|
||||
},
|
||||
});
|
||||
callClusterSuccess = true;
|
||||
} catch (e) {
|
||||
callClusterError = e;
|
||||
}
|
||||
// Call scoped cluster
|
||||
const callScopedCluster = services.getScopedCallCluster(clusterClient);
|
||||
let callScopedClusterSuccess = false;
|
||||
let callScopedClusterError;
|
||||
try {
|
||||
await callScopedCluster('index', {
|
||||
index: params.callClusterAuthorizationIndex,
|
||||
refresh: 'wait_for',
|
||||
body: {
|
||||
param1: 'test',
|
||||
},
|
||||
});
|
||||
callScopedClusterSuccess = true;
|
||||
} catch (e) {
|
||||
callScopedClusterError = e;
|
||||
}
|
||||
// Saved objects client
|
||||
let savedObjectsClientSuccess = false;
|
||||
let savedObjectsClientError;
|
||||
try {
|
||||
await services.savedObjectsClient.get(
|
||||
params.savedObjectsClientType,
|
||||
params.savedObjectsClientId
|
||||
);
|
||||
savedObjectsClientSuccess = true;
|
||||
} catch (e) {
|
||||
savedObjectsClientError = e;
|
||||
}
|
||||
// Save the result
|
||||
await services.callCluster('index', {
|
||||
index: params.index,
|
||||
refresh: 'wait_for',
|
||||
body: {
|
||||
state: {
|
||||
callClusterSuccess,
|
||||
callClusterError,
|
||||
callScopedClusterSuccess,
|
||||
callScopedClusterError,
|
||||
savedObjectsClientSuccess,
|
||||
savedObjectsClientError,
|
||||
},
|
||||
params,
|
||||
reference: params.reference,
|
||||
source: 'action:test.authorization',
|
||||
},
|
||||
});
|
||||
return {
|
||||
actionId,
|
||||
status: 'ok',
|
||||
};
|
||||
},
|
||||
};
|
||||
actions.registerType(noopActionType);
|
||||
actions.registerType(indexRecordActionType);
|
||||
actions.registerType(failingActionType);
|
||||
actions.registerType(rateLimitedActionType);
|
||||
actions.registerType(authorizationActionType);
|
||||
|
||||
const alwaysFiringAlertType: AlertType = {
|
||||
id: 'test.always-firing',
|
||||
name: 'Test: Always Firing',
|
||||
actionGroups: [
|
||||
{ id: 'default', name: 'Default' },
|
||||
{ id: 'other', name: 'Other' },
|
||||
],
|
||||
producer: 'alerting',
|
||||
defaultActionGroupId: 'default',
|
||||
actionVariables: {
|
||||
state: [{ name: 'instanceStateValue', description: 'the instance state value' }],
|
||||
context: [{ name: 'instanceContextValue', description: 'the instance context value' }],
|
||||
},
|
||||
async executor(alertExecutorOptions: AlertExecutorOptions) {
|
||||
const {
|
||||
services,
|
||||
params,
|
||||
state,
|
||||
alertId,
|
||||
spaceId,
|
||||
namespace,
|
||||
name,
|
||||
tags,
|
||||
createdBy,
|
||||
updatedBy,
|
||||
} = alertExecutorOptions;
|
||||
let group = 'default';
|
||||
const alertInfo = { alertId, spaceId, namespace, name, tags, createdBy, updatedBy };
|
||||
|
||||
if (params.groupsToScheduleActionsInSeries) {
|
||||
const index = state.groupInSeriesIndex || 0;
|
||||
group = params.groupsToScheduleActionsInSeries[index];
|
||||
}
|
||||
|
||||
if (group) {
|
||||
services
|
||||
.alertInstanceFactory('1')
|
||||
.replaceState({ instanceStateValue: true })
|
||||
.scheduleActions(group, {
|
||||
instanceContextValue: true,
|
||||
});
|
||||
}
|
||||
await services.callCluster('index', {
|
||||
index: params.index,
|
||||
refresh: 'wait_for',
|
||||
body: {
|
||||
state,
|
||||
params,
|
||||
reference: params.reference,
|
||||
source: 'alert:test.always-firing',
|
||||
alertInfo,
|
||||
},
|
||||
});
|
||||
return {
|
||||
globalStateValue: true,
|
||||
groupInSeriesIndex: (state.groupInSeriesIndex || 0) + 1,
|
||||
};
|
||||
},
|
||||
};
|
||||
// Alert types
|
||||
const cumulativeFiringAlertType: AlertType = {
|
||||
id: 'test.cumulative-firing',
|
||||
name: 'Test: Cumulative Firing',
|
||||
actionGroups: [
|
||||
{ id: 'default', name: 'Default' },
|
||||
{ id: 'other', name: 'Other' },
|
||||
],
|
||||
producer: 'alerting',
|
||||
defaultActionGroupId: 'default',
|
||||
async executor(alertExecutorOptions: AlertExecutorOptions) {
|
||||
const { services, state } = alertExecutorOptions;
|
||||
const group = 'default';
|
||||
|
||||
const runCount = (state.runCount || 0) + 1;
|
||||
|
||||
times(runCount, index => {
|
||||
services
|
||||
.alertInstanceFactory(`instance-${index}`)
|
||||
.replaceState({ instanceStateValue: true })
|
||||
.scheduleActions(group);
|
||||
});
|
||||
|
||||
return {
|
||||
runCount,
|
||||
};
|
||||
},
|
||||
};
|
||||
const neverFiringAlertType: AlertType = {
|
||||
id: 'test.never-firing',
|
||||
name: 'Test: Never firing',
|
||||
actionGroups: [
|
||||
{
|
||||
id: 'default',
|
||||
name: 'Default',
|
||||
},
|
||||
],
|
||||
producer: 'alerting',
|
||||
defaultActionGroupId: 'default',
|
||||
async executor({ services, params, state }: AlertExecutorOptions) {
|
||||
await services.callCluster('index', {
|
||||
index: params.index,
|
||||
refresh: 'wait_for',
|
||||
body: {
|
||||
state,
|
||||
params,
|
||||
reference: params.reference,
|
||||
source: 'alert:test.never-firing',
|
||||
},
|
||||
});
|
||||
return {
|
||||
globalStateValue: true,
|
||||
};
|
||||
},
|
||||
};
|
||||
const failingAlertType: AlertType = {
|
||||
id: 'test.failing',
|
||||
name: 'Test: Failing',
|
||||
actionGroups: [
|
||||
{
|
||||
id: 'default',
|
||||
name: 'Default',
|
||||
},
|
||||
],
|
||||
producer: 'alerting',
|
||||
defaultActionGroupId: 'default',
|
||||
async executor({ services, params, state }: AlertExecutorOptions) {
|
||||
await services.callCluster('index', {
|
||||
index: params.index,
|
||||
refresh: 'wait_for',
|
||||
body: {
|
||||
state,
|
||||
params,
|
||||
reference: params.reference,
|
||||
source: 'alert:test.failing',
|
||||
},
|
||||
});
|
||||
throw new Error('Failed to execute alert type');
|
||||
},
|
||||
};
|
||||
const authorizationAlertType: AlertType = {
|
||||
id: 'test.authorization',
|
||||
name: 'Test: Authorization',
|
||||
actionGroups: [
|
||||
{
|
||||
id: 'default',
|
||||
name: 'Default',
|
||||
},
|
||||
],
|
||||
defaultActionGroupId: 'default',
|
||||
producer: 'alerting',
|
||||
validate: {
|
||||
params: schema.object({
|
||||
callClusterAuthorizationIndex: schema.string(),
|
||||
savedObjectsClientType: schema.string(),
|
||||
savedObjectsClientId: schema.string(),
|
||||
index: schema.string(),
|
||||
reference: schema.string(),
|
||||
}),
|
||||
},
|
||||
async executor({ services, params, state }: AlertExecutorOptions) {
|
||||
// Call cluster
|
||||
let callClusterSuccess = false;
|
||||
let callClusterError;
|
||||
try {
|
||||
await services.callCluster('index', {
|
||||
index: params.callClusterAuthorizationIndex,
|
||||
refresh: 'wait_for',
|
||||
body: {
|
||||
param1: 'test',
|
||||
},
|
||||
});
|
||||
callClusterSuccess = true;
|
||||
} catch (e) {
|
||||
callClusterError = e;
|
||||
}
|
||||
// Call scoped cluster
|
||||
const callScopedCluster = services.getScopedCallCluster(clusterClient);
|
||||
let callScopedClusterSuccess = false;
|
||||
let callScopedClusterError;
|
||||
try {
|
||||
await callScopedCluster('index', {
|
||||
index: params.callClusterAuthorizationIndex,
|
||||
refresh: 'wait_for',
|
||||
body: {
|
||||
param1: 'test',
|
||||
},
|
||||
});
|
||||
callScopedClusterSuccess = true;
|
||||
} catch (e) {
|
||||
callScopedClusterError = e;
|
||||
}
|
||||
// Saved objects client
|
||||
let savedObjectsClientSuccess = false;
|
||||
let savedObjectsClientError;
|
||||
try {
|
||||
await services.savedObjectsClient.get(
|
||||
params.savedObjectsClientType,
|
||||
params.savedObjectsClientId
|
||||
);
|
||||
savedObjectsClientSuccess = true;
|
||||
} catch (e) {
|
||||
savedObjectsClientError = e;
|
||||
}
|
||||
// Save the result
|
||||
await services.callCluster('index', {
|
||||
index: params.index,
|
||||
refresh: 'wait_for',
|
||||
body: {
|
||||
state: {
|
||||
callClusterSuccess,
|
||||
callClusterError,
|
||||
callScopedClusterSuccess,
|
||||
callScopedClusterError,
|
||||
savedObjectsClientSuccess,
|
||||
savedObjectsClientError,
|
||||
},
|
||||
params,
|
||||
reference: params.reference,
|
||||
source: 'alert:test.authorization',
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
const validationAlertType: AlertType = {
|
||||
id: 'test.validation',
|
||||
name: 'Test: Validation',
|
||||
actionGroups: [
|
||||
{
|
||||
id: 'default',
|
||||
name: 'Default',
|
||||
},
|
||||
],
|
||||
producer: 'alerting',
|
||||
defaultActionGroupId: 'default',
|
||||
validate: {
|
||||
params: schema.object({
|
||||
param1: schema.string(),
|
||||
}),
|
||||
},
|
||||
async executor({ services, params, state }: AlertExecutorOptions) {},
|
||||
};
|
||||
const noopAlertType: AlertType = {
|
||||
id: 'test.noop',
|
||||
name: 'Test: Noop',
|
||||
actionGroups: [{ id: 'default', name: 'Default' }],
|
||||
producer: 'alerting',
|
||||
defaultActionGroupId: 'default',
|
||||
async executor({ services, params, state }: AlertExecutorOptions) {},
|
||||
};
|
||||
const onlyContextVariablesAlertType: AlertType = {
|
||||
id: 'test.onlyContextVariables',
|
||||
name: 'Test: Only Context Variables',
|
||||
actionGroups: [{ id: 'default', name: 'Default' }],
|
||||
producer: 'alerting',
|
||||
defaultActionGroupId: 'default',
|
||||
actionVariables: {
|
||||
context: [{ name: 'aContextVariable', description: 'this is a context variable' }],
|
||||
},
|
||||
async executor(opts: AlertExecutorOptions) {},
|
||||
};
|
||||
const onlyStateVariablesAlertType: AlertType = {
|
||||
id: 'test.onlyStateVariables',
|
||||
name: 'Test: Only State Variables',
|
||||
actionGroups: [{ id: 'default', name: 'Default' }],
|
||||
producer: 'alerting',
|
||||
defaultActionGroupId: 'default',
|
||||
actionVariables: {
|
||||
state: [{ name: 'aStateVariable', description: 'this is a state variable' }],
|
||||
},
|
||||
async executor(opts: AlertExecutorOptions) {},
|
||||
};
|
||||
alerting.registerType(alwaysFiringAlertType);
|
||||
alerting.registerType(cumulativeFiringAlertType);
|
||||
alerting.registerType(neverFiringAlertType);
|
||||
alerting.registerType(failingAlertType);
|
||||
alerting.registerType(validationAlertType);
|
||||
alerting.registerType(authorizationAlertType);
|
||||
alerting.registerType(noopAlertType);
|
||||
alerting.registerType(onlyContextVariablesAlertType);
|
||||
alerting.registerType(onlyStateVariablesAlertType);
|
||||
defineActionTypes(core, { actions });
|
||||
defineAlertTypes(core, { alerting });
|
||||
defineRoutes(core);
|
||||
}
|
||||
|
||||
public start() {}
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import {
|
||||
CoreSetup,
|
||||
RequestHandlerContext,
|
||||
KibanaRequest,
|
||||
KibanaResponseFactory,
|
||||
IKibanaResponse,
|
||||
} from 'kibana/server';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
|
||||
export function defineRoutes(core: CoreSetup) {
|
||||
const router = core.http.createRouter();
|
||||
router.put(
|
||||
{
|
||||
path: '/api/alerts_fixture/saved_object/{type}/{id}',
|
||||
validate: {
|
||||
params: schema.object({
|
||||
type: schema.string(),
|
||||
id: schema.string(),
|
||||
}),
|
||||
body: schema.object({
|
||||
attributes: schema.recordOf(schema.string(), schema.any()),
|
||||
version: schema.maybe(schema.string()),
|
||||
references: schema.maybe(
|
||||
schema.arrayOf(
|
||||
schema.object({
|
||||
name: schema.string(),
|
||||
type: schema.string(),
|
||||
id: schema.string(),
|
||||
})
|
||||
)
|
||||
),
|
||||
}),
|
||||
},
|
||||
},
|
||||
async (
|
||||
context: RequestHandlerContext,
|
||||
req: KibanaRequest<any, any, any, any>,
|
||||
res: KibanaResponseFactory
|
||||
): Promise<IKibanaResponse<any>> => {
|
||||
const { type, id } = req.params;
|
||||
const { attributes, version, references } = req.body;
|
||||
const options = { version, references };
|
||||
|
||||
const [{ savedObjects }] = await core.getStartServices();
|
||||
const savedObjectsWithAlerts = await savedObjects.getScopedClient(req, {
|
||||
includedHiddenTypes: ['alert'],
|
||||
});
|
||||
const result = await savedObjectsWithAlerts.update(type, id, attributes, options);
|
||||
return res.ok({ body: result });
|
||||
}
|
||||
);
|
||||
}
|
|
@ -117,7 +117,9 @@ export default function createDeleteTests({ getService }: FtrProviderContext) {
|
|||
.expect(200);
|
||||
|
||||
await supertest
|
||||
.put(`${getUrlPrefix(space.id)}/api/saved_objects/alert/${createdAlert.id}`)
|
||||
.put(
|
||||
`${getUrlPrefix(space.id)}/api/alerts_fixture/saved_object/alert/${createdAlert.id}`
|
||||
)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.send({
|
||||
attributes: {
|
||||
|
|
|
@ -93,7 +93,9 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte
|
|||
objectRemover.add(space.id, createdAlert.id, 'alert');
|
||||
|
||||
await supertest
|
||||
.put(`${getUrlPrefix(space.id)}/api/saved_objects/alert/${createdAlert.id}`)
|
||||
.put(
|
||||
`${getUrlPrefix(space.id)}/api/alerts_fixture/saved_object/alert/${createdAlert.id}`
|
||||
)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.send({
|
||||
attributes: {
|
||||
|
|
|
@ -98,7 +98,9 @@ export default function createEnableAlertTests({ getService }: FtrProviderContex
|
|||
objectRemover.add(space.id, createdAlert.id, 'alert');
|
||||
|
||||
await supertest
|
||||
.put(`${getUrlPrefix(space.id)}/api/saved_objects/alert/${createdAlert.id}`)
|
||||
.put(
|
||||
`${getUrlPrefix(space.id)}/api/alerts_fixture/saved_object/alert/${createdAlert.id}`
|
||||
)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.send({
|
||||
attributes: {
|
||||
|
|
|
@ -117,7 +117,9 @@ export default function createUpdateTests({ getService }: FtrProviderContext) {
|
|||
objectRemover.add(space.id, createdAlert.id, 'alert');
|
||||
|
||||
await supertest
|
||||
.put(`${getUrlPrefix(space.id)}/api/saved_objects/alert/${createdAlert.id}`)
|
||||
.put(
|
||||
`${getUrlPrefix(space.id)}/api/alerts_fixture/saved_object/alert/${createdAlert.id}`
|
||||
)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.send({
|
||||
attributes: {
|
||||
|
|
|
@ -83,7 +83,9 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte
|
|||
objectRemover.add(space.id, createdAlert.id, 'alert');
|
||||
|
||||
await supertest
|
||||
.put(`${getUrlPrefix(space.id)}/api/saved_objects/alert/${createdAlert.id}`)
|
||||
.put(
|
||||
`${getUrlPrefix(space.id)}/api/alerts_fixture/saved_object/alert/${createdAlert.id}`
|
||||
)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.send({
|
||||
attributes: {
|
||||
|
|
|
@ -26,7 +26,9 @@ export function registerHiddenSORoutes(
|
|||
try {
|
||||
return response.ok({
|
||||
body: await encryptedSavedObjects
|
||||
.getClient([request.params.type])
|
||||
.getClient({
|
||||
includedHiddenTypes: [request.params.type],
|
||||
})
|
||||
.getDecryptedAsInternalUser(request.params.type, request.params.id, { namespace }),
|
||||
});
|
||||
} catch (err) {
|
||||
|
|
Loading…
Reference in a new issue