Skip to content

Commit 9d957e8

Browse files
committed
fix(osgen): treat ISM/KNN/LTR/ML/PPL/SM/UBI/WLM as acronyms
Go identifier convention requires acronyms to be all-uppercase (the generator already does this for API, HTTP, JSON, URL, etc.). The OpenSearch plugin-namespace acronyms were missing from the table, so the generated surface emitted IsmPolicy, KnnStats, MlModel, SmPolicy, and similar mis-cased identifiers. Add the missing entries to the acronyms map so titleSegment expands them: ISM Index State Management KNN k-Nearest Neighbors LTR Learning to Rank ML Machine Learning PPL Piped Processing Language SM Snapshot Management UBI User Behavior Insights WLM Workload Management Matching is whole-segment only (segments are split on '.' and '_' before lookup), so words like "smile" are unaffected; a regression test pins this. The rename takes effect on the next code generation, which rewrites the affected *_gen.go identifiers (e.g. IsmPolicy -> ISMPolicy) in one pass. Closes opensearch-project#863 Signed-off-by: Sean Chittenden <sean.chittenden@crowdstrike.com>
1 parent 300d1ae commit 9d957e8

2 files changed

Lines changed: 80 additions & 1 deletion

File tree

cmd/osgen/naming.go

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"fmt"
1111
"go/token"
1212
"go/types"
13+
"sort"
1314
"strings"
1415
)
1516

