[Index Management] Support rollover of datastreams from component templates linked to managed/packaged index templates (#187733)

This commit is contained in:
Ignacio Rivas 2024-07-23 15:45:28 +02:00 committed by GitHub
parent 9bcefe356f
commit dc3d9600da
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 248 additions and 25 deletions

View file

@ -49,3 +49,11 @@ export interface ComponentTemplateListItem {
export interface ComponentTemplateDatastreams { export interface ComponentTemplateDatastreams {
data_streams: string[]; data_streams: string[];
} }
export interface ComponentTemplateMeta {
managed: boolean;
managed_by: string;
package: {
name: string;
};
}

View file

@ -78,7 +78,6 @@ describe('<ComponentTemplateEdit />', () => {
template: { template: {
settings: { number_of_shards: 1 }, settings: { number_of_shards: 1 },
}, },
_kbnMeta: { usedBy: [], isManaged: false },
}; };
beforeEach(async () => { beforeEach(async () => {
@ -166,27 +165,30 @@ describe('<ComponentTemplateEdit />', () => {
}), }),
}) })
); );
// Mapping rollout modal should not be opened if the component template is not managed by Fleet // Mapping rollout modal should not be opened if the component template is not managed
expect(coreStart.overlays.openModal).not.toBeCalled(); expect(coreStart.overlays.openModal).not.toBeCalled();
}); });
}); });
describe('managed by fleet', () => { describe('can rollover linked datastreams', () => {
const DATASTREAM_NAME = 'logs-test-default'; const DATASTREAM_NAME = 'logs-test-default';
const CUSTOM_COMPONENT_TEMPLATE = 'comp-1@custom';
const ENCODED_CUSTOM_COMPONENT_TEMPLATE = encodeURIComponent(CUSTOM_COMPONENT_TEMPLATE);
beforeEach(async () => { beforeEach(async () => {
httpRequestsMockHelpers.setLoadComponentTemplateResponse( httpRequestsMockHelpers.setLoadComponentTemplateResponse(
COMPONENT_TEMPLATE_TO_EDIT.name, ENCODED_CUSTOM_COMPONENT_TEMPLATE,
Object.assign({}, COMPONENT_TEMPLATE_TO_EDIT, { Object.assign({}, COMPONENT_TEMPLATE_TO_EDIT, {
_meta: { managed_by: 'fleet' }, name: CUSTOM_COMPONENT_TEMPLATE,
}) })
); );
httpRequestsMockHelpers.setGetComponentTemplateDatastream(COMPONENT_TEMPLATE_TO_EDIT.name, { httpRequestsMockHelpers.setGetComponentTemplateDatastream(ENCODED_CUSTOM_COMPONENT_TEMPLATE, {
data_streams: [DATASTREAM_NAME], data_streams: [DATASTREAM_NAME],
}); });
await act(async () => { await act(async () => {
testBed = await setup(httpSetup); testBed = await setup(httpSetup, '@custom');
}); });
testBed.component.update(); testBed.component.update();
@ -221,7 +223,7 @@ describe('<ComponentTemplateEdit />', () => {
component.update(); component.update();
expect(httpSetup.put).toHaveBeenLastCalledWith( expect(httpSetup.put).toHaveBeenLastCalledWith(
`${API_BASE_PATH}/component_templates/${COMPONENT_TEMPLATE_TO_EDIT.name}`, `${API_BASE_PATH}/component_templates/${ENCODED_CUSTOM_COMPONENT_TEMPLATE}`,
expect.anything() expect.anything()
); );
expect(httpSetup.post).toHaveBeenLastCalledWith( expect(httpSetup.post).toHaveBeenLastCalledWith(
@ -259,7 +261,7 @@ describe('<ComponentTemplateEdit />', () => {
component.update(); component.update();
expect(httpSetup.put).toHaveBeenLastCalledWith( expect(httpSetup.put).toHaveBeenLastCalledWith(
`${API_BASE_PATH}/component_templates/${COMPONENT_TEMPLATE_TO_EDIT.name}`, `${API_BASE_PATH}/component_templates/${ENCODED_CUSTOM_COMPONENT_TEMPLATE}`,
expect.anything() expect.anything()
); );
expect(httpSetup.post).toHaveBeenLastCalledWith( expect(httpSetup.post).toHaveBeenLastCalledWith(
@ -269,5 +271,76 @@ describe('<ComponentTemplateEdit />', () => {
expect(coreStart.overlays.openModal).not.toBeCalled(); expect(coreStart.overlays.openModal).not.toBeCalled();
}); });
it('should show mappings rollover modal on save if referenced index template is managed and packaged', async () => {
httpRequestsMockHelpers.setLoadComponentTemplateResponse(
COMPONENT_TEMPLATE_TO_EDIT.name,
Object.assign({}, COMPONENT_TEMPLATE_TO_EDIT, {
_meta: {},
})
);
httpRequestsMockHelpers.setGetComponentTemplateDatastream(COMPONENT_TEMPLATE_TO_EDIT.name, {
data_streams: [DATASTREAM_NAME],
});
httpRequestsMockHelpers.setLoadReferencedIndexTemplateMetaResponse(
COMPONENT_TEMPLATE_TO_EDIT.name,
{
package: {
name: 'security',
},
managed_by: 'security',
managed: true,
}
);
await act(async () => {
testBed = await setup(httpSetup);
});
testBed.component.update();
httpRequestsMockHelpers.setPostDatastreamMappingsFromTemplate(
DATASTREAM_NAME,
{},
{ message: 'Bad request', statusCode: 400 }
);
const { exists, actions, component, form, coreStart } = testBed;
await act(async () => {
form.setInputValue('versionField.input', '1');
});
await act(async () => {
actions.clickNextButton();
});
component.update();
await actions.completeStepSettings();
await actions.completeStepMappings();
await actions.completeStepAliases();
// Make sure the list of affected mappings is shown
expect(exists('affectedMappingsList')).toBe(true);
await act(async () => {
actions.clickNextButton();
});
component.update();
expect(httpSetup.put).toHaveBeenLastCalledWith(
`${API_BASE_PATH}/component_templates/${COMPONENT_TEMPLATE_TO_EDIT.name}`,
expect.anything()
);
expect(httpSetup.post).toHaveBeenLastCalledWith(
`${API_BASE_PATH}/data_streams/${DATASTREAM_NAME}/mappings_from_template`,
expect.anything()
);
expect(coreStart.overlays.openModal).toBeCalled();
});
}); });
}); });

View file

@ -189,4 +189,5 @@ export type ComponentTemplateFormTestSubjects =
| 'aliasesEditor' | 'aliasesEditor'
| 'mappingsEditor' | 'mappingsEditor'
| 'settingsEditor' | 'settingsEditor'
| 'affectedMappingsList'
| 'versionField.input'; | 'versionField.input';

View file

@ -76,6 +76,18 @@ const registerHttpRequestMockHelpers = (
error?: ResponseError error?: ResponseError
) => mockResponse('GET', `${API_BASE_PATH}/component_templates/${templateId}`, response, error); ) => mockResponse('GET', `${API_BASE_PATH}/component_templates/${templateId}`, response, error);
const setLoadReferencedIndexTemplateMetaResponse = (
templateId: string,
response?: HttpResponse,
error?: ResponseError
) =>
mockResponse(
'GET',
`${API_BASE_PATH}/component_templates/${templateId}/referenced_index_template_meta`,
response,
error
);
const setDeleteComponentTemplateResponse = ( const setDeleteComponentTemplateResponse = (
templateId: string, templateId: string,
response?: HttpResponse, response?: HttpResponse,
@ -100,6 +112,7 @@ const registerHttpRequestMockHelpers = (
return { return {
setLoadComponentTemplatesResponse, setLoadComponentTemplatesResponse,
setLoadReferencedIndexTemplateMetaResponse,
setDeleteComponentTemplateResponse, setDeleteComponentTemplateResponse,
setLoadComponentTemplateResponse, setLoadComponentTemplateResponse,
setCreateComponentTemplateResponse, setCreateComponentTemplateResponse,

View file

@ -17,7 +17,6 @@ import { useComponentTemplatesContext } from '../../component_templates_context'
import { ComponentTemplateForm } from '../component_template_form'; import { ComponentTemplateForm } from '../component_template_form';
import { useStepFromQueryString } from '../use_step_from_query_string'; import { useStepFromQueryString } from '../use_step_from_query_string';
import { useDatastreamsRollover } from '../component_template_datastreams_rollover/use_datastreams_rollover'; import { useDatastreamsRollover } from '../component_template_datastreams_rollover/use_datastreams_rollover';
import { MANAGED_BY_FLEET } from '../../constants';
interface Props { interface Props {
/** /**
@ -33,13 +32,38 @@ export const ComponentTemplateCreate: React.FunctionComponent<RouteComponentProp
const [isSaving, setIsSaving] = useState<boolean>(false); const [isSaving, setIsSaving] = useState<boolean>(false);
const [saveError, setSaveError] = useState<any>(null); const [saveError, setSaveError] = useState<any>(null);
const redirectTo = useRedirectPath(history); const redirectTo = useRedirectPath(history);
const [currentStep, setCurrentStep] = useState<string>('logistics');
const [componentName, setComponentName] = useState<string | undefined>();
const [canRollover, setCanRollover] = useState<boolean>(false);
const { api } = useComponentTemplatesContext();
const { activeStep: defaultActiveStep, updateStep } = useStepFromQueryString(history); const { activeStep: defaultActiveStep, updateStep } = useStepFromQueryString(history);
const locationSearchParams = useMemo(() => { const locationSearchParams = useMemo(() => {
return new URLSearchParams(history.location.search); return new URLSearchParams(history.location.search);
}, [history.location.search]); }, [history.location.search]);
// Effect for computing if we should allow the user to rollover attached datastreams
useEffect(() => {
async function computeCanRollover() {
// When the current step is not logistics, we have an available component template
// name that we can use to query the referenced index template.
if (currentStep !== 'logistics') {
// If the component template is referenced by an index template that is part of
// a package and is managed we can allow the user to roll it over if possible.
const { data: refIndexTemplate } = await api.getReferencedIndexTemplateMeta(
componentName as string
);
setCanRollover(Boolean(refIndexTemplate?.managed_by && refIndexTemplate?.package));
}
setCanRollover(false);
}
computeCanRollover();
}, [api, currentStep, componentName, setCanRollover]);
const defaultValue = useMemo(() => { const defaultValue = useMemo(() => {
if (sourceComponentTemplate) { if (sourceComponentTemplate) {
return sourceComponentTemplate; return sourceComponentTemplate;
@ -59,7 +83,6 @@ export const ComponentTemplateCreate: React.FunctionComponent<RouteComponentProp
}; };
}, [locationSearchParams, sourceComponentTemplate]); }, [locationSearchParams, sourceComponentTemplate]);
const { api } = useComponentTemplatesContext();
const { showDatastreamRolloverModal } = useDatastreamsRollover(); const { showDatastreamRolloverModal } = useDatastreamsRollover();
const onSave = async (componentTemplate: ComponentTemplateDeserialized) => { const onSave = async (componentTemplate: ComponentTemplateDeserialized) => {
@ -76,7 +99,11 @@ export const ComponentTemplateCreate: React.FunctionComponent<RouteComponentProp
setSaveError(error); setSaveError(error);
return; return;
} }
if (componentTemplate._meta?.managed_by === MANAGED_BY_FLEET) {
// We only want to allow rolling over linked datastreams for either @custom templates
// or when the component template is referenced by an index template that is part of
// a package and is managed.
if (componentTemplate.name.endsWith('@custom') || canRollover) {
await showDatastreamRolloverModal(componentTemplate.name); await showDatastreamRolloverModal(componentTemplate.name);
} }
@ -121,11 +148,15 @@ export const ComponentTemplateCreate: React.FunctionComponent<RouteComponentProp
<ComponentTemplateForm <ComponentTemplateForm
defaultActiveWizardSection={defaultActiveStep} defaultActiveWizardSection={defaultActiveStep}
onStepChange={updateStep} onStepChange={(step) => {
setCurrentStep(step);
updateStep(step);
}}
defaultValue={defaultValue} defaultValue={defaultValue}
onSave={onSave} onSave={onSave}
isSaving={isSaving} isSaving={isSaving}
saveError={saveError} saveError={saveError}
setComponentName={setComponentName}
clearSaveError={clearSaveError} clearSaveError={clearSaveError}
/> />
</EuiPageSection> </EuiPageSection>

View file

@ -21,7 +21,6 @@ import {
} from '../../shared_imports'; } from '../../shared_imports';
import { ComponentTemplateForm } from '../component_template_form'; import { ComponentTemplateForm } from '../component_template_form';
import { useRedirectPath } from '../../../../hooks/redirect_path'; import { useRedirectPath } from '../../../../hooks/redirect_path';
import { MANAGED_BY_FLEET } from '../../constants';
import { useStepFromQueryString } from '../use_step_from_query_string'; import { useStepFromQueryString } from '../use_step_from_query_string';
import { useDatastreamsRollover } from '../component_template_datastreams_rollover/use_datastreams_rollover'; import { useDatastreamsRollover } from '../component_template_datastreams_rollover/use_datastreams_rollover';
@ -48,6 +47,13 @@ export const ComponentTemplateEdit: React.FunctionComponent<RouteComponentProps<
const { error, data: componentTemplate, isLoading } = api.useLoadComponentTemplate(decodedName); const { error, data: componentTemplate, isLoading } = api.useLoadComponentTemplate(decodedName);
const { data: dataStreamResponse } = api.useLoadComponentTemplatesDatastream(decodedName); const { data: dataStreamResponse } = api.useLoadComponentTemplatesDatastream(decodedName);
const dataStreams = useMemo(() => dataStreamResponse?.data_streams ?? [], [dataStreamResponse]); const dataStreams = useMemo(() => dataStreamResponse?.data_streams ?? [], [dataStreamResponse]);
// If the component template is referenced by an index template that is part of
// a package and is managed we can allow the user to roll it over if possible.
const { data: refIndexTemplate } = api.useLoadReferencedIndexTemplateMeta(decodedName);
const canRollover = useMemo(
() => Boolean(refIndexTemplate?.managed_by && refIndexTemplate?.package),
[refIndexTemplate]
);
const { showDatastreamRolloverModal } = useDatastreamsRollover(); const { showDatastreamRolloverModal } = useDatastreamsRollover();
@ -68,9 +74,13 @@ export const ComponentTemplateEdit: React.FunctionComponent<RouteComponentProps<
return; return;
} }
if (updatedComponentTemplate._meta?.managed_by === MANAGED_BY_FLEET) { // We only want to allow rolling over linked datastreams for either @custom templates
// or when the component template is referenced by an index template that is part of
// a package and is managed.
if (updatedComponentTemplate.name.endsWith('@custom') || canRollover) {
await showDatastreamRolloverModal(updatedComponentTemplate.name); await showDatastreamRolloverModal(updatedComponentTemplate.name);
} }
redirectTo({ redirectTo({
pathname: encodeURI( pathname: encodeURI(
`/component_templates/${encodeURIComponent(updatedComponentTemplate.name)}` `/component_templates/${encodeURIComponent(updatedComponentTemplate.name)}`
@ -150,6 +160,7 @@ export const ComponentTemplateEdit: React.FunctionComponent<RouteComponentProps<
<ComponentTemplateForm <ComponentTemplateForm
defaultValue={componentTemplate!} defaultValue={componentTemplate!}
dataStreams={dataStreams} dataStreams={dataStreams}
canRollover={canRollover}
defaultActiveWizardSection={defaultActiveStep} defaultActiveWizardSection={defaultActiveStep}
onStepChange={updateStep} onStepChange={updateStep}
onSave={onSave} onSave={onSave}

View file

@ -38,6 +38,7 @@ export type WizardSection = keyof WizardContent | 'review';
interface Props { interface Props {
onSave: (componentTemplate: ComponentTemplateDeserialized) => void; onSave: (componentTemplate: ComponentTemplateDeserialized) => void;
clearSaveError: () => void; clearSaveError: () => void;
setComponentName?: (name: string) => void;
isSaving: boolean; isSaving: boolean;
saveError: any; saveError: any;
defaultValue?: ComponentTemplateDeserialized; defaultValue?: ComponentTemplateDeserialized;
@ -45,6 +46,7 @@ interface Props {
defaultActiveWizardSection?: WizardSection; defaultActiveWizardSection?: WizardSection;
onStepChange?: (stepId: string) => void; onStepChange?: (stepId: string) => void;
dataStreams?: string[]; dataStreams?: string[];
canRollover?: boolean;
} }
const wizardSections: { [id: string]: { id: WizardSection; label: string } } = { const wizardSections: { [id: string]: { id: WizardSection; label: string } } = {
@ -90,7 +92,9 @@ export const ComponentTemplateForm = ({
isManaged: false, isManaged: false,
}, },
}, },
setComponentName,
dataStreams, dataStreams,
canRollover,
isEditing, isEditing,
isSaving, isSaving,
saveError, saveError,
@ -237,6 +241,16 @@ export const ComponentTemplateForm = ({
texts={i18nTexts} texts={i18nTexts}
defaultActiveStep={defaultActiveStepIndex} defaultActiveStep={defaultActiveStepIndex}
onStepChange={onStepChange} onStepChange={onStepChange}
onChange={(attrs) => {
// Let the parent component know the name of the component template in the
// form has changed, so that it can re-compute the canRollover prop.
// This is needed for determinating if the user should see a rollover
// attached datastreams modal or not.
const data = attrs.getData();
if (setComponentName) {
setComponentName(data.logistics.name);
}
}}
> >
<FormWizardStep <FormWizardStep
id={wizardSections.logistics.id} id={wizardSections.logistics.id}
@ -262,6 +276,7 @@ export const ComponentTemplateForm = ({
<StepReviewContainer <StepReviewContainer
getComponentTemplateData={buildComponentTemplateObject(defaultValue)} getComponentTemplateData={buildComponentTemplateObject(defaultValue)}
dataStreams={dataStreams} dataStreams={dataStreams}
canRollover={canRollover}
/> />
</FormWizardStep> </FormWizardStep>
</FormWizard> </FormWizard>

View file

@ -27,7 +27,6 @@ import {
serializers, serializers,
serializeComponentTemplate, serializeComponentTemplate,
} from '../../../shared_imports'; } from '../../../shared_imports';
import { MANAGED_BY_FLEET } from '../../../constants';
import { getLifecycleValue } from '../../../../../lib/data_streams'; import { getLifecycleValue } from '../../../../../lib/data_streams';
const INFINITE_AS_ICON = true; const INFINITE_AS_ICON = true;
@ -52,10 +51,11 @@ const getDescriptionText = (data: any) => {
interface Props { interface Props {
componentTemplate: ComponentTemplateDeserialized; componentTemplate: ComponentTemplateDeserialized;
dataStreams?: string[]; dataStreams?: string[];
canRollover?: boolean;
} }
export const StepReview: React.FunctionComponent<Props> = React.memo( export const StepReview: React.FunctionComponent<Props> = React.memo(
({ dataStreams, componentTemplate }) => { ({ dataStreams, canRollover, componentTemplate }) => {
const { name } = componentTemplate; const { name } = componentTemplate;
const serializedComponentTemplate = serializeComponentTemplate( const serializedComponentTemplate = serializeComponentTemplate(
@ -70,8 +70,8 @@ export const StepReview: React.FunctionComponent<Props> = React.memo(
version: serializedVersion, version: serializedVersion,
} = serializedComponentTemplate; } = serializedComponentTemplate;
const isFleetDatastreamsVisible = const areDatastreamsVisible =
Boolean(dataStreams?.length) && componentTemplate._meta?.managed_by === MANAGED_BY_FLEET; Boolean(dataStreams?.length) && (componentTemplate.name.endsWith('@custom') || canRollover);
const SummaryTab = () => ( const SummaryTab = () => (
<div data-test-subj="summaryTab"> <div data-test-subj="summaryTab">
@ -138,8 +138,8 @@ export const StepReview: React.FunctionComponent<Props> = React.memo(
</EuiDescriptionListDescription> </EuiDescriptionListDescription>
</EuiDescriptionList> </EuiDescriptionList>
</EuiFlexItem> </EuiFlexItem>
{isFleetDatastreamsVisible && dataStreams && ( {areDatastreamsVisible && dataStreams && (
<EuiFlexItem> <EuiFlexItem data-test-subj="affectedMappingsList">
{/* Datastream mappings */} {/* Datastream mappings */}
<FormattedMessage <FormattedMessage
id="xpack.idxMgmt.templateForm.stepReview.summaryTab.datastreamsLabel" id="xpack.idxMgmt.templateForm.stepReview.summaryTab.datastreamsLabel"
@ -207,7 +207,7 @@ export const StepReview: React.FunctionComponent<Props> = React.memo(
{request} {request}
</EuiCodeBlock> </EuiCodeBlock>
{isFleetDatastreamsVisible && ( {areDatastreamsVisible && (
<> <>
<EuiSpacer size="m" /> <EuiSpacer size="m" />
<EuiText> <EuiText>

View file

@ -14,16 +14,23 @@ import { StepReview } from './step_review';
interface Props { interface Props {
getComponentTemplateData: (wizardContent: WizardContent) => ComponentTemplateDeserialized; getComponentTemplateData: (wizardContent: WizardContent) => ComponentTemplateDeserialized;
dataStreams?: string[]; dataStreams?: string[];
canRollover?: boolean;
} }
export const StepReviewContainer = React.memo( export const StepReviewContainer = React.memo(
({ getComponentTemplateData, dataStreams }: Props) => { ({ getComponentTemplateData, dataStreams, canRollover }: Props) => {
const { getData } = Forms.useMultiContentContext<WizardContent>(); const { getData } = Forms.useMultiContentContext<WizardContent>();
const wizardContent = getData(); const wizardContent = getData();
// Build the final template object, providing the wizard content data // Build the final template object, providing the wizard content data
const componentTemplate = getComponentTemplateData(wizardContent); const componentTemplate = getComponentTemplateData(wizardContent);
return <StepReview dataStreams={dataStreams} componentTemplate={componentTemplate} />; return (
<StepReview
dataStreams={dataStreams}
canRollover={canRollover}
componentTemplate={componentTemplate}
/>
);
} }
); );

View file

@ -11,6 +11,7 @@ import {
ComponentTemplateDeserialized, ComponentTemplateDeserialized,
ComponentTemplateSerialized, ComponentTemplateSerialized,
ComponentTemplateDatastreams, ComponentTemplateDatastreams,
ComponentTemplateMeta,
} from '../shared_imports'; } from '../shared_imports';
import { import {
UIM_COMPONENT_TEMPLATE_DELETE_MANY, UIM_COMPONENT_TEMPLATE_DELETE_MANY,
@ -109,13 +110,33 @@ export const getApi = (
}); });
} }
function useLoadReferencedIndexTemplateMeta(name: string) {
return useRequest<ComponentTemplateMeta>({
path: `${apiBasePath}/component_templates/${encodeURIComponent(
name
)}/referenced_index_template_meta`,
method: 'get',
});
}
async function getReferencedIndexTemplateMeta(name: string) {
return sendRequest<ComponentTemplateMeta>({
path: `${apiBasePath}/component_templates/${encodeURIComponent(
name
)}/referenced_index_template_meta`,
method: 'get',
});
}
return { return {
useLoadComponentTemplates, useLoadComponentTemplates,
deleteComponentTemplates, deleteComponentTemplates,
useLoadComponentTemplate, useLoadComponentTemplate,
createComponentTemplate, createComponentTemplate,
updateComponentTemplate, updateComponentTemplate,
useLoadReferencedIndexTemplateMeta,
useLoadComponentTemplatesDatastream, useLoadComponentTemplatesDatastream,
getReferencedIndexTemplateMeta,
getComponentTemplateDatastreams, getComponentTemplateDatastreams,
postDataStreamRollover, postDataStreamRollover,
postDataStreamMappingsFromTemplate, postDataStreamMappingsFromTemplate,

View file

@ -68,6 +68,7 @@ export type {
ComponentTemplateDeserialized, ComponentTemplateDeserialized,
ComponentTemplateListItem, ComponentTemplateListItem,
ComponentTemplateDatastreams, ComponentTemplateDatastreams,
ComponentTemplateMeta,
} from '../../../../common'; } from '../../../../common';
export { serializeComponentTemplate } from '../../../../common/lib'; export { serializeComponentTemplate } from '../../../../common/lib';

View file

@ -12,13 +12,17 @@ import { registerCreateRoute } from './register_create_route';
import { registerUpdateRoute } from './register_update_route'; import { registerUpdateRoute } from './register_update_route';
import { registerDeleteRoute } from './register_delete_route'; import { registerDeleteRoute } from './register_delete_route';
import { registerPrivilegesRoute } from './register_privileges_route'; import { registerPrivilegesRoute } from './register_privileges_route';
import { registerGetDatastreams } from './register_datastream_route'; import {
registerGetDatastreams,
registerReferencedIndexTemplateMeta,
} from './register_datastream_route';
export function registerComponentTemplateRoutes(dependencies: RouteDependencies) { export function registerComponentTemplateRoutes(dependencies: RouteDependencies) {
registerGetAllRoute(dependencies); registerGetAllRoute(dependencies);
registerCreateRoute(dependencies); registerCreateRoute(dependencies);
registerUpdateRoute(dependencies); registerUpdateRoute(dependencies);
registerGetDatastreams(dependencies); registerGetDatastreams(dependencies);
registerReferencedIndexTemplateMeta(dependencies);
registerDeleteRoute(dependencies); registerDeleteRoute(dependencies);
registerPrivilegesRoute(dependencies); registerPrivilegesRoute(dependencies);
} }

View file

@ -77,3 +77,41 @@ export const registerGetDatastreams = ({
} }
); );
}; };
export const registerReferencedIndexTemplateMeta = ({
router,
lib: { handleEsError },
}: RouteDependencies): void => {
router.get(
{
path: addBasePath('/component_templates/{name}/referenced_index_template_meta'),
validate: {
params: paramsSchema,
},
},
async (context, request, response) => {
const { client } = (await context.core).elasticsearch;
const { name } = request.params;
try {
const { index_templates: indexTemplates } =
await client.asCurrentUser.indices.getIndexTemplate();
const result = indexTemplates.filter((indexTemplate) =>
indexTemplate.index_template?.composed_of?.includes(name)
);
// We should always match against the first result which should yield
// the index template we are after.
if (result[0]) {
return response.ok({
body: result[0].index_template._meta,
});
}
return response.notFound();
} catch (error) {
return handleEsError({ error, response });
}
}
);
};