...

Source file src/github.com/go-openapi/spec/swagger.go

Documentation: github.com/go-openapi/spec

     1  // Copyright 2015 go-swagger maintainers
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package spec
    16  
    17  import (
    18  	"bytes"
    19  	"encoding/gob"
    20  	"encoding/json"
    21  	"fmt"
    22  	"strconv"
    23  
    24  	"github.com/go-openapi/jsonpointer"
    25  	"github.com/go-openapi/swag"
    26  )
    27  
    28  // Swagger this is the root document object for the API specification.
    29  // It combines what previously was the Resource Listing and API Declaration (version 1.2 and earlier)
    30  // together into one document.
    31  //
    32  // For more information: http://goo.gl/8us55a#swagger-object-
    33  type Swagger struct {
    34  	VendorExtensible
    35  	SwaggerProps
    36  }
    37  
    38  // JSONLookup look up a value by the json property name
    39  func (s Swagger) JSONLookup(token string) (interface{}, error) {
    40  	if ex, ok := s.Extensions[token]; ok {
    41  		return &ex, nil
    42  	}
    43  	r, _, err := jsonpointer.GetForToken(s.SwaggerProps, token)
    44  	return r, err
    45  }
    46  
    47  // MarshalJSON marshals this swagger structure to json
    48  func (s Swagger) MarshalJSON() ([]byte, error) {
    49  	b1, err := json.Marshal(s.SwaggerProps)
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  	b2, err := json.Marshal(s.VendorExtensible)
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  	return swag.ConcatJSON(b1, b2), nil
    58  }
    59  
    60  // UnmarshalJSON unmarshals a swagger spec from json
    61  func (s *Swagger) UnmarshalJSON(data []byte) error {
    62  	var sw Swagger
    63  	if err := json.Unmarshal(data, &sw.SwaggerProps); err != nil {
    64  		return err
    65  	}
    66  	if err := json.Unmarshal(data, &sw.VendorExtensible); err != nil {
    67  		return err
    68  	}
    69  	*s = sw
    70  	return nil
    71  }
    72  
    73  // GobEncode provides a safe gob encoder for Swagger, including extensions
    74  func (s Swagger) GobEncode() ([]byte, error) {
    75  	var b bytes.Buffer
    76  	raw := struct {
    77  		Props SwaggerProps
    78  		Ext   VendorExtensible
    79  	}{
    80  		Props: s.SwaggerProps,
    81  		Ext:   s.VendorExtensible,
    82  	}
    83  	err := gob.NewEncoder(&b).Encode(raw)
    84  	return b.Bytes(), err
    85  }
    86  
    87  // GobDecode provides a safe gob decoder for Swagger, including extensions
    88  func (s *Swagger) GobDecode(b []byte) error {
    89  	var raw struct {
    90  		Props SwaggerProps
    91  		Ext   VendorExtensible
    92  	}
    93  	buf := bytes.NewBuffer(b)
    94  	err := gob.NewDecoder(buf).Decode(&raw)
    95  	if err != nil {
    96  		return err
    97  	}
    98  	s.SwaggerProps = raw.Props
    99  	s.VendorExtensible = raw.Ext
   100  	return nil
   101  }
   102  
   103  // SwaggerProps captures the top-level properties of an Api specification
   104  //
   105  // NOTE: validation rules
   106  // - the scheme, when present must be from [http, https, ws, wss]
   107  // - BasePath must start with a leading "/"
   108  // - Paths is required
   109  type SwaggerProps struct {
   110  	ID                  string                 `json:"id,omitempty"`
   111  	Consumes            []string               `json:"consumes,omitempty"`
   112  	Produces            []string               `json:"produces,omitempty"`
   113  	Schemes             []string               `json:"schemes,omitempty"`
   114  	Swagger             string                 `json:"swagger,omitempty"`
   115  	Info                *Info                  `json:"info,omitempty"`
   116  	Host                string                 `json:"host,omitempty"`
   117  	BasePath            string                 `json:"basePath,omitempty"`
   118  	Paths               *Paths                 `json:"paths"`
   119  	Definitions         Definitions            `json:"definitions,omitempty"`
   120  	Parameters          map[string]Parameter   `json:"parameters,omitempty"`
   121  	Responses           map[string]Response    `json:"responses,omitempty"`
   122  	SecurityDefinitions SecurityDefinitions    `json:"securityDefinitions,omitempty"`
   123  	Security            []map[string][]string  `json:"security,omitempty"`
   124  	Tags                []Tag                  `json:"tags,omitempty"`
   125  	ExternalDocs        *ExternalDocumentation `json:"externalDocs,omitempty"`
   126  }
   127  
   128  type swaggerPropsAlias SwaggerProps
   129  
   130  type gobSwaggerPropsAlias struct {
   131  	Security []map[string]struct {
   132  		List []string
   133  		Pad  bool
   134  	}
   135  	Alias           *swaggerPropsAlias
   136  	SecurityIsEmpty bool
   137  }
   138  
   139  // GobEncode provides a safe gob encoder for SwaggerProps, including empty security requirements
   140  func (o SwaggerProps) GobEncode() ([]byte, error) {
   141  	raw := gobSwaggerPropsAlias{
   142  		Alias: (*swaggerPropsAlias)(&o),
   143  	}
   144  
   145  	var b bytes.Buffer
   146  	if o.Security == nil {
   147  		// nil security requirement
   148  		err := gob.NewEncoder(&b).Encode(raw)
   149  		return b.Bytes(), err
   150  	}
   151  
   152  	if len(o.Security) == 0 {
   153  		// empty, but non-nil security requirement
   154  		raw.SecurityIsEmpty = true
   155  		raw.Alias.Security = nil
   156  		err := gob.NewEncoder(&b).Encode(raw)
   157  		return b.Bytes(), err
   158  	}
   159  
   160  	raw.Security = make([]map[string]struct {
   161  		List []string
   162  		Pad  bool
   163  	}, 0, len(o.Security))
   164  	for _, req := range o.Security {
   165  		v := make(map[string]struct {
   166  			List []string
   167  			Pad  bool
   168  		}, len(req))
   169  		for k, val := range req {
   170  			v[k] = struct {
   171  				List []string
   172  				Pad  bool
   173  			}{
   174  				List: val,
   175  			}
   176  		}
   177  		raw.Security = append(raw.Security, v)
   178  	}
   179  
   180  	err := gob.NewEncoder(&b).Encode(raw)
   181  	return b.Bytes(), err
   182  }
   183  
   184  // GobDecode provides a safe gob decoder for SwaggerProps, including empty security requirements
   185  func (o *SwaggerProps) GobDecode(b []byte) error {
   186  	var raw gobSwaggerPropsAlias
   187  
   188  	buf := bytes.NewBuffer(b)
   189  	err := gob.NewDecoder(buf).Decode(&raw)
   190  	if err != nil {
   191  		return err
   192  	}
   193  	if raw.Alias == nil {
   194  		return nil
   195  	}
   196  
   197  	switch {
   198  	case raw.SecurityIsEmpty:
   199  		// empty, but non-nil security requirement
   200  		raw.Alias.Security = []map[string][]string{}
   201  	case len(raw.Alias.Security) == 0:
   202  		// nil security requirement
   203  		raw.Alias.Security = nil
   204  	default:
   205  		raw.Alias.Security = make([]map[string][]string, 0, len(raw.Security))
   206  		for _, req := range raw.Security {
   207  			v := make(map[string][]string, len(req))
   208  			for k, val := range req {
   209  				v[k] = make([]string, 0, len(val.List))
   210  				v[k] = append(v[k], val.List...)
   211  			}
   212  			raw.Alias.Security = append(raw.Alias.Security, v)
   213  		}
   214  	}
   215  
   216  	*o = *(*SwaggerProps)(raw.Alias)
   217  	return nil
   218  }
   219  
   220  // Dependencies represent a dependencies property
   221  type Dependencies map[string]SchemaOrStringArray
   222  
   223  // SchemaOrBool represents a schema or boolean value, is biased towards true for the boolean property
   224  type SchemaOrBool struct {
   225  	Allows bool
   226  	Schema *Schema
   227  }
   228  
   229  // JSONLookup implements an interface to customize json pointer lookup
   230  func (s SchemaOrBool) JSONLookup(token string) (interface{}, error) {
   231  	if token == "allows" {
   232  		return s.Allows, nil
   233  	}
   234  	r, _, err := jsonpointer.GetForToken(s.Schema, token)
   235  	return r, err
   236  }
   237  
   238  var jsTrue = []byte("true")
   239  var jsFalse = []byte("false")
   240  
   241  // MarshalJSON convert this object to JSON
   242  func (s SchemaOrBool) MarshalJSON() ([]byte, error) {
   243  	if s.Schema != nil {
   244  		return json.Marshal(s.Schema)
   245  	}
   246  
   247  	if s.Schema == nil && !s.Allows {
   248  		return jsFalse, nil
   249  	}
   250  	return jsTrue, nil
   251  }
   252  
   253  // UnmarshalJSON converts this bool or schema object from a JSON structure
   254  func (s *SchemaOrBool) UnmarshalJSON(data []byte) error {
   255  	var nw SchemaOrBool
   256  	if len(data) > 0 {
   257  		if data[0] == '{' {
   258  			var sch Schema
   259  			if err := json.Unmarshal(data, &sch); err != nil {
   260  				return err
   261  			}
   262  			nw.Schema = &sch
   263  		}
   264  		nw.Allows = !bytes.Equal(data, []byte("false"))
   265  	}
   266  	*s = nw
   267  	return nil
   268  }
   269  
   270  // SchemaOrStringArray represents a schema or a string array
   271  type SchemaOrStringArray struct {
   272  	Schema   *Schema
   273  	Property []string
   274  }
   275  
   276  // JSONLookup implements an interface to customize json pointer lookup
   277  func (s SchemaOrStringArray) JSONLookup(token string) (interface{}, error) {
   278  	r, _, err := jsonpointer.GetForToken(s.Schema, token)
   279  	return r, err
   280  }
   281  
   282  // MarshalJSON converts this schema object or array into JSON structure
   283  func (s SchemaOrStringArray) MarshalJSON() ([]byte, error) {
   284  	if len(s.Property) > 0 {
   285  		return json.Marshal(s.Property)
   286  	}
   287  	if s.Schema != nil {
   288  		return json.Marshal(s.Schema)
   289  	}
   290  	return []byte("null"), nil
   291  }
   292  
   293  // UnmarshalJSON converts this schema object or array from a JSON structure
   294  func (s *SchemaOrStringArray) UnmarshalJSON(data []byte) error {
   295  	var first byte
   296  	if len(data) > 1 {
   297  		first = data[0]
   298  	}
   299  	var nw SchemaOrStringArray
   300  	if first == '{' {
   301  		var sch Schema
   302  		if err := json.Unmarshal(data, &sch); err != nil {
   303  			return err
   304  		}
   305  		nw.Schema = &sch
   306  	}
   307  	if first == '[' {
   308  		if err := json.Unmarshal(data, &nw.Property); err != nil {
   309  			return err
   310  		}
   311  	}
   312  	*s = nw
   313  	return nil
   314  }
   315  
   316  // Definitions contains the models explicitly defined in this spec
   317  // An object to hold data types that can be consumed and produced by operations.
   318  // These data types can be primitives, arrays or models.
   319  //
   320  // For more information: http://goo.gl/8us55a#definitionsObject
   321  type Definitions map[string]Schema
   322  
   323  // SecurityDefinitions a declaration of the security schemes available to be used in the specification.
   324  // This does not enforce the security schemes on the operations and only serves to provide
   325  // the relevant details for each scheme.
   326  //
   327  // For more information: http://goo.gl/8us55a#securityDefinitionsObject
   328  type SecurityDefinitions map[string]*SecurityScheme
   329  
   330  // StringOrArray represents a value that can either be a string
   331  // or an array of strings. Mainly here for serialization purposes
   332  type StringOrArray []string
   333  
   334  // Contains returns true when the value is contained in the slice
   335  func (s StringOrArray) Contains(value string) bool {
   336  	for _, str := range s {
   337  		if str == value {
   338  			return true
   339  		}
   340  	}
   341  	return false
   342  }
   343  
   344  // JSONLookup implements an interface to customize json pointer lookup
   345  func (s SchemaOrArray) JSONLookup(token string) (interface{}, error) {
   346  	if _, err := strconv.Atoi(token); err == nil {
   347  		r, _, err := jsonpointer.GetForToken(s.Schemas, token)
   348  		return r, err
   349  	}
   350  	r, _, err := jsonpointer.GetForToken(s.Schema, token)
   351  	return r, err
   352  }
   353  
   354  // UnmarshalJSON unmarshals this string or array object from a JSON array or JSON string
   355  func (s *StringOrArray) UnmarshalJSON(data []byte) error {
   356  	var first byte
   357  	if len(data) > 1 {
   358  		first = data[0]
   359  	}
   360  
   361  	if first == '[' {
   362  		var parsed []string
   363  		if err := json.Unmarshal(data, &parsed); err != nil {
   364  			return err
   365  		}
   366  		*s = StringOrArray(parsed)
   367  		return nil
   368  	}
   369  
   370  	var single interface{}
   371  	if err := json.Unmarshal(data, &single); err != nil {
   372  		return err
   373  	}
   374  	if single == nil {
   375  		return nil
   376  	}
   377  	switch v := single.(type) {
   378  	case string:
   379  		*s = StringOrArray([]string{v})
   380  		return nil
   381  	default:
   382  		return fmt.Errorf("only string or array is allowed, not %T", single)
   383  	}
   384  }
   385  
   386  // MarshalJSON converts this string or array to a JSON array or JSON string
   387  func (s StringOrArray) MarshalJSON() ([]byte, error) {
   388  	if len(s) == 1 {
   389  		return json.Marshal([]string(s)[0])
   390  	}
   391  	return json.Marshal([]string(s))
   392  }
   393  
   394  // SchemaOrArray represents a value that can either be a Schema
   395  // or an array of Schema. Mainly here for serialization purposes
   396  type SchemaOrArray struct {
   397  	Schema  *Schema
   398  	Schemas []Schema
   399  }
   400  
   401  // Len returns the number of schemas in this property
   402  func (s SchemaOrArray) Len() int {
   403  	if s.Schema != nil {
   404  		return 1
   405  	}
   406  	return len(s.Schemas)
   407  }
   408  
   409  // ContainsType returns true when one of the schemas is of the specified type
   410  func (s *SchemaOrArray) ContainsType(name string) bool {
   411  	if s.Schema != nil {
   412  		return s.Schema.Type != nil && s.Schema.Type.Contains(name)
   413  	}
   414  	return false
   415  }
   416  
   417  // MarshalJSON converts this schema object or array into JSON structure
   418  func (s SchemaOrArray) MarshalJSON() ([]byte, error) {
   419  	if len(s.Schemas) > 0 {
   420  		return json.Marshal(s.Schemas)
   421  	}
   422  	return json.Marshal(s.Schema)
   423  }
   424  
   425  // UnmarshalJSON converts this schema object or array from a JSON structure
   426  func (s *SchemaOrArray) UnmarshalJSON(data []byte) error {
   427  	var nw SchemaOrArray
   428  	var first byte
   429  	if len(data) > 1 {
   430  		first = data[0]
   431  	}
   432  	if first == '{' {
   433  		var sch Schema
   434  		if err := json.Unmarshal(data, &sch); err != nil {
   435  			return err
   436  		}
   437  		nw.Schema = &sch
   438  	}
   439  	if first == '[' {
   440  		if err := json.Unmarshal(data, &nw.Schemas); err != nil {
   441  			return err
   442  		}
   443  	}
   444  	*s = nw
   445  	return nil
   446  }
   447  
   448  // vim:set ft=go noet sts=2 sw=2 ts=2:
   449  

View as plain text