github.com/djarvur/go-swagger@v0.18.0/scan/validators.go (about)

     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 scan
    16  
    17  import (
    18  	"encoding/json"
    19  	"fmt"
    20  	"regexp"
    21  	"strconv"
    22  	"strings"
    23  
    24  	"github.com/go-openapi/spec"
    25  )
    26  
    27  type validationBuilder interface {
    28  	SetMaximum(float64, bool)
    29  	SetMinimum(float64, bool)
    30  	SetMultipleOf(float64)
    31  
    32  	SetMinItems(int64)
    33  	SetMaxItems(int64)
    34  
    35  	SetMinLength(int64)
    36  	SetMaxLength(int64)
    37  	SetPattern(string)
    38  
    39  	SetUnique(bool)
    40  	SetEnum(string)
    41  	SetDefault(interface{})
    42  	SetExample(interface{})
    43  }
    44  
    45  type valueParser interface {
    46  	Parse([]string) error
    47  	Matches(string) bool
    48  }
    49  
    50  type setMaximum struct {
    51  	builder validationBuilder
    52  	rx      *regexp.Regexp
    53  }
    54  
    55  func (sm *setMaximum) Parse(lines []string) error {
    56  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
    57  		return nil
    58  	}
    59  	matches := sm.rx.FindStringSubmatch(lines[0])
    60  	if len(matches) > 2 && len(matches[2]) > 0 {
    61  		max, err := strconv.ParseFloat(matches[2], 64)
    62  		if err != nil {
    63  			return err
    64  		}
    65  		sm.builder.SetMaximum(max, matches[1] == "<")
    66  	}
    67  	return nil
    68  }
    69  
    70  func (sm *setMaximum) Matches(line string) bool {
    71  	return sm.rx.MatchString(line)
    72  }
    73  
    74  type setMinimum struct {
    75  	builder validationBuilder
    76  	rx      *regexp.Regexp
    77  }
    78  
    79  func (sm *setMinimum) Matches(line string) bool {
    80  	return sm.rx.MatchString(line)
    81  }
    82  
    83  func (sm *setMinimum) Parse(lines []string) error {
    84  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
    85  		return nil
    86  	}
    87  	matches := sm.rx.FindStringSubmatch(lines[0])
    88  	if len(matches) > 2 && len(matches[2]) > 0 {
    89  		min, err := strconv.ParseFloat(matches[2], 64)
    90  		if err != nil {
    91  			return err
    92  		}
    93  		sm.builder.SetMinimum(min, matches[1] == ">")
    94  	}
    95  	return nil
    96  }
    97  
    98  type setMultipleOf struct {
    99  	builder validationBuilder
   100  	rx      *regexp.Regexp
   101  }
   102  
   103  func (sm *setMultipleOf) Matches(line string) bool {
   104  	return sm.rx.MatchString(line)
   105  }
   106  
   107  func (sm *setMultipleOf) Parse(lines []string) error {
   108  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
   109  		return nil
   110  	}
   111  	matches := sm.rx.FindStringSubmatch(lines[0])
   112  	if len(matches) > 2 && len(matches[1]) > 0 {
   113  		multipleOf, err := strconv.ParseFloat(matches[1], 64)
   114  		if err != nil {
   115  			return err
   116  		}
   117  		sm.builder.SetMultipleOf(multipleOf)
   118  	}
   119  	return nil
   120  }
   121  
   122  type setMaxItems struct {
   123  	builder validationBuilder
   124  	rx      *regexp.Regexp
   125  }
   126  
   127  func (sm *setMaxItems) Matches(line string) bool {
   128  	return sm.rx.MatchString(line)
   129  }
   130  
   131  func (sm *setMaxItems) Parse(lines []string) error {
   132  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
   133  		return nil
   134  	}
   135  	matches := sm.rx.FindStringSubmatch(lines[0])
   136  	if len(matches) > 1 && len(matches[1]) > 0 {
   137  		maxItems, err := strconv.ParseInt(matches[1], 10, 64)
   138  		if err != nil {
   139  			return err
   140  		}
   141  		sm.builder.SetMaxItems(maxItems)
   142  	}
   143  	return nil
   144  }
   145  
   146  type setMinItems struct {
   147  	builder validationBuilder
   148  	rx      *regexp.Regexp
   149  }
   150  
   151  func (sm *setMinItems) Matches(line string) bool {
   152  	return sm.rx.MatchString(line)
   153  }
   154  
   155  func (sm *setMinItems) Parse(lines []string) error {
   156  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
   157  		return nil
   158  	}
   159  	matches := sm.rx.FindStringSubmatch(lines[0])
   160  	if len(matches) > 1 && len(matches[1]) > 0 {
   161  		minItems, err := strconv.ParseInt(matches[1], 10, 64)
   162  		if err != nil {
   163  			return err
   164  		}
   165  		sm.builder.SetMinItems(minItems)
   166  	}
   167  	return nil
   168  }
   169  
   170  type setMaxLength struct {
   171  	builder validationBuilder
   172  	rx      *regexp.Regexp
   173  }
   174  
   175  func (sm *setMaxLength) Parse(lines []string) error {
   176  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
   177  		return nil
   178  	}
   179  	matches := sm.rx.FindStringSubmatch(lines[0])
   180  	if len(matches) > 1 && len(matches[1]) > 0 {
   181  		maxLength, err := strconv.ParseInt(matches[1], 10, 64)
   182  		if err != nil {
   183  			return err
   184  		}
   185  		sm.builder.SetMaxLength(maxLength)
   186  	}
   187  	return nil
   188  }
   189  
   190  func (sm *setMaxLength) Matches(line string) bool {
   191  	return sm.rx.MatchString(line)
   192  }
   193  
   194  type setMinLength struct {
   195  	builder validationBuilder
   196  	rx      *regexp.Regexp
   197  }
   198  
   199  func (sm *setMinLength) Parse(lines []string) error {
   200  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
   201  		return nil
   202  	}
   203  	matches := sm.rx.FindStringSubmatch(lines[0])
   204  	if len(matches) > 1 && len(matches[1]) > 0 {
   205  		minLength, err := strconv.ParseInt(matches[1], 10, 64)
   206  		if err != nil {
   207  			return err
   208  		}
   209  		sm.builder.SetMinLength(minLength)
   210  	}
   211  	return nil
   212  }
   213  
   214  func (sm *setMinLength) Matches(line string) bool {
   215  	return sm.rx.MatchString(line)
   216  }
   217  
   218  type setPattern struct {
   219  	builder validationBuilder
   220  	rx      *regexp.Regexp
   221  }
   222  
   223  func (sm *setPattern) Parse(lines []string) error {
   224  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
   225  		return nil
   226  	}
   227  	matches := sm.rx.FindStringSubmatch(lines[0])
   228  	if len(matches) > 1 && len(matches[1]) > 0 {
   229  		sm.builder.SetPattern(matches[1])
   230  	}
   231  	return nil
   232  }
   233  
   234  func (sm *setPattern) Matches(line string) bool {
   235  	return sm.rx.MatchString(line)
   236  }
   237  
   238  type setCollectionFormat struct {
   239  	builder operationValidationBuilder
   240  	rx      *regexp.Regexp
   241  }
   242  
   243  func (sm *setCollectionFormat) Parse(lines []string) error {
   244  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
   245  		return nil
   246  	}
   247  	matches := sm.rx.FindStringSubmatch(lines[0])
   248  	if len(matches) > 1 && len(matches[1]) > 0 {
   249  		sm.builder.SetCollectionFormat(matches[1])
   250  	}
   251  	return nil
   252  }
   253  
   254  func (sm *setCollectionFormat) Matches(line string) bool {
   255  	return sm.rx.MatchString(line)
   256  }
   257  
   258  type setUnique struct {
   259  	builder validationBuilder
   260  	rx      *regexp.Regexp
   261  }
   262  
   263  func (su *setUnique) Matches(line string) bool {
   264  	return su.rx.MatchString(line)
   265  }
   266  
   267  func (su *setUnique) Parse(lines []string) error {
   268  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
   269  		return nil
   270  	}
   271  	matches := su.rx.FindStringSubmatch(lines[0])
   272  	if len(matches) > 1 && len(matches[1]) > 0 {
   273  		req, err := strconv.ParseBool(matches[1])
   274  		if err != nil {
   275  			return err
   276  		}
   277  		su.builder.SetUnique(req)
   278  	}
   279  	return nil
   280  }
   281  
   282  type setEnum struct {
   283  	builder validationBuilder
   284  	rx      *regexp.Regexp
   285  }
   286  
   287  func (se *setEnum) Matches(line string) bool {
   288  	return se.rx.MatchString(line)
   289  }
   290  
   291  func (se *setEnum) Parse(lines []string) error {
   292  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
   293  		return nil
   294  	}
   295  	matches := se.rx.FindStringSubmatch(lines[0])
   296  	if len(matches) > 1 && len(matches[1]) > 0 {
   297  		se.builder.SetEnum(matches[1])
   298  	}
   299  	return nil
   300  }
   301  
   302  func parseValueFromSchema(s string, schema *spec.SimpleSchema) (interface{}, error) {
   303  	if schema != nil {
   304  		switch strings.Trim(schema.TypeName(), "\"") {
   305  		case "integer", "int", "int64", "int32", "int16":
   306  			return strconv.Atoi(s)
   307  		case "bool", "boolean":
   308  			return strconv.ParseBool(s)
   309  		case "number", "float64", "float32":
   310  			return strconv.ParseFloat(s, 64)
   311  		case "object":
   312  			var obj map[string]interface{}
   313  			if err := json.Unmarshal([]byte(s), &obj); err != nil {
   314  				// If we can't parse it, just return the string.
   315  				return s, nil
   316  			}
   317  			return obj, nil
   318  		case "array":
   319  			var slice []interface{}
   320  			if err := json.Unmarshal([]byte(s), &slice); err != nil {
   321  				// If we can't parse it, just return the string.
   322  				return s, nil
   323  			}
   324  			return slice, nil
   325  		default:
   326  			return s, nil
   327  		}
   328  	} else {
   329  		return s, nil
   330  	}
   331  }
   332  
   333  type setDefault struct {
   334  	scheme  *spec.SimpleSchema
   335  	builder validationBuilder
   336  	rx      *regexp.Regexp
   337  }
   338  
   339  func (sd *setDefault) Matches(line string) bool {
   340  	return sd.rx.MatchString(line)
   341  }
   342  
   343  func (sd *setDefault) Parse(lines []string) error {
   344  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
   345  		return nil
   346  	}
   347  	matches := sd.rx.FindStringSubmatch(lines[0])
   348  	if len(matches) > 1 && len(matches[1]) > 0 {
   349  		d, err := parseValueFromSchema(matches[1], sd.scheme)
   350  		if err != nil {
   351  			return err
   352  		}
   353  		sd.builder.SetDefault(d)
   354  	}
   355  	return nil
   356  }
   357  
   358  type setExample struct {
   359  	scheme  *spec.SimpleSchema
   360  	builder validationBuilder
   361  	rx      *regexp.Regexp
   362  }
   363  
   364  func (se *setExample) Matches(line string) bool {
   365  	return se.rx.MatchString(line)
   366  }
   367  
   368  func (se *setExample) Parse(lines []string) error {
   369  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
   370  		return nil
   371  	}
   372  	matches := se.rx.FindStringSubmatch(lines[0])
   373  	if len(matches) > 1 && len(matches[1]) > 0 {
   374  		d, err := parseValueFromSchema(matches[1], se.scheme)
   375  		if err != nil {
   376  			return err
   377  		}
   378  		se.builder.SetExample(d)
   379  	}
   380  	return nil
   381  }
   382  
   383  type matchOnlyParam struct {
   384  	tgt *spec.Parameter
   385  	rx  *regexp.Regexp
   386  }
   387  
   388  func (mo *matchOnlyParam) Matches(line string) bool {
   389  	return mo.rx.MatchString(line)
   390  }
   391  
   392  func (mo *matchOnlyParam) Parse(lines []string) error {
   393  	return nil
   394  }
   395  
   396  type setRequiredParam struct {
   397  	tgt *spec.Parameter
   398  }
   399  
   400  func (su *setRequiredParam) Matches(line string) bool {
   401  	return rxRequired.MatchString(line)
   402  }
   403  
   404  func (su *setRequiredParam) Parse(lines []string) error {
   405  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
   406  		return nil
   407  	}
   408  	matches := rxRequired.FindStringSubmatch(lines[0])
   409  	if len(matches) > 1 && len(matches[1]) > 0 {
   410  		req, err := strconv.ParseBool(matches[1])
   411  		if err != nil {
   412  			return err
   413  		}
   414  		su.tgt.Required = req
   415  	}
   416  	return nil
   417  }
   418  
   419  type setReadOnlySchema struct {
   420  	tgt *spec.Schema
   421  }
   422  
   423  func (su *setReadOnlySchema) Matches(line string) bool {
   424  	return rxReadOnly.MatchString(line)
   425  }
   426  
   427  func (su *setReadOnlySchema) Parse(lines []string) error {
   428  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
   429  		return nil
   430  	}
   431  	matches := rxReadOnly.FindStringSubmatch(lines[0])
   432  	if len(matches) > 1 && len(matches[1]) > 0 {
   433  		req, err := strconv.ParseBool(matches[1])
   434  		if err != nil {
   435  			return err
   436  		}
   437  		su.tgt.ReadOnly = req
   438  	}
   439  	return nil
   440  }
   441  
   442  type setDiscriminator struct {
   443  	schema *spec.Schema
   444  	field  string
   445  }
   446  
   447  func (su *setDiscriminator) Matches(line string) bool {
   448  	return rxDiscriminator.MatchString(line)
   449  }
   450  
   451  func (su *setDiscriminator) Parse(lines []string) error {
   452  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
   453  		return nil
   454  	}
   455  	matches := rxDiscriminator.FindStringSubmatch(lines[0])
   456  	if len(matches) > 1 && len(matches[1]) > 0 {
   457  		req, err := strconv.ParseBool(matches[1])
   458  		if err != nil {
   459  			return err
   460  		}
   461  		if req {
   462  			su.schema.Discriminator = su.field
   463  		} else {
   464  			if su.schema.Discriminator == su.field {
   465  				su.schema.Discriminator = ""
   466  			}
   467  		}
   468  	}
   469  	return nil
   470  }
   471  
   472  type setRequiredSchema struct {
   473  	schema *spec.Schema
   474  	field  string
   475  }
   476  
   477  func (su *setRequiredSchema) Matches(line string) bool {
   478  	return rxRequired.MatchString(line)
   479  }
   480  
   481  func (su *setRequiredSchema) Parse(lines []string) error {
   482  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
   483  		return nil
   484  	}
   485  	matches := rxRequired.FindStringSubmatch(lines[0])
   486  	if len(matches) > 1 && len(matches[1]) > 0 {
   487  		req, err := strconv.ParseBool(matches[1])
   488  		if err != nil {
   489  			return err
   490  		}
   491  		midx := -1
   492  		for i, nm := range su.schema.Required {
   493  			if nm == su.field {
   494  				midx = i
   495  				break
   496  			}
   497  		}
   498  		if req {
   499  			if midx < 0 {
   500  				su.schema.Required = append(su.schema.Required, su.field)
   501  			}
   502  		} else if midx >= 0 {
   503  			su.schema.Required = append(su.schema.Required[:midx], su.schema.Required[midx+1:]...)
   504  		}
   505  	}
   506  	return nil
   507  }
   508  
   509  func newMultilineDropEmptyParser(rx *regexp.Regexp, set func([]string)) *multiLineDropEmptyParser {
   510  	return &multiLineDropEmptyParser{
   511  		rx:  rx,
   512  		set: set,
   513  	}
   514  }
   515  
   516  type multiLineDropEmptyParser struct {
   517  	set func([]string)
   518  	rx  *regexp.Regexp
   519  }
   520  
   521  func (m *multiLineDropEmptyParser) Matches(line string) bool {
   522  	return m.rx.MatchString(line)
   523  }
   524  
   525  func (m *multiLineDropEmptyParser) Parse(lines []string) error {
   526  	m.set(removeEmptyLines(lines))
   527  	return nil
   528  }
   529  
   530  func newSetSchemes(set func([]string)) *setSchemes {
   531  	return &setSchemes{
   532  		set: set,
   533  		rx:  rxSchemes,
   534  	}
   535  }
   536  
   537  type setSchemes struct {
   538  	set func([]string)
   539  	rx  *regexp.Regexp
   540  }
   541  
   542  func (ss *setSchemes) Matches(line string) bool {
   543  	return ss.rx.MatchString(line)
   544  }
   545  
   546  func (ss *setSchemes) Parse(lines []string) error {
   547  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
   548  		return nil
   549  	}
   550  	matches := ss.rx.FindStringSubmatch(lines[0])
   551  	if len(matches) > 1 && len(matches[1]) > 0 {
   552  		sch := strings.Split(matches[1], ", ")
   553  
   554  		var schemes []string
   555  		for _, s := range sch {
   556  			ts := strings.TrimSpace(s)
   557  			if ts != "" {
   558  				schemes = append(schemes, ts)
   559  			}
   560  		}
   561  		ss.set(schemes)
   562  	}
   563  	return nil
   564  }
   565  
   566  func newSetSecurity(rx *regexp.Regexp, setter func([]map[string][]string)) *setSecurity {
   567  	return &setSecurity{
   568  		set: setter,
   569  		rx:  rx,
   570  	}
   571  }
   572  
   573  type setSecurity struct {
   574  	set func([]map[string][]string)
   575  	rx  *regexp.Regexp
   576  }
   577  
   578  func (ss *setSecurity) Matches(line string) bool {
   579  	return ss.rx.MatchString(line)
   580  }
   581  
   582  func (ss *setSecurity) Parse(lines []string) error {
   583  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
   584  		return nil
   585  	}
   586  
   587  	var result []map[string][]string
   588  	for _, line := range lines {
   589  		kv := strings.SplitN(line, ":", 2)
   590  		scopes := []string{}
   591  		var key string
   592  
   593  		if len(kv) > 1 {
   594  			scs := strings.Split(kv[1], ",")
   595  			for _, scope := range scs {
   596  				tr := strings.TrimSpace(scope)
   597  				if tr != "" {
   598  					tr = strings.SplitAfter(tr, " ")[0]
   599  					scopes = append(scopes, strings.TrimSpace(tr))
   600  				}
   601  			}
   602  
   603  			key = strings.TrimSpace(kv[0])
   604  
   605  			result = append(result, map[string][]string{key: scopes})
   606  		}
   607  	}
   608  	ss.set(result)
   609  	return nil
   610  }
   611  
   612  func newSetResponses(definitions map[string]spec.Schema, responses map[string]spec.Response, setter func(*spec.Response, map[int]spec.Response)) *setOpResponses {
   613  	return &setOpResponses{
   614  		set:         setter,
   615  		rx:          rxResponses,
   616  		definitions: definitions,
   617  		responses:   responses,
   618  	}
   619  }
   620  
   621  type setOpResponses struct {
   622  	set         func(*spec.Response, map[int]spec.Response)
   623  	rx          *regexp.Regexp
   624  	definitions map[string]spec.Schema
   625  	responses   map[string]spec.Response
   626  }
   627  
   628  func (ss *setOpResponses) Matches(line string) bool {
   629  	return ss.rx.MatchString(line)
   630  }
   631  
   632  //ResponseTag used when specifying a response to point to a defined swagger:response
   633  const ResponseTag = "response"
   634  
   635  //BodyTag used when specifying a response to point to a model/schema
   636  const BodyTag = "body"
   637  
   638  //DescriptionTag used when specifying a response that gives a description of the response
   639  const DescriptionTag = "description"
   640  
   641  func parseTags(line string) (modelOrResponse string, arrays int, isDefinitionRef bool, description string, err error) {
   642  	tags := strings.Split(line, " ")
   643  	parsedModelOrResponse := false
   644  
   645  	for i, tagAndValue := range tags {
   646  		tagValList := strings.SplitN(tagAndValue, ":", 2)
   647  		var tag, value string
   648  		if len(tagValList) > 1 {
   649  			tag = tagValList[0]
   650  			value = tagValList[1]
   651  		} else {
   652  			//TODO: Print a warning, and in the long term, do not support not tagged values
   653  			//Add a default tag if none is supplied
   654  			if i == 0 {
   655  				tag = ResponseTag
   656  			} else {
   657  				tag = DescriptionTag
   658  			}
   659  			value = tagValList[0]
   660  		}
   661  
   662  		foundModelOrResponse := false
   663  		if !parsedModelOrResponse {
   664  			if tag == BodyTag {
   665  				foundModelOrResponse = true
   666  				isDefinitionRef = true
   667  			}
   668  			if tag == ResponseTag {
   669  				foundModelOrResponse = true
   670  				isDefinitionRef = false
   671  			}
   672  		}
   673  		if foundModelOrResponse {
   674  			//Read the model or response tag
   675  			parsedModelOrResponse = true
   676  			//Check for nested arrays
   677  			arrays = 0
   678  			for strings.HasPrefix(value, "[]") {
   679  				arrays++
   680  				value = value[2:]
   681  			}
   682  			//What's left over is the model name
   683  			modelOrResponse = value
   684  		} else {
   685  			foundDescription := false
   686  			if tag == DescriptionTag {
   687  				foundDescription = true
   688  			}
   689  			if foundDescription {
   690  				//Descriptions are special, they make they read the rest of the line
   691  				descriptionWords := []string{value}
   692  				if i < len(tags)-1 {
   693  					descriptionWords = append(descriptionWords, tags[i+1:]...)
   694  				}
   695  				description = strings.Join(descriptionWords, " ")
   696  				break
   697  			} else {
   698  				if tag == ResponseTag || tag == BodyTag || tag == DescriptionTag {
   699  					err = fmt.Errorf("Found valid tag %s, but not in a valid position", tag)
   700  				} else {
   701  					err = fmt.Errorf("Found invalid tag: %s", tag)
   702  				}
   703  				//return error
   704  				return
   705  			}
   706  		}
   707  	}
   708  
   709  	//TODO: Maybe do, if !parsedModelOrResponse {return some error}
   710  	return
   711  }
   712  
   713  func (ss *setOpResponses) Parse(lines []string) error {
   714  	if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) {
   715  		return nil
   716  	}
   717  
   718  	var def *spec.Response
   719  	var scr map[int]spec.Response
   720  
   721  	for _, line := range lines {
   722  		kv := strings.SplitN(line, ":", 2)
   723  		var key, value string
   724  
   725  		if len(kv) > 1 {
   726  			key = strings.TrimSpace(kv[0])
   727  			if key == "" {
   728  				// this must be some weird empty line
   729  				continue
   730  			}
   731  			value = strings.TrimSpace(kv[1])
   732  			if value == "" {
   733  				var resp spec.Response
   734  				if strings.EqualFold("default", key) {
   735  					if def == nil {
   736  						def = &resp
   737  					}
   738  				} else {
   739  					if sc, err := strconv.Atoi(key); err == nil {
   740  						if scr == nil {
   741  							scr = make(map[int]spec.Response)
   742  						}
   743  						scr[sc] = resp
   744  					}
   745  				}
   746  				continue
   747  			}
   748  			refTarget, arrays, isDefinitionRef, description, err := parseTags(value)
   749  			if err != nil {
   750  				return err
   751  			}
   752  			//A possible exception for having a definition
   753  			if _, ok := ss.responses[refTarget]; !ok {
   754  				if _, ok := ss.definitions[refTarget]; ok {
   755  					isDefinitionRef = true
   756  				}
   757  			}
   758  
   759  			var ref spec.Ref
   760  			if isDefinitionRef {
   761  				if description == "" {
   762  					description = refTarget
   763  				}
   764  				ref, err = spec.NewRef("#/definitions/" + refTarget)
   765  			} else {
   766  				ref, err = spec.NewRef("#/responses/" + refTarget)
   767  			}
   768  			if err != nil {
   769  				return err
   770  			}
   771  
   772  			// description should used on anyway.
   773  			resp := spec.Response{ResponseProps: spec.ResponseProps{Description: description}}
   774  
   775  			if isDefinitionRef {
   776  				resp.Schema = new(spec.Schema)
   777  				resp.Description = description
   778  				if arrays == 0 {
   779  					resp.Schema.Ref = ref
   780  				} else {
   781  					cs := resp.Schema
   782  					for i := 0; i < arrays; i++ {
   783  						cs.Typed("array", "")
   784  						cs.Items = new(spec.SchemaOrArray)
   785  						cs.Items.Schema = new(spec.Schema)
   786  						cs = cs.Items.Schema
   787  					}
   788  					cs.Ref = ref
   789  				}
   790  				// ref. could be empty while use description tag
   791  			} else if len(refTarget) > 0 {
   792  				resp.Ref = ref
   793  			}
   794  
   795  			if strings.EqualFold("default", key) {
   796  				if def == nil {
   797  					def = &resp
   798  				}
   799  			} else {
   800  				if sc, err := strconv.Atoi(key); err == nil {
   801  					if scr == nil {
   802  						scr = make(map[int]spec.Response)
   803  					}
   804  					scr[sc] = resp
   805  				}
   806  			}
   807  		}
   808  	}
   809  	ss.set(def, scr)
   810  	return nil
   811  }