Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[Component templates] Server side #66596

Merged
merged 9 commits into from
Jun 1, 2020
63 changes: 63 additions & 0 deletions x-pack/plugins/index_management/server/client/elasticsearch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

export const elasticsearchJsPlugin = (Client: any, config: any, components: any) => {
const ca = components.clientAction.factory;

Client.prototype.dataManagement = components.clientAction.namespaceFactory();
const dataManagement = Client.prototype.dataManagement.prototype;

dataManagement.getComponentTemplates = ca({
urls: [
{
fmt: '/_component_template',
},
],
method: 'GET',
});

dataManagement.getComponentTemplate = ca({
urls: [
{
fmt: '/_component_template/<%=name%>',
req: {
name: {
type: 'string',
},
},
},
],
method: 'GET',
});

dataManagement.saveComponentTemplate = ca({
urls: [
{
fmt: '/_component_template/<%=name%>',
req: {
name: {
type: 'string',
},
},
},
],
method: 'PUT',
});

dataManagement.deleteComponentTemplate = ca({
urls: [
{
fmt: '/_component_template/<%=name%>',
req: {
name: {
type: 'string',
},
},
},
],
method: 'DELETE',
});
};
49 changes: 46 additions & 3 deletions x-pack/plugins/index_management/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,52 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

declare module 'kibana/server' {
interface RequestHandlerContext {
dataManagement?: DataManagementContext;
}
}

import { i18n } from '@kbn/i18n';
import { CoreSetup, Plugin, Logger, PluginInitializerContext } from 'src/core/server';
import {
CoreSetup,
Plugin,
Logger,
PluginInitializerContext,
IScopedClusterClient,
ICustomClusterClient,
} from 'src/core/server';

import { PLUGIN } from '../common';
import { Dependencies } from './types';
import { ApiRoutes } from './routes';
import { License, IndexDataEnricher } from './services';
import { isEsError } from './lib/is_es_error';
import { elasticsearchJsPlugin } from './client/elasticsearch';

export interface DataManagementContext {
client: IScopedClusterClient;
}

export interface IndexManagementPluginSetup {
indexDataEnricher: {
add: IndexDataEnricher['add'];
};
}

async function getCustomEsClient(getStartServices: CoreSetup['getStartServices']) {
const [core] = await getStartServices();
const esClientConfig = { plugins: [elasticsearchJsPlugin] };
return core.elasticsearch.legacy.createClient('dataManagement', esClientConfig);
}

