...

Source file src/github.com/go-openapi/spec/operation.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  	"sort"
    22  
    23  	"github.com/go-openapi/jsonpointer"
    24  	"github.com/go-openapi/swag"
    25  )
    26  
    27  func init() {
    28  	gob.Register(map[string]interface{}{})
    29  	gob.Register([]interface{}{})
    30  }
    31  
    32  // OperationProps describes an operation
    33  //
    34  // NOTES:
    35  // - schemes, when present must be from [http, https, ws, wss]: see validate
    36  // - Security is handled as a special case: see MarshalJSON function
    37  type OperationProps struct {
    38  	Description  string                 `json:"description,omitempty"`
    39  	Consumes     []string               `json:"consumes,omitempty"`
    40  	Produces     []string               `json:"produces,omitempty"`
    41  	Schemes      []string               `json:"schemes,omitempty"`
    42  	Tags         []string               `json:"tags,omitempty"`
    43  	Summary      string                 `json:"summary,omitempty"`
    44  	ExternalDocs *ExternalDocumentation `json:"externalDocs,omitempty"`
    45  	ID           string                 `json:"operationId,omitempty"`
    46  	Deprecated   bool                   `json:"deprecated,omitempty"`
    47  	Security     []map[string][]string  `json:"security,omitempty"`
    48  	Parameters   []Parameter            `json:"parameters,omitempty"`
    49  	Responses    *Responses             `json:"responses,omitempty"`
    50  }
    51  
    52  // MarshalJSON takes care of serializing operation properties to JSON
    53  //
    54  // We use a custom marhaller here to handle a special cases related to
    55  // the Security field. We need to preserve zero length slice
    56  // while omitting the field when the value is nil/unset.
    57  func (op OperationProps) MarshalJSON() ([]byte, error) {
    58  	type Alias OperationProps
    59  	if op.Security == nil {
    60  		return json.Marshal(&struct {
    61  			Security []map[string][]string `json:"security,omitempty"`
    62  			*Alias
    63  		}{
    64  			Security: op.Security,
    65  			Alias:    (*Alias)(&op),
    66  		})
    67  	}
    68  	return json.Marshal(&struct {
    69  		Security []map[string][]string `json:"security"`
    70  		*Alias
    71  	}{
    72  		Security: op.Security,
    73  		Alias:    (*Alias)(&op),
    74  	})
    75  }
    76  
    77  // Operation describes a single API operation on a path.
    78  //
    79  // For more information: http://goo.gl/8us55a#operationObject
    80  type Operation struct {
    81  	VendorExtensible
    82  	OperationProps
    83  }
    84  
    85  // SuccessResponse gets a success response model
    86  func (o *Operation) SuccessResponse() (*Response, int, bool) {
    87  	if o.Responses == nil {
    88  		return nil, 0, false
    89  	}
    90  
    91  	responseCodes := make([]int, 0, len(o.Responses.StatusCodeResponses))
    92  	for k := range o.Responses.StatusCodeResponses {
    93  		if k >= 200 && k < 300 {
    94  			responseCodes = append(responseCodes, k)
    95  		}
    96  	}
    97  	if len(responseCodes) > 0 {
    98  		sort.Ints(responseCodes)
    99  		v := o.Responses.StatusCodeResponses[responseCodes[0]]
   100  		return &v, responseCodes[0], true
   101  	}
   102  
   103  	return o.Responses.Default, 0, false
   104  }
   105  
   106  // JSONLookup look up a value by the json property name
   107  func (o Operation) JSONLookup(token string) (interface{}, error) {
   108  	if ex, ok := o.Extensions[token]; ok {
   109  		return &ex, nil
   110  	}
   111  	r, _, err := jsonpointer.GetForToken(o.OperationProps, token)
   112  	return r, err
   113  }
   114  
   115  // UnmarshalJSON hydrates this items instance with the data from JSON
   116  func (o *Operation) UnmarshalJSON(data []byte) error {
   117  	if err := json.Unmarshal(data, &o.OperationProps); err != nil {
   118  		return err
   119  	}
   120  	return json.Unmarshal(data, &o.VendorExtensible)
   121  }
   122  
   123  // MarshalJSON converts this items object to JSON
   124  func (o Operation) MarshalJSON() ([]byte, error) {
   125  	b1, err := json.Marshal(o.OperationProps)
   126  	if err != nil {
   127  		return nil, err
   128  	}
   129  	b2, err := json.Marshal(o.VendorExtensible)
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  	concated := swag.ConcatJSON(b1, b2)
   134  	return concated, nil
   135  }
   136  
   137  // NewOperation creates a new operation instance.
   138  // It expects an ID as parameter but not passing an ID is also valid.
   139  func NewOperation(id string) *Operation {
   140  	op := new(Operation)
   141  	op.ID = id
   142  	return op
   143  }
   144  
   145  // WithID sets the ID property on this operation, allows for chaining.
   146  func (o *Operation) WithID(id string) *Operation {
   147  	o.ID = id
   148  	return o
   149  }
   150  
   151  // WithDescription sets the description on this operation, allows for chaining
   152  func (o *Operation) WithDescription(description string) *Operation {
   153  	o.Description = description
   154  	return o
   155  }
   156  
   157  // WithSummary sets the summary on this operation, allows for chaining
   158  func (o *Operation) WithSummary(summary string) *Operation {
   159  	o.Summary = summary
   160  	return o
   161  }
   162  
   163  // WithExternalDocs sets/removes the external docs for/from this operation.
   164  // When you pass empty strings as params the external documents will be removed.
   165  // When you pass non-empty string as one value then those values will be used on the external docs object.
   166  // So when you pass a non-empty description, you should also pass the url and vice versa.
   167  func (o *Operation) WithExternalDocs(description, url string) *Operation {
   168  	if description == "" && url == "" {
   169  		o.ExternalDocs = nil
   170  		return o
   171  	}
   172  
   173  	if o.ExternalDocs == nil {
   174  		o.ExternalDocs = &ExternalDocumentation{}
   175  	}
   176  	o.ExternalDocs.Description = description
   177  	o.ExternalDocs.URL = url
   178  	return o
   179  }
   180  
   181  // Deprecate marks the operation as deprecated
   182  func (o *Operation) Deprecate() *Operation {
   183  	o.Deprecated = true
   184  	return o
   185  }
   186  
   187  // Undeprecate marks the operation as not deprected
   188  func (o *Operation) Undeprecate() *Operation {
   189  	o.Deprecated = false
   190  	return o
   191  }
   192  
   193  // WithConsumes adds media types for incoming body values
   194  func (o *Operation) WithConsumes(mediaTypes ...string) *Operation {
   195  	o.Consumes = append(o.Consumes, mediaTypes...)
   196  	return o
   197  }
   198  
   199  // WithProduces adds media types for outgoing body values
   200  func (o *Operation) WithProduces(mediaTypes ...string) *Operation {
   201  	o.Produces = append(o.Produces, mediaTypes...)
   202  	return o
   203  }
   204  
   205  // WithTags adds tags for this operation
   206  func (o *Operation) WithTags(tags ...string) *Operation {
   207  	o.Tags = append(o.Tags, tags...)
   208  	return o
   209  }
   210  
   211  // AddParam adds a parameter to this operation, when a parameter for that location
   212  // and with that name already exists it will be replaced
   213  func (o *Operation) AddParam(param *Parameter) *Operation {
   214  	if param == nil {
   215  		return o
   216  	}
   217  
   218  	for i, p := range o.Parameters {
   219  		if p.Name == param.Name && p.In == param.In {
   220  			params := make([]Parameter, 0, len(o.Parameters)+1)
   221  			params = append(params, o.Parameters[:i]...)
   222  			params = append(params, *param)
   223  			params = append(params, o.Parameters[i+1:]...)
   224  			o.Parameters = params
   225  
   226  			return o
   227  		}
   228  	}
   229  
   230  	o.Parameters = append(o.Parameters, *param)
   231  	return o
   232  }
   233  
   234  // RemoveParam removes a parameter from the operation
   235  func (o *Operation) RemoveParam(name, in string) *Operation {
   236  	for i, p := range o.Parameters {
   237  		if p.Name == name && p.In == in {
   238  			o.Parameters = append(o.Parameters[:i], o.Parameters[i+1:]...)
   239  			return o
   240  		}
   241  	}
   242  	return o
   243  }
   244  
   245  // SecuredWith adds a security scope to this operation.
   246  func (o *Operation) SecuredWith(name string, scopes ...string) *Operation {
   247  	o.Security = append(o.Security, map[string][]string{name: scopes})
   248  	return o
   249  }
   250  
   251  // WithDefaultResponse adds a default response to the operation.
   252  // Passing a nil value will remove the response
   253  func (o *Operation) WithDefaultResponse(response *Response) *Operation {
   254  	return o.RespondsWith(0, response)
   255  }
   256  
   257  // RespondsWith adds a status code response to the operation.
   258  // When the code is 0 the value of the response will be used as default response value.
   259  // When the value of the response is nil it will be removed from the operation
   260  func (o *Operation) RespondsWith(code int, response *Response) *Operation {
   261  	if o.Responses == nil {
   262  		o.Responses = new(Responses)
   263  	}
   264  	if code == 0 {
   265  		o.Responses.Default = response
   266  		return o
   267  	}
   268  	if response == nil {
   269  		delete(o.Responses.StatusCodeResponses, code)
   270  		return o
   271  	}
   272  	if o.Responses.StatusCodeResponses == nil {
   273  		o.Responses.StatusCodeResponses = make(map[int]Response)
   274  	}
   275  	o.Responses.StatusCodeResponses[code] = *response
   276  	return o
   277  }
   278  
   279  type opsAlias OperationProps
   280  
   281  type gobAlias struct {
   282  	Security []map[string]struct {
   283  		List []string
   284  		Pad  bool
   285  	}
   286  	Alias           *opsAlias
   287  	SecurityIsEmpty bool
   288  }
   289  
   290  // GobEncode provides a safe gob encoder for Operation, including empty security requirements
   291  func (o Operation) GobEncode() ([]byte, error) {
   292  	raw := struct {
   293  		Ext   VendorExtensible
   294  		Props OperationProps
   295  	}{
   296  		Ext:   o.VendorExtensible,
   297  		Props: o.OperationProps,
   298  	}
   299  	var b bytes.Buffer
   300  	err := gob.NewEncoder(&b).Encode(raw)
   301  	return b.Bytes(), err
   302  }
   303  
   304  // GobDecode provides a safe gob decoder for Operation, including empty security requirements
   305  func (o *Operation) GobDecode(b []byte) error {
   306  	var raw struct {
   307  		Ext   VendorExtensible
   308  		Props OperationProps
   309  	}
   310  
   311  	buf := bytes.NewBuffer(b)
   312  	err := gob.NewDecoder(buf).Decode(&raw)
   313  	if err != nil {
   314  		return err
   315  	}
   316  	o.VendorExtensible = raw.Ext
   317  	o.OperationProps = raw.Props
   318  	return nil
   319  }
   320  
   321  // GobEncode provides a safe gob encoder for Operation, including empty security requirements
   322  func (op OperationProps) GobEncode() ([]byte, error) {
   323  	raw := gobAlias{
   324  		Alias: (*opsAlias)(&op),
   325  	}
   326  
   327  	var b bytes.Buffer
   328  	if op.Security == nil {
   329  		// nil security requirement
   330  		err := gob.NewEncoder(&b).Encode(raw)
   331  		return b.Bytes(), err
   332  	}
   333  
   334  	if len(op.Security) == 0 {
   335  		// empty, but non-nil security requirement
   336  		raw.SecurityIsEmpty = true
   337  		raw.Alias.Security = nil
   338  		err := gob.NewEncoder(&b).Encode(raw)
   339  		return b.Bytes(), err
   340  	}
   341  
   342  	raw.Security = make([]map[string]struct {
   343  		List []string
   344  		Pad  bool
   345  	}, 0, len(op.Security))
   346  	for _, req := range op.Security {
   347  		v := make(map[string]struct {
   348  			List []string
   349  			Pad  bool
   350  		}, len(req))
   351  		for k, val := range req {
   352  			v[k] = struct {
   353  				List []string
   354  				Pad  bool
   355  			}{
   356  				List: val,
   357  			}
   358  		}
   359  		raw.Security = append(raw.Security, v)
   360  	}
   361  
   362  	err := gob.NewEncoder(&b).Encode(raw)
   363  	return b.Bytes(), err
   364  }
   365  
   366  // GobDecode provides a safe gob decoder for Operation, including empty security requirements
   367  func (op *OperationProps) GobDecode(b []byte) error {
   368  	var raw gobAlias
   369  
   370  	buf := bytes.NewBuffer(b)
   371  	err := gob.NewDecoder(buf).Decode(&raw)
   372  	if err != nil {
   373  		return err
   374  	}
   375  	if raw.Alias == nil {
   376  		return nil
   377  	}
   378  
   379  	switch {
   380  	case raw.SecurityIsEmpty:
   381  		// empty, but non-nil security requirement
   382  		raw.Alias.Security = []map[string][]string{}
   383  	case len(raw.Alias.Security) == 0:
   384  		// nil security requirement
   385  		raw.Alias.Security = nil
   386  	default:
   387  		raw.Alias.Security = make([]map[string][]string, 0, len(raw.Security))
   388  		for _, req := range raw.Security {
   389  			v := make(map[string][]string, len(req))
   390  			for k, val := range req {
   391  				v[k] = make([]string, 0, len(val.List))
   392  				v[k] = append(v[k], val.List...)
   393  			}
   394  			raw.Alias.Security = append(raw.Alias.Security, v)
   395  		}
   396  	}
   397  
   398  	*op = *(*OperationProps)(raw.Alias)
   399  	return nil
   400  }
   401  

View as plain text