mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-06 12:37:49 +00:00
(feat): Unpack extensions into complex function types #8
The more in-depth we use extensions, the more likely is is that we need custom extensions. New `UnpackExtensions` function in the high package makes this easy. Low level models have been updated to support feature fully and docs added in README and examples as well. Signed-off-by: Dave Shanley <dave@quobix.com>
This commit is contained in:
391
README.md
391
README.md
@@ -5,8 +5,27 @@
|
|||||||

|

|
||||||
[](https://goreportcard.com/report/github.com/pb33f/libopenapi)
|
[](https://goreportcard.com/report/github.com/pb33f/libopenapi)
|
||||||
[](https://codecov.io/gh/pb33f/libopenapi)
|
[](https://codecov.io/gh/pb33f/libopenapi)
|
||||||
|
[](https://pkg.go.dev/github.com/pb33f/libopenapi)
|
||||||
|
|
||||||
libopenapi has full support for Swagger (OpenAPI 2), OpenAPI 3, and OpenAPI 3.1.
|
libopenapi has full support for Swagger (OpenAPI 2), OpenAPI 3, and OpenAPI 3.1. It can handle the largest and most
|
||||||
|
complex specifications you can think of.
|
||||||
|
|
||||||
|
## Contents
|
||||||
|
|
||||||
|
- [Installing libopenapi](#installing)
|
||||||
|
- [Load an OpenAPI 3.1 or 3.0 specification into a model](#load-an-openapi-spec-into-a-model)
|
||||||
|
- [Load a Swagger Spec into a model](#load-a-swagger-spec-into-a-model)
|
||||||
|
- [Discover what changed](#discover-what-changed)
|
||||||
|
- [Loading complex types using extensions](#loading-extensions-using-complex-types)
|
||||||
|
- [Creating an index of an OpenAPI specification](#creating-an-index-of-an-openapi-specification)
|
||||||
|
- [Resolving an OpenAPI specification](#resolving-an-openapi-specification)
|
||||||
|
- [Checking for circular errors without resolving](#checking-for-circular-errors-without-resolving)
|
||||||
|
- [Extracting circular refs and resolving errors when building a document](#extracting-circular-refs-and-resolving-errors-when-building-a-document)
|
||||||
|
- [Mutating the model](#mutating-the-model)
|
||||||
|
|
||||||
|
> **Read the docs at [https://pkg.go.dev/github.com/pb33f/libopenapi](https://pkg.go.dev/github.com/pb33f/libopenapi)**
|
||||||
|
>
|
||||||
|
---
|
||||||
|
|
||||||
## Introduction - Why?
|
## Introduction - Why?
|
||||||
|
|
||||||
@@ -77,7 +96,7 @@ Grab the latest release of **libopenapi**
|
|||||||
go get github.com/pb33f/libopenapi
|
go get github.com/pb33f/libopenapi
|
||||||
```
|
```
|
||||||
|
|
||||||
### Load an OpenAPI 3+ spec into a model
|
## Load an OpenAPI spec into a model
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// import the library
|
// import the library
|
||||||
@@ -124,7 +143,7 @@ There are 13 paths and 8 schemas in the document
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### Load a Swagger (OpenAPI 2) spec into a model
|
## Load a Swagger spec into a model
|
||||||
```go
|
```go
|
||||||
// import the library
|
// import the library
|
||||||
import "github.com/pb33f/libopenapi"
|
import "github.com/pb33f/libopenapi"
|
||||||
@@ -135,7 +154,7 @@ func readSpec() {
|
|||||||
petstore, _ := ioutil.ReadFile("test_specs/petstorev2.json")
|
petstore, _ := ioutil.ReadFile("test_specs/petstorev2.json")
|
||||||
|
|
||||||
// create a new document from specification bytes
|
// create a new document from specification bytes
|
||||||
document, err := libeopnapi.NewDocument(petstore)
|
document, err := libopenapi.NewDocument(petstore)
|
||||||
|
|
||||||
// if anything went wrong, an error is thrown
|
// if anything went wrong, an error is thrown
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -205,7 +224,7 @@ fmt.Printf("value is %s, the value is on line %d, " +
|
|||||||
```
|
```
|
||||||
---
|
---
|
||||||
|
|
||||||
## What changed?
|
## Discover what changed
|
||||||
|
|
||||||
libopenapi comes with a complete **diff engine**
|
libopenapi comes with a complete **diff engine**
|
||||||
|
|
||||||
@@ -260,97 +279,143 @@ Every change can be explored and navigated just like you would use the high or l
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## But wait, there's more - Mutating the model
|
## Loading extensions using complex types
|
||||||
|
|
||||||
Having a read-only model is great, but what about when we want to modify the model and mutate values, or even add new
|
If you're using extensions with complex types (rather that just simple strings and primitives), then there is a good
|
||||||
content to the model? What if we also want to save that output as an updated specification - but we don't want to
|
chance you're going to want some simple way to marshal extensions into those structs.
|
||||||
jumble up the original ordering of the source.
|
|
||||||
|
|
||||||
### marshaling and unmarshalling to and from structs into JSON/YAML is not ideal.
|
Since version v0.4.0 There is a new method to make this simple available in the `high` package within the `datamodel`
|
||||||
|
package.
|
||||||
|
|
||||||
When we straight up use `json.Marshal` or `yaml.Marshal` to send structs to be rendered into the desired format, there
|
Here is an example of complex types being extracted easily from OpenAPI extensions.
|
||||||
is no guarantee as to the order in which each component will be rendered. This works great if...
|
|
||||||
|
|
||||||
- We don't care about the spec being randomly ordered.
|
|
||||||
- We don't care about code-reviews.
|
|
||||||
- We don't actually care about this very much.
|
|
||||||
|
|
||||||
### But if we do care...
|
|
||||||
|
|
||||||
Then libopenpi provides a way to mutate the model, that keeps the original [yaml.Node API](https://pkg.go.dev/gopkg.in/yaml.v3#Node)
|
|
||||||
tree in-tact. It allows us to make changes to values in place, and serialize back to JSON or YAML without any changes to
|
|
||||||
other content order.
|
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// create very small, and useless spec that does nothing useful, except showcase this feature.
|
|
||||||
spec := `
|
|
||||||
openapi: 3.1.0
|
|
||||||
info:
|
|
||||||
title: This is a title
|
|
||||||
contact:
|
|
||||||
name: Some Person
|
|
||||||
email: some@emailaddress.com
|
|
||||||
license:
|
|
||||||
url: http://some-place-on-the-internet.com/license
|
|
||||||
`
|
|
||||||
// create a new document from specification bytes
|
|
||||||
document, err := libopenapi.NewDocument([]byte(spec))
|
|
||||||
|
|
||||||
// if anything went wrong, an error is thrown
|
import (
|
||||||
if err != nil {
|
"github.com/pb33f/libopenapi"
|
||||||
|
high "github.com/pb33f/libopenapi/datamodel/high"
|
||||||
|
lowbase "github.com/pb33f/libopenapi/datamodel/low/base"
|
||||||
|
lowv3 "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// define an example struct representing a cake
|
||||||
|
type cake struct {
|
||||||
|
Candles int
|
||||||
|
Frosting string
|
||||||
|
}
|
||||||
|
|
||||||
|
// define a struct that holds a map of cake pointers.
|
||||||
|
type cakes struct {
|
||||||
|
Description string
|
||||||
|
Cakes map[string]*cake
|
||||||
|
}
|
||||||
|
|
||||||
|
// define a struct representing a burger
|
||||||
|
type burger struct {
|
||||||
|
Sauce string
|
||||||
|
Patty string
|
||||||
|
}
|
||||||
|
|
||||||
|
// define a struct that holds a map of cake pointers
|
||||||
|
type burgers struct {
|
||||||
|
Description string
|
||||||
|
Burgers map[string]*burger
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
// create a specification with a schema and parameter that use complex
|
||||||
|
// custom cakes and burgers extensions.
|
||||||
|
spec := `openapi: "3.1"
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
SchemaOne:
|
||||||
|
description: "Some schema with custom complex extensions"
|
||||||
|
x-custom-cakes:
|
||||||
|
description: some cakes
|
||||||
|
cakes:
|
||||||
|
someCake:
|
||||||
|
candles: 10
|
||||||
|
frosting: blue
|
||||||
|
anotherCake:
|
||||||
|
candles: 1
|
||||||
|
frosting: green
|
||||||
|
parameters:
|
||||||
|
ParameterOne:
|
||||||
|
description: "Some parameter also using complex extensions"
|
||||||
|
x-custom-burgers:
|
||||||
|
description: some burgers
|
||||||
|
burgers:
|
||||||
|
someBurger:
|
||||||
|
sauce: ketchup
|
||||||
|
patty: meat
|
||||||
|
anotherBurger:
|
||||||
|
sauce: mayo
|
||||||
|
patty: lamb`
|
||||||
|
|
||||||
|
// create a new document from specification bytes
|
||||||
|
doc, err := libopenapi.NewDocument([]byte(spec))
|
||||||
|
|
||||||
|
// if anything went wrong, an error is thrown
|
||||||
|
if err != nil {
|
||||||
panic(fmt.Sprintf("cannot create new document: %e", err))
|
panic(fmt.Sprintf("cannot create new document: %e", err))
|
||||||
}
|
|
||||||
|
|
||||||
// because we know this is a v3 spec, we can build a ready to go model from it.
|
|
||||||
v3Model, errors := document.BuildV3Model()
|
|
||||||
|
|
||||||
// if anything went wrong when building the v3 model, a slice of errors will be returned
|
|
||||||
if len(errors) > 0 {
|
|
||||||
for i := range errors {
|
|
||||||
fmt.Printf("error: %e\n", errors[i])
|
|
||||||
}
|
}
|
||||||
panic(fmt.Sprintf("cannot create v3 model from document: %d errors reported", len(errors)))
|
|
||||||
|
// build a v3 model.
|
||||||
|
docModel, errs := doc.BuildV3Model()
|
||||||
|
|
||||||
|
// if anything went wrong building, indexing and resolving the model, an error is thrown
|
||||||
|
if errs != nil {
|
||||||
|
panic(fmt.Sprintf("cannot create new document: %e", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// get a reference to SchemaOne and ParameterOne
|
||||||
|
schemaOne := docModel.Model.Components.Schemas["SchemaOne"].Schema()
|
||||||
|
parameterOne := docModel.Model.Components.Parameters["ParameterOne"]
|
||||||
|
|
||||||
|
// unpack schemaOne extensions into complex `cakes` type
|
||||||
|
schemaOneExtensions, schemaUnpackErrors := high.UnpackExtensions[cakes, *lowbase.Schema](schemaOne)
|
||||||
|
if schemaUnpackErrors != nil {
|
||||||
|
panic(fmt.Sprintf("cannot unpack schema extensions: %e", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// unpack parameterOne into complex `burgers` type
|
||||||
|
parameterOneExtensions, paramUnpackErrors := high.UnpackExtensions[burgers, *lowv3.Parameter](parameterOne)
|
||||||
|
if paramUnpackErrors != nil {
|
||||||
|
panic(fmt.Sprintf("cannot unpack parameter extensions: %e", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// extract extension by name for schemaOne
|
||||||
|
customCakes := schemaOneExtensions["x-custom-cakes"]
|
||||||
|
|
||||||
|
// extract extension by name for schemaOne
|
||||||
|
customBurgers := parameterOneExtensions["x-custom-burgers"]
|
||||||
|
|
||||||
|
// print out schemaOne complex extension details.
|
||||||
|
fmt.Printf("schemaOne 'x-custom-cakes' (%s) has %d cakes, 'someCake' has %d candles and %s frosting\n",
|
||||||
|
customCakes.Description,
|
||||||
|
len(customCakes.Cakes),
|
||||||
|
customCakes.Cakes["someCake"].Candles,
|
||||||
|
customCakes.Cakes["someCake"].Frosting,
|
||||||
|
)
|
||||||
|
|
||||||
|
// print out parameterOne complex extension details.
|
||||||
|
fmt.Printf("parameterOne 'x-custom-burgers' (%s) has %d burgers, 'anotherBurger' has %s sauce and a %s patty\n",
|
||||||
|
customBurgers.Description,
|
||||||
|
len(customBurgers.Burgers),
|
||||||
|
customBurgers.Burgers["anotherBurger"].Sauce,
|
||||||
|
customBurgers.Burgers["anotherBurger"].Patty,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// mutate the title, to do this we currently need to drop down to the low-level API.
|
|
||||||
v3Model.Model.GoLow().Info.Value.Title.Mutate("A new title for a useless spec")
|
|
||||||
|
|
||||||
// mutate the email address in the contact object.
|
|
||||||
v3Model.Model.GoLow().Info.Value.Contact.Value.Email.Mutate("buckaroo@pb33f.io")
|
|
||||||
|
|
||||||
// mutate the name in the contact object.
|
|
||||||
v3Model.Model.GoLow().Info.Value.Contact.Value.Name.Mutate("Buckaroo")
|
|
||||||
|
|
||||||
// mutate the URL for the license object.
|
|
||||||
v3Model.Model.GoLow().Info.Value.License.Value.URL.Mutate("https://pb33f.io/license")
|
|
||||||
|
|
||||||
// serialize the document back into the original YAML or JSON
|
|
||||||
mutatedSpec, serialError := document.Serialize()
|
|
||||||
|
|
||||||
// if something went wrong serializing
|
|
||||||
if serialError != nil {
|
|
||||||
panic(fmt.Sprintf("cannot serialize document: %e", serialError))
|
|
||||||
}
|
|
||||||
|
|
||||||
// print our modified spec!
|
|
||||||
fmt.Println(string(mutatedSpec))
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Which will output:
|
This will output:
|
||||||
|
|
||||||
```yaml
|
|
||||||
openapi: 3.1.0
|
|
||||||
info:
|
|
||||||
title: A new title for a useless spec
|
|
||||||
contact:
|
|
||||||
name: Buckaroo
|
|
||||||
email: buckaroo@pb33f.io
|
|
||||||
license:
|
|
||||||
url: https://pb33f.io/license
|
|
||||||
|
|
||||||
|
```text
|
||||||
|
schemaOne 'x-custom-cakes' (some cakes) has 2 cakes, 'someCake' has 10 candles and blue frosting
|
||||||
|
parameterOne 'x-custom-burgers' (some burgers) has 2 burgers, 'anotherBurger' has mayo sauce and a lamb patty
|
||||||
```
|
```
|
||||||
> It's worth noting that the original line numbers and column numbers **won't be respected** when calling `Serialize()`,
|
---
|
||||||
> A new `Document` needs to be created from that raw YAML to continue processing after serialization.
|
|
||||||
|
|
||||||
## Creating an index of an OpenAPI Specification
|
## Creating an index of an OpenAPI Specification
|
||||||
|
|
||||||
@@ -518,8 +583,168 @@ fmt.Printf("There are %d circular reference errors, " +
|
|||||||
len(resolver.GetNonPolymorphicCircularErrors()))
|
len(resolver.GetNonPolymorphicCircularErrors()))
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Extracting circular refs and resolving errors when building a document
|
||||||
|
|
||||||
|
To avoid having to create an index and a resolver each time you want to both create
|
||||||
|
a document and resolve it / check for errors, don't worry, circular references are checked
|
||||||
|
automatically and are available in the returned `[]errors` which building a document.
|
||||||
|
|
||||||
|
The errors returned by the slice are pointers to `*resolver.ResolvingError` which contains
|
||||||
|
rich details about the issue, where it was found and the journey it took to get there.
|
||||||
|
|
||||||
|
Here is an example:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// create a specification with an obvious and deliberate circular reference
|
||||||
|
spec := `
|
||||||
|
openapi: "3.1"
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
One:
|
||||||
|
description: "test one"
|
||||||
|
properties:
|
||||||
|
things:
|
||||||
|
"$ref": "#/components/schemas/Two"
|
||||||
|
Two:
|
||||||
|
description: "test two"
|
||||||
|
properties:
|
||||||
|
testThing:
|
||||||
|
"$ref": "#/components/schemas/One"
|
||||||
|
`
|
||||||
|
// create a new document from specification bytes
|
||||||
|
doc, err := NewDocument([]byte(spec))
|
||||||
|
|
||||||
|
// if anything went wrong, an error is thrown
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("cannot create new document: %e", err))
|
||||||
|
}
|
||||||
|
_, errs := doc.BuildV3Model()
|
||||||
|
|
||||||
|
// extract resolving error
|
||||||
|
resolvingError := errs[0]
|
||||||
|
|
||||||
|
// resolving error is a pointer to *resolver.ResolvingError
|
||||||
|
// which provides access to rich details about the error.
|
||||||
|
circularReference := resolvingError.(*resolver.ResolvingError).CircularReference
|
||||||
|
|
||||||
|
// capture the journey with all details
|
||||||
|
var buf strings.Builder
|
||||||
|
for n := range circularReference.Journey {
|
||||||
|
|
||||||
|
// add the full definition name to the journey.
|
||||||
|
buf.WriteString(circularReference.Journey[n].Definition)
|
||||||
|
if n < len(circularReference.Journey)-1 {
|
||||||
|
buf.WriteString(" -> ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// print out the journey and the loop point.
|
||||||
|
fmt.Printf("Journey: %s\n", buf.String())
|
||||||
|
fmt.Printf("Loop Point: %s", circularReference.LoopPoint.Definition)
|
||||||
|
```
|
||||||
|
|
||||||
|
Will output:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Journey: #/components/schemas/Two -> #/components/schemas/One -> #/components/schemas/Two
|
||||||
|
Loop Point: #/components/schemas/Two
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
|
## Mutating the model
|
||||||
|
|
||||||
|
Having a read-only model is great, but what about when we want to modify the model and mutate values, or even add new
|
||||||
|
content to the model? What if we also want to save that output as an updated specification - but we don't want to
|
||||||
|
jumble up the original ordering of the source.
|
||||||
|
|
||||||
|
### marshaling and unmarshalling to and from structs into JSON/YAML is not ideal.
|
||||||
|
|
||||||
|
When we straight up use `json.Marshal` or `yaml.Marshal` to send structs to be rendered into the desired format, there
|
||||||
|
is no guarantee as to the order in which each component will be rendered. This works great if...
|
||||||
|
|
||||||
|
- We don't care about the spec being randomly ordered.
|
||||||
|
- We don't care about code-reviews.
|
||||||
|
- We don't actually care about this very much.
|
||||||
|
|
||||||
|
### But if we do care...
|
||||||
|
|
||||||
|
Then libopenpi provides a way to mutate the model, that keeps the original [yaml.Node API](https://pkg.go.dev/gopkg.in/yaml.v3#Node)
|
||||||
|
tree in-tact. It allows us to make changes to values in place, and serialize back to JSON or YAML without any changes to
|
||||||
|
other content order.
|
||||||
|
|
||||||
|
```go
|
||||||
|
// create very small, and useless spec that does nothing useful, except showcase this feature.
|
||||||
|
spec := `
|
||||||
|
openapi: 3.1.0
|
||||||
|
info:
|
||||||
|
title: This is a title
|
||||||
|
contact:
|
||||||
|
name: Some Person
|
||||||
|
email: some@emailaddress.com
|
||||||
|
license:
|
||||||
|
url: http://some-place-on-the-internet.com/license
|
||||||
|
`
|
||||||
|
// create a new document from specification bytes
|
||||||
|
document, err := libopenapi.NewDocument([]byte(spec))
|
||||||
|
|
||||||
|
// if anything went wrong, an error is thrown
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("cannot create new document: %e", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// because we know this is a v3 spec, we can build a ready to go model from it.
|
||||||
|
v3Model, errors := document.BuildV3Model()
|
||||||
|
|
||||||
|
// if anything went wrong when building the v3 model, a slice of errors will be returned
|
||||||
|
if len(errors) > 0 {
|
||||||
|
for i := range errors {
|
||||||
|
fmt.Printf("error: %e\n", errors[i])
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("cannot create v3 model from document: %d errors reported", len(errors)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// mutate the title, to do this we currently need to drop down to the low-level API.
|
||||||
|
v3Model.Model.GoLow().Info.Value.Title.Mutate("A new title for a useless spec")
|
||||||
|
|
||||||
|
// mutate the email address in the contact object.
|
||||||
|
v3Model.Model.GoLow().Info.Value.Contact.Value.Email.Mutate("buckaroo@pb33f.io")
|
||||||
|
|
||||||
|
// mutate the name in the contact object.
|
||||||
|
v3Model.Model.GoLow().Info.Value.Contact.Value.Name.Mutate("Buckaroo")
|
||||||
|
|
||||||
|
// mutate the URL for the license object.
|
||||||
|
v3Model.Model.GoLow().Info.Value.License.Value.URL.Mutate("https://pb33f.io/license")
|
||||||
|
|
||||||
|
// serialize the document back into the original YAML or JSON
|
||||||
|
mutatedSpec, serialError := document.Serialize()
|
||||||
|
|
||||||
|
// if something went wrong serializing
|
||||||
|
if serialError != nil {
|
||||||
|
panic(fmt.Sprintf("cannot serialize document: %e", serialError))
|
||||||
|
}
|
||||||
|
|
||||||
|
// print our modified spec!
|
||||||
|
fmt.Println(string(mutatedSpec))
|
||||||
|
```
|
||||||
|
|
||||||
|
Which will output:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
openapi: 3.1.0
|
||||||
|
info:
|
||||||
|
title: A new title for a useless spec
|
||||||
|
contact:
|
||||||
|
name: Buckaroo
|
||||||
|
email: buckaroo@pb33f.io
|
||||||
|
license:
|
||||||
|
url: https://pb33f.io/license
|
||||||
|
|
||||||
|
```
|
||||||
|
> It's worth noting that the original line numbers and column numbers **won't be respected** when calling `Serialize()`,
|
||||||
|
> A new `Document` needs to be created from that raw YAML to continue processing after serialization.
|
||||||
|
|
||||||
> **Read the full docs at [https://pkg.go.dev](https://pkg.go.dev/github.com/pb33f/libopenapi)**
|
> **Read the full docs at [https://pkg.go.dev](https://pkg.go.dev/github.com/pb33f/libopenapi)**
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -38,3 +38,7 @@ func NewExternalDoc(extDoc *low.ExternalDoc) *ExternalDoc {
|
|||||||
func (e *ExternalDoc) GoLow() *low.ExternalDoc {
|
func (e *ExternalDoc) GoLow() *low.ExternalDoc {
|
||||||
return e.low
|
return e.low
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *ExternalDoc) GetExtensions() map[string]any {
|
||||||
|
return e.Extensions
|
||||||
|
}
|
||||||
@@ -35,6 +35,7 @@ x-hack: code`
|
|||||||
|
|
||||||
wentLow := highExt.GoLow()
|
wentLow := highExt.GoLow()
|
||||||
assert.Equal(t, 2, wentLow.URL.ValueNode.Line)
|
assert.Equal(t, 2, wentLow.URL.ValueNode.Line)
|
||||||
|
assert.Len(t, highExt.GetExtensions(), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleNewExternalDoc() {
|
func ExampleNewExternalDoc() {
|
||||||
|
|||||||
@@ -33,3 +33,34 @@ func ExtractExtensions(extensions map[low.KeyReference[string]]low.ValueReferenc
|
|||||||
}
|
}
|
||||||
return extracted
|
return extracted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnpackExtensions is a convenience function that makes it easy and simple to unpack an objects extensions
|
||||||
|
// into a complex type, provided as a generic. This function is for high-level models that implement `GoesLow()`
|
||||||
|
// and for low-level models that support extensions via `HasExtensions`.
|
||||||
|
//
|
||||||
|
// This feature will be upgraded at some point to hold a registry of types and extension mappings to make this
|
||||||
|
// functionality available a little more automatically.
|
||||||
|
// You can read more about the discussion here: https://github.com/pb33f/libopenapi/issues/8
|
||||||
|
//
|
||||||
|
// `T` represents the Type you want to unpack into
|
||||||
|
// `R` represents the LOW type of the object that contains the extensions (not the high)
|
||||||
|
// `low` represents the HIGH type of the object that contains the extensions.
|
||||||
|
//
|
||||||
|
// to use:
|
||||||
|
// schema := schemaProxy.Schema() // any high-level object that has extensions
|
||||||
|
// extensions, err := UnpackExtensions[MyComplexType, low.Schema](schema)
|
||||||
|
func UnpackExtensions[T any, R low.HasExtensions[T]](low GoesLow[R]) (map[string]*T, error) {
|
||||||
|
m := make(map[string]*T)
|
||||||
|
ext := low.GoLow().GetExtensions()
|
||||||
|
for i := range ext {
|
||||||
|
key := i.Value
|
||||||
|
g := new(T)
|
||||||
|
valueNode := ext[i].ValueNode
|
||||||
|
err := valueNode.Decode(g)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
m[key] = g
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ package high
|
|||||||
import (
|
import (
|
||||||
"github.com/pb33f/libopenapi/datamodel/low"
|
"github.com/pb33f/libopenapi/datamodel/low"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -19,3 +20,111 @@ func TestExtractExtensions(t *testing.T) {
|
|||||||
ext := ExtractExtensions(n)
|
ext := ExtractExtensions(n)
|
||||||
assert.Equal(t, "new cowboy in town", ext["pb33f"])
|
assert.Equal(t, "new cowboy in town", ext["pb33f"])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type textExtension struct {
|
||||||
|
Cowboy string
|
||||||
|
Power int
|
||||||
|
}
|
||||||
|
|
||||||
|
type parent struct {
|
||||||
|
low *child
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parent) GoLow() *child {
|
||||||
|
return p.low
|
||||||
|
}
|
||||||
|
|
||||||
|
type child struct {
|
||||||
|
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *child) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||||
|
return c.Extensions
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnpackExtensions(t *testing.T) {
|
||||||
|
|
||||||
|
var resultA, resultB yaml.Node
|
||||||
|
|
||||||
|
ymlA := `
|
||||||
|
cowboy: buckaroo
|
||||||
|
power: 100`
|
||||||
|
|
||||||
|
ymlB := `
|
||||||
|
cowboy: frogman
|
||||||
|
power: 2`
|
||||||
|
|
||||||
|
err := yaml.Unmarshal([]byte(ymlA), &resultA)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = yaml.Unmarshal([]byte(ymlB), &resultB)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
n := make(map[low.KeyReference[string]]low.ValueReference[any])
|
||||||
|
n[low.KeyReference[string]{
|
||||||
|
Value: "x-rancher-a",
|
||||||
|
}] = low.ValueReference[any]{
|
||||||
|
ValueNode: resultA.Content[0],
|
||||||
|
}
|
||||||
|
|
||||||
|
n[low.KeyReference[string]{
|
||||||
|
Value: "x-rancher-b",
|
||||||
|
}] = low.ValueReference[any]{
|
||||||
|
ValueNode: resultB.Content[0],
|
||||||
|
}
|
||||||
|
|
||||||
|
c := new(child)
|
||||||
|
c.Extensions = n
|
||||||
|
|
||||||
|
p := new(parent)
|
||||||
|
p.low = c
|
||||||
|
|
||||||
|
res, err := UnpackExtensions[textExtension, *child](p)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotEmpty(t, res)
|
||||||
|
assert.Equal(t, "buckaroo", res["x-rancher-a"].Cowboy)
|
||||||
|
assert.Equal(t, 100, res["x-rancher-a"].Power)
|
||||||
|
assert.Equal(t, "frogman", res["x-rancher-b"].Cowboy)
|
||||||
|
assert.Equal(t, 2, res["x-rancher-b"].Power)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnpackExtensions_Fail(t *testing.T) {
|
||||||
|
|
||||||
|
var resultA, resultB yaml.Node
|
||||||
|
|
||||||
|
ymlA := `
|
||||||
|
cowboy: buckaroo
|
||||||
|
power: 100`
|
||||||
|
|
||||||
|
// this is incorrect types, unpacking will fail.
|
||||||
|
ymlB := `
|
||||||
|
cowboy: 0
|
||||||
|
power: hello`
|
||||||
|
|
||||||
|
err := yaml.Unmarshal([]byte(ymlA), &resultA)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = yaml.Unmarshal([]byte(ymlB), &resultB)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
n := make(map[low.KeyReference[string]]low.ValueReference[any])
|
||||||
|
n[low.KeyReference[string]{
|
||||||
|
Value: "x-rancher-a",
|
||||||
|
}] = low.ValueReference[any]{
|
||||||
|
ValueNode: resultA.Content[0],
|
||||||
|
}
|
||||||
|
|
||||||
|
n[low.KeyReference[string]{
|
||||||
|
Value: "x-rancher-b",
|
||||||
|
}] = low.ValueReference[any]{
|
||||||
|
ValueNode: resultB.Content[0],
|
||||||
|
}
|
||||||
|
|
||||||
|
c := new(child)
|
||||||
|
c.Extensions = n
|
||||||
|
|
||||||
|
p := new(parent)
|
||||||
|
p.low = c
|
||||||
|
|
||||||
|
res, er := UnpackExtensions[textExtension, *child](p)
|
||||||
|
assert.Error(t, er)
|
||||||
|
assert.Empty(t, res)
|
||||||
|
}
|
||||||
@@ -35,6 +35,11 @@ func (i *Info) FindExtension(ext string) *low.ValueReference[any] {
|
|||||||
return low.FindItemInMap(ext, i.Extensions)
|
return low.FindItemInMap(ext, i.Extensions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetExtensions returns all extensions for Info
|
||||||
|
func (i *Info) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||||
|
return i.Extensions
|
||||||
|
}
|
||||||
|
|
||||||
// Build will extract out the Contact and Info objects from the supplied root node.
|
// Build will extract out the Contact and Info objects from the supplied root node.
|
||||||
func (i *Info) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (i *Info) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
i.Extensions = low.ExtractExtensions(root)
|
i.Extensions = low.ExtractExtensions(root)
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ x-cli-name: pizza cli`
|
|||||||
cliName := n.FindExtension("x-cli-name")
|
cliName := n.FindExtension("x-cli-name")
|
||||||
assert.NotNil(t, cliName)
|
assert.NotNil(t, cliName)
|
||||||
assert.Equal(t, "pizza cli", cliName.Value)
|
assert.Equal(t, "pizza cli", cliName.Value)
|
||||||
|
assert.Len(t, n.GetExtensions(), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContact_Build(t *testing.T) {
|
func TestContact_Build(t *testing.T) {
|
||||||
|
|||||||
@@ -381,6 +381,11 @@ func (s *Schema) FindProperty(name string) *low.ValueReference[*SchemaProxy] {
|
|||||||
return low.FindItemInMap[*SchemaProxy](name, s.Properties.Value)
|
return low.FindItemInMap[*SchemaProxy](name, s.Properties.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetExtensions returns all extensions for Schema
|
||||||
|
func (s *Schema) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||||
|
return s.Extensions
|
||||||
|
}
|
||||||
|
|
||||||
// Build will perform a number of operations.
|
// Build will perform a number of operations.
|
||||||
// Extraction of the following happens in this method:
|
// Extraction of the following happens in this method:
|
||||||
// - Extensions
|
// - Extensions
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ import (
|
|||||||
|
|
||||||
func TestSchemaProxy_Build(t *testing.T) {
|
func TestSchemaProxy_Build(t *testing.T) {
|
||||||
|
|
||||||
yml := `description: something`
|
yml := `x-windows: washed
|
||||||
|
description: something`
|
||||||
|
|
||||||
var sch SchemaProxy
|
var sch SchemaProxy
|
||||||
var idxNode yaml.Node
|
var idxNode yaml.Node
|
||||||
@@ -21,7 +22,7 @@ func TestSchemaProxy_Build(t *testing.T) {
|
|||||||
err := sch.Build(idxNode.Content[0], nil)
|
err := sch.Build(idxNode.Content[0], nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, "3fc9b689459d738f8c88a3a48aa9e33542016b7a4052e001aaa536fca74813cb",
|
assert.Equal(t, "db2a35dd6fb3d9481d0682571b9d687616bb2a34c1887f7863f0b2e769ca7b23",
|
||||||
low.GenerateHashString(&sch))
|
low.GenerateHashString(&sch))
|
||||||
|
|
||||||
assert.Equal(t, "something", sch.Schema().Description.Value)
|
assert.Equal(t, "something", sch.Schema().Description.Value)
|
||||||
@@ -30,9 +31,11 @@ func TestSchemaProxy_Build(t *testing.T) {
|
|||||||
assert.False(t, sch.IsSchemaReference())
|
assert.False(t, sch.IsSchemaReference())
|
||||||
|
|
||||||
// already rendered, should spit out the same
|
// already rendered, should spit out the same
|
||||||
assert.Equal(t, "3fc9b689459d738f8c88a3a48aa9e33542016b7a4052e001aaa536fca74813cb",
|
assert.Equal(t, "db2a35dd6fb3d9481d0682571b9d687616bb2a34c1887f7863f0b2e769ca7b23",
|
||||||
low.GenerateHashString(&sch))
|
low.GenerateHashString(&sch))
|
||||||
|
|
||||||
|
assert.Len(t, sch.Schema().GetExtensions(), 1)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSchemaProxy_Build_CheckRef(t *testing.T) {
|
func TestSchemaProxy_Build_CheckRef(t *testing.T) {
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ func (x *XML) Build(root *yaml.Node, _ *index.SpecIndex) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetExtensions returns all Tag extensions and satisfies the low.HasExtensions interface.
|
||||||
func (x *XML) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
func (x *XML) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||||
return x.Extensions
|
return x.Extensions
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ type Hashable interface {
|
|||||||
|
|
||||||
// HasExtensions is implemented by any object that exposes extensions
|
// HasExtensions is implemented by any object that exposes extensions
|
||||||
type HasExtensions[T any] interface {
|
type HasExtensions[T any] interface {
|
||||||
|
|
||||||
|
// GetExtensions returns generic low level extensions
|
||||||
GetExtensions() map[KeyReference[string]]ValueReference[any]
|
GetExtensions() map[KeyReference[string]]ValueReference[any]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,11 @@ func (h *Header) FindExtension(ext string) *low.ValueReference[any] {
|
|||||||
return low.FindItemInMap[any](ext, h.Extensions)
|
return low.FindItemInMap[any](ext, h.Extensions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetExtensions returns all Header extensions and satisfies the low.HasExtensions interface.
|
||||||
|
func (h *Header) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||||
|
return h.Extensions
|
||||||
|
}
|
||||||
|
|
||||||
// Build will build out items, extensions and default value from the supplied node.
|
// Build will build out items, extensions and default value from the supplied node.
|
||||||
func (h *Header) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (h *Header) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
h.Extensions = low.ExtractExtensions(root)
|
h.Extensions = low.ExtractExtensions(root)
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ func TestHeader_Build(t *testing.T) {
|
|||||||
|
|
||||||
func TestHeader_DefaultAsSlice(t *testing.T) {
|
func TestHeader_DefaultAsSlice(t *testing.T) {
|
||||||
|
|
||||||
yml := `default:
|
yml := `x-ext: thing
|
||||||
|
default:
|
||||||
- why
|
- why
|
||||||
- so many
|
- so many
|
||||||
- variations`
|
- variations`
|
||||||
@@ -47,7 +48,7 @@ func TestHeader_DefaultAsSlice(t *testing.T) {
|
|||||||
|
|
||||||
assert.NotNil(t, n.Default.Value)
|
assert.NotNil(t, n.Default.Value)
|
||||||
assert.Len(t, n.Default.Value, 3)
|
assert.Len(t, n.Default.Value, 3)
|
||||||
|
assert.Len(t, n.GetExtensions(), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHeader_DefaultAsObject(t *testing.T) {
|
func TestHeader_DefaultAsObject(t *testing.T) {
|
||||||
|
|||||||
@@ -45,6 +45,11 @@ func (i *Items) FindExtension(ext string) *low.ValueReference[any] {
|
|||||||
return low.FindItemInMap[any](ext, i.Extensions)
|
return low.FindItemInMap[any](ext, i.Extensions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetExtensions returns all Items extensions and satisfies the low.HasExtensions interface.
|
||||||
|
func (i *Items) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||||
|
return i.Extensions
|
||||||
|
}
|
||||||
|
|
||||||
// Hash will return a consistent SHA256 Hash of the Items object
|
// Hash will return a consistent SHA256 Hash of the Items object
|
||||||
func (i *Items) Hash() [32]byte {
|
func (i *Items) Hash() [32]byte {
|
||||||
var f []string
|
var f []string
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ func TestItems_Build(t *testing.T) {
|
|||||||
|
|
||||||
func TestItems_DefaultAsSlice(t *testing.T) {
|
func TestItems_DefaultAsSlice(t *testing.T) {
|
||||||
|
|
||||||
yml := `default:
|
yml := `x-thing: thing
|
||||||
|
default:
|
||||||
- pizza
|
- pizza
|
||||||
- cake`
|
- cake`
|
||||||
|
|
||||||
@@ -44,6 +45,7 @@ func TestItems_DefaultAsSlice(t *testing.T) {
|
|||||||
_ = n.Build(idxNode.Content[0], idx)
|
_ = n.Build(idxNode.Content[0], idx)
|
||||||
|
|
||||||
assert.Len(t, n.Default.Value, 2)
|
assert.Len(t, n.Default.Value, 2)
|
||||||
|
assert.Len(t, n.GetExtensions(), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestItems_DefaultAsMap(t *testing.T) {
|
func TestItems_DefaultAsMap(t *testing.T) {
|
||||||
|
|||||||
@@ -78,6 +78,11 @@ func (p *Parameter) FindExtension(ext string) *low.ValueReference[any] {
|
|||||||
return low.FindItemInMap[any](ext, p.Extensions)
|
return low.FindItemInMap[any](ext, p.Extensions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetExtensions returns all Parameter extensions and satisfies the low.HasExtensions interface.
|
||||||
|
func (p *Parameter) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||||
|
return p.Extensions
|
||||||
|
}
|
||||||
|
|
||||||
// Build will extract out extensions, schema, items and default value
|
// Build will extract out extensions, schema, items and default value
|
||||||
func (p *Parameter) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (p *Parameter) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
p.Extensions = low.ExtractExtensions(root)
|
p.Extensions = low.ExtractExtensions(root)
|
||||||
|
|||||||
@@ -207,5 +207,6 @@ allowEmptyValue: true
|
|||||||
assert.Equal(t, "int", v.Value.A) // A is v2
|
assert.Equal(t, "int", v.Value.A) // A is v2
|
||||||
assert.True(t, n.GetRequired().Value)
|
assert.True(t, n.GetRequired().Value)
|
||||||
assert.True(t, n.GetAllowEmptyValue().Value)
|
assert.True(t, n.GetAllowEmptyValue().Value)
|
||||||
|
assert.Len(t, n.GetExtensions(), 1)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,11 @@ func (p *PathItem) FindExtension(ext string) *low.ValueReference[any] {
|
|||||||
return low.FindItemInMap[any](ext, p.Extensions)
|
return low.FindItemInMap[any](ext, p.Extensions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetExtensions returns all PathItem extensions and satisfies the low.HasExtensions interface.
|
||||||
|
func (p *PathItem) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||||
|
return p.Extensions
|
||||||
|
}
|
||||||
|
|
||||||
// Build will extract extensions, parameters and operations for all methods. Every method is handled
|
// Build will extract extensions, parameters and operations for all methods. Every method is handled
|
||||||
// asynchronously, in order to keep things moving quickly for complex operations.
|
// asynchronously, in order to keep things moving quickly for complex operations.
|
||||||
func (p *PathItem) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (p *PathItem) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
|||||||
@@ -107,5 +107,6 @@ parameters:
|
|||||||
|
|
||||||
// hash
|
// hash
|
||||||
assert.Equal(t, n.Hash(), n2.Hash())
|
assert.Equal(t, n.Hash(), n2.Hash())
|
||||||
|
assert.Len(t, n.GetExtensions(), 1)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,11 @@ type Paths struct {
|
|||||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetExtensions returns all Paths extensions and satisfies the low.HasExtensions interface.
|
||||||
|
func (p *Paths) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||||
|
return p.Extensions
|
||||||
|
}
|
||||||
|
|
||||||
// FindPath attempts to locate a PathItem instance, given a path key.
|
// FindPath attempts to locate a PathItem instance, given a path key.
|
||||||
func (p *Paths) FindPath(path string) *low.ValueReference[*PathItem] {
|
func (p *Paths) FindPath(path string) *low.ValueReference[*PathItem] {
|
||||||
for k, j := range p.PathItems {
|
for k, j := range p.PathItems {
|
||||||
|
|||||||
@@ -96,5 +96,6 @@ x-milk: creamy`
|
|||||||
|
|
||||||
// hash
|
// hash
|
||||||
assert.Equal(t, n.Hash(), n2.Hash())
|
assert.Equal(t, n.Hash(), n2.Hash())
|
||||||
|
assert.Len(t, n.GetExtensions(), 1)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,11 @@ func (r *Response) FindExtension(ext string) *low.ValueReference[any] {
|
|||||||
return low.FindItemInMap[any](ext, r.Extensions)
|
return low.FindItemInMap[any](ext, r.Extensions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetExtensions returns all Response extensions and satisfies the low.HasExtensions interface.
|
||||||
|
func (r *Response) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||||
|
return r.Extensions
|
||||||
|
}
|
||||||
|
|
||||||
// FindHeader will attempt to locate a Header value, given a key
|
// FindHeader will attempt to locate a Header value, given a key
|
||||||
func (r *Response) FindHeader(hType string) *low.ValueReference[*Header] {
|
func (r *Response) FindHeader(hType string) *low.ValueReference[*Header] {
|
||||||
return low.FindItemInMap[*Header](hType, r.Headers.Value)
|
return low.FindItemInMap[*Header](hType, r.Headers.Value)
|
||||||
|
|||||||
@@ -110,4 +110,5 @@ headers:
|
|||||||
|
|
||||||
// hash
|
// hash
|
||||||
assert.Equal(t, n.Hash(), n2.Hash())
|
assert.Equal(t, n.Hash(), n2.Hash())
|
||||||
|
assert.Len(t, n.GetExtensions(), 1)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,11 @@ type Responses struct {
|
|||||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetExtensions returns all Responses extensions and satisfies the low.HasExtensions interface.
|
||||||
|
func (r *Responses) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||||
|
return r.Extensions
|
||||||
|
}
|
||||||
|
|
||||||
// Build will extract default value and extensions from node.
|
// Build will extract default value and extensions from node.
|
||||||
func (r *Responses) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (r *Responses) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
r.Extensions = low.ExtractExtensions(root)
|
r.Extensions = low.ExtractExtensions(root)
|
||||||
|
|||||||
@@ -114,5 +114,6 @@ x-tea: warm`
|
|||||||
|
|
||||||
// hash
|
// hash
|
||||||
assert.Equal(t, n.Hash(), n2.Hash())
|
assert.Equal(t, n.Hash(), n2.Hash())
|
||||||
|
assert.Len(t, n.GetExtensions(), 1)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,11 @@ type Scopes struct {
|
|||||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetExtensions returns all Scopes extensions and satisfies the low.HasExtensions interface.
|
||||||
|
func (s *Scopes) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||||
|
return s.Extensions
|
||||||
|
}
|
||||||
|
|
||||||
// FindScope will attempt to locate a scope string using a key.
|
// FindScope will attempt to locate a scope string using a key.
|
||||||
func (s *Scopes) FindScope(scope string) *low.ValueReference[string] {
|
func (s *Scopes) FindScope(scope string) *low.ValueReference[string] {
|
||||||
return low.FindItemInMap[string](scope, s.Values)
|
return low.FindItemInMap[string](scope, s.Values)
|
||||||
|
|||||||
@@ -39,5 +39,6 @@ burgers: chips`
|
|||||||
|
|
||||||
// hash
|
// hash
|
||||||
assert.Equal(t, n.Hash(), n2.Hash())
|
assert.Equal(t, n.Hash(), n2.Hash())
|
||||||
|
assert.Len(t, n.GetExtensions(), 1)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,11 @@ type SecurityScheme struct {
|
|||||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetExtensions returns all SecurityScheme extensions and satisfies the low.HasExtensions interface.
|
||||||
|
func (ss *SecurityScheme) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||||
|
return ss.Extensions
|
||||||
|
}
|
||||||
|
|
||||||
// Build will extract extensions and scopes from the node.
|
// Build will extract extensions and scopes from the node.
|
||||||
func (ss *SecurityScheme) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (ss *SecurityScheme) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
ss.Extensions = low.ExtractExtensions(root)
|
ss.Extensions = low.ExtractExtensions(root)
|
||||||
|
|||||||
@@ -94,5 +94,6 @@ authorizationUrl: https://pb33f.io
|
|||||||
|
|
||||||
// hash
|
// hash
|
||||||
assert.Equal(t, n.Hash(), n2.Hash())
|
assert.Equal(t, n.Hash(), n2.Hash())
|
||||||
|
assert.Len(t, n.GetExtensions(), 1)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,6 +116,11 @@ func (s *Swagger) FindExtension(ext string) *low.ValueReference[any] {
|
|||||||
return low.FindItemInMap[any](ext, s.Extensions)
|
return low.FindItemInMap[any](ext, s.Extensions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetExtensions returns all Swagger/Top level extensions and satisfies the low.HasExtensions interface.
|
||||||
|
func (s *Swagger) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||||
|
return s.Extensions
|
||||||
|
}
|
||||||
|
|
||||||
func CreateDocument(info *datamodel.SpecInfo) (*Swagger, []error) {
|
func CreateDocument(info *datamodel.SpecInfo) (*Swagger, []error) {
|
||||||
|
|
||||||
doc := Swagger{Swagger: low.ValueReference[string]{Value: info.Version, ValueNode: info.RootNode}}
|
doc := Swagger{Swagger: low.ValueReference[string]{Value: info.Version, ValueNode: info.RootNode}}
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ func TestCreateDocument(t *testing.T) {
|
|||||||
assert.Equal(t, true, doc.FindExtension("x-pet").Value)
|
assert.Equal(t, true, doc.FindExtension("x-pet").Value)
|
||||||
assert.Equal(t, true, doc.FindExtension("X-Pet").Value)
|
assert.Equal(t, true, doc.FindExtension("X-Pet").Value)
|
||||||
assert.NotNil(t, doc.GetExternalDocs())
|
assert.NotNil(t, doc.GetExternalDocs())
|
||||||
|
assert.Len(t, doc.GetExtensions(), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocument_Info(t *testing.T) {
|
func TestCreateDocument_Info(t *testing.T) {
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ type Callback struct {
|
|||||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetExtensions returns all Callback extensions and satisfies the low.HasExtensions interface.
|
||||||
|
func (cb *Callback) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||||
|
return cb.Extensions
|
||||||
|
}
|
||||||
|
|
||||||
// FindExpression will locate a string expression and return a ValueReference containing the located PathItem
|
// FindExpression will locate a string expression and return a ValueReference containing the located PathItem
|
||||||
func (cb *Callback) FindExpression(exp string) *low.ValueReference[*PathItem] {
|
func (cb *Callback) FindExpression(exp string) *low.ValueReference[*PathItem] {
|
||||||
return low.FindItemInMap[*PathItem](exp, cb.Expression.Value)
|
return low.FindItemInMap[*PathItem](exp, cb.Expression.Value)
|
||||||
|
|||||||
@@ -150,5 +150,6 @@ beer:
|
|||||||
|
|
||||||
// hash
|
// hash
|
||||||
assert.Equal(t, n.Hash(), n2.Hash())
|
assert.Equal(t, n.Hash(), n2.Hash())
|
||||||
|
assert.Len(t, n.GetExtensions(), 2)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,11 @@ type Components struct {
|
|||||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetExtensions returns all Components extensions and satisfies the low.HasExtensions interface.
|
||||||
|
func (co *Components) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||||
|
return co.Extensions
|
||||||
|
}
|
||||||
|
|
||||||
// Hash will return a consistent SHA256 Hash of the Encoding object
|
// Hash will return a consistent SHA256 Hash of the Encoding object
|
||||||
func (co *Components) Hash() [32]byte {
|
func (co *Components) Hash() [32]byte {
|
||||||
var f []string
|
var f []string
|
||||||
|
|||||||
@@ -221,7 +221,7 @@ func TestComponents_Build_HashEmpty(t *testing.T) {
|
|||||||
err = n.Build(idxNode.Content[0], idx)
|
err = n.Build(idxNode.Content[0], idx)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "seagull", n.FindExtension("x-curry").Value)
|
assert.Equal(t, "seagull", n.FindExtension("x-curry").Value)
|
||||||
|
assert.Len(t, n.GetExtensions(), 1)
|
||||||
assert.Equal(t, "9cf2c6ab3f9ff7e5231fcb391c8af5c47406711d2ca366533f21a8bb2f67edfe",
|
assert.Equal(t, "9cf2c6ab3f9ff7e5231fcb391c8af5c47406711d2ca366533f21a8bb2f67edfe",
|
||||||
low.GenerateHashString(&n))
|
low.GenerateHashString(&n))
|
||||||
|
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ func TestCreateDocument(t *testing.T) {
|
|||||||
assert.Equal(t, "Burger Shop", doc.Info.Value.Title.Value)
|
assert.Equal(t, "Burger Shop", doc.Info.Value.Title.Value)
|
||||||
assert.NotEmpty(t, doc.Info.Value.Title.Value)
|
assert.NotEmpty(t, doc.Info.Value.Title.Value)
|
||||||
assert.Equal(t, "https://pb33f.io/schema", doc.JsonSchemaDialect.Value)
|
assert.Equal(t, "https://pb33f.io/schema", doc.JsonSchemaDialect.Value)
|
||||||
|
assert.Len(t, doc.GetExtensions(), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocument_Info(t *testing.T) {
|
func TestCreateDocument_Info(t *testing.T) {
|
||||||
|
|||||||
@@ -96,6 +96,11 @@ func (d *Document) FindSecurityRequirement(name string) []low.ValueReference[str
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetExtensions returns all Document extensions and satisfies the low.HasExtensions interface.
|
||||||
|
func (d *Document) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||||
|
return d.Extensions
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Document) GetExternalDocs() *low.NodeReference[any] {
|
func (d *Document) GetExternalDocs() *low.NodeReference[any] {
|
||||||
return &low.NodeReference[any]{
|
return &low.NodeReference[any]{
|
||||||
KeyNode: d.ExternalDocs.KeyNode,
|
KeyNode: d.ExternalDocs.KeyNode,
|
||||||
|
|||||||
@@ -47,6 +47,11 @@ func (h *Header) FindContent(ext string) *low.ValueReference[*MediaType] {
|
|||||||
return low.FindItemInMap[*MediaType](ext, h.Content.Value)
|
return low.FindItemInMap[*MediaType](ext, h.Content.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetExtensions returns all Header extensions and satisfies the low.HasExtensions interface.
|
||||||
|
func (h *Header) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||||
|
return h.Extensions
|
||||||
|
}
|
||||||
|
|
||||||
// Hash will return a consistent SHA256 Hash of the Header object
|
// Hash will return a consistent SHA256 Hash of the Header object
|
||||||
func (h *Header) Hash() [32]byte {
|
func (h *Header) Hash() [32]byte {
|
||||||
var f []string
|
var f []string
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ content:
|
|||||||
|
|
||||||
ext := n.FindExtension("x-family-love").Value
|
ext := n.FindExtension("x-family-love").Value
|
||||||
assert.Equal(t, "strong", ext)
|
assert.Equal(t, "strong", ext)
|
||||||
|
assert.Len(t, n.GetExtensions(), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHeader_Build_Success_Examples(t *testing.T) {
|
func TestHeader_Build_Success_Examples(t *testing.T) {
|
||||||
|
|||||||
@@ -35,6 +35,11 @@ type Link struct {
|
|||||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetExtensions returns all Link extensions and satisfies the low.HasExtensions interface.
|
||||||
|
func (l *Link) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||||
|
return l.Extensions
|
||||||
|
}
|
||||||
|
|
||||||
// FindParameter will attempt to locate a parameter string value, using a parameter name input.
|
// FindParameter will attempt to locate a parameter string value, using a parameter name input.
|
||||||
func (l *Link) FindParameter(pName string) *low.ValueReference[string] {
|
func (l *Link) FindParameter(pName string) *low.ValueReference[string] {
|
||||||
return low.FindItemInMap[string](pName, l.Parameters)
|
return low.FindItemInMap[string](pName, l.Parameters)
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ x-linky: slinky
|
|||||||
|
|
||||||
assert.NotNil(t, n.Server.Value)
|
assert.NotNil(t, n.Server.Value)
|
||||||
assert.Equal(t, "https://pb33f.io", n.Server.Value.URL.Value)
|
assert.Equal(t, "https://pb33f.io", n.Server.Value.URL.Value)
|
||||||
|
assert.Len(t, n.GetExtensions(), 1)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,11 @@ type MediaType struct {
|
|||||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetExtensions returns all MediaType extensions and satisfies the low.HasExtensions interface.
|
||||||
|
func (mt *MediaType) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||||
|
return mt.Extensions
|
||||||
|
}
|
||||||
|
|
||||||
// FindExtension will attempt to locate an extension with the supplied name.
|
// FindExtension will attempt to locate an extension with the supplied name.
|
||||||
func (mt *MediaType) FindExtension(ext string) *low.ValueReference[any] {
|
func (mt *MediaType) FindExtension(ext string) *low.ValueReference[any] {
|
||||||
return low.FindItemInMap[any](ext, mt.Extensions)
|
return low.FindItemInMap[any](ext, mt.Extensions)
|
||||||
|
|||||||
@@ -141,4 +141,5 @@ example: a thing`
|
|||||||
|
|
||||||
// hash
|
// hash
|
||||||
assert.Equal(t, n.Hash(), n2.Hash())
|
assert.Equal(t, n.Hash(), n2.Hash())
|
||||||
|
assert.Len(t, n.GetExtensions(), 1)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,11 @@ type OAuthFlows struct {
|
|||||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetExtensions returns all OAuthFlows extensions and satisfies the low.HasExtensions interface.
|
||||||
|
func (o *OAuthFlows) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||||
|
return o.Extensions
|
||||||
|
}
|
||||||
|
|
||||||
// FindExtension will attempt to locate an extension with the supplied name.
|
// FindExtension will attempt to locate an extension with the supplied name.
|
||||||
func (o *OAuthFlows) FindExtension(ext string) *low.ValueReference[any] {
|
func (o *OAuthFlows) FindExtension(ext string) *low.ValueReference[any] {
|
||||||
return low.FindItemInMap[any](ext, o.Extensions)
|
return low.FindItemInMap[any](ext, o.Extensions)
|
||||||
@@ -89,6 +94,11 @@ type OAuthFlow struct {
|
|||||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetExtensions returns all OAuthFlow extensions and satisfies the low.HasExtensions interface.
|
||||||
|
func (o *OAuthFlow) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||||
|
return o.Extensions
|
||||||
|
}
|
||||||
|
|
||||||
// FindScope attempts to locate a scope using a specified name.
|
// FindScope attempts to locate a scope using a specified name.
|
||||||
func (o *OAuthFlow) FindScope(scope string) *low.ValueReference[string] {
|
func (o *OAuthFlow) FindScope(scope string) *low.ValueReference[string] {
|
||||||
return low.FindItemInMap[string](scope, o.Scopes.Value)
|
return low.FindItemInMap[string](scope, o.Scopes.Value)
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ x-tasty: herbs
|
|||||||
assert.Equal(t, "https://pb33f.io/token", n.TokenUrl.Value)
|
assert.Equal(t, "https://pb33f.io/token", n.TokenUrl.Value)
|
||||||
assert.Equal(t, "https://pb33f.io/refresh", n.RefreshUrl.Value)
|
assert.Equal(t, "https://pb33f.io/refresh", n.RefreshUrl.Value)
|
||||||
assert.Equal(t, "vanilla", n.FindScope("fresh:cake").Value)
|
assert.Equal(t, "vanilla", n.FindScope("fresh:cake").Value)
|
||||||
|
assert.Len(t, n.GetExtensions(), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOAuthFlow_Build_Implicit(t *testing.T) {
|
func TestOAuthFlow_Build_Implicit(t *testing.T) {
|
||||||
@@ -57,6 +58,7 @@ x-tasty: herbs`
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "herbs", n.FindExtension("x-tasty").Value)
|
assert.Equal(t, "herbs", n.FindExtension("x-tasty").Value)
|
||||||
assert.Equal(t, "https://pb33f.io/auth", n.Implicit.Value.AuthorizationUrl.Value)
|
assert.Equal(t, "https://pb33f.io/auth", n.Implicit.Value.AuthorizationUrl.Value)
|
||||||
|
assert.Len(t, n.GetExtensions(), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOAuthFlow_Build_Implicit_Fail(t *testing.T) {
|
func TestOAuthFlow_Build_Implicit_Fail(t *testing.T) {
|
||||||
|
|||||||
@@ -51,6 +51,11 @@ func (p *Parameter) FindExtension(ext string) *low.ValueReference[any] {
|
|||||||
return low.FindItemInMap[any](ext, p.Extensions)
|
return low.FindItemInMap[any](ext, p.Extensions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetExtensions returns all extensions for Parameter.
|
||||||
|
func (p *Parameter) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||||
|
return p.Extensions
|
||||||
|
}
|
||||||
|
|
||||||
// Build will extract examples, extensions and content/media types.
|
// Build will extract examples, extensions and content/media types.
|
||||||
func (p *Parameter) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (p *Parameter) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
p.Extensions = low.ExtractExtensions(root)
|
p.Extensions = low.ExtractExtensions(root)
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ content:
|
|||||||
|
|
||||||
ext := n.FindExtension("x-family-love").Value
|
ext := n.FindExtension("x-family-love").Value
|
||||||
assert.Equal(t, "strong", ext)
|
assert.Equal(t, "strong", ext)
|
||||||
|
assert.Len(t, n.GetExtensions(), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParameter_Build_Success_Examples(t *testing.T) {
|
func TestParameter_Build_Success_Examples(t *testing.T) {
|
||||||
|
|||||||
@@ -99,6 +99,11 @@ func (p *PathItem) FindExtension(ext string) *low.ValueReference[any] {
|
|||||||
return low.FindItemInMap[any](ext, p.Extensions)
|
return low.FindItemInMap[any](ext, p.Extensions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetExtensions returns all PathItem extensions and satisfies the low.HasExtensions interface.
|
||||||
|
func (p *PathItem) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||||
|
return p.Extensions
|
||||||
|
}
|
||||||
|
|
||||||
// Build extracts extensions, parameters, servers and each http method defined.
|
// Build extracts extensions, parameters, servers and each http method defined.
|
||||||
// everything is extracted asynchronously for speed.
|
// everything is extracted asynchronously for speed.
|
||||||
func (p *PathItem) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (p *PathItem) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
|||||||
@@ -79,4 +79,5 @@ summary: it's another path item`
|
|||||||
|
|
||||||
// hash
|
// hash
|
||||||
assert.Equal(t, n.Hash(), n2.Hash())
|
assert.Equal(t, n.Hash(), n2.Hash())
|
||||||
|
assert.Len(t, n.GetExtensions(), 1)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,6 +50,11 @@ func (p *Paths) FindExtension(ext string) *low.ValueReference[any] {
|
|||||||
return low.FindItemInMap[any](ext, p.Extensions)
|
return low.FindItemInMap[any](ext, p.Extensions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetExtensions returns all Paths extensions and satisfies the low.HasExtensions interface.
|
||||||
|
func (p *Paths) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||||
|
return p.Extensions
|
||||||
|
}
|
||||||
|
|
||||||
// Build will extract extensions and all PathItems. This happens asynchronously for speed.
|
// Build will extract extensions and all PathItems. This happens asynchronously for speed.
|
||||||
func (p *Paths) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (p *Paths) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
p.Extensions = low.ExtractExtensions(root)
|
p.Extensions = low.ExtractExtensions(root)
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ x-milk: cold`
|
|||||||
assert.Len(t, path.Parameters.Value, 1)
|
assert.Len(t, path.Parameters.Value, 1)
|
||||||
assert.Equal(t, "cold", n.FindExtension("x-milk").Value)
|
assert.Equal(t, "cold", n.FindExtension("x-milk").Value)
|
||||||
assert.Equal(t, "hello", path.Parameters.Value[0].Value.Name.Value)
|
assert.Equal(t, "hello", path.Parameters.Value[0].Value.Name.Value)
|
||||||
|
assert.Len(t, n.GetExtensions(), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPaths_Build_Fail(t *testing.T) {
|
func TestPaths_Build_Fail(t *testing.T) {
|
||||||
|
|||||||
@@ -27,6 +27,11 @@ func (rb *RequestBody) FindExtension(ext string) *low.ValueReference[any] {
|
|||||||
return low.FindItemInMap[any](ext, rb.Extensions)
|
return low.FindItemInMap[any](ext, rb.Extensions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetExtensions returns all RequestBody extensions and satisfies the low.HasExtensions interface.
|
||||||
|
func (rb *RequestBody) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||||
|
return rb.Extensions
|
||||||
|
}
|
||||||
|
|
||||||
// FindContent attempts to find content/MediaType defined using a specified name.
|
// FindContent attempts to find content/MediaType defined using a specified name.
|
||||||
func (rb *RequestBody) FindContent(cType string) *low.ValueReference[*MediaType] {
|
func (rb *RequestBody) FindContent(cType string) *low.ValueReference[*MediaType] {
|
||||||
return low.FindItemInMap[*MediaType](cType, rb.Content.Value)
|
return low.FindItemInMap[*MediaType](cType, rb.Content.Value)
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ x-requesto: presto`
|
|||||||
assert.True(t, n.Required.Value)
|
assert.True(t, n.Required.Value)
|
||||||
assert.Equal(t, "nice.", n.FindContent("fresh/fish").Value.Example.Value)
|
assert.Equal(t, "nice.", n.FindContent("fresh/fish").Value.Example.Value)
|
||||||
assert.Equal(t, "presto", n.FindExtension("x-requesto").Value)
|
assert.Equal(t, "presto", n.FindExtension("x-requesto").Value)
|
||||||
|
assert.Len(t, n.GetExtensions(), 1)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,11 @@ func (r *Response) FindExtension(ext string) *low.ValueReference[any] {
|
|||||||
return low.FindItemInMap[any](ext, r.Extensions)
|
return low.FindItemInMap[any](ext, r.Extensions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetExtensions returns all OAuthFlow extensions and satisfies the low.HasExtensions interface.
|
||||||
|
func (r *Response) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||||
|
return r.Extensions
|
||||||
|
}
|
||||||
|
|
||||||
// FindContent will attempt to locate a MediaType instance using the supplied key.
|
// FindContent will attempt to locate a MediaType instance using the supplied key.
|
||||||
func (r *Response) FindContent(cType string) *low.ValueReference[*MediaType] {
|
func (r *Response) FindContent(cType string) *low.ValueReference[*MediaType] {
|
||||||
return low.FindItemInMap[*MediaType](cType, r.Content.Value)
|
return low.FindItemInMap[*MediaType](cType, r.Content.Value)
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ default:
|
|||||||
assert.Equal(t, "c009b2046101bc03df802b4cf23f78176931137e6115bf7b445ca46856c06b51",
|
assert.Equal(t, "c009b2046101bc03df802b4cf23f78176931137e6115bf7b445ca46856c06b51",
|
||||||
low.GenerateHashString(&n))
|
low.GenerateHashString(&n))
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResponses_NoDefault(t *testing.T) {
|
func TestResponses_NoDefault(t *testing.T) {
|
||||||
@@ -98,6 +99,9 @@ x-shoes: old`
|
|||||||
assert.Equal(t, "54ab66e6cb8bd226940f421c2387e45215b84c946182435dfe2a3036043fa07c",
|
assert.Equal(t, "54ab66e6cb8bd226940f421c2387e45215b84c946182435dfe2a3036043fa07c",
|
||||||
low.GenerateHashString(&n))
|
low.GenerateHashString(&n))
|
||||||
|
|
||||||
|
assert.Len(t, n.FindResponseByCode("200").Value.GetExtensions(), 1)
|
||||||
|
assert.Len(t, n.GetExtensions(), 1)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResponses_Build_FailCodes_WrongType(t *testing.T) {
|
func TestResponses_Build_FailCodes_WrongType(t *testing.T) {
|
||||||
|
|||||||
@@ -38,6 +38,11 @@ type Responses struct {
|
|||||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetExtensions returns all Responses extensions and satisfies the low.HasExtensions interface.
|
||||||
|
func (r *Responses) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||||
|
return r.Extensions
|
||||||
|
}
|
||||||
|
|
||||||
// Build will extract default response and all Response objects for each code
|
// Build will extract default response and all Response objects for each code
|
||||||
func (r *Responses) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (r *Responses) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
r.Extensions = low.ExtractExtensions(root)
|
r.Extensions = low.ExtractExtensions(root)
|
||||||
|
|||||||
@@ -58,6 +58,11 @@ func (ss *SecurityScheme) FindExtension(ext string) *low.ValueReference[any] {
|
|||||||
return low.FindItemInMap[any](ext, ss.Extensions)
|
return low.FindItemInMap[any](ext, ss.Extensions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetExtensions returns all SecurityScheme extensions and satisfies the low.HasExtensions interface.
|
||||||
|
func (ss *SecurityScheme) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||||
|
return ss.Extensions
|
||||||
|
}
|
||||||
|
|
||||||
// Build will extract OAuthFlows and extensions from the node.
|
// Build will extract OAuthFlows and extensions from the node.
|
||||||
func (ss *SecurityScheme) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (ss *SecurityScheme) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
ss.Extensions = low.ExtractExtensions(root)
|
ss.Extensions = low.ExtractExtensions(root)
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ x-milk: please`
|
|||||||
assert.Equal(t, "https://pb33f.io/openid", n.OpenIdConnectUrl.Value)
|
assert.Equal(t, "https://pb33f.io/openid", n.OpenIdConnectUrl.Value)
|
||||||
assert.Equal(t, "please", n.FindExtension("x-milk").Value)
|
assert.Equal(t, "please", n.FindExtension("x-milk").Value)
|
||||||
assert.Equal(t, "https://pb33f.io", n.Flows.Value.Implicit.Value.TokenUrl.Value)
|
assert.Equal(t, "https://pb33f.io", n.Flows.Value.Implicit.Value.TokenUrl.Value)
|
||||||
|
assert.Len(t, n.GetExtensions(), 1)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,11 @@ type Server struct {
|
|||||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetExtensions returns all Paths extensions and satisfies the low.HasExtensions interface.
|
||||||
|
func (s *Server) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||||
|
return s.Extensions
|
||||||
|
}
|
||||||
|
|
||||||
// FindVariable attempts to locate a ServerVariable instance using the supplied key.
|
// FindVariable attempts to locate a ServerVariable instance using the supplied key.
|
||||||
func (s *Server) FindVariable(serverVar string) *low.ValueReference[*ServerVariable] {
|
func (s *Server) FindVariable(serverVar string) *low.ValueReference[*ServerVariable] {
|
||||||
return low.FindItemInMap[*ServerVariable](serverVar, s.Variables.Value)
|
return low.FindItemInMap[*ServerVariable](serverVar, s.Variables.Value)
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ import (
|
|||||||
|
|
||||||
func TestServer_Build(t *testing.T) {
|
func TestServer_Build(t *testing.T) {
|
||||||
|
|
||||||
yml := `url: https://pb33f.io
|
yml := `x-coffee: hot
|
||||||
|
url: https://pb33f.io
|
||||||
description: high quality software for developers.
|
description: high quality software for developers.
|
||||||
variables:
|
variables:
|
||||||
var1:
|
var1:
|
||||||
@@ -45,6 +46,8 @@ variables:
|
|||||||
assert.Equal(t, "00eef99ee4a7b746be7b4ccdece59c5a96222c6206f846fafed782c9f3f9b46b",
|
assert.Equal(t, "00eef99ee4a7b746be7b4ccdece59c5a96222c6206f846fafed782c9f3f9b46b",
|
||||||
low.GenerateHashString(s.Value))
|
low.GenerateHashString(s.Value))
|
||||||
|
|
||||||
|
assert.Len(t, n.GetExtensions(), 1)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServer_Build_NoVars(t *testing.T) {
|
func TestServer_Build_NoVars(t *testing.T) {
|
||||||
|
|||||||
120
document_test.go
120
document_test.go
@@ -5,6 +5,9 @@ package libopenapi
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/pb33f/libopenapi/datamodel/high"
|
||||||
|
low "github.com/pb33f/libopenapi/datamodel/low/base"
|
||||||
|
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||||
"github.com/pb33f/libopenapi/resolver"
|
"github.com/pb33f/libopenapi/resolver"
|
||||||
"github.com/pb33f/libopenapi/utils"
|
"github.com/pb33f/libopenapi/utils"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -534,3 +537,120 @@ components:
|
|||||||
// Loop Point: #/components/schemas/Two
|
// Loop Point: #/components/schemas/Two
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If you're using complex types with OpenAPI Extensions, it's simple to unpack extensions into complex
|
||||||
|
// types using `high.UnpackExtensions()`. libopenapi retains the original raw data in the low model (not the high)
|
||||||
|
// which means unpacking them can be a little complex.
|
||||||
|
//
|
||||||
|
// This example demonstrates how to use the `UnpackExtensions` with custom OpenAPI extensions.
|
||||||
|
func ExampleNewDocument_unpacking_extensions() {
|
||||||
|
|
||||||
|
// define an example struct representing a cake
|
||||||
|
type cake struct {
|
||||||
|
Candles int
|
||||||
|
Frosting string
|
||||||
|
}
|
||||||
|
|
||||||
|
// define a struct that holds a map of cake pointers.
|
||||||
|
type cakes struct {
|
||||||
|
Description string
|
||||||
|
Cakes map[string]*cake
|
||||||
|
}
|
||||||
|
|
||||||
|
// define a struct representing a burger
|
||||||
|
type burger struct {
|
||||||
|
Sauce string
|
||||||
|
Patty string
|
||||||
|
}
|
||||||
|
|
||||||
|
// define a struct that holds a map of cake pointers
|
||||||
|
type burgers struct {
|
||||||
|
Description string
|
||||||
|
Burgers map[string]*burger
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a specification with a schema and parameter that use complex custom cakes and burgers extensions.
|
||||||
|
spec := `openapi: "3.1"
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
SchemaOne:
|
||||||
|
description: "Some schema with custom complex extensions"
|
||||||
|
x-custom-cakes:
|
||||||
|
description: some cakes
|
||||||
|
cakes:
|
||||||
|
someCake:
|
||||||
|
candles: 10
|
||||||
|
frosting: blue
|
||||||
|
anotherCake:
|
||||||
|
candles: 1
|
||||||
|
frosting: green
|
||||||
|
parameters:
|
||||||
|
ParameterOne:
|
||||||
|
description: "Some parameter also using complex extensions"
|
||||||
|
x-custom-burgers:
|
||||||
|
description: some burgers
|
||||||
|
burgers:
|
||||||
|
someBurger:
|
||||||
|
sauce: ketchup
|
||||||
|
patty: meat
|
||||||
|
anotherBurger:
|
||||||
|
sauce: mayo
|
||||||
|
patty: lamb`
|
||||||
|
// create a new document from specification bytes
|
||||||
|
doc, err := NewDocument([]byte(spec))
|
||||||
|
|
||||||
|
// if anything went wrong, an error is thrown
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("cannot create new document: %e", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// build a v3 model.
|
||||||
|
docModel, errs := doc.BuildV3Model()
|
||||||
|
|
||||||
|
// if anything went wrong building, indexing and resolving the model, an error is thrown
|
||||||
|
if errs != nil {
|
||||||
|
panic(fmt.Sprintf("cannot create new document: %e", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// get a reference to SchemaOne and ParameterOne
|
||||||
|
schemaOne := docModel.Model.Components.Schemas["SchemaOne"].Schema()
|
||||||
|
parameterOne := docModel.Model.Components.Parameters["ParameterOne"]
|
||||||
|
|
||||||
|
// unpack schemaOne extensions into complex `cakes` type
|
||||||
|
schemaOneExtensions, schemaUnpackErrors := high.UnpackExtensions[cakes, *low.Schema](schemaOne)
|
||||||
|
if schemaUnpackErrors != nil {
|
||||||
|
panic(fmt.Sprintf("cannot unpack schema extensions: %e", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// unpack parameterOne into complex `burgers` type
|
||||||
|
parameterOneExtensions, paramUnpackErrors := high.UnpackExtensions[burgers, *v3.Parameter](parameterOne)
|
||||||
|
if paramUnpackErrors != nil {
|
||||||
|
panic(fmt.Sprintf("cannot unpack parameter extensions: %e", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// extract extension by name for schemaOne
|
||||||
|
customCakes := schemaOneExtensions["x-custom-cakes"]
|
||||||
|
|
||||||
|
// extract extension by name for schemaOne
|
||||||
|
customBurgers := parameterOneExtensions["x-custom-burgers"]
|
||||||
|
|
||||||
|
// print out schemaOne complex extension details.
|
||||||
|
fmt.Printf("schemaOne 'x-custom-cakes' (%s) has %d cakes, 'someCake' has %d candles and %s frosting\n",
|
||||||
|
customCakes.Description,
|
||||||
|
len(customCakes.Cakes),
|
||||||
|
customCakes.Cakes["someCake"].Candles,
|
||||||
|
customCakes.Cakes["someCake"].Frosting,
|
||||||
|
)
|
||||||
|
|
||||||
|
// print out parameterOne complex extension details.
|
||||||
|
fmt.Printf("parameterOne 'x-custom-burgers' (%s) has %d burgers, 'anotherBurger' has %s sauce and a %s patty\n",
|
||||||
|
customBurgers.Description,
|
||||||
|
len(customBurgers.Burgers),
|
||||||
|
customBurgers.Burgers["anotherBurger"].Sauce,
|
||||||
|
customBurgers.Burgers["anotherBurger"].Patty,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Output: schemaOne 'x-custom-cakes' (some cakes) has 2 cakes, 'someCake' has 10 candles and blue frosting
|
||||||
|
//parameterOne 'x-custom-burgers' (some burgers) has 2 burgers, 'anotherBurger' has mayo sauce and a lamb patty
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user