Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Signed-off-by: Pierre Fenoll <[email protected]>
  • Loading branch information
fenollp committed Mar 14, 2023
1 parent 2f56aaf commit dabf0fe
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 18 deletions.
110 changes: 105 additions & 5 deletions openapi3/callback.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package openapi3

import (
"context"
"encoding/json"
"fmt"
"sort"
"strings"

"github.com/go-openapi/jsonpointer"
)
Expand All @@ -27,19 +29,117 @@ func (c Callbacks) JSONLookup(token string) (interface{}, error) {

// Callback is specified by OpenAPI/Swagger standard version 3.
// See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#callback-object
type Callback map[string]*PathItem
type Callback struct {
Extensions map[string]interface{} `json:"-" yaml:"-"`

Callback map[string]*PathItem // json:???
}

// type Callback struct {
// om *orderedmap.OrderedMap[string, *PathItem]
// }

// // MarshalJSON returns the JSON encoding of Callback.
// func (callback *Callback) MarshalJSON() ([]byte, error) {
// return callback.om.MarshalJSON()
// }

// // UnmarshalJSON sets Callback to a copy of data.
// func (callback *Callback) UnmarshalJSON(data []byte) error {
// return json.Unmarshal(data, &callback.om)
// }

// func (callback *Callback) Value(key string) *PathItem {
// // if callback == nil || callback.om == nil {
// // return nil
// // }
// return callback.om.Value(key)
// }

// func (callback *Callback) Set(key string, value *PathItem) {
// // if callback != nil || callback.om != nil {
// _, _ = callback.om.Set(key, value)
// // }
// }

// func (callback *Callback) Len() int {
// if callback == nil || callback.om == nil {
// return 0
// }
// return callback.om.Len()
// }

// func (callback *Callback) Iter() *callbackKV {
// if callback == nil || callback.om == nil {
// return nil
// }
// return (*callbackKV)(callback.om.Oldest())
// }

// type callbackKV orderedmap.Pair[string, *PathItem] //FIXME: pub?

// func (pair *callbackKV) Next() *callbackKV {
// ompair := (*orderedmap.Pair[string, *PathItem])(pair)
// return (*callbackKV)(ompair.Next())
// }

// MarshalJSON returns the JSON encoding of Callback.
func (callback Callback) MarshalJSON() ([]byte, error) {
m := make(map[string]interface{}, len(callback.Callback)+len(callback.Extensions))
for k, v := range callback.Extensions {
m[k] = v
}
for k, v := range callback.Callback {
m[k] = v
}
return json.Marshal(m)
}

// UnmarshalJSON sets Callback to a copy of data.
func (callback *Callback) UnmarshalJSON(data []byte) error {
var m map[string]interface{}
if err := json.Unmarshal(data, &m); err != nil {
return err
}

ks := make([]string, 0, len(m))
for k := range m {
ks = append(ks, k)
}
sort.Strings(ks)

x := Callback{
Extensions: make(map[string]interface{}),
Callback: make(map[string]*PathItem, len(m)),
}

for _, k := range ks {
v := m[k]
if !strings.HasPrefix(k, "x-") {
if v, ok := v.(*PathItem); ok {
x.Callback[k] = v
} else {
panic(fmt.Sprintf(">>> %T %+v", v, v))
}
} else {
x.Extensions[k] = v
}
}
*callback = x
return nil
}

// Validate returns an error if Callback does not comply with the OpenAPI spec.
func (callback Callback) Validate(ctx context.Context, opts ...ValidationOption) error {
func (callback *Callback) Validate(ctx context.Context, opts ...ValidationOption) error {
ctx = WithValidationOptions(ctx, opts...)

keys := make([]string, 0, len(callback))
for key := range callback {
keys := make([]string, 0, len(callback.Callback))
for key := range callback.Callback {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
v := callback[key]
v := callback.Callback[key]
if err := v.Validate(ctx); err != nil {
return err
}
Expand Down
6 changes: 4 additions & 2 deletions openapi3/internalize_refs.go
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,8 @@ func (doc *T) derefPaths(paths map[string]*PathItem, refNameResolver RefNameReso
for _, cb := range op.Callbacks {
isExternal := doc.addCallbackToSpec(cb, refNameResolver, parentIsExternal)
if cb.Value != nil {
doc.derefPaths(*cb.Value, refNameResolver, parentIsExternal || isExternal)
cbValue := (*cb.Value).Callback
doc.derefPaths(cbValue, refNameResolver, parentIsExternal || isExternal)
}
}
doc.derefResponses(op.Responses, refNameResolver, parentIsExternal)
Expand Down Expand Up @@ -425,7 +426,8 @@ func (doc *T) InternalizeRefs(ctx context.Context, refNameResolver func(ref stri
isExternal := doc.addCallbackToSpec(cb, refNameResolver, false)
if cb != nil && cb.Value != nil {
cb.Ref = "" // always dereference the top level
doc.derefPaths(*cb.Value, refNameResolver, isExternal)
cbValue := (*cb.Value).Callback
doc.derefPaths(cbValue, refNameResolver, isExternal)
}
}
}
Expand Down
20 changes: 12 additions & 8 deletions openapi3/issue301_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,17 @@ func TestIssue301(t *testing.T) {
err = doc.Validate(sl.Context)
require.NoError(t, err)

transCallbacks := doc.Paths["/trans"].Post.Callbacks["transactionCallback"].Value
require.Equal(t, "object", (*transCallbacks)["http://notificationServer.com?transactionId={$request.body#/id}&email={$request.body#/email}"].Post.RequestBody.
Value.Content["application/json"].Schema.
Value.Type)
require.Equal(t, "object", doc.Paths["/trans"].
Post.Callbacks["transactionCallback"].Value.
Callback["http://notificationServer.com?transactionId={$request.body#/id}&email={$request.body#/email}"].
Post.RequestBody.Value.
Content["application/json"].Schema.Value.
Type)

otherCallbacks := doc.Paths["/other"].Post.Callbacks["myEvent"].Value
require.Equal(t, "boolean", (*otherCallbacks)["{$request.query.queryUrl}"].Post.RequestBody.
Value.Content["application/json"].Schema.
Value.Type)
require.Equal(t, "boolean", doc.Paths["/other"].
Post.Callbacks["myEvent"].Value.
Callback["{$request.query.queryUrl}"].
Post.RequestBody.Value.
Content["application/json"].Schema.Value.
Type)
}
2 changes: 1 addition & 1 deletion openapi3/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -910,7 +910,7 @@ func (loader *Loader) resolveCallbackRef(doc *T, component *CallbackRef, documen
return nil
}

for _, pathItem := range *value {
for _, pathItem := range (*value).Callback {
if err = loader.resolvePathItemRef(doc, pathItem, documentPath); err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion openapi3/paths.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (

// Paths is specified by OpenAPI/Swagger standard version 3.
// See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#paths-object
type Paths map[string]*PathItem
type Paths map[string]*PathItem //

// Validate returns an error if Paths does not comply with the OpenAPI spec.
func (paths Paths) Validate(ctx context.Context, opts ...ValidationOption) error {
Expand Down
4 changes: 3 additions & 1 deletion openapi3/response.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (

// Responses is specified by OpenAPI/Swagger 3.0 standard.
// See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#responses-object
type Responses map[string]*ResponseRef
type Responses map[string]*ResponseRef //

var _ jsonpointer.JSONPointable = (*Responses)(nil)

Expand All @@ -31,6 +31,8 @@ func (responses Responses) Get(status int) *ResponseRef {
return responses[strconv.FormatInt(int64(status), 10)]
}

// Any HTTP status code can be used as the property name, but only one property per code, to describe the expected response for that HTTP status code. A Reference Object can link to a response that is defined in the OpenAPI Object's components/responses section. This field MUST be enclosed in quotation marks (for example, "200") for compatibility between JSON and YAML. To define a range of response codes, this field MAY contain the uppercase wildcard character X. For example, 2XX represents all response codes between [200-299]. Only the following range definitions are allowed: 1XX, 2XX, 3XX, 4XX, and 5XX. If a response is defined using an explicit code, the explicit code definition takes precedence over the range definition for that code.

// Validate returns an error if Responses does not comply with the OpenAPI spec.
func (responses Responses) Validate(ctx context.Context, opts ...ValidationOption) error {
ctx = WithValidationOptions(ctx, opts...)
Expand Down

0 comments on commit dabf0fe

Please sign in to comment.