diff --git a/x-pack/platform/plugins/shared/fleet/common/services/package_to_package_policy.ts b/x-pack/platform/plugins/shared/fleet/common/services/package_to_package_policy.ts index d1a68c81d39f5..0b9c1e005582c 100644 --- a/x-pack/platform/plugins/shared/fleet/common/services/package_to_package_policy.ts +++ b/x-pack/platform/plugins/shared/fleet/common/services/package_to_package_policy.ts @@ -89,6 +89,10 @@ export const varsReducer = ( configObject: PackagePolicyConfigRecord, registryVar: RegistryVarsEntry ): PackagePolicyConfigRecord => { + // section_header vars are decorative only and hold no value + if (registryVar.type === 'section_header') { + return configObject; + } const configEntry: PackagePolicyConfigRecordEntry = { value: !registryVar.default && registryVar.multi ? [] : registryVar.default, }; diff --git a/x-pack/platform/plugins/shared/fleet/common/types/models/epm.ts b/x-pack/platform/plugins/shared/fleet/common/types/models/epm.ts index 3b058da24c7cf..5ead043d5f2e1 100644 --- a/x-pack/platform/plugins/shared/fleet/common/types/models/epm.ts +++ b/x-pack/platform/plugins/shared/fleet/common/types/models/epm.ts @@ -323,6 +323,7 @@ export enum RegistryInputKeys { deprecated = 'deprecated', migrate_from = 'migrate_from', dynamic_signal_types = 'dynamic_signal_types', + show_divider = 'show_divider', } export type RegistryInputGroup = 'logs' | 'metrics'; @@ -344,6 +345,8 @@ export interface RegistryInput { [RegistryInputKeys.migrate_from]?: string; /** When true the data stream signal type (logs/metrics/traces) is determined at runtime by the agent. Valid for OTel collector inputs in composable integrations. */ [RegistryInputKeys.dynamic_signal_types]?: boolean; + /** When false, suppresses the automatic horizontal divider rendered after the input-level config section. Defaults to true. */ + [RegistryInputKeys.show_divider]?: boolean; } export enum RegistryStreamKeys { @@ -548,7 +551,8 @@ export type RegistryVarType = | 'string' | 'textarea' | 'duration' - | 'url'; + | 'url' + | 'section_header'; export enum RegistryVarsEntryKeys { name = 'name', title = 'title', diff --git a/x-pack/platform/plugins/shared/fleet/common/types/models/package_spec.ts b/x-pack/platform/plugins/shared/fleet/common/types/models/package_spec.ts index 5f1f99da7e28d..39c34505d5466 100644 --- a/x-pack/platform/plugins/shared/fleet/common/types/models/package_spec.ts +++ b/x-pack/platform/plugins/shared/fleet/common/types/models/package_spec.ts @@ -28,6 +28,7 @@ export interface RegistryVarGroup { selector_title: string; description?: string; required?: boolean; // When true, all vars in the selected option are treated as required + show_divider?: boolean; // When false, suppresses the automatic horizontal divider rendered after this var_group's stream section options: RegistryVarGroupOption[]; } diff --git a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_config.tsx b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_config.tsx index 9620dce4f9f6d..4363ec92d88da 100644 --- a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_config.tsx +++ b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_config.tsx @@ -26,7 +26,7 @@ import type { } from '../../../../../../types'; import type { PackagePolicyConfigValidationResults } from '../../../services'; import { isAdvancedVar, validationHasErrors } from '../../../services'; -import { shouldShowVar } from '../../../services/var_group_helpers'; +import { shouldShowVar, getVarsControlledByVarGroups } from '../../../services/var_group_helpers'; import type { VarGroupSelection } from '../../../services/var_group_helpers'; import { useAgentless } from '../../../single_page_layout/hooks/setup_technology'; @@ -72,10 +72,15 @@ export const PackagePolicyInputConfig: React.FunctionComponent<{ const [isShowingAdvanced, setIsShowingAdvanced] = useState(false); const { isAgentlessEnabled } = useAgentless(); - // Split vars into required and advanced, filtering by var_group visibility and deprecated vars - const [requiredVars, advancedVars] = useMemo(() => { + // Split vars into required and advanced, filtering by var_group visibility and deprecated vars. + // Required vars are further split into pre-group (not controlled by any var_group, rendered + // above var_group selectors) and post-group (controlled by a selected var_group option, + // rendered below var_group selectors) to preserve manifest declaration order intent. + const [preGroupRequiredVars, postGroupRequiredVars, advancedVars] = useMemo(() => { const _advancedVars: RegistryVarsEntry[] = []; - const _requiredVars: RegistryVarsEntry[] = []; + const _preGroupVars: RegistryVarsEntry[] = []; + const _postGroupVars: RegistryVarsEntry[] = []; + const controlledVars = varGroups?.length ? getVarsControlledByVarGroups(varGroups) : null; (packageInputVars || []).forEach((varDef) => { if (!isEditPage && !!varDef.deprecated) { return; @@ -89,11 +94,15 @@ export const PackagePolicyInputConfig: React.FunctionComponent<{ } if (isAdvancedVar(varDef, varGroups, varGroupSelections)) { _advancedVars.push(varDef); + } else if (controlledVars?.has(varDef.name)) { + // Var is controlled by a var_group option — render after the var_group selector + _postGroupVars.push(varDef); } else { - _requiredVars.push(varDef); + // Var is independent of var_groups — render before the var_group selector + _preGroupVars.push(varDef); } }); - return [_requiredVars, _advancedVars]; + return [_preGroupVars, _postGroupVars, _advancedVars]; }, [packageInputVars, varGroups, varGroupSelections, isEditPage]); const allAdvancedVars = useMemo(() => { @@ -189,6 +198,36 @@ export const PackagePolicyInputConfig: React.FunctionComponent<{ ) : null} + {preGroupRequiredVars.map((varDef) => { + const { name: varName, type: varType } = varDef; + + const value = packagePolicyInput.vars?.[varName]?.value; + const frozen = packagePolicyInput.vars?.[varName]?.frozen; + + return ( + + { + updatePackagePolicyInput({ + vars: { + ...packagePolicyInput.vars, + [varName]: { + type: varType, + value: newValue, + }, + }, + }); + }} + errors={inputValidationResults.vars?.[varName]} + forceShowErrors={forceShowErrors} + isEditPage={isEditPage} + /> + + ); + })} {varGroups?.map((varGroup) => ( ))} - {requiredVars.map((varDef) => { + {postGroupRequiredVars.map((varDef) => { const { name: varName, type: varType } = varDef; const value = packagePolicyInput.vars?.[varName]?.value; diff --git a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.tsx b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.tsx index a515c537e5e68..72092f3f1122c 100644 --- a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.tsx +++ b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.tsx @@ -525,7 +525,9 @@ export const PackagePolicyInputPanel: React.FunctionComponent<{ showDescriptionColumn={!isSingleInputAndStreams} streamAdvancedVars={consolidatedStreamAdvancedVars} /> - {hasInputStreams && !shouldConsolidateAdvancedSections ? ( + {hasInputStreams && + !shouldConsolidateAdvancedSections && + packageInput.show_divider !== false ? ( ) : ( diff --git a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_var_field.tsx b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_var_field.tsx index 811d67b32515a..5f6aa973b4bab 100644 --- a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_var_field.tsx +++ b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_var_field.tsx @@ -13,6 +13,7 @@ import { EuiSwitch, EuiFieldText, EuiText, + EuiTitle, EuiFieldPassword, EuiCodeBlock, EuiTextArea, @@ -112,6 +113,21 @@ export const PackagePolicyInputVarField: React.FunctionComponent + +

{title || name}

+
+ {description && ( + + + + )} + + ); + } + if (name === DATASET_VAR_NAME && packageType === 'input') { return ( { + // section_header vars are decorative and always render with required vars, never under "Advanced options" + if (varDef.type === 'section_header') { + return false; + } + // If var is in a selected var_group option, treat as non-advanced (override show_user: false) if ( varGroups &&