Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -1353,5 +1353,175 @@ describe('PackagePolicyInputPanel', () => {
expect(lastCall?.streams?.[0]?.vars).not.toHaveProperty(USE_APM_VAR_NAME);
});
});

it('hoists the non-GA release badge up to the input header for input-type packages', async () => {
const betaOtelStreams: RegistryStreamWithDataStream[] = [
{
...otelPackageInputStreams[0],
data_stream: {
...otelPackageInputStreams[0].data_stream,
release: 'beta',
},
},
];
const otelPolicyInput = {
id: 'input-1',
type: OTEL_COLLECTOR_INPUT_TYPE,
policy_template: 'otel_template',
enabled: true,
streams: [
{
id: 'otel-stream-1',
data_stream: { type: 'logs', dataset: 'my_otel.data' },
enabled: true,
vars: {},
},
],
} as NewPackagePolicyInput;

renderResult = testRenderer.render(
<PackagePolicyInputPanel
packageInfo={otelPackageInfo}
packageInput={otelPackageInput}
packageInputStreams={betaOtelStreams}
packagePolicyInput={otelPolicyInput}
updatePackagePolicyInput={mockUpdateOtelInput}
inputValidationResults={otelInputValidationResults}
/>
);

await waitFor(() => {
expect(renderResult.getByText('Beta')).toBeInTheDocument();
// Stream-level toggle should not render for input-type packages,
// which is why the badge is hoisted up to the input header instead.
expect(renderResult.queryByTestId('streamOptions.switch')).not.toBeInTheDocument();
});
});
});

