Skip to content

Commit ae87572

Browse files
applies_to: Add backwards-compatible product applicability dictionary (#1444)
* applies_to: Add backwards-compatible product applicability dictionary * Update src/Elastic.Markdown/Myst/Components/ApplicableToComponent.cshtml Co-authored-by: shainaraskas <[email protected]> --------- Co-authored-by: shainaraskas <[email protected]>
1 parent e183c9d commit ae87572

File tree

5 files changed

+253
-32
lines changed

5 files changed

+253
-32
lines changed

docs/testing/req.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ The tutorial assumes that you have no previous knowledge of Elasticsearch or gen
99

1010
- Python development
1111
- The [Flask](https://flask.palletsprojects.com/) web framework for Python.
12-
- The command prompt or terminal application in your operating system.
12+
- The command prompt or terminal application in your operating system.

src/Elastic.Markdown/Myst/Components/ApplicableToComponent.cshtml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,26 @@
5656
{
5757
@RenderProduct("", appliesTo.Product)
5858
}
59+
@if (appliesTo.ProductApplicability is not null)
60+
{
61+
var pa = appliesTo.ProductApplicability;
62+
if (pa.Ecctl is not null) { @RenderProduct("ECCTL", pa.Ecctl); }
63+
if (pa.Curator is not null) { @RenderProduct("Curator", pa.Curator); }
64+
if (pa.ApmAgentDotnet is not null) { @RenderProduct("APM Agent .NET", pa.ApmAgentDotnet); }
65+
if (pa.ApmAgentGo is not null) { @RenderProduct("APM Agent Go", pa.ApmAgentGo); }
66+
if (pa.ApmAgentJava is not null) { @RenderProduct("APM Agent Java", pa.ApmAgentJava); }
67+
if (pa.ApmAgentNode is not null) { @RenderProduct("APM Agent Node.js", pa.ApmAgentNode); }
68+
if (pa.ApmAgentPython is not null) { @RenderProduct("APM Agent Python", pa.ApmAgentPython); }
69+
if (pa.ApmAgentRuby is not null) { @RenderProduct("APM Agent Ruby", pa.ApmAgentRuby); }
70+
if (pa.ApmAgentRum is not null) { @RenderProduct("APM Agent RUM", pa.ApmAgentRum); }
71+
if (pa.EdotIos is not null) { @RenderProduct("OpenTelemetry iOS", pa.EdotIos); }
72+
if (pa.EdotAndroid is not null) { @RenderProduct("OpenTelemetry Android", pa.EdotAndroid); }
73+
if (pa.EdotDotnet is not null) { @RenderProduct("OpenTelemetry .NET", pa.EdotDotnet); }
74+
if (pa.EdotJava is not null) { @RenderProduct("OpenTelemetry Java", pa.EdotJava); }
75+
if (pa.EdotNode is not null) { @RenderProduct("OpenTelemetry Node.js", pa.EdotNode); }
76+
if (pa.EdotPhp is not null) { @RenderProduct("OpenTelemetry PHP", pa.EdotPhp); }
77+
if (pa.EdotPython is not null) { @RenderProduct("OpenTelemetry Python", pa.EdotPython); }
78+
}
5979

6080
@functions {
6181

src/Elastic.Markdown/Myst/FrontMatter/ApplicableTo.cs

Lines changed: 219 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ public record ApplicableTo
5050
[YamlMember(Alias = "product")]
5151
public AppliesCollection? Product { get; set; }
5252

53+
public ProductApplicability? ProductApplicability { get; set; }
54+
5355
internal YamlDiagnosticsCollection? Diagnostics { get; set; }
5456

5557
public static ApplicableTo All { get; } = new()
@@ -113,12 +115,69 @@ public record ServerlessProjectApplicability
113115
};
114116
}
115117

118+
[YamlSerializable]
119+
public record ProductApplicability
120+
{
121+
[YamlMember(Alias = "ecctl")]
122+
public AppliesCollection? Ecctl { get; set; }
123+
124+
[YamlMember(Alias = "curator")]
125+
public AppliesCollection? Curator { get; set; }
126+
127+
[YamlMember(Alias = "apm_agent_dotnet")]
128+
public AppliesCollection? ApmAgentDotnet { get; set; }
129+
130+
[YamlMember(Alias = "apm_agent_go")]
131+
public AppliesCollection? ApmAgentGo { get; set; }
132+
133+
[YamlMember(Alias = "apm_agent_java")]
134+
public AppliesCollection? ApmAgentJava { get; set; }
135+
136+
[YamlMember(Alias = "apm_agent_node")]
137+
public AppliesCollection? ApmAgentNode { get; set; }
138+
139+
[YamlMember(Alias = "apm_agent_python")]
140+
public AppliesCollection? ApmAgentPython { get; set; }
141+
142+
[YamlMember(Alias = "apm_agent_ruby")]
143+
public AppliesCollection? ApmAgentRuby { get; set; }
144+
145+
[YamlMember(Alias = "apm_agent_rum")]
146+
public AppliesCollection? ApmAgentRum { get; set; }
147+
148+
[YamlMember(Alias = "edot_ios")]
149+
public AppliesCollection? EdotIos { get; set; }
150+
151+
[YamlMember(Alias = "edot_android")]
152+
public AppliesCollection? EdotAndroid { get; set; }
153+
154+
[YamlMember(Alias = "edot_dotnet")]
155+
public AppliesCollection? EdotDotnet { get; set; }
156+
157+
[YamlMember(Alias = "edot_java")]
158+
public AppliesCollection? EdotJava { get; set; }
159+
160+
[YamlMember(Alias = "edot_node")]
161+
public AppliesCollection? EdotNode { get; set; }
162+
163+
[YamlMember(Alias = "edot_php")]
164+
public AppliesCollection? EdotPhp { get; set; }
165+
166+
[YamlMember(Alias = "edot_python")]
167+
public AppliesCollection? EdotPython { get; set; }
168+
}
169+
116170
public class ApplicableToConverter : IYamlTypeConverter
117171
{
118172
private static readonly string[] KnownKeys =
119-
["stack", "deployment", "serverless", "product", "ece",
120-
"eck", "ess", "self", "elasticsearch", "observability","security"
121-
];
173+
[
174+
"stack", "deployment", "serverless", "product",
175+
"ece", "eck", "ess", "self",
176+
"elasticsearch", "observability", "security",
177+
"ecctl", "curator",
178+
"apm_agent_dotnet", "apm_agent_go", "apm_agent_java", "apm_agent_node", "apm_agent_python", "apm_agent_ruby", "apm_agent_rum",
179+
"edot_ios", "edot_android", "edot_dotnet", "edot_java", "edot_node", "edot_php", "edot_python"
180+
];
122181

123182
public bool Accepts(Type type) => type == typeof(ApplicableTo);
124183

@@ -154,9 +213,7 @@ public class ApplicableToConverter : IYamlTypeConverter
154213
if (TryGetApplicabilityOverTime(dictionary, "stack", diagnostics, out var stackAvailability))
155214
applicableTo.Stack = stackAvailability;
156215

157-
if (TryGetApplicabilityOverTime(dictionary, "product", diagnostics, out var productAvailability))
158-
applicableTo.Product = productAvailability;
159-
216+
AssignProduct(dictionary, applicableTo, diagnostics);
160217
AssignServerless(dictionary, applicableTo, diagnostics);
161218
AssignDeploymentType(dictionary, applicableTo, diagnostics);
162219

@@ -166,6 +223,9 @@ public class ApplicableToConverter : IYamlTypeConverter
166223
if (TryGetProjectApplicability(dictionary, diagnostics, out var serverless))
167224
applicableTo.Serverless = serverless;
168225

226+
if (TryGetProductApplicability(dictionary, diagnostics, out var product))
227+
applicableTo.ProductApplicability = product;
228+
169229
if (diagnostics.Count > 0)
170230
applicableTo.Diagnostics = new YamlDiagnosticsCollection(diagnostics);
171231
return applicableTo;
@@ -196,6 +256,48 @@ private static void AssignDeploymentType(Dictionary<object, object?> dictionary,
196256
}
197257
}
198258

259+
private static void AssignProduct(Dictionary<object, object?> dictionary, ApplicableTo applicableTo, List<(Severity, string)> diagnostics)
260+
{
261+
if (!dictionary.TryGetValue("product", out var productValue))
262+
return;
263+
264+
// This handles string, null, and empty string cases.
265+
if (productValue is not Dictionary<object, object?> productDictionary)
266+
{
267+
if (TryGetApplicabilityOverTime(dictionary, "product", diagnostics, out var productAvailability))
268+
applicableTo.Product = productAvailability;
269+
return;
270+
}
271+
272+
// Handle dictionary case
273+
if (TryGetProductApplicability(productDictionary, diagnostics, out var applicability))
274+
applicableTo.ProductApplicability = applicability;
275+
}
276+
277+
private static void AssignServerless(Dictionary<object, object?> dictionary, ApplicableTo applicableTo, List<(Severity, string)> diagnostics)
278+
{
279+
if (!dictionary.TryGetValue("serverless", out var serverless))
280+
return;
281+
282+
if (serverless is null || (serverless is string s && string.IsNullOrWhiteSpace(s)))
283+
applicableTo.Serverless = ServerlessProjectApplicability.All;
284+
else if (serverless is string serverlessString)
285+
{
286+
var av = AppliesCollection.TryParse(serverlessString, diagnostics, out var a) ? a : null;
287+
applicableTo.Serverless = new ServerlessProjectApplicability
288+
{
289+
Elasticsearch = av,
290+
Observability = av,
291+
Security = av
292+
};
293+
}
294+
else if (serverless is Dictionary<object, object?> serverlessDictionary)
295+
{
296+
if (TryGetProjectApplicability(serverlessDictionary, diagnostics, out var applicability))
297+
applicableTo.Serverless = applicability;
298+
}
299+
}
300+
199301
private static bool TryGetDeployment(Dictionary<object, object?> dictionary, List<(Severity, string)> diagnostics,
200302
[NotNullWhen(true)] out DeploymentApplicability? applicability)
201303
{
@@ -207,6 +309,7 @@ private static bool TryGetDeployment(Dictionary<object, object?> dictionary, Lis
207309
d.Ece = ece;
208310
assigned = true;
209311
}
312+
210313
if (TryGetApplicabilityOverTime(dictionary, "eck", diagnostics, out var eck))
211314
{
212315
d.Eck = eck;
@@ -234,30 +337,6 @@ private static bool TryGetDeployment(Dictionary<object, object?> dictionary, Lis
234337
return false;
235338
}
236339

237-
private static void AssignServerless(Dictionary<object, object?> dictionary, ApplicableTo applicableTo, List<(Severity, string)> diagnostics)
238-
{
239-
if (!dictionary.TryGetValue("serverless", out var serverless))
240-
return;
241-
242-
if (serverless is null || (serverless is string s && string.IsNullOrWhiteSpace(s)))
243-
applicableTo.Serverless = ServerlessProjectApplicability.All;
244-
else if (serverless is string serverlessString)
245-
{
246-
var av = AppliesCollection.TryParse(serverlessString, diagnostics, out var a) ? a : null;
247-
applicableTo.Serverless = new ServerlessProjectApplicability
248-
{
249-
Elasticsearch = av,
250-
Observability = av,
251-
Security = av
252-
};
253-
}
254-
else if (serverless is Dictionary<object, object?> serverlessDictionary)
255-
{
256-
if (TryGetProjectApplicability(serverlessDictionary, diagnostics, out var applicability))
257-
applicableTo.Serverless = applicability;
258-
}
259-
}
260-
261340
private static bool TryGetProjectApplicability(Dictionary<object, object?> dictionary,
262341
List<(Severity, string)> diagnostics,
263342
[NotNullWhen(true)] out ServerlessProjectApplicability? applicability)
@@ -270,6 +349,7 @@ private static bool TryGetProjectApplicability(Dictionary<object, object?> dicti
270349
serverlessAvailability.Elasticsearch = elasticsearch;
271350
assigned = true;
272351
}
352+
273353
if (TryGetApplicabilityOverTime(dictionary, "observability", diagnostics, out var observability))
274354
{
275355
serverlessAvailability.Observability = observability;
@@ -288,6 +368,115 @@ private static bool TryGetProjectApplicability(Dictionary<object, object?> dicti
288368
return true;
289369
}
290370

371+
private static bool TryGetProductApplicability(Dictionary<object, object?> dictionary,
372+
List<(Severity, string)> diagnostics,
373+
[NotNullWhen(true)] out ProductApplicability? applicability)
374+
{
375+
applicability = null;
376+
var productAvailability = new ProductApplicability();
377+
var assigned = false;
378+
if (TryGetApplicabilityOverTime(dictionary, "ecctl", diagnostics, out var ecctl))
379+
{
380+
productAvailability.Ecctl = ecctl;
381+
assigned = true;
382+
}
383+
384+
if (TryGetApplicabilityOverTime(dictionary, "curator", diagnostics, out var curator))
385+
{
386+
productAvailability.Curator = curator;
387+
assigned = true;
388+
}
389+
390+
if (TryGetApplicabilityOverTime(dictionary, "apm_agent_dotnet", diagnostics, out var apmAgentDotnet))
391+
{
392+
productAvailability.ApmAgentDotnet = apmAgentDotnet;
393+
assigned = true;
394+
}
395+
396+
if (TryGetApplicabilityOverTime(dictionary, "apm_agent_go", diagnostics, out var apmAgentGo))
397+
{
398+
productAvailability.ApmAgentGo = apmAgentGo;
399+
assigned = true;
400+
}
401+
402+
if (TryGetApplicabilityOverTime(dictionary, "apm_agent_java", diagnostics, out var apmAgentJava))
403+
{
404+
productAvailability.ApmAgentJava = apmAgentJava;
405+
assigned = true;
406+
}
407+
408+
if (TryGetApplicabilityOverTime(dictionary, "apm_agent_node", diagnostics, out var apmAgentNode))
409+
{
410+
productAvailability.ApmAgentNode = apmAgentNode;
411+
assigned = true;
412+
}
413+
414+
if (TryGetApplicabilityOverTime(dictionary, "apm_agent_python", diagnostics, out var apmAgentPython))
415+
{
416+
productAvailability.ApmAgentPython = apmAgentPython;
417+
assigned = true;
418+
}
419+
420+
if (TryGetApplicabilityOverTime(dictionary, "apm_agent_ruby", diagnostics, out var apmAgentRuby))
421+
{
422+
productAvailability.ApmAgentRuby = apmAgentRuby;
423+
assigned = true;
424+
}
425+
426+
if (TryGetApplicabilityOverTime(dictionary, "apm_agent_rum", diagnostics, out var apmAgentRum))
427+
{
428+
productAvailability.ApmAgentRum = apmAgentRum;
429+
assigned = true;
430+
}
431+
432+
if (TryGetApplicabilityOverTime(dictionary, "edot_ios", diagnostics, out var edotIos))
433+
{
434+
productAvailability.EdotIos = edotIos;
435+
assigned = true;
436+
}
437+
438+
if (TryGetApplicabilityOverTime(dictionary, "edot_android", diagnostics, out var edotAndroid))
439+
{
440+
productAvailability.EdotAndroid = edotAndroid;
441+
assigned = true;
442+
}
443+
444+
if (TryGetApplicabilityOverTime(dictionary, "edot_dotnet", diagnostics, out var edotDotnet))
445+
{
446+
productAvailability.EdotDotnet = edotDotnet;
447+
assigned = true;
448+
}
449+
450+
if (TryGetApplicabilityOverTime(dictionary, "edot_java", diagnostics, out var edotJava))
451+
{
452+
productAvailability.EdotJava = edotJava;
453+
assigned = true;
454+
}
455+
456+
if (TryGetApplicabilityOverTime(dictionary, "edot_node", diagnostics, out var edotNode))
457+
{
458+
productAvailability.EdotNode = edotNode;
459+
assigned = true;
460+
}
461+
462+
if (TryGetApplicabilityOverTime(dictionary, "edot_php", diagnostics, out var edotPhp))
463+
{
464+
productAvailability.EdotPhp = edotPhp;
465+
assigned = true;
466+
}
467+
468+
if (TryGetApplicabilityOverTime(dictionary, "edot_python", diagnostics, out var edotPython))
469+
{
470+
productAvailability.EdotPython = edotPython;
471+
assigned = true;
472+
}
473+
474+
if (!assigned)
475+
return false;
476+
applicability = productAvailability;
477+
return true;
478+
}
479+
291480
private static bool TryGetApplicabilityOverTime(Dictionary<object, object?> dictionary, string key, List<(Severity, string)> diagnostics,
292481
out AppliesCollection? availability)
293482
{

tests/authoring/Applicability/AppliesToDirective.fs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ serverless:
4141
security: ga 9.0.0
4242
elasticsearch: beta 9.1.0
4343
observability: discontinued 9.2.0
44+
apm_agent_dotnet: ga 9.0
45+
apm_agent_node: ga 10.0
4446
```
4547
"""
4648

@@ -54,6 +56,10 @@ serverless:
5456
Security=AppliesCollection.op_Explicit "ga 9.0.0",
5557
Elasticsearch=AppliesCollection.op_Explicit "beta 9.1.0",
5658
Observability=AppliesCollection.op_Explicit "discontinued 9.2.0"
59+
),
60+
ProductApplicability=ProductApplicability(
61+
ApmAgentDotnet=AppliesCollection.op_Explicit "ga 9.0",
62+
ApmAgentNode=AppliesCollection.op_Explicit "ga 10.0"
5763
)
5864
))
5965

tests/authoring/Applicability/AppliesToFrontMatter.fs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,8 @@ applies_to:
179179
elasticsearch: beta 9.1.0
180180
observability: discontinued 9.2.0
181181
product: preview 9.5, discontinued 9.7
182+
apm_agent_dotnet: ga 9.0
183+
ecctl: ga 10.0
182184
stack: ga 9.1
183185
"""
184186
[<Fact>]
@@ -196,7 +198,11 @@ applies_to:
196198
Observability=AppliesCollection.op_Explicit "discontinued 9.2.0"
197199
),
198200
Stack=AppliesCollection.op_Explicit "ga 9.1.0",
199-
Product=AppliesCollection.op_Explicit "preview 9.5, discontinued 9.7"
201+
Product=AppliesCollection.op_Explicit "preview 9.5, discontinued 9.7",
202+
ProductApplicability=ProductApplicability(
203+
ApmAgentDotnet=AppliesCollection.op_Explicit "ga 9.0",
204+
Ecctl=AppliesCollection.op_Explicit "ga 10.0"
205+
)
200206
))
201207

202208
type ``parses empty applies_to as null`` () =

0 commit comments

Comments
 (0)