Skip to content

Commit

Permalink
Create the ftrSoApis FTR plugin (elastic#149188)
Browse files Browse the repository at this point in the history
## Summary

Fix elastic#148412

More and more SO types will not be accessible from the HTTP APIs (either
`hidden:true` or `hiddenFromHTTPApis: true`).

However, the FTR SO client (`KbnClientSavedObjects`) still needs to be
able to access and manipulate all SO types.

This PR introduces a `ftrSoApis` plugin that is loaded for all FTR
suites. This plugin exposes SO APIs that are used by the FTR client
instead of the public SO HTTP APIs. These APIs are configured to know
about all types, even hidden ones.

Co-authored-by: kibanamachine <[email protected]>
  • Loading branch information
2 people authored and kqualters-elastic committed Feb 6, 2023
1 parent d9644fc commit fe2bf9a
Show file tree
Hide file tree
Showing 36 changed files with 1,657 additions and 12 deletions.
1 change: 1 addition & 0 deletions .buildkite/ftr_configs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ enabled:
- x-pack/test/examples/config.ts
- x-pack/test/fleet_api_integration/config.ts
- x-pack/test/fleet_functional/config.ts
- x-pack/test/ftr_apis/security_and_spaces/config.ts
- x-pack/test/functional_basic/config.ts
- x-pack/test/functional_cors/config.ts
- x-pack/test/functional_embedded/config.ts
Expand Down
4 changes: 4 additions & 0 deletions docs/developer/plugin-list.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,10 @@ for use in their own application.
|Minimal interface for admins to manage files in Kibana.
|{kib-repo}blob/{branch}/src/plugins/ftr_apis/README.md[ftrApis]
|This plugin exposes a set of APIs used internally during functional tests by the FTR.
|{kib-repo}blob/{branch}/src/plugins/guided_onboarding/README.md[guidedOnboarding]
|This plugin contains the code for the Guided Onboarding project. Guided onboarding consists of guides for Solutions (Enterprise Search, Observability, Security) that can be completed as a checklist of steps. The guides help users to ingest their data and to navigate to the correct Solutions pages.
Expand Down
28 changes: 16 additions & 12 deletions packages/kbn-test/src/kbn_client/kbn_client_saved_objects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
*/

import { inspect } from 'util';

import * as Rx from 'rxjs';
import { mergeMap } from 'rxjs/operators';
import { isAxiosResponseError } from '@kbn/dev-utils';
Expand Down Expand Up @@ -90,6 +89,11 @@ async function concurrently<T>(maxConcurrency: number, arr: T[], fn: (item: T) =
}
}

