Skip to content

Commit

Permalink
[Component templates] Add APIs (#66684)
Browse files Browse the repository at this point in the history
  • Loading branch information
alisonelizabeth committed May 27, 2020
1 parent 4b4f04b commit 5baef1d
Show file tree
Hide file tree
Showing 15 changed files with 729 additions and 7 deletions.
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
Expand Up @@ -3,3 +3,5 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

export { registerComponentTemplateRoutes } from './register_component_template_routes';
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

0 comments on commit 5baef1d

Please sign in to comment.