export class IndexMgmtServerPlugin implements Plugin<IndexManagementPluginSetup, void, any, any> {
private readonly apiRoutes: ApiRoutes;
private readonly license: License;
private readonly logger: Logger;
private readonly indexDataEnricher: IndexDataEnricher;
private dataManagementESClient?: ICustomClusterClient;

constructor(initContext: PluginInitializerContext) {
this.logger = initContext.logger.get();
Expand All @@ -31,7 +57,10 @@ export class IndexMgmtServerPlugin implements Plugin<IndexManagementPluginSetup,
this.indexDataEnricher = new IndexDataEnricher();
}

setup({ http }: CoreSetup, { licensing }: Dependencies): IndexManagementPluginSetup {
setup(
{ http, getStartServices }: CoreSetup,
{ licensing }: Dependencies
): IndexManagementPluginSetup {
const router = http.createRouter();

this.license.setup(
Expand All @@ -48,6 +77,15 @@ export class IndexMgmtServerPlugin implements Plugin<IndexManagementPluginSetup,
}
);

http.registerRouteHandlerContext('dataManagement', async (ctx, request) => {
this.dataManagementESClient =
this.dataManagementESClient ?? (await getCustomEsClient(getStartServices));

return {
client: this.dataManagementESClient.asScoped(request),
};
});

this.apiRoutes.setup({
router,
license: this.license,
Expand All @@ -65,5 +103,10 @@ export class IndexMgmtServerPlugin implements Plugin<IndexManagementPluginSetup,
}

start() {}
stop() {}

stop() {
if (this.dataManagementESClient) {
this.dataManagementESClient.close();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
import { schema } from '@kbn/config-schema';

import { RouteDependencies } from '../../../types';
import { addBasePath } from '../index';
import { componentTemplateSchema } from './schema_validation';

const bodySchema = schema.object({
name: schema.string(),
...componentTemplateSchema,
});

export const registerCreateRoute = ({
router,
license,
lib: { isEsError },
}: RouteDependencies): void => {
router.post(
{
path: addBasePath('/component_templates'),
validate: {
body: bodySchema,
},
},
license.guardApiRoute(async (ctx, req, res) => {
const { callAsCurrentUser } = ctx.dataManagement!.client;

const { name, ...componentTemplateDefinition } = req.body;

try {
// Check that a component template with the same name doesn't already exist
const componentTemplateResponse = await callAsCurrentUser(
'dataManagement.getComponentTemplate',
{ name }
);

const { component_templates: componentTemplates } = componentTemplateResponse;

if (componentTemplates.length) {
return res.conflict({
body: new Error(
i18n.translate('xpack.idxMgmt.componentTemplates.createRoute.duplicateErrorMessage', {
defaultMessage: "There is already a component template with name '{name}'.",
values: {
name,
},
})
),
});
}
} catch (e) {
// Silently swallow error
}

try {
const response = await callAsCurrentUser('dataManagement.saveComponentTemplate', {
name,
body: componentTemplateDefinition,
});

return res.ok({ body: response });
} catch (error) {
if (isEsError(error)) {
return res.customError({
statusCode: error.statusCode,
body: error,
});
}

return res.internalError({ body: error });
}
})
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { schema } from '@kbn/config-schema';

import { RouteDependencies } from '../../../types';
import { addBasePath } from '../index';

const paramsSchema = schema.object({
names: schema.string(),
});

export const registerDeleteRoute = ({ router, license }: RouteDependencies): void => {
router.delete(
{
path: addBasePath('/component_templates/{names}'),
validate: {
params: paramsSchema,
},
},
license.guardApiRoute(async (ctx, req, res) => {
const { callAsCurrentUser } = ctx.dataManagement!.client;
const { names } = req.params;
const componentNames = names.split(',');

const response: { itemsDeleted: string[]; errors: any[] } = {
itemsDeleted: [],
errors: [],
};

await Promise.all(
componentNames.map((componentName) => {
return callAsCurrentUser('dataManagement.deleteComponentTemplate', {
name: componentName,
})
.then(() => response.itemsDeleted.push(componentName))
.catch((e) =>
response.errors.push({
name: componentName,
error: e,
})
);
})
);

return res.ok({ body: response });
})
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { schema } from '@kbn/config-schema';

import { RouteDependencies } from '../../../types';
import { addBasePath } from '../index';

const paramsSchema = schema.object({
name: schema.string(),
});

export function registerGetAllRoute({ router, license, lib: { isEsError } }: RouteDependencies) {
// Get all component templates
router.get(
{ path: addBasePath('/component_templates'), validate: false },
license.guardApiRoute(async (ctx, req, res) => {
const { callAsCurrentUser } = ctx.dataManagement!.client;

try {
const response = await callAsCurrentUser('dataManagement.getComponentTemplates');

return res.ok({ body: response.component_templates });
} catch (error) {
if (isEsError(error)) {
return res.customError({
statusCode: error.statusCode,
body: error,
});
}

return res.internalError({ body: error });
}
})
);

// Get single component template
router.get(
{
path: addBasePath('/component_templates/{name}'),
validate: {
params: paramsSchema,
},
},
license.guardApiRoute(async (ctx, req, res) => {
const { callAsCurrentUser } = ctx.dataManagement!.client;
const { name } = req.params;

try {
const { component_templates: componentTemplates } = await callAsCurrentUser(
'dataManagement.getComponentTemplates',
{
name,
}
);

return res.ok({
body: {
...componentTemplates[0],
name,
},
});
} catch (error) {
if (isEsError(error)) {
return res.customError({
statusCode: error.statusCode,
body: error,
});
}

return res.internalError({ body: error });
}
})
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { RouteDependencies } from '../../../types';

import { registerGetAllRoute } from './get';
import { registerCreateRoute } from './create';
import { registerUpdateRoute } from './update';
import { registerDeleteRoute } from './delete';

export function registerComponentTemplateRoutes(dependencies: RouteDependencies) {
registerGetAllRoute(dependencies);
registerCreateRoute(dependencies);
registerUpdateRoute(dependencies);
registerDeleteRoute(dependencies);
}
Loading