diff --git a/tools/cli/internal/apiversion/stabilitylevel.go b/tools/cli/internal/apiversion/stabilitylevel.go new file mode 100644 index 0000000000..e988b5cae8 --- /dev/null +++ b/tools/cli/internal/apiversion/stabilitylevel.go @@ -0,0 +1,59 @@ +// Copyright 2025 MongoDB Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package apiversion + +import ( + "fmt" + "strings" +) + +const ( + StableStabilityLevel = "stable" + PreviewStabilityLevel = "preview" + PrivatePreviewStabilityLevel = "private-preview" + PublicPreviewSabilityLevel = "public-preview" +) + +var supportedValues = []string{StableStabilityLevel, PublicPreviewSabilityLevel, PrivatePreviewStabilityLevel} + +// IsPreviewSabilityLevel checks if the version is a preview version, public or private. +func IsPreviewSabilityLevel(value string) bool { + return IsPrivatePreviewSabilityLevel(value) || IsPublicPreviewSabilityLevel(value) +} + +// IsPrivatePreviewSabilityLevel checks if the version is a private preview version. +func IsPrivatePreviewSabilityLevel(value string) bool { + return strings.Contains(strings.ToLower(value), PrivatePreviewStabilityLevel) +} + +// IsPublicPreviewSabilityLevel checks if the version is a public preview version. +func IsPublicPreviewSabilityLevel(value string) bool { + return strings.EqualFold(value, PublicPreviewSabilityLevel) || strings.EqualFold(value, PreviewStabilityLevel) +} + +// IsStableSabilityLevel checks if the version is a stable version. +func IsStableSabilityLevel(value string) bool { + return strings.EqualFold(value, StableStabilityLevel) +} + +// IsValidStabilityLevel checks if the version is a valid stability level. +// ValidateStabilityLevel checks if the version is a valid stability level. +func ValidateStabilityLevel(value string) error { + if IsStableSabilityLevel(value) || IsPreviewSabilityLevel(value) { + return nil + } + + return fmt.Errorf("invalid stability level value must be in %q, got %q", supportedValues, value) +} diff --git a/tools/cli/internal/apiversion/version.go b/tools/cli/internal/apiversion/version.go index cc28c648ea..3329697be9 100644 --- a/tools/cli/internal/apiversion/version.go +++ b/tools/cli/internal/apiversion/version.go @@ -32,10 +32,7 @@ type APIVersion struct { } const ( - dateFormat = "2006-01-02" - StableStabilityLevel = "stable" - PreviewStabilityLevel = "preview" - PrivatePreviewStabilityLevel = "private-preview" + dateFormat = "2006-01-02" ) var contentPattern = regexp.MustCompile(`application/vnd\.atlas\.((\d{4})-(\d{2})-(\d{2})|preview)\+(.+)`) @@ -107,7 +104,7 @@ func withContent(contentType string) Option { // WithFullContent returns an Option to generate a new APIVersion given the contentType and contentValue. func WithFullContent(contentType string, contentValue *openapi3.MediaType) Option { return func(v *APIVersion) error { - if !IsPreviewSabilityLevel(contentType) { + if !strings.Contains(contentType, PreviewStabilityLevel) { return withContent(contentType)(v) } @@ -175,15 +172,6 @@ func (v *APIVersion) IsPublicPreview() bool { return v.IsPreview() && !v.IsPrivatePreview() } -func IsPreviewSabilityLevel(value string) bool { - // we also need string match given private preview versions like "private-preview-" - return strings.EqualFold(value, PreviewStabilityLevel) || strings.Contains(value, PreviewStabilityLevel) -} - -func IsStableSabilityLevel(value string) bool { - return strings.EqualFold(value, StableStabilityLevel) -} - func FindMatchesFromContentType(contentType string) []string { return contentPattern.FindStringSubmatch(contentType) } diff --git a/tools/cli/internal/cli/usage/usage.go b/tools/cli/internal/cli/usage/usage.go index 43fc6a365b..8dc166a582 100644 --- a/tools/cli/internal/cli/usage/usage.go +++ b/tools/cli/internal/cli/usage/usage.go @@ -37,5 +37,5 @@ const ( SlackChannelID = "Slack Channel ID." From = "Date in the format YYYY-MM-DD that indicates the start of a date range" To = "Date in the format YYYY-MM-DD that indicates the end of a date range" - StabilityLevel = "Stability level related to the API Version. Valid values: [STABLE, PREVIEW]" + StabilityLevel = "Stability level related to the API Version. Valid values: [STABLE, PUBLIC-PREVIEW, PRIVATE-PREVIEW]" ) diff --git a/tools/cli/internal/cli/versions/versions.go b/tools/cli/internal/cli/versions/versions.go index 43f7e57355..d2d884500c 100644 --- a/tools/cli/internal/cli/versions/versions.go +++ b/tools/cli/internal/cli/versions/versions.go @@ -35,7 +35,7 @@ type Opts struct { outputPath string format string env string - stabilityLevel string + stabilityLevel []string } func (o *Opts) Run() error { @@ -70,21 +70,30 @@ func (o *Opts) Run() error { } func (o *Opts) filterStabilityLevelVersions(apiVersions []string) []string { - if o.stabilityLevel == "" || apiVersions == nil { + if len(o.stabilityLevel) == 0 || apiVersions == nil { return apiVersions } var out []string for _, v := range apiVersions { - if (apiversion.IsStableSabilityLevel(o.stabilityLevel)) && !apiversion.IsPreviewSabilityLevel(v) { - out = append(out, v) - } - - if (apiversion.IsPreviewSabilityLevel(o.stabilityLevel)) && apiversion.IsPreviewSabilityLevel(v) { - out = append(out, v) + for _, stabilityLevel := range o.stabilityLevel { + if (apiversion.IsStableSabilityLevel(stabilityLevel)) && !apiversion.IsPreviewSabilityLevel(v) { + out = append(out, v) + } + + if (apiversion.IsPrivatePreviewSabilityLevel(stabilityLevel)) && apiversion.IsPrivatePreviewSabilityLevel(v) { + out = append(out, v) + } + + if (apiversion.IsPublicPreviewSabilityLevel(stabilityLevel)) && apiversion.IsPublicPreviewSabilityLevel(v) { + out = append(out, v) + } } } + if len(out) == 0 { + return []string{} + } return out } @@ -112,9 +121,12 @@ func (o *Opts) versionsAsBytes(versions []string) ([]byte, error) { } func (o *Opts) PreRunE(_ []string) error { - o.stabilityLevel = strings.ToLower(o.stabilityLevel) - if o.stabilityLevel != "" && o.stabilityLevel != apiversion.PreviewStabilityLevel && o.stabilityLevel != apiversion.StableStabilityLevel { - return fmt.Errorf("stability level must be %q or %q, got %q", apiversion.PreviewStabilityLevel, apiversion.StableStabilityLevel, o.stabilityLevel) + for i, v := range o.stabilityLevel { + o.stabilityLevel[i] = strings.ToLower(v) + + if err := apiversion.ValidateStabilityLevel(o.stabilityLevel[i]); err != nil { + return err + } } if o.basePath == "" { @@ -154,7 +166,7 @@ func Builder() *cobra.Command { cmd.Flags().StringVarP(&opts.basePath, flag.Spec, flag.SpecShort, "", usage.Spec) cmd.Flags().StringVar(&opts.env, flag.Environment, "", usage.Environment) - cmd.Flags().StringVarP(&opts.stabilityLevel, flag.StabilityLevel, flag.StabilityLevelShort, "", usage.StabilityLevel) + cmd.Flags().StringArrayVar(&opts.stabilityLevel, flag.StabilityLevel, nil, usage.StabilityLevel) cmd.Flags().StringVarP(&opts.outputPath, flag.Output, flag.OutputShort, "", usage.Output) cmd.Flags().StringVarP(&opts.format, flag.Format, flag.FormatShort, "json", usage.Format) return cmd diff --git a/tools/cli/internal/cli/versions/versions_test.go b/tools/cli/internal/cli/versions/versions_test.go index df5c27a288..48fae173ea 100644 --- a/tools/cli/internal/cli/versions/versions_test.go +++ b/tools/cli/internal/cli/versions/versions_test.go @@ -82,7 +82,7 @@ func TestVersion_RunStabilityLevelPreviewAndPrivatePreview(t *testing.T) { outputPath: "foas.json", fs: fs, env: "staging", - stabilityLevel: "PREVIEW", + stabilityLevel: []string{"private-preview"}, } require.NoError(t, opts.Run()) @@ -102,7 +102,7 @@ func TestVersion_PreviewAndPublicPreview(t *testing.T) { outputPath: "foas.json", fs: fs, env: "staging", - stabilityLevel: "PREVIEW", + stabilityLevel: []string{"public-preview"}, } require.NoError(t, opts.Run()) @@ -122,7 +122,7 @@ func TestVersion_RunStabilityLevelStable(t *testing.T) { outputPath: "foas.json", fs: fs, env: "staging", - stabilityLevel: "STABLE", + stabilityLevel: []string{"STABLE"}, } require.NoError(t, opts.Run()) @@ -189,18 +189,18 @@ func TestVersion_PreRun(t *testing.T) { basePath: "base", outputPath: "output.yaml", format: "yaml", - stabilityLevel: "invalid", + stabilityLevel: []string{"invalid"}, } err := opts.PreRunE(nil) require.Error(t, err) - assert.Contains(t, err.Error(), "stability level must be") + assert.Contains(t, err.Error(), "invalid stability level value m") }) t.Run("ValidStabilityLevelPreview", func(t *testing.T) { opts := &Opts{ basePath: "base", outputPath: "output.yaml", format: "yaml", - stabilityLevel: "preview", + stabilityLevel: []string{"PREVIEW"}, } err := opts.PreRunE(nil) require.NoError(t, err) @@ -211,7 +211,7 @@ func TestVersion_PreRun(t *testing.T) { basePath: "base", outputPath: "output.yaml", format: "yaml", - stabilityLevel: "PREVIEW", + stabilityLevel: []string{"PREVIEW"}, } err := opts.PreRunE(nil) require.NoError(t, err)