* plugged Task Manager lifecycle into status reactively
* fixed tests
* Revert "fixed tests"
This reverts commit e9f2cd05bd
.
* made action group fields optional
* revert deletion
* again
* extracted action type for mto its own component
* extracted more sections of the action form to their own components
* updated icon
* added docs
* fixed always firing alert
* fixed export of components
* fixed react warning
* Adding flag for notifying on state change
* Updating logic in task runner
* Starting to update tests
* Adding tests
* Fixing types check
* Tests and types
* Tests
* Tests
* Tests
* Tests
* Tests
* Renaming field to a more descriptive name. Adding migrations
* Renaming field to a more descriptive name. Adding migrations
* Fixing tests
* Type check and tests
* Moving schedule and notify interval to bottom of flyout. Implementing dropdown from mockup in new component
* Changing boolean flag to enum type and updating in triggers_actions_ui
* Changing boolean flag to enum type and updating in alerts plugin
* Fixing types check
* Fixing monitoring jest tests
* Changing last references to old variable names
* Moving form inputs back to the top
* Renaming to alert_notify_when
* Updating functional tests
* Adding new functional test for notifyWhen onActionGroupChange
* Updating wording
* Incorporating action subgroups into logic
* PR fixes
* Updating functional test
* Fixing types check
* Changing default throttle interval to hour
* Fixing types check
Co-authored-by: Gidi Meir Morris <github@gidi.io>
665 lines
19 KiB
TypeScript
665 lines
19 KiB
TypeScript
/*
|
|
* 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 { SanitizedAlert, AlertInstanceSummary } from '../types';
|
|
import { IValidatedEvent } from '../../../event_log/server';
|
|
import { EVENT_LOG_ACTIONS, EVENT_LOG_PROVIDER, LEGACY_EVENT_LOG_ACTIONS } from '../plugin';
|
|
import { alertInstanceSummaryFromEventLog } from './alert_instance_summary_from_event_log';
|
|
|
|
const ONE_HOUR_IN_MILLIS = 60 * 60 * 1000;
|
|
const dateStart = '2020-06-18T00:00:00.000Z';
|
|
const dateEnd = dateString(dateStart, ONE_HOUR_IN_MILLIS);
|
|
|
|
describe('alertInstanceSummaryFromEventLog', () => {
|
|
test('no events and muted ids', async () => {
|
|
const alert = createAlert({});
|
|
const events: IValidatedEvent[] = [];
|
|
const summary: AlertInstanceSummary = alertInstanceSummaryFromEventLog({
|
|
alert,
|
|
events,
|
|
dateStart,
|
|
dateEnd,
|
|
});
|
|
|
|
expect(summary).toMatchInlineSnapshot(`
|
|
Object {
|
|
"alertTypeId": "123",
|
|
"consumer": "alert-consumer",
|
|
"enabled": false,
|
|
"errorMessages": Array [],
|
|
"id": "alert-123",
|
|
"instances": Object {},
|
|
"lastRun": undefined,
|
|
"muteAll": false,
|
|
"name": "alert-name",
|
|
"status": "OK",
|
|
"statusEndDate": "2020-06-18T01:00:00.000Z",
|
|
"statusStartDate": "2020-06-18T00:00:00.000Z",
|
|
"tags": Array [],
|
|
"throttle": null,
|
|
}
|
|
`);
|
|
});
|
|
|
|
test('different alert properties', async () => {
|
|
const alert = createAlert({
|
|
id: 'alert-456',
|
|
alertTypeId: '456',
|
|
schedule: { interval: '100s' },
|
|
enabled: true,
|
|
name: 'alert-name-2',
|
|
tags: ['tag-1', 'tag-2'],
|
|
consumer: 'alert-consumer-2',
|
|
throttle: '1h',
|
|
muteAll: true,
|
|
});
|
|
const events: IValidatedEvent[] = [];
|
|
const summary: AlertInstanceSummary = alertInstanceSummaryFromEventLog({
|
|
alert,
|
|
events,
|
|
dateStart: dateString(dateEnd, ONE_HOUR_IN_MILLIS),
|
|
dateEnd: dateString(dateEnd, ONE_HOUR_IN_MILLIS * 2),
|
|
});
|
|
|
|
expect(summary).toMatchInlineSnapshot(`
|
|
Object {
|
|
"alertTypeId": "456",
|
|
"consumer": "alert-consumer-2",
|
|
"enabled": true,
|
|
"errorMessages": Array [],
|
|
"id": "alert-456",
|
|
"instances": Object {},
|
|
"lastRun": undefined,
|
|
"muteAll": true,
|
|
"name": "alert-name-2",
|
|
"status": "OK",
|
|
"statusEndDate": "2020-06-18T03:00:00.000Z",
|
|
"statusStartDate": "2020-06-18T02:00:00.000Z",
|
|
"tags": Array [
|
|
"tag-1",
|
|
"tag-2",
|
|
],
|
|
"throttle": "1h",
|
|
}
|
|
`);
|
|
});
|
|
|
|
test('two muted instances', async () => {
|
|
const alert = createAlert({
|
|
mutedInstanceIds: ['instance-1', 'instance-2'],
|
|
});
|
|
const events: IValidatedEvent[] = [];
|
|
const summary: AlertInstanceSummary = alertInstanceSummaryFromEventLog({
|
|
alert,
|
|
events,
|
|
dateStart,
|
|
dateEnd,
|
|
});
|
|
|
|
const { lastRun, status, instances } = summary;
|
|
expect({ lastRun, status, instances }).toMatchInlineSnapshot(`
|
|
Object {
|
|
"instances": Object {
|
|
"instance-1": Object {
|
|
"actionGroupId": undefined,
|
|
"actionSubgroup": undefined,
|
|
"activeStartDate": undefined,
|
|
"muted": true,
|
|
"status": "OK",
|
|
},
|
|
"instance-2": Object {
|
|
"actionGroupId": undefined,
|
|
"actionSubgroup": undefined,
|
|
"activeStartDate": undefined,
|
|
"muted": true,
|
|
"status": "OK",
|
|
},
|
|
},
|
|
"lastRun": undefined,
|
|
"status": "OK",
|
|
}
|
|
`);
|
|
});
|
|
|
|
test('active alert but no instances', async () => {
|
|
const alert = createAlert({});
|
|
const eventsFactory = new EventsFactory();
|
|
const events = eventsFactory.addExecute().advanceTime(10000).addExecute().getEvents();
|
|
|
|
const summary: AlertInstanceSummary = alertInstanceSummaryFromEventLog({
|
|
alert,
|
|
events,
|
|
dateStart,
|
|
dateEnd,
|
|
});
|
|
|
|
const { lastRun, status, instances } = summary;
|
|
expect({ lastRun, status, instances }).toMatchInlineSnapshot(`
|
|
Object {
|
|
"instances": Object {},
|
|
"lastRun": "2020-06-18T00:00:10.000Z",
|
|
"status": "OK",
|
|
}
|
|
`);
|
|
});
|
|
|
|
test('active alert with no instances but has errors', async () => {
|
|
const alert = createAlert({});
|
|
const eventsFactory = new EventsFactory();
|
|
const events = eventsFactory
|
|
.addExecute('oof!')
|
|
.advanceTime(10000)
|
|
.addExecute('rut roh!')
|
|
.getEvents();
|
|
|
|
const summary: AlertInstanceSummary = alertInstanceSummaryFromEventLog({
|
|
alert,
|
|
events,
|
|
dateStart,
|
|
dateEnd,
|
|
});
|
|
|
|
const { lastRun, status, errorMessages, instances } = summary;
|
|
expect({ lastRun, status, errorMessages, instances }).toMatchInlineSnapshot(`
|
|
Object {
|
|
"errorMessages": Array [
|
|
Object {
|
|
"date": "2020-06-18T00:00:00.000Z",
|
|
"message": "oof!",
|
|
},
|
|
Object {
|
|
"date": "2020-06-18T00:00:10.000Z",
|
|
"message": "rut roh!",
|
|
},
|
|
],
|
|
"instances": Object {},
|
|
"lastRun": "2020-06-18T00:00:10.000Z",
|
|
"status": "Error",
|
|
}
|
|
`);
|
|
});
|
|
|
|
test('alert with currently inactive instance', async () => {
|
|
const alert = createAlert({});
|
|
const eventsFactory = new EventsFactory();
|
|
const events = eventsFactory
|
|
.addExecute()
|
|
.addNewInstance('instance-1')
|
|
.addActiveInstance('instance-1', 'action group A')
|
|
.advanceTime(10000)
|
|
.addExecute()
|
|
.addRecoveredInstance('instance-1')
|
|
.getEvents();
|
|
|
|
const summary: AlertInstanceSummary = alertInstanceSummaryFromEventLog({
|
|
alert,
|
|
events,
|
|
dateStart,
|
|
dateEnd,
|
|
});
|
|
|
|
const { lastRun, status, instances } = summary;
|
|
expect({ lastRun, status, instances }).toMatchInlineSnapshot(`
|
|
Object {
|
|
"instances": Object {
|
|
"instance-1": Object {
|
|
"actionGroupId": undefined,
|
|
"actionSubgroup": undefined,
|
|
"activeStartDate": undefined,
|
|
"muted": false,
|
|
"status": "OK",
|
|
},
|
|
},
|
|
"lastRun": "2020-06-18T00:00:10.000Z",
|
|
"status": "OK",
|
|
}
|
|
`);
|
|
});
|
|
|
|
test('legacy alert with currently inactive instance', async () => {
|
|
const alert = createAlert({});
|
|
const eventsFactory = new EventsFactory();
|
|
const events = eventsFactory
|
|
.addExecute()
|
|
.addNewInstance('instance-1')
|
|
.addActiveInstance('instance-1', 'action group A')
|
|
.advanceTime(10000)
|
|
.addExecute()
|
|
.addLegacyResolvedInstance('instance-1')
|
|
.getEvents();
|
|
|
|
const summary: AlertInstanceSummary = alertInstanceSummaryFromEventLog({
|
|
alert,
|
|
events,
|
|
dateStart,
|
|
dateEnd,
|
|
});
|
|
|
|
const { lastRun, status, instances } = summary;
|
|
expect({ lastRun, status, instances }).toMatchInlineSnapshot(`
|
|
Object {
|
|
"instances": Object {
|
|
"instance-1": Object {
|
|
"actionGroupId": undefined,
|
|
"actionSubgroup": undefined,
|
|
"activeStartDate": undefined,
|
|
"muted": false,
|
|
"status": "OK",
|
|
},
|
|
},
|
|
"lastRun": "2020-06-18T00:00:10.000Z",
|
|
"status": "OK",
|
|
}
|
|
`);
|
|
});
|
|
|
|
test('alert with currently inactive instance, no new-instance', async () => {
|
|
const alert = createAlert({});
|
|
const eventsFactory = new EventsFactory();
|
|
const events = eventsFactory
|
|
.addExecute()
|
|
.addActiveInstance('instance-1', 'action group A')
|
|
.advanceTime(10000)
|
|
.addExecute()
|
|
.addRecoveredInstance('instance-1')
|
|
.getEvents();
|
|
|
|
const summary: AlertInstanceSummary = alertInstanceSummaryFromEventLog({
|
|
alert,
|
|
events,
|
|
dateStart,
|
|
dateEnd,
|
|
});
|
|
|
|
const { lastRun, status, instances } = summary;
|
|
expect({ lastRun, status, instances }).toMatchInlineSnapshot(`
|
|
Object {
|
|
"instances": Object {
|
|
"instance-1": Object {
|
|
"actionGroupId": undefined,
|
|
"actionSubgroup": undefined,
|
|
"activeStartDate": undefined,
|
|
"muted": false,
|
|
"status": "OK",
|
|
},
|
|
},
|
|
"lastRun": "2020-06-18T00:00:10.000Z",
|
|
"status": "OK",
|
|
}
|
|
`);
|
|
});
|
|
|
|
test('alert with currently active instance', async () => {
|
|
const alert = createAlert({});
|
|
const eventsFactory = new EventsFactory();
|
|
const events = eventsFactory
|
|
.addExecute()
|
|
.addNewInstance('instance-1')
|
|
.addActiveInstance('instance-1', 'action group A')
|
|
.advanceTime(10000)
|
|
.addExecute()
|
|
.addActiveInstance('instance-1', 'action group A')
|
|
.getEvents();
|
|
|
|
const summary: AlertInstanceSummary = alertInstanceSummaryFromEventLog({
|
|
alert,
|
|
events,
|
|
dateStart,
|
|
dateEnd,
|
|
});
|
|
|
|
const { lastRun, status, instances } = summary;
|
|
expect({ lastRun, status, instances }).toMatchInlineSnapshot(`
|
|
Object {
|
|
"instances": Object {
|
|
"instance-1": Object {
|
|
"actionGroupId": "action group A",
|
|
"actionSubgroup": undefined,
|
|
"activeStartDate": "2020-06-18T00:00:00.000Z",
|
|
"muted": false,
|
|
"status": "Active",
|
|
},
|
|
},
|
|
"lastRun": "2020-06-18T00:00:10.000Z",
|
|
"status": "Active",
|
|
}
|
|
`);
|
|
});
|
|
|
|
test('alert with currently active instance with no action group in event log', async () => {
|
|
const alert = createAlert({});
|
|
const eventsFactory = new EventsFactory();
|
|
const events = eventsFactory
|
|
.addExecute()
|
|
.addNewInstance('instance-1')
|
|
.addActiveInstance('instance-1', undefined)
|
|
.advanceTime(10000)
|
|
.addExecute()
|
|
.addActiveInstance('instance-1', undefined)
|
|
.getEvents();
|
|
|
|
const summary: AlertInstanceSummary = alertInstanceSummaryFromEventLog({
|
|
alert,
|
|
events,
|
|
dateStart,
|
|
dateEnd,
|
|
});
|
|
|
|
const { lastRun, status, instances } = summary;
|
|
expect({ lastRun, status, instances }).toMatchInlineSnapshot(`
|
|
Object {
|
|
"instances": Object {
|
|
"instance-1": Object {
|
|
"actionGroupId": undefined,
|
|
"actionSubgroup": undefined,
|
|
"activeStartDate": "2020-06-18T00:00:00.000Z",
|
|
"muted": false,
|
|
"status": "Active",
|
|
},
|
|
},
|
|
"lastRun": "2020-06-18T00:00:10.000Z",
|
|
"status": "Active",
|
|
}
|
|
`);
|
|
});
|
|
|
|
test('alert with currently active instance that switched action groups', async () => {
|
|
const alert = createAlert({});
|
|
const eventsFactory = new EventsFactory();
|
|
const events = eventsFactory
|
|
.addExecute()
|
|
.addNewInstance('instance-1')
|
|
.addActiveInstance('instance-1', 'action group A')
|
|
.advanceTime(10000)
|
|
.addExecute()
|
|
.addActiveInstance('instance-1', 'action group B')
|
|
.getEvents();
|
|
|
|
const summary: AlertInstanceSummary = alertInstanceSummaryFromEventLog({
|
|
alert,
|
|
events,
|
|
dateStart,
|
|
dateEnd,
|
|
});
|
|
|
|
const { lastRun, status, instances } = summary;
|
|
expect({ lastRun, status, instances }).toMatchInlineSnapshot(`
|
|
Object {
|
|
"instances": Object {
|
|
"instance-1": Object {
|
|
"actionGroupId": "action group B",
|
|
"actionSubgroup": undefined,
|
|
"activeStartDate": "2020-06-18T00:00:00.000Z",
|
|
"muted": false,
|
|
"status": "Active",
|
|
},
|
|
},
|
|
"lastRun": "2020-06-18T00:00:10.000Z",
|
|
"status": "Active",
|
|
}
|
|
`);
|
|
});
|
|
|
|
test('alert with currently active instance, no new-instance', async () => {
|
|
const alert = createAlert({});
|
|
const eventsFactory = new EventsFactory();
|
|
const events = eventsFactory
|
|
.addExecute()
|
|
.addActiveInstance('instance-1', 'action group A')
|
|
.advanceTime(10000)
|
|
.addExecute()
|
|
.addActiveInstance('instance-1', 'action group A')
|
|
.getEvents();
|
|
|
|
const summary: AlertInstanceSummary = alertInstanceSummaryFromEventLog({
|
|
alert,
|
|
events,
|
|
dateStart,
|
|
dateEnd,
|
|
});
|
|
|
|
const { lastRun, status, instances } = summary;
|
|
expect({ lastRun, status, instances }).toMatchInlineSnapshot(`
|
|
Object {
|
|
"instances": Object {
|
|
"instance-1": Object {
|
|
"actionGroupId": "action group A",
|
|
"actionSubgroup": undefined,
|
|
"activeStartDate": undefined,
|
|
"muted": false,
|
|
"status": "Active",
|
|
},
|
|
},
|
|
"lastRun": "2020-06-18T00:00:10.000Z",
|
|
"status": "Active",
|
|
}
|
|
`);
|
|
});
|
|
|
|
test('alert with active and inactive muted alerts', async () => {
|
|
const alert = createAlert({ mutedInstanceIds: ['instance-1', 'instance-2'] });
|
|
const eventsFactory = new EventsFactory();
|
|
const events = eventsFactory
|
|
.addExecute()
|
|
.addNewInstance('instance-1')
|
|
.addActiveInstance('instance-1', 'action group A')
|
|
.addNewInstance('instance-2')
|
|
.addActiveInstance('instance-2', 'action group B')
|
|
.advanceTime(10000)
|
|
.addExecute()
|
|
.addActiveInstance('instance-1', 'action group A')
|
|
.addRecoveredInstance('instance-2')
|
|
.getEvents();
|
|
|
|
const summary: AlertInstanceSummary = alertInstanceSummaryFromEventLog({
|
|
alert,
|
|
events,
|
|
dateStart,
|
|
dateEnd,
|
|
});
|
|
|
|
const { lastRun, status, instances } = summary;
|
|
expect({ lastRun, status, instances }).toMatchInlineSnapshot(`
|
|
Object {
|
|
"instances": Object {
|
|
"instance-1": Object {
|
|
"actionGroupId": "action group A",
|
|
"actionSubgroup": undefined,
|
|
"activeStartDate": "2020-06-18T00:00:00.000Z",
|
|
"muted": true,
|
|
"status": "Active",
|
|
},
|
|
"instance-2": Object {
|
|
"actionGroupId": undefined,
|
|
"actionSubgroup": undefined,
|
|
"activeStartDate": undefined,
|
|
"muted": true,
|
|
"status": "OK",
|
|
},
|
|
},
|
|
"lastRun": "2020-06-18T00:00:10.000Z",
|
|
"status": "Active",
|
|
}
|
|
`);
|
|
});
|
|
|
|
test('alert with active and inactive alerts over many executes', async () => {
|
|
const alert = createAlert({});
|
|
const eventsFactory = new EventsFactory();
|
|
const events = eventsFactory
|
|
.addExecute()
|
|
.addNewInstance('instance-1')
|
|
.addActiveInstance('instance-1', 'action group A')
|
|
.addNewInstance('instance-2')
|
|
.addActiveInstance('instance-2', 'action group B')
|
|
.advanceTime(10000)
|
|
.addExecute()
|
|
.addActiveInstance('instance-1', 'action group A')
|
|
.addRecoveredInstance('instance-2')
|
|
.advanceTime(10000)
|
|
.addExecute()
|
|
.addActiveInstance('instance-1', 'action group B')
|
|
.advanceTime(10000)
|
|
.addExecute()
|
|
.addActiveInstance('instance-1', 'action group B')
|
|
.getEvents();
|
|
|
|
const summary: AlertInstanceSummary = alertInstanceSummaryFromEventLog({
|
|
alert,
|
|
events,
|
|
dateStart,
|
|
dateEnd,
|
|
});
|
|
|
|
const { lastRun, status, instances } = summary;
|
|
expect({ lastRun, status, instances }).toMatchInlineSnapshot(`
|
|
Object {
|
|
"instances": Object {
|
|
"instance-1": Object {
|
|
"actionGroupId": "action group B",
|
|
"actionSubgroup": undefined,
|
|
"activeStartDate": "2020-06-18T00:00:00.000Z",
|
|
"muted": false,
|
|
"status": "Active",
|
|
},
|
|
"instance-2": Object {
|
|
"actionGroupId": undefined,
|
|
"actionSubgroup": undefined,
|
|
"activeStartDate": undefined,
|
|
"muted": false,
|
|
"status": "OK",
|
|
},
|
|
},
|
|
"lastRun": "2020-06-18T00:00:30.000Z",
|
|
"status": "Active",
|
|
}
|
|
`);
|
|
});
|
|
});
|
|
|
|
function dateString(isoBaseDate: string, offsetMillis = 0): string {
|
|
return new Date(Date.parse(isoBaseDate) + offsetMillis).toISOString();
|
|
}
|
|
|
|
export class EventsFactory {
|
|
private events: IValidatedEvent[] = [];
|
|
|
|
constructor(private date: string = dateStart) {}
|
|
|
|
getEvents(): IValidatedEvent[] {
|
|
// ES normally returns events sorted newest to oldest, so we need to sort
|
|
// that way also
|
|
const events = this.events.slice();
|
|
events.sort((a, b) => -a!['@timestamp']!.localeCompare(b!['@timestamp']!));
|
|
return events;
|
|
}
|
|
|
|
getTime(): string {
|
|
return this.date;
|
|
}
|
|
|
|
advanceTime(millis: number): EventsFactory {
|
|
this.date = dateString(this.date, millis);
|
|
return this;
|
|
}
|
|
|
|
addExecute(errorMessage?: string): EventsFactory {
|
|
let event: IValidatedEvent = {
|
|
'@timestamp': this.date,
|
|
event: {
|
|
provider: EVENT_LOG_PROVIDER,
|
|
action: EVENT_LOG_ACTIONS.execute,
|
|
},
|
|
};
|
|
|
|
if (errorMessage) {
|
|
event = { ...event, error: { message: errorMessage } };
|
|
}
|
|
|
|
this.events.push(event);
|
|
return this;
|
|
}
|
|
|
|
addActiveInstance(instanceId: string, actionGroupId: string | undefined): EventsFactory {
|
|
const kibanaAlerting = actionGroupId
|
|
? { instance_id: instanceId, action_group_id: actionGroupId }
|
|
: { instance_id: instanceId };
|
|
this.events.push({
|
|
'@timestamp': this.date,
|
|
event: {
|
|
provider: EVENT_LOG_PROVIDER,
|
|
action: EVENT_LOG_ACTIONS.activeInstance,
|
|
},
|
|
kibana: { alerting: kibanaAlerting },
|
|
});
|
|
return this;
|
|
}
|
|
|
|
addNewInstance(instanceId: string): EventsFactory {
|
|
this.events.push({
|
|
'@timestamp': this.date,
|
|
event: {
|
|
provider: EVENT_LOG_PROVIDER,
|
|
action: EVENT_LOG_ACTIONS.newInstance,
|
|
},
|
|
kibana: { alerting: { instance_id: instanceId } },
|
|
});
|
|
return this;
|
|
}
|
|
|
|
addRecoveredInstance(instanceId: string): EventsFactory {
|
|
this.events.push({
|
|
'@timestamp': this.date,
|
|
event: {
|
|
provider: EVENT_LOG_PROVIDER,
|
|
action: EVENT_LOG_ACTIONS.recoveredInstance,
|
|
},
|
|
kibana: { alerting: { instance_id: instanceId } },
|
|
});
|
|
return this;
|
|
}
|
|
|
|
addLegacyResolvedInstance(instanceId: string): EventsFactory {
|
|
this.events.push({
|
|
'@timestamp': this.date,
|
|
event: {
|
|
provider: EVENT_LOG_PROVIDER,
|
|
action: LEGACY_EVENT_LOG_ACTIONS.resolvedInstance,
|
|
},
|
|
kibana: { alerting: { instance_id: instanceId } },
|
|
});
|
|
return this;
|
|
}
|
|
}
|
|
|
|
function createAlert(overrides: Partial<SanitizedAlert>): SanitizedAlert {
|
|
return { ...BaseAlert, ...overrides };
|
|
}
|
|
|
|
const BaseAlert: SanitizedAlert = {
|
|
id: 'alert-123',
|
|
alertTypeId: '123',
|
|
schedule: { interval: '10s' },
|
|
enabled: false,
|
|
name: 'alert-name',
|
|
tags: [],
|
|
consumer: 'alert-consumer',
|
|
throttle: null,
|
|
notifyWhen: null,
|
|
muteAll: false,
|
|
mutedInstanceIds: [],
|
|
params: { bar: true },
|
|
actions: [],
|
|
createdBy: null,
|
|
updatedBy: null,
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
apiKeyOwner: null,
|
|
executionStatus: {
|
|
status: 'unknown',
|
|
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
|
|
},
|
|
};
|