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
17 changes: 17 additions & 0 deletions docs/syntax/automated_settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,23 @@ The `{settings}` directive is generic. Although the largest current examples com
::::
```

#### Options

`:deployment: <value>`
: Filters the rendered settings to only those available for the specified deployment type. When omitted, all settings are shown regardless of deployment.

Valid values: `ech` (Elastic Cloud Hosted), `ece` (Elastic Cloud Enterprise), `eck` (Elastic Cloud on Kubernetes), `self` (self-managed).

A setting is considered available for a deployment type if its `applies_to.deployment` block explicitly lists that deployment with a non-removed lifecycle. If a setting has `applies_to` metadata but no entry for the requested deployment, it is treated as unavailable and hidden.

Settings with no `applies_to` metadata at all are always shown, regardless of the filter.

```markdown
::::{settings} /syntax/settings-with-applies-example.yml
:deployment: ech
::::
```

### Schema

The schema below reflects the structure currently supported by docs-builder. For the original settings-gen schema that inspired this format, see [the Kibana schema reference](https://github.com/elastic/kibana/tree/main/docs/settings-gen#schema).
Expand Down
26 changes: 26 additions & 0 deletions docs/testing/kibana-settings-yaml-samples.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,29 @@ Some descriptions use links and anchors that target the real Kibana reference pa

:::{settings} /testing/kibana-security-settings.yml
:::

## Deployment filter preview

The `:deployment:` option filters settings to only those available for the specified deployment type.
Settings with no `applies_to` are always shown. Settings with `applies_to` that do not explicitly list
the deployment are treated as unavailable.

Accepted values: `ech`, `ece`, `eck`, `self`.

### kibana-general-settings.yml — ECH only

:::{settings} /testing/kibana-general-settings.yml
:deployment: ech
:::

### kibana-general-settings.yml — self-managed only

:::{settings} /testing/kibana-general-settings.yml
:deployment: self
:::

### kibana-security-settings.yml — ECH only

:::{settings} /testing/kibana-security-settings.yml
:deployment: ech
:::
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,7 @@ private static void WriteSettingsBlock(HtmlRenderer renderer, SettingsBlock bloc
SettingsCollection = settings,
GroupHeadingLevel = block.GroupHeadingLevel,
VersionsConfig = block.Build.VersionsConfiguration,
ActiveDeploymentFilter = block.ActiveDeploymentFilter,
RenderMarkdown = s =>
{
var normalized = SettingsMarkdownNormalizer.Normalize(s, settings.Product);
Expand Down
63 changes: 51 additions & 12 deletions src/Elastic.Markdown/Myst/Directives/Settings/SettingsBlock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ public class SettingsBlock(DirectiveBlockParser parser, ParserContext context) :

public bool Found { get; private set; }

/// <summary>
/// When set, only settings available for this deployment type are rendered.
/// Accepted values: <c>ech</c>, <c>ece</c>, <c>eck</c>, <c>self</c>.
/// </summary>
public string? ActiveDeploymentFilter { get; private set; }

/// <summary>
/// Heading level for each YAML group title (e.g. 3 when the directive follows an <c>##</c> heading), same rule as <see cref="Stepper.StepBlock"/>.
/// </summary>
Expand All @@ -48,19 +54,26 @@ public IEnumerable<PageTocItem> GeneratedTableOfContent
return [];

var level = GroupHeadingLevel;
return settings.Groups.Select(g => new PageTocItem
{
Heading = g.Name ?? string.Empty,
Slug = SettingsViewModel.GroupHeadingSlug(g),
Level = level
}).Where(t => !string.IsNullOrEmpty(t.Slug));
return settings.Groups
.Where(g => ActiveDeploymentFilter is null ||
DeploymentFilter.AnyVisible(g.Settings, ActiveDeploymentFilter, null))
.Select(g => new PageTocItem
{
Heading = g.Name ?? string.Empty,
Slug = SettingsViewModel.GroupHeadingSlug(g),
Level = level
}).Where(t => !string.IsNullOrEmpty(t.Slug));
}
}


//TODO add all options from
//https://mystmd.org/guide/directives#directive-include
public override void FinalizeAndValidate(ParserContext context) => ExtractInclusionPath(context);
public override void FinalizeAndValidate(ParserContext context)
{
ExtractInclusionPath(context);
ValidateDeploymentFilter();
}

/// <summary>
/// Records docset substitution keys referenced in raw settings YAML (e.g. <c>page_description</c>) so
Expand Down Expand Up @@ -117,6 +130,24 @@ private static void PrepareSettingForRendering(Setting setting, ParserContext co
PrepareSettingForRendering(child, context);
}

private void ValidateDeploymentFilter()
{
var raw = Prop("deployment");
if (raw is null)
return;

var trimmed = raw.Trim().ToLowerInvariant();
if (!DeploymentFilter.ValidValues.Contains(trimmed))
{
this.EmitWarning(
$"Unknown deployment filter '{raw}'. Valid values are: {string.Join(", ", DeploymentFilter.ValidValues)}."
);
return;
}

ActiveDeploymentFilter = trimmed;
}

private void ExtractInclusionPath(ParserContext context)
{
var includePath = Arguments;
Expand Down Expand Up @@ -206,33 +237,41 @@ private string[] LoadGeneratedAnchors()
if (TryLoadSettings() is not { } settings)
return [];

return CollectSettingIds(settings).Distinct(StringComparer.OrdinalIgnoreCase).ToArray();
return CollectSettingIds(settings, ActiveDeploymentFilter).Distinct(StringComparer.OrdinalIgnoreCase).ToArray();
}

private static IEnumerable<string> CollectSettingIds(YamlSettings yaml)
private static IEnumerable<string> CollectSettingIds(YamlSettings yaml, string? deploymentFilter)
{
if (!string.IsNullOrWhiteSpace(yaml.Id))
yield return yaml.Id;

foreach (var group in yaml.Groups)
{
var groupVisible = deploymentFilter is null ||
DeploymentFilter.AnyVisible(group.Settings, deploymentFilter, null);
if (!groupVisible)
continue;

var groupSlug = SettingsViewModel.GroupHeadingSlug(group);
if (!string.IsNullOrEmpty(groupSlug))
yield return groupSlug;
foreach (var id in CollectSettingIds(group.Settings, parentName: null))
foreach (var id in CollectSettingIds(group.Settings, parentName: null, deploymentFilter))
yield return id;
}
}

private static IEnumerable<string> CollectSettingIds(Setting[] settings, string? parentName)
private static IEnumerable<string> CollectSettingIds(Setting[] settings, string? parentName, string? deploymentFilter)
{
foreach (var setting in settings)
{
if (deploymentFilter is not null && !setting.IsVisibleForDeployment(deploymentFilter, null))
continue;

var displayName = SettingsViewModel.ComposeSettingName(parentName, setting.Name);
var fragmentId = SettingsViewModel.SettingFragmentId(setting, displayName);
if (!string.IsNullOrWhiteSpace(fragmentId))
yield return fragmentId;
foreach (var id in CollectSettingIds(setting.Settings, displayName))
foreach (var id in CollectSettingIds(setting.Settings, displayName, deploymentFilter))
yield return id;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
@RenderAdmonition("note", Model.SettingsCollection.Note)
@foreach (var group in Model.SettingsCollection.Groups)
{
@RenderGroup(group)
if (Model.IsGroupVisible(group))
{
@RenderGroup(group)
}
}
@functions {

Expand Down Expand Up @@ -65,12 +68,15 @@
@RenderMarkdown(group.Description)
@RenderAdmonition("note", group.Note)
@RenderExample(group.Example)
<dl>
@foreach (var setting in group.Settings)
<dl>
@foreach (var setting in group.Settings)
{
if (Model.IsSettingVisible(setting, null))
{
@RenderSetting(setting, null, null)
}
</dl>
}
</dl>
return HtmlString.Empty;
}

Expand Down Expand Up @@ -99,7 +105,10 @@
<dl>
@foreach (var child in setting.Settings)
{
@RenderSetting(child, displayName, appliesTo)
if (Model.IsSettingVisible(child, appliesTo))
{
@RenderSetting(child, displayName, appliesTo)
}
}
</dl>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,20 @@ public class SettingsViewModel
/// <summary>Markdown heading level for each group section (1–6).</summary>
public required int GroupHeadingLevel { get; init; }

/// <summary>
/// When set, only settings visible for this deployment type are rendered.
/// Accepted values: <c>ech</c>, <c>ece</c>, <c>eck</c>, <c>self</c>.
/// </summary>
public string? ActiveDeploymentFilter { get; init; }

public bool IsGroupVisible(SettingsGrouping group) =>
ActiveDeploymentFilter is null ||
DeploymentFilter.AnyVisible(group.Settings, ActiveDeploymentFilter, null);

public bool IsSettingVisible(Setting setting, ApplicableTo? inheritedAppliesTo) =>
ActiveDeploymentFilter is null ||
setting.IsVisibleForDeployment(ActiveDeploymentFilter, inheritedAppliesTo);

public string RenderAppliesToInline(ApplicableTo? appliesTo) =>
RenderAppliesToPlacement(appliesTo, ApplicabilityBadgePlacement.Combined);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,54 @@ public static class SettingDisplay
_ => value.ToString()
};
}

public static class DeploymentFilter
{
/// <summary>Valid filter tokens accepted by the <c>:deployment:</c> directive option.</summary>
public static readonly IReadOnlySet<string> ValidValues =
new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "ech", "ece", "eck", "self" };

/// <summary>
/// Returns the <see cref="AppliesCollection"/> for the given deployment filter key,
/// mapping the canonical <c>ech</c> token to the <c>ess</c> model field.
/// Returns <c>null</c> when the deployment type is not mentioned (i.e. not available).
/// </summary>
public static AppliesCollection? GetForDeployment(this DeploymentApplicability deployment, string key) =>
key.ToLowerInvariant() switch
{
"ech" => deployment.Ess,
"ece" => deployment.Ece,
"eck" => deployment.Eck,
"self" => deployment.Self,
_ => null
};

/// <summary>
/// Returns <c>true</c> when the setting should be shown for the given deployment filter.
/// A setting with no <c>applies_to</c> at all is always visible (no restriction).
/// A setting with <c>applies_to</c> that does not explicitly list the deployment is considered unavailable.
/// </summary>
public static bool IsVisibleForDeployment(this Setting setting, string deploymentFilter, ApplicableTo? inheritedAppliesTo)
{
var appliesTo = setting.ResolveAppliesTo(inheritedAppliesTo);

if (appliesTo is null)
return true;

if (appliesTo.Deployment is not { } deployment)
return false;

var col = deployment.GetForDeployment(deploymentFilter);
if (col is null)
return false;

return col.Any(a => a.Lifecycle is not ProductLifecycle.Removed and not ProductLifecycle.Unavailable);
}

/// <summary>Returns <c>true</c> when at least one setting (recursively) in <paramref name="settings"/> is visible.</summary>
public static bool AnyVisible(Setting[] settings, string deploymentFilter, ApplicableTo? inheritedAppliesTo) =>
settings.Any(s =>
s.IsVisibleForDeployment(deploymentFilter, inheritedAppliesTo) ||
AnyVisible(s.Settings, deploymentFilter, s.ResolveAppliesTo(inheritedAppliesTo))
);
}
Loading
Loading