describe('Non-GA release badge hoisting', () => {
beforeEach(() => {
jest.spyOn(ExperimentalFeaturesService, 'get').mockReturnValue({
enableVarGroups: true,
} as any);
useAgentlessMock.mockReturnValue({
isAgentlessEnabled: false,
isAgentlessDefault: false,
isAgentlessAgentPolicy: jest.fn(),
getAgentlessStatusForPackage: jest
.fn()
.mockReturnValue({ isAgentless: false, isDefaultDeploymentMode: false }),
isServerless: false,
isCloud: false,
});
});

const singleBetaStream: RegistryStreamWithDataStream[] = [
{
input: 'logfile',
title: 'Stream 1',
template_path: 'stream.yml.hbs',
vars: [
{
name: 'paths',
type: 'text',
title: 'Paths',
multi: false,
required: false,
show_user: true,
},
],
description: 'Test stream',
data_stream: {
...mockPackageInputStreams[0].data_stream,
release: 'beta',
},
},
];

const singleStreamPolicyInput = {
...packagePolicyInput,
streams: [packagePolicyInput.streams[0]],
} as NewPackagePolicyInput;

it('hoists the release badge to the input header when there is a single stream', async () => {
renderResult = testRenderer.render(
<PackagePolicyInputPanel
packageInfo={mockPackageInfo}
packageInput={mockPackageInput}
packageInputStreams={singleBetaStream}
packagePolicyInput={singleStreamPolicyInput}
updatePackagePolicyInput={mockUpdatePackagePolicyInput}
inputValidationResults={inputValidationResults}
/>
);
await waitFor(() => {
expect(renderResult.getByText('Beta')).toBeInTheDocument();
// Single-stream rows don't render their own toggle, so the badge
// would otherwise float alone - here we expect it at the input header.
expect(renderResult.queryByTestId('streamOptions.switch')).not.toBeInTheDocument();
});
});

it('renders the release badge at the stream row for multi-stream integrations', async () => {
const multiStreamsWithBeta: RegistryStreamWithDataStream[] = [
{
input: 'logfile',
title: 'Stream 1',
template_path: 'stream.yml.hbs',
vars: [
{
name: 'paths',
type: 'text',
title: 'Paths',
multi: false,
required: false,
show_user: true,
},
],
description: 'Test stream 1',
data_stream: {
...mockPackageInputStreams[0].data_stream,
release: 'beta',
},
},
{
input: 'logfile',
title: 'Stream 2',
template_path: 'stream.yml.hbs',
vars: [
{
name: 'paths',
type: 'text',
title: 'Paths',
multi: false,
required: false,
show_user: true,
},
],
description: 'Test stream 2',
data_stream: {
...mockPackageInputStreams[1].data_stream,
},
},
];

renderResult = testRenderer.render(
<PackagePolicyInputPanel
packageInfo={mockPackageInfo}
packageInput={mockPackageInput}
packageInputStreams={multiStreamsWithBeta}
packagePolicyInput={packagePolicyInput}
updatePackagePolicyInput={mockUpdatePackagePolicyInput}
inputValidationResults={inputValidationResults}
/>
);
await waitFor(() => {
expect(renderResult.getByText('Beta')).toBeInTheDocument();
// Multi-stream integrations show per-stream toggles, so the badge
// stays at the stream row - no need to hoist.
expect(renderResult.getAllByTestId('streamOptions.switch').length).toBeGreaterThan(0);
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import React, { useState, Fragment, memo, useMemo, useCallback, useEffect, useRef } from 'react';
import React, { useState, memo, useMemo, useCallback, useEffect, useRef } from 'react';
import ReactMarkdown from 'react-markdown';
import styled from 'styled-components';
import { FormattedMessage } from '@kbn/i18n-react';
Expand Down Expand Up @@ -42,7 +42,9 @@ import {
DATA_STREAM_USE_APM_VAR,
shouldIncludeUseAPMVar,
hasDynamicSignalTypes,
mapPackageReleaseToIntegrationCardRelease,
} from '../../../../../../../../../common/services';
import { InlineReleaseBadge } from '../../../../../../components';
import {
DATA_STREAM_TYPE_VAR_NAME,
USE_APM_VAR_NAME,
Expand Down Expand Up @@ -256,11 +258,6 @@ export const PackagePolicyInputPanel: React.FunctionComponent<{
const errorCount = inputValidationResults && countValidationErrors(inputValidationResults);
const hasErrors = forceShowErrors && errorCount;

const hasInputStreams = useMemo(
() => packageInputStreams.length > 0,
[packageInputStreams.length]
);

const inputStreams = useMemo(
() =>
packageInputStreams
Expand All @@ -276,6 +273,7 @@ export const PackagePolicyInputPanel: React.FunctionComponent<{
.filter((stream) => Boolean(stream.packagePolicyInputStream)),
[packageInputStreamShouldBeVisible, packageInputStreams, packagePolicyInput.streams]
);
const hasInputStreams = useMemo(() => inputStreams.length > 0, [inputStreams.length]);
const showTopLevelDescription = inputStreams.length === 1;

const dynamicSignalTypes = useMemo(
Expand Down Expand Up @@ -367,6 +365,27 @@ export const PackagePolicyInputPanel: React.FunctionComponent<{
defaultMessage: 'This input is deprecated.',
});

// Whether the individual stream rows will render their own toggle switch.
// When they won't (input-type package or single visible stream), we hoist
// the non-GA release badge up to the input header so it doesn't float alone.
const hasStreamToggle = packageInfo.type !== 'input' && inputStreams.length > 1;

const inputReleaseBadge = useMemo(() => {
if (hasStreamToggle) return null;
const preReleaseStream = inputStreams.find(
({ packageInputStream }) =>
packageInputStream.data_stream.release && packageInputStream.data_stream.release !== 'ga'
);
if (!preReleaseStream?.packageInputStream.data_stream.release) return null;
return (
<InlineReleaseBadge
release={mapPackageReleaseToIntegrationCardRelease(
preReleaseStream.packageInputStream.data_stream.release
)}
/>
);
}, [hasStreamToggle, inputStreams]);

// Check if any vars or streams in this input are deprecated
const hasDeprecatedFeatures = useMemo(() => {
const inputVarsDeprecated = (packageInput.vars || []).some((v) => !!v.deprecated);
Expand Down Expand Up @@ -404,6 +423,7 @@ export const PackagePolicyInputPanel: React.FunctionComponent<{
</h3>
</EuiTitle>
</EuiFlexItem>
{inputReleaseBadge && <EuiFlexItem grow={false}>{inputReleaseBadge}</EuiFlexItem>}
{migrationTooltip}
</EuiFlexGroup>
<EuiSpacer size="s" />
Expand All @@ -429,6 +449,9 @@ export const PackagePolicyInputPanel: React.FunctionComponent<{
</h3>
</EuiTitle>
</EuiFlexItem>
{inputReleaseBadge && (
<EuiFlexItem grow={false}>{inputReleaseBadge}</EuiFlexItem>
)}
{migrationTooltip}
{isDeprecatedInput && (
<EuiFlexItem grow={false}>
Expand Down Expand Up @@ -527,11 +550,16 @@ export const PackagePolicyInputPanel: React.FunctionComponent<{
</EuiFlexItem>
</EuiFlexGroup>

{/* Header rule break */}
{isShowingStreams ? <EuiSpacer size="l" /> : null}
{/* Spacing if we are showing rest of content */}
{isShowingStreams &&
hasInputStreams &&
((packageInput.vars && packageInput.vars.length) || !shouldConsolidateAdvancedSections) ? (
<EuiSpacer size="m" />
) : null}

{/* Input level policy */}
{isShowingStreams && packageInput.vars && packageInput.vars.length ? (
<Fragment>
<>
<PackagePolicyInputConfig
data-test-subj="PackagePolicy.InputConfig"
hasInputStreams={hasInputStreams}
Expand All @@ -553,13 +581,13 @@ export const PackagePolicyInputPanel: React.FunctionComponent<{
packageInput.show_divider !== false ? (
<ShortenedHorizontalRule margin="m" />
) : (
<EuiSpacer size="l" />
<EuiSpacer size="m" />
)}
</Fragment>
</>
) : null}

{/* Per-stream policy */}
{isShowingStreams && !shouldConsolidateAdvancedSections ? (
{isShowingStreams && hasInputStreams && !shouldConsolidateAdvancedSections ? (
<EuiFlexGroup direction="column" data-test-subj="PackagePolicy.InputConfig.streams">
{inputStreams.map(({ packageInputStream, packagePolicyInputStream }, index) => {
return (
Expand All @@ -568,7 +596,7 @@ export const PackagePolicyInputPanel: React.FunctionComponent<{
data-test-subj="PackagePolicy.InputStreamConfig"
packageInfo={packageInfo}
packageInputStream={packageInputStream}
totalStreams={inputStreams.length}
hasStreamToggle={hasStreamToggle}
packagePolicyInputStream={packagePolicyInputStream!}
inputPolicyTemplate={packagePolicyInput.policy_template}
showDescriptionColumn={!isSingleInputAndStreams}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ describe('PackagePolicyInputStreamConfig', () => {
updatePackagePolicyInputStream={mockUpdatePackagePolicyInputStream}
inputStreamValidationResults={{ vars: {} }}
forceShowErrors={false}
totalStreams={2}
hasStreamToggle={true}
inputPolicyTemplate={inputPolicyTemplate}
/>
);
Expand Down Expand Up @@ -297,7 +297,7 @@ describe('PackagePolicyInputStreamConfig', () => {
updatePackagePolicyInputStream={mockUpdatePackagePolicyInputStream}
inputStreamValidationResults={{ vars: {} }}
forceShowErrors={false}
totalStreams={2}
hasStreamToggle={true}
/>
);

Expand Down
Loading
Loading