Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Saved Objects] adds support for including hidden types in saved objects client #66879

Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ Describes the factory used to create instances of the Saved Objects Client.
<b>Signature:</b>

```typescript
export declare type SavedObjectsClientFactory = ({ request, }: {
export declare type SavedObjectsClientFactory = ({ request, includedHiddenTypes, }: {
request: KibanaRequest;
includedHiddenTypes?: string[];
}) => SavedObjectsClientContract;
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [SavedObjectsClientProviderOptions](./kibana-plugin-core-server.savedobjectsclientprovideroptions.md) &gt; [includedHiddenTypes](./kibana-plugin-core-server.savedobjectsclientprovideroptions.includedhiddentypes.md)

## SavedObjectsClientProviderOptions.includedHiddenTypes property

<b>Signature:</b>

```typescript
includedHiddenTypes?: string[];
```
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ export interface SavedObjectsClientProviderOptions
| Property | Type | Description |
| --- | --- | --- |
| [excludedWrappers](./kibana-plugin-core-server.savedobjectsclientprovideroptions.excludedwrappers.md) | <code>string[]</code> | |
| [includedHiddenTypes](./kibana-plugin-core-server.savedobjectsclientprovideroptions.includedhiddentypes.md) | <code>string[]</code> | |

Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ Creates a [Saved Objects repository](./kibana-plugin-core-server.isavedobjectsre
<b>Signature:</b>

```typescript
createInternalRepository: (extraTypes?: string[]) => ISavedObjectsRepository;
createInternalRepository: (includedHiddenTypes?: string[]) => ISavedObjectsRepository;
```
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ Creates a [Saved Objects repository](./kibana-plugin-core-server.isavedobjectsre
<b>Signature:</b>

```typescript
createScopedRepository: (req: KibanaRequest, extraTypes?: string[]) => ISavedObjectsRepository;
createScopedRepository: (req: KibanaRequest, includedHiddenTypes?: string[]) => ISavedObjectsRepository;
```
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ export interface SavedObjectsRepositoryFactory

| Property | Type | Description |
| --- | --- | --- |
| [createInternalRepository](./kibana-plugin-core-server.savedobjectsrepositoryfactory.createinternalrepository.md) | <code>(extraTypes?: string[]) =&gt; ISavedObjectsRepository</code> | Creates a [Saved Objects repository](./kibana-plugin-core-server.isavedobjectsrepository.md) that uses the internal Kibana user for authenticating with Elasticsearch. |
| [createScopedRepository](./kibana-plugin-core-server.savedobjectsrepositoryfactory.createscopedrepository.md) | <code>(req: KibanaRequest, extraTypes?: string[]) =&gt; ISavedObjectsRepository</code> | Creates a [Saved Objects repository](./kibana-plugin-core-server.isavedobjectsrepository.md) that uses the credentials from the passed in request to authenticate with Elasticsearch. |
| [createInternalRepository](./kibana-plugin-core-server.savedobjectsrepositoryfactory.createinternalrepository.md) | <code>(includedHiddenTypes?: string[]) =&gt; ISavedObjectsRepository</code> | Creates a [Saved Objects repository](./kibana-plugin-core-server.isavedobjectsrepository.md) that uses the internal Kibana user for authenticating with Elasticsearch. |
| [createScopedRepository](./kibana-plugin-core-server.savedobjectsrepositoryfactory.createscopedrepository.md) | <code>(req: KibanaRequest, includedHiddenTypes?: string[]) =&gt; ISavedObjectsRepository</code> | Creates a [Saved Objects repository](./kibana-plugin-core-server.isavedobjectsrepository.md) that uses the credentials from the passed in request to authenticate with Elasticsearch. |

Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ Creates a [Saved Objects repository](./kibana-plugin-core-server.isavedobjectsre
<b>Signature:</b>

```typescript
createInternalRepository: (extraTypes?: string[]) => ISavedObjectsRepository;
createInternalRepository: (includedHiddenTypes?: string[]) => ISavedObjectsRepository;
```
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Creates a [Saved Objects repository](./kibana-plugin-core-server.isavedobjectsre
<b>Signature:</b>

```typescript
createScopedRepository: (req: KibanaRequest, extraTypes?: string[]) => ISavedObjectsRepository;
createScopedRepository: (req: KibanaRequest, includedHiddenTypes?: string[]) => ISavedObjectsRepository;
```

## Remarks
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ export interface SavedObjectsServiceStart

| Property | Type | Description |
| --- | --- | --- |
| [createInternalRepository](./kibana-plugin-core-server.savedobjectsservicestart.createinternalrepository.md) | <code>(extraTypes?: string[]) =&gt; ISavedObjectsRepository</code> | Creates a [Saved Objects repository](./kibana-plugin-core-server.isavedobjectsrepository.md) that uses the internal Kibana user for authenticating with Elasticsearch. |
| [createScopedRepository](./kibana-plugin-core-server.savedobjectsservicestart.createscopedrepository.md) | <code>(req: KibanaRequest, extraTypes?: string[]) =&gt; ISavedObjectsRepository</code> | Creates a [Saved Objects repository](./kibana-plugin-core-server.isavedobjectsrepository.md) that uses the credentials from the passed in request to authenticate with Elasticsearch. |
| [createInternalRepository](./kibana-plugin-core-server.savedobjectsservicestart.createinternalrepository.md) | <code>(includedHiddenTypes?: string[]) =&gt; ISavedObjectsRepository</code> | Creates a [Saved Objects repository](./kibana-plugin-core-server.isavedobjectsrepository.md) that uses the internal Kibana user for authenticating with Elasticsearch. |
| [createScopedRepository](./kibana-plugin-core-server.savedobjectsservicestart.createscopedrepository.md) | <code>(req: KibanaRequest, includedHiddenTypes?: string[]) =&gt; ISavedObjectsRepository</code> | Creates a [Saved Objects repository](./kibana-plugin-core-server.isavedobjectsrepository.md) that uses the credentials from the passed in request to authenticate with Elasticsearch. |
| [createSerializer](./kibana-plugin-core-server.savedobjectsservicestart.createserializer.md) | <code>() =&gt; SavedObjectsSerializer</code> | Creates a [serializer](./kibana-plugin-core-server.savedobjectsserializer.md) that is aware of all registered types. |
| [getScopedClient](./kibana-plugin-core-server.savedobjectsservicestart.getscopedclient.md) | <code>(req: KibanaRequest, options?: SavedObjectsClientProviderOptions) =&gt; SavedObjectsClientContract</code> | Creates a [Saved Objects client](./kibana-plugin-core-server.savedobjectsclientcontract.md) that uses the credentials from the passed in request to authenticate with Elasticsearch. If other plugins have registered Saved Objects client wrappers, these will be applied to extend the functionality of the client.<!-- -->A client that is already scoped to the incoming request is also exposed from the route handler context see [RequestHandlerContext](./kibana-plugin-core-server.requesthandlercontext.md)<!-- -->. |
| [getTypeRegistry](./kibana-plugin-core-server.savedobjectsservicestart.gettyperegistry.md) | <code>() =&gt; ISavedObjectTypeRegistry</code> | Returns the [registry](./kibana-plugin-core-server.isavedobjecttyperegistry.md) containing all registered [saved object types](./kibana-plugin-core-server.savedobjectstype.md) |
Expand Down
86 changes: 86 additions & 0 deletions src/core/server/saved_objects/saved_objects_service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ import { legacyServiceMock } from '../legacy/legacy_service.mock';
import { httpServiceMock } from '../http/http_service.mock';
import { SavedObjectsClientFactoryProvider } from './service/lib';
import { NodesVersionCompatibility } from '../elasticsearch/version_check/ensure_es_version';
import { SavedObjectsRepository } from './service/lib/repository';
import { KibanaRequest } from '../http';

jest.mock('./service/lib/repository');

describe('SavedObjectsService', () => {
const createCoreContext = ({
Expand Down Expand Up @@ -269,5 +273,87 @@ describe('SavedObjectsService', () => {
expect(getTypeRegistry()).toBe(typeRegistryInstanceMock);
});
});

describe('#createScopedRepository', () => {
it('creates a respository scoped to the user', async () => {
const coreContext = createCoreContext({ skipMigration: false });
const soService = new SavedObjectsService(coreContext);
const coreSetup = createSetupDeps();
await soService.setup(coreSetup);
const { createScopedRepository } = await soService.start({});

const req = {} as KibanaRequest;
createScopedRepository(req);

expect(coreSetup.elasticsearch.adminClient.asScoped).toHaveBeenCalledWith(req);

const [
{
value: { callAsCurrentUser },
},
] = coreSetup.elasticsearch.adminClient.asScoped.mock.results;

const [
[, , , callCluster, includedHiddenTypes],
] = (SavedObjectsRepository.createRepository as jest.Mocked<any>).mock.calls;

// expect(coreSetup.elasticsearch.adminClient.callAsInternalUser).toBe(callCluster);
gmmorris marked this conversation as resolved.
Show resolved Hide resolved
expect(callCluster).toBe(callAsCurrentUser);
expect(includedHiddenTypes).toEqual([]);
});

it('creates a respository including hidden types when specified', async () => {
const coreContext = createCoreContext({ skipMigration: false });
const soService = new SavedObjectsService(coreContext);
const coreSetup = createSetupDeps();
await soService.setup(coreSetup);
const { createScopedRepository } = await soService.start({});

const req = {} as KibanaRequest;
createScopedRepository(req, ['someHiddenType']);

const [
[, , , , includedHiddenTypes],
] = (SavedObjectsRepository.createRepository as jest.Mocked<any>).mock.calls;

expect(includedHiddenTypes).toEqual(['someHiddenType']);
});
});

describe('#createInternalRepository', () => {
it('creates a respository using the admin user', async () => {
const coreContext = createCoreContext({ skipMigration: false });
const soService = new SavedObjectsService(coreContext);
const coreSetup = createSetupDeps();
await soService.setup(coreSetup);
const { createInternalRepository } = await soService.start({});

createInternalRepository();

const [
[, , , callCluster, includedHiddenTypes],
] = (SavedObjectsRepository.createRepository as jest.Mocked<any>).mock.calls;

expect(coreSetup.elasticsearch.adminClient.callAsInternalUser).toBe(callCluster);
expect(callCluster).toBe(coreSetup.elasticsearch.adminClient.callAsInternalUser);
expect(includedHiddenTypes).toEqual([]);
});

it('creates a respository including hidden types when specified', async () => {
const coreContext = createCoreContext({ skipMigration: false });
const soService = new SavedObjectsService(coreContext);
const coreSetup = createSetupDeps();
await soService.setup(coreSetup);
const { createInternalRepository } = await soService.start({});

createInternalRepository(['someHiddenType']);

const [
[, , , , includedHiddenTypes],
] = (SavedObjectsRepository.createRepository as jest.Mocked<any>).mock.calls;

expect(includedHiddenTypes).toEqual(['someHiddenType']);
});
});
});
});
38 changes: 22 additions & 16 deletions src/core/server/saved_objects/saved_objects_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,20 +198,23 @@ export interface SavedObjectsServiceStart {
* Elasticsearch.
*
* @param req - The request to create the scoped repository from.
* @param extraTypes - A list of additional hidden types the repository should have access to.
* @param includedHiddenTypes - A list of additional hidden types the repository should have access to.
*
* @remarks
* Prefer using `getScopedClient`. This should only be used when using methods
* not exposed on {@link SavedObjectsClientContract}
*/
createScopedRepository: (req: KibanaRequest, extraTypes?: string[]) => ISavedObjectsRepository;
createScopedRepository: (
req: KibanaRequest,
includedHiddenTypes?: string[]
) => ISavedObjectsRepository;
/**
* Creates a {@link ISavedObjectsRepository | Saved Objects repository} that
* uses the internal Kibana user for authenticating with Elasticsearch.
*
* @param extraTypes - A list of additional hidden types the repository should have access to.
* @param includedHiddenTypes - A list of additional hidden types the repository should have access to.
*/
createInternalRepository: (extraTypes?: string[]) => ISavedObjectsRepository;
createInternalRepository: (includedHiddenTypes?: string[]) => ISavedObjectsRepository;
/**
* Creates a {@link SavedObjectsSerializer | serializer} that is aware of all registered types.
*/
Expand Down Expand Up @@ -246,16 +249,19 @@ export interface SavedObjectsRepositoryFactory {
* uses the credentials from the passed in request to authenticate with
* Elasticsearch.
*
* @param extraTypes - A list of additional hidden types the repository should have access to.
* @param includedHiddenTypes - A list of additional hidden types the repository should have access to.
*/
createScopedRepository: (req: KibanaRequest, extraTypes?: string[]) => ISavedObjectsRepository;
createScopedRepository: (
req: KibanaRequest,
includedHiddenTypes?: string[]
) => ISavedObjectsRepository;
/**
* Creates a {@link ISavedObjectsRepository | Saved Objects repository} that
* uses the internal Kibana user for authenticating with Elasticsearch.
*
* @param extraTypes - A list of additional hidden types the repository should have access to.
* @param includedHiddenTypes - A list of additional hidden types the repository should have access to.
*/
createInternalRepository: (extraTypes?: string[]) => ISavedObjectsRepository;
createInternalRepository: (includedHiddenTypes?: string[]) => ISavedObjectsRepository;
}

/** @internal */
Expand Down Expand Up @@ -417,26 +423,26 @@ export class SavedObjectsService
await migrator.runMigrations();
}

const createRepository = (callCluster: APICaller, extraTypes: string[] = []) => {
const createRepository = (callCluster: APICaller, includedHiddenTypes: string[] = []) => {
return SavedObjectsRepository.createRepository(
migrator,
this.typeRegistry,
kibanaConfig.index,
callCluster,
extraTypes
includedHiddenTypes
);
};

const repositoryFactory: SavedObjectsRepositoryFactory = {
createInternalRepository: (extraTypes?: string[]) =>
createRepository(adminClient.callAsInternalUser, extraTypes),
createScopedRepository: (req: KibanaRequest, extraTypes?: string[]) =>
createRepository(adminClient.asScoped(req).callAsCurrentUser, extraTypes),
createInternalRepository: (includedHiddenTypes?: string[]) =>
createRepository(adminClient.callAsInternalUser, includedHiddenTypes),
createScopedRepository: (req: KibanaRequest, includedHiddenTypes?: string[]) =>
createRepository(adminClient.asScoped(req).callAsCurrentUser, includedHiddenTypes),
};

const clientProvider = new SavedObjectsClientProvider({
defaultClientFactory({ request }) {
const repository = repositoryFactory.createScopedRepository(request);
defaultClientFactory({ request, includedHiddenTypes }) {
const repository = repositoryFactory.createScopedRepository(request, includedHiddenTypes);
return new SavedObjectsClient(repository);
},
typeRegistry: this.typeRegistry,
Expand Down
6 changes: 3 additions & 3 deletions src/core/server/saved_objects/service/lib/repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,22 +132,22 @@ export class SavedObjectsRepository {
typeRegistry: SavedObjectTypeRegistry,
indexName: string,
callCluster: APICaller,
extraTypes: string[] = [],
includedHiddenTypes: string[] = [],
injectedConstructor: any = SavedObjectsRepository
): ISavedObjectsRepository {
const mappings = migrator.getActiveMappings();
const allTypes = Object.keys(getRootPropertiesObjects(mappings));
const serializer = new SavedObjectsSerializer(typeRegistry);
const visibleTypes = allTypes.filter(type => !typeRegistry.isHidden(type));

const missingTypeMappings = extraTypes.filter(type => !allTypes.includes(type));
const missingTypeMappings = includedHiddenTypes.filter(type => !allTypes.includes(type));
if (missingTypeMappings.length > 0) {
throw new Error(
`Missing mappings for saved objects types: '${missingTypeMappings.join(', ')}'`
);
}

const allowedTypes = [...new Set(visibleTypes.concat(extraTypes))];
const allowedTypes = [...new Set(visibleTypes.concat(includedHiddenTypes))];

return new injectedConstructor({
index: indexName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,3 +167,23 @@ test(`allows all wrappers to be excluded`, () => {
expect(firstClientWrapperFactoryMock).not.toHaveBeenCalled();
expect(secondClientWrapperFactoryMock).not.toHaveBeenCalled();
});

test(`allows typed to be included`, () => {
gmmorris marked this conversation as resolved.
Show resolved Hide resolved
const defaultClient = Symbol();
const defaultClientFactoryMock = jest.fn().mockReturnValue(defaultClient);
const clientProvider = new SavedObjectsClientProvider({
defaultClientFactory: defaultClientFactoryMock,
typeRegistry: typeRegistryMock.create(),
});
const request = Symbol();

const actualClient = clientProvider.getClient(request, {
includedHiddenTypes: ['task'],
});

expect(actualClient).toBe(defaultClient);
expect(defaultClientFactoryMock).toHaveBeenCalledWith({
request,
includedHiddenTypes: ['task'],
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,10 @@ export type SavedObjectsClientWrapperFactory = (
*/
export type SavedObjectsClientFactory = ({
request,
includedHiddenTypes,
}: {
request: KibanaRequest;
includedHiddenTypes?: string[];
}) => SavedObjectsClientContract;

/**
Expand All @@ -64,6 +66,7 @@ export type SavedObjectsClientFactoryProvider = (
*/
export interface SavedObjectsClientProviderOptions {
excludedWrappers?: string[];
includedHiddenTypes?: string[];
}

/**
Expand Down Expand Up @@ -121,14 +124,13 @@ export class SavedObjectsClientProvider {

getClient(
request: KibanaRequest,
options: SavedObjectsClientProviderOptions = {}
{ includedHiddenTypes, excludedWrappers = [] }: SavedObjectsClientProviderOptions = {}
): SavedObjectsClientContract {
const client = this._clientFactory({
request,
includedHiddenTypes,
});

const excludedWrappers = options.excludedWrappers || [];

return this._wrapperFactories
.toPrioritizedArray()
.reduceRight((clientToWrap, { id, factory }) => {
Expand Down
Loading