Skip to content

Commit 2d8213a

Browse files
CLOUDP-311374: Update foascli version to list 'upcoming' apis
1 parent f4ba231 commit 2d8213a

File tree

6 files changed

+144
-8
lines changed

6 files changed

+144
-8
lines changed

tools/cli/internal/apiversion/stabilitylevel.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
const (
2323
StableStabilityLevel = "stable"
2424
PreviewStabilityLevel = "preview"
25+
UpcomingStabilityLevel = "upcoming"
2526
PrivatePreviewStabilityLevel = "private-preview"
2627
PublicPreviewStabilityLevel = "public-preview"
2728
)
@@ -30,7 +31,8 @@ var supportedValues = []string{StableStabilityLevel, PublicPreviewStabilityLevel
3031

3132
// IsPreviewStabilityLevel checks if the version is a preview version, public or private.
3233
func IsPreviewStabilityLevel(value string) bool {
33-
return IsPrivatePreviewStabilityLevel(value) || IsPublicPreviewStabilityLevel(value)
34+
lowerCaseValue := strings.ToLower(value)
35+
return IsPrivatePreviewStabilityLevel(lowerCaseValue) || IsPublicPreviewStabilityLevel(lowerCaseValue)
3436
}
3537

3638
// IsPrivatePreviewStabilityLevel checks if the version is a private preview version.
@@ -40,18 +42,24 @@ func IsPrivatePreviewStabilityLevel(value string) bool {
4042

4143
// IsPublicPreviewStabilityLevel checks if the version is a public preview version.
4244
func IsPublicPreviewStabilityLevel(value string) bool {
43-
return strings.EqualFold(value, PublicPreviewStabilityLevel) || strings.EqualFold(value, PreviewStabilityLevel)
45+
lowerCaseValue := strings.ToLower(value)
46+
return strings.EqualFold(lowerCaseValue, PublicPreviewStabilityLevel) || strings.EqualFold(lowerCaseValue, PreviewStabilityLevel)
4447
}
4548

4649
// IsStableStabilityLevel checks if the version is a stable version.
4750
func IsStableStabilityLevel(value string) bool {
48-
return strings.EqualFold(value, StableStabilityLevel)
51+
return strings.EqualFold(strings.ToLower(value), StableStabilityLevel)
52+
}
53+
54+
// IsUpcomingStabilityLevel checks if the version is an "upcoming" version.
55+
func IsUpcomingStabilityLevel(value string) bool {
56+
return strings.Contains(strings.ToLower(value), UpcomingStabilityLevel)
4957
}
5058

51-
// IsValidStabilityLevel checks if the version is a valid stability level.
5259
// ValidateStabilityLevel checks if the version is a valid stability level.
5360
func ValidateStabilityLevel(value string) error {
54-
if IsStableStabilityLevel(value) || IsPreviewStabilityLevel(value) {
61+
lowerCaseValue := strings.ToLower(value)
62+
if IsStableStabilityLevel(lowerCaseValue) || IsPreviewStabilityLevel(lowerCaseValue) {
5563
return nil
5664
}
5765

tools/cli/internal/apiversion/version.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,12 @@ const (
3636
previewDate = "3000-01-01"
3737
)
3838

39-
var contentPattern = regexp.MustCompile(`application/vnd\.atlas\.((\d{4})-(\d{2})-(\d{2})|preview)\+(.+)`)
39+
// This regex will match:
40+
// 1. application/vnd.atlas.2025-01-01.json
41+
// 2. application/vnd.atlas.preview.json
42+
// 3. application/vnd.atlas.2025-01-01.upcoming.json
43+
// 4. application/vnd.atlas.2025-01-01.upcoming.yaml
44+
var contentPattern = regexp.MustCompile(`application/vnd\.atlas\.((\d{4})-(\d{2})-(\d{2})(\.upcoming)?|preview)\+(.+)`)
4045

4146
// Option is a function that sets a value on the APIVersion.
4247
type Option func(v *APIVersion) error

tools/cli/internal/cli/versions/versions.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ func (o *Opts) filterStabilityLevelVersions(apiVersions []string) []string {
8888
if (apiversion.IsPublicPreviewStabilityLevel(stabilityLevel)) && apiversion.IsPublicPreviewStabilityLevel(v) {
8989
out = append(out, v)
9090
}
91+
92+
if apiversion.IsUpcomingStabilityLevel(stabilityLevel) && apiversion.IsUpcomingStabilityLevel(v) {
93+
out = append(out, v)
94+
}
9195
}
9296
}
9397

@@ -145,7 +149,7 @@ func (o *Opts) PreRunE(_ []string) error {
145149
}
146150

147151
// Builder builds the versions command with the following signature:
148-
// versions -s oas --env dev|qa|staging|prod -stability-level STABLE|PREVIEW.
152+
// versions -s oas --env dev|qa|staging|prod -stability-level STABLE|UPCOMING|PREVIEW.
149153
func Builder() *cobra.Command {
150154
opts := &Opts{
151155
fs: afero.NewOsFs(),

tools/cli/internal/openapi/filter/info.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
14+
1415
package filter
1516

1617
import (

tools/cli/internal/openapi/versions.go

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ package openapi
1616

1717
import (
1818
"fmt"
19+
"log"
1920
"sort"
21+
"strings"
2022

2123
"github.com/getkin/kin-openapi/openapi3"
2224
"github.com/mongodb/openapi/tools/cli/internal/apiversion"
@@ -75,7 +77,34 @@ func extractVersions(oas *openapi3.T) ([]string, error) {
7577
}
7678
}
7779

78-
return mapKeysToSortedSlice(versions), nil
80+
return mapKeysToSortedSlice(filterUpcomingVersions(versions)), nil
81+
}
82+
83+
// filterUpcomingVersions removes the "YYYY.MM.DD.upcoming" apis version if the
84+
// related stable api YYYY.MM.DD is available.
85+
func filterUpcomingVersions(apiVersions map[string]struct{}) map[string]struct{} {
86+
apiVersionSet := make(map[string]struct{}, len(apiVersions))
87+
upcomingVersions := make([]string, 0, len(apiVersions))
88+
89+
for v := range apiVersions {
90+
if apiversion.IsUpcomingStabilityLevel(strings.ToLower(v)) {
91+
upcomingVersions = append(upcomingVersions, v)
92+
continue
93+
}
94+
apiVersionSet[v] = struct{}{}
95+
}
96+
97+
for _, upcomingVersion := range upcomingVersions {
98+
date := strings.ReplaceAll(upcomingVersion, ".upcoming", "")
99+
if _, ok := apiVersionSet[date]; !ok {
100+
apiVersionSet[upcomingVersion] = struct{}{}
101+
} else {
102+
log.Printf("The API Version %[1]q was removed as the Stable API Version %[2]q was detected."+
103+
" Please, remove %[1]q from the OpenAPI Spec.\n", upcomingVersion, date)
104+
}
105+
}
106+
107+
return apiVersionSet
79108
}
80109

81110
// mapKeysToSortedSlice converts map keys to a sorted slice.

tools/cli/internal/openapi/versions_test.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,18 @@ func TestVersions_PublicPreview(t *testing.T) {
3939
assert.Equal(t, []string{"2023-01-01", "2023-02-01", "preview"}, versions)
4040
}
4141

42+
func TestVersions_UpcomingAPI(t *testing.T) {
43+
versions, err := ExtractVersionsWithEnv(NewVersionedResponsesWithUpcoming(t), "")
44+
require.NoError(t, err)
45+
assert.Equal(t, []string{"2023-01-01", "2025-09-22.upcoming"}, versions)
46+
}
47+
48+
func TestVersions_UpcomingAndStableAPI(t *testing.T) {
49+
versions, err := ExtractVersionsWithEnv(NewVersionedResponsesWithUpcomingAndStable(t), "")
50+
require.NoError(t, err)
51+
assert.Equal(t, []string{"2023-01-01", "2025-09-22"}, versions)
52+
}
53+
4254
func TestVersions_InvalidPreviewData(t *testing.T) {
4355
r := NewVersionedResponses(t)
4456
// override the extension so something invalid like "public": true
@@ -194,3 +206,80 @@ func NewVersionedResponses(t *testing.T) *openapi3.T {
194206

195207
return oas
196208
}
209+
210+
func NewVersionedResponsesWithUpcoming(t *testing.T) *openapi3.T {
211+
t.Helper()
212+
inputPath := &openapi3.Paths{}
213+
214+
extension := map[string]any{
215+
"x-xgen-version": "2023-01-01",
216+
}
217+
response := &openapi3.ResponseRef{
218+
Value: &openapi3.Response{
219+
Extensions: extension,
220+
Content: map[string]*openapi3.MediaType{
221+
"application/vnd.atlas.2023-01-01+json": {},
222+
"application/vnd.atlas.2025-09-22.upcoming+json": {},
223+
},
224+
},
225+
}
226+
227+
responses := &openapi3.Responses{}
228+
responses.Set("200", response)
229+
230+
inputPath.Set("pathBase1", &openapi3.PathItem{
231+
Extensions: nil,
232+
Ref: "",
233+
Summary: "pathBase1",
234+
Description: "pathBase1Description",
235+
Get: &openapi3.Operation{
236+
Tags: []string{"tag1"},
237+
Responses: responses,
238+
},
239+
})
240+
241+
oas := &openapi3.T{
242+
Paths: inputPath,
243+
}
244+
245+
return oas
246+
}
247+
248+
func NewVersionedResponsesWithUpcomingAndStable(t *testing.T) *openapi3.T {
249+
t.Helper()
250+
inputPath := &openapi3.Paths{}
251+
252+
extension := map[string]any{
253+
"x-xgen-version": "2023-01-01",
254+
}
255+
response := &openapi3.ResponseRef{
256+
Value: &openapi3.Response{
257+
Extensions: extension,
258+
Content: map[string]*openapi3.MediaType{
259+
"application/vnd.atlas.2023-01-01+json": {},
260+
"application/vnd.atlas.2025-09-22.upcoming+json": {},
261+
"application/vnd.atlas.2025-09-22+json": {},
262+
},
263+
},
264+
}
265+
266+
responses := &openapi3.Responses{}
267+
responses.Set("200", response)
268+
269+
inputPath.Set("pathBase1", &openapi3.PathItem{
270+
Extensions: nil,
271+
Ref: "",
272+
Summary: "pathBase1",
273+
Description: "pathBase1Description",
274+
Get: &openapi3.Operation{
275+
Tags: []string{"tag1"},
276+
Responses: responses,
277+
},
278+
})
279+
280+
oas := &openapi3.T{
281+
Paths: inputPath,
282+
}
283+
284+
return oas
285+
}

0 commit comments

Comments
 (0)