Skip to content

Commit

Permalink
openapi3gen: add CreateComponentSchemas option to export object schem…
Browse files Browse the repository at this point in the history
…as to components
  • Loading branch information
tcdsv authored and fenollp committed Apr 6, 2024
1 parent 7d030b2 commit d3cf6c0
Show file tree
Hide file tree
Showing 54 changed files with 3,090 additions and 407 deletions.
2 changes: 1 addition & 1 deletion .github/docs/openapi2.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ type Parameter struct {
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
CollectionFormat string `json:"collectionFormat,omitempty" yaml:"collectionFormat,omitempty"`
Type string `json:"type,omitempty" yaml:"type,omitempty"`
Type *openapi3.Types `json:"type,omitempty" yaml:"type,omitempty"`
Format string `json:"format,omitempty" yaml:"format,omitempty"`
Pattern string `json:"pattern,omitempty" yaml:"pattern,omitempty"`
AllowEmptyValue bool `json:"allowEmptyValue,omitempty" yaml:"allowEmptyValue,omitempty"`
Expand Down
44 changes: 42 additions & 2 deletions .github/docs/openapi3.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const (
TypeNumber = "number"
TypeObject = "object"
TypeString = "string"
TypeNull = "null"
)
const (
// FormatOfStringForUUIDOfRFC4122 is an optional predefined format for UUID v1-v5 as specified by RFC4122
Expand Down Expand Up @@ -133,6 +134,9 @@ type AdditionalProperties struct {
func (addProps AdditionalProperties) MarshalJSON() ([]byte, error)
MarshalJSON returns the JSON encoding of AdditionalProperties.

func (addProps AdditionalProperties) MarshalYAML() (interface{}, error)
MarshalYAML returns the YAML encoding of AdditionalProperties.

func (addProps *AdditionalProperties) UnmarshalJSON(data []byte) error
UnmarshalJSON sets AdditionalProperties to a copy of data.

Expand All @@ -150,6 +154,9 @@ func NewCallback(opts ...NewCallbackOption) *Callback
func NewCallbackWithCapacity(cap int) *Callback
NewCallbackWithCapacity builds a callback object of the given capacity.

func (callback *Callback) Delete(key string)
Delete removes the entry associated with key 'key' from 'callback'.

func (callback Callback) JSONLookup(token string) (interface{}, error)
JSONLookup implements
https://github.com/go-openapi/jsonpointer#JSONPointable
Expand Down Expand Up @@ -583,6 +590,9 @@ func (loader *Loader) LoadFromDataWithPath(data []byte, location *url.URL) (*T,
func (loader *Loader) LoadFromFile(location string) (*T, error)
LoadFromFile loads a spec from a local file path

func (loader *Loader) LoadFromIoReader(reader io.Reader) (*T, error)
LoadFromStdin loads a spec from io.Reader

func (loader *Loader) LoadFromStdin() (*T, error)
LoadFromStdin loads a spec from stdin

Expand Down Expand Up @@ -915,6 +925,9 @@ func NewPaths(opts ...NewPathsOption) *Paths
func NewPathsWithCapacity(cap int) *Paths
NewPathsWithCapacity builds a paths object of the given capacity.

func (paths *Paths) Delete(key string)
Delete removes the entry associated with key 'key' from 'paths'.

func (paths *Paths) Find(key string) *PathItem
Find returns a path that matches the key.

Expand Down Expand Up @@ -950,6 +963,9 @@ func (paths *Paths) Map() (m map[string]*PathItem)
func (paths *Paths) MarshalJSON() ([]byte, error)
MarshalJSON returns the JSON encoding of Paths.

func (paths *Paths) MarshalYAML() (any, error)
Support YAML Marshaler interface for gopkg.in/yaml

func (paths *Paths) Set(key string, value *PathItem)
Set adds or replaces key 'key' of 'paths' with 'value'. Note: 'paths' MUST
be non-nil
Expand Down Expand Up @@ -1145,6 +1161,9 @@ func NewResponsesWithCapacity(cap int) *Responses
func (responses *Responses) Default() *ResponseRef
Default returns the default response

func (responses *Responses) Delete(key string)
Delete removes the entry associated with key 'key' from 'responses'.

func (responses Responses) JSONLookup(token string) (interface{}, error)
JSONLookup implements
https://github.com/go-openapi/jsonpointer#JSONPointable
Expand All @@ -1158,6 +1177,9 @@ func (responses *Responses) Map() (m map[string]*ResponseRef)
func (responses *Responses) MarshalJSON() ([]byte, error)
MarshalJSON returns the JSON encoding of Responses.

func (responses *Responses) MarshalYAML() (any, error)
Support YAML Marshaler interface for gopkg.in/yaml

func (responses *Responses) Set(key string, value *ResponseRef)
Set adds or replaces key 'key' of 'responses' with 'value'. Note:
'responses' MUST be non-nil
Expand Down Expand Up @@ -1185,7 +1207,7 @@ type Schema struct {
AnyOf SchemaRefs `json:"anyOf,omitempty" yaml:"anyOf,omitempty"`
AllOf SchemaRefs `json:"allOf,omitempty" yaml:"allOf,omitempty"`
Not *SchemaRef `json:"not,omitempty" yaml:"not,omitempty"`
Type string `json:"type,omitempty" yaml:"type,omitempty"`
Type *Types `json:"type,omitempty" yaml:"type,omitempty"`
Title string `json:"title,omitempty" yaml:"title,omitempty"`
Format string `json:"format,omitempty" yaml:"format,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
Expand Down Expand Up @@ -1287,6 +1309,8 @@ func (schema Schema) MarshalJSON() ([]byte, error)

func (schema *Schema) NewRef() *SchemaRef

func (schema *Schema) PermitsNull() bool

func (schema *Schema) UnmarshalJSON(data []byte) error
UnmarshalJSON sets Schema to a copy of data.

Expand Down Expand Up @@ -1672,7 +1696,7 @@ func (doc *T) JSONLookup(token string) (interface{}, error)
JSONLookup implements
https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable

func (doc T) MarshalJSON() ([]byte, error)
func (doc *T) MarshalJSON() ([]byte, error)
MarshalJSON returns the JSON encoding of T.

func (doc *T) UnmarshalJSON(data []byte) error
Expand Down Expand Up @@ -1709,6 +1733,22 @@ func (tags Tags) Get(name string) *Tag
func (tags Tags) Validate(ctx context.Context, opts ...ValidationOption) error
Validate returns an error if Tags does not comply with the OpenAPI spec.

type Types []string

func (pTypes *Types) Includes(typ string) bool

func (types *Types) Is(typ string) bool

func (pTypes *Types) MarshalJSON() ([]byte, error)

func (pTypes *Types) MarshalYAML() (interface{}, error)

func (types *Types) Permits(typ string) bool

func (types *Types) Slice() []string

func (types *Types) UnmarshalJSON(data []byte) error

type ValidationOption func(options *ValidationOptions)
ValidationOption allows the modification of how the OpenAPI document is
validated.
Expand Down
10 changes: 6 additions & 4 deletions .github/docs/openapi3filter.txt
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ func RegisterBodyDecoder(contentType string, decoder BodyDecoder)
body decoders should not be created/destroyed by multiple goroutines.

func RegisterBodyEncoder(contentType string, encoder BodyEncoder)
RegisterBodyEncoder enables package-wide decoding of contentType values

func TrimJSONPrefix(data []byte) []byte
TrimJSONPrefix trims one of the possible prefixes

Expand All @@ -80,8 +82,7 @@ func UnregisterBodyDecoder(contentType string)
goroutines.

func UnregisterBodyEncoder(contentType string)
This call is not thread-safe: body encoders should not be created/destroyed
by multiple goroutines.
UnregisterBodyEncoder disables package-wide decoding of contentType values

func ValidateParameter(ctx context.Context, input *RequestValidationInput, parameter *openapi3.Parameter) error
ValidateParameter validates a parameter's value by JSON schema. The function
Expand Down Expand Up @@ -152,14 +153,13 @@ func RegisteredBodyDecoder(contentType string) BodyDecoder
by multiple goroutines.

type BodyEncoder func(body interface{}) ([]byte, error)
BodyEncoder really is an (encoding/json).Marshaler

func RegisteredBodyEncoder(contentType string) BodyEncoder
RegisteredBodyEncoder returns the registered body encoder for the given
content type.

If no encoder was registered for the given content type, nil is returned.
This call is not thread-safe: body encoders should not be created/destroyed
by multiple goroutines.

type ContentParameterDecoder func(param *openapi3.Parameter, values []string) (interface{}, *openapi3.Schema, error)
A ContentParameterDecoder takes a parameter definition from the OpenAPI
Expand Down Expand Up @@ -331,6 +331,8 @@ type SecurityRequirementsError struct {

func (err *SecurityRequirementsError) Error() string

func (err SecurityRequirementsError) Unwrap() []error

type StatusCoder interface {
StatusCode() int
}
Expand Down
Empty file.
7 changes: 7 additions & 0 deletions .github/docs/openapi3gen.txt
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,10 @@ type SchemaCustomizerFn func(name string, t reflect.Type, tag reflect.StructTag,
A SchemaCustomizerFn can return an ExcludeSchemaSentinel error to indicate
that the schema for this field should not be included in the final output

type SetSchemar interface {
SetSchema(*openapi3.Schema)
}
SetSchemar allows client to set their own schema definition according to
their specification. Useful when some custom datatype is needed and/or some
custom logic is needed on how the schema values would be generated

3 changes: 2 additions & 1 deletion docs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ set -o pipefail

outdir=.github/docs
mkdir -p "$outdir"
for pkgpath in $(git ls-files | grep / | while read -r path; do dirname "$path"; done | sort -u | grep -vE '[.]git|testdata|cmd/'); do
for pkgpath in $(git ls-files | grep / | while read -r path; do dirname "$path"; done | sort -u | grep -vE '[.]git|testdata|internal|cmd/'); do
echo $pkgpath
go doc -all ./"$pkgpath" | tee "$outdir/${pkgpath////_}.txt"
done

Expand Down
17 changes: 15 additions & 2 deletions maps.sh
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ EOF
}


maplike_ValueSetLen() {
maplike_ValueSetLenDelete() {
cat <<EOF >>"$maplike"
// Value returns the ${name} for key or nil
func (${name} ${type}) Value(key string) ${value_type} {
Expand All @@ -109,6 +109,13 @@ func (${name} ${type}) Len() int {
return len(${name}.m)
}
// Delete removes the entry associated with key 'key' from '${name}'.
func (${name} ${type}) Delete(key string) {
if ${name} != nil && ${name}.m != nil {
delete(${name}.m, key)
}
}
// Map returns ${name} as a 'map'.
// Note: iteration on Go maps is not ordered.
func (${name} ${type}) Map() (m map[string]${value_type}) {
Expand Down Expand Up @@ -213,6 +220,7 @@ test_body() {
require.Equal(t, map[string]${value_type}{}, x.Map())
require.Equal(t, (${value_type})(nil), x.Value("key"))
require.Panics(t, func() { x.Set("key", &${value_type#'*'}{}) })
require.NotPanics(t, func() { x.Delete("key") })
})
t.Run("nonnil", func(t *testing.T) {
x := &${type#'*'}{}
Expand All @@ -223,6 +231,11 @@ test_body() {
require.Equal(t, 1, x.Len())
require.Equal(t, map[string]${value_type}{"key": {}}, x.Map())
require.Equal(t, &${value_type#'*'}{}, x.Value("key"))
x.Delete("key")
require.Equal(t, 0, x.Len())
require.Equal(t, map[string]${value_type}{}, x.Map())
require.Equal(t, (${value_type})(nil), x.Value("key"))
require.NotPanics(t, func() { x.Delete("key") })
})
})
Expand All @@ -241,7 +254,7 @@ for i in "${!types[@]}"; do
name=${names[$i]}

type="$type" name="$name" value_type="$value_type" maplike_NewWithCapa
type="$type" name="$name" value_type="$value_type" maplike_ValueSetLen
type="$type" name="$name" value_type="$value_type" maplike_ValueSetLenDelete
type="$type" name="$name" deref_v="$deref_v" maplike_Pointable
type="$type" name="$name" value_type="$value_type" maplike_UnMarsh
[[ $((i+1)) != "${#types[@]}" ]] && echo >>"$maplike"
Expand Down
4 changes: 2 additions & 2 deletions openapi2/parameter.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ type Parameter struct {
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
CollectionFormat string `json:"collectionFormat,omitempty" yaml:"collectionFormat,omitempty"`
Type string `json:"type,omitempty" yaml:"type,omitempty"`
Type *openapi3.Types `json:"type,omitempty" yaml:"type,omitempty"`
Format string `json:"format,omitempty" yaml:"format,omitempty"`
Pattern string `json:"pattern,omitempty" yaml:"pattern,omitempty"`
AllowEmptyValue bool `json:"allowEmptyValue,omitempty" yaml:"allowEmptyValue,omitempty"`
Expand Down Expand Up @@ -76,7 +76,7 @@ func (parameter Parameter) MarshalJSON() ([]byte, error) {
if x := parameter.CollectionFormat; x != "" {
m["collectionFormat"] = x
}
if x := parameter.Type; x != "" {
if x := parameter.Type; x != nil {
m["type"] = x
}
if x := parameter.Format; x != "" {
Expand Down
14 changes: 7 additions & 7 deletions openapi2conv/openapi2_conv.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,8 @@ func ToV3Parameter(components *openapi3.Components, parameter *openapi2.Paramete

case "formData":
format, typ := parameter.Format, parameter.Type
if typ == "file" {
format, typ = "binary", "string"
if typ.Is("file") {
format, typ = "binary", &openapi3.Types{"string"}
}
if parameter.Extensions == nil {
parameter.Extensions = make(map[string]interface{}, 1)
Expand Down Expand Up @@ -347,7 +347,7 @@ func formDataBody(bodies map[string]*openapi3.SchemaRef, reqs map[string]bool, c
}
}
schema := &openapi3.Schema{
Type: "object",
Type: &openapi3.Types{"object"},
Properties: ToV3Schemas(bodies),
Required: requireds,
}
Expand Down Expand Up @@ -772,8 +772,8 @@ func FromV3SchemaRef(schema *openapi3.SchemaRef, components *openapi3.Components
}

if schema.Value != nil {
if schema.Value.Type == "string" && schema.Value.Format == "binary" {
paramType := "file"
if schema.Value.Type.Is("string") && schema.Value.Format == "binary" {
paramType := &openapi3.Types{"file"}
required := false

value, _ := schema.Value.Extensions["x-formData-name"]
Expand Down Expand Up @@ -825,7 +825,7 @@ func FromV3SchemaRef(schema *openapi3.SchemaRef, components *openapi3.Components
for i, v := range schema.Value.AllOf {
schema.Value.AllOf[i], _ = FromV3SchemaRef(v, components)
}
if schema.Value.Nullable {
if schema.Value.PermitsNull() {
schema.Value.Nullable = false
if schema.Value.Extensions == nil {
schema.Value.Extensions = make(map[string]interface{})
Expand Down Expand Up @@ -893,7 +893,7 @@ func FromV3RequestBodyFormData(mediaType *openapi3.MediaType) openapi2.Parameter
val := schemaRef.Value
typ := val.Type
if val.Format == "binary" {
typ = "file"
typ = &openapi3.Types{"file"}
}
required := false
for _, name := range val.Required {
Expand Down
40 changes: 40 additions & 0 deletions openapi3/additionalProperties_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package openapi3_test

import (
"bytes"
"context"
"os"
"testing"

"github.com/stretchr/testify/require"
"gopkg.in/yaml.v3"

"github.com/getkin/kin-openapi/openapi3"
)

func TestMarshalAdditionalProperties(t *testing.T) {
ctx := context.Background()
data, err := os.ReadFile("testdata/test.openapi.additionalproperties.yml")
require.NoError(t, err)

loader := openapi3.NewLoader()
loader.IsExternalRefsAllowed = true
spec, err := loader.LoadFromData(data)
require.NoError(t, err)

err = spec.Validate(ctx)
require.NoError(t, err)

var buf bytes.Buffer
enc := yaml.NewEncoder(&buf)
enc.SetIndent(2)
err = enc.Encode(spec)
require.NoError(t, err)

// Load the doc from the serialized yaml.
spec2, err := loader.LoadFromData(buf.Bytes())
require.NoError(t, err)

err = spec2.Validate(ctx)
require.NoError(t, err)
}
4 changes: 2 additions & 2 deletions openapi3/issue301_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ func TestIssue301(t *testing.T) {
err = doc.Validate(sl.Context)
require.NoError(t, err)

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

require.Equal(t, "boolean", doc.
require.Equal(t, &Types{"boolean"}, doc.
Paths.Value("/other").
Post.Callbacks["myEvent"].Value.
Value("{$request.query.queryUrl}").
Expand Down
2 changes: 1 addition & 1 deletion openapi3/issue341_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func TestIssue341(t *testing.T) {
}
}`, string(bs))

require.Equal(t, "string", doc.
require.Equal(t, &Types{"string"}, doc.
Paths.Value("/testpath").
Get.
Responses.Value("200").Value.
Expand Down
2 changes: 1 addition & 1 deletion openapi3/issue344_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ func TestIssue344(t *testing.T) {
err = doc.Validate(sl.Context)
require.NoError(t, err)

require.Equal(t, "string", doc.Components.Schemas["Test"].Value.Properties["test"].Value.Properties["name"].Value.Type)
require.Equal(t, &Types{"string"}, doc.Components.Schemas["Test"].Value.Properties["test"].Value.Properties["name"].Value.Type)
}
Loading

0 comments on commit d3cf6c0

Please sign in to comment.