diff --git a/bool_or_schema.go b/bool_or_schema.go index 72035e9..acb1998 100644 --- a/bool_or_schema.go +++ b/bool_or_schema.go @@ -71,3 +71,14 @@ func (o *BoolOrSchema) validateSpec(path string, opts *specValidationOptions) [] } return errs } + +func NewBoolOrSchema(v any) *BoolOrSchema { + switch v := v.(type) { + case bool: + return &BoolOrSchema{Allowed: v} + case *RefOrSpec[Schema]: + return &BoolOrSchema{Schema: v} + default: + return nil + } +} diff --git a/callback.go b/callback.go index 87827d4..6d780eb 100644 --- a/callback.go +++ b/callback.go @@ -29,33 +29,77 @@ import ( // '200': // description: callback successfully processed type Callback struct { - Callback map[string]*RefOrSpec[Extendable[PathItem]] + Paths map[string]*RefOrSpec[Extendable[PathItem]] } // MarshalJSON implements json.Marshaler interface. func (o *Callback) MarshalJSON() ([]byte, error) { - return json.Marshal(&o.Callback) + return json.Marshal(&o.Paths) } // UnmarshalJSON implements json.Unmarshaler interface. func (o *Callback) UnmarshalJSON(data []byte) error { - return json.Unmarshal(data, &o.Callback) + return json.Unmarshal(data, &o.Paths) } // MarshalYAML implements yaml.Marshaler interface. func (o *Callback) MarshalYAML() (any, error) { - return o.Callback, nil + return o.Paths, nil } // UnmarshalYAML implements yaml.Unmarshaler interface. func (o *Callback) UnmarshalYAML(node *yaml.Node) error { - return node.Decode(&o.Callback) + return node.Decode(&o.Paths) } func (o *Callback) validateSpec(location string, opts *specValidationOptions) []*validationError { var errs []*validationError - for k, v := range o.Callback { + for k, v := range o.Paths { errs = append(errs, v.validateSpec(joinLoc(location, k), opts)...) } return nil } + +func (o *Callback) Add(expression string, item *RefOrSpec[Extendable[PathItem]]) *Callback { + if o.Paths == nil { + o.Paths = make(map[string]*RefOrSpec[Extendable[PathItem]], 1) + } + o.Paths[expression] = item + return o +} + +type CallbackBuilder struct { + spec *RefOrSpec[Extendable[Callback]] +} + +func NewCallbackBuilder() *CallbackBuilder { + return &CallbackBuilder{ + spec: NewRefOrExtSpec[Callback](&Callback{ + Paths: make(map[string]*RefOrSpec[Extendable[PathItem]]), + }), + } +} + +func (b *CallbackBuilder) Build() *RefOrSpec[Extendable[Callback]] { + return b.spec +} + +func (b *CallbackBuilder) Extensions(v map[string]any) *CallbackBuilder { + b.spec.Spec.Extensions = v + return b +} + +func (b *CallbackBuilder) AddExt(name string, value any) *CallbackBuilder { + b.spec.Spec.Add(name, value) + return b +} + +func (b *CallbackBuilder) Paths(paths map[string]*RefOrSpec[Extendable[PathItem]]) *CallbackBuilder { + b.spec.Spec.Spec.Paths = paths + return b +} + +func (b *CallbackBuilder) AddPathItem(expression string, item *RefOrSpec[Extendable[PathItem]]) *CallbackBuilder { + b.spec.Spec.Spec.Add(expression, item) + return b +} diff --git a/components.go b/components.go index eeb6c56..af2f41f 100644 --- a/components.go +++ b/components.go @@ -1,7 +1,5 @@ package openapi -import "fmt" - // Components holds a set of reusable objects for different aspects of the OAS. // All objects defined within the components object will have no effect on the API unless they are explicitly referenced // from properties outside the components object. @@ -100,156 +98,61 @@ type Components struct { Paths map[string]*RefOrSpec[Extendable[PathItem]] `json:"paths,omitempty" yaml:"paths,omitempty"` } -// WithRefOrSpec adds the given object to the appropriate list based on a type and returns the current object (self|this). -func (o *Components) WithRefOrSpec(name string, v any) *Components { +// Add adds the given object to the appropriate list based on a type and returns the current object (self|this). +func (o *Components) Add(name string, v any) *Components { switch spec := v.(type) { case *RefOrSpec[Schema]: if o.Schemas == nil { o.Schemas = make(map[string]*RefOrSpec[Schema], 1) } o.Schemas[name] = spec - case *Schema: - if o.Schemas == nil { - o.Schemas = make(map[string]*RefOrSpec[Schema], 1) - } - o.Schemas[name] = NewRefOrSpec[Schema](spec) case *RefOrSpec[Extendable[Response]]: if o.Responses == nil { o.Responses = make(map[string]*RefOrSpec[Extendable[Response]], 1) } o.Responses[name] = spec - case *Extendable[Response]: - if o.Responses == nil { - o.Responses = make(map[string]*RefOrSpec[Extendable[Response]], 1) - } - o.Responses[name] = NewRefOrSpec[Extendable[Response]](spec) - case *Response: - if o.Responses == nil { - o.Responses = make(map[string]*RefOrSpec[Extendable[Response]], 1) - } - o.Responses[name] = NewRefOrSpec[Extendable[Response]](NewExtendable(spec)) case *RefOrSpec[Extendable[Parameter]]: if o.Parameters == nil { o.Parameters = make(map[string]*RefOrSpec[Extendable[Parameter]], 1) } o.Parameters[name] = spec - case *Extendable[Parameter]: - if o.Parameters == nil { - o.Parameters = make(map[string]*RefOrSpec[Extendable[Parameter]], 1) - } - o.Parameters[name] = NewRefOrSpec[Extendable[Parameter]](spec) - case *Parameter: - if o.Parameters == nil { - o.Parameters = make(map[string]*RefOrSpec[Extendable[Parameter]], 1) - } - o.Parameters[name] = NewRefOrSpec[Extendable[Parameter]](NewExtendable(spec)) case *RefOrSpec[Extendable[Example]]: if o.Examples == nil { o.Examples = make(map[string]*RefOrSpec[Extendable[Example]], 1) } o.Examples[name] = spec - case *Extendable[Example]: - if o.Examples == nil { - o.Examples = make(map[string]*RefOrSpec[Extendable[Example]], 1) - } - o.Examples[name] = NewRefOrSpec[Extendable[Example]](spec) - case *Example: - if o.Examples == nil { - o.Examples = make(map[string]*RefOrSpec[Extendable[Example]], 1) - } - o.Examples[name] = NewRefOrSpec[Extendable[Example]](NewExtendable(spec)) case *RefOrSpec[Extendable[RequestBody]]: if o.RequestBodies == nil { o.RequestBodies = make(map[string]*RefOrSpec[Extendable[RequestBody]], 1) } o.RequestBodies[name] = spec - case *Extendable[RequestBody]: - if o.RequestBodies == nil { - o.RequestBodies = make(map[string]*RefOrSpec[Extendable[RequestBody]], 1) - } - o.RequestBodies[name] = NewRefOrSpec[Extendable[RequestBody]](spec) - case *RequestBody: - if o.RequestBodies == nil { - o.RequestBodies = make(map[string]*RefOrSpec[Extendable[RequestBody]], 1) - } - o.RequestBodies[name] = NewRefOrSpec[Extendable[RequestBody]](NewExtendable(spec)) case *RefOrSpec[Extendable[Header]]: if o.Headers == nil { o.Headers = make(map[string]*RefOrSpec[Extendable[Header]], 1) } o.Headers[name] = spec - case *Extendable[Header]: - if o.Headers == nil { - o.Headers = make(map[string]*RefOrSpec[Extendable[Header]], 1) - } - o.Headers[name] = NewRefOrSpec[Extendable[Header]](spec) - case *Header: - if o.Headers == nil { - o.Headers = make(map[string]*RefOrSpec[Extendable[Header]], 1) - } - o.Headers[name] = NewRefOrSpec[Extendable[Header]](NewExtendable(spec)) case *RefOrSpec[Extendable[SecurityScheme]]: if o.SecuritySchemes == nil { o.SecuritySchemes = make(map[string]*RefOrSpec[Extendable[SecurityScheme]], 1) } o.SecuritySchemes[name] = spec - case *Extendable[SecurityScheme]: - if o.SecuritySchemes == nil { - o.SecuritySchemes = make(map[string]*RefOrSpec[Extendable[SecurityScheme]], 1) - } - o.SecuritySchemes[name] = NewRefOrSpec[Extendable[SecurityScheme]](spec) - case *SecurityScheme: - if o.SecuritySchemes == nil { - o.SecuritySchemes = make(map[string]*RefOrSpec[Extendable[SecurityScheme]], 1) - } - o.SecuritySchemes[name] = NewRefOrSpec[Extendable[SecurityScheme]](NewExtendable(spec)) case *RefOrSpec[Extendable[Link]]: if o.Links == nil { o.Links = make(map[string]*RefOrSpec[Extendable[Link]], 1) } o.Links[name] = spec - case *Extendable[Link]: - if o.Links == nil { - o.Links = make(map[string]*RefOrSpec[Extendable[Link]], 1) - } - o.Links[name] = NewRefOrSpec[Extendable[Link]](spec) - case *Link: - if o.Links == nil { - o.Links = make(map[string]*RefOrSpec[Extendable[Link]], 1) - } - o.Links[name] = NewRefOrSpec[Extendable[Link]](NewExtendable(spec)) case *RefOrSpec[Extendable[Callback]]: if o.Callbacks == nil { o.Callbacks = make(map[string]*RefOrSpec[Extendable[Callback]], 1) } o.Callbacks[name] = spec - case *Extendable[Callback]: - if o.Callbacks == nil { - o.Callbacks = make(map[string]*RefOrSpec[Extendable[Callback]], 1) - } - o.Callbacks[name] = NewRefOrSpec[Extendable[Callback]](spec) - case *Callback: - if o.Callbacks == nil { - o.Callbacks = make(map[string]*RefOrSpec[Extendable[Callback]], 1) - } - o.Callbacks[name] = NewRefOrSpec[Extendable[Callback]](NewExtendable(spec)) case *RefOrSpec[Extendable[PathItem]]: if o.Paths == nil { o.Paths = make(map[string]*RefOrSpec[Extendable[PathItem]], 1) } o.Paths[name] = spec - case *Extendable[PathItem]: - if o.Paths == nil { - o.Paths = make(map[string]*RefOrSpec[Extendable[PathItem]], 1) - } - o.Paths[name] = NewRefOrSpec[Extendable[PathItem]](spec) - case *PathItem: - if o.Paths == nil { - o.Paths = make(map[string]*RefOrSpec[Extendable[PathItem]], 1) - } - o.Paths[name] = NewRefOrSpec[Extendable[PathItem]](NewExtendable(spec)) default: - panic(fmt.Errorf("wrong component type: %T", spec)) + // ignore to avoid panic } return o } @@ -309,3 +212,7 @@ func (o *Components) validateSpec(location string, opts *specValidationOptions) return errs } + +func NewComponents() *Extendable[Components] { + return NewExtendable[Components](&Components{}) +} diff --git a/components_test.go b/components_test.go index 11d2641..a2da8d8 100644 --- a/components_test.go +++ b/components_test.go @@ -8,34 +8,17 @@ import ( "github.com/sv-tools/openapi" ) -func TestComponents_WithRefOrSpec(t *testing.T) { +func TestComponents_Add(t *testing.T) { for _, tt := range []struct { name string create func() (string, any) check func(tb testing.TB, c *openapi.Components) }{ - { - name: "schema spec", - create: func() (string, any) { - o := &openapi.Schema{ - Title: "test", - } - return "testSchema", o - }, - check: func(tb testing.TB, c *openapi.Components) { - require.Len(tb, c.Schemas, 1) - require.NotNil(tb, c.Schemas["testSchema"]) - require.NotNil(tb, c.Schemas["testSchema"].Spec) - require.Equal(tb, "test", c.Schemas["testSchema"].Spec.Title) - }, - }, { name: "schema ref or spec", create: func() (string, any) { - o := &openapi.Schema{ - Title: "test", - } - return "testSchema", openapi.NewRefOrSpec[openapi.Schema](o) + o := openapi.NewSchemaBuilder().Title("test").Build() + return "testSchema", o }, check: func(tb testing.TB, c *openapi.Components) { require.Len(tb, c.Schemas, 1) @@ -47,9 +30,7 @@ func TestComponents_WithRefOrSpec(t *testing.T) { { name: "response spec", create: func() (string, any) { - o := &openapi.Response{ - Description: "test", - } + o := openapi.NewResponseBuilder().Description("test").Build() return "testResponse", o }, check: func(tb testing.TB, c *openapi.Components) { @@ -60,44 +41,10 @@ func TestComponents_WithRefOrSpec(t *testing.T) { require.Equal(tb, "test", c.Responses["testResponse"].Spec.Spec.Description) }, }, - { - name: "response ext spec", - create: func() (string, any) { - o := &openapi.Response{ - Description: "test", - } - return "testResponse", openapi.NewExtendable[openapi.Response](o) - }, - check: func(tb testing.TB, c *openapi.Components) { - require.Len(tb, c.Responses, 1) - require.NotNil(tb, c.Responses["testResponse"]) - require.NotNil(tb, c.Responses["testResponse"].Spec) - require.NotNil(tb, c.Responses["testResponse"].Spec.Spec) - require.Equal(tb, "test", c.Responses["testResponse"].Spec.Spec.Description) - }, - }, - { - name: "response ref or spec", - create: func() (string, any) { - o := &openapi.Response{ - Description: "test", - } - return "testResponse", openapi.NewRefOrExtSpec[openapi.Response](o) - }, - check: func(tb testing.TB, c *openapi.Components) { - require.Len(tb, c.Responses, 1) - require.NotNil(tb, c.Responses["testResponse"]) - require.NotNil(tb, c.Responses["testResponse"].Spec) - require.NotNil(tb, c.Responses["testResponse"].Spec.Spec) - require.Equal(tb, "test", c.Responses["testResponse"].Spec.Spec.Description) - }, - }, { name: "parameter spec", create: func() (string, any) { - o := &openapi.Parameter{ - Description: "test", - } + o := openapi.NewParameterBuilder().Description("test").Build() return "testParameter", o }, check: func(tb testing.TB, c *openapi.Components) { @@ -108,44 +55,10 @@ func TestComponents_WithRefOrSpec(t *testing.T) { require.Equal(tb, "test", c.Parameters["testParameter"].Spec.Spec.Description) }, }, - { - name: "parameter ext spec", - create: func() (string, any) { - o := &openapi.Parameter{ - Description: "test", - } - return "testParameter", openapi.NewExtendable[openapi.Parameter](o) - }, - check: func(tb testing.TB, c *openapi.Components) { - require.Len(tb, c.Parameters, 1) - require.NotNil(tb, c.Parameters["testParameter"]) - require.NotNil(tb, c.Parameters["testParameter"].Spec) - require.NotNil(tb, c.Parameters["testParameter"].Spec.Spec) - require.Equal(tb, "test", c.Parameters["testParameter"].Spec.Spec.Description) - }, - }, - { - name: "parameter ref or spec", - create: func() (string, any) { - o := &openapi.Parameter{ - Description: "test", - } - return "testParameter", openapi.NewRefOrExtSpec[openapi.Parameter](o) - }, - check: func(tb testing.TB, c *openapi.Components) { - require.Len(tb, c.Parameters, 1) - require.NotNil(tb, c.Parameters["testParameter"]) - require.NotNil(tb, c.Parameters["testParameter"].Spec) - require.NotNil(tb, c.Parameters["testParameter"].Spec.Spec) - require.Equal(tb, "test", c.Parameters["testParameter"].Spec.Spec.Description) - }, - }, { name: "examples spec", create: func() (string, any) { - o := &openapi.Example{ - Description: "test", - } + o := openapi.NewExampleBuilder().Description("test").Build() return "testExamples", o }, check: func(tb testing.TB, c *openapi.Components) { @@ -156,44 +69,10 @@ func TestComponents_WithRefOrSpec(t *testing.T) { require.Equal(tb, "test", c.Examples["testExamples"].Spec.Spec.Description) }, }, - { - name: "examples ext spec", - create: func() (string, any) { - o := &openapi.Example{ - Description: "test", - } - return "testExamples", openapi.NewExtendable[openapi.Example](o) - }, - check: func(tb testing.TB, c *openapi.Components) { - require.Len(tb, c.Examples, 1) - require.NotNil(tb, c.Examples["testExamples"]) - require.NotNil(tb, c.Examples["testExamples"].Spec) - require.NotNil(tb, c.Examples["testExamples"].Spec.Spec) - require.Equal(tb, "test", c.Examples["testExamples"].Spec.Spec.Description) - }, - }, - { - name: "examples ref or spec", - create: func() (string, any) { - o := &openapi.Example{ - Description: "test", - } - return "testExamples", openapi.NewRefOrExtSpec[openapi.Example](o) - }, - check: func(tb testing.TB, c *openapi.Components) { - require.Len(tb, c.Examples, 1) - require.NotNil(tb, c.Examples["testExamples"]) - require.NotNil(tb, c.Examples["testExamples"].Spec) - require.NotNil(tb, c.Examples["testExamples"].Spec.Spec) - require.Equal(tb, "test", c.Examples["testExamples"].Spec.Spec.Description) - }, - }, { name: "request body spec", create: func() (string, any) { - o := &openapi.RequestBody{ - Description: "test", - } + o := openapi.NewRequestBodyBuilder().Description("test").Build() return "testRequestBodies", o }, check: func(tb testing.TB, c *openapi.Components) { @@ -204,44 +83,10 @@ func TestComponents_WithRefOrSpec(t *testing.T) { require.Equal(tb, "test", c.RequestBodies["testRequestBodies"].Spec.Spec.Description) }, }, - { - name: "request body ext spec", - create: func() (string, any) { - o := &openapi.RequestBody{ - Description: "test", - } - return "testRequestBodies", openapi.NewExtendable[openapi.RequestBody](o) - }, - check: func(tb testing.TB, c *openapi.Components) { - require.Len(tb, c.RequestBodies, 1) - require.NotNil(tb, c.RequestBodies["testRequestBodies"]) - require.NotNil(tb, c.RequestBodies["testRequestBodies"].Spec) - require.NotNil(tb, c.RequestBodies["testRequestBodies"].Spec.Spec) - require.Equal(tb, "test", c.RequestBodies["testRequestBodies"].Spec.Spec.Description) - }, - }, - { - name: "request body ref or spec", - create: func() (string, any) { - o := &openapi.RequestBody{ - Description: "test", - } - return "testRequestBodies", openapi.NewRefOrExtSpec[openapi.RequestBody](o) - }, - check: func(tb testing.TB, c *openapi.Components) { - require.Len(tb, c.RequestBodies, 1) - require.NotNil(tb, c.RequestBodies["testRequestBodies"]) - require.NotNil(tb, c.RequestBodies["testRequestBodies"].Spec) - require.NotNil(tb, c.RequestBodies["testRequestBodies"].Spec.Spec) - require.Equal(tb, "test", c.RequestBodies["testRequestBodies"].Spec.Spec.Description) - }, - }, { name: "headers spec", create: func() (string, any) { - o := &openapi.Header{ - Description: "test", - } + o := openapi.NewHeaderBuilder().Description("test").Build() return "testHeader", o }, check: func(tb testing.TB, c *openapi.Components) { @@ -252,72 +97,8 @@ func TestComponents_WithRefOrSpec(t *testing.T) { require.Equal(tb, "test", c.Headers["testHeader"].Spec.Spec.Description) }, }, - { - name: "headers ext spec", - create: func() (string, any) { - o := &openapi.Header{ - Description: "test", - } - return "testHeader", openapi.NewExtendable[openapi.Header](o) - }, - check: func(tb testing.TB, c *openapi.Components) { - require.Len(tb, c.Headers, 1) - require.NotNil(tb, c.Headers["testHeader"]) - require.NotNil(tb, c.Headers["testHeader"].Spec) - require.NotNil(tb, c.Headers["testHeader"].Spec.Spec) - require.Equal(tb, "test", c.Headers["testHeader"].Spec.Spec.Description) - }, - }, - { - name: "headers ref or spec", - create: func() (string, any) { - o := &openapi.Header{ - Description: "test", - } - return "testHeader", openapi.NewRefOrExtSpec[openapi.Header](o) - }, - check: func(tb testing.TB, c *openapi.Components) { - require.Len(tb, c.Headers, 1) - require.NotNil(tb, c.Headers["testHeader"]) - require.NotNil(tb, c.Headers["testHeader"].Spec) - require.NotNil(tb, c.Headers["testHeader"].Spec.Spec) - require.Equal(tb, "test", c.Headers["testHeader"].Spec.Spec.Description) - }, - }, { name: "security schemes spec", - create: func() (string, any) { - o := &openapi.SecurityScheme{ - Description: "test", - } - return "testSecurityScheme", o - }, - check: func(tb testing.TB, c *openapi.Components) { - require.Len(tb, c.SecuritySchemes, 1) - require.NotNil(tb, c.SecuritySchemes["testSecurityScheme"]) - require.NotNil(tb, c.SecuritySchemes["testSecurityScheme"].Spec) - require.NotNil(tb, c.SecuritySchemes["testSecurityScheme"].Spec.Spec) - require.Equal(tb, "test", c.SecuritySchemes["testSecurityScheme"].Spec.Spec.Description) - }, - }, - { - name: "security schemes ext spec", - create: func() (string, any) { - o := &openapi.SecurityScheme{ - Description: "test", - } - return "testSecurityScheme", openapi.NewExtendable[openapi.SecurityScheme](o) - }, - check: func(tb testing.TB, c *openapi.Components) { - require.Len(tb, c.SecuritySchemes, 1) - require.NotNil(tb, c.SecuritySchemes["testSecurityScheme"]) - require.NotNil(tb, c.SecuritySchemes["testSecurityScheme"].Spec) - require.NotNil(tb, c.SecuritySchemes["testSecurityScheme"].Spec.Spec) - require.Equal(tb, "test", c.SecuritySchemes["testSecurityScheme"].Spec.Spec.Description) - }, - }, - { - name: "security schemes ref or spec", create: func() (string, any) { o := &openapi.SecurityScheme{ Description: "test", @@ -335,9 +116,7 @@ func TestComponents_WithRefOrSpec(t *testing.T) { { name: "link spec", create: func() (string, any) { - o := &openapi.Link{ - Description: "test", - } + o := openapi.NewLinkBuilder().Description("test").Build() return "testLink", o }, check: func(tb testing.TB, c *openapi.Components) { @@ -348,48 +127,13 @@ func TestComponents_WithRefOrSpec(t *testing.T) { require.Equal(tb, "test", c.Links["testLink"].Spec.Spec.Description) }, }, - { - name: "link ext spec", - create: func() (string, any) { - o := &openapi.Link{ - Description: "test", - } - return "testLink", openapi.NewExtendable[openapi.Link](o) - }, - check: func(tb testing.TB, c *openapi.Components) { - require.Len(tb, c.Links, 1) - require.NotNil(tb, c.Links["testLink"]) - require.NotNil(tb, c.Links["testLink"].Spec) - require.NotNil(tb, c.Links["testLink"].Spec.Spec) - require.Equal(tb, "test", c.Links["testLink"].Spec.Spec.Description) - }, - }, - { - name: "link ref or spec", - create: func() (string, any) { - o := &openapi.Link{ - Description: "test", - } - return "testLink", openapi.NewRefOrExtSpec[openapi.Link](o) - }, - check: func(tb testing.TB, c *openapi.Components) { - require.Len(tb, c.Links, 1) - require.NotNil(tb, c.Links["testLink"]) - require.NotNil(tb, c.Links["testLink"].Spec) - require.NotNil(tb, c.Links["testLink"].Spec.Spec) - require.Equal(tb, "test", c.Links["testLink"].Spec.Spec.Description) - }, - }, { name: "callback spec", create: func() (string, any) { - o := &openapi.Callback{ - Callback: map[string]*openapi.RefOrSpec[openapi.Extendable[openapi.PathItem]]{ - "testPath": openapi.NewRefOrExtSpec[openapi.PathItem](&openapi.PathItem{ - Description: "test", - }), - }, - } + o := openapi.NewCallbackBuilder().AddPathItem( + "testPath", + openapi.NewPathItemBuilder().Description("test").Build(), + ).Build() return "testCallback", o }, check: func(tb testing.TB, c *openapi.Components) { @@ -397,57 +141,7 @@ func TestComponents_WithRefOrSpec(t *testing.T) { require.NotNil(tb, c.Callbacks["testCallback"]) require.NotNil(tb, c.Callbacks["testCallback"].Spec) require.NotNil(tb, c.Callbacks["testCallback"].Spec.Spec) - paths := c.Callbacks["testCallback"].Spec.Spec.Callback - require.Len(tb, paths, 1) - require.NotNil(tb, paths["testPath"]) - require.NotNil(tb, paths["testPath"].Spec) - require.NotNil(tb, paths["testPath"].Spec.Spec) - require.Equal(tb, "test", paths["testPath"].Spec.Spec.Description) - }, - }, - { - name: "callback ext spec", - create: func() (string, any) { - o := &openapi.Callback{ - Callback: map[string]*openapi.RefOrSpec[openapi.Extendable[openapi.PathItem]]{ - "testPath": openapi.NewRefOrExtSpec[openapi.PathItem](&openapi.PathItem{ - Description: "test", - }), - }, - } - return "testCallback", openapi.NewExtendable[openapi.Callback](o) - }, - check: func(tb testing.TB, c *openapi.Components) { - require.Len(tb, c.Callbacks, 1) - require.NotNil(tb, c.Callbacks["testCallback"]) - require.NotNil(tb, c.Callbacks["testCallback"].Spec) - require.NotNil(tb, c.Callbacks["testCallback"].Spec.Spec) - paths := c.Callbacks["testCallback"].Spec.Spec.Callback - require.Len(tb, paths, 1) - require.NotNil(tb, paths["testPath"]) - require.NotNil(tb, paths["testPath"].Spec) - require.NotNil(tb, paths["testPath"].Spec.Spec) - require.Equal(tb, "test", paths["testPath"].Spec.Spec.Description) - }, - }, - { - name: "callback ref or spec", - create: func() (string, any) { - o := &openapi.Callback{ - Callback: map[string]*openapi.RefOrSpec[openapi.Extendable[openapi.PathItem]]{ - "testPath": openapi.NewRefOrExtSpec[openapi.PathItem](&openapi.PathItem{ - Description: "test", - }), - }, - } - return "testCallback", openapi.NewRefOrExtSpec[openapi.Callback](o) - }, - check: func(tb testing.TB, c *openapi.Components) { - require.Len(tb, c.Callbacks, 1) - require.NotNil(tb, c.Callbacks["testCallback"]) - require.NotNil(tb, c.Callbacks["testCallback"].Spec) - require.NotNil(tb, c.Callbacks["testCallback"].Spec.Spec) - paths := c.Callbacks["testCallback"].Spec.Spec.Callback + paths := c.Callbacks["testCallback"].Spec.Spec.Paths require.Len(tb, paths, 1) require.NotNil(tb, paths["testPath"]) require.NotNil(tb, paths["testPath"].Spec) @@ -458,9 +152,7 @@ func TestComponents_WithRefOrSpec(t *testing.T) { { name: "path item spec", create: func() (string, any) { - o := &openapi.PathItem{ - Description: "test", - } + o := openapi.NewPathItemBuilder().Description("test").Build() return "testPathItem", o }, check: func(tb testing.TB, c *openapi.Components) { @@ -471,48 +163,10 @@ func TestComponents_WithRefOrSpec(t *testing.T) { require.Equal(tb, "test", c.Paths["testPathItem"].Spec.Spec.Description) }, }, - { - name: "path item ext spec", - create: func() (string, any) { - o := &openapi.PathItem{ - Description: "test", - } - return "testPathItem", openapi.NewExtendable[openapi.PathItem](o) - }, - check: func(tb testing.TB, c *openapi.Components) { - require.Len(tb, c.Paths, 1) - require.NotNil(tb, c.Paths["testPathItem"]) - require.NotNil(tb, c.Paths["testPathItem"].Spec) - require.NotNil(tb, c.Paths["testPathItem"].Spec.Spec) - require.Equal(tb, "test", c.Paths["testPathItem"].Spec.Spec.Description) - }, - }, - { - name: "path item ref or spec", - create: func() (string, any) { - o := &openapi.PathItem{ - Description: "test", - } - return "testPathItem", openapi.NewRefOrExtSpec[openapi.PathItem](o) - }, - check: func(tb testing.TB, c *openapi.Components) { - require.Len(tb, c.Paths, 1) - require.NotNil(tb, c.Paths["testPathItem"]) - require.NotNil(tb, c.Paths["testPathItem"].Spec) - require.NotNil(tb, c.Paths["testPathItem"].Spec.Spec) - require.Equal(tb, "test", c.Paths["testPathItem"].Spec.Spec.Description) - }, - }, } { t.Run(tt.name, func(t *testing.T) { name, obj := tt.create() - tt.check(t, (&openapi.Components{}).WithRefOrSpec(name, obj)) + tt.check(t, (&openapi.Components{}).Add(name, obj)) }) } - - t.Run("panic", func(t *testing.T) { - require.Panics(t, func() { - (&openapi.Components{}).WithRefOrSpec("panic", 42) - }) - }) } diff --git a/contact.go b/contact.go index 41998e5..8e63406 100644 --- a/contact.go +++ b/contact.go @@ -30,3 +30,42 @@ func (o *Contact) validateSpec(location string, opts *specValidationOptions) []* } return errs } + +type ContactBuilder struct { + spec *Extendable[Contact] +} + +func NewContactBuilder() *ContactBuilder { + return &ContactBuilder{ + spec: NewExtendable(&Contact{}), + } +} + +func (b *ContactBuilder) Build() *Extendable[Contact] { + return b.spec +} + +func (b *ContactBuilder) Extensions(v map[string]any) *ContactBuilder { + b.spec.Extensions = v + return b +} + +func (b *ContactBuilder) AddExt(name string, value any) *ContactBuilder { + b.spec.Add(name, value) + return b +} + +func (b *ContactBuilder) Name(v string) *ContactBuilder { + b.spec.Spec.Name = v + return b +} + +func (b *ContactBuilder) URL(v string) *ContactBuilder { + b.spec.Spec.URL = v + return b +} + +func (b *ContactBuilder) Email(v string) *ContactBuilder { + b.spec.Spec.Email = v + return b +} diff --git a/discriminator.go b/discriminator.go index c3bb167..ef72904 100644 --- a/discriminator.go +++ b/discriminator.go @@ -34,5 +34,41 @@ func (o *Discriminator) validateSpec(location string, opts *specValidationOption if o.PropertyName == "" { errs = append(errs, newValidationError(joinLoc(location, "propertyName"), ErrRequired)) } + for k, v := range o.Mapping { + ref := NewRefOrSpec[Schema](v) + errs = append(errs, ref.validateSpec(joinLoc(location, "mapping", k), opts)...) + } return errs } + +type DiscriminatorBuilder struct { + spec *Discriminator +} + +func NewDiscriminatorBuilder() *DiscriminatorBuilder { + return &DiscriminatorBuilder{ + spec: &Discriminator{}, + } +} + +func (b *DiscriminatorBuilder) Build() *Discriminator { + return b.spec +} + +func (b *DiscriminatorBuilder) Mapping(v map[string]string) *DiscriminatorBuilder { + b.spec.Mapping = v + return b +} + +func (b *DiscriminatorBuilder) AddMapping(name, value string) *DiscriminatorBuilder { + if b.spec.Mapping == nil { + b.spec.Mapping = make(map[string]string, 1) + } + b.spec.Mapping[name] = value + return b +} + +func (b *DiscriminatorBuilder) PropertyName(v string) *DiscriminatorBuilder { + b.spec.PropertyName = v + return b +} diff --git a/encoding.go b/encoding.go index f7a90d4..ee27bb6 100644 --- a/encoding.go +++ b/encoding.go @@ -85,7 +85,64 @@ func (o *Encoding) validateSpec(location string, opts *specValidationOptions) [] switch o.Style { case "", StyleForm, StyleSpaceDelimited, StylePipeDelimited, StyleDeepObject: default: - errs = append(errs, newValidationError(joinLoc(location, "style"), "invalid value, expected be one of [%s, %s, %s, %s], but got '%s'", StyleForm, StyleSpaceDelimited, StylePipeDelimited, StyleDeepObject, o.Style)) + errs = append(errs, newValidationError(joinLoc(location, "style"), "invalid value, expected one of [%s, %s, %s, %s], but got '%s'", StyleForm, StyleSpaceDelimited, StylePipeDelimited, StyleDeepObject, o.Style)) } return errs } + +type EncodingBuilder struct { + spec *Extendable[Encoding] +} + +func NewEncodingBuilder() *EncodingBuilder { + return &EncodingBuilder{ + spec: NewExtendable[Encoding](&Encoding{}), + } +} + +func (b *EncodingBuilder) Build() *Extendable[Encoding] { + return b.spec +} + +func (b *EncodingBuilder) Extensions(v map[string]any) *EncodingBuilder { + b.spec.Extensions = v + return b +} + +func (b *EncodingBuilder) AddExt(name string, value any) *EncodingBuilder { + b.spec.Add(name, value) + return b +} + +func (b *EncodingBuilder) ContentType(v string) *EncodingBuilder { + b.spec.Spec.ContentType = v + return b +} + +func (b *EncodingBuilder) Headers(v map[string]*RefOrSpec[Extendable[Header]]) *EncodingBuilder { + b.spec.Spec.Headers = v + return b +} + +func (b *EncodingBuilder) Header(name string, value *RefOrSpec[Extendable[Header]]) *EncodingBuilder { + if b.spec.Spec.Headers == nil { + b.spec.Spec.Headers = make(map[string]*RefOrSpec[Extendable[Header]], 1) + } + b.spec.Spec.Headers[name] = value + return b +} + +func (b *EncodingBuilder) Style(v string) *EncodingBuilder { + b.spec.Spec.Style = v + return b +} + +func (b *EncodingBuilder) Explode(v bool) *EncodingBuilder { + b.spec.Spec.Explode = v + return b +} + +func (b *EncodingBuilder) AllowReserved(v bool) *EncodingBuilder { + b.spec.Spec.AllowReserved = v + return b +} diff --git a/example.go b/example.go index 1fa60ac..4c5ef85 100644 --- a/example.go +++ b/example.go @@ -59,3 +59,47 @@ func (o *Example) validateSpec(location string, opts *specValidationOptions) []* // should be validated in the object that defines the example and a schema return errs } + +type ExampleBuilder struct { + spec *RefOrSpec[Extendable[Example]] +} + +func NewExampleBuilder() *ExampleBuilder { + return &ExampleBuilder{ + spec: NewRefOrExtSpec[Example](&Example{}), + } +} + +func (b *ExampleBuilder) Build() *RefOrSpec[Extendable[Example]] { + return b.spec +} + +func (b *ExampleBuilder) Extensions(v map[string]any) *ExampleBuilder { + b.spec.Spec.Extensions = v + return b +} + +func (b *ExampleBuilder) AddExt(name string, value any) *ExampleBuilder { + b.spec.Spec.Add(name, value) + return b +} + +func (b *ExampleBuilder) Summary(v string) *ExampleBuilder { + b.spec.Spec.Spec.Summary = v + return b +} + +func (b *ExampleBuilder) Description(v string) *ExampleBuilder { + b.spec.Spec.Spec.Description = v + return b +} + +func (b *ExampleBuilder) Value(v any) *ExampleBuilder { + b.spec.Spec.Spec.Value = v + return b +} + +func (b *ExampleBuilder) ExternalValue(v string) *ExampleBuilder { + b.spec.Spec.Spec.ExternalValue = v + return b +} diff --git a/extensions.go b/extensions.go index 4b5873a..936893e 100644 --- a/extensions.go +++ b/extensions.go @@ -42,9 +42,9 @@ func NewExtendable[T any](spec *T) *Extendable[T] { return &ext } -// WithExt sets the extension and returns the current object. -// The `x-` will be added automatically to given name. -func (o *Extendable[T]) WithExt(name string, value any) *Extendable[T] { +// Add sets the extension and returns the current object. +// The `x-` prefix will be added automatically to given name. +func (o *Extendable[T]) Add(name string, value any) *Extendable[T] { if o.Extensions == nil { o.Extensions = make(map[string]any, 1) } @@ -55,6 +55,18 @@ func (o *Extendable[T]) WithExt(name string, value any) *Extendable[T] { return o } +// Get returns the extension value by name. +// The `x-` prefix will be added automatically to given name. +func (o *Extendable[T]) Get(name string) any { + if o.Extensions == nil { + return nil + } + if !strings.HasPrefix(name, ExtensionPrefix) { + name = ExtensionPrefix + name + } + return o.Extensions[name] +} + // MarshalJSON implements json.Marshaler interface. func (o *Extendable[T]) MarshalJSON() ([]byte, error) { var raw map[string]json.RawMessage diff --git a/extensions_test.go b/extensions_test.go index 53a44ec..6a9e6f4 100644 --- a/extensions_test.go +++ b/extensions_test.go @@ -99,7 +99,7 @@ func TestExtendable_WithExt(t *testing.T) { } { t.Run(tt.name, func(t *testing.T) { ext := openapi.NewExtendable(&testExtendable{}) - ext.WithExt(tt.key, tt.value) + ext.Add(tt.key, tt.value) require.Equal(t, tt.expected, ext.Extensions) }) } diff --git a/external-docs.go b/external-docs.go index 1227921..bea757f 100644 --- a/external-docs.go +++ b/external-docs.go @@ -28,3 +28,37 @@ func (o *ExternalDocs) validateSpec(location string, opts *specValidationOptions } return errs } + +type ExternalDocsBuilder struct { + spec *Extendable[ExternalDocs] +} + +func NewExternalDocsBuilder() *ExternalDocsBuilder { + return &ExternalDocsBuilder{ + spec: NewExtendable[ExternalDocs](&ExternalDocs{}), + } +} + +func (b *ExternalDocsBuilder) Build() *Extendable[ExternalDocs] { + return b.spec +} + +func (b *ExternalDocsBuilder) Extensions(v map[string]any) *ExternalDocsBuilder { + b.spec.Extensions = v + return b +} + +func (b *ExternalDocsBuilder) AddExt(name string, value any) *ExternalDocsBuilder { + b.spec.Add(name, value) + return b +} + +func (b *ExternalDocsBuilder) Description(v string) *ExternalDocsBuilder { + b.spec.Spec.Description = v + return b +} + +func (b *ExternalDocsBuilder) URL(v string) *ExternalDocsBuilder { + b.spec.Spec.URL = v + return b +} diff --git a/header.go b/header.go index f9c0226..3ea6a19 100644 --- a/header.go +++ b/header.go @@ -64,3 +64,70 @@ func (o *Header) validateSpec(location string, opts *specValidationOptions) []*v return errs } + +type HeaderBuilder struct { + spec *RefOrSpec[Extendable[Header]] +} + +func NewHeaderBuilder() *HeaderBuilder { + return &HeaderBuilder{ + spec: NewRefOrExtSpec[Header](&Header{}), + } +} + +func (b *HeaderBuilder) Build() *RefOrSpec[Extendable[Header]] { + return b.spec +} + +func (b *HeaderBuilder) Extensions(v map[string]any) *HeaderBuilder { + b.spec.Spec.Extensions = v + return b +} + +func (b *HeaderBuilder) AddExt(name string, value any) *HeaderBuilder { + b.spec.Spec.Add(name, value) + return b +} + +func (b *HeaderBuilder) Schema(v *RefOrSpec[Schema]) *HeaderBuilder { + b.spec.Spec.Spec.Schema = v + return b +} + +func (b *HeaderBuilder) Content(v map[string]*Extendable[MediaType]) *HeaderBuilder { + b.spec.Spec.Spec.Content = v + return b +} + +func (b *HeaderBuilder) AddContent(name string, value *Extendable[MediaType]) *HeaderBuilder { + if b.spec.Spec.Spec.Content == nil { + b.spec.Spec.Spec.Content = make(map[string]*Extendable[MediaType], 1) + } + b.spec.Spec.Spec.Content[name] = value + return b +} + +func (b *HeaderBuilder) Description(v string) *HeaderBuilder { + b.spec.Spec.Spec.Description = v + return b +} + +func (b *HeaderBuilder) Style(v string) *HeaderBuilder { + b.spec.Spec.Spec.Style = v + return b +} + +func (b *HeaderBuilder) Explode(v bool) *HeaderBuilder { + b.spec.Spec.Spec.Explode = v + return b +} + +func (b *HeaderBuilder) Required(v bool) *HeaderBuilder { + b.spec.Spec.Spec.Required = v + return b +} + +func (b *HeaderBuilder) Deprecated(v bool) *HeaderBuilder { + b.spec.Spec.Spec.Deprecated = v + return b +} diff --git a/info.go b/info.go index cc138f1..a65cad2 100644 --- a/info.go +++ b/info.go @@ -59,3 +59,62 @@ func (o *Info) validateSpec(location string, opts *specValidationOptions) []*val } return errs } + +type InfoBuilder struct { + spec *Extendable[Info] +} + +func NewInfoBuilder() *InfoBuilder { + return &InfoBuilder{ + spec: NewExtendable[Info](&Info{}), + } +} + +func (b *InfoBuilder) Build() *Extendable[Info] { + return b.spec +} + +func (b *InfoBuilder) Extensions(v map[string]any) *InfoBuilder { + b.spec.Extensions = v + return b +} + +func (o *InfoBuilder) AddExt(name string, value any) *InfoBuilder { + o.spec.Add(name, value) + return o +} + +func (b *InfoBuilder) Title(v string) *InfoBuilder { + b.spec.Spec.Title = v + return b +} + +func (b *InfoBuilder) Summary(v string) *InfoBuilder { + b.spec.Spec.Summary = v + return b +} + +func (b *InfoBuilder) Description(v string) *InfoBuilder { + b.spec.Spec.Description = v + return b +} + +func (b *InfoBuilder) TermsOfService(v string) *InfoBuilder { + b.spec.Spec.TermsOfService = v + return b +} + +func (b *InfoBuilder) Contact(v *Extendable[Contact]) *InfoBuilder { + b.spec.Spec.Contact = v + return b +} + +func (b *InfoBuilder) License(v *Extendable[License]) *InfoBuilder { + b.spec.Spec.License = v + return b +} + +func (b *InfoBuilder) Version(v string) *InfoBuilder { + b.spec.Spec.Version = v + return b +} diff --git a/license.go b/license.go index 800fb72..8810bcd 100644 --- a/license.go +++ b/license.go @@ -34,3 +34,42 @@ func (o *License) validateSpec(location string, opts *specValidationOptions) []* } return errs } + +type LicenseBuilder struct { + spec *Extendable[License] +} + +func NewLicenseBuilder() *LicenseBuilder { + return &LicenseBuilder{ + spec: NewExtendable[License](&License{}), + } +} + +func (b *LicenseBuilder) Build() *Extendable[License] { + return b.spec +} + +func (b *LicenseBuilder) Extensions(v map[string]any) *LicenseBuilder { + b.spec.Extensions = v + return b +} + +func (b *LicenseBuilder) AddExt(name string, value any) *LicenseBuilder { + b.spec.Add(name, value) + return b +} + +func (b *LicenseBuilder) Name(v string) *LicenseBuilder { + b.spec.Spec.Name = v + return b +} + +func (b *LicenseBuilder) Identifier(v string) *LicenseBuilder { + b.spec.Spec.Identifier = v + return b +} + +func (b *LicenseBuilder) URL(v string) *LicenseBuilder { + b.spec.Spec.URL = v + return b +} diff --git a/link.go b/link.go index 5de44f2..cbbc4a6 100644 --- a/link.go +++ b/link.go @@ -6,7 +6,8 @@ package openapi // Unlike dynamic links (i.e. links provided in the response payload), // the OAS linking mechanism does not require link information in the runtime response. // For computing links, and providing instructions to execute them, -// a runtime expression is used for accessing values in an operation and using them as parameters while invoking the linked operation. +// a runtime expression is used for accessing values in an operation +// and using them as parameters while invoking the linked operation. // // https://spec.openapis.org/oas/v3.1.1#link-object // @@ -90,8 +91,75 @@ func (o *Link) validateSpec(location string, opts *specValidationOptions) []*val opts.linkToOperationID[joinLoc(location, "operationId")] = o.OperationID } } + // uncomment when JSONLookup is implemented + //if o.OperationRef != "" { + // ref := NewRefOrExtSpec[Operation](o.OperationRef) + // errs = append(errs, ref.validateSpec(joinLoc(location, "operationRef"), opts)...) + //} if o.Server != nil { errs = append(errs, o.Server.validateSpec(joinLoc(location, "server"), opts)...) } return errs } + +type LinkBuilder struct { + spec *RefOrSpec[Extendable[Link]] +} + +func NewLinkBuilder() *LinkBuilder { + return &LinkBuilder{ + spec: NewRefOrExtSpec[Link](&Link{}), + } +} + +func (b *LinkBuilder) Build() *RefOrSpec[Extendable[Link]] { + return b.spec +} + +func (b *LinkBuilder) Extensions(v map[string]any) *LinkBuilder { + b.spec.Spec.Extensions = v + return b +} + +func (b *LinkBuilder) AddExt(name string, value any) *LinkBuilder { + b.spec.Spec.Add(name, value) + return b +} + +func (b *LinkBuilder) RequestBody(v any) *LinkBuilder { + b.spec.Spec.Spec.RequestBody = v + return b +} + +func (b *LinkBuilder) Parameters(v map[string]any) *LinkBuilder { + b.spec.Spec.Spec.Parameters = v + return b +} + +func (b *LinkBuilder) AddParameter(name string, value any) *LinkBuilder { + if b.spec.Spec.Spec.Parameters == nil { + b.spec.Spec.Spec.Parameters = make(map[string]any, 1) + } + b.spec.Spec.Spec.Parameters[name] = value + return b +} + +func (b *LinkBuilder) Server(v *Extendable[Server]) *LinkBuilder { + b.spec.Spec.Spec.Server = v + return b +} + +func (b *LinkBuilder) OperationRef(v string) *LinkBuilder { + b.spec.Spec.Spec.OperationRef = v + return b +} + +func (b *LinkBuilder) OperationID(v string) *LinkBuilder { + b.spec.Spec.Spec.OperationID = v + return b +} + +func (b *LinkBuilder) Description(v string) *LinkBuilder { + b.spec.Spec.Spec.Description = v + return b +} diff --git a/media_type.go b/media_type.go index acdddfa..3376048 100644 --- a/media_type.go +++ b/media_type.go @@ -93,3 +93,63 @@ func (o *MediaType) validateSpec(location string, opts *specValidationOptions) [ return errs } + +type MediaTypeBuilder struct { + spec *Extendable[MediaType] +} + +func NewMediaTypeBuilder() *MediaTypeBuilder { + return &MediaTypeBuilder{ + spec: NewExtendable[MediaType](&MediaType{}), + } +} + +func (b *MediaTypeBuilder) Build() *Extendable[MediaType] { + return b.spec +} + +func (b *MediaTypeBuilder) Extensions(v map[string]any) *MediaTypeBuilder { + b.spec.Extensions = v + return b +} + +func (b *MediaTypeBuilder) AddExt(name string, value any) *MediaTypeBuilder { + b.spec.Add(name, value) + return b +} + +func (b *MediaTypeBuilder) Schema(v *RefOrSpec[Schema]) *MediaTypeBuilder { + b.spec.Spec.Schema = v + return b +} + +func (b *MediaTypeBuilder) Example(v any) *MediaTypeBuilder { + b.spec.Spec.Example = v + return b +} + +func (b *MediaTypeBuilder) Examples(v map[string]*RefOrSpec[Extendable[Example]]) *MediaTypeBuilder { + b.spec.Spec.Examples = v + return b +} + +func (b *MediaTypeBuilder) AddExample(name string, value *RefOrSpec[Extendable[Example]]) *MediaTypeBuilder { + if b.spec.Spec.Examples == nil { + b.spec.Spec.Examples = make(map[string]*RefOrSpec[Extendable[Example]], 1) + } + b.spec.Spec.Examples[name] = value + return b +} + +func (b *MediaTypeBuilder) Encoding(v map[string]*Extendable[Encoding]) *MediaTypeBuilder { + b.spec.Spec.Encoding = v + return b +} + +func (b *MediaTypeBuilder) AddEncoding(name string, value *Extendable[Encoding]) *MediaTypeBuilder { + if b.spec.Spec.Encoding == nil { + b.spec.Spec.Encoding = make(map[string]*Extendable[Encoding], 1) + } + b.spec.Spec.Encoding[name] = value + return b +} diff --git a/oauth-flow.go b/oauth-flow.go index d290c7d..51830d0 100644 --- a/oauth-flow.go +++ b/oauth-flow.go @@ -50,3 +50,55 @@ func (o *OAuthFlow) validateSpec(path string, opts *specValidationOptions) []*va // all the validations are done in the parent object return nil } + +type OAuthFlowBuilder struct { + spec *Extendable[OAuthFlow] +} + +func NewOAuthFlowBuilder() *OAuthFlowBuilder { + return &OAuthFlowBuilder{ + spec: NewExtendable[OAuthFlow](&OAuthFlow{}), + } +} + +func (b *OAuthFlowBuilder) Build() *Extendable[OAuthFlow] { + return b.spec +} + +func (b *OAuthFlowBuilder) Extensions(v map[string]any) *OAuthFlowBuilder { + b.spec.Extensions = v + return b +} + +func (b *OAuthFlowBuilder) AddExt(name string, value any) *OAuthFlowBuilder { + b.spec.Add(name, value) + return b +} + +func (b *OAuthFlowBuilder) Scopes(v map[string]string) *OAuthFlowBuilder { + b.spec.Spec.Scopes = v + return b +} + +func (b *OAuthFlowBuilder) AddScope(name, value string) *OAuthFlowBuilder { + if b.spec.Spec.Scopes == nil { + b.spec.Spec.Scopes = make(map[string]string, 1) + } + b.spec.Spec.Scopes[name] = value + return b +} + +func (b *OAuthFlowBuilder) AuthorizationURL(v string) *OAuthFlowBuilder { + b.spec.Spec.AuthorizationURL = v + return b +} + +func (b *OAuthFlowBuilder) TokenURL(v string) *OAuthFlowBuilder { + b.spec.Spec.TokenURL = v + return b +} + +func (b *OAuthFlowBuilder) RefreshURL(v string) *OAuthFlowBuilder { + b.spec.Spec.RefreshURL = v + return b +} diff --git a/oauth-flows.go b/oauth-flows.go index 62e654b..4d23225 100644 --- a/oauth-flows.go +++ b/oauth-flows.go @@ -64,3 +64,47 @@ func (o *OAuthFlows) validateSpec(location string, opts *specValidationOptions) return errs } + +type OAuthFlowsBuilder struct { + spec *Extendable[OAuthFlows] +} + +func NewOAuthFlowsBuilder() *OAuthFlowsBuilder { + return &OAuthFlowsBuilder{ + spec: NewExtendable[OAuthFlows](&OAuthFlows{}), + } +} + +func (b *OAuthFlowsBuilder) Build() *Extendable[OAuthFlows] { + return b.spec +} + +func (b *OAuthFlowsBuilder) Extensions(v map[string]any) *OAuthFlowsBuilder { + b.spec.Extensions = v + return b +} + +func (b *OAuthFlowsBuilder) AddExt(name string, value any) *OAuthFlowsBuilder { + b.spec.Add(name, value) + return b +} + +func (b *OAuthFlowsBuilder) Implicit(v *Extendable[OAuthFlow]) *OAuthFlowsBuilder { + b.spec.Spec.Implicit = v + return b +} + +func (b *OAuthFlowsBuilder) Password(v *Extendable[OAuthFlow]) *OAuthFlowsBuilder { + b.spec.Spec.Password = v + return b +} + +func (b *OAuthFlowsBuilder) ClientCredentials(v *Extendable[OAuthFlow]) *OAuthFlowsBuilder { + b.spec.Spec.ClientCredentials = v + return b +} + +func (b *OAuthFlowsBuilder) AuthorizationCode(v *Extendable[OAuthFlow]) *OAuthFlowsBuilder { + b.spec.Spec.AuthorizationCode = v + return b +} diff --git a/openapi.go b/openapi.go index a3a28da..06bbe48 100644 --- a/openapi.go +++ b/openapi.go @@ -33,7 +33,7 @@ type OpenAPI struct { // for example by an out of band registration. // The key name is a unique string to refer to each webhook, while the (optionally referenced) PathItem Object describes // a request that may be initiated by the API provider and the expected responses. - WebHooks map[string]*RefOrSpec[Extendable[PathItem]] `json:"webhooks,omitempty" yaml:"webhooks,omitempty"` + WebHooks Webhooks `json:"webhooks,omitempty" yaml:"webhooks,omitempty"` // The default value for the $schema keyword within Schema Objects contained within this OAS document. // This MUST be in the form of a URI. JsonSchemaDialect string `json:"jsonSchemaDialect,omitempty" yaml:"jsonSchemaDialect,omitempty"` @@ -149,3 +149,117 @@ func (o *OpenAPI) validateSpec(location string, opts *specValidationOptions) []* } return errs } + +type OpenAPIBuilder struct { + spec *Extendable[OpenAPI] +} + +func NewOpenAPIBuilder() *OpenAPIBuilder { + return &OpenAPIBuilder{spec: NewExtendable(&OpenAPI{ + OpenAPI: "3.1.1", + JsonSchemaDialect: "https://spec.openapis.org/oas/3.1/dialect/base", + })} +} + +func (b *OpenAPIBuilder) Build() *Extendable[OpenAPI] { + return b.spec +} + +func (b *OpenAPIBuilder) Extensions(v map[string]any) *OpenAPIBuilder { + b.spec.Extensions = v + return b +} + +func (b *OpenAPIBuilder) AddExt(name string, value any) *OpenAPIBuilder { + b.spec.Add(name, value) + return b +} + +func (b *OpenAPIBuilder) OpenAPI(openAPI string) *OpenAPIBuilder { + b.spec.Spec.OpenAPI = openAPI + return b +} + +func (b *OpenAPIBuilder) Info(info *Extendable[Info]) *OpenAPIBuilder { + b.spec.Spec.Info = info + return b +} + +func (b *OpenAPIBuilder) Components(components *Extendable[Components]) *OpenAPIBuilder { + b.spec.Spec.Components = components + return b +} + +func (b *OpenAPIBuilder) AddComponent(name string, component any) *OpenAPIBuilder { + if b.spec.Spec.Components == nil { + b.spec.Spec.Components = NewComponents() + } + b.spec.Spec.Components.Spec.Add(name, component) + return b +} + +func (b *OpenAPIBuilder) ExternalDocs(externalDocs *Extendable[ExternalDocs]) *OpenAPIBuilder { + b.spec.Spec.ExternalDocs = externalDocs + return b +} + +func (b *OpenAPIBuilder) Paths(paths *Extendable[Paths]) *OpenAPIBuilder { + b.spec.Spec.Paths = paths + return b +} + +func (b *OpenAPIBuilder) AddPath(path string, item *RefOrSpec[Extendable[PathItem]]) *OpenAPIBuilder { + if b.spec.Spec.Paths == nil { + b.spec.Spec.Paths = NewPaths() + } + b.spec.Spec.Paths.Spec.Add(path, item) + return b +} + +func (b *OpenAPIBuilder) WebHooks(webHooks Webhooks) *OpenAPIBuilder { + b.spec.Spec.WebHooks = webHooks + return b +} + +func (b *OpenAPIBuilder) AddWebHook(name string, path *RefOrSpec[Extendable[PathItem]]) *OpenAPIBuilder { + if b.spec.Spec.WebHooks == nil { + b.spec.Spec.WebHooks = NewWebhooks() + } + b.spec.Spec.WebHooks[name] = path + return b +} + +func (b *OpenAPIBuilder) JsonSchemaDialect(jsonSchemaDialect string) *OpenAPIBuilder { + b.spec.Spec.JsonSchemaDialect = jsonSchemaDialect + return b +} + +func (b *OpenAPIBuilder) Security(security ...SecurityRequirement) *OpenAPIBuilder { + b.spec.Spec.Security = security + return b +} + +func (b *OpenAPIBuilder) AddSecurity(v ...SecurityRequirement) *OpenAPIBuilder { + b.spec.Spec.Security = append(b.spec.Spec.Security, v...) + return b +} + +func (b *OpenAPIBuilder) Tags(tags ...*Extendable[Tag]) *OpenAPIBuilder { + b.spec.Spec.Tags = tags + return b +} + +func (b *OpenAPIBuilder) AddTags(tags ...*Extendable[Tag]) *OpenAPIBuilder { + b.spec.Spec.Tags = append(b.spec.Spec.Tags, tags...) + return b +} + +func (b *OpenAPIBuilder) Servers(servers ...*Extendable[Server]) *OpenAPIBuilder { + b.spec.Spec.Servers = servers + return b +} + +func (b *OpenAPIBuilder) AddServers(servers ...*Extendable[Server]) *OpenAPIBuilder { + b.spec.Spec.Servers = append(b.spec.Spec.Servers, servers...) + return b +} diff --git a/operation.go b/operation.go index c71332b..26081fe 100644 --- a/operation.go +++ b/operation.go @@ -162,3 +162,110 @@ func (o *Operation) validateSpec(location string, opts *specValidationOptions) [ return errs } + +type OperationBuilder struct { + spec *Extendable[Operation] +} + +func NewOperationBuilder() *OperationBuilder { + return &OperationBuilder{ + spec: NewExtendable[Operation](&Operation{}), + } +} + +func (b *OperationBuilder) Build() *Extendable[Operation] { + return b.spec +} + +func (b *OperationBuilder) Extensions(v map[string]any) *OperationBuilder { + b.spec.Extensions = v + return b +} + +func (b *OperationBuilder) AddExt(name string, value any) *OperationBuilder { + b.spec.Add(name, value) + return b +} + +func (b *OperationBuilder) RequestBody(v *RefOrSpec[Extendable[RequestBody]]) *OperationBuilder { + b.spec.Spec.RequestBody = v + return b +} + +func (b *OperationBuilder) Callbacks(v map[string]*RefOrSpec[Extendable[Callback]]) *OperationBuilder { + b.spec.Spec.Callbacks = v + return b +} + +func (b *OperationBuilder) AddCallback(name string, value *RefOrSpec[Extendable[Callback]]) *OperationBuilder { + if b.spec.Spec.Callbacks == nil { + b.spec.Spec.Callbacks = make(map[string]*RefOrSpec[Extendable[Callback]], 1) + } + b.spec.Spec.Callbacks[name] = value + return b +} + +func (b *OperationBuilder) ExternalDocs(v *Extendable[ExternalDocs]) *OperationBuilder { + b.spec.Spec.ExternalDocs = v + return b +} + +func (b *OperationBuilder) OperationID(v string) *OperationBuilder { + b.spec.Spec.OperationID = v + return b +} + +func (b *OperationBuilder) Summary(v string) *OperationBuilder { + b.spec.Spec.Summary = v + return b +} + +func (b *OperationBuilder) Description(v string) *OperationBuilder { + b.spec.Spec.Description = v + return b +} + +func (b *OperationBuilder) Parameters(v ...*RefOrSpec[Extendable[Parameter]]) *OperationBuilder { + b.spec.Spec.Parameters = v + return b +} + +func (b *OperationBuilder) AddParameters(v ...*RefOrSpec[Extendable[Parameter]]) *OperationBuilder { + b.spec.Spec.Parameters = append(b.spec.Spec.Parameters, v...) + return b +} + +func (b *OperationBuilder) Tags(v ...string) *OperationBuilder { + b.spec.Spec.Tags = v + return b +} + +func (b *OperationBuilder) AddTags(v ...string) *OperationBuilder { + b.spec.Spec.Tags = append(b.spec.Spec.Tags, v...) + return b +} + +func (b *OperationBuilder) Security(v ...SecurityRequirement) *OperationBuilder { + b.spec.Spec.Security = v + return b +} + +func (b *OperationBuilder) AddSecurity(v ...SecurityRequirement) *OperationBuilder { + b.spec.Spec.Security = append(b.spec.Spec.Security, v...) + return b +} + +func (b *OperationBuilder) Servers(v ...*Extendable[Server]) *OperationBuilder { + b.spec.Spec.Servers = v + return b +} + +func (b *OperationBuilder) AddServers(v ...*Extendable[Server]) *OperationBuilder { + b.spec.Spec.Servers = append(b.spec.Spec.Servers, v...) + return b +} + +func (b *OperationBuilder) Deprecated(v bool) *OperationBuilder { + b.spec.Spec.Deprecated = v + return b +} diff --git a/parameter.go b/parameter.go index 2329c8c..9866e24 100644 --- a/parameter.go +++ b/parameter.go @@ -295,3 +295,108 @@ func (o *Parameter) validateSpec(location string, opts *specValidationOptions) [ } return errs } + +type ParameterBuilder struct { + spec *RefOrSpec[Extendable[Parameter]] +} + +func NewParameterBuilder() *ParameterBuilder { + return &ParameterBuilder{ + spec: NewRefOrExtSpec[Parameter](&Parameter{}), + } +} + +func (b *ParameterBuilder) Build() *RefOrSpec[Extendable[Parameter]] { + return b.spec +} + +func (b *ParameterBuilder) Extensions(v map[string]any) *ParameterBuilder { + b.spec.Spec.Extensions = v + return b +} + +func (b *ParameterBuilder) AddExt(name string, value any) *ParameterBuilder { + b.spec.Spec.Add(name, value) + return b +} + +func (b *ParameterBuilder) Example(v any) *ParameterBuilder { + b.spec.Spec.Spec.Example = v + return b +} + +func (b *ParameterBuilder) Content(v map[string]*Extendable[MediaType]) *ParameterBuilder { + b.spec.Spec.Spec.Content = v + return b +} + +func (b *ParameterBuilder) AddContent(name string, value *Extendable[MediaType]) *ParameterBuilder { + if b.spec.Spec.Spec.Content == nil { + b.spec.Spec.Spec.Content = make(map[string]*Extendable[MediaType], 1) + } + b.spec.Spec.Spec.Content[name] = value + return b +} + +func (b *ParameterBuilder) Examples(v map[string]*RefOrSpec[Extendable[Example]]) *ParameterBuilder { + b.spec.Spec.Spec.Examples = v + return b +} + +func (b *ParameterBuilder) AddExample(name string, value *RefOrSpec[Extendable[Example]]) *ParameterBuilder { + if b.spec.Spec.Spec.Examples == nil { + b.spec.Spec.Spec.Examples = make(map[string]*RefOrSpec[Extendable[Example]], 1) + } + b.spec.Spec.Spec.Examples[name] = value + return b +} + +func (b *ParameterBuilder) Schema(v *RefOrSpec[Schema]) *ParameterBuilder { + b.spec.Spec.Spec.Schema = v + return b +} + +func (b *ParameterBuilder) In(v string) *ParameterBuilder { + b.spec.Spec.Spec.In = v + return b +} + +func (b *ParameterBuilder) Description(v string) *ParameterBuilder { + b.spec.Spec.Spec.Description = v + return b +} + +func (b *ParameterBuilder) Style(v string) *ParameterBuilder { + b.spec.Spec.Spec.Style = v + return b +} + +func (b *ParameterBuilder) Name(v string) *ParameterBuilder { + b.spec.Spec.Spec.Name = v + return b +} + +func (b *ParameterBuilder) Explode(v bool) *ParameterBuilder { + b.spec.Spec.Spec.Explode = v + return b +} + +func (b *ParameterBuilder) AllowReserved(v bool) *ParameterBuilder { + b.spec.Spec.Spec.AllowReserved = v + return b +} + +func (b *ParameterBuilder) AllowEmptyValue(v bool) *ParameterBuilder { + b.spec.Spec.Spec.AllowEmptyValue = v + return b +} + +func (b *ParameterBuilder) Deprecated(v bool) *ParameterBuilder { + b.spec.Spec.Spec.Deprecated = v + return b +} + +func (b *ParameterBuilder) Required(v bool) *ParameterBuilder { + b.spec.Spec.Spec.Required = v + return b +} diff --git a/path_item.go b/path_item.go index 18293f6..ae6eec2 100644 --- a/path_item.go +++ b/path_item.go @@ -107,3 +107,97 @@ func (o *PathItem) validateSpec(location string, opts *specValidationOptions) [] } return errs } + +type PathItemBuilder struct { + spec *RefOrSpec[Extendable[PathItem]] +} + +func NewPathItemBuilder() *PathItemBuilder { + return &PathItemBuilder{ + spec: NewRefOrExtSpec[PathItem](&PathItem{}), + } +} + +func (b *PathItemBuilder) Build() *RefOrSpec[Extendable[PathItem]] { + return b.spec +} + +func (b *PathItemBuilder) Extensions(v map[string]any) *PathItemBuilder { + b.spec.Spec.Extensions = v + return b +} + +func (b *PathItemBuilder) AddExt(name string, value any) *PathItemBuilder { + b.spec.Spec.Add(name, value) + return b +} + +func (b *PathItemBuilder) Summary(v string) *PathItemBuilder { + b.spec.Spec.Spec.Summary = v + return b +} + +func (b *PathItemBuilder) Description(v string) *PathItemBuilder { + b.spec.Spec.Spec.Description = v + return b +} + +func (b *PathItemBuilder) Get(v *Extendable[Operation]) *PathItemBuilder { + b.spec.Spec.Spec.Get = v + return b +} + +func (b *PathItemBuilder) Put(v *Extendable[Operation]) *PathItemBuilder { + b.spec.Spec.Spec.Put = v + return b +} + +func (b *PathItemBuilder) Post(v *Extendable[Operation]) *PathItemBuilder { + b.spec.Spec.Spec.Post = v + return b +} + +func (b *PathItemBuilder) Delete(v *Extendable[Operation]) *PathItemBuilder { + b.spec.Spec.Spec.Delete = v + return b +} + +func (b *PathItemBuilder) Options(v *Extendable[Operation]) *PathItemBuilder { + b.spec.Spec.Spec.Options = v + return b +} + +func (b *PathItemBuilder) Head(v *Extendable[Operation]) *PathItemBuilder { + b.spec.Spec.Spec.Head = v + return b +} + +func (b *PathItemBuilder) Patch(v *Extendable[Operation]) *PathItemBuilder { + b.spec.Spec.Spec.Patch = v + return b +} + +func (b *PathItemBuilder) Trace(v *Extendable[Operation]) *PathItemBuilder { + b.spec.Spec.Spec.Trace = v + return b +} + +func (b *PathItemBuilder) Servers(v ...*Extendable[Server]) *PathItemBuilder { + b.spec.Spec.Spec.Servers = v + return b +} + +func (b *PathItemBuilder) AddServers(v ...*Extendable[Server]) *PathItemBuilder { + b.spec.Spec.Spec.Servers = append(b.spec.Spec.Spec.Servers, v...) + return b +} + +func (b *PathItemBuilder) Parameters(v ...*RefOrSpec[Extendable[Parameter]]) *PathItemBuilder { + b.spec.Spec.Spec.Parameters = v + return b +} + +func (b *PathItemBuilder) AddParameters(v ...*RefOrSpec[Extendable[Parameter]]) *PathItemBuilder { + b.spec.Spec.Spec.Parameters = append(b.spec.Spec.Spec.Parameters, v...) + return b +} diff --git a/paths.go b/paths.go index bf25bc3..376d75a 100644 --- a/paths.go +++ b/paths.go @@ -73,3 +73,15 @@ func (o *Paths) validateSpec(location string, opts *specValidationOptions) []*va } return errs } + +func (o *Paths) Add(path string, item *RefOrSpec[Extendable[PathItem]]) *Paths { + if o.Paths == nil { + o.Paths = make(map[string]*RefOrSpec[Extendable[PathItem]]) + } + o.Paths[path] = item + return o +} + +func NewPaths() *Extendable[Paths] { + return NewExtendable[Paths](&Paths{}) +} diff --git a/ref_test.go b/ref_test.go index eb059db..5fda116 100644 --- a/ref_test.go +++ b/ref_test.go @@ -275,7 +275,7 @@ func TestRefOrSpec_GetSpec(t *testing.T) { name: "correct ref and components", ref: openapi.NewRefOrSpec[openapi.Schema]("#/components/schemas/Pet"), c: openapi.NewExtendable((&openapi.Components{}). - WithRefOrSpec("Pet", openapi.NewRefOrSpec[openapi.Schema](&openapi.Schema{Title: "foo"})), + Add("Pet", openapi.NewRefOrSpec[openapi.Schema](&openapi.Schema{Title: "foo"})), ), exp: &openapi.Schema{Title: "foo"}, }, @@ -283,8 +283,8 @@ func TestRefOrSpec_GetSpec(t *testing.T) { name: "ref to ref", ref: openapi.NewRefOrSpec[openapi.Schema]("#/components/schemas/Pet"), c: openapi.NewExtendable((&openapi.Components{}). - WithRefOrSpec("Pet", openapi.NewRefOrSpec[openapi.Schema]("#/components/schemas/Pet2")). - WithRefOrSpec("Pet2", openapi.NewRefOrSpec[openapi.Schema](&openapi.Schema{Title: "foo"})), + Add("Pet", openapi.NewRefOrSpec[openapi.Schema]("#/components/schemas/Pet2")). + Add("Pet2", openapi.NewRefOrSpec[openapi.Schema](&openapi.Schema{Title: "foo"})), ), exp: &openapi.Schema{Title: "foo"}, }, @@ -292,7 +292,7 @@ func TestRefOrSpec_GetSpec(t *testing.T) { name: "ref to incorrect ref", ref: openapi.NewRefOrSpec[openapi.Schema]("#/components/schemas/Pet"), c: openapi.NewExtendable((&openapi.Components{}). - WithRefOrSpec("Pet", openapi.NewRefOrSpec[openapi.Schema]("fooo")), + Add("Pet", openapi.NewRefOrSpec[openapi.Schema]("fooo")), ), expErr: "is not implemented", }, @@ -300,8 +300,8 @@ func TestRefOrSpec_GetSpec(t *testing.T) { name: "cycle ref", ref: openapi.NewRefOrSpec[openapi.Schema]("#/components/schemas/Pet"), c: openapi.NewExtendable((&openapi.Components{}). - WithRefOrSpec("Pet", openapi.NewRefOrSpec[openapi.Schema]("#/components/schemas/Pet2")). - WithRefOrSpec("Pet2", openapi.NewRefOrSpec[openapi.Schema]("#/components/schemas/Pet")), + Add("Pet", openapi.NewRefOrSpec[openapi.Schema]("#/components/schemas/Pet2")). + Add("Pet2", openapi.NewRefOrSpec[openapi.Schema]("#/components/schemas/Pet")), ), expErr: "cycle ref", }, @@ -315,7 +315,7 @@ func TestRefOrSpec_GetSpec(t *testing.T) { name: "ref to unexpected component", ref: openapi.NewRefOrSpec[openapi.Operation]("#/components/schemas/Pet"), c: openapi.NewExtendable((&openapi.Components{}). - WithRefOrSpec("Pet", openapi.NewRefOrSpec[openapi.Schema](&openapi.Schema{Title: "foo"})), + Add("Pet", openapi.NewRefOrSpec[openapi.Schema](&openapi.Schema{Title: "foo"})), ), expErr: "expected spec of type", }, diff --git a/request_body.go b/request_body.go index 89cd1e9..3605b34 100644 --- a/request_body.go +++ b/request_body.go @@ -58,3 +58,50 @@ func (o *RequestBody) validateSpec(location string, opts *specValidationOptions) } return errs } + +type RequestBodyBuilder struct { + spec *RefOrSpec[Extendable[RequestBody]] +} + +func NewRequestBodyBuilder() *RequestBodyBuilder { + return &RequestBodyBuilder{ + spec: NewRefOrExtSpec[RequestBody](&RequestBody{}), + } +} + +func (b *RequestBodyBuilder) Build() *RefOrSpec[Extendable[RequestBody]] { + return b.spec +} + +func (b *RequestBodyBuilder) Extensions(v map[string]any) *RequestBodyBuilder { + b.spec.Spec.Extensions = v + return b +} + +func (b *RequestBodyBuilder) AddExt(name string, value any) *RequestBodyBuilder { + b.spec.Spec.Add(name, value) + return b +} + +func (b *RequestBodyBuilder) Content(v map[string]*Extendable[MediaType]) *RequestBodyBuilder { + b.spec.Spec.Spec.Content = v + return b +} + +func (b *RequestBodyBuilder) AddContent(key string, value *Extendable[MediaType]) *RequestBodyBuilder { + if b.spec.Spec.Spec.Content == nil { + b.spec.Spec.Spec.Content = make(map[string]*Extendable[MediaType], 1) + } + b.spec.Spec.Spec.Content[key] = value + return b +} + +func (b *RequestBodyBuilder) Description(v string) *RequestBodyBuilder { + b.spec.Spec.Spec.Description = v + return b +} + +func (b *RequestBodyBuilder) Required(v bool) *RequestBodyBuilder { + b.spec.Spec.Spec.Required = v + return b +} diff --git a/response.go b/response.go index dbd1d48..6672cfa 100644 --- a/response.go +++ b/response.go @@ -53,3 +53,71 @@ func (o *Response) validateSpec(location string, opts *specValidationOptions) [] } return errs } + +type ResponseBuilder struct { + spec *RefOrSpec[Extendable[Response]] +} + +func NewResponseBuilder() *ResponseBuilder { + return &ResponseBuilder{ + spec: NewRefOrExtSpec[Response](&Response{}), + } +} + +func (b *ResponseBuilder) Build() *RefOrSpec[Extendable[Response]] { + return b.spec +} + +func (b *ResponseBuilder) Extensions(v map[string]any) *ResponseBuilder { + b.spec.Spec.Extensions = v + return b +} + +func (b *ResponseBuilder) AddExt(name string, value any) *ResponseBuilder { + b.spec.Spec.Add(name, value) + return b +} + +func (b *ResponseBuilder) Headers(v map[string]*RefOrSpec[Extendable[Header]]) *ResponseBuilder { + b.spec.Spec.Spec.Headers = v + return b +} + +func (b *ResponseBuilder) AddHeader(key string, value *RefOrSpec[Extendable[Header]]) *ResponseBuilder { + if b.spec.Spec.Spec.Headers == nil { + b.spec.Spec.Spec.Headers = make(map[string]*RefOrSpec[Extendable[Header]], 1) + } + b.spec.Spec.Spec.Headers[key] = value + return b +} + +func (b *ResponseBuilder) Content(v map[string]*Extendable[MediaType]) *ResponseBuilder { + b.spec.Spec.Spec.Content = v + return b +} + +func (b *ResponseBuilder) AddContent(key string, value *Extendable[MediaType]) *ResponseBuilder { + if b.spec.Spec.Spec.Content == nil { + b.spec.Spec.Spec.Content = make(map[string]*Extendable[MediaType], 1) + } + b.spec.Spec.Spec.Content[key] = value + return b +} + +func (b *ResponseBuilder) Links(v map[string]*RefOrSpec[Extendable[Link]]) *ResponseBuilder { + b.spec.Spec.Spec.Links = v + return b +} + +func (b *ResponseBuilder) AddLink(key string, value *RefOrSpec[Extendable[Link]]) *ResponseBuilder { + if b.spec.Spec.Spec.Links == nil { + b.spec.Spec.Spec.Links = make(map[string]*RefOrSpec[Extendable[Link]], 1) + } + b.spec.Spec.Spec.Links[key] = value + return b +} + +func (b *ResponseBuilder) Description(v string) *ResponseBuilder { + b.spec.Spec.Spec.Description = v + return b +} diff --git a/responses.go b/responses.go index f3983c6..f1365b4 100644 --- a/responses.go +++ b/responses.go @@ -146,3 +146,45 @@ func (o *Responses) validateSpec(location string, opts *specValidationOptions) [ } return errs } + +type ResponsesBuilder struct { + spec *RefOrSpec[Extendable[Responses]] +} + +func NewResponsesBuilder() *ResponsesBuilder { + return &ResponsesBuilder{ + spec: NewRefOrExtSpec[Responses](&Responses{}), + } +} + +func (b *ResponsesBuilder) Build() *RefOrSpec[Extendable[Responses]] { + return b.spec +} + +func (b *ResponsesBuilder) Extensions(v map[string]any) *ResponsesBuilder { + b.spec.Spec.Extensions = v + return b +} + +func (b *ResponsesBuilder) AddExt(name string, value any) *ResponsesBuilder { + b.spec.Spec.Add(name, value) + return b +} + +func (b *ResponsesBuilder) Default(v *RefOrSpec[Extendable[Response]]) *ResponsesBuilder { + b.spec.Spec.Spec.Default = v + return b +} + +func (b *ResponsesBuilder) Response(v map[string]*RefOrSpec[Extendable[Response]]) *ResponsesBuilder { + b.spec.Spec.Spec.Response = v + return b +} + +func (b *ResponsesBuilder) AddResponse(key string, value *RefOrSpec[Extendable[Response]]) *ResponsesBuilder { + if b.spec.Spec.Spec.Response == nil { + b.spec.Spec.Spec.Response = make(map[string]*RefOrSpec[Extendable[Response]], 1) + } + b.spec.Spec.Spec.Response[key] = value + return b +} diff --git a/schema.go b/schema.go index 18196c9..e37b6c5 100644 --- a/schema.go +++ b/schema.go @@ -28,9 +28,24 @@ const ( type Schema struct { // *** Core Fields *** + // The $schema keyword is used to declare which dialect of JSON Schema the schema was written for. + // The value of the $schema keyword is also the identifier for a schema that can be used to verify + // that the schema is valid according to the dialect $schema identifies. + // A schema that describes another schema is called a "meta-schema". + // $schema applies to the entire document and must be at the root level. + // It does not apply to externally referenced ($ref, $dynamicRef) documents. + // Those schemas need to declare their own $schema. + // If $schema is not used, an implementation might allow you to specify a value externally or + // it might make assumptions about which specification version should be used to evaluate the schema. + // It's recommended that all JSON Schemas have a $schema keyword to communicate to readers and + // tooling which specification version is intended. + // // https://json-schema.org/understanding-json-schema/reference/schema#schema Schema string `json:"$schema,omitempty" yaml:"$schema,omitempty"` - // https://json-schema.org/understanding-json-schema/structuring#dollarid + // The value of $id is a URI-reference without a fragment that resolves against the retrieval-uri. + // The resulting URI is the base URI for the schema. + // + // https://json-schema.org/understanding-json-schema/structuring#id ID string `json:"$id,omitempty" yaml:"$id,omitempty"` // https://json-schema.org/understanding-json-schema/structuring#dollardefs Defs map[string]*RefOrSpec[Schema] `json:"$defs,omitempty" yaml:"$defs,omitempty"` @@ -300,10 +315,10 @@ type Schema struct { Extensions map[string]any `json:"-" yaml:"-"` } -// WithExt sets the extension and returns the current object (self|this). +// AddExt sets the extension and returns the current object (self|this). // Schema does not require special `x-` prefix. // The extension will be ignored if the name overlaps with a struct field during marshalling to JSON or YAML. -func (o *Schema) WithExt(name string, value any) *Schema { +func (o *Schema) AddExt(name string, value any) *Schema { if o.Extensions == nil { o.Extensions = make(map[string]any, 1) } @@ -311,6 +326,16 @@ func (o *Schema) WithExt(name string, value any) *Schema { return o } +func (o *Schema) GetExt(name string) any { + if o.Extensions == nil { + return nil + } + if !strings.HasPrefix(name, ExtensionPrefix) { + name = ExtensionPrefix + name + } + return o.Extensions[name] +} + // returns the list of public fields for given tag and ignores `-` names func getFields(t reflect.Type, tag string) map[string]struct{} { if t.Kind() == reflect.Pointer { @@ -687,3 +712,416 @@ func (o *Schema) validateSpec(location string, opts *specValidationOptions) []*v } return errs } + +type SchemaBulder struct { + spec *RefOrSpec[Schema] +} + +func NewSchemaBuilder() *SchemaBulder { + return &SchemaBulder{ + spec: NewRefOrSpec[Schema](&Schema{ + Schema: Draft202012, + }), + } +} + +func (b *SchemaBulder) Build() *RefOrSpec[Schema] { + return b.spec +} + +func (b *SchemaBulder) Extensions(v map[string]any) *SchemaBulder { + b.spec.Spec.Extensions = v + return b +} + +func (b *SchemaBulder) AddExt(name string, value any) *SchemaBulder { + b.spec.Spec.AddExt(name, value) + return b +} + +func (b *SchemaBulder) Schema(v string) *SchemaBulder { + b.spec.Spec.Schema = v + return b +} + +func (b *SchemaBulder) ID(v string) *SchemaBulder { + b.spec.Spec.ID = v + return b +} + +func (b *SchemaBulder) Defs(v map[string]*RefOrSpec[Schema]) *SchemaBulder { + b.spec.Spec.Defs = v + return b +} + +func (b *SchemaBulder) AddDef(name string, value *RefOrSpec[Schema]) *SchemaBulder { + if b.spec.Spec.Defs == nil { + b.spec.Spec.Defs = make(map[string]*RefOrSpec[Schema], 1) + } + b.spec.Spec.Defs[name] = value + return b +} + +func (b *SchemaBulder) DynamicRef(v string) *SchemaBulder { + b.spec.Spec.DynamicRef = v + return b +} + +func (b *SchemaBulder) Vocabulary(v map[string]bool) *SchemaBulder { + b.spec.Spec.Vocabulary = v + return b +} + +func (b *SchemaBulder) AddVocabulary(name string, value bool) *SchemaBulder { + if b.spec.Spec.Vocabulary == nil { + b.spec.Spec.Vocabulary = make(map[string]bool, 1) + } + b.spec.Spec.Vocabulary[name] = value + return b +} + +func (b *SchemaBulder) DynamicAnchor(v string) *SchemaBulder { + b.spec.Spec.DynamicAnchor = v + return b +} + +func (b *SchemaBulder) Type(v ...string) *SchemaBulder { + b.spec.Spec.Type = NewSingleOrArray[string](v...) + return b +} + +func (b *SchemaBulder) AddType(v ...string) *SchemaBulder { + if b.spec.Spec.Type == nil { + b.spec.Spec.Type = NewSingleOrArray[string](v...) + } else { + b.spec.Spec.Type.Add(v...) + } + return b +} + +func (b *SchemaBulder) Default(v any) *SchemaBulder { + b.spec.Spec.Default = v + return b +} + +func (b *SchemaBulder) Title(v string) *SchemaBulder { + b.spec.Spec.Title = v + return b +} + +func (b *SchemaBulder) Description(v string) *SchemaBulder { + b.spec.Spec.Description = v + return b +} + +func (b *SchemaBulder) Const(v string) *SchemaBulder { + b.spec.Spec.Const = v + return b +} + +func (b *SchemaBulder) Comment(v string) *SchemaBulder { + b.spec.Spec.Comment = v + return b +} + +func (b *SchemaBulder) Enum(v ...any) *SchemaBulder { + b.spec.Spec.Enum = v + return b +} + +func (b *SchemaBulder) AddEnum(v ...any) *SchemaBulder { + b.spec.Spec.Enum = append(b.spec.Spec.Enum, v...) + return b +} + +func (b *SchemaBulder) Examples(v ...any) *SchemaBulder { + b.spec.Spec.Examples = v + return b +} + +func (b *SchemaBulder) AddExamples(v ...any) *SchemaBulder { + b.spec.Spec.Examples = append(b.spec.Spec.Examples, v...) + return b +} + +func (b *SchemaBulder) ReadOnly(v bool) *SchemaBulder { + b.spec.Spec.ReadOnly = v + return b +} + +func (b *SchemaBulder) WriteOnly(v bool) *SchemaBulder { + b.spec.Spec.WriteOnly = v + return b +} + +func (b *SchemaBulder) Deprecated(v bool) *SchemaBulder { + b.spec.Spec.Deprecated = v + return b +} + +func (b *SchemaBulder) ContentSchema(v *RefOrSpec[Schema]) *SchemaBulder { + b.spec.Spec.ContentSchema = v + return b +} + +func (b *SchemaBulder) ContentMediaType(v string) *SchemaBulder { + b.spec.Spec.ContentMediaType = v + return b +} + +func (b *SchemaBulder) ContentEncoding(v string) *SchemaBulder { + b.spec.Spec.ContentEncoding = v + return b +} + +func (b *SchemaBulder) Not(v *RefOrSpec[Schema]) *SchemaBulder { + b.spec.Spec.Not = v + return b +} + +func (b *SchemaBulder) AllOf(v ...*RefOrSpec[Schema]) *SchemaBulder { + b.spec.Spec.AllOf = v + return b +} + +func (b *SchemaBulder) AddAllOf(v ...*RefOrSpec[Schema]) *SchemaBulder { + b.spec.Spec.AllOf = append(b.spec.Spec.AllOf, v...) + return b +} + +func (b *SchemaBulder) AnyOf(v ...*RefOrSpec[Schema]) *SchemaBulder { + b.spec.Spec.AnyOf = v + return b +} + +func (b *SchemaBulder) AddAnyOf(v ...*RefOrSpec[Schema]) *SchemaBulder { + b.spec.Spec.AnyOf = append(b.spec.Spec.AnyOf, v...) + return b +} + +func (b *SchemaBulder) OneOf(v ...*RefOrSpec[Schema]) *SchemaBulder { + b.spec.Spec.OneOf = v + return b +} + +func (b *SchemaBulder) AddOneOf(v ...*RefOrSpec[Schema]) *SchemaBulder { + b.spec.Spec.OneOf = append(b.spec.Spec.OneOf, v...) + return b +} + +func (b *SchemaBulder) DependentRequired(v map[string][]string) *SchemaBulder { + b.spec.Spec.DependentRequired = v + return b +} + +func (b *SchemaBulder) AddDependentRequired(name string, value ...string) *SchemaBulder { + if b.spec.Spec.DependentRequired == nil { + b.spec.Spec.DependentRequired = make(map[string][]string, 1) + } + b.spec.Spec.DependentRequired[name] = append(b.spec.Spec.DependentRequired[name], value...) + return b +} + +func (b *SchemaBulder) DependentSchemas(v map[string]*RefOrSpec[Schema]) *SchemaBulder { + b.spec.Spec.DependentSchemas = v + return b +} + +func (b *SchemaBulder) AddDependentSchema(name string, value *RefOrSpec[Schema]) *SchemaBulder { + if b.spec.Spec.DependentSchemas == nil { + b.spec.Spec.DependentSchemas = make(map[string]*RefOrSpec[Schema], 1) + } + b.spec.Spec.DependentSchemas[name] = value + return b +} + +func (b *SchemaBulder) If(v *RefOrSpec[Schema]) *SchemaBulder { + b.spec.Spec.If = v + return b +} + +func (b *SchemaBulder) Then(v *RefOrSpec[Schema]) *SchemaBulder { + b.spec.Spec.Then = v + return b +} + +func (b *SchemaBulder) Else(v *RefOrSpec[Schema]) *SchemaBulder { + b.spec.Spec.Else = v + return b +} + +func (b *SchemaBulder) MultipleOf(v int) *SchemaBulder { + b.spec.Spec.MultipleOf = &v + return b +} + +func (b *SchemaBulder) Minimum(v int) *SchemaBulder { + b.spec.Spec.Minimum = &v + return b +} + +func (b *SchemaBulder) ExclusiveMinimum(v int) *SchemaBulder { + b.spec.Spec.ExclusiveMinimum = &v + return b +} + +func (b *SchemaBulder) Maximum(v int) *SchemaBulder { + b.spec.Spec.Maximum = &v + return b +} + +func (b *SchemaBulder) ExclusiveMaximum(v int) *SchemaBulder { + b.spec.Spec.ExclusiveMaximum = &v + return b +} + +func (b *SchemaBulder) MinLength(v int) *SchemaBulder { + b.spec.Spec.MinLength = &v + return b +} + +func (b *SchemaBulder) MaxLength(v int) *SchemaBulder { + b.spec.Spec.MaxLength = &v + return b +} + +func (b *SchemaBulder) Pattern(v string) *SchemaBulder { + b.spec.Spec.Pattern = v + return b +} + +func (b *SchemaBulder) Format(v string) *SchemaBulder { + b.spec.Spec.Format = v + return b +} + +func (b *SchemaBulder) Items(v *BoolOrSchema) *SchemaBulder { + b.spec.Spec.Items = v + return b +} + +func (b *SchemaBulder) MaxItems(v int) *SchemaBulder { + b.spec.Spec.MaxItems = &v + return b +} + +func (b *SchemaBulder) UnevaluatedItems(v *BoolOrSchema) *SchemaBulder { + b.spec.Spec.UnevaluatedItems = v + return b +} + +func (b *SchemaBulder) Contains(v *RefOrSpec[Schema]) *SchemaBulder { + b.spec.Spec.Contains = v + return b +} + +func (b *SchemaBulder) MinContains(v int) *SchemaBulder { + b.spec.Spec.MinContains = &v + return b +} + +func (b *SchemaBulder) MaxContains(v int) *SchemaBulder { + b.spec.Spec.MaxContains = &v + return b +} + +func (b *SchemaBulder) MinItems(v int) *SchemaBulder { + b.spec.Spec.MinItems = &v + return b +} + +func (b *SchemaBulder) UniqueItems(v bool) *SchemaBulder { + b.spec.Spec.UniqueItems = &v + return b +} + +func (b *SchemaBulder) PrefixItems(v ...*RefOrSpec[Schema]) *SchemaBulder { + b.spec.Spec.PrefixItems = v + return b +} + +func (b *SchemaBulder) AddPrefixItems(v ...*RefOrSpec[Schema]) *SchemaBulder { + b.spec.Spec.PrefixItems = append(b.spec.Spec.PrefixItems, v...) + return b +} + +func (b *SchemaBulder) Properties(v map[string]*RefOrSpec[Schema]) *SchemaBulder { + b.spec.Spec.Properties = v + return b +} + +func (b *SchemaBulder) AddProperty(name string, value *RefOrSpec[Schema]) *SchemaBulder { + if b.spec.Spec.Properties == nil { + b.spec.Spec.Properties = make(map[string]*RefOrSpec[Schema], 1) + } + b.spec.Spec.Properties[name] = value + return b +} + +func (b *SchemaBulder) PatternProperties(v map[string]*RefOrSpec[Schema]) *SchemaBulder { + b.spec.Spec.PatternProperties = v + return b +} + +func (b *SchemaBulder) AddPatternProperty(name string, value *RefOrSpec[Schema]) *SchemaBulder { + if b.spec.Spec.PatternProperties == nil { + b.spec.Spec.PatternProperties = make(map[string]*RefOrSpec[Schema], 1) + } + b.spec.Spec.PatternProperties[name] = value + return b +} + +func (b *SchemaBulder) AdditionalProperties(v *BoolOrSchema) *SchemaBulder { + b.spec.Spec.AdditionalProperties = v + return b +} + +func (b *SchemaBulder) UnevaluatedProperties(v *BoolOrSchema) *SchemaBulder { + b.spec.Spec.UnevaluatedProperties = v + return b +} + +func (b *SchemaBulder) PropertyNames(v *RefOrSpec[Schema]) *SchemaBulder { + b.spec.Spec.PropertyNames = v + return b +} + +func (b *SchemaBulder) MinProperties(v int) *SchemaBulder { + b.spec.Spec.MinProperties = &v + return b +} + +func (b *SchemaBulder) MaxProperties(v int) *SchemaBulder { + b.spec.Spec.MaxProperties = &v + return b +} + +func (b *SchemaBulder) Required(v ...string) *SchemaBulder { + b.spec.Spec.Required = v + return b +} + +func (b *SchemaBulder) AddRequired(v ...string) *SchemaBulder { + b.spec.Spec.Required = append(b.spec.Spec.Required, v...) + return b +} + +func (b *SchemaBulder) Discriminator(v *Discriminator) *SchemaBulder { + b.spec.Spec.Discriminator = v + return b +} + +func (b *SchemaBulder) XML(v *Extendable[XML]) *SchemaBulder { + b.spec.Spec.XML = v + return b +} + +func (b *SchemaBulder) ExternalDocs(v *Extendable[ExternalDocs]) *SchemaBulder { + b.spec.Spec.ExternalDocs = v + return b +} + +func (b *SchemaBulder) Example(v any) *SchemaBulder { + b.spec.Spec.Example = v + return b +} diff --git a/schema_test.go b/schema_test.go index c2d26ef..76582c6 100644 --- a/schema_test.go +++ b/schema_test.go @@ -63,7 +63,7 @@ func TestSchema_Marshal_Unmarshal(t *testing.T) { } } -func TestSchema_WithExt(t *testing.T) { +func TestSchema_AddExt(t *testing.T) { for _, tt := range []struct { name string key string @@ -89,7 +89,7 @@ func TestSchema_WithExt(t *testing.T) { } { t.Run(tt.name, func(t *testing.T) { ext := openapi.Schema{} - ext.WithExt(tt.key, tt.value) + ext.AddExt(tt.key, tt.value) require.Equal(t, tt.expected, ext.Extensions) }) } diff --git a/security-requirement.go b/security-requirement.go index 2c4acc4..1ef9f90 100644 --- a/security-requirement.go +++ b/security-requirement.go @@ -20,3 +20,22 @@ func (o *SecurityRequirement) validateSpec(path string, opts *specValidationOpti } return nil // nothing to validate } + +type SecurityRequirementBuilder struct { + spec SecurityRequirement +} + +func NewSecurityRequirementBuilder() *SecurityRequirementBuilder { + return &SecurityRequirementBuilder{ + spec: make(SecurityRequirement), + } +} + +func (b *SecurityRequirementBuilder) Build() *SecurityRequirement { + return &b.spec +} + +func (b *SecurityRequirementBuilder) Add(name string, scopes ...string) *SecurityRequirementBuilder { + b.spec[name] = append(b.spec[name], scopes...) + return b +} diff --git a/security-scheme.go b/security-scheme.go index 7cc5d28..3be4e73 100644 --- a/security-scheme.go +++ b/security-scheme.go @@ -114,3 +114,67 @@ func (o *SecurityScheme) validateSpec(location string, opts *specValidationOptio } return errs } + +type SecuritySchemeBuilder struct { + spec *RefOrSpec[Extendable[SecurityScheme]] +} + +func NewSecuritySchemeBuilder() *SecuritySchemeBuilder { + return &SecuritySchemeBuilder{ + spec: NewRefOrExtSpec[SecurityScheme](&SecurityScheme{}), + } +} + +func (b *SecuritySchemeBuilder) Build() *RefOrSpec[Extendable[SecurityScheme]] { + return b.spec +} + +func (b *SecuritySchemeBuilder) Extensions(v map[string]any) *SecuritySchemeBuilder { + b.spec.Spec.Extensions = v + return b +} + +func (b *SecuritySchemeBuilder) AddExt(name string, value any) *SecuritySchemeBuilder { + b.spec.Spec.Add(name, value) + return b +} + +func (b *SecuritySchemeBuilder) Type(v string) *SecuritySchemeBuilder { + b.spec.Spec.Spec.Type = v + return b +} + +func (b *SecuritySchemeBuilder) Description(v string) *SecuritySchemeBuilder { + b.spec.Spec.Spec.Description = v + return b +} + +func (b *SecuritySchemeBuilder) Name(v string) *SecuritySchemeBuilder { + b.spec.Spec.Spec.Name = v + return b +} + +func (b *SecuritySchemeBuilder) In(v string) *SecuritySchemeBuilder { + b.spec.Spec.Spec.In = v + return b +} + +func (b *SecuritySchemeBuilder) Scheme(v string) *SecuritySchemeBuilder { + b.spec.Spec.Spec.Scheme = v + return b +} + +func (b *SecuritySchemeBuilder) BearerFormat(v string) *SecuritySchemeBuilder { + b.spec.Spec.Spec.BearerFormat = v + return b +} + +func (b *SecuritySchemeBuilder) Flows(v *Extendable[OAuthFlows]) *SecuritySchemeBuilder { + b.spec.Spec.Spec.Flows = v + return b +} + +func (b *SecuritySchemeBuilder) OpenIDConnectURL(v string) *SecuritySchemeBuilder { + b.spec.Spec.Spec.OpenIDConnectURL = v + return b +} diff --git a/server.go b/server.go index 96f030c..9dc1cd2 100644 --- a/server.go +++ b/server.go @@ -52,3 +52,50 @@ func (o *Server) validateSpec(location string, opts *specValidationOptions) []*v } return errs } + +type ServerBuilder struct { + spec *Extendable[Server] +} + +func NewServerBuilder() *ServerBuilder { + return &ServerBuilder{ + spec: NewExtendable[Server](&Server{}), + } +} + +func (b *ServerBuilder) Build() *Extendable[Server] { + return b.spec +} + +func (b *ServerBuilder) Extensions(v map[string]any) *ServerBuilder { + b.spec.Extensions = v + return b +} + +func (b *ServerBuilder) AddExt(name string, value any) *ServerBuilder { + b.spec.Add(name, value) + return b +} + +func (b *ServerBuilder) Variables(v map[string]*Extendable[ServerVariable]) *ServerBuilder { + b.spec.Spec.Variables = v + return b +} + +func (b *ServerBuilder) AddVariable(name string, value *Extendable[ServerVariable]) *ServerBuilder { + if b.spec.Spec.Variables == nil { + b.spec.Spec.Variables = make(map[string]*Extendable[ServerVariable], 1) + } + b.spec.Spec.Variables[name] = value + return b +} + +func (b *ServerBuilder) URL(v string) *ServerBuilder { + b.spec.Spec.URL = v + return b +} + +func (b *ServerBuilder) Description(v string) *ServerBuilder { + b.spec.Spec.Description = v + return b +} diff --git a/server_variable.go b/server_variable.go index f59a475..5eceb17 100644 --- a/server_variable.go +++ b/server_variable.go @@ -25,3 +25,47 @@ func (o *ServerVariable) validateSpec(location string, opts *specValidationOptio } return errs } + +type ServerVariableBuilder struct { + spec *Extendable[ServerVariable] +} + +func NewServerVariableBuilder() *ServerVariableBuilder { + return &ServerVariableBuilder{ + spec: NewExtendable[ServerVariable](&ServerVariable{}), + } +} + +func (b *ServerVariableBuilder) Build() *Extendable[ServerVariable] { + return b.spec +} + +func (b *ServerVariableBuilder) Extensions(v map[string]any) *ServerVariableBuilder { + b.spec.Extensions = v + return b +} + +func (b *ServerVariableBuilder) AddExt(name string, value any) *ServerVariableBuilder { + b.spec.Add(name, value) + return b +} + +func (b *ServerVariableBuilder) Default(v string) *ServerVariableBuilder { + b.spec.Spec.Default = v + return b +} + +func (b *ServerVariableBuilder) Description(v string) *ServerVariableBuilder { + b.spec.Spec.Description = v + return b +} + +func (b *ServerVariableBuilder) Enum(v ...string) *ServerVariableBuilder { + b.spec.Spec.Enum = v + return b +} + +func (b *ServerVariableBuilder) AddEnum(v ...string) *ServerVariableBuilder { + b.spec.Spec.Enum = append(b.spec.Spec.Enum, v...) + return b +} diff --git a/single_or_array.go b/single_or_array.go index ed23a24..09b7930 100644 --- a/single_or_array.go +++ b/single_or_array.go @@ -11,8 +11,8 @@ type SingleOrArray[T any] []T // NewSingleOrArray creates SingleOrArray object. func NewSingleOrArray[T any](v ...T) *SingleOrArray[T] { - a := append(SingleOrArray[T]{}, v...) - return &a + vv := SingleOrArray[T](v) + return &vv } // UnmarshalJSON implements json.Unmarshaler interface. @@ -60,3 +60,8 @@ func (o *SingleOrArray[T]) MarshalYAML() (any, error) { } return v, nil } + +func (o *SingleOrArray[T]) Add(v ...T) *SingleOrArray[T] { + *o = append(*o, v...) + return o +} diff --git a/tag.go b/tag.go index b12c0b0..092bb74 100644 --- a/tag.go +++ b/tag.go @@ -31,3 +31,42 @@ func (o *Tag) validateSpec(location string, opts *specValidationOptions) []*vali opts.visited[joinLoc("tags", o.Name)] = true return errs } + +type TagBuilder struct { + spec *Extendable[Tag] +} + +func NewTagBuilder() *TagBuilder { + return &TagBuilder{ + spec: NewExtendable[Tag](&Tag{}), + } +} + +func (b *TagBuilder) Build() *Extendable[Tag] { + return b.spec +} + +func (b *TagBuilder) Extensions(v map[string]any) *TagBuilder { + b.spec.Extensions = v + return b +} + +func (b *TagBuilder) AddExt(name string, value any) *TagBuilder { + b.spec.Add(name, value) + return b +} + +func (b *TagBuilder) ExternalDocs(v *Extendable[ExternalDocs]) *TagBuilder { + b.spec.Spec.ExternalDocs = v + return b +} + +func (b *TagBuilder) Name(v string) *TagBuilder { + b.spec.Spec.Name = v + return b +} + +func (b *TagBuilder) Description(v string) *TagBuilder { + b.spec.Spec.Description = v + return b +} diff --git a/validation_test.go b/validation_test.go index 4635ae8..6f81e1f 100644 --- a/validation_test.go +++ b/validation_test.go @@ -57,174 +57,178 @@ func TestValidator_ValidateSpec_ManuallyCreated(t *testing.T) { err string }{ { - name: "empty", - spec: openapi.NewExtendable(&openapi.OpenAPI{}), - err: "openapi: required", + name: "info required", + spec: openapi.NewOpenAPIBuilder().Build(), + err: "/info: required", + }, + { + name: "any of path or webhooks or components required", + spec: openapi.NewOpenAPIBuilder().Build(), + err: "/paths||webhooks||components: required", }, { name: "minimal valid with empty paths", - spec: openapi.NewExtendable(&openapi.OpenAPI{ - OpenAPI: "3.1.1", - Info: openapi.NewExtendable(&openapi.Info{ - Title: "Minimal Valid Spec", - Version: "1.0.0", - }), - Paths: openapi.NewExtendable[openapi.Paths](&openapi.Paths{}), - }), + spec: openapi.NewOpenAPIBuilder().Info( + openapi.NewInfoBuilder(). + Title("Minimal Valid Spec"). + Version("1.0.0"). + Build(), + ).Paths(openapi.NewPaths()).Build(), }, { name: "minimal valid with empty components", - spec: openapi.NewExtendable(&openapi.OpenAPI{ - OpenAPI: "3.1.1", - Info: openapi.NewExtendable(&openapi.Info{ - Title: "Minimal Valid Spec", - Version: "1.0.0", - }), - Components: openapi.NewExtendable[openapi.Components](&openapi.Components{}), - }), + spec: openapi.NewOpenAPIBuilder().Info( + openapi.NewInfoBuilder(). + Title("Minimal Valid Spec"). + Version("1.0.0"). + Build(), + ).Components(openapi.NewComponents()).Build(), }, { name: "minimal valid with empty webhooks", - spec: openapi.NewExtendable(&openapi.OpenAPI{ - OpenAPI: "3.1.1", - Info: openapi.NewExtendable(&openapi.Info{ - Title: "Minimal Valid Spec", - Version: "1.0.0", - }), - WebHooks: make(map[string]*openapi.RefOrSpec[openapi.Extendable[openapi.PathItem]]), - }), + spec: openapi.NewOpenAPIBuilder().Info( + openapi.NewInfoBuilder(). + Title("Minimal Valid Spec"). + Version("1.0.0"). + Build(), + ).WebHooks(openapi.NewWebhooks()).Build(), }, { name: "xml component", - spec: openapi.NewExtendable(&openapi.OpenAPI{ - OpenAPI: "3.1.1", - Info: openapi.NewExtendable(&openapi.Info{ - Title: "Minimal Valid Spec", - Version: "1.0.0", - }), - Components: openapi.NewExtendable[openapi.Components]((&openapi.Components{}).WithRefOrSpec( - "Person", - &openapi.Schema{ - Type: openapi.NewSingleOrArray[string]("object"), - Properties: map[string]*openapi.RefOrSpec[openapi.Schema]{ - "id": openapi.NewRefOrSpec[openapi.Schema](&openapi.Schema{ - Type: openapi.NewSingleOrArray[string]("integer"), - Format: "int32", - XML: openapi.NewExtendable(&openapi.XML{ - Attribute: true, - }), - }), - "name": openapi.NewRefOrSpec[openapi.Schema](&openapi.Schema{ - Type: openapi.NewSingleOrArray[string]("string"), - XML: openapi.NewExtendable(&openapi.XML{ - Namespace: "https://example.com/schema/sample", - Prefix: "sample", - }), - }), - }, - }, - )), - }), + spec: openapi.NewOpenAPIBuilder().Info( + openapi.NewInfoBuilder(). + Title("Minimal Valid Spec"). + Version("1.0.0"). + Build(), + ).AddComponent("Person", openapi.NewSchemaBuilder(). + AddType("object"). + AddProperty("id", openapi.NewSchemaBuilder(). + AddType("integer"). + Format("int32"). + XML(openapi.NewXMLBuilder().Attribute(true).Build()). + Build(), + ). + AddProperty("name", openapi.NewSchemaBuilder(). + AddType("string"). + XML(openapi.NewXMLBuilder(). + Namespace("https://example.com/schema/sample"). + Prefix("sample"). + Build(), + ). + Build(), + ). + Build(), + ).Build(), opts: []openapi.SpecValidationOption{openapi.AllowUnusedComponents()}, }, { name: "properties examples", - spec: openapi.NewExtendable(&openapi.OpenAPI{ - OpenAPI: "3.1.1", - Info: openapi.NewExtendable(&openapi.Info{ - Title: "Minimal Valid Spec", - Version: "1.0.0", - }), - Components: openapi.NewExtendable[openapi.Components]((&openapi.Components{}).WithRefOrSpec( - "Person", - &openapi.Schema{ - Type: openapi.NewSingleOrArray[string]("object"), - Properties: map[string]*openapi.RefOrSpec[openapi.Schema]{ - "id": openapi.NewRefOrSpec[openapi.Schema](&openapi.Schema{ - Type: openapi.NewSingleOrArray[string]("integer"), - Format: "int32", - }), - "name": openapi.NewRefOrSpec[openapi.Schema](&openapi.Schema{ - Type: openapi.NewSingleOrArray[string]("string"), - }), - }, - Examples: []any{ - map[string]any{ - "id": 123, - "name": "John Doe 1", - }, - struct { - ID int `json:"id"` - Name string `json:"name"` - }{ - ID: 124, - Name: "John Doe 2", - }, - }, + spec: openapi.NewOpenAPIBuilder().Info( + openapi.NewInfoBuilder(). + Title("Minimal Valid Spec"). + Version("1.0.0"). + Build(), + ).AddComponent("Person", openapi.NewSchemaBuilder(). + AddType("object"). + AddProperty("id", openapi.NewSchemaBuilder(). + AddType("integer"). + Format("int32"). + Build(), + ). + AddProperty("name", openapi.NewSchemaBuilder(). + AddType("string"). + Build(), + ). + AddExamples( + map[string]any{ + "id": 123, + "name": "John Doe 1", + }, + struct { + ID int `json:"id"` + Name string `json:"name"` + }{ + ID: 124, + Name: "John Doe 2", }, - )), - }), + ).Build(), + ).Build(), opts: []openapi.SpecValidationOption{openapi.AllowUnusedComponents()}, }, { - name: "properties default", - spec: openapi.NewExtendable(&openapi.OpenAPI{ - OpenAPI: "3.1.1", - Info: openapi.NewExtendable(&openapi.Info{ - Title: "Minimal Valid Spec", - Version: "1.0.0", - }), - Components: openapi.NewExtendable[openapi.Components]((&openapi.Components{}).WithRefOrSpec( - "Person", - &openapi.Schema{ - Type: openapi.NewSingleOrArray[string]("object"), - Properties: map[string]*openapi.RefOrSpec[openapi.Schema]{ - "id": openapi.NewRefOrSpec[openapi.Schema](&openapi.Schema{ - Type: openapi.NewSingleOrArray[string]("integer"), - Format: "int32", - Default: 42, - }), - "name": openapi.NewRefOrSpec[openapi.Schema](&openapi.Schema{ - Type: openapi.NewSingleOrArray[string]("string"), - Default: "John Doe", - }), - }, + name: "properties examples error", + spec: openapi.NewOpenAPIBuilder().Info( + openapi.NewInfoBuilder(). + Title("Minimal Valid Spec"). + Version("1.0.0"). + Build(), + ).AddComponent("Person", openapi.NewSchemaBuilder(). + AddType("object"). + AddProperty("id", openapi.NewSchemaBuilder(). + AddType("integer"). + Format("int32"). + Build(), + ). + AddProperty("name", openapi.NewSchemaBuilder(). + AddType("string"). + Build(), + ). + AddExamples( + map[string]any{ + "id": "123", + "name": false, }, - )), - }), + ).Build(), + ).Build(), opts: []openapi.SpecValidationOption{openapi.AllowUnusedComponents()}, + err: "at '/id': got string, want integer", }, { - name: "properties example", - spec: openapi.NewExtendable(&openapi.OpenAPI{ - OpenAPI: "3.1.1", - Info: openapi.NewExtendable(&openapi.Info{ - Title: "Minimal Valid Spec", - Version: "1.0.0", - }), - Components: openapi.NewExtendable[openapi.Components]((&openapi.Components{}).WithRefOrSpec( - "Person", - &openapi.Schema{ - Type: openapi.NewSingleOrArray[string]("object"), - Properties: map[string]*openapi.RefOrSpec[openapi.Schema]{ - "id": openapi.NewRefOrSpec[openapi.Schema](&openapi.Schema{ - Type: openapi.NewSingleOrArray[string]("integer"), - Format: "int32", - Default: 42, - }), - "name": openapi.NewRefOrSpec[openapi.Schema](&openapi.Schema{ - Type: openapi.NewSingleOrArray[string]("string"), - Default: "John Doe", - }), - }, - Example: map[string]any{ - "id": 123, - "name": "John Doe", - }, - }, - )), - }), + name: "properties default", + spec: openapi.NewOpenAPIBuilder().Info( + openapi.NewInfoBuilder(). + Title("Minimal Valid Spec"). + Version("1.0.0"). + Build(), + ).AddComponent("Person", openapi.NewSchemaBuilder(). + AddType("object"). + AddProperty("id", openapi.NewSchemaBuilder(). + AddType("integer"). + Format("int32"). + Default(42). + Build(), + ). + AddProperty("name", openapi.NewSchemaBuilder(). + AddType("string"). + Default("John Doe"). + Build(), + ).Build(), + ).Build(), + opts: []openapi.SpecValidationOption{openapi.AllowUnusedComponents()}, + }, + { + name: "properties default error", + spec: openapi.NewOpenAPIBuilder().Info( + openapi.NewInfoBuilder(). + Title("Minimal Valid Spec"). + Version("1.0.0"). + Build(), + ).AddComponent("Person", openapi.NewSchemaBuilder(). + AddType("object"). + AddProperty("id", openapi.NewSchemaBuilder(). + AddType("integer"). + Format("int32"). + Default("42"). + Build(), + ). + AddProperty("name", openapi.NewSchemaBuilder(). + AddType("string"). + Default(false). + Build(), + ).Build(), + ).Build(), opts: []openapi.SpecValidationOption{openapi.AllowUnusedComponents()}, + err: "at '': got string, want integer", }, } { t.Run(tt.name, func(t *testing.T) { diff --git a/webhooks.go b/webhooks.go new file mode 100644 index 0000000..2cf4768 --- /dev/null +++ b/webhooks.go @@ -0,0 +1,7 @@ +package openapi + +type Webhooks = map[string]*RefOrSpec[Extendable[PathItem]] + +func NewWebhooks() Webhooks { + return make(Webhooks) +} diff --git a/xml.go b/xml.go index 8925e89..fa1c52c 100644 --- a/xml.go +++ b/xml.go @@ -49,3 +49,52 @@ type XML struct { func (o *XML) validateSpec(path string, opts *specValidationOptions) []*validationError { return nil // nothing to validate } + +type XMLBuilder struct { + spec *Extendable[XML] +} + +func NewXMLBuilder() *XMLBuilder { + return &XMLBuilder{ + spec: NewExtendable[XML](&XML{}), + } +} + +func (b *XMLBuilder) Build() *Extendable[XML] { + return b.spec +} + +func (b *XMLBuilder) Extensions(v map[string]any) *XMLBuilder { + b.spec.Extensions = v + return b +} + +func (b *XMLBuilder) AddExt(name string, value any) *XMLBuilder { + b.spec.Add(name, value) + return b +} + +func (b *XMLBuilder) Name(v string) *XMLBuilder { + b.spec.Spec.Name = v + return b +} + +func (b *XMLBuilder) Namespace(v string) *XMLBuilder { + b.spec.Spec.Namespace = v + return b +} + +func (b *XMLBuilder) Prefix(v string) *XMLBuilder { + b.spec.Spec.Prefix = v + return b +} + +func (b *XMLBuilder) Attribute(v bool) *XMLBuilder { + b.spec.Spec.Attribute = v + return b +} + +func (b *XMLBuilder) Wrapped(v bool) *XMLBuilder { + b.spec.Spec.Wrapped = v + return b +}