kibana/x-pack/platform/plugins/shared/lens/server/plugin.tsx
Nick Partridge 17c2556fc6
[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>
2025-06-25 20:01:35 +01:00

129 lines
4.6 KiB
TypeScript

/*
* 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 { Plugin, CoreSetup, CoreStart, PluginInitializerContext, Logger } from '@kbn/core/server';
import { PluginStart as DataViewsServerPluginStart } from '@kbn/data-views-plugin/server';
import {
PluginStart as DataPluginStart,
PluginSetup as DataPluginSetup,
} from '@kbn/data-plugin/server';
import { ExpressionsServerSetup } from '@kbn/expressions-plugin/server';
import { FieldFormatsStart } from '@kbn/field-formats-plugin/server';
import type { MigrateFunctionsObject } from '@kbn/kibana-utils-plugin/common';
import { ContentManagementServerSetup } from '@kbn/content-management-plugin/server';
import {
TaskManagerSetupContract,
TaskManagerStartContract,
} from '@kbn/task-manager-plugin/server';
import { EmbeddableSetup } from '@kbn/embeddable-plugin/server';
import { DataViewPersistableStateService } from '@kbn/data-views-plugin/common';
import { SharePluginSetup } from '@kbn/share-plugin/server';
import { setupSavedObjects } from './saved_objects';
import { setupExpressions } from './expressions';
import { makeLensEmbeddableFactory } from './embeddable/make_lens_embeddable_factory';
import type { CustomVisualizationMigrations } from './migrations/types';
import { LensAppLocatorDefinition } from '../common/locator/locator';
import { CONTENT_ID, LATEST_VERSION } from '../common/content_management';
import { LensStorage } from './content_management';
import { registerLensAPIRoutes } from './api/routes';
export interface PluginSetupContract {
taskManager?: TaskManagerSetupContract;
embeddable: EmbeddableSetup;
expressions: ExpressionsServerSetup;
data: DataPluginSetup;
share?: SharePluginSetup;
contentManagement: ContentManagementServerSetup;
}
export interface PluginStartContract {
taskManager?: TaskManagerStartContract;
fieldFormats: FieldFormatsStart;
data: DataPluginStart;
dataViews: DataViewsServerPluginStart;
}
export interface LensServerPluginSetup {
/**
* Server side embeddable definition which provides migrations to run if Lens state is embedded into another saved object somewhere
*/
lensEmbeddableFactory: ReturnType<typeof makeLensEmbeddableFactory>;
/**
* Register custom migration functions for custom third party Lens visualizations
*/
registerVisualizationMigration: (
id: string,
migrationsGetter: () => MigrateFunctionsObject
) => void;
}
export class LensServerPlugin
implements Plugin<LensServerPluginSetup, {}, PluginSetupContract, PluginStartContract>
{
private customVisualizationMigrations: CustomVisualizationMigrations = {};
private readonly logger: Logger;
constructor(private initializerContext: PluginInitializerContext) {
this.logger = initializerContext.logger.get();
}
setup(core: CoreSetup<PluginStartContract>, plugins: PluginSetupContract) {
const getFilterMigrations = plugins.data.query.filterManager.getAllMigrations.bind(
plugins.data.query.filterManager
);
setupSavedObjects(core, getFilterMigrations, this.customVisualizationMigrations);
setupExpressions(core, plugins.expressions);
if (plugins.share) {
plugins.share.url.locators.create(new LensAppLocatorDefinition());
}
plugins.contentManagement.register({
id: CONTENT_ID,
storage: new LensStorage({
throwOnResultValidationError: this.initializerContext.env.mode.dev,
logger: this.initializerContext.logger.get('storage'),
}),
version: {
latest: LATEST_VERSION,
},
});
const lensEmbeddableFactory = makeLensEmbeddableFactory(
getFilterMigrations,
DataViewPersistableStateService.getAllMigrations.bind(DataViewPersistableStateService),
this.customVisualizationMigrations
);
plugins.embeddable.registerEmbeddableFactory(lensEmbeddableFactory());
registerLensAPIRoutes({
http: core.http,
contentManagement: plugins.contentManagement,
logger: this.logger,
});
return {
lensEmbeddableFactory,
registerVisualizationMigration: (
id: string,
migrationsGetter: () => MigrateFunctionsObject
) => {
if (this.customVisualizationMigrations[id]) {
throw new Error(`Migrations object for visualization ${id} registered already`);
}
this.customVisualizationMigrations[id] = migrationsGetter;
},
};
}
start(core: CoreStart, plugins: PluginStartContract) {
return {};
}
stop() {}
}