Repository: ogen-go/ogen
Pull request #84 (2021-12-17 10:22:00)

[Feature] DSL
This PR introduces a fluent interface to ease construction and manipulation of the specification by code.


Patch:
diff --git a/dsl.go b/dsl.go
new file mode 100644
index 00000000..b0d9a3f2
--- /dev/null
+++ b/dsl.go
@@ -0,0 +1,1052 @@
+package ogen
+
+import (
+	"encoding/json"
+	"strings"
+
+	"github.com/ogen-go/ogen/internal/ir"
+	"github.com/ogen-go/ogen/internal/oas"
+)
+
+// NewSpec returns a new Spec.
+func NewSpec() *Spec {
+	return new(Spec)
+}
+
+// SetOpenAPI sets the OpenAPI Specification version of the document.
+func (s *Spec) SetOpenAPI(v string) *Spec {
+	s.OpenAPI = v
+	return s
+}
+
+// SetInfo sets the Info of the Spec.
+func (s *Spec) SetInfo(i *Info) *Spec {
+	if i != nil {
+		s.Info = *i
+	}
+	return s
+}
+
+// SetServers sets the Servers of the Spec.
+func (s *Spec) SetServers(srvs []Server) *Spec {
+	s.Servers = srvs
+	return s
+}
+
+// AddServers adds Servers to the Servers of the Spec.
+func (s *Spec) AddServers(srvs ...*Server) *Spec {
+	for _, srv := range srvs {
+		if srv != nil {
+			s.Servers = append(s.Servers, *srv)
+		}
+	}
+	return s
+}
+
+// SetPaths sets the Paths of the Spec.
+func (s *Spec) SetPaths(p Paths) *Spec {
+	s.Paths = p
+	return s
+}
+
+// AddPathItem adds the given PathItem under the given Name to the Paths of the Spec.
+func (s *Spec) AddPathItem(n string, p *PathItem) *Spec {
+	s.initPaths()
+	s.Paths[n] = p
+	return s
+}
+
+// AddNamedPathItems adds the given namedPaths to the Paths of the Spec.
+func (s *Spec) AddNamedPathItems(ps ...*NamedPathItem) *Spec {
+	for _, p := range ps {
+		s.AddPathItem(p.Name, p.PathItem)
+	}
+	return s
+}
+
+// SetComponents sets the Components of the Spec.
+func (s *Spec) SetComponents(c *Components) *Spec {
+	s.Components = c
+	return s
+}
+
+// initPaths ensures the Paths map is allocated.
+func (s *Spec) initPaths() {
+	if s.Paths == nil {
+		s.Paths = make(Paths)
+	}
+}
+
+// AddSchema adds the given Schema under the given Name to the Components of the Spec.
+func (s *Spec) AddSchema(n string, sc *Schema) *Spec {
+	s.initSchemas()
+	s.Components.Schemas[n] = sc
+	return s
+}
+
+// AddNamedSchemas adds the given namedSchemas to the Components of the Spec.
+func (s *Spec) AddNamedSchemas(scs ...*NamedSchema) *Spec {
+	for _, sc := range scs {
+		s.AddSchema(sc.Name, sc.Schema)
+	}
+	return s
+}
+
+// AddResponse adds the given Response under the given Name to the Components of the Spec.
+func (s *Spec) AddResponse(n string, sc *Response) *Spec {
+	s.initResponses()
+	s.Components.Responses[n] = sc
+	return s
+}
+
+// AddNamedResponses adds the given namedResponses to the Components of the Spec.
+func (s *Spec) AddNamedResponses(scs ...*NamedResponse) *Spec {
+	for _, sc := range scs {
+		s.AddResponse(sc.Name, sc.Response)
+	}
+	return s
+}
+
+// AddParameter adds the given Parameter under the given Name to the Components of the Spec.
+func (s *Spec) AddParameter(n string, p *Parameter) *Spec {
+	s.initParameters()
+	s.Components.Parameters[n] = p
+	return s
+}
+
+// AddNamedParameters adds the given namedParameters to the Components of the Spec.
+func (s *Spec) AddNamedParameters(ps ...*NamedParameter) *Spec {
+	for _, p := range ps {
+		s.AddParameter(p.Name, p.Parameter)
+	}
+	return s
+}
+
+// AddRequestBody adds the given RequestBody under the given Name to the Components of the Spec.
+func (s *Spec) AddRequestBody(n string, sc *RequestBody) *Spec {
+	s.initRequestBodies()
+	s.Components.RequestBodies[n] = sc
+	return s
+}
+
+// AddNamedRequestBodies adds the given namedRequestBodies to the Components of the Spec.
+func (s *Spec) AddNamedRequestBodies(scs ...*NamedRequestBody) *Spec {
+	for _, sc := range scs {
+		s.AddRequestBody(sc.Name, sc.RequestBody)
+	}
+	return s
+}
+
+// RefSchema returns a new Schema referencing the given name.
+func (s *Spec) RefSchema(n string) *NamedSchema {
+	if s.Components != nil && s.Components.Schemas != nil {
+		if r, ok := s.Components.Schemas[n]; ok {
+			return NewNamedSchema(n, r).AsLocalRef().ToNamed(n)
+		}
+	}
+	return nil
+}
+
+// RefResponse returns a new Response referencing the given name.
+func (s *Spec) RefResponse(n string) *NamedResponse {
+	if s.Components != nil && s.Components.Responses != nil {
+		if r, ok := s.Components.Responses[n]; ok {
+			return NewNamedResponse(n, r).AsLocalRef().ToNamed(n)
+		}
+	}
+	return nil
+}
+
+// RefRequestBody returns a new RequestBody referencing the given name.
+func (s *Spec) RefRequestBody(n string) *NamedRequestBody {
+	if s.Components != nil && s.Components.RequestBodies != nil {
+		if r, ok := s.Components.RequestBodies[n]; ok {
+			return NewNamedRequestBody(n, r).AsLocalRef().ToNamed(n)
+		}
+	}
+	return nil
+}
+
+// initComponents ensures the Components property is non-nil.
+func (s *Spec) initComponents() {
+	if s.Components == nil {
+		s.Components = new(Components)
+	}
+}
+
+// initParameters ensures the Parameters map is allocated.
+func (s *Spec) initParameters() {
+	s.initComponents()
+	if s.Components.Parameters == nil {
+		s.Components.Parameters = make(map[string]*Parameter)
+	}
+}
+
+// initSchemas ensures the Schemas map is allocated.
+func (s *Spec) initSchemas() {
+	s.initComponents()
+	if s.Components.Schemas == nil {
+		s.Components.Schemas = make(map[string]*Schema)
+	}
+}
+
+// initResponses ensures the Responses map is allocated.
+func (s *Spec) initResponses() {
+	s.initComponents()
+	if s.Components.Responses == nil {
+		s.Components.Responses = make(map[string]*Response)
+	}
+}
+
+// initRequestBodies ensures the RequestBodies map is allocated.
+func (s *Spec) initRequestBodies() {
+	s.initComponents()
+	if s.Components.RequestBodies == nil {
+		s.Components.RequestBodies = make(map[string]*RequestBody)
+	}
+}
+
+// NewRequestBody returns a new RequestBody.
+func NewRequestBody() *RequestBody {
+	return new(RequestBody)
+}
+
+// SetRef sets the Ref of the RequestBody.
+func (r *RequestBody) SetRef(ref string) *RequestBody {
+	r.Ref = ref
+	return r
+}
+
+// SetDescription sets the Description of the RequestBody.
+func (r *RequestBody) SetDescription(d string) *RequestBody {
+	r.Description = d
+	return r
+}
+
+// SetContent sets the Content of the RequestBody.
+func (r *RequestBody) SetContent(c map[string]Media) *RequestBody {
+	r.Content = c
+	return r
+}
+
+// AddContent adds the given Schema under the MediaType to the Content of the Response.
+func (r *RequestBody) AddContent(mt string, s *Schema) *RequestBody {
+	if s != nil {
+		r.initContent()
+		r.Content[mt] = Media{*s}
+	}
+	return r
+}
+
+// SetJSONContent sets the given Schema under the JSON MediaType to the Content of the Response.
+func (r *RequestBody) SetJSONContent(s *Schema) *RequestBody {
+	return r.AddContent(string(ir.ContentTypeJSON), s)
+}
+
+// initContent ensures the Content map is allocated.
+func (r *RequestBody) initContent() {
+	if r.Content == nil {
+		r.Content = make(map[string]Media)
+	}
+}
+
+// SetRequired sets the Required of the RequestBody.
+func (r *RequestBody) SetRequired(req bool) *RequestBody {
+	r.Required = req
+	return r
+}
+
+// ToNamed returns a NamedRequestBody wrapping the receiver.
+func (r *RequestBody) ToNamed(n string) *NamedRequestBody {
+	return NewNamedRequestBody(n, r)
+}
+
+// NamedRequestBody can be used to construct a reference to the wrapped RequestBody.
+type NamedRequestBody struct {
+	RequestBody *RequestBody
+	Name        string
+}
+
+// NewNamedRequestBody returns a new NamedRequestBody.
+func NewNamedRequestBody(n string, p *RequestBody) *NamedRequestBody {
+	return &NamedRequestBody{p, n}
+}
+
+// AsLocalRef returns a new RequestBody referencing the wrapped RequestBody in the local document.
+func (p *NamedRequestBody) AsLocalRef() *RequestBody {
+	return NewRequestBody().SetRef("#/components/requestBodies/" + escapeRef(p.Name))
+}
+
+// NewInfo returns a new Info.
+func NewInfo() *Info {
+	return new(Info)
+}
+
+// SetTitle sets the title of the Info.
+func (i *Info) SetTitle(t string) *Info {
+	i.Title = t
+	return i
+}
+
+// SetDescription sets the description of the Info.
+func (i *Info) SetDescription(d string) *Info {
+	i.Description = d
+	return i
+}
+
+// SetTermsOfService sets the terms of service of the Info.
+func (i *Info) SetTermsOfService(t string) *Info {
+	i.TermsOfService = t
+	return i
+}
+
+// SetContact sets the Contact of the Info.
+func (i *Info) SetContact(c *Contact) *Info {
+	i.Contact = c
+	return i
+}
+
+// SetLicense sets the License of the Info.
+func (i *Info) SetLicense(l *License) *Info {
+	i.License = l
+	return i
+}
+
+// SetVersion sets the version of the Info.
+func (i *Info) SetVersion(v string) *Info {
+	i.Version = v
+	return i
+}
+
+// NewContact returns a new Contact.
+func NewContact() *Contact {
+	return new(Contact)
+}
+
+// SetName sets the Name of the Contact.
+func (c *Contact) SetName(n string) *Contact {
+	c.Name = n
+	return c
+}
+
+// SetURL sets the URL of the Contact.
+func (c *Contact) SetURL(url string) *Contact {
+	c.URL = url
+	return c
+}
+
+// SetEmail sets the Email of the Contact.
+func (c *Contact) SetEmail(e string) *Contact {
+	c.Email = e
+	return c
+}
+
+// NewLicense returns a new License.
+func NewLicense() *License {
+	return new(License)
+}
+
+// SetName sets the Name of the License.
+func (l *License) SetName(n string) *License {
+	l.Name = n
+	return l
+}
+
+// SetURL sets the URL of the License.
+func (l *License) SetURL(url string) *License {
+	l.URL = url
+	return l
+}
+
+// NewServer returns a new Server.
+func NewServer() *Server {
+	return new(Server)
+}
+
+// SetDescription sets the Description of the Server.
+func (s *Server) SetDescription(d string) *Server {
+	s.Description = d
+	return s
+}
+
+// SetURL sets the URL of the Server.
+func (s *Server) SetURL(url string) *Server {
+	s.URL = url
+	return s
+}
+
+// NewPathItem returns a new PathItem.
+func NewPathItem() *PathItem {
+	return new(PathItem)
+}
+
+// SetRef sets the Ref of the PathItem.
+func (p *PathItem) SetRef(r string) *PathItem {
+	p.Ref = r
+	return p
+}
+
+// SetDescription sets the Description of the PathItem.
+func (p *PathItem) SetDescription(d string) *PathItem {
+	p.Description = d
+	return p
+}
+
+// SetGet sets the Get of the PathItem.
+func (p *PathItem) SetGet(o *Operation) *PathItem {
+	p.Get = o
+	return p
+}
+
+// SetPut sets the Put of the PathItem.
+func (p *PathItem) SetPut(o *Operation) *PathItem {
+	p.Put = o
+	return p
+}
+
+// SetPost sets the Post of the PathItem.
+func (p *PathItem) SetPost(o *Operation) *PathItem {
+	p.Post = o
+	return p
+}
+
+// SetDelete sets the Delete of the PathItem.
+func (p *PathItem) SetDelete(o *Operation) *PathItem {
+	p.Delete = o
+	return p
+}
+
+// SetOptions sets the Options of the PathItem.
+func (p *PathItem) SetOptions(o *Operation) *PathItem {
+	p.Options = o
+	return p
+}
+
+// SetHead sets the Head of the PathItem.
+func (p *PathItem) SetHead(o *Operation) *PathItem {
+	p.Head = o
+	return p
+}
+
+// SetPatch sets the Patch of the PathItem.
+func (p *PathItem) SetPatch(o *Operation) *PathItem {
+	p.Patch = o
+	return p
+}
+
+// SetTrace sets the Trace of the PathItem.
+func (p *PathItem) SetTrace(o *Operation) *PathItem {
+	p.Trace = o
+	return p
+}
+
+// SetServers sets the Servers of the PathItem.
+func (p *PathItem) SetServers(srvs []Server) *PathItem {
+	p.Servers = srvs
+	return p
+}
+
+// AddServers adds Servers to the Servers of the PathItem.
+func (p *PathItem) AddServers(srvs ...*Server) *PathItem {
+	for _, srv := range srvs {
+		if srv != nil {
+			p.Servers = append(p.Servers, *srv)
+		}
+	}
+	return p
+}
+
+// SetParameters sets the Parameters of the PathItem.
+func (p *PathItem) SetParameters(ps []*Parameter) *PathItem {
+	p.Parameters = ps
+	return p
+}
+
+// AddParameters adds Parameters to the Parameters of the PathItem.
+func (p *PathItem) AddParameters(ps ...*Parameter) *PathItem {
+	p.Parameters = append(p.Parameters, ps...)
+	return p
+}
+
+// ToNamed returns a NamedPathItem wrapping the receiver.
+func (p *PathItem) ToNamed(n string) *NamedPathItem {
+	return NewNamedPathItem(n, p)
+}
+
+// NamedPathItem can be used to construct a reference to the wrapped PathItem.
+type NamedPathItem struct {
+	PathItem *PathItem
+	Name     string
+}
+
+// NewNamedPathItem returns a new NamedPathItem.
+func NewNamedPathItem(n string, p *PathItem) *NamedPathItem {
+	return &NamedPathItem{p, n}
+}
+
+// AsLocalRef returns a new PathItem referencing the wrapped PathItem in the local document.
+func (p *NamedPathItem) AsLocalRef() *PathItem {
+	return NewPathItem().SetRef("#/paths/" + escapeRef(p.Name))
+}
+
+// NewOperation returns a new Operation.
+func NewOperation() *Operation {
+	return new(Operation)
+}
+
+// SetTags sets the Tags of the Operation.
+func (o *Operation) SetTags(ts []string) *Operation {
+	o.Tags = ts
+	return o
+}
+
+// AddTags adds Tags to the Tags of the Operation.
+func (o *Operation) AddTags(ts ...string) *Operation {
+	o.Tags = append(o.Tags, ts...)
+	return o
+}
+
+// SetSummary sets the Summary of the Operation.
+func (o *Operation) SetSummary(s string) *Operation {
+	o.Summary = s
+	return o
+}
+
+// SetDescription sets the Description of the Operation.
+func (o *Operation) SetDescription(d string) *Operation {
+	o.Description = d
+	return o
+}
+
+// SetOperationID sets the OperationID of the Operation.
+func (o *Operation) SetOperationID(id string) *Operation {
+	o.OperationID = id
+	return o
+}
+
+// SetParameters sets the Parameters of the Operation.
+func (o *Operation) SetParameters(ps []*Parameter) *Operation {
+	o.Parameters = ps
+	return o
+}
+
+// AddParameters adds Parameters to the Parameters of the Operation.
+func (o *Operation) AddParameters(ps ...*Parameter) *Operation {
+	o.Parameters = append(o.Parameters, ps...)
+	return o
+}
+
+// SetRequestBody sets the RequestBody of the Operation.
+func (o *Operation) SetRequestBody(r *RequestBody) *Operation {
+	o.RequestBody = r
+	return o
+}
+
+// SetResponses sets the Responses of the Operation.
+func (o *Operation) SetResponses(r Responses) *Operation {
+	o.Responses = r
+	return o
+}
+
+// AddResponse adds the given Response under the given Name to the Responses of the Operation.
+func (o *Operation) AddResponse(n string, p *Response) *Operation {
+	o.initResponses()
+	o.Responses[n] = p
+	return o
+}
+
+// AddNamedResponses adds the given namedResponses to the Responses of the Operation.
+func (o *Operation) AddNamedResponses(ps ...*NamedResponse) *Operation {
+	for _, p := range ps {
+		o.AddResponse(p.Name, p.Response)
+	}
+	return o
+}
+
+// initResponses ensures the Responses map is allocated.
+func (o *Operation) initResponses() {
+	if o.Responses == nil {
+		o.Responses = make(Responses)
+	}
+}
+
+// NewParameter returns a new Parameter.
+func NewParameter() *Parameter {
+	return new(Parameter)
+}
+
+// SetRef sets the Ref of the Parameter.
+func (p *Parameter) SetRef(r string) *Parameter {
+	p.Ref = r
+	return p
+}
+
+// SetName sets the Name of the Parameter.
+func (p *Parameter) SetName(n string) *Parameter {
+	p.Name = n
+	return p
+}
+
+// SetIn sets the In of the Parameter.
+func (p *Parameter) SetIn(i string) *Parameter {
+	p.In = i
+	return p
+}
+
+// InPath sets the In of the Parameter to "PathItem".
+func (p *Parameter) InPath() *Parameter {
+	return p.SetIn(string(oas.LocationPath))
+}
+
+// InQuery sets the In of the Parameter to "query".
+func (p *Parameter) InQuery() *Parameter {
+	return p.SetIn(string(oas.LocationQuery))
+}
+
+// InHeader sets the In of the Parameter to "header".
+func (p *Parameter) InHeader() *Parameter {
+	return p.SetIn(string(oas.LocationHeader))
+}
+
+// InCookie sets the In of the Parameter to "cookie".
+func (p *Parameter) InCookie() *Parameter {
+	return p.SetIn(string(oas.LocationCookie))
+}
+
+// SetDescription sets the Description of the Parameter.
+func (p *Parameter) SetDescription(d string) *Parameter {
+	p.Description = d
+	return p
+}
+
+// SetSchema sets the Schema of the Parameter.
+func (p *Parameter) SetSchema(s *Schema) *Parameter {
+	if s != nil {
+		p.Schema = *s
+	}
+	return p
+}
+
+// SetRequired sets the Required of the Parameter.
+func (p *Parameter) SetRequired(r bool) *Parameter {
+	p.Required = r
+	return p
+}
+
+// SetDeprecated sets the Deprecated of the Parameter.
+func (p *Parameter) SetDeprecated(d bool) *Parameter {
+	p.Deprecated = d
+	return p
+}
+
+// SetContent sets the Content of the Parameter.
+func (p *Parameter) SetContent(c map[string]Media) *Parameter {
+	p.Content = c
+	return p
+}
+
+// TODO(masseelch): Add Content helpers for Parameter
+
+// SetStyle sets the Style of the Parameter.
+func (p *Parameter) SetStyle(s string) *Parameter {
+	p.Style = s
+	return p
+}
+
+// SetExplode sets the Explode of the Parameter.
+func (p *Parameter) SetExplode(e bool) *Parameter {
+	p.Explode = &e
+	return p
+}
+
+// ToNamed returns a NamedParameter wrapping the receiver.
+func (p *Parameter) ToNamed(n string) *NamedParameter {
+	return NewNamedParameter(n, p)
+}
+
+// NamedParameter can be used to construct a reference to the wrapped Parameter.
+type NamedParameter struct {
+	Parameter *Parameter
+	Name      string
+}
+
+// NewNamedParameter returns a new NamedParameter.
+func NewNamedParameter(n string, p *Parameter) *NamedParameter {
+	return &NamedParameter{p, n}
+}
+
+// AsLocalRef returns a new Parameter referencing the wrapped Parameter in the local document.
+func (p *NamedParameter) AsLocalRef() *Parameter {
+	return NewParameter().SetRef("#/components/parameters/" + escapeRef(p.Name))
+}
+
+// NewResponse returns a new Response.
+func NewResponse() *Response {
+	return new(Response)
+}
+
+// SetRef sets the Ref of the Response.
+func (r *Response) SetRef(ref string) *Response {
+	r.Ref = ref
+	return r
+}
+
+// SetDescription sets the Description of the Response.
+func (r *Response) SetDescription(d string) *Response {
+	r.Description = d
+	return r
+}
+
+// SetHeader sets the Header of the Response.
+func (r *Response) SetHeader(h map[string]interface{}) *Response {
+	r.Header = h
+	return r
+}
+
+// SetContent sets the Content of the Response.
+func (r *Response) SetContent(c map[string]Media) *Response {
+	r.Content = c
+	return r
+}
+
+// AddContent adds the given Schema under the MediaType to the Content of the Response.
+func (r *Response) AddContent(mt string, s *Schema) *Response {
+	if s != nil {
+		r.initContent()
+		r.Content[mt] = Media{*s}
+	}
+	return r
+}
+
+// SetJSONContent sets the given Schema under the JSON MediaType to the Content of the Response.
+func (r *Response) SetJSONContent(s *Schema) *Response {
+	return r.AddContent(string(ir.ContentTypeJSON), s)
+}
+
+// initContent ensures the Content map is allocated.
+func (r *Response) initContent() {
+	if r.Content == nil {
+		r.Content = make(map[string]Media)
+	}
+}
+
+// SetLinks sets the Links of the Response.
+func (r *Response) SetLinks(l map[string]interface{}) *Response {
+	r.Links = l
+	return r
+}
+
+// ToNamed returns a NamedResponse wrapping the receiver.
+func (r *Response) ToNamed(n string) *NamedResponse {
+	return NewNamedResponse(n, r)
+}
+
+// NamedResponse can be used to construct a reference to the wrapped Response.
+type NamedResponse struct {
+	Response *Response
+	Name     string
+}
+
+// NewNamedResponse returns a new NamedResponse.
+func NewNamedResponse(n string, p *Response) *NamedResponse {
+	return &NamedResponse{p, n}
+}
+
+// AsLocalRef returns a new Response referencing the wrapped Response in the local document.
+func (p *NamedResponse) AsLocalRef() *Response {
+	return NewResponse().SetRef("#/components/responses/" + escapeRef(p.Name))
+}
+
+// TODO(masseelch): Discriminator
+
+// NewSchema returns a new Schema.
+func NewSchema() *Schema {
+	return new(Schema)
+}
+
+// SetRef sets the Ref of the Schema.
+func (s *Schema) SetRef(r string) *Schema {
+	s.Ref = r
+	return s
+}
+
+// SetDescription sets the Description of the Schema.
+func (s *Schema) SetDescription(d string) *Schema {
+	s.Description = d
+	return s
+}
+
+// SetType sets the Type of the Schema.
+func (s *Schema) SetType(t string) *Schema {
+	s.Type = t
+	return s
+}
+
+// SetFormat sets the Format of the Schema.
+func (s *Schema) SetFormat(f string) *Schema {
+	s.Format = f
+	return s
+}
+
+// SetProperties sets the Properties of the Schema.
+func (s *Schema) SetProperties(p *Properties) *Schema {
+	if p != nil {
+		s.Properties = *p
+	}
+	return s
+}
+
+// AddOptionalProperties adds the Properties to the Properties of the Schema.
+func (s *Schema) AddOptionalProperties(ps ...*Property) *Schema {
+	for _, p := range ps {
+		if p != nil {
+			s.Properties = append(s.Properties, *p)
+		}
+	}
+	return s
+}
+
+// AddRequiredProperties adds the Properties to the Properties of the Schema and marks them as required.
+func (s *Schema) AddRequiredProperties(ps ...*Property) *Schema {
+	s.AddOptionalProperties(ps...)
+	for _, p := range ps {
+		if p != nil {
+			s.Required = append(s.Required, p.Name)
+		}
+	}
+	return s
+}
+
+// SetRequired sets the Required of the Schema.
+func (s *Schema) SetRequired(r []string) *Schema {
+	s.Required = r
+	return s
+}
+
+// SetItems sets the Items of the Schema.
+func (s *Schema) SetItems(i *Schema) *Schema {
+	s.Items = i
+	return s
+}
+
+// SetNullable sets the Nullable of the Schema.
+func (s *Schema) SetNullable(n bool) *Schema {
+	s.Nullable = n
+	return s
+}
+
+// SetAllOf sets the AllOf of the Schema.
+func (s *Schema) SetAllOf(a []*Schema) *Schema {
+	s.AllOf = a
+	return s
+}
+
+// SetOneOf sets the OneOf of the Schema.
+func (s *Schema) SetOneOf(o []*Schema) *Schema {
+	s.OneOf = o
+	return s
+}
+
+// SetAnyOf sets the AnyOf of the Schema.
+func (s *Schema) SetAnyOf(a []*Schema) *Schema {
+	s.AnyOf = a
+	return s
+}
+
+// SetDiscriminator sets the Discriminator of the Schema.
+func (s *Schema) SetDiscriminator(d *Discriminator) *Schema {
+	s.Discriminator = d
+	return s
+}
+
+// SetEnum sets the Enum of the Schema.
+func (s *Schema) SetEnum(e []json.RawMessage) *Schema {
+	s.Enum = e
+	return s
+}
+
+// SetMultipleOf sets the MultipleOf of the Schema.
+func (s *Schema) SetMultipleOf(m *int) *Schema {
+	s.MultipleOf = m
+	return s
+}
+
+// SetMaximum sets the Maximum of the Schema.
+func (s *Schema) SetMaximum(m *int64) *Schema {
+	s.Maximum = m
+	return s
+}
+
+// SetExclusiveMaximum sets the ExclusiveMaximum of the Schema.
+func (s *Schema) SetExclusiveMaximum(e bool) *Schema {
+	s.ExclusiveMaximum = e
+	return s
+}
+
+// SetMinimum sets the Minimum of the Schema.
+func (s *Schema) SetMinimum(m *int64) *Schema {
+	s.Minimum = m
+	return s
+}
+
+// SetExclusiveMinimum sets the ExclusiveMinimum of the Schema.
+func (s *Schema) SetExclusiveMinimum(e bool) *Schema {
+	s.ExclusiveMinimum = e
+	return s
+}
+
+// SetMaxLength sets the MaxLength of the Schema.
+func (s *Schema) SetMaxLength(m *uint64) *Schema {
+	s.MaxLength = m
+	return s
+}
+
+// SetMinLength sets the MinLength of the Schema.
+func (s *Schema) SetMinLength(m *int64) *Schema {
+	s.MinLength = m
+	return s
+}
+
+// SetPattern sets the Pattern of the Schema.
+func (s *Schema) SetPattern(p string) *Schema {
+	s.Pattern = p
+	return s
+}
+
+// SetMaxItems sets the MaxItems of the Schema.
+func (s *Schema) SetMaxItems(m *uint64) *Schema {
+	s.MaxItems = m
+	return s
+}
+
+// SetMinItems sets the MinItems of the Schema.
+func (s *Schema) SetMinItems(m *uint64) *Schema {
+	s.MinItems = m
+	return s
+}
+
+// SetUniqueItems sets the UniqueItems of the Schema.
+func (s *Schema) SetUniqueItems(u bool) *Schema {
+	s.UniqueItems = u
+	return s
+}
+
+// SetMaxProperties sets the MaxProperties of the Schema.
+func (s *Schema) SetMaxProperties(m *uint64) *Schema {
+	s.MaxProperties = m
+	return s
+}
+
+// SetMinProperties sets the MinProperties of the Schema.
+func (s *Schema) SetMinProperties(m *uint64) *Schema {
+	s.MinProperties = m
+	return s
+}
+
+// SetDefault sets the Default of the Schema.
+func (s *Schema) SetDefault(d json.RawMessage) *Schema {
+	s.Default = d
+	return s
+}
+
+// ToNamed returns a NamedSchema wrapping the receiver.
+func (s *Schema) ToNamed(n string) *NamedSchema {
+	return NewNamedSchema(n, s)
+}
+
+// Int32 returns an 32-bit integer OAS data type (Schema).
+func Int32() *Schema { return schema("integer", "int32") }
+
+// Int64 returns an 64-bit integer OAS data type (Schema).
+func Int64() *Schema { return schema("integer", "int64") }
+
+// Float returns a float OAS data type (Schema).
+func Float() *Schema { return schema("number", "float") }
+
+// Double returns a double OAS data type (Schema).
+func Double() *Schema { return schema("number", "double") }
+
+// String returns a string OAS data type (Schema).
+func String() *Schema { return schema("string", "") }
+
+// Bytes returns a base64 encoded OAS data type (Schema).
+func Bytes() *Schema { return schema("string", "byte") }
+
+// Binary returns a sequence of octets OAS data type (Schema).
+func Binary() *Schema { return schema("string", "binary") }
+
+// Bool returns a boolean OAS data type (Schema).
+func Bool() *Schema { return schema("boolean", "") }
+
+// Date returns a date as defined by full-date - RFC3339 OAS data type (Schema).
+func Date() *Schema { return schema("string", "date") }
+
+// DateTime returns a date as defined by date-time - RFC3339 OAS data type (Schema).
+func DateTime() *Schema { return schema("string", "date-time") }
+
+// Password returns an obscured OAS data type (Schema).
+func Password() *Schema { return schema("string", "password") }
+
+// schema returns a Schema for a primitive type.
+func schema(t, f string) *Schema {
+	return NewSchema().SetType(t).SetFormat(f)
+}
+
+// AsArray returns a new "array" Schema wrapping the receiver.
+func (s *Schema) AsArray() *Schema {
+	return &Schema{
+		Type:  string(oas.Array),
+		Items: s,
+	}
+}
+
+// AsEnum returns a new "enum" Schema wrapping the receiver.
+func (s *Schema) AsEnum(def json.RawMessage, values ...json.RawMessage) *Schema {
+	return &Schema{
+		Type:    s.Type,
+		Default: def,
+		Enum:    values,
+	}
+}
+
+// ToProperty returns a Property with the given name and with this Schema.
+func (s *Schema) ToProperty(n string) *Property {
+	return NewProperty().SetName(n).SetSchema(s)
+}
+
+// NamedSchema can be used to construct a reference to the wrapped Schema.
+type NamedSchema struct {
+	Schema *Schema
+	Name   string
+}
+
+// NewNamedSchema returns a new NamedSchema.
+func NewNamedSchema(n string, p *Schema) *NamedSchema {
+	return &NamedSchema{p, n}
+}
+
+// AsLocalRef returns a new Schema referencing the wrapped Schema in the local document.
+func (p *NamedSchema) AsLocalRef() *Schema {
+	return NewSchema().SetRef("#/components/schemas/" + escapeRef(p.Name))
+}
+
+// NewProperty returns a new Property.
+func NewProperty() *Property {
+	return new(Property)
+}
+
+// SetName sets the Name of the Property.
+func (p *Property) SetName(n string) *Property {
+	p.Name = n
+	return p
+}
+
+// SetSchema sets the Schema of the Property.
+func (p *Property) SetSchema(s *Schema) *Property {
+	p.Schema = s
+	return p
+}
+
+func escapeRef(ref string) string {
+	return strings.NewReplacer("~", "~0", "/", "~1").Replace(ref)
+}


