mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Alerting] Add alert.updatedAt
field to represent date of last user edit (#83784)
* Adding alert.updatedAt field that only updates on user edit * Updating unit tests * Functional tests * Updating alert attributes excluded from AAD * Fixing test * PR comments * Unskipping tests and updating es archiver data
This commit is contained in:
parent
ad5cf9e78b
commit
e45b76c1b2
33 changed files with 181 additions and 42 deletions
|
@ -228,14 +228,17 @@ export class AlertsClient {
|
|||
|
||||
this.validateActions(alertType, data.actions);
|
||||
|
||||
const createTime = Date.now();
|
||||
const { references, actions } = await this.denormalizeActions(data.actions);
|
||||
|
||||
const rawAlert: RawAlert = {
|
||||
...data,
|
||||
...this.apiKeyAsAlertAttributes(createdAPIKey, username),
|
||||
actions,
|
||||
createdBy: username,
|
||||
updatedBy: username,
|
||||
createdAt: new Date().toISOString(),
|
||||
createdAt: new Date(createTime).toISOString(),
|
||||
updatedAt: new Date(createTime).toISOString(),
|
||||
params: validatedAlertTypeParams as RawAlert['params'],
|
||||
muteAll: false,
|
||||
mutedInstanceIds: [],
|
||||
|
@ -289,12 +292,7 @@ export class AlertsClient {
|
|||
});
|
||||
createdAlert.attributes.scheduledTaskId = scheduledTask.id;
|
||||
}
|
||||
return this.getAlertFromRaw(
|
||||
createdAlert.id,
|
||||
createdAlert.attributes,
|
||||
createdAlert.updated_at,
|
||||
references
|
||||
);
|
||||
return this.getAlertFromRaw(createdAlert.id, createdAlert.attributes, references);
|
||||
}
|
||||
|
||||
public async get({ id }: { id: string }): Promise<SanitizedAlert> {
|
||||
|
@ -304,7 +302,7 @@ export class AlertsClient {
|
|||
result.attributes.consumer,
|
||||
ReadOperations.Get
|
||||
);
|
||||
return this.getAlertFromRaw(result.id, result.attributes, result.updated_at, result.references);
|
||||
return this.getAlertFromRaw(result.id, result.attributes, result.references);
|
||||
}
|
||||
|
||||
public async getAlertState({ id }: { id: string }): Promise<AlertTaskState | void> {
|
||||
|
@ -393,13 +391,11 @@ export class AlertsClient {
|
|||
type: 'alert',
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const authorizedData = data.map(({ id, attributes, updated_at, references }) => {
|
||||
const authorizedData = data.map(({ id, attributes, references }) => {
|
||||
ensureAlertTypeIsAuthorized(attributes.alertTypeId, attributes.consumer);
|
||||
return this.getAlertFromRaw(
|
||||
id,
|
||||
fields ? (pick(attributes, fields) as RawAlert) : attributes,
|
||||
updated_at,
|
||||
references
|
||||
);
|
||||
});
|
||||
|
@ -585,6 +581,7 @@ export class AlertsClient {
|
|||
params: validatedAlertTypeParams as RawAlert['params'],
|
||||
actions,
|
||||
updatedBy: username,
|
||||
updatedAt: new Date().toISOString(),
|
||||
});
|
||||
try {
|
||||
updatedObject = await this.unsecuredSavedObjectsClient.create<RawAlert>(
|
||||
|
@ -607,12 +604,7 @@ export class AlertsClient {
|
|||
throw e;
|
||||
}
|
||||
|
||||
return this.getPartialAlertFromRaw(
|
||||
id,
|
||||
updatedObject.attributes,
|
||||
updatedObject.updated_at,
|
||||
updatedObject.references
|
||||
);
|
||||
return this.getPartialAlertFromRaw(id, updatedObject.attributes, updatedObject.references);
|
||||
}
|
||||
|
||||
private apiKeyAsAlertAttributes(
|
||||
|
@ -677,6 +669,7 @@ export class AlertsClient {
|
|||
await this.createAPIKey(this.generateAPIKeyName(attributes.alertTypeId, attributes.name)),
|
||||
username
|
||||
),
|
||||
updatedAt: new Date().toISOString(),
|
||||
updatedBy: username,
|
||||
});
|
||||
try {
|
||||
|
@ -751,6 +744,7 @@ export class AlertsClient {
|
|||
username
|
||||
),
|
||||
updatedBy: username,
|
||||
updatedAt: new Date().toISOString(),
|
||||
});
|
||||
try {
|
||||
await this.unsecuredSavedObjectsClient.update('alert', id, updateAttributes, { version });
|
||||
|
@ -829,6 +823,7 @@ export class AlertsClient {
|
|||
apiKey: null,
|
||||
apiKeyOwner: null,
|
||||
updatedBy: await this.getUserName(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
}),
|
||||
{ version }
|
||||
);
|
||||
|
@ -875,6 +870,7 @@ export class AlertsClient {
|
|||
muteAll: true,
|
||||
mutedInstanceIds: [],
|
||||
updatedBy: await this.getUserName(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
});
|
||||
const updateOptions = { version };
|
||||
|
||||
|
@ -913,6 +909,7 @@ export class AlertsClient {
|
|||
muteAll: false,
|
||||
mutedInstanceIds: [],
|
||||
updatedBy: await this.getUserName(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
});
|
||||
const updateOptions = { version };
|
||||
|
||||
|
@ -957,6 +954,7 @@ export class AlertsClient {
|
|||
this.updateMeta({
|
||||
mutedInstanceIds,
|
||||
updatedBy: await this.getUserName(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
}),
|
||||
{ version }
|
||||
);
|
||||
|
@ -999,6 +997,7 @@ export class AlertsClient {
|
|||
alertId,
|
||||
this.updateMeta({
|
||||
updatedBy: await this.getUserName(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
mutedInstanceIds: mutedInstanceIds.filter((id: string) => id !== alertInstanceId),
|
||||
}),
|
||||
{ version }
|
||||
|
@ -1050,19 +1049,17 @@ export class AlertsClient {
|
|||
private getAlertFromRaw(
|
||||
id: string,
|
||||
rawAlert: RawAlert,
|
||||
updatedAt: SavedObject['updated_at'],
|
||||
references: SavedObjectReference[] | undefined
|
||||
): Alert {
|
||||
// In order to support the partial update API of Saved Objects we have to support
|
||||
// partial updates of an Alert, but when we receive an actual RawAlert, it is safe
|
||||
// to cast the result to an Alert
|
||||
return this.getPartialAlertFromRaw(id, rawAlert, updatedAt, references) as Alert;
|
||||
return this.getPartialAlertFromRaw(id, rawAlert, references) as Alert;
|
||||
}
|
||||
|
||||
private getPartialAlertFromRaw(
|
||||
id: string,
|
||||
{ createdAt, meta, scheduledTaskId, ...rawAlert }: Partial<RawAlert>,
|
||||
updatedAt: SavedObject['updated_at'] = createdAt,
|
||||
{ createdAt, updatedAt, meta, scheduledTaskId, ...rawAlert }: Partial<RawAlert>,
|
||||
references: SavedObjectReference[] | undefined
|
||||
): PartialAlert {
|
||||
// Not the prettiest code here, but if we want to use most of the
|
||||
|
|
|
@ -196,6 +196,7 @@ describe('create()', () => {
|
|||
createdAt: '2019-02-12T21:01:22.479Z',
|
||||
createdBy: 'elastic',
|
||||
updatedBy: 'elastic',
|
||||
updatedAt: '2019-02-12T21:01:22.479Z',
|
||||
muteAll: false,
|
||||
mutedInstanceIds: [],
|
||||
actions: [
|
||||
|
@ -330,6 +331,7 @@ describe('create()', () => {
|
|||
"foo",
|
||||
],
|
||||
"throttle": null,
|
||||
"updatedAt": "2019-02-12T21:01:22.479Z",
|
||||
"updatedBy": "elastic",
|
||||
}
|
||||
`);
|
||||
|
@ -418,6 +420,7 @@ describe('create()', () => {
|
|||
bar: true,
|
||||
},
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
actions: [
|
||||
{
|
||||
group: 'default',
|
||||
|
@ -555,6 +558,7 @@ describe('create()', () => {
|
|||
bar: true,
|
||||
},
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
actions: [
|
||||
{
|
||||
group: 'default',
|
||||
|
@ -631,6 +635,7 @@ describe('create()', () => {
|
|||
bar: true,
|
||||
},
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
actions: [
|
||||
{
|
||||
group: 'default',
|
||||
|
@ -971,6 +976,7 @@ describe('create()', () => {
|
|||
createdBy: 'elastic',
|
||||
createdAt: '2019-02-12T21:01:22.479Z',
|
||||
updatedBy: 'elastic',
|
||||
updatedAt: '2019-02-12T21:01:22.479Z',
|
||||
enabled: true,
|
||||
meta: {
|
||||
versionApiKeyLastmodified: 'v7.10.0',
|
||||
|
@ -1092,6 +1098,7 @@ describe('create()', () => {
|
|||
createdBy: 'elastic',
|
||||
createdAt: '2019-02-12T21:01:22.479Z',
|
||||
updatedBy: 'elastic',
|
||||
updatedAt: '2019-02-12T21:01:22.479Z',
|
||||
enabled: false,
|
||||
meta: {
|
||||
versionApiKeyLastmodified: 'v7.10.0',
|
||||
|
|
|
@ -12,7 +12,7 @@ import { encryptedSavedObjectsMock } from '../../../../encrypted_saved_objects/s
|
|||
import { actionsAuthorizationMock } from '../../../../actions/server/mocks';
|
||||
import { AlertsAuthorization } from '../../authorization/alerts_authorization';
|
||||
import { ActionsAuthorization } from '../../../../actions/server';
|
||||
import { getBeforeSetup } from './lib';
|
||||
import { getBeforeSetup, setGlobalDate } from './lib';
|
||||
import { InvalidatePendingApiKey } from '../../types';
|
||||
|
||||
const taskManager = taskManagerMock.createStart();
|
||||
|
@ -45,6 +45,8 @@ beforeEach(() => {
|
|||
getBeforeSetup(alertsClientParams, taskManager, alertTypeRegistry);
|
||||
});
|
||||
|
||||
setGlobalDate();
|
||||
|
||||
describe('disable()', () => {
|
||||
let alertsClient: AlertsClient;
|
||||
const existingAlert = {
|
||||
|
@ -136,6 +138,7 @@ describe('disable()', () => {
|
|||
scheduledTaskId: null,
|
||||
apiKey: null,
|
||||
apiKeyOwner: null,
|
||||
updatedAt: '2019-02-12T21:01:22.479Z',
|
||||
updatedBy: 'elastic',
|
||||
actions: [
|
||||
{
|
||||
|
@ -190,6 +193,7 @@ describe('disable()', () => {
|
|||
scheduledTaskId: null,
|
||||
apiKey: null,
|
||||
apiKeyOwner: null,
|
||||
updatedAt: '2019-02-12T21:01:22.479Z',
|
||||
updatedBy: 'elastic',
|
||||
actions: [
|
||||
{
|
||||
|
|
|
@ -13,7 +13,7 @@ import { actionsAuthorizationMock } from '../../../../actions/server/mocks';
|
|||
import { AlertsAuthorization } from '../../authorization/alerts_authorization';
|
||||
import { ActionsAuthorization } from '../../../../actions/server';
|
||||
import { TaskStatus } from '../../../../task_manager/server';
|
||||
import { getBeforeSetup } from './lib';
|
||||
import { getBeforeSetup, setGlobalDate } from './lib';
|
||||
import { InvalidatePendingApiKey } from '../../types';
|
||||
|
||||
const taskManager = taskManagerMock.createStart();
|
||||
|
@ -46,6 +46,8 @@ beforeEach(() => {
|
|||
getBeforeSetup(alertsClientParams, taskManager, alertTypeRegistry);
|
||||
});
|
||||
|
||||
setGlobalDate();
|
||||
|
||||
describe('enable()', () => {
|
||||
let alertsClient: AlertsClient;
|
||||
const existingAlert = {
|
||||
|
@ -186,6 +188,7 @@ describe('enable()', () => {
|
|||
meta: {
|
||||
versionApiKeyLastmodified: kibanaVersion,
|
||||
},
|
||||
updatedAt: '2019-02-12T21:01:22.479Z',
|
||||
updatedBy: 'elastic',
|
||||
apiKey: null,
|
||||
apiKeyOwner: null,
|
||||
|
@ -292,6 +295,7 @@ describe('enable()', () => {
|
|||
apiKey: Buffer.from('123:abc').toString('base64'),
|
||||
apiKeyOwner: 'elastic',
|
||||
updatedBy: 'elastic',
|
||||
updatedAt: '2019-02-12T21:01:22.479Z',
|
||||
actions: [
|
||||
{
|
||||
group: 'default',
|
||||
|
|
|
@ -79,6 +79,7 @@ describe('find()', () => {
|
|||
bar: true,
|
||||
},
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
actions: [
|
||||
{
|
||||
group: 'default',
|
||||
|
|
|
@ -59,6 +59,7 @@ describe('get()', () => {
|
|||
bar: true,
|
||||
},
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
actions: [
|
||||
{
|
||||
group: 'default',
|
||||
|
|
|
@ -76,6 +76,7 @@ const BaseAlertInstanceSummarySavedObject: SavedObject<RawAlert> = {
|
|||
createdBy: null,
|
||||
updatedBy: null,
|
||||
createdAt: mockedDateString,
|
||||
updatedAt: mockedDateString,
|
||||
apiKey: null,
|
||||
apiKeyOwner: null,
|
||||
throttle: null,
|
||||
|
|
|
@ -12,7 +12,7 @@ import { encryptedSavedObjectsMock } from '../../../../encrypted_saved_objects/s
|
|||
import { actionsAuthorizationMock } from '../../../../actions/server/mocks';
|
||||
import { AlertsAuthorization } from '../../authorization/alerts_authorization';
|
||||
import { ActionsAuthorization } from '../../../../actions/server';
|
||||
import { getBeforeSetup } from './lib';
|
||||
import { getBeforeSetup, setGlobalDate } from './lib';
|
||||
|
||||
const taskManager = taskManagerMock.createStart();
|
||||
const alertTypeRegistry = alertTypeRegistryMock.create();
|
||||
|
@ -43,6 +43,8 @@ beforeEach(() => {
|
|||
getBeforeSetup(alertsClientParams, taskManager, alertTypeRegistry);
|
||||
});
|
||||
|
||||
setGlobalDate();
|
||||
|
||||
describe('muteAll()', () => {
|
||||
test('mutes an alert', async () => {
|
||||
const alertsClient = new AlertsClient(alertsClientParams);
|
||||
|
@ -74,6 +76,7 @@ describe('muteAll()', () => {
|
|||
{
|
||||
muteAll: true,
|
||||
mutedInstanceIds: [],
|
||||
updatedAt: '2019-02-12T21:01:22.479Z',
|
||||
updatedBy: 'elastic',
|
||||
},
|
||||
{
|
||||
|
|
|
@ -12,7 +12,7 @@ import { encryptedSavedObjectsMock } from '../../../../encrypted_saved_objects/s
|
|||
import { actionsAuthorizationMock } from '../../../../actions/server/mocks';
|
||||
import { AlertsAuthorization } from '../../authorization/alerts_authorization';
|
||||
import { ActionsAuthorization } from '../../../../actions/server';
|
||||
import { getBeforeSetup } from './lib';
|
||||
import { getBeforeSetup, setGlobalDate } from './lib';
|
||||
|
||||
const taskManager = taskManagerMock.createStart();
|
||||
const alertTypeRegistry = alertTypeRegistryMock.create();
|
||||
|
@ -44,6 +44,8 @@ beforeEach(() => {
|
|||
getBeforeSetup(alertsClientParams, taskManager, alertTypeRegistry);
|
||||
});
|
||||
|
||||
setGlobalDate();
|
||||
|
||||
describe('muteInstance()', () => {
|
||||
test('mutes an alert instance', async () => {
|
||||
const alertsClient = new AlertsClient(alertsClientParams);
|
||||
|
@ -68,6 +70,7 @@ describe('muteInstance()', () => {
|
|||
'1',
|
||||
{
|
||||
mutedInstanceIds: ['2'],
|
||||
updatedAt: '2019-02-12T21:01:22.479Z',
|
||||
updatedBy: 'elastic',
|
||||
},
|
||||
{
|
||||
|
|
|
@ -12,7 +12,7 @@ import { encryptedSavedObjectsMock } from '../../../../encrypted_saved_objects/s
|
|||
import { actionsAuthorizationMock } from '../../../../actions/server/mocks';
|
||||
import { AlertsAuthorization } from '../../authorization/alerts_authorization';
|
||||
import { ActionsAuthorization } from '../../../../actions/server';
|
||||
import { getBeforeSetup } from './lib';
|
||||
import { getBeforeSetup, setGlobalDate } from './lib';
|
||||
|
||||
const taskManager = taskManagerMock.createStart();
|
||||
const alertTypeRegistry = alertTypeRegistryMock.create();
|
||||
|
@ -44,6 +44,8 @@ beforeEach(() => {
|
|||
getBeforeSetup(alertsClientParams, taskManager, alertTypeRegistry);
|
||||
});
|
||||
|
||||
setGlobalDate();
|
||||
|
||||
describe('unmuteAll()', () => {
|
||||
test('unmutes an alert', async () => {
|
||||
const alertsClient = new AlertsClient(alertsClientParams);
|
||||
|
@ -75,6 +77,7 @@ describe('unmuteAll()', () => {
|
|||
{
|
||||
muteAll: false,
|
||||
mutedInstanceIds: [],
|
||||
updatedAt: '2019-02-12T21:01:22.479Z',
|
||||
updatedBy: 'elastic',
|
||||
},
|
||||
{
|
||||
|
|
|
@ -12,7 +12,7 @@ import { encryptedSavedObjectsMock } from '../../../../encrypted_saved_objects/s
|
|||
import { actionsAuthorizationMock } from '../../../../actions/server/mocks';
|
||||
import { AlertsAuthorization } from '../../authorization/alerts_authorization';
|
||||
import { ActionsAuthorization } from '../../../../actions/server';
|
||||
import { getBeforeSetup } from './lib';
|
||||
import { getBeforeSetup, setGlobalDate } from './lib';
|
||||
|
||||
const taskManager = taskManagerMock.createStart();
|
||||
const alertTypeRegistry = alertTypeRegistryMock.create();
|
||||
|
@ -44,6 +44,8 @@ beforeEach(() => {
|
|||
getBeforeSetup(alertsClientParams, taskManager, alertTypeRegistry);
|
||||
});
|
||||
|
||||
setGlobalDate();
|
||||
|
||||
describe('unmuteInstance()', () => {
|
||||
test('unmutes an alert instance', async () => {
|
||||
const alertsClient = new AlertsClient(alertsClientParams);
|
||||
|
@ -69,6 +71,7 @@ describe('unmuteInstance()', () => {
|
|||
{
|
||||
mutedInstanceIds: [],
|
||||
updatedBy: 'elastic',
|
||||
updatedAt: '2019-02-12T21:01:22.479Z',
|
||||
},
|
||||
{ version: '123' }
|
||||
);
|
||||
|
|
|
@ -140,8 +140,8 @@ describe('update()', () => {
|
|||
],
|
||||
scheduledTaskId: 'task-123',
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
},
|
||||
updated_at: new Date().toISOString(),
|
||||
references: [
|
||||
{
|
||||
name: 'action_0',
|
||||
|
@ -300,6 +300,7 @@ describe('update()', () => {
|
|||
"foo",
|
||||
],
|
||||
"throttle": null,
|
||||
"updatedAt": "2019-02-12T21:01:22.479Z",
|
||||
"updatedBy": "elastic",
|
||||
}
|
||||
`);
|
||||
|
@ -362,6 +363,7 @@ describe('update()', () => {
|
|||
bar: true,
|
||||
},
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
actions: [
|
||||
{
|
||||
group: 'default',
|
||||
|
@ -484,6 +486,7 @@ describe('update()', () => {
|
|||
"foo",
|
||||
],
|
||||
"throttle": "5m",
|
||||
"updatedAt": "2019-02-12T21:01:22.479Z",
|
||||
"updatedBy": "elastic",
|
||||
}
|
||||
`);
|
||||
|
@ -534,6 +537,7 @@ describe('update()', () => {
|
|||
bar: true,
|
||||
},
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
actions: [
|
||||
{
|
||||
group: 'default',
|
||||
|
@ -648,6 +652,7 @@ describe('update()', () => {
|
|||
"foo",
|
||||
],
|
||||
"throttle": "5m",
|
||||
"updatedAt": "2019-02-12T21:01:22.479Z",
|
||||
"updatedBy": "elastic",
|
||||
}
|
||||
`);
|
||||
|
|
|
@ -12,7 +12,7 @@ import { encryptedSavedObjectsMock } from '../../../../encrypted_saved_objects/s
|
|||
import { actionsAuthorizationMock } from '../../../../actions/server/mocks';
|
||||
import { AlertsAuthorization } from '../../authorization/alerts_authorization';
|
||||
import { ActionsAuthorization } from '../../../../actions/server';
|
||||
import { getBeforeSetup } from './lib';
|
||||
import { getBeforeSetup, setGlobalDate } from './lib';
|
||||
import { InvalidatePendingApiKey } from '../../types';
|
||||
|
||||
const taskManager = taskManagerMock.createStart();
|
||||
|
@ -44,6 +44,8 @@ beforeEach(() => {
|
|||
getBeforeSetup(alertsClientParams, taskManager, alertTypeRegistry);
|
||||
});
|
||||
|
||||
setGlobalDate();
|
||||
|
||||
describe('updateApiKey()', () => {
|
||||
let alertsClient: AlertsClient;
|
||||
const existingAlert = {
|
||||
|
@ -113,6 +115,7 @@ describe('updateApiKey()', () => {
|
|||
apiKey: Buffer.from('234:abc').toString('base64'),
|
||||
apiKeyOwner: 'elastic',
|
||||
updatedBy: 'elastic',
|
||||
updatedAt: '2019-02-12T21:01:22.479Z',
|
||||
actions: [
|
||||
{
|
||||
group: 'default',
|
||||
|
@ -162,6 +165,7 @@ describe('updateApiKey()', () => {
|
|||
enabled: true,
|
||||
apiKey: Buffer.from('234:abc').toString('base64'),
|
||||
apiKeyOwner: 'elastic',
|
||||
updatedAt: '2019-02-12T21:01:22.479Z',
|
||||
updatedBy: 'elastic',
|
||||
actions: [
|
||||
{
|
||||
|
|
|
@ -16,6 +16,7 @@ export const AlertAttributesExcludedFromAAD = [
|
|||
'muteAll',
|
||||
'mutedInstanceIds',
|
||||
'updatedBy',
|
||||
'updatedAt',
|
||||
'executionStatus',
|
||||
];
|
||||
|
||||
|
@ -28,6 +29,7 @@ export type AlertAttributesExcludedFromAADType =
|
|||
| 'muteAll'
|
||||
| 'mutedInstanceIds'
|
||||
| 'updatedBy'
|
||||
| 'updatedAt'
|
||||
| 'executionStatus';
|
||||
|
||||
export function setupSavedObjects(
|
||||
|
|
|
@ -62,6 +62,9 @@
|
|||
"createdAt": {
|
||||
"type": "date"
|
||||
},
|
||||
"updatedAt": {
|
||||
"type": "date"
|
||||
},
|
||||
"apiKey": {
|
||||
"type": "binary"
|
||||
},
|
||||
|
|
|
@ -261,8 +261,48 @@ describe('7.10.0 migrates with failure', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('7.11.0', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
encryptedSavedObjectsSetup.createMigration.mockImplementation(
|
||||
(shouldMigrateWhenPredicate, migration) => migration
|
||||
);
|
||||
});
|
||||
|
||||
test('add updatedAt field to alert - set to SavedObject updated_at attribute', () => {
|
||||
const migration711 = getMigrations(encryptedSavedObjectsSetup)['7.11.0'];
|
||||
const alert = getMockData({}, true);
|
||||
expect(migration711(alert, { log })).toEqual({
|
||||
...alert,
|
||||
attributes: {
|
||||
...alert.attributes,
|
||||
updatedAt: alert.updated_at,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('add updatedAt field to alert - set to createdAt when SavedObject updated_at is not defined', () => {
|
||||
const migration711 = getMigrations(encryptedSavedObjectsSetup)['7.11.0'];
|
||||
const alert = getMockData({});
|
||||
expect(migration711(alert, { log })).toEqual({
|
||||
...alert,
|
||||
attributes: {
|
||||
...alert.attributes,
|
||||
updatedAt: alert.attributes.createdAt,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function getUpdatedAt(): string {
|
||||
const updatedAt = new Date();
|
||||
updatedAt.setHours(updatedAt.getHours() + 2);
|
||||
return updatedAt.toISOString();
|
||||
}
|
||||
|
||||
function getMockData(
|
||||
overwrites: Record<string, unknown> = {}
|
||||
overwrites: Record<string, unknown> = {},
|
||||
withSavedObjectUpdatedAt: boolean = false
|
||||
): SavedObjectUnsanitizedDoc<Partial<RawAlert>> {
|
||||
return {
|
||||
attributes: {
|
||||
|
@ -295,6 +335,7 @@ function getMockData(
|
|||
],
|
||||
...overwrites,
|
||||
},
|
||||
updated_at: withSavedObjectUpdatedAt ? getUpdatedAt() : undefined,
|
||||
id: uuid.v4(),
|
||||
type: 'alert',
|
||||
};
|
||||
|
|
|
@ -37,8 +37,15 @@ export function getMigrations(
|
|||
)
|
||||
);
|
||||
|
||||
const migrationAlertUpdatedAtDate = encryptedSavedObjects.createMigration<RawAlert, RawAlert>(
|
||||
// migrate all documents in 7.11 in order to add the "updatedAt" field
|
||||
(doc): doc is SavedObjectUnsanitizedDoc<RawAlert> => true,
|
||||
pipeMigrations(setAlertUpdatedAtDate)
|
||||
);
|
||||
|
||||
return {
|
||||
'7.10.0': executeMigrationWithErrorHandling(migrationWhenRBACWasIntroduced, '7.10.0'),
|
||||
'7.11.0': executeMigrationWithErrorHandling(migrationAlertUpdatedAtDate, '7.11.0'),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -59,6 +66,19 @@ function executeMigrationWithErrorHandling(
|
|||
};
|
||||
}
|
||||
|
||||
const setAlertUpdatedAtDate = (
|
||||
doc: SavedObjectUnsanitizedDoc<RawAlert>
|
||||
): SavedObjectUnsanitizedDoc<RawAlert> => {
|
||||
const updatedAt = doc.updated_at || doc.attributes.createdAt;
|
||||
return {
|
||||
...doc,
|
||||
attributes: {
|
||||
...doc.attributes,
|
||||
updatedAt,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const consumersToChange: Map<string, string> = new Map(
|
||||
Object.entries({
|
||||
alerting: 'alerts',
|
||||
|
|
|
@ -95,6 +95,7 @@ const DefaultAttributes = {
|
|||
muteAll: true,
|
||||
mutedInstanceIds: ['muted-instance-id-1', 'muted-instance-id-2'],
|
||||
updatedBy: 'someone',
|
||||
updatedAt: '2019-02-12T21:01:22.479Z',
|
||||
};
|
||||
|
||||
const InvalidAttributes = { ...DefaultAttributes, foo: 'bar' };
|
||||
|
|
|
@ -147,6 +147,7 @@ export interface RawAlert extends SavedObjectAttributes {
|
|||
createdBy: string | null;
|
||||
updatedBy: string | null;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
apiKey: string | null;
|
||||
apiKeyOwner: string | null;
|
||||
throttle: string | null;
|
||||
|
|
|
@ -30,8 +30,7 @@ import { loginAndWaitForPage } from '../tasks/login';
|
|||
|
||||
import { DETECTIONS_URL } from '../urls/navigation';
|
||||
|
||||
// FLAKY: https://github.com/elastic/kibana/issues/83773
|
||||
describe.skip('Alerts', () => {
|
||||
describe('Alerts', () => {
|
||||
context('Closing alerts', () => {
|
||||
beforeEach(() => {
|
||||
esArchiverLoad('alerts');
|
||||
|
|
|
@ -114,8 +114,7 @@ const expectedEditedtags = editedRule.tags.join('');
|
|||
const expectedEditedIndexPatterns =
|
||||
editedRule.index && editedRule.index.length ? editedRule.index : indexPatterns;
|
||||
|
||||
// SKIP: https://github.com/elastic/kibana/issues/83769
|
||||
describe.skip('Custom detection rules creation', () => {
|
||||
describe('Custom detection rules creation', () => {
|
||||
before(() => {
|
||||
esArchiverLoad('timeline');
|
||||
});
|
||||
|
@ -216,8 +215,7 @@ describe.skip('Custom detection rules creation', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// FLAKY: https://github.com/elastic/kibana/issues/83793
|
||||
describe.skip('Custom detection rules deletion and edition', () => {
|
||||
describe('Custom detection rules deletion and edition', () => {
|
||||
beforeEach(() => {
|
||||
esArchiverLoad('custom_rules');
|
||||
loginAndWaitForPageWithoutDateRange(DETECTIONS_URL);
|
||||
|
|
|
@ -17,8 +17,7 @@ import { DETECTIONS_URL } from '../urls/navigation';
|
|||
|
||||
const EXPECTED_EXPORTED_RULE_FILE_PATH = 'cypress/test_files/expected_rules_export.ndjson';
|
||||
|
||||
// SKIP: https://github.com/elastic/kibana/issues/83769
|
||||
describe.skip('Export rules', () => {
|
||||
describe('Export rules', () => {
|
||||
before(() => {
|
||||
esArchiverLoad('export_rule');
|
||||
cy.server();
|
||||
|
|
|
@ -17,8 +17,7 @@ import { loginAndWaitForPage } from '../tasks/login';
|
|||
|
||||
import { DETECTIONS_URL } from '../urls/navigation';
|
||||
|
||||
// FLAKY: https://github.com/elastic/kibana/issues/83771
|
||||
describe.skip('Alerts timeline', () => {
|
||||
describe('Alerts timeline', () => {
|
||||
beforeEach(() => {
|
||||
esArchiverLoad('timeline_alerts');
|
||||
loginAndWaitForPage(DETECTIONS_URL);
|
||||
|
|
|
@ -17,8 +17,7 @@ import { loginAndWaitForPageWithoutDateRange } from '../tasks/login';
|
|||
|
||||
import { CASES_URL } from '../urls/navigation';
|
||||
|
||||
// FLAKY: https://github.com/elastic/kibana/issues/65278
|
||||
describe.skip('Cases connectors', () => {
|
||||
describe('Cases connectors', () => {
|
||||
before(() => {
|
||||
cy.server();
|
||||
cy.route('POST', '**/api/actions/action').as('createConnector');
|
||||
|
|
|
@ -91,6 +91,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) {
|
|||
});
|
||||
expect(Date.parse(response.body.createdAt)).to.be.greaterThan(0);
|
||||
expect(Date.parse(response.body.updatedAt)).to.be.greaterThan(0);
|
||||
expect(Date.parse(response.body.updatedAt)).to.eql(Date.parse(response.body.createdAt));
|
||||
|
||||
expect(typeof response.body.scheduledTaskId).to.be('string');
|
||||
const { _source: taskRecord } = await getScheduledTask(response.body.scheduledTaskId);
|
||||
|
|
|
@ -63,6 +63,7 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon
|
|||
);
|
||||
expect(response.status).to.eql(200);
|
||||
const alertId = response.body.id;
|
||||
const alertUpdatedAt = response.body.updatedAt;
|
||||
dates.push(response.body.executionStatus.lastExecutionDate);
|
||||
objectRemover.add(Spaces.space1.id, alertId, 'alert', 'alerts');
|
||||
|
||||
|
@ -70,6 +71,7 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon
|
|||
dates.push(executionStatus.lastExecutionDate);
|
||||
dates.push(Date.now());
|
||||
ensureDatetimesAreOrdered(dates);
|
||||
ensureAlertUpdatedAtHasNotChanged(alertId, alertUpdatedAt);
|
||||
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
|
@ -97,6 +99,7 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon
|
|||
);
|
||||
expect(response.status).to.eql(200);
|
||||
const alertId = response.body.id;
|
||||
const alertUpdatedAt = response.body.updatedAt;
|
||||
dates.push(response.body.executionStatus.lastExecutionDate);
|
||||
objectRemover.add(Spaces.space1.id, alertId, 'alert', 'alerts');
|
||||
|
||||
|
@ -104,6 +107,7 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon
|
|||
dates.push(executionStatus.lastExecutionDate);
|
||||
dates.push(Date.now());
|
||||
ensureDatetimesAreOrdered(dates);
|
||||
ensureAlertUpdatedAtHasNotChanged(alertId, alertUpdatedAt);
|
||||
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
|
@ -128,6 +132,7 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon
|
|||
);
|
||||
expect(response.status).to.eql(200);
|
||||
const alertId = response.body.id;
|
||||
const alertUpdatedAt = response.body.updatedAt;
|
||||
dates.push(response.body.executionStatus.lastExecutionDate);
|
||||
objectRemover.add(Spaces.space1.id, alertId, 'alert', 'alerts');
|
||||
|
||||
|
@ -135,6 +140,7 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon
|
|||
dates.push(executionStatus.lastExecutionDate);
|
||||
dates.push(Date.now());
|
||||
ensureDatetimesAreOrdered(dates);
|
||||
ensureAlertUpdatedAtHasNotChanged(alertId, alertUpdatedAt);
|
||||
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
|
@ -162,12 +168,14 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon
|
|||
);
|
||||
expect(response.status).to.eql(200);
|
||||
const alertId = response.body.id;
|
||||
const alertUpdatedAt = response.body.updatedAt;
|
||||
objectRemover.add(Spaces.space1.id, alertId, 'alert', 'alerts');
|
||||
|
||||
const executionStatus = await waitForStatus(alertId, new Set(['error']));
|
||||
expect(executionStatus.error).to.be.ok();
|
||||
expect(executionStatus.error.reason).to.be('execute');
|
||||
expect(executionStatus.error.message).to.be('this alert is intended to fail');
|
||||
ensureAlertUpdatedAtHasNotChanged(alertId, alertUpdatedAt);
|
||||
});
|
||||
|
||||
it('should eventually have error reason "unknown" when appropriate', async () => {
|
||||
|
@ -183,6 +191,7 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon
|
|||
);
|
||||
expect(response.status).to.eql(200);
|
||||
const alertId = response.body.id;
|
||||
const alertUpdatedAt = response.body.updatedAt;
|
||||
objectRemover.add(Spaces.space1.id, alertId, 'alert', 'alerts');
|
||||
|
||||
let executionStatus = await waitForStatus(alertId, new Set(['ok']));
|
||||
|
@ -201,6 +210,7 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon
|
|||
executionStatus = await waitForStatus(alertId, new Set(['error']));
|
||||
expect(executionStatus.error).to.be.ok();
|
||||
expect(executionStatus.error.reason).to.be('unknown');
|
||||
ensureAlertUpdatedAtHasNotChanged(alertId, alertUpdatedAt);
|
||||
|
||||
const message = 'params invalid: [param1]: expected value of type [string] but got [number]';
|
||||
expect(executionStatus.error.message).to.be(message);
|
||||
|
@ -306,6 +316,18 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon
|
|||
await delay(WaitForStatusIncrement);
|
||||
return await waitForStatus(id, statuses, waitMillis - WaitForStatusIncrement);
|
||||
}
|
||||
|
||||
async function ensureAlertUpdatedAtHasNotChanged(alertId: string, originalUpdatedAt: string) {
|
||||
const response = await supertest.get(
|
||||
`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${alertId}`
|
||||
);
|
||||
const { updatedAt, executionStatus } = response.body;
|
||||
expect(Date.parse(updatedAt)).to.be.greaterThan(0);
|
||||
expect(Date.parse(updatedAt)).to.eql(Date.parse(originalUpdatedAt));
|
||||
expect(Date.parse(executionStatus.lastExecutionDate)).to.be.greaterThan(
|
||||
Date.parse(originalUpdatedAt)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function expectErrorExecutionStatus(executionStatus: Record<string, any>, startDate: number) {
|
||||
|
|
|
@ -82,5 +82,14 @@ export default function createGetTests({ getService }: FtrProviderContext) {
|
|||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('7.11.0 migrates alerts to contain `updatedAt` field', async () => {
|
||||
const response = await supertest.get(
|
||||
`${getUrlPrefix(``)}/api/alerts/alert/74f3e6d7-b7bb-477d-ac28-92ee22728e6e`
|
||||
);
|
||||
|
||||
expect(response.status).to.eql(200);
|
||||
expect(response.body.updatedAt).to.eql('2020-06-17T15:35:39.839Z');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
Binary file not shown.
|
@ -321,6 +321,9 @@
|
|||
"throttle": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"updatedAt": {
|
||||
"type": "date"
|
||||
},
|
||||
"updatedBy": {
|
||||
"type": "keyword"
|
||||
},
|
||||
|
|
Binary file not shown.
|
@ -191,6 +191,9 @@
|
|||
"throttle": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"updatedAt": {
|
||||
"type": "date"
|
||||
},
|
||||
"updatedBy": {
|
||||
"type": "keyword"
|
||||
}
|
||||
|
|
Binary file not shown.
|
@ -322,6 +322,9 @@
|
|||
"throttle": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"updatedAt": {
|
||||
"type": "date"
|
||||
},
|
||||
"updatedBy": {
|
||||
"type": "keyword"
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue