Skip to content

Commit

Permalink
[8.x] [Deprecations] Logs Sources settings in all spaces (#203042) (#…
Browse files Browse the repository at this point in the history
…204476)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Deprecations] Logs Sources settings in all spaces
(#203042)](#203042)

<!--- Backport version: 8.9.8 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Alejandro Fernández
Haro","email":"[email protected]"},"sourceCommit":{"committedDate":"2024-12-16T12:40:56Z","message":"[Deprecations]
Logs Sources settings in all spaces
(#203042)","sha":"2ed34427c056e935f77c6dded03afc11f82d301c","branchLabelMapping":{"^v9.0.0$":"main","^v8.18.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["Team:Core","Team:Security","release_note:skip","v9.0.0","backport:prev-minor","ci:project-deploy-observability","Team:obs-ux-logs"],"number":203042,"url":"https://github.com/elastic/kibana/pull/203042","mergeCommit":{"message":"[Deprecations]
Logs Sources settings in all spaces
(#203042)","sha":"2ed34427c056e935f77c6dded03afc11f82d301c"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","labelRegex":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/203042","number":203042,"mergeCommit":{"message":"[Deprecations]
Logs Sources settings in all spaces
(#203042)","sha":"2ed34427c056e935f77c6dded03afc11f82d301c"}}]}]
BACKPORT-->
  • Loading branch information
afharo authored Dec 16, 2024
1 parent 7f97a3e commit 3718918
Show file tree
Hide file tree
Showing 32 changed files with 300 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type {
DeprecationsRequestHandlerContext,
DeprecationsClient,
} from '@kbn/core-deprecations-server';
import type { KibanaRequest } from '@kbn/core-http-server';
import type { InternalDeprecationsServiceStart } from './deprecations_service';

/**
Expand All @@ -25,14 +26,16 @@ export class CoreDeprecationsRouteHandlerContext implements DeprecationsRequestH
constructor(
private readonly deprecationsStart: InternalDeprecationsServiceStart,
private readonly elasticsearchRouterHandlerContext: CoreElasticsearchRouteHandlerContext,
private readonly savedObjectsRouterHandlerContext: CoreSavedObjectsRouteHandlerContext
private readonly savedObjectsRouterHandlerContext: CoreSavedObjectsRouteHandlerContext,
private readonly request: KibanaRequest
) {}

public get client() {
if (this.#client == null) {
this.#client = this.deprecationsStart.asScopedToClient(
this.elasticsearchRouterHandlerContext.client,
this.savedObjectsRouterHandlerContext.client
this.savedObjectsRouterHandlerContext.client,
this.request
);
}
return this.#client;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
registerConfigDeprecationsInfoMock,
} from './deprecations_service.test.mocks';
import { mockCoreContext } from '@kbn/core-base-server-mocks';
import { httpServiceMock } from '@kbn/core-http-server-mocks';
import { httpServerMock, httpServiceMock } from '@kbn/core-http-server-mocks';
import { coreUsageDataServiceMock } from '@kbn/core-usage-data-server-mocks';
import { configServiceMock } from '@kbn/config-mocks';
import { savedObjectsClientMock } from '@kbn/core-saved-objects-api-server-mocks';
Expand Down Expand Up @@ -83,12 +83,13 @@ describe('DeprecationsService', () => {
it('returns client with #getAllDeprecations method', async () => {
const esClient = elasticsearchServiceMock.createScopedClusterClient();
const savedObjectsClient = savedObjectsClientMock.create();
const request = httpServerMock.createKibanaRequest();
const deprecationsService = new DeprecationsService(coreContext);

await deprecationsService.setup(deprecationsCoreSetupDeps);

const start = deprecationsService.start();
const deprecationsClient = start.asScopedToClient(esClient, savedObjectsClient);
const deprecationsClient = start.asScopedToClient(esClient, savedObjectsClient, request);

expect(deprecationsClient.getAllDeprecations).toBeDefined();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import type {
DeprecationsClient,
} from '@kbn/core-deprecations-server';
import { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-server-internal';
import type { KibanaRequest } from '@kbn/core-http-server';
import { DeprecationsFactory } from './deprecations_factory';
import { registerRoutes } from './routes';
import { config as deprecationConfig, DeprecationConfigType } from './deprecation_config';
Expand All @@ -36,7 +37,8 @@ export interface InternalDeprecationsServiceStart {
*/
asScopedToClient(
esClient: IScopedClusterClient,
savedObjectsClient: SavedObjectsClientContract
savedObjectsClient: SavedObjectsClientContract,
request: KibanaRequest
): DeprecationsClient;
}

Expand Down Expand Up @@ -117,13 +119,19 @@ export class DeprecationsService

private createScopedDeprecations(): (
esClient: IScopedClusterClient,
savedObjectsClient: SavedObjectsClientContract
savedObjectsClient: SavedObjectsClientContract,
request: KibanaRequest
) => DeprecationsClient {
return (esClient: IScopedClusterClient, savedObjectsClient: SavedObjectsClientContract) => {
return (
esClient: IScopedClusterClient,
savedObjectsClient: SavedObjectsClientContract,
request: KibanaRequest
) => {
return {
getAllDeprecations: this.deprecationsFactory!.getAllDeprecations.bind(null, {
savedObjectsClient,
esClient,
request,
}),
};
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-m
import type { GetDeprecationsContext } from '@kbn/core-deprecations-server';
import { savedObjectsClientMock } from '@kbn/core-saved-objects-api-server-mocks';
import type { DeprecationsRegistry } from '../deprecations_registry';
import { httpServerMock } from '@kbn/core-http-server-mocks';

type DeprecationsRegistryContract = PublicMethodsOf<DeprecationsRegistry>;

Expand All @@ -28,6 +29,7 @@ const createGetDeprecationsContextMock = () => {
const mocked: jest.Mocked<GetDeprecationsContext> = {
esClient: elasticsearchClientMock.createScopedClusterClient(),
savedObjectsClient: savedObjectsClientMock.create(),
request: httpServerMock.createKibanaRequest(),
};

return mocked;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type { MaybePromise } from '@kbn/utility-types';
import type { DeprecationsDetails } from '@kbn/core-deprecations-common';
import type { IScopedClusterClient } from '@kbn/core-elasticsearch-server';
import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server';
import type { KibanaRequest } from '@kbn/core-http-server';

/**
* The deprecations service provides a way for the Kibana platform to communicate deprecated
Expand Down Expand Up @@ -121,6 +122,7 @@ export interface GetDeprecationsContext {
esClient: IScopedClusterClient;
/** Saved Objects client scoped to the current user and space */
savedObjectsClient: SavedObjectsClientContract;
request: KibanaRequest;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"@kbn/core-deprecations-common",
"@kbn/core-elasticsearch-server",
"@kbn/core-saved-objects-api-server",
"@kbn/core-http-server",
],
"exclude": [
"target/**/*",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ export class CoreRouteHandlerContext implements CoreRequestHandlerContext {
this.#deprecations = new CoreDeprecationsRouteHandlerContext(
this.coreStart.deprecations,
this.elasticsearch,
this.savedObjects
this.savedObjects,
this.request
);
}
return this.#deprecations;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,23 @@ import {

import { SavedObjectsRepository } from './repository';
import { loggerMock } from '@kbn/logging-mocks';
import { SavedObjectsSerializer } from '@kbn/core-saved-objects-base-server-internal';
import { kibanaMigratorMock } from '../mocks';
import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks';

import {
mockTimestamp,
mappings,
createRegistry,
createDocumentMigrator,
createSpySerializer,
} from '../test_helpers/repository.test.common';
import type { ISavedObjectsRepository } from '@kbn/core-saved-objects-api-server';
import { ISavedObjectsSpacesExtension } from '@kbn/core-saved-objects-server';
import { savedObjectsExtensionsMock } from '../mocks/saved_objects_extensions.mock';

describe('SavedObjectsRepository', () => {
let client: ReturnType<typeof elasticsearchClientMock.createElasticsearchClient>;
let repository: SavedObjectsRepository;
let repository: ISavedObjectsRepository;
let migrator: ReturnType<typeof kibanaMigratorMock.create>;
let logger: ReturnType<typeof loggerMock.create>;
let serializer: jest.Mocked<SavedObjectsSerializer>;

const registry = createRegistry();
const documentMigrator = createDocumentMigrator(registry);
Expand All @@ -46,23 +45,13 @@ describe('SavedObjectsRepository', () => {
migrator.runMigrations = jest.fn().mockResolvedValue([{ status: 'skipped' }]);
logger = loggerMock.create();

// create a mock serializer "shim" so we can track function calls, but use the real serializer's implementation
serializer = createSpySerializer(registry);

const allTypes = registry.getAllTypes().map((type) => type.name);
const allowedTypes = [...new Set(allTypes.filter((type) => !registry.isHidden(type)))];

// @ts-expect-error must use the private constructor to use the mocked serializer
repository = new SavedObjectsRepository({
index: '.kibana-test',
mappings,
client,
repository = SavedObjectsRepository.createRepository(
migrator,
typeRegistry: registry,
serializer,
allowedTypes,
logger,
});
registry,
'.kibana-test',
client,
logger
);

mockGetCurrentTime.mockReturnValue(mockTimestamp);
mockGetSearchDsl.mockClear();
Expand All @@ -87,4 +76,60 @@ describe('SavedObjectsRepository', () => {
expect(repository.getCurrentNamespace('space-a')).toBe('space-a');
});
});

describe('#asScopedToNamespace', () => {
it('returns a new client with undefined spacesExtensions (not available)', () => {
const scopedRepository = repository.asScopedToNamespace('space-a');
expect(scopedRepository).toBeInstanceOf(SavedObjectsRepository);
expect(scopedRepository).not.toStrictEqual(repository);

// Checking extensions.spacesExtension are both undefined
// @ts-expect-error type is ISavedObjectsRepository, but in reality is SavedObjectsRepository
expect(repository.extensions.spacesExtension).toBeUndefined();
// @ts-expect-error type is ISavedObjectsRepository, but in reality is SavedObjectsRepository
expect(scopedRepository.extensions.spacesExtension).toBeUndefined();
// @ts-expect-error type is ISavedObjectsRepository, but in reality is SavedObjectsRepository
expect(scopedRepository.extensions.spacesExtension).toStrictEqual(
// @ts-expect-error type is ISavedObjectsRepository, but in reality is SavedObjectsRepository
repository.extensions.spacesExtension
);
});
});

describe('with spacesExtension', () => {
let spacesExtension: jest.Mocked<ISavedObjectsSpacesExtension>;

beforeEach(() => {
spacesExtension = savedObjectsExtensionsMock.createSpacesExtension();
repository = SavedObjectsRepository.createRepository(
migrator,
registry,
'.kibana-test',
client,
logger,
[],
{ spacesExtension }
);
});

describe('#asScopedToNamespace', () => {
it('returns a new client with space-scoped spacesExtensions', () => {
const scopedRepository = repository.asScopedToNamespace('space-a');
expect(scopedRepository).toBeInstanceOf(SavedObjectsRepository);
expect(scopedRepository).not.toStrictEqual(repository);
expect(spacesExtension.asScopedToNamespace).toHaveBeenCalledWith('space-a');

// Checking extensions.spacesExtension are both defined but different
// @ts-expect-error type is ISavedObjectsRepository, but in reality is SavedObjectsRepository
expect(repository.extensions.spacesExtension).not.toBeUndefined();
// @ts-expect-error type is ISavedObjectsRepository, but in reality is SavedObjectsRepository
expect(scopedRepository.extensions.spacesExtension).not.toBeUndefined();
// @ts-expect-error type is ISavedObjectsRepository, but in reality is SavedObjectsRepository
expect(scopedRepository.extensions.spacesExtension).not.toStrictEqual(
// @ts-expect-error type is ISavedObjectsRepository, but in reality is SavedObjectsRepository
repository.extensions.spacesExtension
);
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ export class SavedObjectsRepository implements ISavedObjectsRepository {
});
}

private constructor(options: SavedObjectsRepositoryOptions) {
private constructor(private readonly options: SavedObjectsRepositoryOptions) {
const {
index,
mappings,
Expand Down Expand Up @@ -564,4 +564,17 @@ export class SavedObjectsRepository implements ISavedObjectsRepository {
getCurrentNamespace(namespace?: string) {
return this.helpers.common.getCurrentNamespace(namespace);
}

/**
* {@inheritDoc ISavedObjectsRepository.asScopedToNamespace}
*/
asScopedToNamespace(namespace: string) {
return new SavedObjectsRepository({
...this.options,
extensions: {
...this.options.extensions,
spacesExtension: this.extensions.spacesExtension?.asScopedToNamespace(namespace),
},
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const createRepositoryMock = () => {
collectMultiNamespaceReferences: jest.fn(),
updateObjectsSpaces: jest.fn(),
getCurrentNamespace: jest.fn(),
asScopedToNamespace: jest.fn().mockImplementation(createRepositoryMock),
};

return mock;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const createSecurityExtension = (): jest.Mocked<ISavedObjectsSecurityExtension>
const createSpacesExtension = (): jest.Mocked<ISavedObjectsSpacesExtension> => ({
getCurrentNamespace: jest.fn(),
getSearchableNamespaces: jest.fn(),
asScopedToNamespace: jest.fn().mockImplementation(createSpacesExtension),
});

const create = () => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -335,4 +335,12 @@ describe('SavedObjectsClient', () => {
expect(client.getCurrentNamespace()).toEqual('ns');
expect(mockRepository.getCurrentNamespace).toHaveBeenCalledWith();
});

test('#asScopedToNamespace', () => {
const client = new SavedObjectsClient(mockRepository);

const rescopedClient = client.asScopedToNamespace('ns');
expect(rescopedClient).not.toStrictEqual(client);
expect(mockRepository.asScopedToNamespace).toHaveBeenCalledWith('ns');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -216,4 +216,9 @@ export class SavedObjectsClient implements SavedObjectsClientContract {
getCurrentNamespace() {
return this._repository.getCurrentNamespace();
}

/** {@inheritDoc SavedObjectsClientContract.asScopedToNamespace} */
asScopedToNamespace(namespace: string) {
return new SavedObjectsClient(this._repository.asScopedToNamespace(namespace));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const create = () => {
collectMultiNamespaceReferences: jest.fn(),
updateObjectsSpaces: jest.fn(),
getCurrentNamespace: jest.fn(),
asScopedToNamespace: jest.fn().mockImplementation(create),
};

mock.createPointInTimeFinder = savedObjectsPointInTimeFinderMock.create({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,10 @@
*/

import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server';
import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server';
import { savedObjectsPointInTimeFinderMock } from './point_in_time_finder.mock';

const create = () => {
const mock = {
errors: SavedObjectsErrorHelpers,
const mock: jest.Mocked<SavedObjectsClientContract> = {
create: jest.fn(),
bulkCreate: jest.fn(),
checkConflicts: jest.fn(),
Expand All @@ -33,7 +31,8 @@ const create = () => {
collectMultiNamespaceReferences: jest.fn(),
updateObjectsSpaces: jest.fn(),
getCurrentNamespace: jest.fn(),
} as unknown as jest.Mocked<SavedObjectsClientContract>;
asScopedToNamespace: jest.fn().mockImplementation(create),
};

mock.createPointInTimeFinder = savedObjectsPointInTimeFinderMock.create({
savedObjectsMock: mock,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const createSecurityExtension = (): jest.Mocked<ISavedObjectsSecurityExtension>
const createSpacesExtension = (): jest.Mocked<ISavedObjectsSpacesExtension> => ({
getCurrentNamespace: jest.fn(),
getSearchableNamespaces: jest.fn(),
asScopedToNamespace: jest.fn().mockImplementation(createSpacesExtension),
});

const create = (): jest.Mocked<SavedObjectsExtensions> => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -427,4 +427,10 @@ export interface SavedObjectsClientContract {
* Returns the namespace associated with the client. If the namespace is the default one, this method returns `undefined`.
*/
getCurrentNamespace(): string | undefined;

/**
* Returns a clone of the current Saved Objects client but scoped to the specified namespace.
* @param namespace Space to which the client should be scoped to.
*/
asScopedToNamespace(namespace: string): SavedObjectsClientContract;
}
Original file line number Diff line number Diff line change
Expand Up @@ -552,4 +552,10 @@ export interface ISavedObjectsRepository {
* namespace.
*/
getCurrentNamespace(namespace?: string): string | undefined;

/**
* Returns a new Saved Objects repository scoped to the specified namespace.
* @param namespace Space to which the repository should be scoped to.
*/
asScopedToNamespace(namespace: string): ISavedObjectsRepository;
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,9 @@ export interface ISavedObjectsSpacesExtension {
* If a wildcard '*' is used, it is expanded to an explicit list of namespace strings.
*/
getSearchableNamespaces: (namespaces: string[] | undefined) => Promise<string[]>;
/**
* Returns a new Saved Objects Spaces Extension scoped to the specified namespace.
* @param namespace Space to which the extension should be scoped to.
*/
asScopedToNamespace(namespace: string): ISavedObjectsSpacesExtension;
}
Loading

0 comments on commit 3718918

Please sign in to comment.