@@ -45,12 +46,67 @@ var idiomaticAbbreviations = []struct {
4546
// s, matching each pattern at PascalCase boundaries (followed by
4647
// uppercase, or end-of-string for entries with tailUpperOnly=false).
4748
func applyIdiomaticAbbreviations(s string) string {
49+
// Normalize embedded acronyms first. A single spec token like
50+
// "IsmTemplate" pascal-cases to "IsmTemplate" (titleSegment only expands
51+
// whole, separately-delimited segments), leaving the acronym in mixed
52+
// case. Canonicalizing "Ism" -> "ISM" here -- at PascalCase boundaries --
53+
// keeps acronym casing consistent wherever it appears in an identifier
54+
// (prefix or local part), which also lets deStutterPrefix match.
55+
for _, a := range acronymBoundaryReplacements() {
56+
s = replaceAtPascalBoundary(s, a.from, a.to, a.tailUpperOnly)
57+
}
4858
for _, a := range idiomaticAbbreviations {
4959
s = replaceAtPascalBoundary(s, a.from, a.to, a.tailUpperOnly)
5060
}
5161
return s
5262
}
5363

64+
// acronymBoundaryReplacements derives, from the acronyms table, the
65+
// substitutions that canonicalize a title-cased embedded acronym (e.g. "Ism",
66+
// "Knn") to its idiomatic all-caps form (e.g. "ISM", "KNN"). Two-letter and
67+
// longer acronyms qualify; single-letter "acronyms" would over-match, so they
68+
// are skipped. Each is applied at PascalCase boundaries (followed by an
69+
// uppercase letter or end-of-string), so "Ismael"-style words are untouched.
70+
//
71+
//nolint:gochecknoglobals // derived once from the acronyms table
72+
var acronymBoundaryReplacementsCache []struct {
73+
from, to string
74+
tailUpperOnly bool
75+
}
76+
77+
func acronymBoundaryReplacements() []struct {
78+
from, to string
79+
tailUpperOnly bool
80+
} {
81+
if acronymBoundaryReplacementsCache != nil {
82+
return acronymBoundaryReplacementsCache
83+
}
84+
for lower, upper := range acronyms {
85+
if len(lower) < 2 {
86+
continue
87+
}
88+
title := strings.ToUpper(lower[:1]) + lower[1:]
89+
if title == upper {
90+
continue // already idiomatic (e.g. all-lowercase has no title form to fix)
91+
}
92+
acronymBoundaryReplacementsCache = append(acronymBoundaryReplacementsCache, struct {
93+
from, to string
94+
tailUpperOnly bool
95+
}{from: title, to: upper})
96+
}
97+
// Deterministic order: map iteration is random, and codegen output must be
98+
// stable across runs. Longest-from first so multi-token acronyms can't be
99+
// partially shadowed by a shorter one.
100+
sort.Slice(acronymBoundaryReplacementsCache, func(i, j int) bool {
101+
a, b := acronymBoundaryReplacementsCache[i].from, acronymBoundaryReplacementsCache[j].from
102+
if len(a) != len(b) {
103+
return len(a) > len(b)
104+
}
105+
return a < b
106+
})
107+
return acronymBoundaryReplacementsCache
108+
}
109+
54110
// replaceAtPascalBoundary replaces every occurrence of old in s with
55111
// next, but only when old is followed by an uppercase letter -- or
56112
// end-of-string if tailUpperOnly is false. Lowercase-suffix variants
@@ -89,16 +145,24 @@ var acronyms = map[string]string{
89145
"https": "HTTPS",
90146
"id": "ID",
91147
"ip": "IP",
148+
"ism": "ISM", // Index State Management
92149
"json": "JSON",
93-
"pit": "PIT",
150+
"knn": "KNN", // k-Nearest Neighbors
151+
"ltr": "LTR", // Learning to Rank
152+
"ml": "ML", // Machine Learning
153+
"pit": "PIT", // Point In Time
154+
"ppl": "PPL", // Piped Processing Language
155+
"sm": "SM", // Snapshot Management
94156
"sql": "SQL",
95157
"ssl": "SSL",
96158
"tcp": "TCP",
97159
"tls": "TLS",
98160
"ttl": "TTL",
161+
"ubi": "UBI", // User Behavior Insights
99162
"uri": "URI",
100163
"url": "URL",
101164
"uuid": "UUID",
165+
"wlm": "WLM", // Workload Management
102166
"xml": "XML",
103167
}
104168

cmd/osgen/naming_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,15 @@ func TestTitleSegment(t *testing.T) {
3333
{name: "ssl acronym", input: "ssl", want: "SSL"},
3434
{name: "api acronym", input: "api", want: "API"},
3535
{name: "json acronym", input: "json", want: "JSON"},
36+
{name: "ism acronym", input: "ism", want: "ISM"},
37+
{name: "knn acronym", input: "knn", want: "KNN"},
38+
{name: "ltr acronym", input: "ltr", want: "LTR"},
39+
{name: "ml acronym", input: "ml", want: "ML"},
40+
{name: "ppl acronym", input: "ppl", want: "PPL"},
41+
{name: "sm acronym", input: "sm", want: "SM"},
42+
{name: "ubi acronym", input: "ubi", want: "UBI"},
43+
{name: "wlm acronym", input: "wlm", want: "WLM"},
44+
{name: "whole-segment only, not substring", input: "smile", want: "Smile"},
3645
{name: "empty", input: "", want: ""},
3746
{name: "mixed case id", input: "ID", want: "ID"},
3847
{name: "mixed case uuid", input: "UUID", want: "UUID"},
@@ -276,6 +285,12 @@ func TestSchemaTypeName(t *testing.T) {
276285
{name: "group._common cluster", schemaKey: "cluster._common___ComponentTemplate", want: "ClusterComponentTemplate"},
277286
{name: "acronyms", schemaKey: "security._common___SSLInfo", want: "SecuritySSLInfo"},
278287
{name: "sql plugin", schemaKey: "sql._common___SQLQuery", want: "SQLQuery"},
288+
{name: "ism plugin acronym", schemaKey: "ism._common___Policy", want: "ISMPolicy"},
289+
{name: "knn plugin acronym", schemaKey: "knn._common___Stats", want: "KNNStats"},
290+
// Embedded acronym in the local part must normalize and de-stutter:
291+
// "IsmTemplate" -> "ISMTemplate", not "ISMIsmTemplate".
292+
{name: "ism embedded acronym de-stutters", schemaKey: "ism._common___IsmTemplate", want: "ISMTemplate"},
293+
{name: "knn embedded acronym de-stutters", schemaKey: "knn._common___KnnMethod", want: "KNNMethod"},
279294
{name: "de-stutter empty result kept", schemaKey: "cluster.health___Health", want: "ClusterHealthHealth"},
280295

281296
// Idiomatic abbreviations: M-prefix initialisms, compound nouns,

0 commit comments

Comments
 (0)