Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
/*
* 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 {
PackageInfo,
RegistryPolicyInputOnlyTemplate,
RegistryPolicyIntegrationTemplate,
} from '../types';

import { getDocumentationPageInputs } from './documentation_page_inputs';

const minimalIntegrationPkg = (
partial: Partial<PackageInfo> & Pick<PackageInfo, 'policy_templates'>
): PackageInfo =>
({
name: 'test_pkg',
title: 'Test package',
description: 'desc',
version: '1.0.0',
release: 'ga',
type: 'integration',
...partial,
} as PackageInfo);

describe('getDocumentationPageInputs', () => {
it('uses distinct keys when two inputs share a type but have different names', () => {
const nginxTemplate: RegistryPolicyIntegrationTemplate = {
name: 'nginx',
title: 'Nginx',
description: 'd',
inputs: [
{
type: 'filelog_otel',
name: 'nginx-access',
title: 'Access',
description: '',
vars: [],
},
{
type: 'filelog_otel',
name: 'nginx-error',
title: 'Error',
description: '',
vars: [],
},
],
};

const pkg = minimalIntegrationPkg({
policy_templates: [nginxTemplate],
data_streams: [],
});

const inputs = getDocumentationPageInputs(pkg);
expect(inputs).toHaveLength(2);
expect(inputs.map((i) => i.key)).toEqual(['nginx-access', 'nginx-error']);
});

it('prefixes keys with policy template name when the package has multiple integrations', () => {
const t1: RegistryPolicyIntegrationTemplate = {
name: 'nginx',
title: 'Nginx',
description: 'd',
inputs: [{ type: 'logfile', title: 'L', description: '', vars: [] }],
};
const t2: RegistryPolicyIntegrationTemplate = {
name: 'apache',
title: 'Apache',
description: 'd',
inputs: [{ type: 'logfile', title: 'L', description: '', vars: [] }],
};

const pkg = minimalIntegrationPkg({
policy_templates: [t1, t2],
data_streams: [],
});

const inputs = getDocumentationPageInputs(pkg);
expect(inputs.map((i) => i.key).sort()).toEqual(['apache-logfile', 'nginx-logfile']);
});

it('scopes streams to policy_template.data_streams paths when set', () => {
const tmpl: RegistryPolicyIntegrationTemplate = {
name: 'app',
title: 'App',
description: 'd',
data_streams: ['ds1'],
inputs: [{ type: 'logfile', title: 'Logs', description: '', vars: [] }],
};

const pkg = minimalIntegrationPkg({
policy_templates: [tmpl],
data_streams: [
{
path: 'ds1',
type: 'logs',
dataset: 'pkg.ds1',
title: 'DS1',
release: 'ga',
package: 'test_pkg',
streams: [{ input: 'logfile', title: 'S1', vars: [] }],
},
{
path: 'ds2',
type: 'logs',
dataset: 'pkg.ds2',
title: 'DS2',
release: 'ga',
package: 'test_pkg',
streams: [{ input: 'logfile', title: 'S2', vars: [] }],
},
],
});

const inputs = getDocumentationPageInputs(pkg);
expect(inputs).toHaveLength(1);
expect(inputs[0].streams).toHaveLength(1);
expect(inputs[0].streams[0].data_stream.dataset).toBe('pkg.ds1');
});

it('includes all matching data streams when policy template has no data_streams paths', () => {
const tmpl: RegistryPolicyIntegrationTemplate = {
name: 'app',
title: 'App',
description: 'd',
inputs: [{ type: 'logfile', title: 'Logs', description: '', vars: [] }],
};

const pkg = minimalIntegrationPkg({
policy_templates: [tmpl],
data_streams: [
{
path: 'ds1',
type: 'logs',
dataset: 'pkg.ds1',
title: 'DS1',
release: 'ga',
package: 'test_pkg',
streams: [{ input: 'logfile', title: 'S1', vars: [] }],
},
{
path: 'ds2',
type: 'logs',
dataset: 'pkg.ds2',
title: 'DS2',
release: 'ga',
package: 'test_pkg',
streams: [{ input: 'logfile', title: 'S2', vars: [] }],
},
],
});

const inputs = getDocumentationPageInputs(pkg);
expect(inputs[0].streams).toHaveLength(2);
});

it('returns one input for an input-only policy template', () => {
const inputOnly: RegistryPolicyInputOnlyTemplate = {
input: 'logfile',
type: 'logs',
name: 'standalone',
template_path: 'template.yml',
title: 'Standalone input',
description: 'd',
};

const pkg = minimalIntegrationPkg({
policy_templates: [inputOnly],
data_streams: [],
});

const inputs = getDocumentationPageInputs(pkg);
expect(inputs).toHaveLength(1);
expect(inputs[0].type).toBe('logfile');
expect(inputs[0].key).toBe('logfile');
});

it('filters by integration name when provided', () => {
const nginx: RegistryPolicyIntegrationTemplate = {
name: 'nginx',
title: 'Nginx',
description: 'd',
inputs: [{ type: 'logfile', title: 'L', description: '', vars: [] }],
};
const apache: RegistryPolicyIntegrationTemplate = {
name: 'apache',
title: 'Apache',
description: 'd',
inputs: [{ type: 'logfile', title: 'L', description: '', vars: [] }],
};

const pkg = minimalIntegrationPkg({
policy_templates: [nginx, apache],
data_streams: [],
});

expect(getDocumentationPageInputs(pkg, 'nginx')).toHaveLength(1);
expect(getDocumentationPageInputs(pkg, 'nginx')[0].policy_template).toBe('nginx');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* 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 { PackageInfo, RegistryInput, RegistryStream } from '../types';

import { doesPackageHaveIntegrations } from './packages_with_integrations';
import { getNormalizedInputs, isIntegrationPolicyTemplate } from './policy_template';
import {
buildInputKey,
getInputEffectiveName,
getStreamsForInputType,
} from './package_to_package_policy';

export type DocumentationPageInputStream = RegistryStream & {
data_stream: { type?: string; dataset: string };
};

export type DocumentationPageInput = RegistryInput & {
key: string;
policy_template: string;
streams: DocumentationPageInputStream[];
};

/**
* Inputs and streams for the Integrations package detail "API reference" tab.
* Keys and stream resolution match package policy validation and template generation.
*/
export const getDocumentationPageInputs = (
packageInfo: PackageInfo,
integration?: string | null
): DocumentationPageInput[] => {
const hasIntegrations = doesPackageHaveIntegrations(packageInfo);
const result: DocumentationPageInput[] = [];

packageInfo.policy_templates?.forEach((policyTemplate) => {
if (integration && policyTemplate.name !== integration) {
return;
}

const normalizedInputs = getNormalizedInputs(policyTemplate);
const dataStreamPaths =
isIntegrationPolicyTemplate(policyTemplate) && policyTemplate.data_streams?.length
? policyTemplate.data_streams
: [];

normalizedInputs.forEach((input) => {
const effectiveName = getInputEffectiveName(input);
const key = buildInputKey(effectiveName, policyTemplate.name, hasIntegrations);
const streams = getStreamsForInputType(effectiveName, packageInfo, dataStreamPaths);

result.push({
...input,
key,
policy_template: policyTemplate.name,
streams,
});
});
});

return result;
};
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ export {
getInputEffectiveName,
buildInputKey,
} from './package_to_package_policy';
export type {
DocumentationPageInput,
DocumentationPageInputStream,
} from './documentation_page_inputs';
export { getDocumentationPageInputs } from './documentation_page_inputs';
export { fullAgentPolicyToYaml } from './full_agent_policy_to_yaml';
export { isPackageLimited, doesAgentPolicyAlreadyIncludePackage } from './limited_package';
export {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,46 +23,25 @@ import { FormattedMessage } from '@kbn/i18n-react';

import { i18n } from '@kbn/i18n';

import type {
PackageInfo,
RegistryVarsEntry,
RegistryStream,
RegistryInput,
} from '../../../../../types';
import type { PackageInfo, RegistryVarsEntry } from '../../../../../types';
import { useStartServices } from '../../../../../../../hooks';
import { getStreamsForInputType } from '../../../../../../../../common/services';
import {
getDocumentationPageInputs,
type DocumentationPageInputStream,
} from '../../../../../../../../common/services';
import { SideBarColumn } from '../../../components/side_bar_column';

interface Props {
packageInfo: PackageInfo;
integration?: string | null;
}

const getInputs = ({ packageInfo, integration }: Props) => {
return packageInfo.policy_templates?.reduce((acc, policyTemplate) => {
if (integration && policyTemplate.name !== integration) {
return acc;
}
if ('inputs' in policyTemplate && policyTemplate.inputs) {
return [
...acc,
...policyTemplate.inputs.map((input) => ({
key: `${policyTemplate.name}-${input.type}`,
...input,
streams: getStreamsForInputType(input.type, packageInfo, []),
})),
];
}
return acc;
}, [] as RegistryInputWithStreams[]);
};

export const hasDocumentation = ({ packageInfo, integration }: Props) => {
if (packageInfo.vars && packageInfo.vars.length > 0) {
return true;
}

if ((getInputs({ packageInfo, integration }) || []).length > 0) {
if (getDocumentationPageInputs(packageInfo, integration).length > 0) {
return true;
}
};
Expand Down Expand Up @@ -114,13 +93,8 @@ export const DocumentationPage: React.FunctionComponent<Props> = ({ packageInfo,
);
};

type RegistryInputWithStreams = RegistryInput & {
key: string;
streams: Array<RegistryStream & { data_stream: { type?: string; dataset: string } }>;
};

const StreamsSection: React.FunctionComponent<{
streams: RegistryInputWithStreams['streams'];
streams: DocumentationPageInputStream[];
}> = ({ streams }) => {
if (streams.length === 0) {
return null;
Expand Down Expand Up @@ -166,7 +140,10 @@ const Inputs: React.FunctionComponent<{
packageInfo: PackageInfo;
integration?: string | null;
}> = ({ packageInfo, integration }) => {
const inputs = useMemo(() => getInputs({ packageInfo, integration }), [packageInfo, integration]);
const inputs = useMemo(
() => getDocumentationPageInputs(packageInfo, integration),
[packageInfo, integration]
);
return (
<>
<EuiSpacer size="m" />
Expand Down
Loading