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
10 changes: 6 additions & 4 deletions tools/cli/internal/apiversion/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@ type APIVersion struct {
}

const (
dateFormat = "2006-01-02"
StableStabilityLevel = "stable"
PreviewStabilityLevel = "preview"
dateFormat = "2006-01-02"
StableStabilityLevel = "stable"
PreviewStabilityLevel = "preview"
PrivatePreviewStabilityLevel = "private-preview"
)

var contentPattern = regexp.MustCompile(`application/vnd\.atlas\.((\d{4})-(\d{2})-(\d{2})|preview)\+(.+)`)
Expand Down Expand Up @@ -150,7 +151,8 @@ func (v *APIVersion) IsPreview() bool {
}

func IsPreviewSabilityLevel(value string) bool {
return strings.EqualFold(value, PreviewStabilityLevel)
// we also need string match given private preview versions like "private-preview-<name>"
return strings.EqualFold(value, PreviewStabilityLevel) || strings.Contains(value, PrivatePreviewStabilityLevel)
}

func IsStableSabilityLevel(value string) bool {
Expand Down
80 changes: 77 additions & 3 deletions tools/cli/internal/openapi/versions.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
package openapi

import (
"errors"
"fmt"
"sort"
"strings"

"github.com/getkin/kin-openapi/openapi3"
"github.com/mongodb/openapi/tools/cli/internal/apiversion"
Expand Down Expand Up @@ -53,11 +56,22 @@ func extractVersions(oas *openapi3.T) ([]string, error) {
if response.Value == nil || response.Value.Content == nil {
continue
}
for contentType := range response.Value.Content {
for contentType, contentTypeValue := range response.Value.Content {
version, err := apiversion.Parse(contentType)
if err == nil {
versions[version] = struct{}{}
if err != nil {
continue
}

if apiversion.IsPreviewSabilityLevel(version) {
// parse if it is public or not
version, err = getPreviewVersionName(contentTypeValue)
if err != nil {
fmt.Printf("failed to parse preview version name: %v\n", err)
continue
}
}

versions[version] = struct{}{}
}
}
}
Expand All @@ -66,6 +80,66 @@ func extractVersions(oas *openapi3.T) ([]string, error) {
return mapKeysToSortedSlice(versions), nil
}

func getPreviewVersionName(contentTypeValue *openapi3.MediaType) (name string, err error) {
public, name, err := parsePreviewExtensionData(contentTypeValue)
if err != nil {
return "", err
}

if public {
return "preview", nil
}

if !public && name != "" {
return "private-preview-" + name, nil
}

return "", errors.New("no preview extension found")
}

func parsePreviewExtensionData(contentTypeValue *openapi3.MediaType) (public bool, name string, err error) {
// Expected formats:
//
// "x-xgen-preview": {
// "name": "api-registry-private-preview"
// }
//
// "x-xgen-preview": {
// "public": "true"
// }

name = ""
public = false

if contentTypeValue.Extensions == nil {
return false, "", errors.New("no preview extension found")
}

previewExtension, ok := contentTypeValue.Extensions["x-xgen-preview"]
if !ok {
return false, "", errors.New("no preview extension found")
}

previewExtensionMap, ok := previewExtension.(map[string]any)
if !ok {
return false, "", errors.New("no preview extension found")
}

// Reading if it's public or not
publicV, ok := previewExtensionMap["public"].(string)
if ok {
public = strings.EqualFold(publicV, "true")
}

// Reading the name
nameV, ok := previewExtensionMap["name"].(string)
if ok {
name = nameV
}

return public, name, nil
}

// mapKeysToSortedSlice converts map keys to a sorted slice.
func mapKeysToSortedSlice(m map[string]struct{}) []string {
keys := make([]string, 0, len(m))
Expand Down
89 changes: 89 additions & 0 deletions tools/cli/internal/openapi/versions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@ func TestVersions(t *testing.T) {
assert.Equal(t, []string{"2023-01-01", "2023-02-01"}, versions)
}

func TestVersions_PrivatePreview(t *testing.T) {
versions, err := ExtractVersionsWithEnv(NewVersionedResponses(t), "dev")
require.NoError(t, err)
assert.ElementsMatch(t, []string{"2023-01-01", "2023-02-01", "private-preview-info-resource", "preview"}, versions)
}

func TestVersions_PublicPreview(t *testing.T) {
versions, err := ExtractVersionsWithEnv(NewVersionedResponses(t), "qa")
require.NoError(t, err)
assert.Equal(t, []string{"2023-01-01", "2023-02-01", "preview"}, versions)
}

func NewVersionedResponses(t *testing.T) *openapi3.T {
t.Helper()
inputPath := &openapi3.Paths{}
Expand Down Expand Up @@ -83,6 +95,83 @@ func NewVersionedResponses(t *testing.T) *openapi3.T {
},
})

extensionThree := map[string]any{
"x-xgen-version": "preview",
"x-xgen-preview": map[string]any{
"name": "info-resource",
},
}

responseThree := &openapi3.ResponseRef{
Value: &openapi3.Response{
Extensions: nil,
Content: map[string]*openapi3.MediaType{
"application/vnd.atlas.preview+json": {
Extensions: extensionThree,
},
},
},
}

hiddenEnvExtension := map[string]any{
"x-xgen-hidden-env": map[string]any{
"envs": "qa,prod",
},
}

responsesThree := &openapi3.Responses{}
responsesThree.Set("200", responseThree)

inputPath.Set("pathBase3", &openapi3.PathItem{
Extensions: hiddenEnvExtension,
Ref: "",
Summary: "pathBase3",
Description: "pathBase3Description",
Delete: &openapi3.Operation{
Tags: []string{"tag1"},
Responses: responsesThree,
},
})

extensionFour := map[string]any{
"x-xgen-version": "preview",
"x-xgen-preview": map[string]any{
"public": "true",
},
}

responseFour := &openapi3.ResponseRef{
Value: &openapi3.Response{
Extensions: nil,
Content: map[string]*openapi3.MediaType{
"application/vnd.atlas.preview+json": {
Extensions: extensionFour,
},
},
},
}

hiddenEnvExtensionTwo := map[string]any{
"x-xgen-hidden-env": map[string]any{
"envs": "prod",
},
}

responsesFour := &openapi3.Responses{}
responsesFour.Set("200", responseFour)

inputPath.Set("pathBase4", &openapi3.PathItem{
Extensions: hiddenEnvExtensionTwo,
Ref: "",
Summary: "pathBase4",
Description: "pathBase4Description",
Post: &openapi3.Operation{

Tags: []string{"tag1"},
Responses: responsesFour,
},
})

oas := &openapi3.T{
Paths: inputPath,
}
Expand Down
5 changes: 4 additions & 1 deletion tools/cli/test/data/base_spec_with_preview.json
Original file line number Diff line number Diff line change
Expand Up @@ -4038,7 +4038,10 @@
"$ref": "#/components/schemas/PaginatedAtlasGroupView"
},
"x-xgen-version": "preview",
"x-sunset": "2025-06-30"
"x-sunset": "2025-06-30",
"x-xgen-preview": {
"name": "new-feature"
}
}
}
},
Expand Down
Loading