diff --git a/.github/docs/openapi3.txt b/.github/docs/openapi3.txt index 44ab4d66..a1631dff 100644 --- a/.github/docs/openapi3.txt +++ b/.github/docs/openapi3.txt @@ -468,6 +468,7 @@ func (encoding *Encoding) WithHeaderRef(name string, ref *HeaderRef) *Encoding type Example struct { Extensions map[string]any `json:"-" yaml:"-"` + Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` Summary string `json:"summary,omitempty" yaml:"summary,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` @@ -2240,6 +2241,7 @@ type ValidationOptions struct { type XML struct { Extensions map[string]any `json:"-" yaml:"-"` + Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` Name string `json:"name,omitempty" yaml:"name,omitempty"` Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` diff --git a/openapi3/example.go b/openapi3/example.go index 9d38e434..56aee378 100644 --- a/openapi3/example.go +++ b/openapi3/example.go @@ -10,6 +10,7 @@ import ( // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#example-object type Example struct { Extensions map[string]any `json:"-" yaml:"-"` + Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` Summary string `json:"summary,omitempty" yaml:"summary,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` @@ -59,6 +60,7 @@ func (example *Example) UnmarshalJSON(data []byte) error { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) + delete(x.Extensions, originKey) delete(x.Extensions, "summary") delete(x.Extensions, "description") delete(x.Extensions, "value") diff --git a/openapi3/origin_test.go b/openapi3/origin_test.go index 56d8f1d4..d955056a 100644 --- a/openapi3/origin_test.go +++ b/openapi3/origin_test.go @@ -2,33 +2,11 @@ package openapi3 import ( "context" - "fmt" - "os" "testing" "github.com/stretchr/testify/require" ) -func TestOrigin_All(t *testing.T) { - loader := NewLoader() - loader.IsExternalRefsAllowed = true - loader.IncludeOrigin = true - loader.Context = context.Background() - - const dir = "testdata/origin/" - items, _ := os.ReadDir(dir) - for _, item := range items { - t.Run(item.Name(), func(t *testing.T) { - doc, err := loader.LoadFromFile(fmt.Sprintf("%s/%s", dir, item.Name())) - require.NoError(t, err) - if doc.Paths == nil { - t.Skip("no paths") - } - require.NotEmpty(t, doc.Paths.Origin) - }) - } -} - func TestOrigin_Info(t *testing.T) { loader := NewLoader() loader.IsExternalRefsAllowed = true @@ -301,3 +279,72 @@ func TestOrigin_Security(t *testing.T) { }, base.Flows.Implicit.Origin.Fields["authorizationUrl"]) } + +func TestOrigin_Example(t *testing.T) { + loader := NewLoader() + loader.IsExternalRefsAllowed = true + loader.IncludeOrigin = true + loader.Context = context.Background() + + doc, err := loader.LoadFromFile("testdata/origin/example.yaml") + require.NoError(t, err) + + base := doc.Paths.Find("/subscribe").Post.RequestBody.Value.Content["application/json"].Examples["bar"].Value + require.NotNil(t, base.Origin) + require.Equal(t, + &Location{ + Line: 14, + Column: 15, + }, + base.Origin.Key) + + require.Equal(t, + Location{ + Line: 15, + Column: 17, + }, + base.Origin.Fields["summary"]) + + // Note: + // Example.Value contains an extra field: "origin". + // + // Explanation: + // The example value is defined in the original yaml file as a json object: {"bar": "baz"} + // This json object is also valid in YAML, so yaml.3 decodes it as a map and adds an "origin" field. + require.Contains(t, + base.Value, + originKey) +} + +func TestOrigin_XML(t *testing.T) { + loader := NewLoader() + loader.IsExternalRefsAllowed = true + loader.IncludeOrigin = true + loader.Context = context.Background() + + doc, err := loader.LoadFromFile("testdata/origin/xml.yaml") + require.NoError(t, err) + + base := doc.Paths.Find("/subscribe").Post.RequestBody.Value.Content["application/json"].Schema.Value.Properties["name"].Value.XML + require.NotNil(t, base.Origin) + require.Equal(t, + &Location{ + Line: 21, + Column: 19, + }, + base.Origin.Key) + + require.Equal(t, + Location{ + Line: 22, + Column: 21, + }, + base.Origin.Fields["namespace"]) + + require.Equal(t, + Location{ + Line: 23, + Column: 21, + }, + base.Origin.Fields["prefix"]) +} diff --git a/openapi3/testdata/origin/example.yaml b/openapi3/testdata/origin/example.yaml new file mode 100644 index 00000000..8b50ef26 --- /dev/null +++ b/openapi3/testdata/origin/example.yaml @@ -0,0 +1,19 @@ +openapi: 3.0.0 +info: + title: Security Requirement Example + version: 1.0.0 +paths: + /subscribe: + post: + requestBody: + content: + application/json: + schema: + type: object + examples: + bar: + summary: A bar example + value: {"bar": "baz"} + responses: + "200": + description: OK diff --git a/openapi3/testdata/origin/xml.yaml b/openapi3/testdata/origin/xml.yaml new file mode 100644 index 00000000..3193f32f --- /dev/null +++ b/openapi3/testdata/origin/xml.yaml @@ -0,0 +1,26 @@ +openapi: 3.0.0 +info: + title: Security Requirement Example + version: 1.0.0 +paths: + /subscribe: + post: + requestBody: + content: + application/json: + schema: + type: object + properties: + id: + type: integer + format: int32 + xml: + attribute: true + name: + type: string + xml: + namespace: http://example.com/schema/sample + prefix: sample + responses: + "200": + description: OK diff --git a/openapi3/xml.go b/openapi3/xml.go index 69d1b348..7acd8188 100644 --- a/openapi3/xml.go +++ b/openapi3/xml.go @@ -9,6 +9,7 @@ import ( // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#xml-object type XML struct { Extensions map[string]any `json:"-" yaml:"-"` + Origin *Origin `json:"origin,omitempty" yaml:"origin,omitempty"` Name string `json:"name,omitempty" yaml:"name,omitempty"` Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` @@ -58,6 +59,7 @@ func (xml *XML) UnmarshalJSON(data []byte) error { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) + delete(x.Extensions, originKey) delete(x.Extensions, "name") delete(x.Extensions, "namespace") delete(x.Extensions, "prefix")