diff --git a/pkg/services/featuremgmt/strcase/camel.go b/pkg/services/featuremgmt/strcase/camel.go new file mode 100644 index 00000000000..4e355df5975 --- /dev/null +++ b/pkg/services/featuremgmt/strcase/camel.go @@ -0,0 +1,92 @@ +// https://github.com/iancoleman/strcase/blob/master/camel.go +// https://github.com/iancoleman/strcase/blob/master/acronyms.go + +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 Ian Coleman + * Copyright (c) 2018 Ma_124, + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, Subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or Substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package strcase + +import ( + "strings" +) + +var uppercaseAcronym = map[string]string{ + "ID": "id", +} + +// ConfigureAcronym allows you to add additional words which will be considered acronyms +func ConfigureAcronym(key, val string) { + uppercaseAcronym[key] = val +} + +// Converts a string to CamelCase +func toCamelInitCase(s string, initCase bool) string { + s = strings.TrimSpace(s) + if s == "" { + return s + } + if a, ok := uppercaseAcronym[s]; ok { + s = a + } + + n := strings.Builder{} + n.Grow(len(s)) + capNext := initCase + for i, v := range []byte(s) { + vIsCap := v >= 'A' && v <= 'Z' + vIsLow := v >= 'a' && v <= 'z' + if capNext { + if vIsLow { + v += 'A' + v -= 'a' + } + } else if i == 0 { + if vIsCap { + v += 'a' + v -= 'A' + } + } + if vIsCap || vIsLow { + n.WriteByte(v) + capNext = false + } else if vIsNum := v >= '0' && v <= '9'; vIsNum { + n.WriteByte(v) + capNext = true + } else { + capNext = v == '_' || v == ' ' || v == '-' || v == '.' + } + } + return n.String() +} + +// ToCamel converts a string to CamelCase +func ToCamel(s string) string { + return toCamelInitCase(s, true) +} + +// ToLowerCamel converts a string to lowerCamelCase +func ToLowerCamel(s string) string { + return toCamelInitCase(s, false) +} diff --git a/pkg/services/featuremgmt/toggles_gen_test.go b/pkg/services/featuremgmt/toggles_gen_test.go index 27c3f05a753..8bc082537e7 100644 --- a/pkg/services/featuremgmt/toggles_gen_test.go +++ b/pkg/services/featuremgmt/toggles_gen_test.go @@ -12,20 +12,52 @@ import ( "unicode" "github.com/google/go-cmp/cmp" + "github.com/grafana/grafana/pkg/services/featuremgmt/strcase" + "github.com/stretchr/testify/require" ) func TestFeatureToggleFiles(t *testing.T) { - // Typescript files - verifyAndGenerateFile(t, - "../../../packages/grafana-data/src/types/featureToggles.gen.ts", - generateTypeScript(), - ) + legacyNames := map[string]bool{ + "httpclientprovider_azure_auth": true, + "service-accounts": true, + "database_metrics": true, + "live-config": true, + "live-pipeline": true, + "live-service-web-worker": true, + "accesscontrol-builtins": true, + "prometheus_azure_auth": true, + "disable_http_request_histogram": true, + } - // Golang files - verifyAndGenerateFile(t, - "toggles_gen.go", - generateRegistry(t), - ) + t.Run("verify files", func(t *testing.T) { + // Typescript files + verifyAndGenerateFile(t, + "../../../packages/grafana-data/src/types/featureToggles.gen.ts", + generateTypeScript(), + ) + + // Golang files + verifyAndGenerateFile(t, + "toggles_gen.go", + generateRegistry(t), + ) + }) + + t.Run("check feature naming convention", func(t *testing.T) { + invalidNames := make([]string, 0) + for _, f := range standardFeatureFlags { + if legacyNames[f.Name] { + continue + } + + if f.Name != strcase.ToLowerCamel(f.Name) { + invalidNames = append(invalidNames, f.Name) + } + } + + require.Empty(t, invalidNames, "%s feature names should be camel cased", invalidNames) + // acronyms can be configured as needed via `ConfigureAcronym` function from `./strcase/camel.go` + }) } func verifyAndGenerateFile(t *testing.T, fpath string, gen string) {