Test patch:
diff --git a/dsl_test.go b/dsl_test.go
new file mode 100644
index 00000000..e70da0b8
--- /dev/null
+++ b/dsl_test.go
@@ -0,0 +1,399 @@
+package ogen_test
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/ogen-go/ogen"
+	"github.com/ogen-go/ogen/internal/ir"
+	"github.com/stretchr/testify/assert"
+)
+
+const (
+	pathWithID    = "/path/with/{id}"
+	refPathWithID = "/ref/path/with/id"
+	pathWithBody  = "/path/with/body"
+)
+
+var (
+	// reusable query param
+	_queryParam = ogen.NewParameter().
+			InQuery().
+			SetName("auth").
+			SetDescription("Optional bearer token").
+			ToNamed("authInQuery")
+	// reusable header param
+	_headerParam = ogen.NewNamedParameter(
+		"authInHeader",
+		ogen.NewParameter().
+			SetIn("header").
+			SetName("Authorization").
+			SetDescription("Optional bearer token"),
+	)
+	// reusable cookie param
+	_cookieParam = ogen.NewParameter().
+			InCookie().
+			SetName("csrf").
+			SetDescription("CSRF token").
+			ToNamed("csrf")
+	// reusable pet schema
+	_petSchema = ogen.NewNamedSchema(
+		"Pet",
+		ogen.NewSchema().
+			SetDescription("A Pet").
+			AddRequiredProperties(
+				ogen.Int32().ToProperty("required_Int32"),
+				ogen.Int64().ToProperty("required_Int64"),
+				ogen.Float().ToProperty("required_Float"),
+				ogen.Double().ToProperty("required_Double"),
+				ogen.String().ToProperty("required_String"),
+				ogen.Bytes().ToProperty("required_Bytes"),
+				ogen.Binary().ToProperty("required_Binary"),
+				ogen.Bool().ToProperty("required_Bool"),
+				ogen.Date().ToProperty("required_Date"),
+				ogen.DateTime().ToProperty("required_DateTime"),
+				ogen.Password().ToProperty("required_Password"),
+				ogen.Int32().AsArray().ToProperty("required_array_Int32"),
+				ogen.Int32().AsEnum(json.RawMessage("0"), json.RawMessage("0"), json.RawMessage("1")).
+					ToProperty("required_enum_Int32"),
+				ogen.Int32().AsEnum(json.RawMessage(`"off"`), json.RawMessage(`"0"`), json.RawMessage(`"1"`)).
+					ToProperty("required_enum_String"),
+			).
+			AddOptionalProperties(
+				ogen.Int32().ToProperty("optional_Int32"),
+				ogen.Int64().ToProperty("optional_Int64"),
+				ogen.Float().ToProperty("optional_Float"),
+				ogen.Double().ToProperty("optional_Double"),
+				ogen.String().ToProperty("optional_String"),
+				ogen.Bytes().ToProperty("optional_Bytes"),
+				ogen.Binary().ToProperty("optional_Binary"),
+				ogen.Bool().ToProperty("optional_Bool"),
+				ogen.Date().ToProperty("optional_Date"),
+				ogen.DateTime().ToProperty("optional_DateTime"),
+				ogen.Password().ToProperty("optional_Password"),
+			),
+	)
+	// reusable toy schema
+	_toySchema = ogen.NewSchema().
+			SetDescription("A toy of a Pet").
+			ToNamed("User")
+)
+
+func TestBuilder(t *testing.T) {
+	// expected result
+	ex := &ogen.Spec{
+		OpenAPI: "3.1.0",
+		Info: ogen.Info{
+			Title:          "title",
+			Description:    "description",
+			TermsOfService: "terms of service",
+			Contact: &ogen.Contact{
+				Name:  "Name",
+				URL:   "url",
+				Email: "email",
+			},
+			License: &ogen.License{
+				Name: "Name",
+				URL:  "url",
+			},
+			Version: "0.1.0",
+		},
+		Servers: []ogen.Server{
+			{"staging", "staging.api.com"},
+			{"production", "api.com"},
+		},
+		Paths: map[string]*ogen.PathItem{
+			pathWithID: {
+				Description: "This is my first path",
+				Parameters: []*ogen.Parameter{
+					{Ref: "#/components/parameters/authInQuery"},
+					{Ref: "#/components/parameters/authInHeader"},
+					{Ref: "#/components/parameters/csrf"},
+				},
+				Get: &ogen.Operation{
+					Tags:        []string{"default"},
+					Description: "Description for my path",
+					OperationID: "path-with-id",
+					Parameters: []*ogen.Parameter{
+						{
+							Name:        "id",
+							In:          "path",
+							Description: "ID Parameter in path",
+							Required:    true,
+							Schema:      ogen.Schema{Type: "integer", Format: "int32"},
+						},
+					},
+					Responses: ogen.Responses{
+						"error": {Ref: "#/components/responses/error"},
+						"ok": {
+							Description: "Success",
+							Content: map[string]ogen.Media{
+								string(ir.ContentTypeJSON): {Schema: ogen.Schema{
+									Description: "Success",
+									Properties: []ogen.Property{
+										{Name: "prop1", Schema: &ogen.Schema{Type: "integer", Format: "int32"}},
+										{Name: "prop2", Schema: &ogen.Schema{Type: "string"}},
+									},
+								}},
+							},
+						},
+					},
+				},
+			},
+			refPathWithID: {
+				Ref: "#/paths/~1path~1with~1{id}",
+			},
+			pathWithBody: {
+				Post: &ogen.Operation{
+					Tags:        []string{"post"},
+					Description: "Description for my path with body",
+					OperationID: "path-with-body",
+					Parameters: []*ogen.Parameter{
+						{Ref: "#/components/parameters/authInQuery"},
+						{Ref: "#/components/parameters/authInHeader"},
+						{Ref: "#/components/parameters/csrf"},
+					},
+					Responses:   ogen.Responses{"error": {Ref: "#/components/responses/error"}},
+					RequestBody: &ogen.RequestBody{Ref: "#/components/requestBodies/~1path~1with~1body"},
+				},
+			},
+		},
+		Components: &ogen.Components{
+			Schemas: map[string]*ogen.Schema{
+				_petSchema.Name: _petSchema.Schema,
+				_toySchema.Name: _toySchema.Schema,
+			},
+			Responses: ogen.Responses{
+				"error": {
+					Description: "An Error Response",
+					Content: map[string]ogen.Media{
+						string(ir.ContentTypeJSON): {Schema: ogen.Schema{
+							Description: "Error Response Schema",
+							Properties: []ogen.Property{
+								{Name: "code", Schema: &ogen.Schema{Type: "integer", Format: "int32"}},
+								{Name: "status", Schema: &ogen.Schema{Type: "string"}},
+							},
+						}},
+					},
+				},
+			},
+			Parameters: map[string]*ogen.Parameter{
+				"authInQuery": {
+					Name:        "auth",
+					In:          "query",
+					Description: "Optional bearer token",
+				},
+				"authInHeader": {
+					Name:        "Authorization",
+					In:          "header",
+					Description: "Optional bearer token",
+				},
+				"csrf": {
+					Name:        "csrf",
+					In:          "cookie",
+					Description: "CSRF token",
+				},
+			},
+			RequestBodies: map[string]*ogen.RequestBody{
+				pathWithBody: {
+					Description: "Referenced RequestBody",
+					Content: map[string]ogen.Media{
+						string(ir.ContentTypeJSON): {
+							Schema: ogen.Schema{Ref: "#/components/schemas/" + _toySchema.Name},
+						},
+					},
+					Required: true,
+				},
+			},
+		},
+	}
+	// referenced path
+	path := ogen.NewPathItem().
+		SetDescription(ex.Paths[pathWithID].Description).
+		AddParameters(_queryParam.AsLocalRef(), _headerParam.AsLocalRef(), _cookieParam.AsLocalRef()).
+		SetGet(ogen.NewOperation().
+			AddTags(ex.Paths[pathWithID].Get.Tags...).
+			SetDescription(ex.Paths[pathWithID].Get.Description).
+			SetOperationID(ex.Paths[pathWithID].Get.OperationID).
+			AddParameters(
+				ogen.NewParameter().
+					InPath().
+					SetName(ex.Paths[pathWithID].Get.Parameters[0].Name).
+					SetDescription(ex.Paths[pathWithID].Get.Parameters[0].Description).
+					SetRequired(true).
+					SetSchema(ogen.Int32()),
+			).
+			AddNamedResponses(
+				ex.RefResponse("error"),
+				ogen.NewResponse().
+					SetDescription(ex.Paths[pathWithID].Get.Responses["ok"].Description).
+					SetJSONContent(ogen.NewSchema().
+						SetDescription(ex.Paths[pathWithID].Get.Responses["ok"].Content[string(ir.ContentTypeJSON)].Schema.Description).
+						AddOptionalProperties(
+							ogen.Int32().ToProperty("prop1"),
+							ogen.String().ToProperty("prop2"),
+						),
+					).
+					ToNamed("ok"),
+			),
+		).
+		ToNamed(pathWithID)
+	// actual result
+	ac := ogen.NewSpec().
+		SetOpenAPI(ex.OpenAPI).
+		SetInfo(ogen.NewInfo().
+			SetTitle(ex.Info.Title).
+			SetDescription(ex.Info.Description).
+			SetTermsOfService(ex.Info.TermsOfService).
+			SetContact(ogen.NewContact().
+				SetName(ex.Info.Contact.Name).
+				SetURL(ex.Info.Contact.URL).
+				SetEmail(ex.Info.Contact.Email),
+			).
+			SetLicense(ogen.NewLicense().
+				SetName(ex.Info.License.Name).
+				SetURL(ex.Info.License.URL),
+			).
+			SetVersion(ex.Info.Version),
+		).
+		AddServers(
+			&ex.Servers[0],
+			ogen.NewServer().
+				SetDescription(ex.Servers[1].Description).
+				SetURL(ex.Servers[1].URL),
+		).
+		AddNamedPathItems(
+			path,
+			ogen.NewPathItem().
+				SetDescription(ex.Paths[pathWithBody].Description).
+				SetPost(ogen.NewOperation().
+					AddTags(ex.Paths[pathWithBody].Post.Tags...).
+					SetDescription(ex.Paths[pathWithBody].Post.Description).
+					SetOperationID(ex.Paths[pathWithBody].Post.OperationID).
+					AddParameters(_queryParam.AsLocalRef(), _headerParam.AsLocalRef(), _cookieParam.AsLocalRef()).
+					AddNamedResponses(ex.RefResponse("error")).
+					SetRequestBody(ex.RefRequestBody(pathWithBody).RequestBody),
+				).
+				ToNamed(pathWithBody),
+		).
+		AddPathItem(refPathWithID, path.AsLocalRef()).
+		AddNamedParameters(_queryParam, _headerParam, _cookieParam).
+		AddNamedSchemas(_petSchema, _toySchema).
+		AddNamedResponses(
+			ogen.NewResponse().
+				SetDescription(ex.Components.Responses["error"].Description).
+				SetJSONContent(ogen.NewSchema().
+					SetDescription(ex.Components.Responses["error"].Content[string(ir.ContentTypeJSON)].Schema.Description).
+					AddOptionalProperties(
+						ogen.Int32().ToProperty("code"),
+						ogen.String().ToProperty("status"),
+					),
+				).
+				ToNamed("error"),
+		).
+		AddNamedRequestBodies(
+			ogen.NewRequestBody().
+				SetDescription(ex.Components.RequestBodies[pathWithBody].Description).
+				SetJSONContent(ex.RefSchema(_toySchema.Name).Schema).
+				SetRequired(true).
+				ToNamed(pathWithBody),
+		)
+	assert.Equal(t, ex, ac)
+
+	ex.SetServers(nil)
+	assert.Nil(t, ex.Servers)
+
+	ex.SetPaths(nil)
+	assert.Nil(t, ex.Paths)
+
+	ex.SetComponents(nil)
+	assert.Nil(t, ex.Components)
+	assert.Nil(t, ex.RefSchema(""))
+	assert.Nil(t, ex.RefResponse(""))
+	assert.Nil(t, ex.RefRequestBody(""))
+
+	req := ogen.NewRequestBody().SetContent(map[string]ogen.Media{"key": {}})
+	assert.Equal(t, req, &ogen.RequestBody{Content: map[string]ogen.Media{"key": {}}})
+
+	pi := ogen.NewPathItem().
+		SetPut(ogen.NewOperation().SetOperationID("put").SetTags([]string{"tag1", "tag2"})).
+		SetDelete(ogen.NewOperation().SetOperationID("delete").SetSummary("summary")).
+		SetOptions(ogen.NewOperation().SetOperationID("options").SetParameters([]*ogen.Parameter{_cookieParam.Parameter})).
+		SetHead(ogen.NewOperation().SetOperationID("head").SetResponses(ogen.Responses{"resp": ogen.NewResponse()})).
+		SetPatch(ogen.NewOperation().SetOperationID("patch").AddParameters(ogen.NewParameter().InHeader().SetDeprecated(true))).
+		SetTrace(ogen.NewOperation().SetOperationID("trace")).
+		SetServers([]ogen.Server{{"desc1", "url1"}}).
+		AddServers(ogen.NewServer().SetDescription("desc2").SetURL("url2")).
+		SetParameters([]*ogen.Parameter{_queryParam.Parameter})
+	assert.Equal(t, &ogen.PathItem{
+		Put:        &ogen.Operation{OperationID: "put", Tags: []string{"tag1", "tag2"}},
+		Delete:     &ogen.Operation{OperationID: "delete", Summary: "summary"},
+		Options:    &ogen.Operation{OperationID: "options", Parameters: []*ogen.Parameter{_cookieParam.Parameter}},
+		Head:       &ogen.Operation{OperationID: "head", Responses: ogen.Responses{"resp": &ogen.Response{}}},
+		Patch:      &ogen.Operation{OperationID: "patch", Parameters: []*ogen.Parameter{{In: "header", Deprecated: true}}},
+		Trace:      &ogen.Operation{OperationID: "trace"},
+		Servers:    []ogen.Server{{"desc1", "url1"}, {"desc2", "url2"}},
+		Parameters: []*ogen.Parameter{_queryParam.Parameter},
+	}, pi)
+
+	mlt := 1
+	max := int64(2)
+	umax := uint64(max)
+	assert.Equal(t, &ogen.Schema{
+		Ref:              "ref",
+		Description:      "desc",
+		Type:             "string",
+		Format:           "",
+		Properties:       []ogen.Property{{Name: "prop"}},
+		Required:         []string{"prop"},
+		Items:            ogen.String(),
+		Nullable:         true,
+		AllOf:            []*ogen.Schema{ogen.NewSchema()},
+		OneOf:            []*ogen.Schema{ogen.NewSchema()},
+		AnyOf:            []*ogen.Schema{ogen.NewSchema()},
+		Discriminator:    &ogen.Discriminator{PropertyName: "prop"},
+		Enum:             []json.RawMessage{json.RawMessage("0"), json.RawMessage("1")},
+		MultipleOf:       &mlt,
+		Maximum:          &max,
+		ExclusiveMaximum: true,
+		Minimum:          &max,
+		ExclusiveMinimum: true,
+		MaxLength:        &umax,
+		MinLength:        &max,
+		Pattern:          "",
+		MaxItems:         &umax,
+		MinItems:         &umax,
+		UniqueItems:      true,
+		MaxProperties:    &umax,
+		MinProperties:    &umax,
+		Default:          json.RawMessage("0"),
+	}, ogen.NewSchema().
+		SetRef("ref").
+		SetDescription("desc").
+		SetType("string").
+		SetFormat("").
+		SetProperties(&ogen.Properties{{Name: "prop"}}).
+		SetRequired([]string{"prop"}).
+		SetItems(ogen.String()).
+		SetNullable(true).
+		SetAllOf([]*ogen.Schema{ogen.NewSchema()}).
+		SetOneOf([]*ogen.Schema{ogen.NewSchema()}).
+		SetAnyOf([]*ogen.Schema{ogen.NewSchema()}).
+		SetDiscriminator(&ogen.Discriminator{PropertyName: "prop"}).
+		SetEnum([]json.RawMessage{json.RawMessage("0"), json.RawMessage("1")}).
+		SetMultipleOf(&mlt).
+		SetMaximum(&max).
+		SetExclusiveMaximum(true).
+		SetMinimum(&max).
+		SetExclusiveMinimum(true).
+		SetMaxLength(&umax).
+		SetMinLength(&max).
+		SetPattern("").
+		SetMaxItems(&umax).
+		SetMinItems(&umax).
+		SetUniqueItems(true).
+		SetMaxProperties(&umax).
+		SetMinProperties(&umax).
+		SetDefault(json.RawMessage("0")),
+	)
+}