1515package apiversion
1616
1717import (
18+ "errors"
1819 "fmt"
1920 "log"
2021 "regexp"
@@ -85,8 +86,8 @@ func WithDate(date time.Time) Option {
8586 }
8687}
8788
88- // WithContent returns an Option to generate a new APIVersion given the contentType.
89- func WithContent (contentType string ) Option {
89+ // withContent returns an Option to generate a new APIVersion given the contentType.
90+ func withContent (contentType string ) Option {
9091 return func (v * APIVersion ) error {
9192 version , err := Parse (contentType )
9293 if err != nil {
@@ -103,6 +104,22 @@ func WithContent(contentType string) Option {
103104 }
104105}
105106
107+ // WithFullContent returns an Option to generate a new APIVersion given the contentType and contentValue.
108+ func WithFullContent (contentType string , contentValue * openapi3.MediaType ) Option {
109+ return func (v * APIVersion ) error {
110+ if ! IsPreviewSabilityLevel (contentType ) {
111+ return withContent (contentType )(v )
112+ }
113+
114+ name , err := GetPreviewVersionName (contentValue )
115+ if err != nil {
116+ return err
117+ }
118+ // version will be based on the name, either 'preview' or 'private-preview-<name>'
119+ return WithVersion (name )(v )
120+ }
121+ }
122+
106123func DateFromVersion (version string ) (time.Time , error ) {
107124 if IsPreviewSabilityLevel (version ) {
108125 return time .Now (), nil
@@ -111,7 +128,7 @@ func DateFromVersion(version string) (time.Time, error) {
111128}
112129
113130func (v * APIVersion ) Equal (v2 * APIVersion ) bool {
114- return v .version == v2 .version
131+ return strings . EqualFold ( v .version , v2 .version )
115132}
116133
117134func (v * APIVersion ) GreaterThan (v2 * APIVersion ) bool {
@@ -150,9 +167,17 @@ func (v *APIVersion) IsPreview() bool {
150167 return IsPreviewSabilityLevel (v .version )
151168}
152169
170+ func (v * APIVersion ) IsPrivatePreview () bool {
171+ return strings .Contains (v .version , PrivatePreviewStabilityLevel )
172+ }
173+
174+ func (v * APIVersion ) IsPublicPreview () bool {
175+ return v .IsPreview () && ! v .IsPrivatePreview ()
176+ }
177+
153178func IsPreviewSabilityLevel (value string ) bool {
154179 // we also need string match given private preview versions like "private-preview-<name>"
155- return strings .EqualFold (value , PreviewStabilityLevel ) || strings .Contains (value , PrivatePreviewStabilityLevel )
180+ return strings .EqualFold (value , PreviewStabilityLevel ) || strings .Contains (value , PreviewStabilityLevel )
156181}
157182
158183func IsStableSabilityLevel (value string ) bool {
@@ -205,8 +230,8 @@ func FindLatestContentVersionMatched(op *openapi3.Operation, requestedVersion *A
205230 continue
206231 }
207232
208- for contentType := range response .Value .Content {
209- contentVersion , err := New (WithContent (contentType ))
233+ for contentType , contentValue := range response .Value .Content {
234+ contentVersion , err := New (WithFullContent (contentType , contentValue ))
210235 if err != nil {
211236 log .Printf ("Ignoring invalid content type: %q" , contentType )
212237 continue
@@ -216,7 +241,11 @@ func FindLatestContentVersionMatched(op *openapi3.Operation, requestedVersion *A
216241 return contentVersion
217242 }
218243
219- if contentVersion .ExactMatchOnly () || requestedVersion .ExactMatchOnly () {
244+ if requestedVersion .ExactMatchOnly () {
245+ // for private preview, we will need to match with "preview" and x-xgen-preview name extension
246+ if privatePreviewContentMatch (contentVersion , contentValue , requestedVersion ) {
247+ return contentVersion
248+ }
220249 continue
221250 }
222251
@@ -237,6 +266,21 @@ func FindLatestContentVersionMatched(op *openapi3.Operation, requestedVersion *A
237266 return latestVersionMatch
238267}
239268
269+ func privatePreviewContentMatch (contentVersion * APIVersion , contentValue * openapi3.MediaType , requestedVersion * APIVersion ) bool {
270+ log .Printf ("trying to match in case one of the versions is private preview" )
271+ if ! contentVersion .IsPrivatePreview () && ! requestedVersion .IsPrivatePreview () {
272+ return false
273+ }
274+
275+ name , err := GetPreviewVersionName (contentValue )
276+ if err != nil {
277+ log .Printf ("failed to parse preview version name for content=%v err=%v" , contentVersion .version , err )
278+ return false
279+ }
280+
281+ return strings .EqualFold (name , requestedVersion .version )
282+ }
283+
240284// Sort versions.
241285func Sort (versions []* APIVersion ) {
242286 for i := 0 ; i < len (versions ); i ++ {
@@ -247,3 +291,80 @@ func Sort(versions []*APIVersion) {
247291 }
248292 }
249293}
294+
295+ // GetPreviewVersionName returns the preview version name.
296+ func GetPreviewVersionName (contentTypeValue * openapi3.MediaType ) (name string , err error ) {
297+ public , name , err := parsePreviewExtensionData (contentTypeValue )
298+ if err != nil {
299+ return "" , err
300+ }
301+
302+ if public {
303+ return "preview" , nil
304+ }
305+
306+ if ! public && name != "" {
307+ return "private-preview-" + name , nil
308+ }
309+
310+ return "" , errors .New ("no preview extension found" )
311+ }
312+
313+ func parsePreviewExtensionData (contentTypeValue * openapi3.MediaType ) (public bool , name string , err error ) {
314+ // Expected formats:
315+ //
316+ // "x-xgen-preview": {
317+ // "name": "api-registry-private-preview"
318+ // }
319+ //
320+ // "x-xgen-preview": {
321+ // "public": "true"
322+ // }
323+
324+ name = ""
325+ public = false
326+
327+ if contentTypeValue .Extensions == nil {
328+ return false , "" , errors .New ("no preview extension found" )
329+ }
330+
331+ previewExtension , ok := contentTypeValue .Extensions ["x-xgen-preview" ]
332+ if ! ok {
333+ return false , "" , errors .New ("no preview extension found" )
334+ }
335+
336+ previewExtensionMap , ok := previewExtension .(map [string ]any )
337+ if ! ok {
338+ return false , "" , errors .New ("no preview extension found" )
339+ }
340+
341+ // Reading if it's public or not
342+ publicV , ok := previewExtensionMap ["public" ].(string )
343+ if ok {
344+ public = strings .EqualFold (publicV , "true" )
345+ }
346+
347+ // Reading the name
348+ nameV , ok := previewExtensionMap ["name" ].(string )
349+ if ok {
350+ name = nameV
351+ }
352+
353+ if err := validatePreviewExtensionData (name , publicV ); err != nil {
354+ return false , "" , err
355+ }
356+
357+ return public , name , nil
358+ }
359+
360+ func validatePreviewExtensionData (name , public string ) error {
361+ if name != "" && (public == "true" ) {
362+ return errors .New ("both name and public = true fields are set, only one is allowed" )
363+ }
364+
365+ if name == "" && public != "true" && public != "false" {
366+ return errors .New ("invalid value for 'public' field, only 'true' or 'false' are allowed" )
367+ }
368+
369+ return nil
370+ }
0 commit comments