/**
* SO client for FTR.
*
* @remarks: Leverage the `ftrApis` plugin under the hood.
*/
export class KbnClientSavedObjects {
constructor(private readonly log: ToolingLog, private readonly requester: KbnClientRequester) {}

Expand Down Expand Up @@ -117,8 +121,8 @@ export class KbnClientSavedObjects {
const { data } = await this.requester.request<SavedObjectResponse<Attributes>>({
description: 'get saved object',
path: options.space
? uriencode`/s/${options.space}/api/saved_objects/${options.type}/${options.id}`
: uriencode`/api/saved_objects/${options.type}/${options.id}`,
? uriencode`/s/${options.space}/internal/ftr/kbn_client_so/${options.type}/${options.id}`
: uriencode`/internal/ftr/kbn_client_so/${options.type}/${options.id}`,
method: 'GET',
});
return data;
Expand All @@ -133,8 +137,8 @@ export class KbnClientSavedObjects {
const { data } = await this.requester.request<SavedObjectResponse<Attributes>>({
description: 'update saved object',
path: options.id
? uriencode`/api/saved_objects/${options.type}/${options.id}`
: uriencode`/api/saved_objects/${options.type}`,
? uriencode`/internal/ftr/kbn_client_so/${options.type}/${options.id}`
: uriencode`/internal/ftr/kbn_client_so/${options.type}`,
query: {
overwrite: options.overwrite,
},
Expand All @@ -156,7 +160,7 @@ export class KbnClientSavedObjects {

const { data } = await this.requester.request<SavedObjectResponse<Attributes>>({
description: 'update saved object',
path: uriencode`/api/saved_objects/${options.type}/${options.id}`,
path: uriencode`/internal/ftr/kbn_client_so/${options.type}/${options.id}`,
query: {
overwrite: options.overwrite,
},
Expand All @@ -179,8 +183,8 @@ export class KbnClientSavedObjects {
const { data } = await this.requester.request({
description: 'delete saved object',
path: options.space
? uriencode`/s/${options.space}/api/saved_objects/${options.type}/${options.id}`
: uriencode`/api/saved_objects/${options.type}/${options.id}`,
? uriencode`/s/${options.space}/internal/ftr/kbn_client_so/${options.type}/${options.id}`
: uriencode`/internal/ftr/kbn_client_so/${options.type}/${options.id}`,
method: 'DELETE',
});

Expand All @@ -196,8 +200,8 @@ export class KbnClientSavedObjects {
const resp = await this.requester.request<FindApiResponse>({
method: 'GET',
path: options.space
? uriencode`/s/${options.space}/api/saved_objects/_find`
: '/api/saved_objects/_find',
? uriencode`/s/${options.space}/internal/ftr/kbn_client_so/_find`
: `/internal/ftr/kbn_client_so/_find`,
query: {
per_page: 1000,
type: options.types,
Expand Down Expand Up @@ -270,8 +274,8 @@ export class KbnClientSavedObjects {
await this.requester.request({
method: 'DELETE',
path: options.space
? uriencode`/s/${options.space}/api/saved_objects/${obj.type}/${obj.id}?force=true`
: uriencode`/api/saved_objects/${obj.type}/${obj.id}?force=true`,
? uriencode`/s/${options.space}/internal/ftr/kbn_client_so/${obj.type}/${obj.id}`
: uriencode`/internal/ftr/kbn_client_so/${obj.type}/${obj.id}`,
});
deleted++;
} catch (error) {
Expand Down
8 changes: 8 additions & 0 deletions src/plugins/ftr_apis/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# ftrApis plugin

This plugin exposes a set of APIs used internally during functional tests by the FTR.

The APIs currently exposed are:
1. APIs used by the `KbnClientSavedObjects` (SO service of the FTR)

**Remark: these APIs shouldn't be called directly for any reason**
16 changes: 16 additions & 0 deletions src/plugins/ftr_apis/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

module.exports = {
preset: '@kbn/test',
rootDir: '../../..',
roots: ['<rootDir>/src/plugins/ftr_apis'],
coverageDirectory: '<rootDir>/target/kibana-coverage/jest/src/plugins/ftr_apis',
coverageReporters: ['text', 'html'],
collectCoverageFrom: ['<rootDir>/src/plugins/ftr_apis/{common,public,server}/**/*.{js,ts,tsx}'],
};
11 changes: 11 additions & 0 deletions src/plugins/ftr_apis/kibana.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"id": "ftrApis",
"owner": {
"name": "Core",
"githubTeam": "kibana-core"
},
"version": "kibana",
"configPath": ["ftr_apis"],
"server": true,
"ui": false
}
20 changes: 20 additions & 0 deletions src/plugins/ftr_apis/server/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { schema, type TypeOf } from '@kbn/config-schema';
import type { PluginConfigDescriptor } from '@kbn/core/server';

const configSchema = schema.object({
disableApis: schema.boolean({ defaultValue: false }),
});

export type ConfigType = TypeOf<typeof configSchema>;

export const config: PluginConfigDescriptor<ConfigType> = {
schema: configSchema,
};
16 changes: 16 additions & 0 deletions src/plugins/ftr_apis/server/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import type { PluginInitializerContext } from '@kbn/core/server';
import { FtrApisPlugin } from './plugin';

export function plugin(initializerContext: PluginInitializerContext) {
return new FtrApisPlugin(initializerContext);
}

export { config } from './config';
28 changes: 28 additions & 0 deletions src/plugins/ftr_apis/server/plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import type { CoreSetup, Plugin, PluginInitializerContext } from '@kbn/core/server';
import { registerRoutes } from './routes';
import type { ConfigType } from './config';

export class FtrApisPlugin implements Plugin {
private readonly config: ConfigType;

constructor(initializerContext: PluginInitializerContext) {
this.config = initializerContext.config.get<ConfigType>();
}

public setup({ http, savedObjects }: CoreSetup) {
const router = http.createRouter();
if (!this.config.disableApis) {
registerRoutes(router);
}
}

public start() {}
}
14 changes: 14 additions & 0 deletions src/plugins/ftr_apis/server/routes/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import type { IRouter } from '@kbn/core/server';
import { registerKbnClientSoRoutes } from './kbn_client_so';

export const registerRoutes = (router: IRouter) => {
registerKbnClientSoRoutes(router);
};
38 changes: 38 additions & 0 deletions src/plugins/ftr_apis/server/routes/kbn_client_so/bulk_delete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import type { IRouter } from '@kbn/core/server';
import { schema } from '@kbn/config-schema';
import { KBN_CLIENT_API_PREFIX, listHiddenTypes, catchAndReturnBoomErrors } from './utils';

export const registerBulkDeleteRoute = (router: IRouter) => {
router.post(
{
path: `${KBN_CLIENT_API_PREFIX}/_bulk_delete`,
options: {
tags: ['access:ftrApis'],
},
validate: {
body: schema.arrayOf(
schema.object({
type: schema.string(),
id: schema.string(),
})
),
},
},
catchAndReturnBoomErrors(async (ctx, req, res) => {
const { savedObjects } = await ctx.core;
const hiddenTypes = listHiddenTypes(savedObjects.typeRegistry);
const soClient = savedObjects.getClient({ includedHiddenTypes: hiddenTypes });

const statuses = await soClient.bulkDelete(req.body, { force: true });
return res.ok({ body: statuses });
})
);
};
62 changes: 62 additions & 0 deletions src/plugins/ftr_apis/server/routes/kbn_client_so/create.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import type { IRouter } from '@kbn/core/server';
import { schema } from '@kbn/config-schema';
import { KBN_CLIENT_API_PREFIX, listHiddenTypes, catchAndReturnBoomErrors } from './utils';

export const registerCreateRoute = (router: IRouter) => {
router.post(
{
path: `${KBN_CLIENT_API_PREFIX}/{type}/{id?}`,
options: {
tags: ['access:ftrApis'],
},
validate: {
params: schema.object({
type: schema.string(),
id: schema.maybe(schema.string()),
}),
query: schema.object({
overwrite: schema.boolean({ defaultValue: false }),
}),
body: schema.object({
attributes: schema.recordOf(schema.string(), schema.any()),
migrationVersion: schema.maybe(schema.recordOf(schema.string(), schema.string())),
references: schema.maybe(
schema.arrayOf(
schema.object({
name: schema.string(),
type: schema.string(),
id: schema.string(),
})
)
),
}),
},
},
catchAndReturnBoomErrors(async (ctx, req, res) => {
const { type, id } = req.params;
const { overwrite } = req.query;
const { attributes, migrationVersion, references } = req.body;
const { savedObjects } = await ctx.core;

const hiddenTypes = listHiddenTypes(savedObjects.typeRegistry);
const soClient = savedObjects.getClient({ includedHiddenTypes: hiddenTypes });

const options = {
id,
overwrite,
migrationVersion,
references,
};
const result = await soClient.create(type, attributes, options);
return res.ok({ body: result });
})
);
};
38 changes: 38 additions & 0 deletions src/plugins/ftr_apis/server/routes/kbn_client_so/delete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import type { IRouter } from '@kbn/core/server';
import { schema } from '@kbn/config-schema';
import { KBN_CLIENT_API_PREFIX, listHiddenTypes, catchAndReturnBoomErrors } from './utils';

export const registerDeleteRoute = (router: IRouter) => {
router.delete(
{
path: `${KBN_CLIENT_API_PREFIX}/{type}/{id}`,
options: {
tags: ['access:ftrApis'],
},
validate: {
params: schema.object({
type: schema.string(),
id: schema.string(),
}),
},
},
catchAndReturnBoomErrors(async (ctx, req, res) => {
const { type, id } = req.params;
const { savedObjects } = await ctx.core;

const hiddenTypes = listHiddenTypes(savedObjects.typeRegistry);
const soClient = savedObjects.getClient({ includedHiddenTypes: hiddenTypes });

const result = await soClient.delete(type, id, { force: true });
return res.ok({ body: result });
})
);
};
Loading

0 comments on commit fe2bf9a

Please sign in to comment.