diff --git a/packages/kbn-check-saved-objects-cli/src/migrations/__fixtures__/entity-engine-descriptor-v2/10.5.0.json b/packages/kbn-check-saved-objects-cli/src/migrations/__fixtures__/entity-engine-descriptor-v2/10.5.0.json new file mode 100644 index 0000000000000..8145fe840df5a --- /dev/null +++ b/packages/kbn-check-saved-objects-cli/src/migrations/__fixtures__/entity-engine-descriptor-v2/10.5.0.json @@ -0,0 +1,37 @@ +{ + "10.4.0": [ + { + "type": "user", + "status": "started", + "logExtractionState": { + "lastExecutionTimestamp": "2026-03-04T17:37:20.000Z" + }, + "versionState": { + "version": "2", + "state": "running", + "isMigratedFromV1": true + } + } + ], + "10.5.0": [ + { + "type": "user", + "status": "started", + "logExtractionState": { + "paginationTimestamp": null, + "paginationId": null, + "lastExecutionTimestamp": "2026-03-04T17:37:20.000Z", + "logsPageCursorStartTimestamp": null, + "logsPageCursorStartId": null, + "logsPageCursorEndTimestamp": null, + "logsPageCursorEndId": null + }, + "error": null, + "versionState": { + "version": "2", + "state": "running", + "isMigratedFromV1": true + } + } + ] +} diff --git a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts index eee322bc5f2fe..9433c712b5806 100644 --- a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts +++ b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts @@ -91,7 +91,7 @@ describe('checking migration metadata changes on all registered SO types', () => "entity-analytics-monitoring-entity-source": "a7307895e5c38accc21ebea852bd46ef98ebb73f7a73d116c8dfcd3932f57899", "entity-definition": "28cde811da5815b6fdebb0112419b593253fb5053d60c2cf29b50af3be058c67", "entity-discovery-api-key": "2350e2ce1b6e913f55aef2cd04971b15604b03e049370ef10949c3eeec38c46f", - "entity-engine-descriptor-v2": "eb2f5ab5f8f620f00a2bb7ee8a99da7ee5187905b8e20ec95ce654cf84fe7b78", + "entity-engine-descriptor-v2": "44b60aa3d3d4583082b58500b297f2dddb4ffc14b77ad1d568b70536ca4ae787", "entity-engine-status": "005903620a00737932aa54ae57817b078810b2f71cc42e7715d1c22c5e5b715e", "entity-store-global-state": "8581bc65d1b2bf6d0218b693509129a2515599aeff8933d85353a3fb28d52bda", "epm-packages": "46e4129dba3ac33d4924239672169f12ad75536e9f44f695964220a80ebfeaca", @@ -634,6 +634,7 @@ describe('checking migration metadata changes on all registered SO types', () => "entity-engine-descriptor-v2|global: ae6bdc1b39c24032ba32bbc8a5d032f63d576bb0", "entity-engine-descriptor-v2|mappings: c8194b9fb0346df2967cedb78fb4a3c7139f5ff1", "entity-engine-descriptor-v2|schemas: da39a3ee5e6b4b0d3255bfef95601890afd80709", + "entity-engine-descriptor-v2|10.5.0: 33de56b01a358e06b3e3729f22b7f281e51c9f4bce4dd86067d368462018d258", "entity-engine-descriptor-v2|10.4.0: 52668e17842853b15deb997ceddb19b551e6d38b18d2440123db05bf844e157e", "entity-engine-descriptor-v2|10.3.0: 14e40ca4628481c3db33990704f13a4dcef6e9ae2e6e7539606dc58194169c96", "entity-engine-descriptor-v2|10.2.0: 114c9ccc2679bf611a71b9ba2fc9ee842ab1372e808dc4bf45a061c5ae0e2081", @@ -1455,7 +1456,7 @@ describe('checking migration metadata changes on all registered SO types', () => "entity-analytics-monitoring-entity-source": "10.2.0", "entity-definition": "10.4.0", "entity-discovery-api-key": "10.0.0", - "entity-engine-descriptor-v2": "10.4.0", + "entity-engine-descriptor-v2": "10.5.0", "entity-engine-status": "10.2.0", "entity-store-global-state": "10.1.0", "epm-packages": "10.8.0", @@ -1621,7 +1622,7 @@ describe('checking migration metadata changes on all registered SO types', () => "entity-analytics-monitoring-entity-source": "10.2.0", "entity-definition": "10.4.0", "entity-discovery-api-key": "0.0.0", - "entity-engine-descriptor-v2": "10.4.0", + "entity-engine-descriptor-v2": "10.5.0", "entity-engine-status": "10.2.0", "entity-store-global-state": "10.1.0", "epm-packages": "10.8.0", diff --git a/x-pack/solutions/security/plugins/entity_store/server/domain/logs_extraction/logs_extraction_client.test.ts b/x-pack/solutions/security/plugins/entity_store/server/domain/logs_extraction/logs_extraction_client.test.ts index 6089d5271dd82..97aca5998e9fc 100644 --- a/x-pack/solutions/security/plugins/entity_store/server/domain/logs_extraction/logs_extraction_client.test.ts +++ b/x-pack/solutions/security/plugins/entity_store/server/domain/logs_extraction/logs_extraction_client.test.ts @@ -97,13 +97,13 @@ function createMockEngineDescriptor( }> ) { const logExtractionState = { - paginationTimestamp: overrides?.paginationTimestamp, - paginationId: overrides?.paginationId, - lastExecutionTimestamp: overrides?.lastExecutionTimestamp, - logsPageCursorStartTimestamp: overrides?.logsPageCursorStartTimestamp, - logsPageCursorStartId: overrides?.logsPageCursorStartId, - logsPageCursorEndTimestamp: overrides?.logsPageCursorEndTimestamp, - logsPageCursorEndId: overrides?.logsPageCursorEndId, + paginationTimestamp: overrides?.paginationTimestamp ?? null, + paginationId: overrides?.paginationId ?? null, + lastExecutionTimestamp: overrides?.lastExecutionTimestamp ?? null, + logsPageCursorStartTimestamp: overrides?.logsPageCursorStartTimestamp ?? null, + logsPageCursorStartId: overrides?.logsPageCursorStartId ?? null, + logsPageCursorEndTimestamp: overrides?.logsPageCursorEndTimestamp ?? null, + logsPageCursorEndId: overrides?.logsPageCursorEndId ?? null, }; return { type, @@ -136,7 +136,7 @@ describe('LogsExtractionClient', () => { let mockEsClient: jest.Mocked; let mockDataViewsService: jest.Mocked; let mockEngineDescriptorClient: jest.Mocked< - Pick + Pick >; let mockGlobalStateClient: ReturnType; let mockCcsLogsExtractionClient: ReturnType; @@ -152,7 +152,6 @@ describe('LogsExtractionClient', () => { mockEngineDescriptorClient = { findOrThrow: jest.fn(), update: jest.fn().mockResolvedValue({}), - updateWith: jest.fn().mockResolvedValue({}), }; mockGlobalStateClient = createMockGlobalStateClient(); mockCcsLogsExtractionClient = createMockCcsLogsExtractionClient(); @@ -169,17 +168,6 @@ describe('LogsExtractionClient', () => { }); describe('extractLogs', () => { - function applyLastUpdater( - entityType: EntityType = 'user', - descriptorOverrides?: Partial>> - ) { - const [[, updater]] = mockEngineDescriptorClient.updateWith.mock.calls; - return updater({ - ...createMockEngineDescriptor(entityType), - ...descriptorOverrides, - } as Awaited>); - } - it('should successfully extract logs and ingest entities', async () => { const lastTimestamp = '2024-01-02T12:00:00.000Z'; const mockEsqlResponse: ESQLSearchResponse = { @@ -241,22 +229,20 @@ describe('LogsExtractionClient', () => { abortController: undefined, }); - expect(mockEngineDescriptorClient.updateWith).toHaveBeenCalledWith( + expect(mockEngineDescriptorClient.update).toHaveBeenCalledWith( 'user', - expect.any(Function) + expect.objectContaining({ + logExtractionState: expect.objectContaining({ + paginationTimestamp: null, + paginationId: null, + logsPageCursorStartTimestamp: null, + logsPageCursorStartId: null, + logsPageCursorEndTimestamp: null, + logsPageCursorEndId: null, + lastExecutionTimestamp: expect.any(String), + }), + }) ); - expect(applyLastUpdater()).toMatchObject({ - logExtractionState: expect.objectContaining({ - paginationTimestamp: undefined, - paginationId: undefined, - logsPageCursorStartTimestamp: undefined, - logsPageCursorStartId: undefined, - logsPageCursorEndTimestamp: undefined, - logsPageCursorEndId: undefined, - lastExecutionTimestamp: expect.any(String), - }), - error: undefined, - }); }); it('should handle empty results from ESQL query', async () => { @@ -280,22 +266,20 @@ describe('LogsExtractionClient', () => { expect(mockExecuteEsqlQuery).toHaveBeenCalledTimes(1); expect(mockIngestEntities).toHaveBeenCalledTimes(0); - expect(mockEngineDescriptorClient.updateWith).toHaveBeenCalledWith( + expect(mockEngineDescriptorClient.update).toHaveBeenCalledWith( 'user', - expect.any(Function) + expect.objectContaining({ + logExtractionState: expect.objectContaining({ + paginationTimestamp: null, + paginationId: null, + logsPageCursorStartTimestamp: null, + logsPageCursorStartId: null, + logsPageCursorEndTimestamp: null, + logsPageCursorEndId: null, + lastExecutionTimestamp: expect.any(String), + }), + }) ); - expect(applyLastUpdater()).toMatchObject({ - logExtractionState: expect.objectContaining({ - paginationTimestamp: undefined, - paginationId: undefined, - logsPageCursorStartTimestamp: undefined, - logsPageCursorStartId: undefined, - logsPageCursorEndTimestamp: undefined, - logsPageCursorEndId: undefined, - lastExecutionTimestamp: expect.any(String), - }), - error: undefined, - }); }); it('should compute extraction window from lookbackPeriod and delay when no custom range', async () => { @@ -618,7 +602,7 @@ describe('LogsExtractionClient', () => { esClient: mockEsClient, query: expect.stringContaining(toDate), }); - expect(mockEngineDescriptorClient.updateWith).not.toHaveBeenCalled(); + expect(mockEngineDescriptorClient.update).not.toHaveBeenCalled(); }); it('should not update engine descriptor when specificWindow is provided', async () => { @@ -657,7 +641,7 @@ describe('LogsExtractionClient', () => { expect(result.success && result.count).toBe(2); expect(mockExecuteEsqlQuery).toHaveBeenCalledTimes(3); expect(mockIngestEntities).toHaveBeenCalledTimes(1); - expect(mockEngineDescriptorClient.updateWith).not.toHaveBeenCalled(); + expect(mockEngineDescriptorClient.update).not.toHaveBeenCalled(); }); it('should handle errors from executeEsqlQuery', async () => { @@ -800,22 +784,21 @@ describe('LogsExtractionClient', () => { expect(mockIngestEntities).toHaveBeenCalledTimes(1); // CCS error is stored in the saved object - expect(mockEngineDescriptorClient.updateWith).toHaveBeenCalledWith( + expect(mockEngineDescriptorClient.update).toHaveBeenCalledWith( 'user', - expect.any(Function) + expect.objectContaining({ + logExtractionState: expect.objectContaining({ + paginationTimestamp: null, + paginationId: null, + logsPageCursorStartTimestamp: null, + logsPageCursorStartId: null, + logsPageCursorEndTimestamp: null, + logsPageCursorEndId: null, + lastExecutionTimestamp: expect.any(String), + }), + error: { message: ccsError.message, action: 'extractLogs' }, + }) ); - expect(applyLastUpdater()).toMatchObject({ - logExtractionState: expect.objectContaining({ - paginationTimestamp: undefined, - paginationId: undefined, - logsPageCursorStartTimestamp: undefined, - logsPageCursorStartId: undefined, - logsPageCursorEndTimestamp: undefined, - logsPageCursorEndId: undefined, - lastExecutionTimestamp: expect.any(String), - }), - error: { message: ccsError.message, action: 'extractLogs' }, - }); }); it('should clear a previous error after a successful extraction', async () => { @@ -823,25 +806,20 @@ describe('LogsExtractionClient', () => { getIndexPattern: jest.fn().mockReturnValue('logs-*'), }; - mockEngineDescriptorClient.findOrThrow.mockResolvedValue( - createMockEngineDescriptor('user') as Awaited< - ReturnType - > - ); + mockEngineDescriptorClient.findOrThrow.mockResolvedValue({ + ...createMockEngineDescriptor('user'), + error: { message: 'previous error', action: 'extractLogs' as const }, + } as Awaited>); mockDataViewsService.get.mockResolvedValue(mockDataView as any); mockExecuteEsqlQuery.mockResolvedValue(mockLogPaginationCursorProbeEmpty()); mockIngestEntities.mockResolvedValue(undefined); await client.extractLogs('user'); - expect(mockEngineDescriptorClient.updateWith).toHaveBeenCalledWith( + expect(mockEngineDescriptorClient.update).toHaveBeenCalledWith( 'user', - expect.any(Function) + expect.objectContaining({ error: null }) ); - expect( - applyLastUpdater('user', { error: { message: 'previous error', action: 'extractLogs' } }) - .error - ).toBeUndefined(); }); it('should fallback to logs-* when data view is not found', async () => { @@ -894,21 +872,20 @@ describe('LogsExtractionClient', () => { targetIndex: expect.stringContaining('.entities.v2.latest.security_default'), }) ); - expect(mockEngineDescriptorClient.updateWith).toHaveBeenCalledWith( + expect(mockEngineDescriptorClient.update).toHaveBeenCalledWith( 'host', - expect.any(Function) + expect.objectContaining({ + logExtractionState: expect.objectContaining({ + paginationTimestamp: null, + paginationId: null, + logsPageCursorStartTimestamp: null, + logsPageCursorStartId: null, + logsPageCursorEndTimestamp: null, + logsPageCursorEndId: null, + lastExecutionTimestamp: expect.any(String), + }), + }) ); - expect(applyLastUpdater('host')).toMatchObject({ - logExtractionState: expect.objectContaining({ - paginationTimestamp: undefined, - paginationId: undefined, - logsPageCursorStartTimestamp: undefined, - logsPageCursorStartId: undefined, - logsPageCursorEndTimestamp: undefined, - logsPageCursorEndId: undefined, - lastExecutionTimestamp: expect.any(String), - }), - }); }); it('should return success false when engine is not started', async () => { diff --git a/x-pack/solutions/security/plugins/entity_store/server/domain/logs_extraction/logs_extraction_client.ts b/x-pack/solutions/security/plugins/entity_store/server/domain/logs_extraction/logs_extraction_client.ts index cec5af633ec86..8e8d737f2b545 100644 --- a/x-pack/solutions/security/plugins/entity_store/server/domain/logs_extraction/logs_extraction_client.ts +++ b/x-pack/solutions/security/plugins/entity_store/server/domain/logs_extraction/logs_extraction_client.ts @@ -155,23 +155,18 @@ export class LogsExtractionClient { return operationResult; } - await this.engineDescriptorClient.updateWith(type, (current) => ({ - ...current, + await this.engineDescriptorClient.update(type, { logExtractionState: { - // we went through all the pages, - // therefore we can leave the lastExecutionTimestamp as the beginning of the next - // window - paginationTimestamp: undefined, - paginationId: undefined, - logsPageCursorStartTimestamp: undefined, - logsPageCursorStartId: undefined, - logsPageCursorEndTimestamp: undefined, - logsPageCursorEndId: undefined, - // Store last searched timestamp to start window from here + paginationTimestamp: null, + paginationId: null, + logsPageCursorStartTimestamp: null, + logsPageCursorStartId: null, + logsPageCursorEndTimestamp: null, + logsPageCursorEndId: null, lastExecutionTimestamp: lastSearchTimestamp || moment().utc().toISOString(), }, - error: ccsError ? { message: ccsError.message, action: 'extractLogs' } : undefined, - })); + error: ccsError ? { message: ccsError.message, action: 'extractLogs' } : null, + }); return operationResult; } catch (error) { @@ -196,7 +191,7 @@ export class LogsExtractionClient { const indexPatterns = await this.getLocalIndexPatterns(config.additionalIndexPatterns); const { fromDateISO } = this.getExtractionWindow(config, engineState, delayMs); const toDateISO = moment().utc().toISOString(); - const recoveryId = engineState.paginationId; + const recoveryId = engineState.paginationId ?? undefined; const logsPageCursorStart = paginationFromOptionalFields( engineState.logsPageCursorStartTimestamp, engineState.logsPageCursorStartId @@ -328,8 +323,8 @@ export class LogsExtractionClient { const onAbort = () => this.logger.debug('Aborting execution mid logs extraction'); opts?.abortController?.signal.addEventListener('abort', onAbort); - /** One-shot `paginationId` from a prior run: first bounded query may use recovery `>=` (see query builder). */ - let recoveryId = initialEngineState.paginationId; + /** One-shot `recoveryId` for corrupt state: consumed by the probe or the first bounded extraction batch. */ + let recoveryId = initialEngineState.paginationId ?? undefined; if (recoveryId) { this.logger.warn( `Resuming with paginationId ${recoveryId} and extraction window from ${fromDateISO} (entity pagination at ${ @@ -575,8 +570,8 @@ export class LogsExtractionClient { paginationId: pagination.idCursor, logsPageCursorEndTimestamp: logsPageCursorEnd.timestampCursor, logsPageCursorEndId: logsPageCursorEnd.idCursor, - logsPageCursorStartTimestamp: logsPageCursorStart?.timestampCursor, - logsPageCursorStartId: logsPageCursorStart?.idCursor, + logsPageCursorStartTimestamp: logsPageCursorStart?.timestampCursor ?? null, + logsPageCursorStartId: logsPageCursorStart?.idCursor ?? null, }; await this.persistMainLogExtractionStateIfNotManualWindow(type, opts, state); } @@ -596,10 +591,9 @@ export class LogsExtractionClient { ...state, // this is the leading state for the next log page if we break in the middle of the processing paginationTimestamp: logsPageCursorEnd.timestampCursor, - - paginationId: undefined, - logsPageCursorEndTimestamp: undefined, - logsPageCursorEndId: undefined, + paginationId: null, + logsPageCursorEndTimestamp: null, + logsPageCursorEndId: null, logsPageCursorStartTimestamp: logsPageCursorEnd.timestampCursor, logsPageCursorStartId: logsPageCursorEnd.idCursor, }; @@ -724,8 +718,8 @@ export class LogsExtractionClient { } function paginationFromOptionalFields( - ts: string | undefined, - id: string | undefined + ts: string | null, + id: string | null ): PaginationParams | undefined { if (id && ts) { return { timestampCursor: ts, idCursor: id }; diff --git a/x-pack/solutions/security/plugins/entity_store/server/domain/saved_objects/engine_descriptor/constants.ts b/x-pack/solutions/security/plugins/entity_store/server/domain/saved_objects/engine_descriptor/constants.ts index 59bf075bff649..a667116980523 100644 --- a/x-pack/solutions/security/plugins/entity_store/server/domain/saved_objects/engine_descriptor/constants.ts +++ b/x-pack/solutions/security/plugins/entity_store/server/domain/saved_objects/engine_descriptor/constants.ts @@ -13,15 +13,15 @@ export const EngineStatus = z.enum(['installing', 'started', 'stopped', 'updatin export type EngineLogExtractionState = z.infer; export const EngineLogExtractionState = z.object({ - paginationTimestamp: z.string().optional(), - paginationId: z.string().optional(), - lastExecutionTimestamp: z.string().optional(), + paginationTimestamp: z.string().nullable().default(null), + paginationId: z.string().nullable().default(null), + lastExecutionTimestamp: z.string().nullable().default(null), /** Exclusive lower bound for log-slice scan within the extraction window (`@timestamp`, `_id`). */ - logsPageCursorStartTimestamp: z.string().optional(), - logsPageCursorStartId: z.string().optional(), + logsPageCursorStartTimestamp: z.string().nullable().default(null), + logsPageCursorStartId: z.string().nullable().default(null), /** Inclusive upper bound for the current log slice. */ - logsPageCursorEndTimestamp: z.string().optional(), - logsPageCursorEndId: z.string().optional(), + logsPageCursorEndTimestamp: z.string().nullable().default(null), + logsPageCursorEndId: z.string().nullable().default(null), }); export type EngineError = z.infer; @@ -42,6 +42,6 @@ export const EngineDescriptor = z.object({ type: EntityType, status: EngineStatus, logExtractionState: EngineLogExtractionState, - error: EngineError.optional(), + error: EngineError.nullable().default(null), versionState: VersionState, }); diff --git a/x-pack/solutions/security/plugins/entity_store/server/domain/saved_objects/engine_descriptor/index.ts b/x-pack/solutions/security/plugins/entity_store/server/domain/saved_objects/engine_descriptor/index.ts index 36c49696e24e9..339e05b9d11bb 100644 --- a/x-pack/solutions/security/plugins/entity_store/server/domain/saved_objects/engine_descriptor/index.ts +++ b/x-pack/solutions/security/plugins/entity_store/server/domain/saved_objects/engine_descriptor/index.ts @@ -67,6 +67,7 @@ export class EngineDescriptorClient { { status: ENGINE_STATUS.INSTALLING, type: entityType, + error: null, logExtractionState, versionState: defaultVersionState, }, @@ -76,22 +77,6 @@ export class EngineDescriptorClient { return attributes; } - async updateWith( - entityType: EntityType, - updater: (current: EngineDescriptor) => EngineDescriptor - ): Promise { - const current = await this.findOrThrow(entityType); - const updated = updater(current); - const id = this.getSavedObjectId(entityType); - const { attributes } = await this.soClient.update( - EngineDescriptorTypeName, - id, - updated, - { refresh: 'wait_for', mergeAttributes: false } - ); - return attributes as EngineDescriptor; - } - async update( entityType: EntityType, state: Partial, diff --git a/x-pack/solutions/security/plugins/entity_store/server/domain/saved_objects/engine_descriptor/types.ts b/x-pack/solutions/security/plugins/entity_store/server/domain/saved_objects/engine_descriptor/types.ts index 26256e3f2f7b3..843242bb91f98 100644 --- a/x-pack/solutions/security/plugins/entity_store/server/domain/saved_objects/engine_descriptor/types.ts +++ b/x-pack/solutions/security/plugins/entity_store/server/domain/saved_objects/engine_descriptor/types.ts @@ -218,11 +218,62 @@ const version4: SavedObjectsFullModelVersion = { }, }; +const logExtractionRuntimeStateSchemaV5 = schema.object({ + paginationTimestamp: schema.nullable(schema.string()), + paginationId: schema.nullable(schema.string()), + lastExecutionTimestamp: schema.nullable(schema.string()), + logsPageCursorStartTimestamp: schema.nullable(schema.string()), + logsPageCursorStartId: schema.nullable(schema.string()), + logsPageCursorEndTimestamp: schema.nullable(schema.string()), + logsPageCursorEndId: schema.nullable(schema.string()), +}); + +const engineDescriptorSchemaV5 = schema.object({ + ...engineDescriptorAttributesSchemaV1, + logExtractionState: logExtractionRuntimeStateSchemaV5, + error: schema.nullable( + schema.object({ + message: schema.string(), + action: schema.string(), + }) + ), +}); + +const version5: SavedObjectsFullModelVersion = { + changes: [ + { + type: 'data_backfill' as const, + backfillFn: (document) => { + const { logExtractionState, error } = document.attributes; + return { + attributes: { + logExtractionState: { + paginationTimestamp: logExtractionState?.paginationTimestamp ?? null, + paginationId: logExtractionState?.paginationId ?? null, + lastExecutionTimestamp: logExtractionState?.lastExecutionTimestamp ?? null, + logsPageCursorStartTimestamp: + logExtractionState?.logsPageCursorStartTimestamp ?? null, + logsPageCursorStartId: logExtractionState?.logsPageCursorStartId ?? null, + logsPageCursorEndTimestamp: logExtractionState?.logsPageCursorEndTimestamp ?? null, + logsPageCursorEndId: logExtractionState?.logsPageCursorEndId ?? null, + }, + error: error ?? null, + }, + }; + }, + }, + ], + schemas: { + create: engineDescriptorSchemaV5, + forwardCompatibility: engineDescriptorSchemaV5.extends({}, { unknowns: 'ignore' }), + }, +}; + export const EngineDescriptorType: SavedObjectsType = { name: EngineDescriptorTypeName, hidden: false, namespaceType: 'multiple-isolated', mappings: EngineDescriptorTypeMappings, - modelVersions: { 1: version1, 2: version2, 3: version3, 4: version4 }, + modelVersions: { 1: version1, 2: version2, 3: version3, 4: version4, 5: version5 }, hiddenFromHttpApis: true, }; diff --git a/x-pack/solutions/security/plugins/entity_store/server/routes/apis/status.ts b/x-pack/solutions/security/plugins/entity_store/server/routes/apis/status.ts index 064bf14d47573..8fc4f6da81083 100644 --- a/x-pack/solutions/security/plugins/entity_store/server/routes/apis/status.ts +++ b/x-pack/solutions/security/plugins/entity_store/server/routes/apis/status.ts @@ -74,7 +74,7 @@ function toPublicEngine( enrichPolicyExecutionInterval: null, timestampField: '@timestamp', maxPageSearchSize: 10000, - lastExecutionTimestamp: logExtractionState.lastExecutionTimestamp, + lastExecutionTimestamp: logExtractionState.lastExecutionTimestamp ?? undefined, }; }