mirror of
https://github.com/elastic/kibana.git
synced 2025-06-28 11:05:39 -04:00
[Lens] Add internal CRUD api routes (#223296)
## Summary This adds basic Lens CRUD api routes using the Content Management system. | Operation | URI | |--------|--------| | Create | `POST api/lens/visualizations` | | Get | `GET api/lens/visualizations/{id}` | | Search | `GET api/lens/visualizations?query=test` | | Update | `PUT api/lens/visualizations/{id}` | | Delete | `DELETE api/lens/visualizations/{id}` | ### Changes to Lens Content Management The custom `update` method uses `soClient.create` under the hood for reasons (i.e. #160116). However, doing this acts as an update or create method with the provided `id`. I changed this behavior so now any update where the id is not found will return a `404` error. Closes #221941 Closes #221942 - OpenAPI docs auto generate from route schema ### Testing You can testing this locally in kibana dev console like so... ``` GET kbn:/api/lens/visualizations/<id>?apiVersion=1 ``` > The `apiVersion` query param is needed to test `internal` api routes. ## Checklist - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md) - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [x] This was checked for breaking HTTP API changes, and any breaking changes have been approved by the breaking-change committee. The `release_note:breaking` label should be applied in these situations. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Marco Vettorello <marco.vettorello@elastic.co>
This commit is contained in:
parent
2c55a7d9d0
commit
17c2556fc6
37 changed files with 1582 additions and 37 deletions
|
@ -378,6 +378,7 @@ enabled:
|
||||||
- x-pack/platform/test/api_integration/apis/management/config.ts
|
- x-pack/platform/test/api_integration/apis/management/config.ts
|
||||||
- x-pack/platform/test/api_integration/apis/management/index_management/disabled_data_enrichers/config.ts
|
- x-pack/platform/test/api_integration/apis/management/index_management/disabled_data_enrichers/config.ts
|
||||||
- x-pack/platform/test/api_integration/apis/maps/config.ts
|
- x-pack/platform/test/api_integration/apis/maps/config.ts
|
||||||
|
- x-pack/platform/test/api_integration/apis/lens/config.ts
|
||||||
- x-pack/platform/test/api_integration/apis/ml/config.ts
|
- x-pack/platform/test/api_integration/apis/ml/config.ts
|
||||||
- x-pack/platform/test/api_integration/apis/monitoring/config.ts
|
- x-pack/platform/test/api_integration/apis/monitoring/config.ts
|
||||||
- x-pack/platform/test/api_integration/apis/monitoring_collection/config.ts
|
- x-pack/platform/test/api_integration/apis/monitoring_collection/config.ts
|
||||||
|
|
4
.github/CODEOWNERS
vendored
4
.github/CODEOWNERS
vendored
|
@ -1262,7 +1262,8 @@ src/platform/plugins/shared/discover/public/context_awareness/profile_providers/
|
||||||
/x-pack/test/functional/page_objects/lens_page.ts @elastic/kibana-visualizations
|
/x-pack/test/functional/page_objects/lens_page.ts @elastic/kibana-visualizations
|
||||||
/x-pack/test/functional/es_archives/lens @elastic/kibana-visualizations
|
/x-pack/test/functional/es_archives/lens @elastic/kibana-visualizations
|
||||||
/x-pack/test/examples/embedded_lens @elastic/kibana-visualizations
|
/x-pack/test/examples/embedded_lens @elastic/kibana-visualizations
|
||||||
/x-pack/test/api_integration/fixtures/kbn_archiver/lens/constant_keyword.json @elastic/kibana-visualizations
|
/x-pack/test/api_integration/fixtures/kbn_archiver/lens/ @elastic/kibana-visualizations
|
||||||
|
/x-pack/platform/test/api_integration/apis/lens @elastic/kibana-visualizations
|
||||||
/src/platform/test/plugin_functional/test_suites/custom_visualizations @elastic/kibana-visualizations
|
/src/platform/test/plugin_functional/test_suites/custom_visualizations @elastic/kibana-visualizations
|
||||||
/src/platform/test/plugin_functional/plugins/kbn_tp_custom_visualizations @elastic/kibana-visualizations
|
/src/platform/test/plugin_functional/plugins/kbn_tp_custom_visualizations @elastic/kibana-visualizations
|
||||||
/x-pack/test/functional/fixtures/kbn_archiver/visualize @elastic/kibana-visualizations
|
/x-pack/test/functional/fixtures/kbn_archiver/visualize @elastic/kibana-visualizations
|
||||||
|
@ -2742,7 +2743,6 @@ x-pack/solutions/security/plugins/security_solution/public/security_integrations
|
||||||
x-pack/solutions/security/plugins/security_solution/server/security_integrations @elastic/security-service-integrations
|
x-pack/solutions/security/plugins/security_solution/server/security_integrations @elastic/security-service-integrations
|
||||||
x-pack/solutions/security/plugins/security_solution/server/lib/security_integrations @elastic/security-service-integrations
|
x-pack/solutions/security/plugins/security_solution/server/lib/security_integrations @elastic/security-service-integrations
|
||||||
|
|
||||||
|
|
||||||
# Kibana design
|
# Kibana design
|
||||||
# scss overrides should be below this line for specificity
|
# scss overrides should be below this line for specificity
|
||||||
**/*.scss @elastic/kibana-design
|
**/*.scss @elastic/kibana-design
|
||||||
|
|
|
@ -56,25 +56,21 @@ export type LensSavedObject = LensCrudTypes['Item'];
|
||||||
export type PartialLensSavedObject = LensCrudTypes['PartialItem'];
|
export type PartialLensSavedObject = LensCrudTypes['PartialItem'];
|
||||||
|
|
||||||
// ----------- GET --------------
|
// ----------- GET --------------
|
||||||
|
|
||||||
export type LensGetIn = LensCrudTypes['GetIn'];
|
export type LensGetIn = LensCrudTypes['GetIn'];
|
||||||
|
|
||||||
export type LensGetOut = LensCrudTypes['GetOut'];
|
export type LensGetOut = LensCrudTypes['GetOut'];
|
||||||
|
|
||||||
// ----------- CREATE --------------
|
// ----------- CREATE --------------
|
||||||
|
|
||||||
export type LensCreateIn = LensCrudTypes['CreateIn'];
|
export type LensCreateIn = LensCrudTypes['CreateIn'];
|
||||||
|
|
||||||
export type LensCreateOut = LensCrudTypes['CreateOut'];
|
export type LensCreateOut = LensCrudTypes['CreateOut'];
|
||||||
// ----------- UPDATE --------------
|
|
||||||
|
|
||||||
|
// ----------- UPDATE --------------
|
||||||
export type LensUpdateIn = LensCrudTypes['UpdateIn'];
|
export type LensUpdateIn = LensCrudTypes['UpdateIn'];
|
||||||
export type LensUpdateOut = LensCrudTypes['UpdateOut'];
|
export type LensUpdateOut = LensCrudTypes['UpdateOut'];
|
||||||
// ----------- DELETE --------------
|
|
||||||
|
|
||||||
|
// ----------- DELETE --------------
|
||||||
export type LensDeleteIn = LensCrudTypes['DeleteIn'];
|
export type LensDeleteIn = LensCrudTypes['DeleteIn'];
|
||||||
export type LensDeleteOut = LensCrudTypes['DeleteOut'];
|
export type LensDeleteOut = LensCrudTypes['DeleteOut'];
|
||||||
// ----------- SEARCH --------------
|
|
||||||
|
|
||||||
|
// ----------- SEARCH --------------
|
||||||
export type LensSearchIn = LensCrudTypes['SearchIn'];
|
export type LensSearchIn = LensCrudTypes['SearchIn'];
|
||||||
export type LensSearchOut = LensCrudTypes['SearchOut'];
|
export type LensSearchOut = LensCrudTypes['SearchOut'];
|
||||||
|
|
11
x-pack/platform/plugins/shared/lens/server/api/constants.ts
Normal file
11
x-pack/platform/plugins/shared/lens/server/api/constants.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const PUBLIC_API_VERSION = '1';
|
||||||
|
export const PUBLIC_API_CONTENT_MANAGEMENT_VERSION = 1;
|
||||||
|
export const PUBLIC_API_PATH = '/api/lens';
|
||||||
|
export const PUBLIC_API_ACCESS = 'internal';
|
|
@ -0,0 +1,13 @@
|
||||||
|
/*
|
||||||
|
* 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { RegisterAPIRoutesArgs } from '../types';
|
||||||
|
import { registerLensVisualizationsAPIRoutes } from './visualizations';
|
||||||
|
|
||||||
|
export function registerLensAPIRoutes(args: RegisterAPIRoutesArgs) {
|
||||||
|
registerLensVisualizationsAPIRoutes(args);
|
||||||
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
/*
|
||||||
|
* 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { schema } from '@kbn/config-schema';
|
||||||
|
|
||||||
|
import { boomify, isBoom } from '@hapi/boom';
|
||||||
|
import { CONTENT_ID, type LensSavedObject } from '../../../../common/content_management';
|
||||||
|
import {
|
||||||
|
PUBLIC_API_PATH,
|
||||||
|
PUBLIC_API_VERSION,
|
||||||
|
PUBLIC_API_CONTENT_MANAGEMENT_VERSION,
|
||||||
|
PUBLIC_API_ACCESS,
|
||||||
|
} from '../../constants';
|
||||||
|
import {
|
||||||
|
lensAttributesSchema,
|
||||||
|
lensCreateOptionsSchema,
|
||||||
|
lensSavedObjectSchema,
|
||||||
|
} from '../../../content_management/v1';
|
||||||
|
import { RegisterAPIRouteFn } from '../../types';
|
||||||
|
|
||||||
|
export const registerLensVisualizationsCreateAPIRoute: RegisterAPIRouteFn = (
|
||||||
|
router,
|
||||||
|
{ contentManagement }
|
||||||
|
) => {
|
||||||
|
const createRoute = router.post({
|
||||||
|
path: `${PUBLIC_API_PATH}/visualizations`,
|
||||||
|
access: PUBLIC_API_ACCESS,
|
||||||
|
enableQueryVersion: true,
|
||||||
|
summary: 'Create Lens visualization',
|
||||||
|
description: 'Create a new Lens visualization.',
|
||||||
|
options: {
|
||||||
|
tags: ['oas-tag:Lens'],
|
||||||
|
availability: {
|
||||||
|
stability: 'experimental',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
security: {
|
||||||
|
authz: {
|
||||||
|
enabled: false,
|
||||||
|
reason: 'Relies on Content Client for authorization',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
createRoute.addVersion(
|
||||||
|
{
|
||||||
|
version: PUBLIC_API_VERSION,
|
||||||
|
validate: {
|
||||||
|
request: {
|
||||||
|
body: schema.object({
|
||||||
|
options: lensCreateOptionsSchema,
|
||||||
|
data: lensAttributesSchema,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
response: {
|
||||||
|
201: {
|
||||||
|
body: () => lensSavedObjectSchema,
|
||||||
|
description: 'Created',
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
description: 'Malformed request',
|
||||||
|
},
|
||||||
|
401: {
|
||||||
|
description: 'Unauthorized',
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
description: 'Forbidden',
|
||||||
|
},
|
||||||
|
500: {
|
||||||
|
description: 'Internal Server Error',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async (ctx, req, res) => {
|
||||||
|
let result;
|
||||||
|
const { data, options } = req.body;
|
||||||
|
const client = contentManagement.contentClient
|
||||||
|
.getForRequest({ request: req, requestHandlerContext: ctx })
|
||||||
|
.for<LensSavedObject>(CONTENT_ID, PUBLIC_API_CONTENT_MANAGEMENT_VERSION);
|
||||||
|
|
||||||
|
try {
|
||||||
|
({ result } = await client.create(data, options));
|
||||||
|
} catch (error) {
|
||||||
|
if (isBoom(error) && error.output.statusCode === 403) {
|
||||||
|
return res.forbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
return boomify(error); // forward unknown error
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.created({ body: result.item });
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
* 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { schema } from '@kbn/config-schema';
|
||||||
|
|
||||||
|
import { boomify, isBoom } from '@hapi/boom';
|
||||||
|
import { CONTENT_ID, type LensSavedObject } from '../../../../common/content_management';
|
||||||
|
import {
|
||||||
|
PUBLIC_API_PATH,
|
||||||
|
PUBLIC_API_VERSION,
|
||||||
|
PUBLIC_API_CONTENT_MANAGEMENT_VERSION,
|
||||||
|
PUBLIC_API_ACCESS,
|
||||||
|
} from '../../constants';
|
||||||
|
import { RegisterAPIRouteFn } from '../../types';
|
||||||
|
|
||||||
|
export const registerLensVisualizationsDeleteAPIRoute: RegisterAPIRouteFn = (
|
||||||
|
router,
|
||||||
|
{ contentManagement }
|
||||||
|
) => {
|
||||||
|
const deleteRoute = router.delete({
|
||||||
|
path: `${PUBLIC_API_PATH}/visualizations/{id}`,
|
||||||
|
access: PUBLIC_API_ACCESS,
|
||||||
|
enableQueryVersion: true,
|
||||||
|
summary: 'Delete Lens visualization',
|
||||||
|
description: 'Delete a Lens visualization by id.',
|
||||||
|
options: {
|
||||||
|
tags: ['oas-tag:Lens'],
|
||||||
|
availability: {
|
||||||
|
stability: 'experimental',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
security: {
|
||||||
|
authz: {
|
||||||
|
enabled: false,
|
||||||
|
reason: 'Relies on Content Client for authorization',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
deleteRoute.addVersion(
|
||||||
|
{
|
||||||
|
version: PUBLIC_API_VERSION,
|
||||||
|
validate: {
|
||||||
|
request: {
|
||||||
|
params: schema.object({
|
||||||
|
id: schema.string({
|
||||||
|
meta: {
|
||||||
|
description: 'The saved object id of a Lens visualization.',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
response: {
|
||||||
|
204: {
|
||||||
|
description: 'No Content',
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
description: 'Malformed request',
|
||||||
|
},
|
||||||
|
401: {
|
||||||
|
description: 'Unauthorized',
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
description: 'Forbidden',
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
description: 'Resource not found',
|
||||||
|
},
|
||||||
|
500: {
|
||||||
|
description: 'Internal Server Error',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async (ctx, req, res) => {
|
||||||
|
const client = contentManagement.contentClient
|
||||||
|
.getForRequest({ request: req, requestHandlerContext: ctx })
|
||||||
|
.for<LensSavedObject>(CONTENT_ID, PUBLIC_API_CONTENT_MANAGEMENT_VERSION);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await client.delete(req.params.id);
|
||||||
|
} catch (error) {
|
||||||
|
if (isBoom(error)) {
|
||||||
|
if (error.output.statusCode === 404) {
|
||||||
|
return res.notFound({
|
||||||
|
body: {
|
||||||
|
message: `A Lens visualization with saved object id [${req.params.id}] was not found.`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (error.output.statusCode === 403) {
|
||||||
|
return res.forbidden();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return boomify(error); // forward unknown error
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.noContent();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,109 @@
|
||||||
|
/*
|
||||||
|
* 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { schema } from '@kbn/config-schema';
|
||||||
|
|
||||||
|
import { boomify, isBoom } from '@hapi/boom';
|
||||||
|
import { CONTENT_ID, type LensSavedObject } from '../../../../common/content_management';
|
||||||
|
import {
|
||||||
|
PUBLIC_API_PATH,
|
||||||
|
PUBLIC_API_VERSION,
|
||||||
|
PUBLIC_API_CONTENT_MANAGEMENT_VERSION,
|
||||||
|
PUBLIC_API_ACCESS,
|
||||||
|
} from '../../constants';
|
||||||
|
import { lensSavedObjectSchema } from '../../../content_management/v1';
|
||||||
|
import { RegisterAPIRouteFn } from '../../types';
|
||||||
|
|
||||||
|
export const registerLensVisualizationsGetAPIRoute: RegisterAPIRouteFn = (
|
||||||
|
router,
|
||||||
|
{ contentManagement }
|
||||||
|
) => {
|
||||||
|
const getRoute = router.get({
|
||||||
|
path: `${PUBLIC_API_PATH}/visualizations/{id}`,
|
||||||
|
access: PUBLIC_API_ACCESS,
|
||||||
|
enableQueryVersion: true,
|
||||||
|
summary: 'Get Lens visualization',
|
||||||
|
description: 'Get a Lens visualization from id.',
|
||||||
|
options: {
|
||||||
|
tags: ['oas-tag:Lens'],
|
||||||
|
availability: {
|
||||||
|
stability: 'experimental',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
security: {
|
||||||
|
authz: {
|
||||||
|
enabled: false,
|
||||||
|
reason: 'Relies on Content Client for authorization',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
getRoute.addVersion(
|
||||||
|
{
|
||||||
|
version: PUBLIC_API_VERSION,
|
||||||
|
validate: {
|
||||||
|
request: {
|
||||||
|
params: schema.object({
|
||||||
|
id: schema.string({
|
||||||
|
meta: {
|
||||||
|
description: 'The saved object id of a Lens visualization.',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
response: {
|
||||||
|
200: {
|
||||||
|
body: () => lensSavedObjectSchema,
|
||||||
|
description: 'Ok',
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
description: 'Malformed request',
|
||||||
|
},
|
||||||
|
401: {
|
||||||
|
description: 'Unauthorized',
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
description: 'Forbidden',
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
description: 'Resource not found',
|
||||||
|
},
|
||||||
|
500: {
|
||||||
|
description: 'Internal Server Error',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async (ctx, req, res) => {
|
||||||
|
let result;
|
||||||
|
const client = contentManagement.contentClient
|
||||||
|
.getForRequest({ request: req, requestHandlerContext: ctx })
|
||||||
|
.for<LensSavedObject>(CONTENT_ID, PUBLIC_API_CONTENT_MANAGEMENT_VERSION);
|
||||||
|
|
||||||
|
try {
|
||||||
|
({ result } = await client.get(req.params.id));
|
||||||
|
} catch (error) {
|
||||||
|
if (isBoom(error)) {
|
||||||
|
if (error.output.statusCode === 404) {
|
||||||
|
return res.notFound({
|
||||||
|
body: {
|
||||||
|
message: `A Lens visualization with saved object id [${req.params.id}] was not found.`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (error.output.statusCode === 403) {
|
||||||
|
return res.forbidden();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return boomify(error); // forward unknown error
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.ok({ body: result.item });
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { RegisterAPIRoutesArgs } from '../../types';
|
||||||
|
import { registerLensVisualizationsCreateAPIRoute } from './create';
|
||||||
|
import { registerLensVisualizationsGetAPIRoute } from './get';
|
||||||
|
import { registerLensVisualizationsUpdateAPIRoute } from './update';
|
||||||
|
import { registerLensVisualizationsDeleteAPIRoute } from './delete';
|
||||||
|
import { registerLensVisualizationsSearchAPIRoute } from './search';
|
||||||
|
|
||||||
|
export function registerLensVisualizationsAPIRoutes({ http, ...rest }: RegisterAPIRoutesArgs) {
|
||||||
|
const { versioned: versionedRouter } = http.createRouter();
|
||||||
|
|
||||||
|
registerLensVisualizationsCreateAPIRoute(versionedRouter, rest);
|
||||||
|
registerLensVisualizationsGetAPIRoute(versionedRouter, rest);
|
||||||
|
registerLensVisualizationsUpdateAPIRoute(versionedRouter, rest);
|
||||||
|
registerLensVisualizationsDeleteAPIRoute(versionedRouter, rest);
|
||||||
|
registerLensVisualizationsSearchAPIRoute(versionedRouter, rest);
|
||||||
|
}
|
|
@ -0,0 +1,124 @@
|
||||||
|
/*
|
||||||
|
* 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { schema } from '@kbn/config-schema';
|
||||||
|
|
||||||
|
import { isBoom, boomify } from '@hapi/boom';
|
||||||
|
import { CONTENT_ID, type LensSavedObject } from '../../../../common/content_management';
|
||||||
|
import {
|
||||||
|
PUBLIC_API_PATH,
|
||||||
|
PUBLIC_API_VERSION,
|
||||||
|
PUBLIC_API_CONTENT_MANAGEMENT_VERSION,
|
||||||
|
PUBLIC_API_ACCESS,
|
||||||
|
} from '../../constants';
|
||||||
|
import { lensSavedObjectSchema } from '../../../content_management/v1';
|
||||||
|
import { RegisterAPIRouteFn } from '../../types';
|
||||||
|
|
||||||
|
export const registerLensVisualizationsSearchAPIRoute: RegisterAPIRouteFn = (
|
||||||
|
router,
|
||||||
|
{ contentManagement }
|
||||||
|
) => {
|
||||||
|
const searchRoute = router.get({
|
||||||
|
path: `${PUBLIC_API_PATH}/visualizations`,
|
||||||
|
access: PUBLIC_API_ACCESS,
|
||||||
|
enableQueryVersion: true,
|
||||||
|
summary: 'Search Lens visualizations',
|
||||||
|
description: 'Get list of Lens visualizations.',
|
||||||
|
options: {
|
||||||
|
tags: ['oas-tag:Lens'],
|
||||||
|
availability: {
|
||||||
|
stability: 'experimental',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
security: {
|
||||||
|
authz: {
|
||||||
|
enabled: false,
|
||||||
|
reason: 'Relies on Content Client for authorization',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
searchRoute.addVersion(
|
||||||
|
{
|
||||||
|
version: PUBLIC_API_VERSION,
|
||||||
|
validate: {
|
||||||
|
request: {
|
||||||
|
query: schema.object({
|
||||||
|
query: schema.maybe(
|
||||||
|
schema.string({
|
||||||
|
meta: {
|
||||||
|
description: 'The text to search for Lens visualizations',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
),
|
||||||
|
page: schema.number({
|
||||||
|
meta: {
|
||||||
|
description: 'Specifies the current page number of the paginated result.',
|
||||||
|
},
|
||||||
|
min: 1,
|
||||||
|
defaultValue: 1,
|
||||||
|
}),
|
||||||
|
perPage: schema.number({
|
||||||
|
meta: {
|
||||||
|
description: 'Maximum number of Lens visualizations included in a single response',
|
||||||
|
},
|
||||||
|
defaultValue: 20,
|
||||||
|
min: 1,
|
||||||
|
max: 1000,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
response: {
|
||||||
|
200: {
|
||||||
|
body: () => schema.arrayOf(lensSavedObjectSchema),
|
||||||
|
description: 'Ok',
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
description: 'Malformed request',
|
||||||
|
},
|
||||||
|
401: {
|
||||||
|
description: 'Unauthorized',
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
description: 'Forbidden',
|
||||||
|
},
|
||||||
|
500: {
|
||||||
|
description: 'Internal Server Error',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async (ctx, req, res) => {
|
||||||
|
let result;
|
||||||
|
const { query, page, perPage: limit } = req.query;
|
||||||
|
const client = contentManagement.contentClient
|
||||||
|
.getForRequest({ request: req, requestHandlerContext: ctx })
|
||||||
|
.for<LensSavedObject>(CONTENT_ID, PUBLIC_API_CONTENT_MANAGEMENT_VERSION);
|
||||||
|
|
||||||
|
try {
|
||||||
|
({ result } = await client.search(
|
||||||
|
{
|
||||||
|
text: query,
|
||||||
|
cursor: page.toString(),
|
||||||
|
limit,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
searchFields: ['title', 'description'],
|
||||||
|
}
|
||||||
|
));
|
||||||
|
} catch (error) {
|
||||||
|
if (isBoom(error) && error.output.statusCode === 403) {
|
||||||
|
return res.forbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
return boomify(error); // forward unknown error
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.ok({ body: result.hits });
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,118 @@
|
||||||
|
/*
|
||||||
|
* 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { schema } from '@kbn/config-schema';
|
||||||
|
import { boomify, isBoom } from '@hapi/boom';
|
||||||
|
|
||||||
|
import { CONTENT_ID, type LensSavedObject } from '../../../../common/content_management';
|
||||||
|
import {
|
||||||
|
PUBLIC_API_PATH,
|
||||||
|
PUBLIC_API_VERSION,
|
||||||
|
PUBLIC_API_CONTENT_MANAGEMENT_VERSION,
|
||||||
|
PUBLIC_API_ACCESS,
|
||||||
|
} from '../../constants';
|
||||||
|
import {
|
||||||
|
lensAttributesSchema,
|
||||||
|
lensCreateOptionsSchema,
|
||||||
|
lensSavedObjectSchema,
|
||||||
|
} from '../../../content_management/v1';
|
||||||
|
import { RegisterAPIRouteFn } from '../../types';
|
||||||
|
|
||||||
|
export const registerLensVisualizationsUpdateAPIRoute: RegisterAPIRouteFn = (
|
||||||
|
router,
|
||||||
|
{ contentManagement }
|
||||||
|
) => {
|
||||||
|
const updateRoute = router.put({
|
||||||
|
path: `${PUBLIC_API_PATH}/visualizations/{id}`,
|
||||||
|
access: PUBLIC_API_ACCESS,
|
||||||
|
enableQueryVersion: true,
|
||||||
|
summary: 'Update Lens visualization',
|
||||||
|
description: 'Update an existing Lens visualization.',
|
||||||
|
options: {
|
||||||
|
tags: ['oas-tag:Lens'],
|
||||||
|
availability: {
|
||||||
|
stability: 'experimental',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
security: {
|
||||||
|
authz: {
|
||||||
|
enabled: false,
|
||||||
|
reason: 'Relies on Content Client for authorization',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
updateRoute.addVersion(
|
||||||
|
{
|
||||||
|
version: PUBLIC_API_VERSION,
|
||||||
|
validate: {
|
||||||
|
request: {
|
||||||
|
params: schema.object({
|
||||||
|
id: schema.string({
|
||||||
|
meta: {
|
||||||
|
description: 'The saved object id of a Lens visualization.',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
body: schema.object({
|
||||||
|
options: lensCreateOptionsSchema,
|
||||||
|
data: lensAttributesSchema,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
response: {
|
||||||
|
200: {
|
||||||
|
body: () => lensSavedObjectSchema,
|
||||||
|
description: 'Ok',
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
description: 'Malformed request',
|
||||||
|
},
|
||||||
|
401: {
|
||||||
|
description: 'Unauthorized',
|
||||||
|
},
|
||||||
|
403: {
|
||||||
|
description: 'Forbidden',
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
description: 'Resource not found',
|
||||||
|
},
|
||||||
|
500: {
|
||||||
|
description: 'Internal Server Error',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async (ctx, req, res) => {
|
||||||
|
let result;
|
||||||
|
const { data, options } = req.body;
|
||||||
|
const client = contentManagement.contentClient
|
||||||
|
.getForRequest({ request: req, requestHandlerContext: ctx })
|
||||||
|
.for<LensSavedObject>(CONTENT_ID, PUBLIC_API_CONTENT_MANAGEMENT_VERSION);
|
||||||
|
|
||||||
|
try {
|
||||||
|
({ result } = await client.update(req.params.id, data, options));
|
||||||
|
} catch (error) {
|
||||||
|
if (isBoom(error)) {
|
||||||
|
if (error.output.statusCode === 404) {
|
||||||
|
return res.notFound({
|
||||||
|
body: {
|
||||||
|
message: `A Lens visualization with saved object id [${req.params.id}] was not found.`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (error.output.statusCode === 403) {
|
||||||
|
return res.forbidden();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return boomify(error); // forward unknown error
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.ok({ body: result.item });
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
21
x-pack/platform/plugins/shared/lens/server/api/types.ts
Normal file
21
x-pack/platform/plugins/shared/lens/server/api/types.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { HttpServiceSetup, Logger, RequestHandlerContext } from '@kbn/core/server';
|
||||||
|
import { ContentManagementServerSetup } from '@kbn/content-management-plugin/server';
|
||||||
|
import { VersionedRouter } from '@kbn/core-http-server';
|
||||||
|
|
||||||
|
export interface RegisterAPIRoutesArgs {
|
||||||
|
http: HttpServiceSetup;
|
||||||
|
contentManagement: ContentManagementServerSetup;
|
||||||
|
logger: Logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type RegisterAPIRouteFn = (
|
||||||
|
router: VersionedRouter<RequestHandlerContext>,
|
||||||
|
args: Omit<RegisterAPIRoutesArgs, 'http'>
|
||||||
|
) => void;
|
|
@ -136,6 +136,9 @@ export class LensStorage extends SOContentStorage<LensCrudTypes> {
|
||||||
// Save data in DB
|
// Save data in DB
|
||||||
const soClient = await savedObjectClientFromRequest(ctx);
|
const soClient = await savedObjectClientFromRequest(ctx);
|
||||||
|
|
||||||
|
// since we use create below this is to throw if SO id not found
|
||||||
|
await soClient.get(CONTENT_ID, id);
|
||||||
|
|
||||||
const savedObject = await soClient.create<LensSavedObjectAttributes>(CONTENT_ID, dataToLatest, {
|
const savedObject = await soClient.create<LensSavedObjectAttributes>(CONTENT_ID, dataToLatest, {
|
||||||
id,
|
id,
|
||||||
overwrite: true,
|
overwrite: true,
|
||||||
|
|
|
@ -25,7 +25,7 @@ const referenceSchema = schema.object(
|
||||||
|
|
||||||
const referencesSchema = schema.arrayOf(referenceSchema);
|
const referencesSchema = schema.arrayOf(referenceSchema);
|
||||||
|
|
||||||
const lensAttributesSchema = schema.object(
|
export const lensAttributesSchema = schema.object(
|
||||||
{
|
{
|
||||||
title: schema.string(),
|
title: schema.string(),
|
||||||
description: schema.maybe(schema.nullable(schema.string())),
|
description: schema.maybe(schema.nullable(schema.string())),
|
||||||
|
@ -38,7 +38,7 @@ const lensAttributesSchema = schema.object(
|
||||||
{ unknowns: 'forbid' }
|
{ unknowns: 'forbid' }
|
||||||
);
|
);
|
||||||
|
|
||||||
const lensSavedObjectSchema = schema.object(
|
export const lensSavedObjectSchema = schema.object(
|
||||||
{
|
{
|
||||||
id: schema.string(),
|
id: schema.string(),
|
||||||
type: schema.string(),
|
type: schema.string(),
|
||||||
|
@ -54,7 +54,7 @@ const lensSavedObjectSchema = schema.object(
|
||||||
{ unknowns: 'allow' }
|
{ unknowns: 'allow' }
|
||||||
);
|
);
|
||||||
|
|
||||||
const getResultSchema = schema.object(
|
const lensGetResultSchema = schema.object(
|
||||||
{
|
{
|
||||||
item: lensSavedObjectSchema,
|
item: lensSavedObjectSchema,
|
||||||
meta: schema.object(
|
meta: schema.object(
|
||||||
|
@ -78,55 +78,12 @@ const getResultSchema = schema.object(
|
||||||
{ unknowns: 'forbid' }
|
{ unknowns: 'forbid' }
|
||||||
);
|
);
|
||||||
|
|
||||||
const createOptionsSchema = schema.object({
|
export const lensCreateOptionsSchema = schema.object({
|
||||||
overwrite: schema.maybe(schema.boolean()),
|
overwrite: schema.maybe(schema.boolean()),
|
||||||
references: schema.maybe(referencesSchema),
|
references: schema.maybe(referencesSchema),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Content management service definition.
|
export const lensSearchOptionsSchema = schema.maybe(
|
||||||
// We need it for BWC support between different versions of the content
|
|
||||||
export const serviceDefinition: ServicesDefinition = {
|
|
||||||
get: {
|
|
||||||
out: {
|
|
||||||
result: {
|
|
||||||
schema: getResultSchema,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
create: {
|
|
||||||
in: {
|
|
||||||
options: {
|
|
||||||
schema: createOptionsSchema,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
schema: lensAttributesSchema,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
out: {
|
|
||||||
result: {
|
|
||||||
schema: schema.object(
|
|
||||||
{
|
|
||||||
item: lensSavedObjectSchema,
|
|
||||||
},
|
|
||||||
{ unknowns: 'forbid' }
|
|
||||||
),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
update: {
|
|
||||||
in: {
|
|
||||||
options: {
|
|
||||||
schema: createOptionsSchema, // same schema as "create"
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
schema: lensAttributesSchema,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
search: {
|
|
||||||
in: {
|
|
||||||
options: {
|
|
||||||
schema: schema.maybe(
|
|
||||||
schema.object(
|
schema.object(
|
||||||
{
|
{
|
||||||
searchFields: schema.maybe(schema.arrayOf(schema.string())),
|
searchFields: schema.maybe(schema.arrayOf(schema.string())),
|
||||||
|
@ -134,7 +91,59 @@ export const serviceDefinition: ServicesDefinition = {
|
||||||
},
|
},
|
||||||
{ unknowns: 'forbid' }
|
{ unknowns: 'forbid' }
|
||||||
)
|
)
|
||||||
),
|
);
|
||||||
|
|
||||||
|
const lensCreateResultSchema = schema.object(
|
||||||
|
{
|
||||||
|
item: lensSavedObjectSchema,
|
||||||
|
},
|
||||||
|
{ unknowns: 'forbid' }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Content management service definition.
|
||||||
|
// We need it for BWC support between different versions of the content
|
||||||
|
export const serviceDefinition: ServicesDefinition = {
|
||||||
|
get: {
|
||||||
|
out: {
|
||||||
|
result: {
|
||||||
|
schema: lensGetResultSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
in: {
|
||||||
|
data: {
|
||||||
|
schema: lensAttributesSchema,
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
schema: lensCreateOptionsSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
out: {
|
||||||
|
result: {
|
||||||
|
schema: lensCreateResultSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
in: {
|
||||||
|
data: {
|
||||||
|
schema: lensAttributesSchema,
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
schema: lensCreateOptionsSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
out: {
|
||||||
|
result: {
|
||||||
|
schema: lensCreateResultSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
search: {
|
||||||
|
in: {
|
||||||
|
options: {
|
||||||
|
schema: lensSearchOptionsSchema,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
/*
|
||||||
|
* 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export * from './cm_services';
|
|
@ -13,4 +13,6 @@ export const plugin = async (initContext: PluginInitializerContext) => {
|
||||||
return new LensServerPlugin(initContext);
|
return new LensServerPlugin(initContext);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export { PUBLIC_API_PATH, PUBLIC_API_VERSION } from './api/constants';
|
||||||
|
|
||||||
export type { LensDocShape715 } from './migrations/types';
|
export type { LensDocShape715 } from './migrations/types';
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
* 2.0.
|
* 2.0.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Plugin, CoreSetup, CoreStart, PluginInitializerContext } from '@kbn/core/server';
|
import { Plugin, CoreSetup, CoreStart, PluginInitializerContext, Logger } from '@kbn/core/server';
|
||||||
import { PluginStart as DataViewsServerPluginStart } from '@kbn/data-views-plugin/server';
|
import { PluginStart as DataViewsServerPluginStart } from '@kbn/data-views-plugin/server';
|
||||||
import {
|
import {
|
||||||
PluginStart as DataPluginStart,
|
PluginStart as DataPluginStart,
|
||||||
|
@ -30,6 +30,7 @@ import type { CustomVisualizationMigrations } from './migrations/types';
|
||||||
import { LensAppLocatorDefinition } from '../common/locator/locator';
|
import { LensAppLocatorDefinition } from '../common/locator/locator';
|
||||||
import { CONTENT_ID, LATEST_VERSION } from '../common/content_management';
|
import { CONTENT_ID, LATEST_VERSION } from '../common/content_management';
|
||||||
import { LensStorage } from './content_management';
|
import { LensStorage } from './content_management';
|
||||||
|
import { registerLensAPIRoutes } from './api/routes';
|
||||||
|
|
||||||
export interface PluginSetupContract {
|
export interface PluginSetupContract {
|
||||||
taskManager?: TaskManagerSetupContract;
|
taskManager?: TaskManagerSetupContract;
|
||||||
|
@ -65,8 +66,11 @@ export class LensServerPlugin
|
||||||
implements Plugin<LensServerPluginSetup, {}, PluginSetupContract, PluginStartContract>
|
implements Plugin<LensServerPluginSetup, {}, PluginSetupContract, PluginStartContract>
|
||||||
{
|
{
|
||||||
private customVisualizationMigrations: CustomVisualizationMigrations = {};
|
private customVisualizationMigrations: CustomVisualizationMigrations = {};
|
||||||
|
private readonly logger: Logger;
|
||||||
|
|
||||||
constructor(private initializerContext: PluginInitializerContext) {}
|
constructor(private initializerContext: PluginInitializerContext) {
|
||||||
|
this.logger = initializerContext.logger.get();
|
||||||
|
}
|
||||||
|
|
||||||
setup(core: CoreSetup<PluginStartContract>, plugins: PluginSetupContract) {
|
setup(core: CoreSetup<PluginStartContract>, plugins: PluginSetupContract) {
|
||||||
const getFilterMigrations = plugins.data.query.filterManager.getAllMigrations.bind(
|
const getFilterMigrations = plugins.data.query.filterManager.getAllMigrations.bind(
|
||||||
|
@ -96,6 +100,13 @@ export class LensServerPlugin
|
||||||
this.customVisualizationMigrations
|
this.customVisualizationMigrations
|
||||||
);
|
);
|
||||||
plugins.embeddable.registerEmbeddableFactory(lensEmbeddableFactory());
|
plugins.embeddable.registerEmbeddableFactory(lensEmbeddableFactory());
|
||||||
|
|
||||||
|
registerLensAPIRoutes({
|
||||||
|
http: core.http,
|
||||||
|
contentManagement: plugins.contentManagement,
|
||||||
|
logger: this.logger,
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
lensEmbeddableFactory,
|
lensEmbeddableFactory,
|
||||||
registerVisualizationMigration: (
|
registerVisualizationMigration: (
|
||||||
|
|
|
@ -124,6 +124,7 @@
|
||||||
"@kbn/fields-metadata-plugin",
|
"@kbn/fields-metadata-plugin",
|
||||||
"@kbn/alerts-ui-shared",
|
"@kbn/alerts-ui-shared",
|
||||||
"@kbn/deeplinks-analytics",
|
"@kbn/deeplinks-analytics",
|
||||||
|
"@kbn/core-http-server",
|
||||||
],
|
],
|
||||||
"exclude": ["target/**/*"]
|
"exclude": ["target/**/*"]
|
||||||
}
|
}
|
||||||
|
|
17
x-pack/platform/test/api_integration/apis/lens/config.ts
Normal file
17
x-pack/platform/test/api_integration/apis/lens/config.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
/*
|
||||||
|
* 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { FtrConfigProviderContext } from '@kbn/test';
|
||||||
|
|
||||||
|
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
||||||
|
const baseIntegrationTestsConfig = await readConfigFile(require.resolve('../../config.ts'));
|
||||||
|
|
||||||
|
return {
|
||||||
|
...baseIntegrationTestsConfig.getAll(),
|
||||||
|
testFiles: [require.resolve('.')],
|
||||||
|
};
|
||||||
|
}
|
74
x-pack/platform/test/api_integration/apis/lens/examples.ts
Normal file
74
x-pack/platform/test/api_integration/apis/lens/examples.ts
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
* 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const getExampleLensBody = (title = `Lens vis - ${Date.now()} - ${Math.random()}`) => ({
|
||||||
|
data: {
|
||||||
|
title,
|
||||||
|
description: '',
|
||||||
|
visualizationType: 'lnsMetric',
|
||||||
|
state: {
|
||||||
|
visualization: {
|
||||||
|
layerId: '32e889c6-89f9-4873-b1f7-d5bea381c582',
|
||||||
|
layerType: 'data',
|
||||||
|
metricAccessor: '1c6729bc-ec92-4000-8dcc-0fdd7b56d5b8',
|
||||||
|
secondaryTrend: {
|
||||||
|
type: 'none',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
query: {
|
||||||
|
query: '',
|
||||||
|
language: 'kuery',
|
||||||
|
},
|
||||||
|
filters: [],
|
||||||
|
datasourceStates: {
|
||||||
|
formBased: {
|
||||||
|
layers: {
|
||||||
|
'32e889c6-89f9-4873-b1f7-d5bea381c582': {
|
||||||
|
columns: {
|
||||||
|
'1c6729bc-ec92-4000-8dcc-0fdd7b56d5b8': {
|
||||||
|
label: 'Count of records',
|
||||||
|
dataType: 'number',
|
||||||
|
operationType: 'count',
|
||||||
|
isBucketed: false,
|
||||||
|
scale: 'ratio',
|
||||||
|
sourceField: '___records___',
|
||||||
|
params: {
|
||||||
|
emptyAsNull: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
columnOrder: ['1c6729bc-ec92-4000-8dcc-0fdd7b56d5b8'],
|
||||||
|
incompleteColumns: {
|
||||||
|
'd0b92889-f74c-4194-b738-76eb5d268524': {
|
||||||
|
operationType: 'date_histogram',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
sampling: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
indexpattern: {
|
||||||
|
layers: {},
|
||||||
|
},
|
||||||
|
textBased: {
|
||||||
|
layers: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
internalReferences: [],
|
||||||
|
adHocDataViews: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
references: [
|
||||||
|
{
|
||||||
|
type: 'index-pattern',
|
||||||
|
id: '91200a00-9efd-11e7-acb3-3dab96693fab',
|
||||||
|
name: 'indexpattern-datasource-layer-32e889c6-89f9-4873-b1f7-d5bea381c582',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
14
x-pack/platform/test/api_integration/apis/lens/index.ts
Normal file
14
x-pack/platform/test/api_integration/apis/lens/index.ts
Normal file
|
@ -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; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||||
|
|
||||||
|
export default function ({ loadTestFile }: FtrProviderContext) {
|
||||||
|
describe('lens', () => {
|
||||||
|
loadTestFile(require.resolve('./visualizations'));
|
||||||
|
});
|
||||||
|
}
|
|
@ -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; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { FtrProviderContext } from '../../../../ftr_provider_context';
|
||||||
|
|
||||||
|
export default function ({ getService, loadTestFile }: FtrProviderContext) {
|
||||||
|
const kibanaServer = getService('kibanaServer');
|
||||||
|
describe('visualizations - create', () => {
|
||||||
|
before(async () => {
|
||||||
|
await kibanaServer.importExport.load(
|
||||||
|
'src/platform/test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
after(async () => {
|
||||||
|
await kibanaServer.savedObjects.cleanStandardList();
|
||||||
|
await kibanaServer.importExport.unload(
|
||||||
|
'src/platform/test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
loadTestFile(require.resolve('./main'));
|
||||||
|
loadTestFile(require.resolve('./validation'));
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import expect from '@kbn/expect';
|
||||||
|
import { PUBLIC_API_PATH, PUBLIC_API_VERSION } from '@kbn/lens-plugin/server';
|
||||||
|
import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';
|
||||||
|
|
||||||
|
import type { FtrProviderContext } from '../../../../ftr_provider_context';
|
||||||
|
import { getExampleLensBody } from '../../examples';
|
||||||
|
|
||||||
|
export default function ({ getService }: FtrProviderContext) {
|
||||||
|
const supertest = getService('supertest');
|
||||||
|
|
||||||
|
describe('main', () => {
|
||||||
|
it('should create a lens visualization', async () => {
|
||||||
|
const response = await supertest
|
||||||
|
.post(`${PUBLIC_API_PATH}/visualizations`)
|
||||||
|
.set('kbn-xsrf', 'true')
|
||||||
|
.set(ELASTIC_HTTP_VERSION_HEADER, PUBLIC_API_VERSION)
|
||||||
|
.send(getExampleLensBody());
|
||||||
|
|
||||||
|
expect(response.status).to.be(201);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import expect from '@kbn/expect';
|
||||||
|
import { PUBLIC_API_PATH, PUBLIC_API_VERSION } from '@kbn/lens-plugin/server';
|
||||||
|
import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';
|
||||||
|
|
||||||
|
import type { FtrProviderContext } from '../../../../ftr_provider_context';
|
||||||
|
|
||||||
|
export default function ({ getService }: FtrProviderContext) {
|
||||||
|
const supertest = getService('supertest');
|
||||||
|
describe('validation', () => {
|
||||||
|
it('should return error if body is empty', async () => {
|
||||||
|
const response = await supertest
|
||||||
|
.post(`${PUBLIC_API_PATH}/visualizations`)
|
||||||
|
.set('kbn-xsrf', 'true')
|
||||||
|
.set(ELASTIC_HTTP_VERSION_HEADER, PUBLIC_API_VERSION)
|
||||||
|
.send({});
|
||||||
|
|
||||||
|
expect(response.status).to.be(400);
|
||||||
|
expect(response.body.message).to.be(
|
||||||
|
'[request body.data.title]: expected value of type [string] but got [undefined]'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { FtrProviderContext } from '../../../../ftr_provider_context';
|
||||||
|
|
||||||
|
export default function ({ getService, loadTestFile }: FtrProviderContext) {
|
||||||
|
const kibanaServer = getService('kibanaServer');
|
||||||
|
describe('visualizations - create', () => {
|
||||||
|
before(async () => {
|
||||||
|
await kibanaServer.importExport.load(
|
||||||
|
'src/platform/test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json'
|
||||||
|
);
|
||||||
|
await kibanaServer.importExport.load(
|
||||||
|
'x-pack/test/api_integration/fixtures/kbn_archiver/lens/example_docs.json'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
after(async () => {
|
||||||
|
await kibanaServer.savedObjects.cleanStandardList();
|
||||||
|
await kibanaServer.importExport.unload(
|
||||||
|
'src/platform/test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json'
|
||||||
|
);
|
||||||
|
await kibanaServer.importExport.unload(
|
||||||
|
'x-pack/test/api_integration/fixtures/kbn_archiver/lens/example_docs.json'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
loadTestFile(require.resolve('./main'));
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import expect from '@kbn/expect';
|
||||||
|
import { PUBLIC_API_PATH, PUBLIC_API_VERSION } from '@kbn/lens-plugin/server';
|
||||||
|
import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';
|
||||||
|
|
||||||
|
import type { FtrProviderContext } from '../../../../ftr_provider_context';
|
||||||
|
|
||||||
|
export default function ({ getService }: FtrProviderContext) {
|
||||||
|
const supertest = getService('supertest');
|
||||||
|
|
||||||
|
describe('main', () => {
|
||||||
|
it('should delete a lens visualization', async () => {
|
||||||
|
const id = '71c9c185-3e6d-49d0-b7e5-f966eaf51625'; // known id
|
||||||
|
const response = await supertest
|
||||||
|
.delete(`${PUBLIC_API_PATH}/visualizations/${id}`)
|
||||||
|
.set('kbn-xsrf', 'true')
|
||||||
|
.set(ELASTIC_HTTP_VERSION_HEADER, PUBLIC_API_VERSION)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
expect(response.status).to.be(204);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should error when deleting an unknown lens visualization', async () => {
|
||||||
|
const id = '123'; // unknown id
|
||||||
|
const response = await supertest
|
||||||
|
.delete(`${PUBLIC_API_PATH}/visualizations/${id}`)
|
||||||
|
.set('kbn-xsrf', 'true')
|
||||||
|
.set(ELASTIC_HTTP_VERSION_HEADER, PUBLIC_API_VERSION)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
expect(response.status).to.be(404);
|
||||||
|
expect(response.body.message).to.be(
|
||||||
|
'A Lens visualization with saved object id [123] was not found.'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { FtrProviderContext } from '../../../../ftr_provider_context';
|
||||||
|
|
||||||
|
export default function ({ getService, loadTestFile }: FtrProviderContext) {
|
||||||
|
const kibanaServer = getService('kibanaServer');
|
||||||
|
describe('visualizations - create', () => {
|
||||||
|
before(async () => {
|
||||||
|
await kibanaServer.importExport.load(
|
||||||
|
'src/platform/test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json'
|
||||||
|
);
|
||||||
|
await kibanaServer.importExport.load(
|
||||||
|
'x-pack/test/api_integration/fixtures/kbn_archiver/lens/example_docs.json'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
after(async () => {
|
||||||
|
await kibanaServer.savedObjects.cleanStandardList();
|
||||||
|
await kibanaServer.importExport.unload(
|
||||||
|
'src/platform/test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json'
|
||||||
|
);
|
||||||
|
await kibanaServer.importExport.unload(
|
||||||
|
'x-pack/test/api_integration/fixtures/kbn_archiver/lens/example_docs.json'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
loadTestFile(require.resolve('./main'));
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import expect from '@kbn/expect';
|
||||||
|
import { PUBLIC_API_PATH, PUBLIC_API_VERSION } from '@kbn/lens-plugin/server';
|
||||||
|
import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';
|
||||||
|
|
||||||
|
import type { FtrProviderContext } from '../../../../ftr_provider_context';
|
||||||
|
|
||||||
|
export default function ({ getService }: FtrProviderContext) {
|
||||||
|
const supertest = getService('supertest');
|
||||||
|
|
||||||
|
describe('main', () => {
|
||||||
|
it('should get a lens visualization', async () => {
|
||||||
|
const id = '71c9c185-3e6d-49d0-b7e5-f966eaf51625'; // known id
|
||||||
|
const response = await supertest
|
||||||
|
.get(`${PUBLIC_API_PATH}/visualizations/${id}`)
|
||||||
|
.set(ELASTIC_HTTP_VERSION_HEADER, PUBLIC_API_VERSION)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
expect(response.status).to.be(200);
|
||||||
|
expect(response.body.attributes.title).to.be('Lens example - 1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should error when fetching an unknown lens visualization', async () => {
|
||||||
|
const id = '123'; // unknown id
|
||||||
|
const response = await supertest
|
||||||
|
.get(`${PUBLIC_API_PATH}/visualizations/${id}`)
|
||||||
|
.set(ELASTIC_HTTP_VERSION_HEADER, PUBLIC_API_VERSION)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
expect(response.status).to.be(404);
|
||||||
|
expect(response.body.message).to.be(
|
||||||
|
'A Lens visualization with saved object id [123] was not found.'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
* 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { FtrProviderContext } from '../../../../functional/ftr_provider_context';
|
||||||
|
|
||||||
|
export default function ({ loadTestFile }: FtrProviderContext) {
|
||||||
|
describe('visualizations', () => {
|
||||||
|
loadTestFile(require.resolve('./create'));
|
||||||
|
loadTestFile(require.resolve('./get'));
|
||||||
|
loadTestFile(require.resolve('./update'));
|
||||||
|
loadTestFile(require.resolve('./delete'));
|
||||||
|
loadTestFile(require.resolve('./search'));
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { FtrProviderContext } from '../../../../ftr_provider_context';
|
||||||
|
|
||||||
|
export default function ({ getService, loadTestFile }: FtrProviderContext) {
|
||||||
|
const kibanaServer = getService('kibanaServer');
|
||||||
|
describe('visualizations - update', () => {
|
||||||
|
before(async () => {
|
||||||
|
await kibanaServer.importExport.load(
|
||||||
|
'src/platform/test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json'
|
||||||
|
);
|
||||||
|
await kibanaServer.importExport.load(
|
||||||
|
'x-pack/test/api_integration/fixtures/kbn_archiver/lens/example_docs.json'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
after(async () => {
|
||||||
|
await kibanaServer.savedObjects.cleanStandardList();
|
||||||
|
await kibanaServer.importExport.unload(
|
||||||
|
'src/platform/test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json'
|
||||||
|
);
|
||||||
|
await kibanaServer.importExport.unload(
|
||||||
|
'x-pack/test/api_integration/fixtures/kbn_archiver/lens/example_docs.json'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
loadTestFile(require.resolve('./main'));
|
||||||
|
loadTestFile(require.resolve('./validation'));
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import expect from '@kbn/expect';
|
||||||
|
import { PUBLIC_API_PATH, PUBLIC_API_VERSION } from '@kbn/lens-plugin/server';
|
||||||
|
import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';
|
||||||
|
|
||||||
|
import type { FtrProviderContext } from '../../../../ftr_provider_context';
|
||||||
|
|
||||||
|
export default function ({ getService }: FtrProviderContext) {
|
||||||
|
const supertest = getService('supertest');
|
||||||
|
|
||||||
|
describe('main', () => {
|
||||||
|
it('should get list of lens visualizations', async () => {
|
||||||
|
const response = await supertest
|
||||||
|
.get(`${PUBLIC_API_PATH}/visualizations`)
|
||||||
|
.set(ELASTIC_HTTP_VERSION_HEADER, PUBLIC_API_VERSION)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
expect(response.status).to.be(200);
|
||||||
|
expect(response.body.length).to.be(4);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should filter lens visualizations by title and description', async () => {
|
||||||
|
const response = await supertest
|
||||||
|
.get(`${PUBLIC_API_PATH}/visualizations`)
|
||||||
|
.query({ query: '1' })
|
||||||
|
.set(ELASTIC_HTTP_VERSION_HEADER, PUBLIC_API_VERSION)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
expect(response.status).to.be(200);
|
||||||
|
expect(response.body.length).to.be(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import expect from '@kbn/expect';
|
||||||
|
import { PUBLIC_API_PATH, PUBLIC_API_VERSION } from '@kbn/lens-plugin/server';
|
||||||
|
import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';
|
||||||
|
|
||||||
|
import type { FtrProviderContext } from '../../../../ftr_provider_context';
|
||||||
|
|
||||||
|
export default function ({ getService }: FtrProviderContext) {
|
||||||
|
const supertest = getService('supertest');
|
||||||
|
describe('validation', () => {
|
||||||
|
it('should return error if using unknown params', async () => {
|
||||||
|
const response = await supertest
|
||||||
|
.get(`${PUBLIC_API_PATH}/visualizations`)
|
||||||
|
.query({ xyz: 'unknown param' })
|
||||||
|
.set(ELASTIC_HTTP_VERSION_HEADER, PUBLIC_API_VERSION)
|
||||||
|
.send({});
|
||||||
|
|
||||||
|
expect(response.status).to.be(400);
|
||||||
|
expect(response.body.message).to.be(
|
||||||
|
'[request query.xyz]: definition for this key is missing'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { FtrProviderContext } from '../../../../ftr_provider_context';
|
||||||
|
|
||||||
|
export default function ({ getService, loadTestFile }: FtrProviderContext) {
|
||||||
|
const kibanaServer = getService('kibanaServer');
|
||||||
|
describe('visualizations - update', () => {
|
||||||
|
before(async () => {
|
||||||
|
await kibanaServer.importExport.load(
|
||||||
|
'src/platform/test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json'
|
||||||
|
);
|
||||||
|
await kibanaServer.importExport.load(
|
||||||
|
'x-pack/test/api_integration/fixtures/kbn_archiver/lens/example_docs.json'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
after(async () => {
|
||||||
|
await kibanaServer.savedObjects.cleanStandardList();
|
||||||
|
await kibanaServer.importExport.unload(
|
||||||
|
'src/platform/test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json'
|
||||||
|
);
|
||||||
|
await kibanaServer.importExport.unload(
|
||||||
|
'x-pack/test/api_integration/fixtures/kbn_archiver/lens/example_docs.json'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
loadTestFile(require.resolve('./main'));
|
||||||
|
loadTestFile(require.resolve('./validation'));
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import expect from '@kbn/expect';
|
||||||
|
import { PUBLIC_API_PATH, PUBLIC_API_VERSION } from '@kbn/lens-plugin/server';
|
||||||
|
import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';
|
||||||
|
|
||||||
|
import type { FtrProviderContext } from '../../../../ftr_provider_context';
|
||||||
|
import { getExampleLensBody } from '../../examples';
|
||||||
|
|
||||||
|
export default function ({ getService }: FtrProviderContext) {
|
||||||
|
const supertest = getService('supertest');
|
||||||
|
|
||||||
|
describe('main', () => {
|
||||||
|
it('should update a lens visualization', async () => {
|
||||||
|
const id = '71c9c185-3e6d-49d0-b7e5-f966eaf51625'; // known id
|
||||||
|
const response = await supertest
|
||||||
|
.put(`${PUBLIC_API_PATH}/visualizations/${id}`)
|
||||||
|
.set('kbn-xsrf', 'true')
|
||||||
|
.set(ELASTIC_HTTP_VERSION_HEADER, PUBLIC_API_VERSION)
|
||||||
|
.send(getExampleLensBody('Custom title'));
|
||||||
|
|
||||||
|
expect(response.status).to.be(200);
|
||||||
|
expect(response.body.attributes.title).to.be('Custom title');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should error when updating an unknown lens visualization', async () => {
|
||||||
|
const id = '123'; // unknown id
|
||||||
|
const response = await supertest
|
||||||
|
.put(`${PUBLIC_API_PATH}/visualizations/${id}`)
|
||||||
|
.set('kbn-xsrf', 'true')
|
||||||
|
.set(ELASTIC_HTTP_VERSION_HEADER, PUBLIC_API_VERSION)
|
||||||
|
.send(getExampleLensBody('Custom title'));
|
||||||
|
|
||||||
|
expect(response.status).to.be(404);
|
||||||
|
expect(response.body.message).to.be(
|
||||||
|
'A Lens visualization with saved object id [123] was not found.'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import expect from '@kbn/expect';
|
||||||
|
import { PUBLIC_API_PATH, PUBLIC_API_VERSION } from '@kbn/lens-plugin/server';
|
||||||
|
import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';
|
||||||
|
|
||||||
|
import type { FtrProviderContext } from '../../../../ftr_provider_context';
|
||||||
|
|
||||||
|
export default function ({ getService }: FtrProviderContext) {
|
||||||
|
const supertest = getService('supertest');
|
||||||
|
describe('validation', () => {
|
||||||
|
it('should return error if body is empty', async () => {
|
||||||
|
const id = '71c9c185-3e6d-49d0-b7e5-f966eaf51625'; // known id
|
||||||
|
const response = await supertest
|
||||||
|
.put(`${PUBLIC_API_PATH}/visualizations/${id}`)
|
||||||
|
.set('kbn-xsrf', 'true')
|
||||||
|
.set(ELASTIC_HTTP_VERSION_HEADER, PUBLIC_API_VERSION)
|
||||||
|
.send({});
|
||||||
|
|
||||||
|
expect(response.status).to.be(400);
|
||||||
|
expect(response.body.message).to.be(
|
||||||
|
'[request body.data.title]: expected value of type [string] but got [undefined]'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -105,5 +105,6 @@
|
||||||
"@kbn/repo-info",
|
"@kbn/repo-info",
|
||||||
"@kbn/dev-cli-errors",
|
"@kbn/dev-cli-errors",
|
||||||
"@kbn/journeys",
|
"@kbn/journeys",
|
||||||
|
"@kbn/lens-plugin",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,315 @@
|
||||||
|
{
|
||||||
|
"id": "71c9c185-3e6d-49d0-b7e5-f966eaf51625",
|
||||||
|
"type": "lens",
|
||||||
|
"namespaces": [
|
||||||
|
"default"
|
||||||
|
],
|
||||||
|
"updated_at": "2025-06-17T15:47:13.313Z",
|
||||||
|
"created_at": "2025-06-17T15:47:13.313Z",
|
||||||
|
"version": "WzU5LDFd",
|
||||||
|
"attributes": {
|
||||||
|
"title": "Lens example - 1",
|
||||||
|
"description": "",
|
||||||
|
"visualizationType": "lnsMetric",
|
||||||
|
"state": {
|
||||||
|
"visualization": {
|
||||||
|
"layerId": "7aa8fd7f-f664-48fe-8232-3a26054f9cdc",
|
||||||
|
"layerType": "data",
|
||||||
|
"metricAccessor": "89a69d8d-a6bc-47a8-80c6-94272762e785",
|
||||||
|
"secondaryTrend": {
|
||||||
|
"type": "none"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"query": {
|
||||||
|
"query": "",
|
||||||
|
"language": "kuery"
|
||||||
|
},
|
||||||
|
"filters": [],
|
||||||
|
"datasourceStates": {
|
||||||
|
"formBased": {
|
||||||
|
"layers": {
|
||||||
|
"7aa8fd7f-f664-48fe-8232-3a26054f9cdc": {
|
||||||
|
"columns": {
|
||||||
|
"89a69d8d-a6bc-47a8-80c6-94272762e785": {
|
||||||
|
"label": "Count of records",
|
||||||
|
"dataType": "number",
|
||||||
|
"operationType": "count",
|
||||||
|
"isBucketed": false,
|
||||||
|
"scale": "ratio",
|
||||||
|
"sourceField": "___records___",
|
||||||
|
"params": {
|
||||||
|
"emptyAsNull": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"columnOrder": [
|
||||||
|
"89a69d8d-a6bc-47a8-80c6-94272762e785"
|
||||||
|
],
|
||||||
|
"incompleteColumns": {
|
||||||
|
"806819b9-b606-4383-9337-e6a40b8602ad": {
|
||||||
|
"operationType": "date_histogram"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sampling": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexpattern": {
|
||||||
|
"layers": {}
|
||||||
|
},
|
||||||
|
"textBased": {
|
||||||
|
"layers": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"internalReferences": [],
|
||||||
|
"adHocDataViews": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"type": "index-pattern",
|
||||||
|
"id": "91200a00-9efd-11e7-acb3-3dab96693fab",
|
||||||
|
"name": "indexpattern-datasource-layer-7aa8fd7f-f664-48fe-8232-3a26054f9cdc"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"managed": false,
|
||||||
|
"coreMigrationVersion": "8.8.0",
|
||||||
|
"typeMigrationVersion": "8.9.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": "4fad4960-5be9-408c-854b-3b53ac80df81",
|
||||||
|
"type": "lens",
|
||||||
|
"namespaces": [
|
||||||
|
"default"
|
||||||
|
],
|
||||||
|
"updated_at": "2025-06-17T15:48:59.917Z",
|
||||||
|
"created_at": "2025-06-17T15:48:59.917Z",
|
||||||
|
"version": "WzYyLDFd",
|
||||||
|
"attributes": {
|
||||||
|
"title": "Lens example - 2",
|
||||||
|
"description": "",
|
||||||
|
"visualizationType": "lnsMetric",
|
||||||
|
"state": {
|
||||||
|
"visualization": {
|
||||||
|
"layerId": "7aa8fd7f-f664-48fe-8232-3a26054f9cdc",
|
||||||
|
"layerType": "data",
|
||||||
|
"metricAccessor": "89a69d8d-a6bc-47a8-80c6-94272762e785",
|
||||||
|
"secondaryTrend": {
|
||||||
|
"type": "none"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"query": {
|
||||||
|
"query": "",
|
||||||
|
"language": "kuery"
|
||||||
|
},
|
||||||
|
"filters": [],
|
||||||
|
"datasourceStates": {
|
||||||
|
"formBased": {
|
||||||
|
"layers": {
|
||||||
|
"7aa8fd7f-f664-48fe-8232-3a26054f9cdc": {
|
||||||
|
"columns": {
|
||||||
|
"89a69d8d-a6bc-47a8-80c6-94272762e785": {
|
||||||
|
"label": "Count of records",
|
||||||
|
"dataType": "number",
|
||||||
|
"operationType": "count",
|
||||||
|
"isBucketed": false,
|
||||||
|
"scale": "ratio",
|
||||||
|
"sourceField": "___records___",
|
||||||
|
"params": {
|
||||||
|
"emptyAsNull": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"columnOrder": [
|
||||||
|
"89a69d8d-a6bc-47a8-80c6-94272762e785"
|
||||||
|
],
|
||||||
|
"incompleteColumns": {
|
||||||
|
"806819b9-b606-4383-9337-e6a40b8602ad": {
|
||||||
|
"operationType": "date_histogram"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sampling": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexpattern": {
|
||||||
|
"layers": {}
|
||||||
|
},
|
||||||
|
"textBased": {
|
||||||
|
"layers": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"internalReferences": [],
|
||||||
|
"adHocDataViews": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"type": "index-pattern",
|
||||||
|
"id": "91200a00-9efd-11e7-acb3-3dab96693fab",
|
||||||
|
"name": "indexpattern-datasource-layer-7aa8fd7f-f664-48fe-8232-3a26054f9cdc"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"managed": false,
|
||||||
|
"coreMigrationVersion": "8.8.0",
|
||||||
|
"typeMigrationVersion": "8.9.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": "af239293-933a-4f35-969f-9dcccfefa8e4",
|
||||||
|
"type": "lens",
|
||||||
|
"namespaces": [
|
||||||
|
"default"
|
||||||
|
],
|
||||||
|
"updated_at": "2025-06-17T15:50:00.097Z",
|
||||||
|
"created_at": "2025-06-17T15:50:00.097Z",
|
||||||
|
"version": "WzY0LDFd",
|
||||||
|
"attributes": {
|
||||||
|
"title": "Lens example - 3",
|
||||||
|
"description": "Some description - 1",
|
||||||
|
"visualizationType": "lnsMetric",
|
||||||
|
"state": {
|
||||||
|
"visualization": {
|
||||||
|
"layerId": "7aa8fd7f-f664-48fe-8232-3a26054f9cdc",
|
||||||
|
"layerType": "data",
|
||||||
|
"metricAccessor": "89a69d8d-a6bc-47a8-80c6-94272762e785",
|
||||||
|
"secondaryTrend": {
|
||||||
|
"type": "none"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"query": {
|
||||||
|
"query": "",
|
||||||
|
"language": "kuery"
|
||||||
|
},
|
||||||
|
"filters": [],
|
||||||
|
"datasourceStates": {
|
||||||
|
"formBased": {
|
||||||
|
"layers": {
|
||||||
|
"7aa8fd7f-f664-48fe-8232-3a26054f9cdc": {
|
||||||
|
"columns": {
|
||||||
|
"89a69d8d-a6bc-47a8-80c6-94272762e785": {
|
||||||
|
"label": "Count of records",
|
||||||
|
"dataType": "number",
|
||||||
|
"operationType": "count",
|
||||||
|
"isBucketed": false,
|
||||||
|
"scale": "ratio",
|
||||||
|
"sourceField": "___records___",
|
||||||
|
"params": {
|
||||||
|
"emptyAsNull": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"columnOrder": [
|
||||||
|
"89a69d8d-a6bc-47a8-80c6-94272762e785"
|
||||||
|
],
|
||||||
|
"incompleteColumns": {
|
||||||
|
"806819b9-b606-4383-9337-e6a40b8602ad": {
|
||||||
|
"operationType": "date_histogram"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sampling": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexpattern": {
|
||||||
|
"layers": {}
|
||||||
|
},
|
||||||
|
"textBased": {
|
||||||
|
"layers": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"internalReferences": [],
|
||||||
|
"adHocDataViews": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"type": "index-pattern",
|
||||||
|
"id": "91200a00-9efd-11e7-acb3-3dab96693fab",
|
||||||
|
"name": "indexpattern-datasource-layer-7aa8fd7f-f664-48fe-8232-3a26054f9cdc"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"managed": false,
|
||||||
|
"coreMigrationVersion": "8.8.0",
|
||||||
|
"typeMigrationVersion": "8.9.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": "f708d303-2418-4313-aa28-c2830f7cf4cd",
|
||||||
|
"type": "lens",
|
||||||
|
"namespaces": [
|
||||||
|
"default"
|
||||||
|
],
|
||||||
|
"updated_at": "2025-06-17T15:50:05.680Z",
|
||||||
|
"created_at": "2025-06-17T15:50:05.680Z",
|
||||||
|
"version": "WzY2LDFd",
|
||||||
|
"attributes": {
|
||||||
|
"title": "Lens example - 4",
|
||||||
|
"description": "",
|
||||||
|
"visualizationType": "lnsMetric",
|
||||||
|
"state": {
|
||||||
|
"visualization": {
|
||||||
|
"layerId": "7aa8fd7f-f664-48fe-8232-3a26054f9cdc",
|
||||||
|
"layerType": "data",
|
||||||
|
"metricAccessor": "89a69d8d-a6bc-47a8-80c6-94272762e785",
|
||||||
|
"secondaryTrend": {
|
||||||
|
"type": "none"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"query": {
|
||||||
|
"query": "",
|
||||||
|
"language": "kuery"
|
||||||
|
},
|
||||||
|
"filters": [],
|
||||||
|
"datasourceStates": {
|
||||||
|
"formBased": {
|
||||||
|
"layers": {
|
||||||
|
"7aa8fd7f-f664-48fe-8232-3a26054f9cdc": {
|
||||||
|
"columns": {
|
||||||
|
"89a69d8d-a6bc-47a8-80c6-94272762e785": {
|
||||||
|
"label": "Count of records",
|
||||||
|
"dataType": "number",
|
||||||
|
"operationType": "count",
|
||||||
|
"isBucketed": false,
|
||||||
|
"scale": "ratio",
|
||||||
|
"sourceField": "___records___",
|
||||||
|
"params": {
|
||||||
|
"emptyAsNull": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"columnOrder": [
|
||||||
|
"89a69d8d-a6bc-47a8-80c6-94272762e785"
|
||||||
|
],
|
||||||
|
"incompleteColumns": {
|
||||||
|
"806819b9-b606-4383-9337-e6a40b8602ad": {
|
||||||
|
"operationType": "date_histogram"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sampling": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexpattern": {
|
||||||
|
"layers": {}
|
||||||
|
},
|
||||||
|
"textBased": {
|
||||||
|
"layers": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"internalReferences": [],
|
||||||
|
"adHocDataViews": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"type": "index-pattern",
|
||||||
|
"id": "91200a00-9efd-11e7-acb3-3dab96693fab",
|
||||||
|
"name": "indexpattern-datasource-layer-7aa8fd7f-f664-48fe-8232-3a26054f9cdc"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"managed": false,
|
||||||
|
"coreMigrationVersion": "8.8.0",
|
||||||
|
"typeMigrationVersion": "8.9.0"
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue