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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"spaces",
"taskManager",
"dataViews",
"security"
"security",
"licensing"
],
"optionalPlugins": [
"encryptedSavedObjects"
Expand Down
2 changes: 2 additions & 0 deletions x-pack/solutions/security/plugins/entity_store/moon.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ dependsOn:
- '@kbn/safer-lodash-set'
- '@kbn/tracing-utils'
- '@kbn/esql-language'
- '@kbn/licensing-plugin'
- '@kbn/licensing-types'
tags:
- plugin
- prod
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import type { HttpStart } from '@kbn/core/public';
import type { EntityType } from '../common';
import { API_VERSIONS, ENTITY_STORE_ROUTES } from '../common/constants';
import { API_VERSIONS, ENTITY_STORE_ROUTES } from '../common';

export interface BulkUpdateEntitiesParams {
entityType: EntityType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
type Services,
} from './useInstallEntityStoreV2';
import { EntityStoreStatus } from '../../common';
import { ENTITY_STORE_ROUTES, FF_ENABLE_ENTITY_STORE_V2 } from '../../common/constants';
import { ENTITY_STORE_ROUTES, FF_ENABLE_ENTITY_STORE_V2 } from '../../common';

interface MockServices {
http: { get: jest.Mock; post: jest.Mock };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type { SpacesPluginStart } from '@kbn/spaces-plugin/public';
import type { HttpFetchOptionsWithPath, HttpSetup, IUiSettingsClient } from '@kbn/core/public';
import { useEffect } from 'react';
import { EntityStoreStatus } from '../../common';
import { ENTITY_STORE_ROUTES, FF_ENABLE_ENTITY_STORE_V2 } from '../../common/constants';
import { ENTITY_STORE_ROUTES, FF_ENABLE_ENTITY_STORE_V2 } from '../../common';
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice 👍

import type { StatusRequestQuery } from '../../server/routes/apis/status';

export interface Services {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@
*/

import type { HttpStart } from '@kbn/core/public';
import type { Entity } from '../common';
import { ENTITY_STORE_ROUTES } from '../common/constants';
import type { EntityType } from '../common';
import { API_VERSIONS } from '../common/constants';
import type { Entity, EntityType } from '../common';
import { API_VERSIONS, ENTITY_STORE_ROUTES } from '../common';
export interface SearchEntitiesFromEntityStoreParams {
entityTypes: EntityType[];
filterQuery?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { loggerMock } from '@kbn/logging-mocks';
import type { TaskManagerStartContract } from '@kbn/task-manager-plugin/server';
import type { KibanaRequest } from '@kbn/core/server';
import { SavedObjectsErrorHelpers } from '@kbn/core/server';
import { DEFAULT_ENTITY_MAINTAINER_MIN_LICENSE } from '../../tasks/entity_maintainers';
import { EntityMaintainerTaskStatus } from '../../tasks/entity_maintainers/types';
import type { EntityMaintainerTaskEntry } from '../../tasks/entity_maintainers/types';

Expand Down Expand Up @@ -180,8 +181,18 @@ describe('EntityMaintainersClient', () => {
describe('init', () => {
it('should schedule only maintainers without an existing task', async () => {
entityMaintainersRegistry.getAll.mockReturnValue([
{ id: 'm1', interval: '5m', description: 'M1' },
{ id: 'm2', interval: '1h', description: 'M2' },
{
id: 'm1',
interval: '5m',
description: 'M1',
minLicense: DEFAULT_ENTITY_MAINTAINER_MIN_LICENSE,
},
{
id: 'm2',
interval: '1h',
description: 'M2',
minLicense: DEFAULT_ENTITY_MAINTAINER_MIN_LICENSE,
},
]);
const taskManager = {
get: jest.fn().mockImplementation((taskId: string) => {
Expand Down Expand Up @@ -211,7 +222,12 @@ describe('EntityMaintainersClient', () => {

it('should not schedule when all maintainers already have tasks', async () => {
entityMaintainersRegistry.getAll.mockReturnValue([
{ id: 'm1', interval: '5m', description: 'M1' },
{
id: 'm1',
interval: '5m',
description: 'M1',
minLicense: DEFAULT_ENTITY_MAINTAINER_MIN_LICENSE,
},
]);
const taskManager = {
get: jest.fn().mockResolvedValue({
Expand All @@ -232,7 +248,12 @@ describe('EntityMaintainersClient', () => {

it('should propagate error when scheduleEntityMaintainerTask rejects', async () => {
entityMaintainersRegistry.getAll.mockReturnValue([
{ id: 'm1', interval: '5m', description: 'M1' },
{
id: 'm1',
interval: '5m',
description: 'M1',
minLicense: DEFAULT_ENTITY_MAINTAINER_MIN_LICENSE,
},
]);
const taskManager = {
get: jest.fn().mockRejectedValue(new Error('Not found')),
Expand Down Expand Up @@ -288,8 +309,8 @@ describe('EntityMaintainersClient', () => {
describe('removeAll', () => {
it('should remove all registered tasks', async () => {
const entries: EntityMaintainerTaskEntry[] = [
{ id: 'm1', interval: '5m' },
{ id: 'm2', interval: '1h' },
{ id: 'm1', interval: '5m', minLicense: DEFAULT_ENTITY_MAINTAINER_MIN_LICENSE },
{ id: 'm2', interval: '1h', minLicense: DEFAULT_ENTITY_MAINTAINER_MIN_LICENSE },
];
entityMaintainersRegistry.getAll.mockReturnValue(entries);
const client = createClient();
Expand All @@ -308,8 +329,8 @@ describe('EntityMaintainersClient', () => {

it('should propagate error when any remove fails', async () => {
const entries: EntityMaintainerTaskEntry[] = [
{ id: 'm1', interval: '5m' },
{ id: 'm2', interval: '1h' },
{ id: 'm1', interval: '5m', minLicense: DEFAULT_ENTITY_MAINTAINER_MIN_LICENSE },
{ id: 'm2', interval: '1h', minLicense: DEFAULT_ENTITY_MAINTAINER_MIN_LICENSE },
];
entityMaintainersRegistry.getAll.mockReturnValue(entries);
(removeEntityMaintainer as jest.Mock)
Expand All @@ -328,6 +349,7 @@ describe('EntityMaintainersClient', () => {
id: 'm1',
interval: '5m',
description: 'Maintainer one',
minLicense: DEFAULT_ENTITY_MAINTAINER_MIN_LICENSE,
},
]);
const taskManagerGet = jest.fn().mockRejectedValue(new Error('Not found'));
Expand All @@ -342,14 +364,17 @@ describe('EntityMaintainersClient', () => {
taskStatus: EntityMaintainerTaskStatus.NEVER_STARTED,
interval: '5m',
description: 'Maintainer one',
minLicense: DEFAULT_ENTITY_MAINTAINER_MIN_LICENSE,
taskSnapshot: undefined,
});
expect(getTaskId).toHaveBeenCalledWith('m1', 'default');
expect(taskManagerGet).toHaveBeenCalledWith('m1:default');
});

it('should return entries with taskSnapshot and taskStatus from task state when task exists', async () => {
entityMaintainersRegistry.getAll.mockReturnValue([{ id: 'm1', interval: '5m' }]);
entityMaintainersRegistry.getAll.mockReturnValue([
{ id: 'm1', interval: '5m', minLicense: 'gold' },
]);
const taskManagerGet = jest.fn().mockResolvedValue({
state: {
taskStatus: EntityMaintainerTaskStatus.STARTED,
Expand All @@ -370,6 +395,7 @@ describe('EntityMaintainersClient', () => {
id: 'm1',
taskStatus: EntityMaintainerTaskStatus.STARTED,
interval: '5m',
minLicense: 'gold',
taskSnapshot: {
runs: 10,
lastSuccessTimestamp: '2024-01-15T12:00:00.000Z',
Expand All @@ -381,7 +407,12 @@ describe('EntityMaintainersClient', () => {

it('should return taskStatus from task state', async () => {
entityMaintainersRegistry.getAll.mockReturnValue([
{ id: 'm1', interval: '5m', description: 'Registry says stopped' },
{
id: 'm1',
interval: '5m',
description: 'Registry says stopped',
minLicense: DEFAULT_ENTITY_MAINTAINER_MIN_LICENSE,
},
]);
const taskManagerGet = jest.fn().mockResolvedValue({
state: {
Expand Down Expand Up @@ -409,7 +440,9 @@ describe('EntityMaintainersClient', () => {
});

it('should propagate non-not-found errors from taskManager.get', async () => {
entityMaintainersRegistry.getAll.mockReturnValue([{ id: 'm1', interval: '5m' }]);
entityMaintainersRegistry.getAll.mockReturnValue([
{ id: 'm1', interval: '5m', minLicense: DEFAULT_ENTITY_MAINTAINER_MIN_LICENSE },
]);
const taskManagerGet = jest.fn().mockRejectedValue(new Error('ES connection failed'));
const client = createClient({ taskManager: { get: taskManagerGet } });
mockSavedObjectsErrorHelpers.isNotFoundError.mockReturnValue(false);
Expand All @@ -418,7 +451,9 @@ describe('EntityMaintainersClient', () => {
});

it('should use default runs and timestamps when task state metadata is missing', async () => {
entityMaintainersRegistry.getAll.mockReturnValue([{ id: 'm1', interval: '5m' }]);
entityMaintainersRegistry.getAll.mockReturnValue([
{ id: 'm1', interval: '5m', minLicense: DEFAULT_ENTITY_MAINTAINER_MIN_LICENSE },
]);
const taskManagerGet = jest.fn().mockResolvedValue({
state: {
taskStatus: EntityMaintainerTaskStatus.STARTED,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import type { Logger } from '@kbn/logging';
import { SavedObjectsErrorHelpers, type KibanaRequest } from '@kbn/core/server';
import type { TaskManagerStartContract } from '@kbn/task-manager-plugin/server';
import type { LicenseType } from '@kbn/licensing-types';
import {
getTaskId,
removeEntityMaintainer,
Expand All @@ -32,6 +33,7 @@ export interface EntityMaintainerListEntry {
taskStatus: EntityMaintainerTaskStatus;
interval: string;
description?: string;
minLicense: LicenseType;
taskSnapshot?: TaskSnapshot;
}

Expand Down Expand Up @@ -162,7 +164,7 @@ export class EntityMaintainersClient {

const results = await Promise.all(
entries.map(async (entry): Promise<EntityMaintainerListEntry> => {
const { id, interval, description } = entry;
const { id, interval, description, minLicense } = entry;
const taskId = getTaskId(id, this.namespace);
let taskSnapshot: TaskSnapshot | undefined;
let taskStatus: EntityMaintainerTaskStatus = EntityMaintainerTaskStatus.NEVER_STARTED;
Expand Down Expand Up @@ -195,6 +197,7 @@ export class EntityMaintainersClient {
taskStatus,
interval,
description,
minLicense,
taskSnapshot,
};
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import type { IKibanaResponse } from '@kbn/core-http-server';
import type { LicenseType } from '@kbn/licensing-types';
import { API_VERSIONS, ENTITY_STORE_ROUTES } from '../../../../common';
import { DEFAULT_ENTITY_STORE_PERMISSIONS } from '../../constants';
import type { EntityStorePluginRouter } from '../../../types';
Expand All @@ -21,6 +22,7 @@ interface EntityMaintainerResponseItem {
taskStatus: EntityMaintainerTaskStatus;
interval: string;
description: string | null;
minLicense: LicenseType;
customState: EntityMaintainerState | null;
runs: number;
lastSuccessTimestamp: string | null;
Expand All @@ -36,6 +38,7 @@ function toGetMaintainersResponseItem(
taskStatus: entry.taskStatus,
interval: entry.interval,
description: entry.description ?? null,
minLicense: entry.minLicense,
customState: snapshot?.state ?? null,
runs: snapshot?.runs ?? 0,
lastSuccessTimestamp: snapshot?.lastSuccessTimestamp ?? null,
Expand Down
Loading
Loading